magic-editor-x 1.3.7 → 1.4.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 (25) hide show
  1. package/README.md +131 -2
  2. package/dist/_chunks/{App-BrAGe1KP.js → App-ByYQ99dO.js} +4 -4
  3. package/dist/_chunks/{App-SlDn2xdO.mjs → App-C8q91Ico.mjs} +4 -4
  4. package/dist/_chunks/CustomBlocksPage-BkmF2iOQ.js +1518 -0
  5. package/dist/_chunks/CustomBlocksPage-rLRB05j0.mjs +1516 -0
  6. package/dist/_chunks/{LicensePage-C6TBYCWM.js → LicensePage-Bdn7zWU2.js} +30 -12
  7. package/dist/_chunks/{LicensePage-zaYXFrKs.mjs → LicensePage-DR_Cwyfw.mjs} +30 -12
  8. package/dist/_chunks/{LiveCollaborationPanel-yNC8e1Qb.mjs → LiveCollaborationPanel-0tplv17N.mjs} +1 -1
  9. package/dist/_chunks/{LiveCollaborationPanel-CGt57XG1.js → LiveCollaborationPanel-BUHIq0CQ.js} +1 -1
  10. package/dist/_chunks/{Settings-BH0Ttu_5.js → Settings-B5mffA2O.js} +1 -1
  11. package/dist/_chunks/{Settings-BXoP5tm9.mjs → Settings-FfpVHlpw.mjs} +1 -1
  12. package/dist/_chunks/{getTranslation-D2oq0PyC.mjs → getTranslation-Bk8tKbUs.mjs} +1 -1
  13. package/dist/_chunks/{getTranslation-D21ValYk.js → getTranslation-DOJ3x1SL.js} +1 -1
  14. package/dist/_chunks/{index-BnTvuHf2.js → index-C5DuaTDl.js} +15 -5
  15. package/dist/_chunks/{index-tbHD7slZ.mjs → index-DkpTkVe7.mjs} +15 -5
  16. package/dist/_chunks/{index-Czk5HVLJ.js → index-DzixAi6O.js} +1183 -16
  17. package/dist/_chunks/{index-CUTx2oym.mjs → index-zOiCW1bZ.mjs} +1183 -16
  18. package/dist/_chunks/{tools-D4Y_iEui.js → tools-BSMn5LLQ.js} +17 -2
  19. package/dist/_chunks/{tools-BGR6Cw4p.mjs → tools-uudZx91W.mjs} +17 -2
  20. package/dist/admin/index.js +1 -1
  21. package/dist/admin/index.mjs +1 -1
  22. package/dist/server/index.js +1172 -9
  23. package/dist/server/index.mjs +1172 -9
  24. package/dist/style.css +116 -7
  25. package/package.json +1 -1
@@ -0,0 +1,1518 @@
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 admin = require("@strapi/strapi/admin");
6
+ const styled = require("styled-components");
7
+ const outline = require("@heroicons/react/24/outline");
8
+ const solid = require("@heroicons/react/24/solid");
9
+ const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
10
+ const styled__default = /* @__PURE__ */ _interopDefault(styled);
11
+ const PLUGIN_ID = "magic-editor-x";
12
+ const fadeIn = styled.keyframes`
13
+ from { opacity: 0; transform: translateY(10px); }
14
+ to { opacity: 1; transform: translateY(0); }
15
+ `;
16
+ const slideIn = styled.keyframes`
17
+ from { opacity: 0; transform: translateX(-20px); }
18
+ to { opacity: 1; transform: translateX(0); }
19
+ `;
20
+ styled.keyframes`
21
+ 0%, 100% { transform: scale(1); }
22
+ 50% { transform: scale(1.05); }
23
+ `;
24
+ styled.keyframes`
25
+ 0% { background-position: -200% 0; }
26
+ 100% { background-position: 200% 0; }
27
+ `;
28
+ const PageWrapper = styled__default.default.div`
29
+ min-height: 100vh;
30
+ background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 50%, #e2e8f0 100%);
31
+ padding: 32px;
32
+ `;
33
+ const Container = styled__default.default.div`
34
+ max-width: 1400px;
35
+ margin: 0 auto;
36
+ animation: ${fadeIn} 0.4s ease-out;
37
+ `;
38
+ const Header = styled__default.default.div`
39
+ display: flex;
40
+ justify-content: space-between;
41
+ align-items: flex-start;
42
+ margin-bottom: 32px;
43
+ gap: 24px;
44
+ flex-wrap: wrap;
45
+ `;
46
+ const HeaderLeft = styled__default.default.div`
47
+ flex: 1;
48
+ min-width: 300px;
49
+ `;
50
+ const HeaderRight = styled__default.default.div`
51
+ display: flex;
52
+ gap: 12px;
53
+ flex-wrap: wrap;
54
+ `;
55
+ const PageTitle = styled__default.default.h1`
56
+ font-size: 2rem;
57
+ font-weight: 800;
58
+ color: #0f172a;
59
+ margin: 0 0 8px 0;
60
+ display: flex;
61
+ align-items: center;
62
+ gap: 12px;
63
+
64
+ svg {
65
+ width: 32px;
66
+ height: 32px;
67
+ color: #7c3aed;
68
+ }
69
+ `;
70
+ const PageSubtitle = styled__default.default.p`
71
+ font-size: 1rem;
72
+ color: #64748b;
73
+ margin: 0;
74
+ line-height: 1.5;
75
+ `;
76
+ const Button = styled__default.default.button`
77
+ display: inline-flex;
78
+ align-items: center;
79
+ gap: 8px;
80
+ padding: ${(props) => props.$size === "small" ? "8px 14px" : "12px 20px"};
81
+ font-size: ${(props) => props.$size === "small" ? "13px" : "14px"};
82
+ font-weight: 600;
83
+ border-radius: 12px;
84
+ border: none;
85
+ cursor: pointer;
86
+ transition: all 0.2s ease;
87
+
88
+ svg {
89
+ width: ${(props) => props.$size === "small" ? "16px" : "18px"};
90
+ height: ${(props) => props.$size === "small" ? "16px" : "18px"};
91
+ }
92
+
93
+ ${(props) => props.$variant === "primary" && `
94
+ background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%);
95
+ color: white;
96
+ box-shadow: 0 4px 14px rgba(124, 58, 237, 0.35);
97
+
98
+ &:hover {
99
+ transform: translateY(-2px);
100
+ box-shadow: 0 6px 20px rgba(124, 58, 237, 0.45);
101
+ }
102
+
103
+ &:active {
104
+ transform: translateY(0);
105
+ }
106
+ `}
107
+
108
+ ${(props) => props.$variant === "secondary" && `
109
+ background: white;
110
+ color: #475569;
111
+ border: 1px solid #e2e8f0;
112
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
113
+
114
+ &:hover {
115
+ background: #f8fafc;
116
+ border-color: #cbd5e1;
117
+ color: #1e293b;
118
+ }
119
+ `}
120
+
121
+ ${(props) => props.$variant === "ghost" && `
122
+ background: transparent;
123
+ color: #64748b;
124
+
125
+ &:hover {
126
+ background: rgba(124, 58, 237, 0.08);
127
+ color: #7c3aed;
128
+ }
129
+ `}
130
+
131
+ ${(props) => props.$variant === "danger" && `
132
+ background: transparent;
133
+ color: #ef4444;
134
+
135
+ &:hover {
136
+ background: rgba(239, 68, 68, 0.08);
137
+ }
138
+ `}
139
+
140
+ &:disabled {
141
+ opacity: 0.5;
142
+ cursor: not-allowed;
143
+ transform: none !important;
144
+ }
145
+ `;
146
+ const IconButton = styled__default.default.button`
147
+ display: flex;
148
+ align-items: center;
149
+ justify-content: center;
150
+ width: 36px;
151
+ height: 36px;
152
+ border-radius: 10px;
153
+ border: none;
154
+ cursor: pointer;
155
+ transition: all 0.2s ease;
156
+ background: transparent;
157
+ color: #64748b;
158
+
159
+ svg {
160
+ width: 18px;
161
+ height: 18px;
162
+ }
163
+
164
+ &:hover {
165
+ background: ${(props) => props.$danger ? "rgba(239, 68, 68, 0.1)" : "rgba(124, 58, 237, 0.1)"};
166
+ color: ${(props) => props.$danger ? "#ef4444" : "#7c3aed"};
167
+ transform: scale(1.05);
168
+ }
169
+
170
+ &:active {
171
+ transform: scale(0.95);
172
+ }
173
+ `;
174
+ const BlocksGrid = styled__default.default.div`
175
+ display: grid;
176
+ grid-template-columns: repeat(auto-fill, minmax(380px, 1fr));
177
+ gap: 20px;
178
+ `;
179
+ const BlockCard = styled__default.default.div`
180
+ background: white;
181
+ border-radius: 16px;
182
+ border: 1px solid #e2e8f0;
183
+ overflow: hidden;
184
+ transition: all 0.25s ease;
185
+ animation: ${slideIn} 0.3s ease-out;
186
+ animation-delay: ${(props) => props.$index * 0.05}s;
187
+ animation-fill-mode: both;
188
+
189
+ &:hover {
190
+ border-color: #c4b5fd;
191
+ box-shadow: 0 12px 32px rgba(124, 58, 237, 0.12);
192
+ transform: translateY(-4px);
193
+ }
194
+
195
+ ${(props) => !props.$enabled && `
196
+ opacity: 0.6;
197
+
198
+ &:hover {
199
+ opacity: 0.8;
200
+ }
201
+ `}
202
+ `;
203
+ const CardHeader = styled__default.default.div`
204
+ display: flex;
205
+ align-items: center;
206
+ gap: 16px;
207
+ padding: 20px;
208
+ border-bottom: 1px solid #f1f5f9;
209
+ `;
210
+ const BlockIconWrapper = styled__default.default.div`
211
+ width: 52px;
212
+ height: 52px;
213
+ border-radius: 14px;
214
+ display: flex;
215
+ align-items: center;
216
+ justify-content: center;
217
+ background: ${(props) => props.$type === "embedded-entry" ? "linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%)" : "linear-gradient(135deg, #10b981 0%, #059669 100%)"};
218
+ color: white;
219
+ box-shadow: ${(props) => props.$type === "embedded-entry" ? "0 4px 14px rgba(124, 58, 237, 0.35)" : "0 4px 14px rgba(16, 185, 129, 0.35)"};
220
+ flex-shrink: 0;
221
+
222
+ svg {
223
+ width: 26px;
224
+ height: 26px;
225
+ }
226
+ `;
227
+ const BlockInfo = styled__default.default.div`
228
+ flex: 1;
229
+ min-width: 0;
230
+ `;
231
+ const BlockName = styled__default.default.h3`
232
+ font-size: 1.1rem;
233
+ font-weight: 700;
234
+ color: #0f172a;
235
+ margin: 0 0 4px 0;
236
+ white-space: nowrap;
237
+ overflow: hidden;
238
+ text-overflow: ellipsis;
239
+ `;
240
+ const BlockSlug = styled__default.default.span`
241
+ font-size: 0.8rem;
242
+ color: #94a3b8;
243
+ font-family: 'SF Mono', Monaco, monospace;
244
+ background: #f8fafc;
245
+ padding: 2px 8px;
246
+ border-radius: 6px;
247
+ `;
248
+ const CardBody = styled__default.default.div`
249
+ padding: 20px;
250
+ `;
251
+ const BlockDescription = styled__default.default.p`
252
+ font-size: 0.9rem;
253
+ color: #64748b;
254
+ margin: 0 0 16px 0;
255
+ line-height: 1.5;
256
+ display: -webkit-box;
257
+ -webkit-line-clamp: 2;
258
+ -webkit-box-orient: vertical;
259
+ overflow: hidden;
260
+ `;
261
+ const MetaRow = styled__default.default.div`
262
+ display: flex;
263
+ flex-wrap: wrap;
264
+ gap: 10px;
265
+ margin-bottom: 16px;
266
+ `;
267
+ const MetaTag = styled__default.default.span`
268
+ display: inline-flex;
269
+ align-items: center;
270
+ gap: 6px;
271
+ padding: 6px 12px;
272
+ font-size: 0.8rem;
273
+ font-weight: 500;
274
+ border-radius: 8px;
275
+
276
+ svg {
277
+ width: 14px;
278
+ height: 14px;
279
+ }
280
+
281
+ ${(props) => props.$type === "type" && `
282
+ background: ${props.$value === "embedded-entry" ? "linear-gradient(135deg, #ede9fe 0%, #ddd6fe 100%)" : "linear-gradient(135deg, #d1fae5 0%, #a7f3d0 100%)"};
283
+ color: ${props.$value === "embedded-entry" ? "#6d28d9" : "#047857"};
284
+ `}
285
+
286
+ ${(props) => props.$type === "status" && `
287
+ background: ${props.$enabled ? "#dcfce7" : "#fee2e2"};
288
+ color: ${props.$enabled ? "#166534" : "#991b1b"};
289
+ `}
290
+
291
+ ${(props) => props.$type === "shortcut" && `
292
+ background: #f1f5f9;
293
+ color: #475569;
294
+ font-family: 'SF Mono', Monaco, monospace;
295
+ font-size: 0.75rem;
296
+ `}
297
+
298
+ ${(props) => props.$type === "content-type" && `
299
+ background: #fef3c7;
300
+ color: #92400e;
301
+ `}
302
+ `;
303
+ const CardFooter = styled__default.default.div`
304
+ display: flex;
305
+ justify-content: space-between;
306
+ align-items: center;
307
+ padding: 16px 20px;
308
+ background: #fafbfc;
309
+ border-top: 1px solid #f1f5f9;
310
+ `;
311
+ const ActionGroup = styled__default.default.div`
312
+ display: flex;
313
+ gap: 4px;
314
+ `;
315
+ const ToggleSwitch = styled__default.default.button`
316
+ position: relative;
317
+ width: 48px;
318
+ height: 26px;
319
+ border-radius: 13px;
320
+ border: none;
321
+ cursor: pointer;
322
+ transition: all 0.25s ease;
323
+ background: ${(props) => props.$enabled ? "#10b981" : "#cbd5e1"};
324
+
325
+ &::after {
326
+ content: '';
327
+ position: absolute;
328
+ top: 3px;
329
+ left: ${(props) => props.$enabled ? "25px" : "3px"};
330
+ width: 20px;
331
+ height: 20px;
332
+ border-radius: 50%;
333
+ background: white;
334
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
335
+ transition: all 0.25s ease;
336
+ }
337
+
338
+ &:hover {
339
+ transform: scale(1.05);
340
+ }
341
+ `;
342
+ const EmptyState = styled__default.default.div`
343
+ text-align: center;
344
+ padding: 80px 40px;
345
+ background: white;
346
+ border-radius: 20px;
347
+ border: 2px dashed #e2e8f0;
348
+ `;
349
+ const EmptyIcon = styled__default.default.div`
350
+ width: 80px;
351
+ height: 80px;
352
+ margin: 0 auto 24px;
353
+ background: linear-gradient(135deg, #f5f3ff 0%, #ede9fe 100%);
354
+ border-radius: 20px;
355
+ display: flex;
356
+ align-items: center;
357
+ justify-content: center;
358
+
359
+ svg {
360
+ width: 40px;
361
+ height: 40px;
362
+ color: #7c3aed;
363
+ }
364
+ `;
365
+ const EmptyTitle = styled__default.default.h2`
366
+ font-size: 1.5rem;
367
+ font-weight: 700;
368
+ color: #0f172a;
369
+ margin: 0 0 12px 0;
370
+ `;
371
+ const EmptyText = styled__default.default.p`
372
+ font-size: 1rem;
373
+ color: #64748b;
374
+ margin: 0 0 32px 0;
375
+ max-width: 400px;
376
+ margin-left: auto;
377
+ margin-right: auto;
378
+ line-height: 1.6;
379
+ `;
380
+ const ModalOverlay = styled__default.default.div`
381
+ position: fixed;
382
+ inset: 0;
383
+ background: rgba(15, 23, 42, 0.6);
384
+ backdrop-filter: blur(4px);
385
+ display: flex;
386
+ align-items: center;
387
+ justify-content: center;
388
+ z-index: 10000;
389
+ padding: 20px;
390
+ animation: ${fadeIn} 0.2s ease-out;
391
+ `;
392
+ const ModalContent = styled__default.default.div`
393
+ background: white;
394
+ border-radius: 20px;
395
+ width: 100%;
396
+ max-width: 640px;
397
+ max-height: 90vh;
398
+ overflow: hidden;
399
+ display: flex;
400
+ flex-direction: column;
401
+ box-shadow: 0 25px 50px rgba(0, 0, 0, 0.25);
402
+ animation: ${fadeIn} 0.3s ease-out;
403
+ `;
404
+ const ModalHeader = styled__default.default.div`
405
+ display: flex;
406
+ align-items: center;
407
+ justify-content: space-between;
408
+ padding: 24px;
409
+ border-bottom: 1px solid #f1f5f9;
410
+ `;
411
+ const ModalTitle = styled__default.default.h2`
412
+ font-size: 1.25rem;
413
+ font-weight: 700;
414
+ color: #0f172a;
415
+ margin: 0;
416
+ display: flex;
417
+ align-items: center;
418
+ gap: 12px;
419
+
420
+ svg {
421
+ width: 24px;
422
+ height: 24px;
423
+ color: #7c3aed;
424
+ }
425
+ `;
426
+ const ModalBody = styled__default.default.div`
427
+ padding: 24px;
428
+ overflow-y: auto;
429
+ flex: 1;
430
+ `;
431
+ const ModalFooter = styled__default.default.div`
432
+ display: flex;
433
+ justify-content: flex-end;
434
+ gap: 12px;
435
+ padding: 20px 24px;
436
+ background: #fafbfc;
437
+ border-top: 1px solid #f1f5f9;
438
+ `;
439
+ const FormSection = styled__default.default.div`
440
+ margin-bottom: 28px;
441
+
442
+ &:last-child {
443
+ margin-bottom: 0;
444
+ }
445
+ `;
446
+ const SectionTitle = styled__default.default.h3`
447
+ font-size: 0.85rem;
448
+ font-weight: 700;
449
+ color: #7c3aed;
450
+ text-transform: uppercase;
451
+ letter-spacing: 0.05em;
452
+ margin: 0 0 16px 0;
453
+ display: flex;
454
+ align-items: center;
455
+ gap: 8px;
456
+
457
+ svg {
458
+ width: 16px;
459
+ height: 16px;
460
+ }
461
+ `;
462
+ const FormGrid = styled__default.default.div`
463
+ display: grid;
464
+ grid-template-columns: ${(props) => props.$columns || "1fr"};
465
+ gap: 16px;
466
+ `;
467
+ const FormGroup = styled__default.default.div`
468
+ display: flex;
469
+ flex-direction: column;
470
+ gap: 6px;
471
+ `;
472
+ const Label = styled__default.default.label`
473
+ font-size: 0.875rem;
474
+ font-weight: 600;
475
+ color: #374151;
476
+ display: flex;
477
+ align-items: center;
478
+ gap: 6px;
479
+
480
+ span {
481
+ color: #ef4444;
482
+ }
483
+ `;
484
+ const Input = styled__default.default.input`
485
+ padding: 12px 16px;
486
+ font-size: 0.95rem;
487
+ border: 1px solid #e2e8f0;
488
+ border-radius: 10px;
489
+ background: #fafbfc;
490
+ color: #1e293b;
491
+ transition: all 0.2s ease;
492
+
493
+ &:focus {
494
+ outline: none;
495
+ border-color: #7c3aed;
496
+ background: white;
497
+ box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.1);
498
+ }
499
+
500
+ &::placeholder {
501
+ color: #94a3b8;
502
+ }
503
+
504
+ &:disabled {
505
+ background: #f1f5f9;
506
+ color: #94a3b8;
507
+ cursor: not-allowed;
508
+ }
509
+ `;
510
+ const TextArea = styled__default.default.textarea`
511
+ padding: 12px 16px;
512
+ font-size: 0.95rem;
513
+ border: 1px solid #e2e8f0;
514
+ border-radius: 10px;
515
+ background: #fafbfc;
516
+ color: #1e293b;
517
+ resize: vertical;
518
+ min-height: ${(props) => props.$rows ? `${props.$rows * 24 + 24}px` : "100px"};
519
+ font-family: ${(props) => props.$mono ? "'SF Mono', Monaco, monospace" : "inherit"};
520
+ transition: all 0.2s ease;
521
+
522
+ &:focus {
523
+ outline: none;
524
+ border-color: #7c3aed;
525
+ background: white;
526
+ box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.1);
527
+ }
528
+
529
+ &::placeholder {
530
+ color: #94a3b8;
531
+ }
532
+ `;
533
+ const Select = styled__default.default.select`
534
+ padding: 12px 16px;
535
+ font-size: 0.95rem;
536
+ border: 1px solid #e2e8f0;
537
+ border-radius: 10px;
538
+ background: #fafbfc;
539
+ color: #1e293b;
540
+ cursor: pointer;
541
+ transition: all 0.2s ease;
542
+ appearance: none;
543
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%2394a3b8'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M19 9l-7 7-7-7'%3E%3C/path%3E%3C/svg%3E");
544
+ background-repeat: no-repeat;
545
+ background-position: right 12px center;
546
+ background-size: 18px;
547
+ padding-right: 40px;
548
+
549
+ &:focus {
550
+ outline: none;
551
+ border-color: #7c3aed;
552
+ background-color: white;
553
+ box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.1);
554
+ }
555
+ `;
556
+ const Hint = styled__default.default.span`
557
+ font-size: 0.8rem;
558
+ color: #94a3b8;
559
+ `;
560
+ const CheckboxRow = styled__default.default.label`
561
+ display: flex;
562
+ align-items: center;
563
+ gap: 12px;
564
+ padding: 14px 16px;
565
+ background: #fafbfc;
566
+ border: 1px solid #e2e8f0;
567
+ border-radius: 10px;
568
+ cursor: pointer;
569
+ transition: all 0.2s ease;
570
+
571
+ &:hover {
572
+ background: #f1f5f9;
573
+ border-color: #cbd5e1;
574
+ }
575
+ `;
576
+ const Checkbox = styled__default.default.input.attrs({ type: "checkbox" })`
577
+ width: 20px;
578
+ height: 20px;
579
+ border-radius: 6px;
580
+ accent-color: #7c3aed;
581
+ cursor: pointer;
582
+ `;
583
+ const CheckboxLabel = styled__default.default.span`
584
+ font-size: 0.95rem;
585
+ color: #374151;
586
+ font-weight: 500;
587
+ `;
588
+ const FieldsContainer = styled__default.default.div`
589
+ background: #f8fafc;
590
+ border-radius: 12px;
591
+ padding: 16px;
592
+ margin-top: 8px;
593
+ `;
594
+ const FieldRow = styled__default.default.div`
595
+ display: grid;
596
+ grid-template-columns: 1fr 1fr 120px 40px;
597
+ gap: 12px;
598
+ padding: 12px;
599
+ background: white;
600
+ border: 1px solid #e2e8f0;
601
+ border-radius: 10px;
602
+ margin-bottom: 10px;
603
+ align-items: center;
604
+
605
+ &:last-child {
606
+ margin-bottom: 0;
607
+ }
608
+ `;
609
+ const FieldInput = styled__default.default(Input)`
610
+ padding: 10px 12px;
611
+ font-size: 0.875rem;
612
+ `;
613
+ const FieldSelect = styled__default.default(Select)`
614
+ padding: 10px 12px;
615
+ font-size: 0.875rem;
616
+ `;
617
+ const AddFieldButton = styled__default.default.button`
618
+ display: flex;
619
+ align-items: center;
620
+ justify-content: center;
621
+ gap: 8px;
622
+ width: 100%;
623
+ padding: 12px;
624
+ background: transparent;
625
+ border: 2px dashed #cbd5e1;
626
+ border-radius: 10px;
627
+ color: #64748b;
628
+ font-size: 0.875rem;
629
+ font-weight: 600;
630
+ cursor: pointer;
631
+ transition: all 0.2s ease;
632
+ margin-top: 12px;
633
+
634
+ svg {
635
+ width: 18px;
636
+ height: 18px;
637
+ }
638
+
639
+ &:hover {
640
+ border-color: #7c3aed;
641
+ color: #7c3aed;
642
+ background: rgba(124, 58, 237, 0.04);
643
+ }
644
+ `;
645
+ const LoadingWrapper = styled__default.default.div`
646
+ display: flex;
647
+ flex-direction: column;
648
+ align-items: center;
649
+ justify-content: center;
650
+ padding: 80px 40px;
651
+ `;
652
+ const LoadingSpinner = styled__default.default.div`
653
+ width: 48px;
654
+ height: 48px;
655
+ border: 4px solid #e2e8f0;
656
+ border-top-color: #7c3aed;
657
+ border-radius: 50%;
658
+ animation: spin 0.8s linear infinite;
659
+ margin-bottom: 16px;
660
+
661
+ @keyframes spin {
662
+ to { transform: rotate(360deg); }
663
+ }
664
+ `;
665
+ const LoadingText = styled__default.default.p`
666
+ font-size: 1rem;
667
+ color: #64748b;
668
+ `;
669
+ const LimitsBanner = styled__default.default.div`
670
+ display: flex;
671
+ align-items: center;
672
+ gap: 16px;
673
+ padding: 16px 20px;
674
+ background: linear-gradient(135deg, #f5f3ff 0%, #ede9fe 100%);
675
+ border: 1px solid #c4b5fd;
676
+ border-radius: 14px;
677
+ margin-bottom: 24px;
678
+ `;
679
+ const LimitsInfo = styled__default.default.div`
680
+ flex: 1;
681
+ `;
682
+ const LimitsText = styled__default.default.div`
683
+ font-size: 0.9rem;
684
+ color: #5b21b6;
685
+ font-weight: 500;
686
+
687
+ strong {
688
+ font-weight: 700;
689
+ }
690
+ `;
691
+ const LimitsProgress = styled__default.default.div`
692
+ display: flex;
693
+ align-items: center;
694
+ gap: 12px;
695
+ margin-top: 8px;
696
+ `;
697
+ const ProgressBar = styled__default.default.div`
698
+ flex: 1;
699
+ max-width: 200px;
700
+ height: 8px;
701
+ background: #ddd6fe;
702
+ border-radius: 4px;
703
+ overflow: hidden;
704
+ `;
705
+ const ProgressFill = styled__default.default.div`
706
+ height: 100%;
707
+ background: ${(props) => props.$percentage >= 100 ? "#ef4444" : props.$percentage >= 80 ? "#f59e0b" : "#7c3aed"};
708
+ border-radius: 4px;
709
+ width: ${(props) => Math.min(props.$percentage, 100)}%;
710
+ transition: width 0.3s ease;
711
+ `;
712
+ const ProgressLabel = styled__default.default.span`
713
+ font-size: 0.8rem;
714
+ color: #6d28d9;
715
+ font-weight: 600;
716
+ `;
717
+ const TierBadge = styled__default.default.span`
718
+ display: inline-flex;
719
+ align-items: center;
720
+ gap: 6px;
721
+ padding: 6px 14px;
722
+ font-size: 0.75rem;
723
+ font-weight: 700;
724
+ text-transform: uppercase;
725
+ letter-spacing: 0.05em;
726
+ border-radius: 20px;
727
+ background: ${(props) => {
728
+ switch (props.$tier) {
729
+ case "enterprise":
730
+ return "linear-gradient(135deg, #1e3a5f 0%, #0f172a 100%)";
731
+ case "advanced":
732
+ return "linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%)";
733
+ case "premium":
734
+ return "linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%)";
735
+ default:
736
+ return "linear-gradient(135deg, #6b7280 0%, #4b5563 100%)";
737
+ }
738
+ }};
739
+ color: white;
740
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
741
+ `;
742
+ const UpgradeButton = styled__default.default.button`
743
+ display: inline-flex;
744
+ align-items: center;
745
+ gap: 6px;
746
+ padding: 8px 16px;
747
+ font-size: 0.85rem;
748
+ font-weight: 600;
749
+ border-radius: 8px;
750
+ border: none;
751
+ background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%);
752
+ color: white;
753
+ cursor: pointer;
754
+ transition: all 0.2s ease;
755
+
756
+ &:hover {
757
+ transform: translateY(-1px);
758
+ box-shadow: 0 4px 12px rgba(124, 58, 237, 0.35);
759
+ }
760
+
761
+ svg {
762
+ width: 16px;
763
+ height: 16px;
764
+ }
765
+ `;
766
+ styled__default.default.div`
767
+ display: flex;
768
+ align-items: center;
769
+ gap: 8px;
770
+ padding: 12px 16px;
771
+ background: #fef3c7;
772
+ border: 1px solid #fcd34d;
773
+ border-radius: 10px;
774
+ margin-top: 16px;
775
+ font-size: 0.85rem;
776
+ color: #92400e;
777
+
778
+ svg {
779
+ width: 18px;
780
+ height: 18px;
781
+ flex-shrink: 0;
782
+ }
783
+ `;
784
+ const FeatureLockedBadge = styled__default.default.span`
785
+ display: inline-flex;
786
+ align-items: center;
787
+ gap: 4px;
788
+ padding: 4px 8px;
789
+ font-size: 0.7rem;
790
+ font-weight: 600;
791
+ text-transform: uppercase;
792
+ border-radius: 4px;
793
+ background: #fee2e2;
794
+ color: #991b1b;
795
+ margin-left: 8px;
796
+
797
+ svg {
798
+ width: 12px;
799
+ height: 12px;
800
+ }
801
+ `;
802
+ const CustomBlocksPage = () => {
803
+ const { toggleNotification } = admin.useNotification();
804
+ const { get, post, put, del } = admin.useFetchClient();
805
+ const [blocks, setBlocks] = React.useState([]);
806
+ const [contentTypes, setContentTypes] = React.useState([]);
807
+ const [isLoading, setIsLoading] = React.useState(true);
808
+ const [isModalOpen, setIsModalOpen] = React.useState(false);
809
+ const [editingBlock, setEditingBlock] = React.useState(null);
810
+ const [formData, setFormData] = React.useState(getEmptyFormData());
811
+ const [limits, setLimits] = React.useState(null);
812
+ function getEmptyFormData() {
813
+ return {
814
+ name: "",
815
+ label: "",
816
+ blockType: "simple",
817
+ description: "",
818
+ icon: "",
819
+ contentType: "",
820
+ displayFields: ["title", "name"],
821
+ titleField: "title",
822
+ previewFields: [],
823
+ fields: [],
824
+ template: "",
825
+ placeholder: "Enter content...",
826
+ styles: {},
827
+ inlineToolbar: true,
828
+ tunes: [],
829
+ shortcut: "",
830
+ sortOrder: 0,
831
+ enabled: true
832
+ };
833
+ }
834
+ const fetchBlocks = React.useCallback(async () => {
835
+ try {
836
+ setIsLoading(true);
837
+ const response = await get(`/${PLUGIN_ID}/custom-blocks?enabledOnly=false`);
838
+ setBlocks(response.data?.data || []);
839
+ } catch (error) {
840
+ console.error("[CustomBlocksPage] Error fetching blocks:", error);
841
+ toggleNotification({
842
+ type: "danger",
843
+ message: "Failed to load custom blocks"
844
+ });
845
+ } finally {
846
+ setIsLoading(false);
847
+ }
848
+ }, [get, toggleNotification]);
849
+ const fetchContentTypes = React.useCallback(async () => {
850
+ try {
851
+ const response = await get(`/${PLUGIN_ID}/content-types`);
852
+ setContentTypes(response.data?.contentTypes || []);
853
+ } catch (error) {
854
+ console.error("[CustomBlocksPage] Error fetching content types:", error);
855
+ }
856
+ }, [get]);
857
+ const fetchLimits = React.useCallback(async () => {
858
+ try {
859
+ const response = await get(`/${PLUGIN_ID}/custom-blocks/limits`);
860
+ setLimits(response.data?.data || null);
861
+ } catch (error) {
862
+ console.error("[CustomBlocksPage] Error fetching limits:", error);
863
+ }
864
+ }, [get]);
865
+ React.useEffect(() => {
866
+ fetchBlocks();
867
+ fetchContentTypes();
868
+ fetchLimits();
869
+ }, [fetchBlocks, fetchContentTypes, fetchLimits]);
870
+ const getUsagePercentage = () => {
871
+ if (!limits || limits.limits.maxTotal === -1) return 0;
872
+ return Math.round(limits.usage.total / limits.limits.maxTotal * 100);
873
+ };
874
+ const canCreateMoreBlocks = () => {
875
+ if (!limits) return true;
876
+ return limits.canCreateSimple || limits.canCreateEmbedded;
877
+ };
878
+ const canCreateEmbeddedEntry = () => {
879
+ if (!limits) return true;
880
+ return limits.canCreateEmbedded;
881
+ };
882
+ const canExportImport = () => {
883
+ if (!limits) return true;
884
+ return limits.limits.exportImport === true;
885
+ };
886
+ const handleCreate = () => {
887
+ if (!canCreateMoreBlocks()) {
888
+ toggleNotification({
889
+ type: "warning",
890
+ message: `Block limit reached (${limits?.usage?.total}/${limits?.limits?.maxTotal}). Upgrade to create more.`
891
+ });
892
+ return;
893
+ }
894
+ setEditingBlock(null);
895
+ setFormData(getEmptyFormData());
896
+ setIsModalOpen(true);
897
+ };
898
+ const handleEdit = (block) => {
899
+ setEditingBlock(block);
900
+ setFormData({
901
+ name: block.name || "",
902
+ label: block.label || "",
903
+ blockType: block.blockType || "simple",
904
+ description: block.description || "",
905
+ icon: block.icon || "",
906
+ contentType: block.contentType || "",
907
+ displayFields: block.displayFields || ["title", "name"],
908
+ titleField: block.titleField || "title",
909
+ previewFields: block.previewFields || [],
910
+ fields: block.fields || [],
911
+ template: block.template || "",
912
+ placeholder: block.placeholder || "Enter content...",
913
+ styles: block.styles || {},
914
+ inlineToolbar: block.inlineToolbar !== false,
915
+ tunes: block.tunes || [],
916
+ shortcut: block.shortcut || "",
917
+ sortOrder: block.sortOrder || 0,
918
+ enabled: block.enabled !== false
919
+ });
920
+ setIsModalOpen(true);
921
+ };
922
+ const handleSave = async () => {
923
+ try {
924
+ if (!formData.name) {
925
+ toggleNotification({ type: "warning", message: "Block name is required" });
926
+ return;
927
+ }
928
+ if (!formData.label) {
929
+ toggleNotification({ type: "warning", message: "Display label is required" });
930
+ return;
931
+ }
932
+ if (editingBlock) {
933
+ await put(`/${PLUGIN_ID}/custom-blocks/${editingBlock.documentId}`, formData);
934
+ toggleNotification({ type: "success", message: "Block updated successfully" });
935
+ } else {
936
+ await post(`/${PLUGIN_ID}/custom-blocks`, formData);
937
+ toggleNotification({ type: "success", message: "Block created successfully" });
938
+ }
939
+ setIsModalOpen(false);
940
+ fetchBlocks();
941
+ fetchLimits();
942
+ } catch (error) {
943
+ console.error("[CustomBlocksPage] Error saving block:", error);
944
+ const errorDetails = error.response?.data?.error?.details;
945
+ if (errorDetails?.code === "LIMIT_EXCEEDED") {
946
+ toggleNotification({
947
+ type: "warning",
948
+ message: error.response?.data?.error?.message || "Block limit reached. Upgrade to create more."
949
+ });
950
+ return;
951
+ }
952
+ toggleNotification({
953
+ type: "danger",
954
+ message: error.response?.data?.error?.message || "Failed to save block"
955
+ });
956
+ }
957
+ };
958
+ const handleDelete = async (block) => {
959
+ if (!window.confirm(`Delete block "${block.label || block.name}"? This cannot be undone.`)) {
960
+ return;
961
+ }
962
+ try {
963
+ await del(`/${PLUGIN_ID}/custom-blocks/${block.documentId}`);
964
+ toggleNotification({ type: "success", message: "Block deleted successfully" });
965
+ fetchBlocks();
966
+ fetchLimits();
967
+ } catch (error) {
968
+ console.error("[CustomBlocksPage] Error deleting block:", error);
969
+ toggleNotification({ type: "danger", message: "Failed to delete block" });
970
+ }
971
+ };
972
+ const handleToggle = async (block) => {
973
+ try {
974
+ await post(`/${PLUGIN_ID}/custom-blocks/${block.documentId}/toggle`);
975
+ fetchBlocks();
976
+ } catch (error) {
977
+ console.error("[CustomBlocksPage] Error toggling block:", error);
978
+ toggleNotification({ type: "danger", message: "Failed to toggle block" });
979
+ }
980
+ };
981
+ const handleDuplicate = async (block) => {
982
+ if (!canCreateMoreBlocks()) {
983
+ toggleNotification({
984
+ type: "warning",
985
+ message: `Block limit reached (${limits?.usage?.total}/${limits?.limits?.maxTotal}). Upgrade to duplicate.`
986
+ });
987
+ return;
988
+ }
989
+ const newName = prompt("Enter name for duplicated block:", `${block.name}_copy`);
990
+ if (!newName) return;
991
+ try {
992
+ await post(`/${PLUGIN_ID}/custom-blocks/${block.documentId}/duplicate`, { newName });
993
+ toggleNotification({ type: "success", message: "Block duplicated successfully" });
994
+ fetchBlocks();
995
+ fetchLimits();
996
+ } catch (error) {
997
+ console.error("[CustomBlocksPage] Error duplicating block:", error);
998
+ const errorDetails = error.response?.data?.error?.details;
999
+ if (errorDetails?.code === "LIMIT_EXCEEDED") {
1000
+ toggleNotification({
1001
+ type: "warning",
1002
+ message: error.response?.data?.error?.message || "Block limit reached. Upgrade to duplicate."
1003
+ });
1004
+ return;
1005
+ }
1006
+ toggleNotification({
1007
+ type: "danger",
1008
+ message: error.response?.data?.error?.message || "Failed to duplicate block"
1009
+ });
1010
+ }
1011
+ };
1012
+ const handleExport = async () => {
1013
+ if (!canExportImport()) {
1014
+ toggleNotification({
1015
+ type: "warning",
1016
+ message: "Export requires Advanced or Enterprise tier."
1017
+ });
1018
+ return;
1019
+ }
1020
+ try {
1021
+ const response = await get(`/${PLUGIN_ID}/custom-blocks/export`);
1022
+ const blob = new Blob([JSON.stringify(response.data, null, 2)], {
1023
+ type: "application/json"
1024
+ });
1025
+ const url = URL.createObjectURL(blob);
1026
+ const a = document.createElement("a");
1027
+ a.href = url;
1028
+ a.download = "custom-blocks.json";
1029
+ a.click();
1030
+ URL.revokeObjectURL(url);
1031
+ toggleNotification({ type: "success", message: "Blocks exported successfully" });
1032
+ } catch (error) {
1033
+ console.error("[CustomBlocksPage] Error exporting blocks:", error);
1034
+ if (error.response?.data?.error?.details?.code === "FEATURE_NOT_AVAILABLE") {
1035
+ toggleNotification({
1036
+ type: "warning",
1037
+ message: "Export requires Advanced or Enterprise tier."
1038
+ });
1039
+ return;
1040
+ }
1041
+ toggleNotification({ type: "danger", message: "Failed to export blocks" });
1042
+ }
1043
+ };
1044
+ const handleImport = () => {
1045
+ if (!canExportImport()) {
1046
+ toggleNotification({
1047
+ type: "warning",
1048
+ message: "Import requires Advanced or Enterprise tier."
1049
+ });
1050
+ return;
1051
+ }
1052
+ const input = document.createElement("input");
1053
+ input.type = "file";
1054
+ input.accept = ".json";
1055
+ input.onchange = async (e) => {
1056
+ const file = e.target.files[0];
1057
+ if (!file) return;
1058
+ try {
1059
+ const text = await file.text();
1060
+ const data = JSON.parse(text);
1061
+ const overwrite = window.confirm("Overwrite existing blocks with same name?");
1062
+ await post(`/${PLUGIN_ID}/custom-blocks/import?overwrite=${overwrite}`, data);
1063
+ toggleNotification({ type: "success", message: "Blocks imported successfully" });
1064
+ fetchBlocks();
1065
+ fetchLimits();
1066
+ } catch (error) {
1067
+ console.error("[CustomBlocksPage] Error importing blocks:", error);
1068
+ if (error.response?.data?.error?.details?.code === "FEATURE_NOT_AVAILABLE") {
1069
+ toggleNotification({
1070
+ type: "warning",
1071
+ message: "Import requires Advanced or Enterprise tier."
1072
+ });
1073
+ return;
1074
+ }
1075
+ toggleNotification({ type: "danger", message: "Failed to import blocks" });
1076
+ }
1077
+ };
1078
+ input.click();
1079
+ };
1080
+ const updateFormField = (field, value) => {
1081
+ setFormData((prev) => ({ ...prev, [field]: value }));
1082
+ };
1083
+ const addField = () => {
1084
+ const newField = {
1085
+ name: `field_${formData.fields.length + 1}`,
1086
+ label: `Field ${formData.fields.length + 1}`,
1087
+ type: "text",
1088
+ default: "",
1089
+ required: false
1090
+ };
1091
+ updateFormField("fields", [...formData.fields, newField]);
1092
+ };
1093
+ const updateField = (index, key, value) => {
1094
+ const newFields = [...formData.fields];
1095
+ newFields[index] = { ...newFields[index], [key]: value };
1096
+ updateFormField("fields", newFields);
1097
+ };
1098
+ const removeField = (index) => {
1099
+ updateFormField(
1100
+ "fields",
1101
+ formData.fields.filter((_, i) => i !== index)
1102
+ );
1103
+ };
1104
+ if (isLoading) {
1105
+ return /* @__PURE__ */ jsxRuntime.jsx(PageWrapper, { children: /* @__PURE__ */ jsxRuntime.jsx(Container, { children: /* @__PURE__ */ jsxRuntime.jsxs(LoadingWrapper, { children: [
1106
+ /* @__PURE__ */ jsxRuntime.jsx(LoadingSpinner, {}),
1107
+ /* @__PURE__ */ jsxRuntime.jsx(LoadingText, { children: "Loading custom blocks..." })
1108
+ ] }) }) });
1109
+ }
1110
+ return /* @__PURE__ */ jsxRuntime.jsx(PageWrapper, { children: /* @__PURE__ */ jsxRuntime.jsxs(Container, { children: [
1111
+ /* @__PURE__ */ jsxRuntime.jsxs(Header, { children: [
1112
+ /* @__PURE__ */ jsxRuntime.jsxs(HeaderLeft, { children: [
1113
+ /* @__PURE__ */ jsxRuntime.jsxs(PageTitle, { children: [
1114
+ /* @__PURE__ */ jsxRuntime.jsx(outline.Squares2X2Icon, {}),
1115
+ "Custom Blocks"
1116
+ ] }),
1117
+ /* @__PURE__ */ jsxRuntime.jsx(PageSubtitle, { children: "Create and manage custom editor blocks to extend Magic Editor X with your own content types." })
1118
+ ] }),
1119
+ /* @__PURE__ */ jsxRuntime.jsxs(HeaderRight, { children: [
1120
+ canExportImport() && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1121
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { $variant: "secondary", onClick: handleExport, disabled: blocks.length === 0, children: [
1122
+ /* @__PURE__ */ jsxRuntime.jsx(outline.ArrowDownTrayIcon, {}),
1123
+ "Export"
1124
+ ] }),
1125
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { $variant: "secondary", onClick: handleImport, children: [
1126
+ /* @__PURE__ */ jsxRuntime.jsx(outline.ArrowUpTrayIcon, {}),
1127
+ "Import"
1128
+ ] })
1129
+ ] }),
1130
+ /* @__PURE__ */ jsxRuntime.jsxs(
1131
+ Button,
1132
+ {
1133
+ $variant: "primary",
1134
+ onClick: handleCreate,
1135
+ disabled: !canCreateMoreBlocks(),
1136
+ title: !canCreateMoreBlocks() ? "Block limit reached. Upgrade to create more." : "",
1137
+ children: [
1138
+ /* @__PURE__ */ jsxRuntime.jsx(outline.PlusIcon, {}),
1139
+ "Create Block"
1140
+ ]
1141
+ }
1142
+ )
1143
+ ] })
1144
+ ] }),
1145
+ limits && limits.limits.maxTotal !== -1 && /* @__PURE__ */ jsxRuntime.jsxs(LimitsBanner, { children: [
1146
+ /* @__PURE__ */ jsxRuntime.jsxs(LimitsInfo, { children: [
1147
+ /* @__PURE__ */ jsxRuntime.jsxs(LimitsText, { children: [
1148
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Custom Blocks Usage" }),
1149
+ " - ",
1150
+ limits.usage.total,
1151
+ " of ",
1152
+ limits.limits.maxTotal,
1153
+ " blocks used",
1154
+ limits.tier === "free" && limits.limits.maxEmbedded === 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { marginLeft: 8, opacity: 0.8 }, children: "(Simple blocks only)" })
1155
+ ] }),
1156
+ /* @__PURE__ */ jsxRuntime.jsxs(LimitsProgress, { children: [
1157
+ /* @__PURE__ */ jsxRuntime.jsx(ProgressBar, { children: /* @__PURE__ */ jsxRuntime.jsx(ProgressFill, { $percentage: getUsagePercentage() }) }),
1158
+ /* @__PURE__ */ jsxRuntime.jsxs(ProgressLabel, { children: [
1159
+ getUsagePercentage(),
1160
+ "%"
1161
+ ] })
1162
+ ] })
1163
+ ] }),
1164
+ /* @__PURE__ */ jsxRuntime.jsx(TierBadge, { $tier: limits.tier, children: limits.tier }),
1165
+ getUsagePercentage() >= 80 && /* @__PURE__ */ jsxRuntime.jsxs(UpgradeButton, { onClick: () => window.location.href = "/admin/settings/magic-editor-x/license", children: [
1166
+ /* @__PURE__ */ jsxRuntime.jsx(outline.SparklesIcon, {}),
1167
+ "Upgrade"
1168
+ ] })
1169
+ ] }),
1170
+ blocks.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs(EmptyState, { children: [
1171
+ /* @__PURE__ */ jsxRuntime.jsx(EmptyIcon, { children: /* @__PURE__ */ jsxRuntime.jsx(outline.CubeIcon, {}) }),
1172
+ /* @__PURE__ */ jsxRuntime.jsx(EmptyTitle, { children: "No Custom Blocks Yet" }),
1173
+ /* @__PURE__ */ jsxRuntime.jsxs(EmptyText, { children: [
1174
+ "Custom blocks allow you to extend the editor with your own content types, templates",
1175
+ limits?.tier !== "free" && ", and embedded Strapi entries",
1176
+ "."
1177
+ ] }),
1178
+ limits && limits.tier === "free" && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 16, padding: "12px 16px", background: "#fef3c7", borderRadius: 8, fontSize: "0.85rem", color: "#92400e" }, children: [
1179
+ "Free tier: Up to ",
1180
+ limits.limits.maxTotal,
1181
+ " simple blocks. Upgrade for embedded entries."
1182
+ ] }),
1183
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { $variant: "primary", onClick: handleCreate, children: [
1184
+ /* @__PURE__ */ jsxRuntime.jsx(outline.PlusIcon, {}),
1185
+ "Create Your First Block"
1186
+ ] })
1187
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(BlocksGrid, { children: blocks.map((block, index) => /* @__PURE__ */ jsxRuntime.jsxs(BlockCard, { $index: index, $enabled: block.enabled, children: [
1188
+ /* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { children: [
1189
+ /* @__PURE__ */ jsxRuntime.jsx(BlockIconWrapper, { $type: block.blockType, children: block.blockType === "embedded-entry" ? /* @__PURE__ */ jsxRuntime.jsx(outline.LinkIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(solid.CubeIcon, {}) }),
1190
+ /* @__PURE__ */ jsxRuntime.jsxs(BlockInfo, { children: [
1191
+ /* @__PURE__ */ jsxRuntime.jsx(BlockName, { children: block.label || block.name }),
1192
+ /* @__PURE__ */ jsxRuntime.jsx(BlockSlug, { children: block.name })
1193
+ ] })
1194
+ ] }),
1195
+ /* @__PURE__ */ jsxRuntime.jsxs(CardBody, { children: [
1196
+ /* @__PURE__ */ jsxRuntime.jsx(BlockDescription, { children: block.description || "No description provided." }),
1197
+ /* @__PURE__ */ jsxRuntime.jsxs(MetaRow, { children: [
1198
+ /* @__PURE__ */ jsxRuntime.jsx(MetaTag, { $type: "type", $value: block.blockType, children: block.blockType === "embedded-entry" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1199
+ /* @__PURE__ */ jsxRuntime.jsx(outline.LinkIcon, {}),
1200
+ "Embedded Entry"
1201
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1202
+ /* @__PURE__ */ jsxRuntime.jsx(outline.CodeBracketIcon, {}),
1203
+ "Simple Block"
1204
+ ] }) }),
1205
+ /* @__PURE__ */ jsxRuntime.jsx(MetaTag, { $type: "status", $enabled: block.enabled, children: block.enabled ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1206
+ /* @__PURE__ */ jsxRuntime.jsx(outline.CheckCircleIcon, {}),
1207
+ "Enabled"
1208
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1209
+ /* @__PURE__ */ jsxRuntime.jsx(outline.XCircleIcon, {}),
1210
+ "Disabled"
1211
+ ] }) }),
1212
+ block.shortcut && /* @__PURE__ */ jsxRuntime.jsxs(MetaTag, { $type: "shortcut", children: [
1213
+ /* @__PURE__ */ jsxRuntime.jsx(outline.CommandLineIcon, {}),
1214
+ block.shortcut
1215
+ ] }),
1216
+ block.contentType && /* @__PURE__ */ jsxRuntime.jsx(MetaTag, { $type: "content-type", children: block.contentType.split("::")[1]?.split(".")[0] || block.contentType })
1217
+ ] })
1218
+ ] }),
1219
+ /* @__PURE__ */ jsxRuntime.jsxs(CardFooter, { children: [
1220
+ /* @__PURE__ */ jsxRuntime.jsx(
1221
+ ToggleSwitch,
1222
+ {
1223
+ $enabled: block.enabled,
1224
+ onClick: () => handleToggle(block),
1225
+ title: block.enabled ? "Disable block" : "Enable block"
1226
+ }
1227
+ ),
1228
+ /* @__PURE__ */ jsxRuntime.jsxs(ActionGroup, { children: [
1229
+ /* @__PURE__ */ jsxRuntime.jsx(IconButton, { onClick: () => handleEdit(block), title: "Edit block", children: /* @__PURE__ */ jsxRuntime.jsx(outline.PencilSquareIcon, {}) }),
1230
+ /* @__PURE__ */ jsxRuntime.jsx(IconButton, { onClick: () => handleDuplicate(block), title: "Duplicate block", children: /* @__PURE__ */ jsxRuntime.jsx(outline.DocumentDuplicateIcon, {}) }),
1231
+ /* @__PURE__ */ jsxRuntime.jsx(IconButton, { $danger: true, onClick: () => handleDelete(block), title: "Delete block", children: /* @__PURE__ */ jsxRuntime.jsx(outline.TrashIcon, {}) })
1232
+ ] })
1233
+ ] })
1234
+ ] }, block.documentId)) }),
1235
+ isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ModalOverlay, { onClick: () => setIsModalOpen(false), children: /* @__PURE__ */ jsxRuntime.jsxs(ModalContent, { onClick: (e) => e.stopPropagation(), children: [
1236
+ /* @__PURE__ */ jsxRuntime.jsxs(ModalHeader, { children: [
1237
+ /* @__PURE__ */ jsxRuntime.jsxs(ModalTitle, { children: [
1238
+ editingBlock ? /* @__PURE__ */ jsxRuntime.jsx(outline.PencilSquareIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(solid.SparklesIcon, {}),
1239
+ editingBlock ? "Edit Block" : "Create Custom Block"
1240
+ ] }),
1241
+ /* @__PURE__ */ jsxRuntime.jsx(IconButton, { onClick: () => setIsModalOpen(false), children: /* @__PURE__ */ jsxRuntime.jsx(outline.XMarkIcon, {}) })
1242
+ ] }),
1243
+ /* @__PURE__ */ jsxRuntime.jsxs(ModalBody, { children: [
1244
+ /* @__PURE__ */ jsxRuntime.jsxs(FormSection, { children: [
1245
+ /* @__PURE__ */ jsxRuntime.jsxs(SectionTitle, { children: [
1246
+ /* @__PURE__ */ jsxRuntime.jsx(outline.CubeIcon, {}),
1247
+ "Basic Information"
1248
+ ] }),
1249
+ /* @__PURE__ */ jsxRuntime.jsxs(FormGrid, { $columns: "1fr 1fr", children: [
1250
+ /* @__PURE__ */ jsxRuntime.jsxs(FormGroup, { children: [
1251
+ /* @__PURE__ */ jsxRuntime.jsxs(Label, { children: [
1252
+ "Block Name ",
1253
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "*" })
1254
+ ] }),
1255
+ /* @__PURE__ */ jsxRuntime.jsx(
1256
+ Input,
1257
+ {
1258
+ value: formData.name,
1259
+ onChange: (e) => updateFormField("name", e.target.value),
1260
+ placeholder: "myCustomBlock",
1261
+ disabled: !!editingBlock
1262
+ }
1263
+ ),
1264
+ /* @__PURE__ */ jsxRuntime.jsx(Hint, { children: "Unique identifier (letters, numbers, underscores)" })
1265
+ ] }),
1266
+ /* @__PURE__ */ jsxRuntime.jsxs(FormGroup, { children: [
1267
+ /* @__PURE__ */ jsxRuntime.jsxs(Label, { children: [
1268
+ "Display Label ",
1269
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "*" })
1270
+ ] }),
1271
+ /* @__PURE__ */ jsxRuntime.jsx(
1272
+ Input,
1273
+ {
1274
+ value: formData.label,
1275
+ onChange: (e) => updateFormField("label", e.target.value),
1276
+ placeholder: "My Custom Block"
1277
+ }
1278
+ ),
1279
+ /* @__PURE__ */ jsxRuntime.jsx(Hint, { children: "Shown in the editor toolbox" })
1280
+ ] })
1281
+ ] }),
1282
+ /* @__PURE__ */ jsxRuntime.jsxs(FormGroup, { style: { marginTop: 16 }, children: [
1283
+ /* @__PURE__ */ jsxRuntime.jsxs(Label, { children: [
1284
+ "Block Type ",
1285
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "*" }),
1286
+ !canCreateEmbeddedEntry() && formData.blockType !== "embedded-entry" && /* @__PURE__ */ jsxRuntime.jsxs(FeatureLockedBadge, { children: [
1287
+ /* @__PURE__ */ jsxRuntime.jsx(outline.XCircleIcon, {}),
1288
+ "Premium+"
1289
+ ] })
1290
+ ] }),
1291
+ /* @__PURE__ */ jsxRuntime.jsxs(
1292
+ Select,
1293
+ {
1294
+ value: formData.blockType,
1295
+ onChange: (e) => {
1296
+ if (e.target.value === "embedded-entry" && !canCreateEmbeddedEntry()) {
1297
+ toggleNotification({
1298
+ type: "warning",
1299
+ message: "Embedded Entry blocks require Premium or higher tier."
1300
+ });
1301
+ return;
1302
+ }
1303
+ updateFormField("blockType", e.target.value);
1304
+ },
1305
+ children: [
1306
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "simple", children: "Simple Block (Text/HTML)" }),
1307
+ /* @__PURE__ */ jsxRuntime.jsxs("option", { value: "embedded-entry", disabled: !canCreateEmbeddedEntry() && !editingBlock, children: [
1308
+ "Embedded Entry (Strapi Content) ",
1309
+ !canCreateEmbeddedEntry() ? "(Premium+)" : ""
1310
+ ] })
1311
+ ]
1312
+ }
1313
+ ),
1314
+ !canCreateEmbeddedEntry() && /* @__PURE__ */ jsxRuntime.jsx(Hint, { style: { color: "#f59e0b" }, children: "Embedded Entry blocks require Premium or higher tier." })
1315
+ ] }),
1316
+ /* @__PURE__ */ jsxRuntime.jsxs(FormGroup, { style: { marginTop: 16 }, children: [
1317
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Description" }),
1318
+ /* @__PURE__ */ jsxRuntime.jsx(
1319
+ TextArea,
1320
+ {
1321
+ value: formData.description,
1322
+ onChange: (e) => updateFormField("description", e.target.value),
1323
+ placeholder: "Describe what this block does...",
1324
+ $rows: 2
1325
+ }
1326
+ )
1327
+ ] })
1328
+ ] }),
1329
+ formData.blockType === "embedded-entry" && /* @__PURE__ */ jsxRuntime.jsxs(FormSection, { children: [
1330
+ /* @__PURE__ */ jsxRuntime.jsxs(SectionTitle, { children: [
1331
+ /* @__PURE__ */ jsxRuntime.jsx(outline.LinkIcon, {}),
1332
+ "Embedded Entry Settings"
1333
+ ] }),
1334
+ /* @__PURE__ */ jsxRuntime.jsxs(FormGroup, { children: [
1335
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Content Type" }),
1336
+ /* @__PURE__ */ jsxRuntime.jsxs(
1337
+ Select,
1338
+ {
1339
+ value: formData.contentType,
1340
+ onChange: (e) => updateFormField("contentType", e.target.value),
1341
+ children: [
1342
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "Any Content Type" }),
1343
+ contentTypes.map((ct) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: ct.uid, children: [
1344
+ ct.displayName,
1345
+ " (",
1346
+ ct.uid,
1347
+ ")"
1348
+ ] }, ct.uid))
1349
+ ]
1350
+ }
1351
+ ),
1352
+ /* @__PURE__ */ jsxRuntime.jsx(Hint, { children: "Leave empty to allow any content type" })
1353
+ ] }),
1354
+ /* @__PURE__ */ jsxRuntime.jsxs(FormGrid, { $columns: "1fr 1fr", style: { marginTop: 16 }, children: [
1355
+ /* @__PURE__ */ jsxRuntime.jsxs(FormGroup, { children: [
1356
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Title Field" }),
1357
+ /* @__PURE__ */ jsxRuntime.jsx(
1358
+ Input,
1359
+ {
1360
+ value: formData.titleField,
1361
+ onChange: (e) => updateFormField("titleField", e.target.value),
1362
+ placeholder: "title"
1363
+ }
1364
+ )
1365
+ ] }),
1366
+ /* @__PURE__ */ jsxRuntime.jsxs(FormGroup, { children: [
1367
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Display Fields" }),
1368
+ /* @__PURE__ */ jsxRuntime.jsx(
1369
+ Input,
1370
+ {
1371
+ value: formData.displayFields.join(", "),
1372
+ onChange: (e) => updateFormField(
1373
+ "displayFields",
1374
+ e.target.value.split(",").map((s) => s.trim())
1375
+ ),
1376
+ placeholder: "title, name, id"
1377
+ }
1378
+ )
1379
+ ] })
1380
+ ] })
1381
+ ] }),
1382
+ formData.blockType === "simple" && /* @__PURE__ */ jsxRuntime.jsxs(FormSection, { children: [
1383
+ /* @__PURE__ */ jsxRuntime.jsxs(SectionTitle, { children: [
1384
+ /* @__PURE__ */ jsxRuntime.jsx(outline.CodeBracketIcon, {}),
1385
+ "Simple Block Settings"
1386
+ ] }),
1387
+ /* @__PURE__ */ jsxRuntime.jsxs(FormGroup, { children: [
1388
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Placeholder Text" }),
1389
+ /* @__PURE__ */ jsxRuntime.jsx(
1390
+ Input,
1391
+ {
1392
+ value: formData.placeholder,
1393
+ onChange: (e) => updateFormField("placeholder", e.target.value),
1394
+ placeholder: "Enter content..."
1395
+ }
1396
+ )
1397
+ ] }),
1398
+ /* @__PURE__ */ jsxRuntime.jsxs(FormGroup, { style: { marginTop: 16 }, children: [
1399
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "HTML Template (Optional)" }),
1400
+ /* @__PURE__ */ jsxRuntime.jsx(
1401
+ TextArea,
1402
+ {
1403
+ value: formData.template,
1404
+ onChange: (e) => updateFormField("template", e.target.value),
1405
+ placeholder: '<div class="my-block">{{content}}</div>',
1406
+ $rows: 4,
1407
+ $mono: true
1408
+ }
1409
+ ),
1410
+ /* @__PURE__ */ jsxRuntime.jsxs(Hint, { children: [
1411
+ "Use ",
1412
+ "{{fieldName}}",
1413
+ " for placeholders"
1414
+ ] })
1415
+ ] }),
1416
+ /* @__PURE__ */ jsxRuntime.jsxs(FormGroup, { style: { marginTop: 16 }, children: [
1417
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Custom Fields" }),
1418
+ formData.fields.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(FieldsContainer, { children: formData.fields.map((field, index) => /* @__PURE__ */ jsxRuntime.jsxs(FieldRow, { children: [
1419
+ /* @__PURE__ */ jsxRuntime.jsx(
1420
+ FieldInput,
1421
+ {
1422
+ value: field.name,
1423
+ onChange: (e) => updateField(index, "name", e.target.value),
1424
+ placeholder: "Field name"
1425
+ }
1426
+ ),
1427
+ /* @__PURE__ */ jsxRuntime.jsx(
1428
+ FieldInput,
1429
+ {
1430
+ value: field.label,
1431
+ onChange: (e) => updateField(index, "label", e.target.value),
1432
+ placeholder: "Label"
1433
+ }
1434
+ ),
1435
+ /* @__PURE__ */ jsxRuntime.jsxs(
1436
+ FieldSelect,
1437
+ {
1438
+ value: field.type,
1439
+ onChange: (e) => updateField(index, "type", e.target.value),
1440
+ children: [
1441
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "text", children: "Text" }),
1442
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "textarea", children: "Textarea" }),
1443
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "select", children: "Select" }),
1444
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "color", children: "Color" }),
1445
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "checkbox", children: "Checkbox" })
1446
+ ]
1447
+ }
1448
+ ),
1449
+ /* @__PURE__ */ jsxRuntime.jsx(IconButton, { $danger: true, onClick: () => removeField(index), children: /* @__PURE__ */ jsxRuntime.jsx(outline.TrashIcon, {}) })
1450
+ ] }, index)) }),
1451
+ /* @__PURE__ */ jsxRuntime.jsxs(AddFieldButton, { onClick: addField, children: [
1452
+ /* @__PURE__ */ jsxRuntime.jsx(outline.PlusIcon, {}),
1453
+ "Add Field"
1454
+ ] })
1455
+ ] })
1456
+ ] }),
1457
+ /* @__PURE__ */ jsxRuntime.jsxs(FormSection, { children: [
1458
+ /* @__PURE__ */ jsxRuntime.jsxs(SectionTitle, { children: [
1459
+ /* @__PURE__ */ jsxRuntime.jsx(outline.SparklesIcon, {}),
1460
+ "Advanced Options"
1461
+ ] }),
1462
+ /* @__PURE__ */ jsxRuntime.jsxs(FormGroup, { children: [
1463
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Icon (SVG)" }),
1464
+ /* @__PURE__ */ jsxRuntime.jsx(
1465
+ TextArea,
1466
+ {
1467
+ value: formData.icon,
1468
+ onChange: (e) => updateFormField("icon", e.target.value),
1469
+ placeholder: '<svg viewBox="0 0 24 24">...</svg>',
1470
+ $rows: 2,
1471
+ $mono: true
1472
+ }
1473
+ ),
1474
+ /* @__PURE__ */ jsxRuntime.jsx(Hint, { children: "Custom SVG icon for the toolbox" })
1475
+ ] }),
1476
+ /* @__PURE__ */ jsxRuntime.jsxs(FormGroup, { style: { marginTop: 16 }, children: [
1477
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Keyboard Shortcut" }),
1478
+ /* @__PURE__ */ jsxRuntime.jsx(
1479
+ Input,
1480
+ {
1481
+ value: formData.shortcut,
1482
+ onChange: (e) => updateFormField("shortcut", e.target.value),
1483
+ placeholder: "CMD+SHIFT+X"
1484
+ }
1485
+ )
1486
+ ] }),
1487
+ /* @__PURE__ */ jsxRuntime.jsxs(FormGrid, { $columns: "1fr 1fr", style: { marginTop: 16 }, children: [
1488
+ /* @__PURE__ */ jsxRuntime.jsxs(CheckboxRow, { children: [
1489
+ /* @__PURE__ */ jsxRuntime.jsx(
1490
+ Checkbox,
1491
+ {
1492
+ checked: formData.inlineToolbar,
1493
+ onChange: (e) => updateFormField("inlineToolbar", e.target.checked)
1494
+ }
1495
+ ),
1496
+ /* @__PURE__ */ jsxRuntime.jsx(CheckboxLabel, { children: "Enable Inline Toolbar" })
1497
+ ] }),
1498
+ /* @__PURE__ */ jsxRuntime.jsxs(CheckboxRow, { children: [
1499
+ /* @__PURE__ */ jsxRuntime.jsx(
1500
+ Checkbox,
1501
+ {
1502
+ checked: formData.enabled,
1503
+ onChange: (e) => updateFormField("enabled", e.target.checked)
1504
+ }
1505
+ ),
1506
+ /* @__PURE__ */ jsxRuntime.jsx(CheckboxLabel, { children: "Block Enabled" })
1507
+ ] })
1508
+ ] })
1509
+ ] })
1510
+ ] }),
1511
+ /* @__PURE__ */ jsxRuntime.jsxs(ModalFooter, { children: [
1512
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { $variant: "secondary", onClick: () => setIsModalOpen(false), children: "Cancel" }),
1513
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { $variant: "primary", onClick: handleSave, children: editingBlock ? "Save Changes" : "Create Block" })
1514
+ ] })
1515
+ ] }) })
1516
+ ] }) });
1517
+ };
1518
+ exports.default = CustomBlocksPage;