@gxp-dev/tools 2.0.5

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 (145) hide show
  1. package/.github/workflows/npm-publish.yml +48 -0
  2. package/CLAUDE.md +400 -0
  3. package/README.md +247 -0
  4. package/REFACTOR_PLAN.md +194 -0
  5. package/bin/gx-devtools.js +87 -0
  6. package/bin/lib/cli.js +251 -0
  7. package/bin/lib/commands/assets.js +337 -0
  8. package/bin/lib/commands/build.js +259 -0
  9. package/bin/lib/commands/datastore.js +433 -0
  10. package/bin/lib/commands/dev.js +328 -0
  11. package/bin/lib/commands/extensions.js +298 -0
  12. package/bin/lib/commands/index.js +35 -0
  13. package/bin/lib/commands/init.js +307 -0
  14. package/bin/lib/commands/publish.js +189 -0
  15. package/bin/lib/commands/socket.js +158 -0
  16. package/bin/lib/commands/ssl.js +47 -0
  17. package/bin/lib/constants.js +120 -0
  18. package/bin/lib/tui/App.tsx +600 -0
  19. package/bin/lib/tui/components/CommandInput.tsx +278 -0
  20. package/bin/lib/tui/components/GeminiPanel.tsx +161 -0
  21. package/bin/lib/tui/components/Header.tsx +27 -0
  22. package/bin/lib/tui/components/LogPanel.tsx +122 -0
  23. package/bin/lib/tui/components/TabBar.tsx +56 -0
  24. package/bin/lib/tui/components/WelcomeScreen.tsx +80 -0
  25. package/bin/lib/tui/index.tsx +63 -0
  26. package/bin/lib/tui/services/ExtensionService.ts +122 -0
  27. package/bin/lib/tui/services/GeminiService.ts +395 -0
  28. package/bin/lib/tui/services/ServiceManager.ts +336 -0
  29. package/bin/lib/tui/services/SocketService.ts +204 -0
  30. package/bin/lib/tui/services/ViteService.ts +107 -0
  31. package/bin/lib/tui/services/index.ts +13 -0
  32. package/bin/lib/utils/files.js +180 -0
  33. package/bin/lib/utils/index.js +17 -0
  34. package/bin/lib/utils/paths.js +138 -0
  35. package/bin/lib/utils/prompts.js +71 -0
  36. package/bin/lib/utils/ssl.js +233 -0
  37. package/browser-extensions/README.md +1 -0
  38. package/browser-extensions/chrome/background.js +857 -0
  39. package/browser-extensions/chrome/content.js +51 -0
  40. package/browser-extensions/chrome/devtools.html +9 -0
  41. package/browser-extensions/chrome/devtools.js +23 -0
  42. package/browser-extensions/chrome/icons/gx_off_128.png +0 -0
  43. package/browser-extensions/chrome/icons/gx_off_16.png +0 -0
  44. package/browser-extensions/chrome/icons/gx_off_32.png +0 -0
  45. package/browser-extensions/chrome/icons/gx_off_64.png +0 -0
  46. package/browser-extensions/chrome/icons/gx_on_128.png +0 -0
  47. package/browser-extensions/chrome/icons/gx_on_16.png +0 -0
  48. package/browser-extensions/chrome/icons/gx_on_32.png +0 -0
  49. package/browser-extensions/chrome/icons/gx_on_64.png +0 -0
  50. package/browser-extensions/chrome/inspector.js +1087 -0
  51. package/browser-extensions/chrome/manifest.json +70 -0
  52. package/browser-extensions/chrome/panel.html +638 -0
  53. package/browser-extensions/chrome/panel.js +862 -0
  54. package/browser-extensions/chrome/popup.html +399 -0
  55. package/browser-extensions/chrome/popup.js +515 -0
  56. package/browser-extensions/chrome/rules.json +1 -0
  57. package/browser-extensions/chrome/test-chrome.html +145 -0
  58. package/browser-extensions/chrome/test-mixed-content.html +190 -0
  59. package/browser-extensions/chrome/test-uri-pattern.html +199 -0
  60. package/browser-extensions/firefox/README.md +134 -0
  61. package/browser-extensions/firefox/background.js +804 -0
  62. package/browser-extensions/firefox/content.js +120 -0
  63. package/browser-extensions/firefox/debug-errors.html +229 -0
  64. package/browser-extensions/firefox/debug-https.html +113 -0
  65. package/browser-extensions/firefox/devtools.html +9 -0
  66. package/browser-extensions/firefox/devtools.js +24 -0
  67. package/browser-extensions/firefox/icons/gx_off_128.png +0 -0
  68. package/browser-extensions/firefox/icons/gx_off_16.png +0 -0
  69. package/browser-extensions/firefox/icons/gx_off_32.png +0 -0
  70. package/browser-extensions/firefox/icons/gx_off_64.png +0 -0
  71. package/browser-extensions/firefox/icons/gx_on_128.png +0 -0
  72. package/browser-extensions/firefox/icons/gx_on_16.png +0 -0
  73. package/browser-extensions/firefox/icons/gx_on_32.png +0 -0
  74. package/browser-extensions/firefox/icons/gx_on_64.png +0 -0
  75. package/browser-extensions/firefox/inspector.js +1087 -0
  76. package/browser-extensions/firefox/manifest.json +67 -0
  77. package/browser-extensions/firefox/panel.html +638 -0
  78. package/browser-extensions/firefox/panel.js +862 -0
  79. package/browser-extensions/firefox/popup.html +525 -0
  80. package/browser-extensions/firefox/popup.js +536 -0
  81. package/browser-extensions/firefox/test-gramercy.html +126 -0
  82. package/browser-extensions/firefox/test-imports.html +58 -0
  83. package/browser-extensions/firefox/test-masking.html +147 -0
  84. package/browser-extensions/firefox/test-uri-pattern.html +199 -0
  85. package/docs/DOCUSAURUS_IMPORT.md +378 -0
  86. package/docs/_category_.json +8 -0
  87. package/docs/app-manifest.md +272 -0
  88. package/docs/building-for-platform.md +315 -0
  89. package/docs/dev-tools.md +291 -0
  90. package/docs/getting-started.md +180 -0
  91. package/docs/gxp-store.md +305 -0
  92. package/docs/index.md +44 -0
  93. package/package.json +77 -0
  94. package/runtime/PortalContainer.vue +326 -0
  95. package/runtime/dev-tools/DevToolsModal.vue +217 -0
  96. package/runtime/dev-tools/LayoutSwitcher.vue +221 -0
  97. package/runtime/dev-tools/MockDataEditor.vue +621 -0
  98. package/runtime/dev-tools/SocketSimulator.vue +562 -0
  99. package/runtime/dev-tools/StoreInspector.vue +644 -0
  100. package/runtime/dev-tools/index.js +6 -0
  101. package/runtime/gxpStringsPlugin.js +428 -0
  102. package/runtime/index.html +22 -0
  103. package/runtime/main.js +32 -0
  104. package/runtime/mock-api/auth-middleware.js +97 -0
  105. package/runtime/mock-api/image-generator.js +221 -0
  106. package/runtime/mock-api/index.js +197 -0
  107. package/runtime/mock-api/response-generator.js +394 -0
  108. package/runtime/mock-api/route-generator.js +323 -0
  109. package/runtime/mock-api/socket-triggers.js +371 -0
  110. package/runtime/mock-api/spec-loader.js +300 -0
  111. package/runtime/server.js +180 -0
  112. package/runtime/stores/gxpPortalConfigStore.js +554 -0
  113. package/runtime/stores/index.js +6 -0
  114. package/runtime/vite-inspector-plugin.js +749 -0
  115. package/runtime/vite-source-tracker-plugin.js +232 -0
  116. package/runtime/vite.config.js +402 -0
  117. package/scripts/launch-chrome.js +90 -0
  118. package/scripts/pack-chrome.js +91 -0
  119. package/socket-events/AiSessionMessageCreated.json +18 -0
  120. package/socket-events/SocialStreamPostCreated.json +24 -0
  121. package/socket-events/SocialStreamPostVariantCompleted.json +23 -0
  122. package/template/README.md +332 -0
  123. package/template/app-manifest.json +32 -0
  124. package/template/dev-assets/images/avatar-placeholder.png +0 -0
  125. package/template/dev-assets/images/background-placeholder.jpg +0 -0
  126. package/template/dev-assets/images/banner-placeholder.jpg +0 -0
  127. package/template/dev-assets/images/icon-placeholder.png +0 -0
  128. package/template/dev-assets/images/logo-placeholder.png +0 -0
  129. package/template/dev-assets/images/product-placeholder.jpg +0 -0
  130. package/template/dev-assets/images/thumbnail-placeholder.jpg +0 -0
  131. package/template/env.example +51 -0
  132. package/template/gitignore +53 -0
  133. package/template/index.html +22 -0
  134. package/template/main.js +28 -0
  135. package/template/src/DemoPage.vue +459 -0
  136. package/template/src/Plugin.vue +38 -0
  137. package/template/src/stores/index.js +9 -0
  138. package/template/src/stores/test-data.json +173 -0
  139. package/template/theme-layouts/AdditionalStyling.css +0 -0
  140. package/template/theme-layouts/PrivateLayout.vue +39 -0
  141. package/template/theme-layouts/PublicLayout.vue +39 -0
  142. package/template/theme-layouts/SystemLayout.vue +39 -0
  143. package/template/vite.config.js +333 -0
  144. package/tsconfig.tui.json +21 -0
  145. package/vite.config.js +164 -0
@@ -0,0 +1,621 @@
1
+ <template>
2
+ <div class="mock-data-editor">
3
+ <div class="editor-description">
4
+ <p>
5
+ Edit mock data that simulates platform responses during development.
6
+ Changes are applied in memory and will reset on page reload.
7
+ </p>
8
+ </div>
9
+
10
+ <div class="data-sections">
11
+ <div class="data-section">
12
+ <div class="section-header" @click="toggleSection('theme')">
13
+ <span class="toggle-icon">{{ expandedSections.theme ? '▼' : '▶' }}</span>
14
+ <h4>Theme Settings</h4>
15
+ </div>
16
+ <div v-if="expandedSections.theme" class="section-content">
17
+ <div class="color-grid">
18
+ <div v-for="(value, key) in themeColors" :key="key" class="color-field">
19
+ <label>{{ formatLabel(key) }}</label>
20
+ <div class="color-input-wrapper">
21
+ <input
22
+ type="color"
23
+ :value="extractColor(value)"
24
+ @input="updateThemeColor(key, $event.target.value)"
25
+ class="color-picker"
26
+ />
27
+ <input
28
+ type="text"
29
+ :value="value"
30
+ @input="updateThemeColor(key, $event.target.value)"
31
+ class="color-text"
32
+ />
33
+ </div>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ </div>
38
+
39
+ <div class="data-section">
40
+ <div class="section-header" @click="toggleSection('navigation')">
41
+ <span class="toggle-icon">{{ expandedSections.navigation ? '▼' : '▶' }}</span>
42
+ <h4>Navigation Items</h4>
43
+ </div>
44
+ <div v-if="expandedSections.navigation" class="section-content">
45
+ <div class="nav-items">
46
+ <div
47
+ v-for="(item, index) in navigationItems"
48
+ :key="index"
49
+ class="nav-item"
50
+ >
51
+ <input
52
+ v-model="item.title"
53
+ class="nav-input"
54
+ placeholder="Title"
55
+ />
56
+ <input
57
+ v-model="item.route"
58
+ class="nav-input"
59
+ placeholder="Route"
60
+ />
61
+ <button class="btn-icon" @click="removeNavItem(index)" title="Remove">
62
+ &times;
63
+ </button>
64
+ </div>
65
+ <button class="btn-add" @click="addNavItem">
66
+ + Add Navigation Item
67
+ </button>
68
+ </div>
69
+ </div>
70
+ </div>
71
+
72
+ <div class="data-section">
73
+ <div class="section-header" @click="toggleSection('user')">
74
+ <span class="toggle-icon">{{ expandedSections.user ? '▼' : '▶' }}</span>
75
+ <h4>User Session</h4>
76
+ </div>
77
+ <div v-if="expandedSections.user" class="section-content">
78
+ <div class="user-fields">
79
+ <div class="field-row">
80
+ <label>Authenticated:</label>
81
+ <label class="toggle-switch">
82
+ <input type="checkbox" v-model="userSession.authenticated" />
83
+ <span class="toggle-slider"></span>
84
+ </label>
85
+ </div>
86
+ <div class="field-row">
87
+ <label>User ID:</label>
88
+ <input
89
+ type="text"
90
+ v-model="userSession.userId"
91
+ class="field-input"
92
+ />
93
+ </div>
94
+ <div class="field-row">
95
+ <label>Username:</label>
96
+ <input
97
+ type="text"
98
+ v-model="userSession.username"
99
+ class="field-input"
100
+ />
101
+ </div>
102
+ <div class="field-row">
103
+ <label>Email:</label>
104
+ <input
105
+ type="email"
106
+ v-model="userSession.email"
107
+ class="field-input"
108
+ />
109
+ </div>
110
+ </div>
111
+ </div>
112
+ </div>
113
+
114
+ <div class="data-section">
115
+ <div class="section-header" @click="toggleSection('permissions')">
116
+ <span class="toggle-icon">{{ expandedSections.permissions ? '▼' : '▶' }}</span>
117
+ <h4>Permission Flags</h4>
118
+ </div>
119
+ <div v-if="expandedSections.permissions" class="section-content">
120
+ <div class="permissions-list">
121
+ <label
122
+ v-for="flag in availablePermissions"
123
+ :key="flag"
124
+ class="permission-item"
125
+ >
126
+ <input
127
+ type="checkbox"
128
+ :checked="activePermissions.includes(flag)"
129
+ @change="togglePermission(flag)"
130
+ />
131
+ <span>{{ flag }}</span>
132
+ </label>
133
+ <div class="add-permission">
134
+ <input
135
+ v-model="newPermission"
136
+ placeholder="Add custom permission..."
137
+ class="field-input"
138
+ @keydown.enter="addPermission"
139
+ />
140
+ <button class="btn-add-sm" @click="addPermission">Add</button>
141
+ </div>
142
+ </div>
143
+ </div>
144
+ </div>
145
+ </div>
146
+
147
+ <div class="editor-actions">
148
+ <button class="btn btn-secondary" @click="resetAllData">
149
+ Reset All to Defaults
150
+ </button>
151
+ <button class="btn btn-primary" @click="exportData">
152
+ Export as JSON
153
+ </button>
154
+ </div>
155
+ </div>
156
+ </template>
157
+
158
+ <script setup>
159
+ import { ref, reactive, computed } from 'vue';
160
+
161
+ const props = defineProps({
162
+ store: {
163
+ type: Object,
164
+ required: true
165
+ }
166
+ });
167
+
168
+ const expandedSections = reactive({
169
+ theme: true,
170
+ navigation: false,
171
+ user: false,
172
+ permissions: false
173
+ });
174
+
175
+ const themeColors = reactive({
176
+ background_color: '#ffffff',
177
+ text_color: '#333333',
178
+ primary_color: '#FFD600',
179
+ start_background_color: '#667eea',
180
+ start_text_color: '#ffffff',
181
+ final_background_color: '#4CAF50',
182
+ final_text_color: '#ffffff'
183
+ });
184
+
185
+ const navigationItems = reactive([
186
+ { title: 'Start', route: '/start' },
187
+ { title: 'Plugin', route: '/plugin' },
188
+ { title: 'Final', route: '/final' },
189
+ { title: 'Logout', route: '/logout', system_type: 'logout' }
190
+ ]);
191
+
192
+ const userSession = reactive({
193
+ authenticated: false,
194
+ userId: '',
195
+ username: '',
196
+ email: ''
197
+ });
198
+
199
+ const availablePermissions = ref([
200
+ 'admin',
201
+ 'editor',
202
+ 'viewer',
203
+ 'can_upload',
204
+ 'can_delete',
205
+ 'can_share'
206
+ ]);
207
+
208
+ const activePermissions = ref([]);
209
+ const newPermission = ref('');
210
+
211
+ function toggleSection(section) {
212
+ expandedSections[section] = !expandedSections[section];
213
+ }
214
+
215
+ function formatLabel(key) {
216
+ return key
217
+ .replace(/_/g, ' ')
218
+ .replace(/\b\w/g, l => l.toUpperCase());
219
+ }
220
+
221
+ function extractColor(value) {
222
+ // Extract hex color from value (handle gradients, etc.)
223
+ if (typeof value !== 'string') return '#ffffff';
224
+ const match = value.match(/#[0-9A-Fa-f]{6}/);
225
+ return match ? match[0] : '#ffffff';
226
+ }
227
+
228
+ function updateThemeColor(key, value) {
229
+ themeColors[key] = value;
230
+ // Update store if available
231
+ if (props.store?.theme) {
232
+ props.store.theme[key] = value;
233
+ }
234
+ console.log(`[DevTools] Theme ${key} updated:`, value);
235
+ }
236
+
237
+ function addNavItem() {
238
+ navigationItems.push({ title: '', route: '' });
239
+ }
240
+
241
+ function removeNavItem(index) {
242
+ navigationItems.splice(index, 1);
243
+ }
244
+
245
+ function togglePermission(flag) {
246
+ const index = activePermissions.value.indexOf(flag);
247
+ if (index > -1) {
248
+ activePermissions.value.splice(index, 1);
249
+ } else {
250
+ activePermissions.value.push(flag);
251
+ }
252
+ // Update store
253
+ if (props.store?.permissionFlags) {
254
+ props.store.permissionFlags.splice(0, props.store.permissionFlags.length, ...activePermissions.value);
255
+ }
256
+ console.log('[DevTools] Permissions updated:', activePermissions.value);
257
+ }
258
+
259
+ function addPermission() {
260
+ if (newPermission.value && !availablePermissions.value.includes(newPermission.value)) {
261
+ availablePermissions.value.push(newPermission.value);
262
+ activePermissions.value.push(newPermission.value);
263
+ newPermission.value = '';
264
+ }
265
+ }
266
+
267
+ function resetAllData() {
268
+ // Reset theme
269
+ Object.assign(themeColors, {
270
+ background_color: '#ffffff',
271
+ text_color: '#333333',
272
+ primary_color: '#FFD600',
273
+ start_background_color: '#667eea',
274
+ start_text_color: '#ffffff',
275
+ final_background_color: '#4CAF50',
276
+ final_text_color: '#ffffff'
277
+ });
278
+
279
+ // Reset navigation
280
+ navigationItems.splice(0, navigationItems.length,
281
+ { title: 'Start', route: '/start' },
282
+ { title: 'Plugin', route: '/plugin' },
283
+ { title: 'Final', route: '/final' },
284
+ { title: 'Logout', route: '/logout', system_type: 'logout' }
285
+ );
286
+
287
+ // Reset user
288
+ Object.assign(userSession, {
289
+ authenticated: false,
290
+ userId: '',
291
+ username: '',
292
+ email: ''
293
+ });
294
+
295
+ // Reset permissions
296
+ activePermissions.value = [];
297
+
298
+ console.log('[DevTools] All mock data reset to defaults');
299
+ }
300
+
301
+ async function exportData() {
302
+ const data = {
303
+ theme: { ...themeColors },
304
+ navigation: [...navigationItems],
305
+ userSession: { ...userSession },
306
+ permissions: [...activePermissions.value]
307
+ };
308
+
309
+ try {
310
+ await navigator.clipboard.writeText(JSON.stringify(data, null, 2));
311
+ console.log('[DevTools] Mock data exported to clipboard');
312
+ } catch (err) {
313
+ console.error('[DevTools] Failed to export:', err);
314
+ }
315
+ }
316
+ </script>
317
+
318
+ <style scoped>
319
+ .mock-data-editor {
320
+ display: flex;
321
+ flex-direction: column;
322
+ gap: 16px;
323
+ }
324
+
325
+ .editor-description {
326
+ background: #2d2d2d;
327
+ padding: 12px 16px;
328
+ border-radius: 8px;
329
+ }
330
+
331
+ .editor-description p {
332
+ margin: 0;
333
+ font-size: 13px;
334
+ color: #888;
335
+ }
336
+
337
+ .data-sections {
338
+ display: flex;
339
+ flex-direction: column;
340
+ gap: 8px;
341
+ }
342
+
343
+ .data-section {
344
+ background: #2d2d2d;
345
+ border-radius: 8px;
346
+ overflow: hidden;
347
+ }
348
+
349
+ .section-header {
350
+ display: flex;
351
+ align-items: center;
352
+ gap: 8px;
353
+ padding: 12px 16px;
354
+ cursor: pointer;
355
+ transition: background 0.2s;
356
+ }
357
+
358
+ .section-header:hover {
359
+ background: #3d3d3d;
360
+ }
361
+
362
+ .section-header h4 {
363
+ margin: 0;
364
+ font-size: 13px;
365
+ font-weight: 500;
366
+ }
367
+
368
+ .toggle-icon {
369
+ font-size: 10px;
370
+ color: #888;
371
+ width: 12px;
372
+ }
373
+
374
+ .section-content {
375
+ padding: 12px 16px;
376
+ border-top: 1px solid #3d3d3d;
377
+ }
378
+
379
+ /* Theme Colors */
380
+ .color-grid {
381
+ display: grid;
382
+ grid-template-columns: repeat(2, 1fr);
383
+ gap: 12px;
384
+ }
385
+
386
+ .color-field label {
387
+ display: block;
388
+ font-size: 11px;
389
+ color: #888;
390
+ margin-bottom: 4px;
391
+ }
392
+
393
+ .color-input-wrapper {
394
+ display: flex;
395
+ gap: 8px;
396
+ }
397
+
398
+ .color-picker {
399
+ width: 40px;
400
+ height: 32px;
401
+ border: none;
402
+ border-radius: 4px;
403
+ cursor: pointer;
404
+ background: transparent;
405
+ }
406
+
407
+ .color-text {
408
+ flex: 1;
409
+ background: #1e1e1e;
410
+ border: 1px solid #3d3d3d;
411
+ color: #e0e0e0;
412
+ padding: 6px 8px;
413
+ border-radius: 4px;
414
+ font-size: 12px;
415
+ font-family: 'SF Mono', Monaco, monospace;
416
+ }
417
+
418
+ /* Navigation */
419
+ .nav-items {
420
+ display: flex;
421
+ flex-direction: column;
422
+ gap: 8px;
423
+ }
424
+
425
+ .nav-item {
426
+ display: flex;
427
+ gap: 8px;
428
+ align-items: center;
429
+ }
430
+
431
+ .nav-input {
432
+ flex: 1;
433
+ background: #1e1e1e;
434
+ border: 1px solid #3d3d3d;
435
+ color: #e0e0e0;
436
+ padding: 8px 12px;
437
+ border-radius: 4px;
438
+ font-size: 12px;
439
+ }
440
+
441
+ .btn-icon {
442
+ background: transparent;
443
+ border: none;
444
+ color: #888;
445
+ font-size: 18px;
446
+ cursor: pointer;
447
+ padding: 4px 8px;
448
+ }
449
+
450
+ .btn-icon:hover {
451
+ color: #ff6b6b;
452
+ }
453
+
454
+ .btn-add {
455
+ background: #3d3d3d;
456
+ border: 1px dashed #555;
457
+ color: #888;
458
+ padding: 8px;
459
+ border-radius: 4px;
460
+ cursor: pointer;
461
+ font-size: 12px;
462
+ text-align: center;
463
+ transition: all 0.2s;
464
+ }
465
+
466
+ .btn-add:hover {
467
+ background: #4d4d4d;
468
+ color: #e0e0e0;
469
+ }
470
+
471
+ /* User Session */
472
+ .user-fields {
473
+ display: flex;
474
+ flex-direction: column;
475
+ gap: 12px;
476
+ }
477
+
478
+ .field-row {
479
+ display: flex;
480
+ align-items: center;
481
+ gap: 12px;
482
+ }
483
+
484
+ .field-row > label:first-child {
485
+ min-width: 100px;
486
+ font-size: 12px;
487
+ color: #888;
488
+ }
489
+
490
+ .field-input {
491
+ flex: 1;
492
+ background: #1e1e1e;
493
+ border: 1px solid #3d3d3d;
494
+ color: #e0e0e0;
495
+ padding: 8px 12px;
496
+ border-radius: 4px;
497
+ font-size: 12px;
498
+ }
499
+
500
+ /* Toggle Switch */
501
+ .toggle-switch {
502
+ position: relative;
503
+ display: inline-block;
504
+ width: 44px;
505
+ height: 24px;
506
+ }
507
+
508
+ .toggle-switch input {
509
+ opacity: 0;
510
+ width: 0;
511
+ height: 0;
512
+ }
513
+
514
+ .toggle-slider {
515
+ position: absolute;
516
+ cursor: pointer;
517
+ top: 0;
518
+ left: 0;
519
+ right: 0;
520
+ bottom: 0;
521
+ background-color: #3d3d3d;
522
+ transition: 0.3s;
523
+ border-radius: 24px;
524
+ }
525
+
526
+ .toggle-slider:before {
527
+ position: absolute;
528
+ content: "";
529
+ height: 18px;
530
+ width: 18px;
531
+ left: 3px;
532
+ bottom: 3px;
533
+ background-color: #888;
534
+ transition: 0.3s;
535
+ border-radius: 50%;
536
+ }
537
+
538
+ input:checked + .toggle-slider {
539
+ background-color: #61dafb;
540
+ }
541
+
542
+ input:checked + .toggle-slider:before {
543
+ transform: translateX(20px);
544
+ background-color: #1e1e1e;
545
+ }
546
+
547
+ /* Permissions */
548
+ .permissions-list {
549
+ display: flex;
550
+ flex-direction: column;
551
+ gap: 8px;
552
+ }
553
+
554
+ .permission-item {
555
+ display: flex;
556
+ align-items: center;
557
+ gap: 8px;
558
+ font-size: 12px;
559
+ cursor: pointer;
560
+ }
561
+
562
+ .permission-item input[type="checkbox"] {
563
+ accent-color: #61dafb;
564
+ }
565
+
566
+ .add-permission {
567
+ display: flex;
568
+ gap: 8px;
569
+ margin-top: 8px;
570
+ }
571
+
572
+ .btn-add-sm {
573
+ background: #3d3d3d;
574
+ border: none;
575
+ color: #e0e0e0;
576
+ padding: 8px 12px;
577
+ border-radius: 4px;
578
+ cursor: pointer;
579
+ font-size: 12px;
580
+ }
581
+
582
+ .btn-add-sm:hover {
583
+ background: #4d4d4d;
584
+ }
585
+
586
+ /* Actions */
587
+ .editor-actions {
588
+ display: flex;
589
+ gap: 8px;
590
+ justify-content: flex-end;
591
+ padding-top: 16px;
592
+ border-top: 1px solid #3d3d3d;
593
+ }
594
+
595
+ .btn {
596
+ padding: 8px 16px;
597
+ border: none;
598
+ border-radius: 4px;
599
+ cursor: pointer;
600
+ font-size: 12px;
601
+ transition: all 0.2s;
602
+ }
603
+
604
+ .btn-primary {
605
+ background: #61dafb;
606
+ color: #1e1e1e;
607
+ }
608
+
609
+ .btn-primary:hover {
610
+ background: #4fc3f7;
611
+ }
612
+
613
+ .btn-secondary {
614
+ background: #3d3d3d;
615
+ color: #e0e0e0;
616
+ }
617
+
618
+ .btn-secondary:hover {
619
+ background: #4d4d4d;
620
+ }
621
+ </style>