@gxp-dev/tools 2.0.6 → 2.0.7

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 (99) hide show
  1. package/browser-extensions/README.md +1 -0
  2. package/browser-extensions/chrome/background.js +857 -0
  3. package/browser-extensions/chrome/content.js +51 -0
  4. package/browser-extensions/chrome/devtools.html +9 -0
  5. package/browser-extensions/chrome/devtools.js +23 -0
  6. package/browser-extensions/chrome/icons/gx_off_128.png +0 -0
  7. package/browser-extensions/chrome/icons/gx_off_16.png +0 -0
  8. package/browser-extensions/chrome/icons/gx_off_32.png +0 -0
  9. package/browser-extensions/chrome/icons/gx_off_64.png +0 -0
  10. package/browser-extensions/chrome/icons/gx_on_128.png +0 -0
  11. package/browser-extensions/chrome/icons/gx_on_16.png +0 -0
  12. package/browser-extensions/chrome/icons/gx_on_32.png +0 -0
  13. package/browser-extensions/chrome/icons/gx_on_64.png +0 -0
  14. package/browser-extensions/chrome/inspector.js +1087 -0
  15. package/browser-extensions/chrome/manifest.json +70 -0
  16. package/browser-extensions/chrome/panel.html +638 -0
  17. package/browser-extensions/chrome/panel.js +862 -0
  18. package/browser-extensions/chrome/popup.html +399 -0
  19. package/browser-extensions/chrome/popup.js +515 -0
  20. package/browser-extensions/chrome/rules.json +1 -0
  21. package/browser-extensions/chrome/test-chrome.html +145 -0
  22. package/browser-extensions/chrome/test-mixed-content.html +190 -0
  23. package/browser-extensions/chrome/test-uri-pattern.html +199 -0
  24. package/browser-extensions/firefox/README.md +134 -0
  25. package/browser-extensions/firefox/background.js +804 -0
  26. package/browser-extensions/firefox/content.js +120 -0
  27. package/browser-extensions/firefox/debug-errors.html +229 -0
  28. package/browser-extensions/firefox/debug-https.html +113 -0
  29. package/browser-extensions/firefox/devtools.html +9 -0
  30. package/browser-extensions/firefox/devtools.js +24 -0
  31. package/browser-extensions/firefox/icons/gx_off_128.png +0 -0
  32. package/browser-extensions/firefox/icons/gx_off_16.png +0 -0
  33. package/browser-extensions/firefox/icons/gx_off_32.png +0 -0
  34. package/browser-extensions/firefox/icons/gx_off_64.png +0 -0
  35. package/browser-extensions/firefox/icons/gx_on_128.png +0 -0
  36. package/browser-extensions/firefox/icons/gx_on_16.png +0 -0
  37. package/browser-extensions/firefox/icons/gx_on_32.png +0 -0
  38. package/browser-extensions/firefox/icons/gx_on_64.png +0 -0
  39. package/browser-extensions/firefox/inspector.js +1087 -0
  40. package/browser-extensions/firefox/manifest.json +67 -0
  41. package/browser-extensions/firefox/panel.html +638 -0
  42. package/browser-extensions/firefox/panel.js +862 -0
  43. package/browser-extensions/firefox/popup.html +525 -0
  44. package/browser-extensions/firefox/popup.js +536 -0
  45. package/browser-extensions/firefox/test-gramercy.html +126 -0
  46. package/browser-extensions/firefox/test-imports.html +58 -0
  47. package/browser-extensions/firefox/test-masking.html +147 -0
  48. package/browser-extensions/firefox/test-uri-pattern.html +199 -0
  49. package/package.json +7 -2
  50. package/runtime/PortalContainer.vue +326 -0
  51. package/runtime/dev-tools/DevToolsModal.vue +217 -0
  52. package/runtime/dev-tools/LayoutSwitcher.vue +221 -0
  53. package/runtime/dev-tools/MockDataEditor.vue +621 -0
  54. package/runtime/dev-tools/SocketSimulator.vue +562 -0
  55. package/runtime/dev-tools/StoreInspector.vue +644 -0
  56. package/runtime/dev-tools/index.js +6 -0
  57. package/runtime/gxpStringsPlugin.js +428 -0
  58. package/runtime/index.html +22 -0
  59. package/runtime/main.js +32 -0
  60. package/runtime/mock-api/auth-middleware.js +97 -0
  61. package/runtime/mock-api/image-generator.js +221 -0
  62. package/runtime/mock-api/index.js +197 -0
  63. package/runtime/mock-api/response-generator.js +394 -0
  64. package/runtime/mock-api/route-generator.js +323 -0
  65. package/runtime/mock-api/socket-triggers.js +371 -0
  66. package/runtime/mock-api/spec-loader.js +300 -0
  67. package/runtime/server.js +180 -0
  68. package/runtime/stores/gxpPortalConfigStore.js +554 -0
  69. package/runtime/stores/index.js +6 -0
  70. package/runtime/vite-inspector-plugin.js +749 -0
  71. package/runtime/vite-source-tracker-plugin.js +232 -0
  72. package/runtime/vite.config.js +402 -0
  73. package/scripts/launch-chrome.js +90 -0
  74. package/scripts/pack-chrome.js +91 -0
  75. package/socket-events/AiSessionMessageCreated.json +18 -0
  76. package/socket-events/SocialStreamPostCreated.json +24 -0
  77. package/socket-events/SocialStreamPostVariantCompleted.json +23 -0
  78. package/template/README.md +332 -0
  79. package/template/app-manifest.json +32 -0
  80. package/template/dev-assets/images/avatar-placeholder.png +0 -0
  81. package/template/dev-assets/images/background-placeholder.jpg +0 -0
  82. package/template/dev-assets/images/banner-placeholder.jpg +0 -0
  83. package/template/dev-assets/images/icon-placeholder.png +0 -0
  84. package/template/dev-assets/images/logo-placeholder.png +0 -0
  85. package/template/dev-assets/images/product-placeholder.jpg +0 -0
  86. package/template/dev-assets/images/thumbnail-placeholder.jpg +0 -0
  87. package/template/env.example +51 -0
  88. package/template/gitignore +53 -0
  89. package/template/index.html +22 -0
  90. package/template/main.js +28 -0
  91. package/template/src/DemoPage.vue +459 -0
  92. package/template/src/Plugin.vue +38 -0
  93. package/template/src/stores/index.js +9 -0
  94. package/template/src/stores/test-data.json +173 -0
  95. package/template/theme-layouts/AdditionalStyling.css +0 -0
  96. package/template/theme-layouts/PrivateLayout.vue +39 -0
  97. package/template/theme-layouts/PublicLayout.vue +39 -0
  98. package/template/theme-layouts/SystemLayout.vue +39 -0
  99. package/template/vite.config.js +333 -0
@@ -0,0 +1,326 @@
1
+ <template>
2
+ <div id="app">
3
+ <!-- Dev Tools Modal -->
4
+ <DevToolsModal
5
+ v-if="showDevTools"
6
+ :store="gxpStore"
7
+ :current-layout="currentLayoutName"
8
+ @close="showDevTools = false"
9
+ @change-layout="changeLayout"
10
+ />
11
+
12
+ <!-- Main Application -->
13
+ <component
14
+ :is="currentLayout"
15
+ :usr-lang="userLanguage"
16
+ :portal-settings="themeSettings"
17
+ :portal-language="portalStringsList"
18
+ :portal-navigation="portalNavigationList"
19
+ :portal-assets="portalAssetList"
20
+ >
21
+ <!-- Your Custom Plugin Content -->
22
+ <Plugin
23
+ :router="mockRouter"
24
+ />
25
+ </component>
26
+
27
+ <!-- Dev Tools Toggle Button (visible in corner) -->
28
+ <button
29
+ class="gx-devtools-trigger"
30
+ @click="showDevTools = true"
31
+ title="Open Dev Tools (Ctrl+Shift+D)"
32
+ >
33
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
34
+ <circle cx="12" cy="12" r="3"/>
35
+ <path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>
36
+ </svg>
37
+ </button>
38
+ </div>
39
+ </template>
40
+
41
+ <style scoped>
42
+ #app {
43
+ font-family: Avenir, Helvetica, Arial, sans-serif;
44
+ -webkit-font-smoothing: antialiased;
45
+ -moz-osx-font-smoothing: grayscale;
46
+ }
47
+
48
+ .gx-devtools-trigger {
49
+ position: fixed;
50
+ bottom: 20px;
51
+ right: 20px;
52
+ width: 44px;
53
+ height: 44px;
54
+ border-radius: 50%;
55
+ background: #1e1e1e;
56
+ border: 2px solid #3d3d3d;
57
+ color: #61dafb;
58
+ cursor: pointer;
59
+ display: flex;
60
+ align-items: center;
61
+ justify-content: center;
62
+ z-index: 99998;
63
+ transition: all 0.2s;
64
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
65
+ }
66
+
67
+ .gx-devtools-trigger:hover {
68
+ background: #2d2d2d;
69
+ border-color: #61dafb;
70
+ transform: scale(1.1);
71
+ }
72
+
73
+ .gx-devtools-trigger svg {
74
+ animation: spin 10s linear infinite;
75
+ }
76
+
77
+ @keyframes spin {
78
+ from { transform: rotate(0deg); }
79
+ to { transform: rotate(360deg); }
80
+ }
81
+ </style>
82
+
83
+ <script setup>
84
+ import { ref, shallowRef, onMounted, onUnmounted } from "vue";
85
+
86
+ // These imports use aliases configured in vite.config.js
87
+ // @/ points to the client project's src/ directory
88
+ // @layouts/ points to the client project's theme-layouts/ directory
89
+ import Plugin from "@/Plugin.vue";
90
+ import "@layouts/AdditionalStyling.css";
91
+ import SystemLayout from "@layouts/SystemLayout.vue";
92
+ import PrivateLayout from "@layouts/PrivateLayout.vue";
93
+ import PublicLayout from "@layouts/PublicLayout.vue";
94
+
95
+ // Dev Tools
96
+ import DevToolsModal from "./dev-tools/DevToolsModal.vue";
97
+
98
+ // Initialize the GxP store from client project's stores/index.js
99
+ // which re-exports useGxpStore from either the toolkit or a local copy
100
+ import { useGxpStore } from "@/stores/index.js";
101
+ window.useGxpStore = { useGxpStore };
102
+ // App state management
103
+ const showDevTools = ref(false);
104
+ const currentLayout = shallowRef(PublicLayout);
105
+ const currentLayoutName = ref('public');
106
+ const currentPage = ref('start');
107
+ const isLoading = ref(false);
108
+ const loadingMessage = ref('Loading...');
109
+
110
+ // Layout management
111
+ const changeLayout = (layout) => {
112
+ currentLayoutName.value = layout;
113
+ switch (layout) {
114
+ case 'system':
115
+ currentLayout.value = SystemLayout;
116
+ break;
117
+ case 'private':
118
+ currentLayout.value = PrivateLayout;
119
+ break;
120
+ default:
121
+ currentLayout.value = PublicLayout;
122
+ currentLayoutName.value = 'public';
123
+ break;
124
+ }
125
+ console.log(`[GxP] Layout changed to: ${currentLayoutName.value}`);
126
+ };
127
+
128
+ // Expose layout control to window
129
+ window.changeLayout = changeLayout;
130
+
131
+ // Initialize the GxP store
132
+ const gxpStore = useGxpStore();
133
+ gxpStore.sockets?.primary?.listenForStateChange?.((event) => {
134
+ console.log('🔗 GXP Store: State change event received', event);
135
+ });
136
+
137
+ // Navigation functions
138
+ const goToPage = (page) => {
139
+ currentPage.value = page;
140
+ };
141
+
142
+ const resetToStart = () => {
143
+ currentPage.value = 'start';
144
+ };
145
+
146
+ const showLoading = (message = 'Loading...') => {
147
+ loadingMessage.value = message;
148
+ isLoading.value = true;
149
+ };
150
+
151
+ const hideLoading = () => {
152
+ isLoading.value = false;
153
+ };
154
+
155
+ const logout = () => {
156
+ alert("Logging Out");
157
+ };
158
+
159
+ // Mock router to simulate platform navigation during development
160
+ const mockRouter = {
161
+ visit: (url, options = {}) => {
162
+ console.log(`🔗 Mock Router: Navigating to ${url}`, options);
163
+
164
+ // Simulate platform navigation behavior
165
+ if (options.onStart) options.onStart();
166
+
167
+ // Map platform routes to local pages
168
+ const routeMap = {
169
+ '/start': 'start',
170
+ '/plugin': 'plugin',
171
+ '/final': 'final',
172
+ '/camera': 'plugin',
173
+ '/results': 'plugin',
174
+ '/share': 'plugin',
175
+ '/instructions': 'plugin'
176
+ };
177
+
178
+ const targetPage = routeMap[url] || 'plugin';
179
+
180
+ // Simulate async navigation
181
+ setTimeout(() => {
182
+ goToPage(targetPage);
183
+ if (options.onFinish) options.onFinish();
184
+ }, 100);
185
+ }
186
+ };
187
+
188
+ const userLanguage = "";
189
+
190
+ // Theme configuration
191
+ const themeSettings = {
192
+ background_color: "#ffffff",
193
+ text_color: "#333333",
194
+ primary_color: "#FFD600",
195
+ start_background_color: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
196
+ start_text_color: "#ffffff",
197
+ final_background_color: "#4CAF50",
198
+ final_text_color: "#ffffff",
199
+ };
200
+
201
+ const themeColors = {
202
+ background_color: "#ffffff",
203
+ };
204
+
205
+ const permissionFlags = [];
206
+ const sockets = {};
207
+ const userAuth = {};
208
+
209
+ // Update dependencyList with all the dependencies that will be set through the custom admin panel
210
+ const dependencyList = {
211
+ "project_location": 4
212
+ };
213
+
214
+ // Update pluginVars with all the variables that will be set through the custom admin panel
215
+ const pluginVars = {
216
+ "primary_color": "#FFD600",
217
+ "projectId": 39,
218
+ "apiPageAuthId": "46|j7vCLmx2KG70Ig8R5mtUcNMFcHExvFPZEOv31kKGe1033f2b",
219
+ "apiBaseUrl": "https://api.efcloud.app",
220
+ "idle_timeout": "30",
221
+ };
222
+
223
+ const userSession = "";
224
+
225
+ const portalNavigationList = [
226
+ {title: "Start", route: "/start"},
227
+ {title: "Plugin", route: "/plugin"},
228
+ {title: "Final", route: "/final"},
229
+ {title: "Logout", route: "/logout", system_type: "logout"},
230
+ ];
231
+
232
+ // Update assetList with all the assets that will be selected through the custom admin panel
233
+ const appAssetList = {
234
+ "main_logo": "https://dashboard.eventfinity.test/storage/assets/69/2HyPwh1692319982.png",
235
+ "background_image": "https://dashboard.eventfinity.test/storage/assets/69/2HyPwh1692319982.png",
236
+ };
237
+
238
+ const portalAssetList = {
239
+ "main_logo": "https://dashboard.eventfinity.test/storage/assets/69/2HyPwh1692319982.png",
240
+ "background_image": "https://dashboard.eventfinity.test/storage/assets/69/2HyPwh1692319982.png",
241
+ };
242
+
243
+ const portalStringsList = {
244
+ "start_line_one": "Welcome to Your App!",
245
+ "start_line_two": "Touch to begin your experience",
246
+ "start_touch_start": "Get started by touching the button below",
247
+ "final_line_one": "Thank You!",
248
+ "final_line_two": "Your experience has been completed successfully",
249
+ "final_line_three": "Touch anywhere to start over",
250
+ "welcome_text": "Hello World",
251
+ };
252
+
253
+ const appStringsList = {
254
+ "start_line_one": "Welcome to Your App!",
255
+ "start_line_two": "Touch to begin your experience",
256
+ "start_touch_start": "Get started by touching the button below",
257
+ "final_line_one": "Thank You!",
258
+ "final_line_two": "Your experience has been completed successfully",
259
+ "final_line_three": "Touch anywhere to start over",
260
+ "welcome_text": "Hello World",
261
+ };
262
+
263
+ // Keyboard shortcut handler
264
+ function handleKeydown(e) {
265
+ // Ctrl+Shift+D (or Cmd+Shift+D on Mac) to toggle dev tools
266
+ if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'D') {
267
+ e.preventDefault();
268
+ showDevTools.value = !showDevTools.value;
269
+ }
270
+ }
271
+
272
+ // Setup window.gxDevTools API
273
+ function setupDevToolsAPI() {
274
+ window.gxDevTools = {
275
+ open: () => {
276
+ showDevTools.value = true;
277
+ console.log('[GxP] Dev Tools opened');
278
+ },
279
+ close: () => {
280
+ showDevTools.value = false;
281
+ console.log('[GxP] Dev Tools closed');
282
+ },
283
+ toggle: () => {
284
+ showDevTools.value = !showDevTools.value;
285
+ console.log(`[GxP] Dev Tools ${showDevTools.value ? 'opened' : 'closed'}`);
286
+ },
287
+ isOpen: () => showDevTools.value,
288
+ // Convenience methods
289
+ store: () => gxpStore,
290
+ setLayout: (layout) => changeLayout(layout),
291
+ getLayout: () => currentLayoutName.value,
292
+ };
293
+
294
+ // Legacy support
295
+ window.toggleConfigPanel = () => window.gxDevTools.toggle();
296
+ }
297
+
298
+ // Expose functions for use in Plugin component
299
+ defineExpose({
300
+ goToPage,
301
+ resetToStart,
302
+ showLoading,
303
+ hideLoading,
304
+ gxpStore
305
+ });
306
+
307
+ onMounted(() => {
308
+ // Setup keyboard shortcut
309
+ document.addEventListener('keydown', handleKeydown);
310
+
311
+ // Setup dev tools API
312
+ setupDevToolsAPI();
313
+
314
+ // Welcome message
315
+ console.log('%c GxP Developer Toolkit ', 'background: #61dafb; color: #1e1e1e; font-size: 14px; padding: 4px 8px; border-radius: 4px;');
316
+ console.log('%c Dev Tools: Press Ctrl+Shift+D or click the gear icon ', 'color: #888; font-size: 12px;');
317
+ console.log('%c Console API: window.gxDevTools.open() / .close() / .toggle() ', 'color: #888; font-size: 12px;');
318
+ });
319
+
320
+ onUnmounted(() => {
321
+ document.removeEventListener('keydown', handleKeydown);
322
+ delete window.gxDevTools;
323
+ delete window.toggleConfigPanel;
324
+ delete window.changeLayout;
325
+ });
326
+ </script>
@@ -0,0 +1,217 @@
1
+ <template>
2
+ <Teleport to="body">
3
+ <div class="gx-devtools-overlay" @click.self="$emit('close')">
4
+ <div class="gx-devtools-modal">
5
+ <header class="gx-devtools-header">
6
+ <h2>GxP Dev Tools</h2>
7
+ <div class="gx-devtools-tabs">
8
+ <button
9
+ v-for="tab in tabs"
10
+ :key="tab.id"
11
+ :class="['gx-devtools-tab', { active: activeTab === tab.id }]"
12
+ @click="activeTab = tab.id"
13
+ >
14
+ {{ tab.label }}
15
+ </button>
16
+ </div>
17
+ <button class="gx-devtools-close" @click="$emit('close')" title="Close (Esc)">
18
+ &times;
19
+ </button>
20
+ </header>
21
+
22
+ <main class="gx-devtools-content">
23
+ <StoreInspector
24
+ v-if="activeTab === 'store'"
25
+ :store="store"
26
+ />
27
+ <LayoutSwitcher
28
+ v-else-if="activeTab === 'layout'"
29
+ :current-layout="currentLayout"
30
+ @change-layout="$emit('change-layout', $event)"
31
+ />
32
+ <SocketSimulator
33
+ v-else-if="activeTab === 'socket'"
34
+ :store="store"
35
+ />
36
+ <MockDataEditor
37
+ v-else-if="activeTab === 'data'"
38
+ :store="store"
39
+ />
40
+ </main>
41
+
42
+ <footer class="gx-devtools-footer">
43
+ <span class="gx-devtools-hint">
44
+ Press <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>D</kbd> or <kbd>Esc</kbd> to close
45
+ </span>
46
+ <span class="gx-devtools-version">GxP Toolkit Dev Tools</span>
47
+ </footer>
48
+ </div>
49
+ </div>
50
+ </Teleport>
51
+ </template>
52
+
53
+ <script setup>
54
+ import { ref, onMounted, onUnmounted } from 'vue';
55
+ import StoreInspector from './StoreInspector.vue';
56
+ import LayoutSwitcher from './LayoutSwitcher.vue';
57
+ import SocketSimulator from './SocketSimulator.vue';
58
+ import MockDataEditor from './MockDataEditor.vue';
59
+
60
+ const props = defineProps({
61
+ store: {
62
+ type: Object,
63
+ required: true
64
+ },
65
+ currentLayout: {
66
+ type: String,
67
+ default: 'public'
68
+ }
69
+ });
70
+
71
+ const emit = defineEmits(['close', 'change-layout']);
72
+
73
+ const tabs = [
74
+ { id: 'store', label: 'Store' },
75
+ { id: 'layout', label: 'Layout' },
76
+ { id: 'socket', label: 'Socket' },
77
+ { id: 'data', label: 'Mock Data' }
78
+ ];
79
+
80
+ const activeTab = ref('store');
81
+
82
+ // Handle escape key to close
83
+ function handleKeydown(e) {
84
+ if (e.key === 'Escape') {
85
+ emit('close');
86
+ }
87
+ }
88
+
89
+ onMounted(() => {
90
+ document.addEventListener('keydown', handleKeydown);
91
+ // Prevent body scroll when modal is open
92
+ document.body.style.overflow = 'hidden';
93
+ });
94
+
95
+ onUnmounted(() => {
96
+ document.removeEventListener('keydown', handleKeydown);
97
+ document.body.style.overflow = '';
98
+ });
99
+ </script>
100
+
101
+ <style scoped>
102
+ .gx-devtools-overlay {
103
+ position: fixed;
104
+ top: 0;
105
+ left: 0;
106
+ right: 0;
107
+ bottom: 0;
108
+ background: rgba(0, 0, 0, 0.5);
109
+ display: flex;
110
+ align-items: center;
111
+ justify-content: center;
112
+ z-index: 99999;
113
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
114
+ }
115
+
116
+ .gx-devtools-modal {
117
+ background: #1e1e1e;
118
+ color: #e0e0e0;
119
+ border-radius: 8px;
120
+ width: 90%;
121
+ max-width: 900px;
122
+ max-height: 85vh;
123
+ display: flex;
124
+ flex-direction: column;
125
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
126
+ overflow: hidden;
127
+ }
128
+
129
+ .gx-devtools-header {
130
+ display: flex;
131
+ align-items: center;
132
+ padding: 12px 16px;
133
+ background: #2d2d2d;
134
+ border-bottom: 1px solid #3d3d3d;
135
+ gap: 16px;
136
+ }
137
+
138
+ .gx-devtools-header h2 {
139
+ margin: 0;
140
+ font-size: 14px;
141
+ font-weight: 600;
142
+ color: #61dafb;
143
+ white-space: nowrap;
144
+ }
145
+
146
+ .gx-devtools-tabs {
147
+ display: flex;
148
+ gap: 4px;
149
+ flex: 1;
150
+ }
151
+
152
+ .gx-devtools-tab {
153
+ background: transparent;
154
+ border: none;
155
+ color: #888;
156
+ padding: 6px 12px;
157
+ cursor: pointer;
158
+ border-radius: 4px;
159
+ font-size: 13px;
160
+ transition: all 0.2s;
161
+ }
162
+
163
+ .gx-devtools-tab:hover {
164
+ color: #e0e0e0;
165
+ background: #3d3d3d;
166
+ }
167
+
168
+ .gx-devtools-tab.active {
169
+ color: #61dafb;
170
+ background: #3d3d3d;
171
+ }
172
+
173
+ .gx-devtools-close {
174
+ background: transparent;
175
+ border: none;
176
+ color: #888;
177
+ font-size: 24px;
178
+ cursor: pointer;
179
+ padding: 0 8px;
180
+ line-height: 1;
181
+ transition: color 0.2s;
182
+ }
183
+
184
+ .gx-devtools-close:hover {
185
+ color: #ff6b6b;
186
+ }
187
+
188
+ .gx-devtools-content {
189
+ flex: 1;
190
+ overflow: auto;
191
+ padding: 16px;
192
+ min-height: 400px;
193
+ }
194
+
195
+ .gx-devtools-footer {
196
+ display: flex;
197
+ justify-content: space-between;
198
+ align-items: center;
199
+ padding: 8px 16px;
200
+ background: #2d2d2d;
201
+ border-top: 1px solid #3d3d3d;
202
+ font-size: 11px;
203
+ color: #666;
204
+ }
205
+
206
+ .gx-devtools-hint kbd {
207
+ background: #3d3d3d;
208
+ padding: 2px 6px;
209
+ border-radius: 3px;
210
+ font-family: inherit;
211
+ font-size: 10px;
212
+ }
213
+
214
+ .gx-devtools-version {
215
+ color: #555;
216
+ }
217
+ </style>