magic-editor-x 1.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 (37) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +890 -0
  3. package/dist/_chunks/App-B1FgOsWa.mjs +2143 -0
  4. package/dist/_chunks/App-mtrlABtd.js +2146 -0
  5. package/dist/_chunks/LicensePage-BnyWSrWs.js +375 -0
  6. package/dist/_chunks/LicensePage-CWH-AFR-.mjs +373 -0
  7. package/dist/_chunks/LiveCollaborationPanel-DbDHwr2C.js +222 -0
  8. package/dist/_chunks/LiveCollaborationPanel-ryjcDAA7.mjs +220 -0
  9. package/dist/_chunks/Settings-Bk9bxJTy.js +440 -0
  10. package/dist/_chunks/Settings-D-V2MLVm.mjs +438 -0
  11. package/dist/_chunks/de-CSrHZWEb.mjs +295 -0
  12. package/dist/_chunks/de-CzSo1oD2.js +295 -0
  13. package/dist/_chunks/en-DuQun2v4.mjs +295 -0
  14. package/dist/_chunks/en-DxIkVPUh.js +295 -0
  15. package/dist/_chunks/es-DAQ_97zx.js +273 -0
  16. package/dist/_chunks/es-DEB0CA8S.mjs +273 -0
  17. package/dist/_chunks/fr-Bqkhvdx2.mjs +273 -0
  18. package/dist/_chunks/fr-ChPabvNP.js +273 -0
  19. package/dist/_chunks/getTranslation-C4uWR0DB.mjs +50985 -0
  20. package/dist/_chunks/getTranslation-D35vbDap.js +51001 -0
  21. package/dist/_chunks/index-B5MzUyo0.mjs +2541 -0
  22. package/dist/_chunks/index-BRVqbnOb.mjs +4450 -0
  23. package/dist/_chunks/index-BiLy_f7C.js +2540 -0
  24. package/dist/_chunks/index-CQx7-dFP.js +4472 -0
  25. package/dist/_chunks/pt-BMoYltav.mjs +273 -0
  26. package/dist/_chunks/pt-Cm74LpyZ.js +273 -0
  27. package/dist/_chunks/tools-CjnQJ9w2.mjs +2155 -0
  28. package/dist/_chunks/tools-DNt2tioN.js +2186 -0
  29. package/dist/admin/index.js +3 -0
  30. package/dist/admin/index.mjs +4 -0
  31. package/dist/server/index.js +2554 -0
  32. package/dist/server/index.mjs +2544 -0
  33. package/dist/style.css +164 -0
  34. package/package.json +122 -0
  35. package/pics/collab-magiceditorX.png +0 -0
  36. package/pics/editorX.png +0 -0
  37. package/pics/liveCollabwidget1.png +0 -0
@@ -0,0 +1,373 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect } from "react";
3
+ import { useFetchClient, useNotification } from "@strapi/strapi/admin";
4
+ import { u as useIntl, a as Flex, L as Loader, T as Typography, d as Badge, B as Box, g as getTranslation, c as Button } from "./getTranslation-C4uWR0DB.mjs";
5
+ import styled from "styled-components";
6
+ import { F as ForwardRef$2o, a as ForwardRef$26, b as ForwardRef$o, c as ForwardRef$1g, d as ForwardRef$y } from "./index-B5MzUyo0.mjs";
7
+ const Container = styled(Box)`
8
+ padding: 32px;
9
+ max-width: 1400px;
10
+ margin: 0 auto;
11
+ `;
12
+ const Header = styled(Box)`
13
+ text-align: center;
14
+ margin-bottom: 48px;
15
+ display: flex;
16
+ flex-direction: column;
17
+ align-items: center;
18
+ gap: 8px;
19
+ `;
20
+ const Title = styled(Typography)`
21
+ font-size: 2.5rem;
22
+ font-weight: 700;
23
+ margin-bottom: 8px;
24
+ background: linear-gradient(135deg, #7C3AED, #6d28d9);
25
+ -webkit-background-clip: text;
26
+ -webkit-text-fill-color: transparent;
27
+ display: block;
28
+ `;
29
+ const Subtitle = styled(Typography)`
30
+ font-size: 1.125rem;
31
+ color: ${(props) => props.theme.colors.neutral600};
32
+ line-height: 1.6;
33
+ display: block;
34
+ `;
35
+ const TierGrid = styled(Flex)`
36
+ gap: 32px;
37
+ margin: 0 auto 48px;
38
+ max-width: 1080px;
39
+ justify-content: center;
40
+ flex-wrap: wrap;
41
+ align-items: stretch;
42
+ `;
43
+ const TierWrapper = styled(Box)`
44
+ flex: 1;
45
+ min-width: 280px;
46
+ max-width: 340px;
47
+ display: flex;
48
+ `;
49
+ const TierCard = styled(Box)`
50
+ background: ${(props) => props.theme.colors.neutral0};
51
+ border-radius: 16px;
52
+ padding: 32px;
53
+ border: 2px solid ${(props) => props.$featured ? "#7C3AED" : props.theme.colors.neutral200};
54
+ position: relative;
55
+ transition: all 0.3s ease;
56
+ box-shadow: ${(props) => props.$featured ? "0 20px 25px -5px rgba(124, 58, 237, 0.25), 0 8px 10px -6px rgba(124, 58, 237, 0.2)" : "0 10px 15px -3px rgba(15, 23, 42, 0.08), 0 4px 6px -4px rgba(15, 23, 42, 0.05)"};
57
+ display: flex;
58
+ flex-direction: column;
59
+ width: 100%;
60
+
61
+ &:hover {
62
+ transform: translateY(-4px);
63
+ box-shadow: 0 20px 25px -5px rgba(15, 23, 42, 0.15), 0 8px 10px -6px rgba(15, 23, 42, 0.1);
64
+ }
65
+ `;
66
+ const PopularBadge = styled(Badge)`
67
+ position: absolute;
68
+ top: -12px;
69
+ right: 24px;
70
+ background: linear-gradient(135deg, #7C3AED, #6d28d9);
71
+ color: white;
72
+ padding: 4px 16px;
73
+ font-size: 12px;
74
+ font-weight: 600;
75
+ `;
76
+ const TierIcon = styled(Box)`
77
+ width: 48px;
78
+ height: 48px;
79
+ border-radius: 12px;
80
+ display: flex;
81
+ align-items: center;
82
+ justify-content: center;
83
+ margin-bottom: 16px;
84
+ background: ${(props) => props.$color};
85
+
86
+ svg {
87
+ width: 28px;
88
+ height: 28px;
89
+ color: white;
90
+ }
91
+ `;
92
+ const TierName = styled(Typography)`
93
+ font-size: 1.5rem;
94
+ font-weight: 700;
95
+ margin-bottom: 8px;
96
+ `;
97
+ const TierPrice = styled(Typography)`
98
+ font-size: 2rem;
99
+ font-weight: 800;
100
+ margin-bottom: 4px;
101
+ `;
102
+ const TierDescription = styled(Typography)`
103
+ color: ${(props) => props.theme.colors.neutral600};
104
+ margin-bottom: 24px;
105
+ `;
106
+ const FeatureList = styled(Box)`
107
+ margin-bottom: 24px;
108
+ flex: 1;
109
+ `;
110
+ const Feature = styled(Flex)`
111
+ gap: 12px;
112
+ margin-bottom: 12px;
113
+ align-items: flex-start;
114
+ `;
115
+ const FeatureIcon = styled(Box)`
116
+ width: 20px;
117
+ height: 20px;
118
+ border-radius: 50%;
119
+ display: flex;
120
+ align-items: center;
121
+ justify-content: center;
122
+ flex-shrink: 0;
123
+ margin-top: 2px;
124
+
125
+ ${(props) => props.$included ? `
126
+ background: #DCFCE7;
127
+ svg { color: #16A34A; }
128
+ ` : `
129
+ background: #FEE2E2;
130
+ svg { color: #DC2626; }
131
+ `}
132
+ `;
133
+ const UpgradeButton = styled(Button)`
134
+ width: 100%;
135
+ height: 48px;
136
+ font-weight: 600;
137
+ font-size: 15px;
138
+ background: ${(props) => props.$gradient};
139
+ border: none;
140
+ color: white;
141
+
142
+ &:hover {
143
+ transform: translateY(-2px);
144
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
145
+ }
146
+ `;
147
+ const CurrentPlanBadge = styled(Badge)`
148
+ width: 100%;
149
+ height: 48px;
150
+ display: flex;
151
+ align-items: center;
152
+ justify-content: center;
153
+ background: ${(props) => props.theme.colors.neutral100};
154
+ color: ${(props) => props.theme.colors.neutral600};
155
+ font-weight: 600;
156
+ font-size: 15px;
157
+ `;
158
+ const UsageBox = styled(Box)`
159
+ background: ${(props) => props.theme.colors.neutral100};
160
+ border: 1px solid ${(props) => props.theme.colors.neutral200};
161
+ border-radius: 12px;
162
+ padding: 20px;
163
+ margin-bottom: 32px;
164
+ `;
165
+ const UsageBar = styled.div`
166
+ height: 8px;
167
+ background: ${(props) => props.theme.colors.neutral200};
168
+ border-radius: 4px;
169
+ overflow: hidden;
170
+ margin-top: 8px;
171
+ `;
172
+ const UsageProgress = styled.div`
173
+ height: 100%;
174
+ background: ${(props) => props.$percentage > 80 ? "#ef4444" : props.$percentage > 50 ? "#f59e0b" : "#10b981"};
175
+ width: ${(props) => Math.min(props.$percentage, 100)}%;
176
+ transition: width 0.3s ease;
177
+ `;
178
+ const LicensePage = () => {
179
+ const { formatMessage } = useIntl();
180
+ const t = (id, defaultMessage, values) => formatMessage({ id: getTranslation(id), defaultMessage }, values);
181
+ const { get } = useFetchClient();
182
+ const { toggleNotification } = useNotification();
183
+ const [currentTier, setCurrentTier] = useState("free");
184
+ const [limits, setLimits] = useState(null);
185
+ const [loading, setLoading] = useState(true);
186
+ useEffect(() => {
187
+ fetchLicenseInfo();
188
+ }, []);
189
+ const fetchLicenseInfo = async () => {
190
+ try {
191
+ const response = await get("/magic-editor-x/license/limits");
192
+ const licenseData = response.data || {};
193
+ setCurrentTier(licenseData.tier || "free");
194
+ setLimits(licenseData.limits);
195
+ setLoading(false);
196
+ } catch (error) {
197
+ console.error("Failed to fetch license info:", error);
198
+ setLoading(false);
199
+ }
200
+ };
201
+ const getTierRank = (tierId) => {
202
+ const ranks = {
203
+ "free": 0,
204
+ "premium": 1,
205
+ "advanced": 2,
206
+ "enterprise": 3
207
+ };
208
+ return ranks[tierId] || 0;
209
+ };
210
+ const getButtonText = (tierId) => {
211
+ const currentRank = getTierRank(currentTier);
212
+ const targetRank = getTierRank(tierId);
213
+ if (currentRank === targetRank) {
214
+ return t("upgradePage.currentPlan", "Current Plan");
215
+ } else if (targetRank > currentRank) {
216
+ return t("upgradePage.upgradeNow", "Upgrade Now");
217
+ } else {
218
+ return t("upgradePage.downgrade", "Downgrade");
219
+ }
220
+ };
221
+ const tiers = [
222
+ {
223
+ id: "free",
224
+ name: "FREE",
225
+ price: "$0",
226
+ period: "forever",
227
+ description: "Perfect for small projects and testing",
228
+ icon: /* @__PURE__ */ jsx(ForwardRef$o, {}),
229
+ color: "linear-gradient(135deg, #6B7280, #4B5563)",
230
+ features: [
231
+ { name: "Full Editor Access", included: true },
232
+ { name: "All Editor Tools", included: true },
233
+ { name: "2 Collaborators", included: true },
234
+ { name: "Real-time Sync", included: true },
235
+ { name: "AI Grammar Check (3/day)", included: true },
236
+ { name: "AI Style + Rewrite", included: false },
237
+ { name: "Version History", included: false },
238
+ { name: "Priority Support", included: false }
239
+ ],
240
+ limits: {
241
+ collaborators: "2"
242
+ }
243
+ },
244
+ {
245
+ id: "premium",
246
+ name: "PREMIUM",
247
+ price: "$9.90",
248
+ period: "/month",
249
+ description: "Enhanced collaboration for teams",
250
+ icon: /* @__PURE__ */ jsx(ForwardRef$1g, {}),
251
+ color: "linear-gradient(135deg, #8B5CF6, #7C3AED)",
252
+ featured: true,
253
+ features: [
254
+ { name: "Full Editor Access", included: true },
255
+ { name: "All Editor Tools", included: true },
256
+ { name: "10 Collaborators", included: true },
257
+ { name: "Real-time Sync", included: true },
258
+ { name: "AI Grammar + Style (10/day)", included: true },
259
+ { name: "Version History", included: true },
260
+ { name: "Priority Support", included: true }
261
+ ],
262
+ limits: {
263
+ collaborators: "10"
264
+ }
265
+ },
266
+ {
267
+ id: "advanced",
268
+ name: "ADVANCED",
269
+ price: "$24.90",
270
+ period: "/month",
271
+ description: "Unlimited collaboration for enterprises",
272
+ icon: /* @__PURE__ */ jsx(ForwardRef$y, {}),
273
+ color: "linear-gradient(135deg, #7C3AED, #6d28d9)",
274
+ features: [
275
+ { name: "Full Editor Access", included: true },
276
+ { name: "All Editor Tools", included: true },
277
+ { name: "Unlimited Collaborators", included: true },
278
+ { name: "Real-time Sync", included: true },
279
+ { name: "AI All Types (Unlimited)", included: true },
280
+ { name: "Version History", included: true },
281
+ { name: "Priority Support", included: true }
282
+ ],
283
+ limits: {
284
+ collaborators: "Unlimited"
285
+ }
286
+ }
287
+ ];
288
+ const handleUpgrade = (tierId) => {
289
+ window.open("https://store.magicdx.dev/", "_blank");
290
+ };
291
+ if (loading) {
292
+ return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsx(Flex, { justifyContent: "center", alignItems: "center", style: { minHeight: "400px" }, children: /* @__PURE__ */ jsx(Loader, { children: t("license.loading", "Loading license information...") }) }) });
293
+ }
294
+ const collaboratorUsage = limits?.collaborators ? {
295
+ current: limits.collaborators.current || 0,
296
+ max: limits.collaborators.max,
297
+ unlimited: limits.collaborators.unlimited,
298
+ percentage: limits.collaborators.unlimited ? 0 : limits.collaborators.current / limits.collaborators.max * 100
299
+ } : { current: 0, max: 2, unlimited: false, percentage: 0 };
300
+ return /* @__PURE__ */ jsxs(Container, { children: [
301
+ /* @__PURE__ */ jsxs(Header, { children: [
302
+ /* @__PURE__ */ jsx(Title, { variant: "alpha", children: t("upgradePage.title", "Magic Editor X") }),
303
+ /* @__PURE__ */ jsx(Subtitle, { variant: "omega", children: t("upgradePage.subtitle", "Choose your plan for collaborative editing") })
304
+ ] }),
305
+ /* @__PURE__ */ jsxs(UsageBox, { children: [
306
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
307
+ /* @__PURE__ */ jsx(Typography, { variant: "beta", fontWeight: "bold", children: t("upgradePage.currentUsage", "Current Usage") }),
308
+ /* @__PURE__ */ jsx(Badge, { style: { background: currentTier === "free" ? "#6B7280" : "#7C3AED", color: "white" }, children: currentTier.toUpperCase() })
309
+ ] }),
310
+ /* @__PURE__ */ jsxs(Box, { marginTop: 4, children: [
311
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", children: [
312
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", children: t("upgradePage.collaborators", "Collaborators") }),
313
+ /* @__PURE__ */ jsxs(Typography, { variant: "omega", fontWeight: "bold", children: [
314
+ collaboratorUsage.current,
315
+ " / ",
316
+ collaboratorUsage.unlimited ? t("license.unlimited", "Unlimited") : collaboratorUsage.max
317
+ ] })
318
+ ] }),
319
+ !collaboratorUsage.unlimited && /* @__PURE__ */ jsx(UsageBar, { children: /* @__PURE__ */ jsx(UsageProgress, { $percentage: collaboratorUsage.percentage }) })
320
+ ] })
321
+ ] }),
322
+ /* @__PURE__ */ jsx(TierGrid, { children: tiers.map((tier) => /* @__PURE__ */ jsx(TierWrapper, { children: /* @__PURE__ */ jsxs(TierCard, { $featured: tier.featured, children: [
323
+ tier.featured && /* @__PURE__ */ jsx(PopularBadge, { children: t("upgradePage.mostPopular", "MOST POPULAR") }),
324
+ /* @__PURE__ */ jsx(TierIcon, { $color: tier.color, children: tier.icon }),
325
+ /* @__PURE__ */ jsx(TierName, { variant: "beta", children: tier.name }),
326
+ /* @__PURE__ */ jsxs(Flex, { alignItems: "baseline", gap: 1, children: [
327
+ /* @__PURE__ */ jsx(TierPrice, { variant: "alpha", children: tier.price }),
328
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", style: { color: "#6B7280" }, children: tier.period })
329
+ ] }),
330
+ /* @__PURE__ */ jsx(TierDescription, { variant: "omega", children: tier.description }),
331
+ /* @__PURE__ */ jsx(
332
+ Box,
333
+ {
334
+ background: "neutral100",
335
+ hasRadius: true,
336
+ padding: 3,
337
+ marginBottom: 5,
338
+ children: /* @__PURE__ */ jsxs(Typography, { variant: "pi", style: { fontSize: "13px" }, children: [
339
+ /* @__PURE__ */ jsx("strong", { children: "Collaborators:" }),
340
+ " ",
341
+ tier.limits.collaborators
342
+ ] })
343
+ }
344
+ ),
345
+ /* @__PURE__ */ jsx(FeatureList, { children: tier.features.map((feature, index) => /* @__PURE__ */ jsxs(Feature, { children: [
346
+ /* @__PURE__ */ jsx(FeatureIcon, { $included: feature.included, children: feature.included ? /* @__PURE__ */ jsx(ForwardRef$2o, { style: { width: 14, height: 14 } }) : /* @__PURE__ */ jsx(ForwardRef$26, { style: { width: 14, height: 14 } }) }),
347
+ /* @__PURE__ */ jsx(
348
+ Typography,
349
+ {
350
+ variant: "omega",
351
+ textColor: feature.included ? "neutral800" : "neutral500",
352
+ style: {
353
+ fontSize: "14px",
354
+ textDecoration: feature.included ? "none" : "line-through"
355
+ },
356
+ children: feature.name
357
+ }
358
+ )
359
+ ] }, index)) }),
360
+ currentTier === tier.id ? /* @__PURE__ */ jsx(CurrentPlanBadge, { children: t("upgradePage.currentPlan", "Current Plan") }) : /* @__PURE__ */ jsx(
361
+ UpgradeButton,
362
+ {
363
+ $gradient: tier.color,
364
+ onClick: () => handleUpgrade(tier.id),
365
+ children: getButtonText(tier.id)
366
+ }
367
+ )
368
+ ] }) }, tier.id)) })
369
+ ] });
370
+ };
371
+ export {
372
+ LicensePage as default
373
+ };
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const React = require("react");
5
+ const getTranslation = require("./getTranslation-D35vbDap.js");
6
+ const styled = require("styled-components");
7
+ const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
8
+ const styled__default = /* @__PURE__ */ _interopDefault(styled);
9
+ const pulse = styled.keyframes`
10
+ 0%, 100% {
11
+ box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.2);
12
+ transform: scale(1);
13
+ }
14
+ 50% {
15
+ box-shadow: 0 0 0 6px rgba(34, 197, 94, 0.1);
16
+ transform: scale(1.1);
17
+ }
18
+ `;
19
+ const StatusCard = styled__default.default.div`
20
+ background: ${(props) => props.theme.colors.neutral0};
21
+ border: 1px solid ${({ $status, theme }) => $status === "connected" ? "rgba(34, 197, 94, 0.3)" : $status === "denied" ? "rgba(239, 68, 68, 0.3)" : theme.colors.neutral200};
22
+ border-radius: 10px;
23
+ padding: 14px 16px;
24
+ display: flex;
25
+ align-items: center;
26
+ gap: 12px;
27
+ `;
28
+ const StatusDot = styled__default.default.div`
29
+ width: 12px;
30
+ height: 12px;
31
+ border-radius: 50%;
32
+ flex-shrink: 0;
33
+ background: ${({ $status }) => $status === "connected" ? "#22c55e" : $status === "connecting" || $status === "requesting" ? "#f59e0b" : $status === "denied" ? "#ef4444" : "#94a3b8"};
34
+
35
+ ${({ $status }) => $status === "connected" && styled.css`
36
+ animation: ${pulse} 2s ease-in-out infinite;
37
+ `}
38
+ `;
39
+ const StatusText = styled__default.default.div`
40
+ display: flex;
41
+ flex-direction: column;
42
+ gap: 2px;
43
+ `;
44
+ const StatusLabel = styled__default.default.span`
45
+ font-size: 14px;
46
+ font-weight: 600;
47
+ color: ${({ $status, theme }) => $status === "connected" ? theme.colors.success600 : $status === "connecting" || $status === "requesting" ? theme.colors.warning600 : $status === "denied" ? theme.colors.danger600 : theme.colors.neutral600};
48
+ `;
49
+ const StatusSubtext = styled__default.default.span`
50
+ font-size: 12px;
51
+ color: ${(props) => props.theme.colors.neutral500};
52
+ `;
53
+ const SectionTitle = styled__default.default.div`
54
+ font-size: 11px;
55
+ font-weight: 600;
56
+ color: ${(props) => props.theme.colors.neutral600};
57
+ text-transform: uppercase;
58
+ letter-spacing: 0.5px;
59
+ margin-bottom: 10px;
60
+ `;
61
+ const PeerItem = styled__default.default.div`
62
+ display: flex;
63
+ align-items: center;
64
+ gap: 12px;
65
+ padding: 12px 14px;
66
+ background: ${(props) => props.theme.colors.neutral0};
67
+ border-radius: 10px;
68
+ border: 1px solid ${(props) => props.theme.colors.neutral150};
69
+ transition: all 0.2s ease;
70
+
71
+ &:hover {
72
+ border-color: ${(props) => props.theme.colors.primary200};
73
+ box-shadow: 0 2px 8px rgba(124, 58, 237, 0.08);
74
+ transform: translateY(-1px);
75
+ }
76
+ `;
77
+ const PEER_COLORS = [
78
+ "linear-gradient(135deg, #7C3AED 0%, #a855f7 100%)",
79
+ "linear-gradient(135deg, #3b82f6 0%, #60a5fa 100%)",
80
+ "linear-gradient(135deg, #10b981 0%, #34d399 100%)",
81
+ "linear-gradient(135deg, #f59e0b 0%, #fbbf24 100%)",
82
+ "linear-gradient(135deg, #ef4444 0%, #f87171 100%)",
83
+ "linear-gradient(135deg, #ec4899 0%, #f472b6 100%)"
84
+ ];
85
+ const PeerAvatar = styled__default.default.div`
86
+ width: 36px;
87
+ height: 36px;
88
+ border-radius: 50%;
89
+ background: ${({ $color }) => $color || PEER_COLORS[0]};
90
+ color: white;
91
+ font-size: 12px;
92
+ font-weight: 700;
93
+ display: flex;
94
+ align-items: center;
95
+ justify-content: center;
96
+ flex-shrink: 0;
97
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
98
+ `;
99
+ const PeerInfo = styled__default.default.div`
100
+ flex: 1;
101
+ min-width: 0;
102
+ display: flex;
103
+ flex-direction: column;
104
+ gap: 2px;
105
+ `;
106
+ const PeerName = styled__default.default.span`
107
+ font-size: 13px;
108
+ font-weight: 600;
109
+ color: ${(props) => props.theme.colors.neutral800};
110
+ white-space: nowrap;
111
+ overflow: hidden;
112
+ text-overflow: ellipsis;
113
+ `;
114
+ const PeerEmail = styled__default.default.span`
115
+ font-size: 11px;
116
+ color: ${(props) => props.theme.colors.neutral500};
117
+ white-space: nowrap;
118
+ overflow: hidden;
119
+ text-overflow: ellipsis;
120
+ `;
121
+ const OnlineBadge = styled__default.default.span`
122
+ font-size: 10px;
123
+ font-weight: 600;
124
+ color: #166534;
125
+ background: #dcfce7;
126
+ padding: 4px 8px;
127
+ border-radius: 12px;
128
+ flex-shrink: 0;
129
+ `;
130
+ const EmptyState = styled__default.default.div`
131
+ text-align: center;
132
+ padding: 16px;
133
+ background: ${(props) => props.theme.colors.neutral100};
134
+ border-radius: 10px;
135
+ border: 1px dashed ${(props) => props.theme.colors.neutral300};
136
+ `;
137
+ const EmptyText = styled__default.default.span`
138
+ font-size: 13px;
139
+ color: ${(props) => props.theme.colors.neutral500};
140
+ `;
141
+ const getPeerInitials = (user = {}) => {
142
+ const first = (user.firstname?.[0] || user.email?.[0] || "?").toUpperCase();
143
+ const last = (user.lastname?.[0] || "").toUpperCase();
144
+ return `${first}${last}`.trim();
145
+ };
146
+ const getPeerName = (user = {}, t) => {
147
+ if (user.firstname) {
148
+ return `${user.firstname} ${user.lastname || ""}`.trim();
149
+ }
150
+ return user.email || t("collab.unknown", "Unknown");
151
+ };
152
+ const LiveCollaborationPanel = ({ documentId, model, document }) => {
153
+ const { formatMessage } = getTranslation.useIntl();
154
+ const t = (id, defaultMessage, values) => formatMessage({ id: getTranslation.getTranslation(id), defaultMessage }, values);
155
+ const [collabState, setCollabState] = React.useState({
156
+ status: "disabled",
157
+ peers: [],
158
+ error: null
159
+ });
160
+ React.useEffect(() => {
161
+ const handleCollabUpdate = (event) => {
162
+ if (event.detail) {
163
+ setCollabState(event.detail);
164
+ }
165
+ };
166
+ window.addEventListener("magic-editor-collab-update", handleCollabUpdate);
167
+ if (window.__MAGIC_EDITOR_COLLAB_STATE__) {
168
+ setCollabState(window.__MAGIC_EDITOR_COLLAB_STATE__);
169
+ }
170
+ return () => {
171
+ window.removeEventListener("magic-editor-collab-update", handleCollabUpdate);
172
+ };
173
+ }, []);
174
+ const { status, peers, error } = collabState;
175
+ const statusLabel = React.useMemo(() => {
176
+ switch (status) {
177
+ case "connected":
178
+ return t("collab.live", "Live");
179
+ case "connecting":
180
+ return t("collab.connecting", "Connecting...");
181
+ case "requesting":
182
+ return t("collab.checkingPermission", "Checking permission");
183
+ case "denied":
184
+ return t("collab.noPermission", "No permission");
185
+ case "disconnected":
186
+ return t("collab.disconnected", "Disconnected");
187
+ case "disabled":
188
+ return t("collab.disabled", "Disabled");
189
+ default:
190
+ return t("collab.ready", "Ready");
191
+ }
192
+ }, [status, t]);
193
+ if (status === "disabled" || status === "idle") {
194
+ return null;
195
+ }
196
+ const isConnected = status === "connected";
197
+ return {
198
+ title: t("collab.title", "Live Collaboration"),
199
+ content: /* @__PURE__ */ jsxRuntime.jsxs(getTranslation.Flex, { direction: "column", gap: 4, alignItems: "stretch", style: { width: "100%" }, children: [
200
+ /* @__PURE__ */ jsxRuntime.jsxs(StatusCard, { $status: status, children: [
201
+ /* @__PURE__ */ jsxRuntime.jsx(StatusDot, { $status: status }),
202
+ /* @__PURE__ */ jsxRuntime.jsxs(StatusText, { children: [
203
+ /* @__PURE__ */ jsxRuntime.jsx(StatusLabel, { $status: status, children: statusLabel }),
204
+ /* @__PURE__ */ jsxRuntime.jsx(StatusSubtext, { children: isConnected ? t("collab.realtimeActive", "Realtime sync active") : error || t("collab.connectionEstablishing", "Connection is being established...") })
205
+ ] })
206
+ ] }),
207
+ isConnected && peers.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
208
+ /* @__PURE__ */ jsxRuntime.jsx(SectionTitle, { children: t("collab.activePeers", "Active Collaborators ({count})", { count: peers.length }) }),
209
+ /* @__PURE__ */ jsxRuntime.jsx(getTranslation.Flex, { direction: "column", gap: 2, alignItems: "stretch", children: peers.map((peer, idx) => /* @__PURE__ */ jsxRuntime.jsxs(PeerItem, { children: [
210
+ /* @__PURE__ */ jsxRuntime.jsx(PeerAvatar, { $color: PEER_COLORS[idx % PEER_COLORS.length], children: getPeerInitials(peer) }),
211
+ /* @__PURE__ */ jsxRuntime.jsxs(PeerInfo, { children: [
212
+ /* @__PURE__ */ jsxRuntime.jsx(PeerName, { children: getPeerName(peer, t) }),
213
+ peer.email && peer.firstname && /* @__PURE__ */ jsxRuntime.jsx(PeerEmail, { children: peer.email })
214
+ ] }),
215
+ /* @__PURE__ */ jsxRuntime.jsx(OnlineBadge, { children: t("collab.online", "Online") })
216
+ ] }, peer.id)) })
217
+ ] }),
218
+ isConnected && peers.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { children: /* @__PURE__ */ jsxRuntime.jsx(EmptyText, { children: t("collab.workingAlone", "You are working alone") }) })
219
+ ] })
220
+ };
221
+ };
222
+ exports.default = LiveCollaborationPanel;