@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,70 @@
1
+ {
2
+ "manifest_version": 3,
3
+ "name": "GxP Developer Toolkit",
4
+ "version": "1.2",
5
+ "description": "JavaScript proxy and component inspector for GxP plugin development",
6
+
7
+ "permissions": [
8
+ "activeTab",
9
+ "declarativeNetRequest",
10
+ "storage",
11
+ "notifications",
12
+ "webRequest",
13
+ "browsingData",
14
+ "scripting"
15
+ ],
16
+
17
+ "host_permissions": [
18
+ "<all_urls>"
19
+ ],
20
+
21
+ "background": {
22
+ "service_worker": "background.js"
23
+ },
24
+
25
+ "devtools_page": "devtools.html",
26
+
27
+ "content_scripts": [
28
+ {
29
+ "matches": ["<all_urls>"],
30
+ "js": ["content.js"],
31
+ "run_at": "document_start"
32
+ },
33
+ {
34
+ "matches": ["<all_urls>"],
35
+ "js": ["inspector.js"],
36
+ "run_at": "document_idle"
37
+ }
38
+ ],
39
+
40
+ "action": {
41
+ "default_popup": "popup.html",
42
+ "default_title": "GxP Developer Toolkit",
43
+ "default_icon": {
44
+ "16": "icons/gx_off_16.png",
45
+ "32": "icons/gx_off_32.png",
46
+ "64": "icons/gx_off_64.png",
47
+ "128": "icons/gx_off_128.png"
48
+ }
49
+ },
50
+
51
+ "icons": {
52
+ "16": "icons/gx_off_16.png",
53
+ "32": "icons/gx_off_32.png",
54
+ "64": "icons/gx_off_64.png",
55
+ "128": "icons/gx_off_128.png"
56
+ },
57
+
58
+ "web_accessible_resources": [{
59
+ "resources": ["content.js", "inspector.js", "panel.html", "panel.js"],
60
+ "matches": ["<all_urls>"]
61
+ }],
62
+
63
+ "declarative_net_request": {
64
+ "rule_resources": [{
65
+ "id": "proxy_rules",
66
+ "enabled": true,
67
+ "path": "rules.json"
68
+ }]
69
+ }
70
+ }
@@ -0,0 +1,638 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <style>
6
+ * {
7
+ box-sizing: border-box;
8
+ }
9
+
10
+ body {
11
+ margin: 0;
12
+ padding: 0;
13
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
14
+ font-size: 12px;
15
+ background: #1e1e1e;
16
+ color: #e0e0e0;
17
+ height: 100vh;
18
+ overflow: hidden;
19
+ display: flex;
20
+ flex-direction: column;
21
+ }
22
+
23
+ /* Header / Toolbar */
24
+ .toolbar {
25
+ display: flex;
26
+ align-items: center;
27
+ gap: 8px;
28
+ padding: 8px 12px;
29
+ background: #2d2d2d;
30
+ border-bottom: 1px solid #3d3d3d;
31
+ flex-shrink: 0;
32
+ }
33
+
34
+ .toolbar-title {
35
+ font-weight: 600;
36
+ color: #61dafb;
37
+ margin-right: auto;
38
+ }
39
+
40
+ .toolbar-btn {
41
+ background: #3d3d3d;
42
+ border: none;
43
+ color: #e0e0e0;
44
+ padding: 6px 12px;
45
+ border-radius: 4px;
46
+ font-size: 11px;
47
+ cursor: pointer;
48
+ display: flex;
49
+ align-items: center;
50
+ gap: 4px;
51
+ }
52
+
53
+ .toolbar-btn:hover {
54
+ background: #4d4d4d;
55
+ }
56
+
57
+ .toolbar-btn.active {
58
+ background: #61dafb;
59
+ color: #1e1e1e;
60
+ }
61
+
62
+ .toolbar-btn.active:hover {
63
+ background: #4fc3f7;
64
+ }
65
+
66
+ .status-indicator {
67
+ width: 8px;
68
+ height: 8px;
69
+ border-radius: 50%;
70
+ background: #dc3545;
71
+ }
72
+
73
+ .status-indicator.connected {
74
+ background: #28a745;
75
+ }
76
+
77
+ /* Main content area */
78
+ .main-content {
79
+ flex: 1;
80
+ display: flex;
81
+ overflow: hidden;
82
+ }
83
+
84
+ /* Sidebar for component tree (future) */
85
+ .sidebar {
86
+ width: 250px;
87
+ background: #252525;
88
+ border-right: 1px solid #3d3d3d;
89
+ display: flex;
90
+ flex-direction: column;
91
+ flex-shrink: 0;
92
+ }
93
+
94
+ .sidebar-header {
95
+ padding: 8px 12px;
96
+ font-weight: 600;
97
+ font-size: 11px;
98
+ text-transform: uppercase;
99
+ color: #888;
100
+ border-bottom: 1px solid #3d3d3d;
101
+ }
102
+
103
+ .sidebar-content {
104
+ flex: 1;
105
+ overflow-y: auto;
106
+ padding: 8px;
107
+ }
108
+
109
+ /* Inspector panel */
110
+ .inspector {
111
+ flex: 1;
112
+ display: flex;
113
+ flex-direction: column;
114
+ overflow: hidden;
115
+ }
116
+
117
+ .inspector-header {
118
+ padding: 12px 16px;
119
+ border-bottom: 1px solid #3d3d3d;
120
+ background: #252525;
121
+ }
122
+
123
+ .component-name {
124
+ font-size: 15px;
125
+ font-weight: 600;
126
+ color: #61dafb;
127
+ margin-bottom: 4px;
128
+ }
129
+
130
+ .component-file {
131
+ font-size: 11px;
132
+ color: #888;
133
+ word-break: break-all;
134
+ }
135
+
136
+ .inspector-body {
137
+ flex: 1;
138
+ overflow-y: auto;
139
+ padding: 16px;
140
+ }
141
+
142
+ /* Sections */
143
+ .section {
144
+ margin-bottom: 20px;
145
+ }
146
+
147
+ .section-title {
148
+ font-size: 11px;
149
+ font-weight: 600;
150
+ text-transform: uppercase;
151
+ color: #888;
152
+ margin-bottom: 8px;
153
+ display: flex;
154
+ align-items: center;
155
+ gap: 6px;
156
+ }
157
+
158
+ .section-title .count {
159
+ background: #3d3d3d;
160
+ padding: 1px 6px;
161
+ border-radius: 10px;
162
+ font-size: 10px;
163
+ }
164
+
165
+ /* Strings list */
166
+ .strings-list {
167
+ display: flex;
168
+ flex-direction: column;
169
+ gap: 4px;
170
+ }
171
+
172
+ .string-item {
173
+ display: flex;
174
+ align-items: center;
175
+ padding: 8px 10px;
176
+ background: #2d2d2d;
177
+ border-radius: 4px;
178
+ border: 1px solid transparent;
179
+ cursor: pointer;
180
+ }
181
+
182
+ .string-item:hover {
183
+ border-color: #3d3d3d;
184
+ background: #333;
185
+ }
186
+
187
+ .string-item.selected {
188
+ border-color: #61dafb;
189
+ background: #2a3a4a;
190
+ }
191
+
192
+ .string-text {
193
+ flex: 1;
194
+ overflow: hidden;
195
+ text-overflow: ellipsis;
196
+ white-space: nowrap;
197
+ font-family: 'Monaco', 'Menlo', monospace;
198
+ font-size: 11px;
199
+ }
200
+
201
+ .string-actions {
202
+ display: flex;
203
+ gap: 4px;
204
+ opacity: 0;
205
+ transition: opacity 0.15s;
206
+ }
207
+
208
+ .string-item:hover .string-actions {
209
+ opacity: 1;
210
+ }
211
+
212
+ .action-btn {
213
+ background: #61dafb;
214
+ color: #1e1e1e;
215
+ border: none;
216
+ padding: 4px 8px;
217
+ border-radius: 3px;
218
+ font-size: 10px;
219
+ font-weight: 600;
220
+ cursor: pointer;
221
+ }
222
+
223
+ .action-btn:hover {
224
+ background: #4fc3f7;
225
+ }
226
+
227
+ .action-btn.secondary {
228
+ background: #4d4d4d;
229
+ color: #e0e0e0;
230
+ }
231
+
232
+ .action-btn.secondary:hover {
233
+ background: #5d5d5d;
234
+ }
235
+
236
+ /* Extract form */
237
+ .extract-form {
238
+ background: #252525;
239
+ border: 1px solid #3d3d3d;
240
+ border-radius: 6px;
241
+ padding: 16px;
242
+ margin-top: 12px;
243
+ }
244
+
245
+ .form-row {
246
+ display: flex;
247
+ flex-direction: column;
248
+ gap: 4px;
249
+ margin-bottom: 12px;
250
+ }
251
+
252
+ .form-row:last-child {
253
+ margin-bottom: 0;
254
+ }
255
+
256
+ .form-label {
257
+ font-size: 11px;
258
+ color: #888;
259
+ font-weight: 500;
260
+ }
261
+
262
+ .form-input {
263
+ background: #1e1e1e;
264
+ border: 1px solid #3d3d3d;
265
+ border-radius: 4px;
266
+ padding: 8px 10px;
267
+ color: #e0e0e0;
268
+ font-size: 12px;
269
+ font-family: 'Monaco', 'Menlo', monospace;
270
+ }
271
+
272
+ .form-input:focus {
273
+ outline: none;
274
+ border-color: #61dafb;
275
+ }
276
+
277
+ .form-input:disabled {
278
+ background: #2d2d2d;
279
+ color: #888;
280
+ }
281
+
282
+ .form-actions {
283
+ display: flex;
284
+ gap: 8px;
285
+ margin-top: 16px;
286
+ }
287
+
288
+ .btn-primary {
289
+ background: #28a745;
290
+ color: white;
291
+ border: none;
292
+ padding: 10px 16px;
293
+ border-radius: 4px;
294
+ font-size: 12px;
295
+ font-weight: 600;
296
+ cursor: pointer;
297
+ flex: 1;
298
+ }
299
+
300
+ .btn-primary:hover {
301
+ background: #218838;
302
+ }
303
+
304
+ .btn-primary:disabled {
305
+ background: #6c757d;
306
+ cursor: not-allowed;
307
+ }
308
+
309
+ .btn-cancel {
310
+ background: #4d4d4d;
311
+ color: #e0e0e0;
312
+ border: none;
313
+ padding: 10px 16px;
314
+ border-radius: 4px;
315
+ font-size: 12px;
316
+ cursor: pointer;
317
+ }
318
+
319
+ .btn-cancel:hover {
320
+ background: #5d5d5d;
321
+ }
322
+
323
+ /* Props tree */
324
+ .props-tree {
325
+ background: #2d2d2d;
326
+ border-radius: 4px;
327
+ padding: 10px;
328
+ font-family: 'Monaco', 'Menlo', monospace;
329
+ font-size: 11px;
330
+ overflow-x: auto;
331
+ max-height: 200px;
332
+ }
333
+
334
+ .prop-item {
335
+ padding: 2px 0;
336
+ }
337
+
338
+ .prop-key {
339
+ color: #9cdcfe;
340
+ }
341
+
342
+ .prop-value {
343
+ color: #ce9178;
344
+ }
345
+
346
+ .prop-value.number {
347
+ color: #b5cea8;
348
+ }
349
+
350
+ .prop-value.boolean {
351
+ color: #569cd6;
352
+ }
353
+
354
+ /* Status messages */
355
+ .status-message {
356
+ padding: 10px 12px;
357
+ border-radius: 4px;
358
+ font-size: 12px;
359
+ margin-top: 12px;
360
+ }
361
+
362
+ .status-message.success {
363
+ background: #28a74520;
364
+ border: 1px solid #28a745;
365
+ color: #28a745;
366
+ }
367
+
368
+ .status-message.error {
369
+ background: #dc354520;
370
+ border: 1px solid #dc3545;
371
+ color: #dc3545;
372
+ }
373
+
374
+ .status-message.info {
375
+ background: #17a2b820;
376
+ border: 1px solid #17a2b8;
377
+ color: #17a2b8;
378
+ }
379
+
380
+ /* Form header for edit form */
381
+ .form-header {
382
+ display: flex;
383
+ align-items: center;
384
+ gap: 8px;
385
+ margin-bottom: 16px;
386
+ padding-bottom: 12px;
387
+ border-bottom: 1px solid #3d3d3d;
388
+ }
389
+
390
+ .form-badge {
391
+ padding: 4px 8px;
392
+ border-radius: 4px;
393
+ font-size: 11px;
394
+ font-weight: 600;
395
+ font-family: 'Monaco', 'Menlo', monospace;
396
+ }
397
+
398
+ .form-badge.getString {
399
+ background: #61dafb20;
400
+ border: 1px solid #61dafb;
401
+ color: #61dafb;
402
+ }
403
+
404
+ .form-subtitle {
405
+ font-size: 12px;
406
+ color: #888;
407
+ }
408
+
409
+ /* String item badges */
410
+ .string-badge {
411
+ padding: 2px 6px;
412
+ border-radius: 3px;
413
+ font-size: 9px;
414
+ font-weight: 600;
415
+ margin-left: 8px;
416
+ }
417
+
418
+ .string-badge.extracted {
419
+ background: #61dafb30;
420
+ color: #61dafb;
421
+ }
422
+
423
+ .string-badge.raw {
424
+ background: #ffc10730;
425
+ color: #ffc107;
426
+ }
427
+
428
+ .string-badge.dynamic {
429
+ background: #9c27b030;
430
+ color: #ce93d8;
431
+ }
432
+
433
+ .string-item.extracted {
434
+ border-left: 3px solid #61dafb;
435
+ }
436
+
437
+ .string-item.dynamic {
438
+ border-left: 3px solid #9c27b0;
439
+ opacity: 0.85;
440
+ }
441
+
442
+ .string-item.dynamic:hover {
443
+ opacity: 1;
444
+ }
445
+
446
+ .string-content {
447
+ flex: 1;
448
+ display: flex;
449
+ flex-direction: column;
450
+ gap: 2px;
451
+ overflow: hidden;
452
+ }
453
+
454
+ .string-expression {
455
+ font-size: 10px;
456
+ color: #ce93d8;
457
+ font-family: 'Monaco', 'Menlo', monospace;
458
+ overflow: hidden;
459
+ text-overflow: ellipsis;
460
+ white-space: nowrap;
461
+ }
462
+
463
+ .string-item.extracted .string-actions .action-btn {
464
+ background: #4d4d4d;
465
+ color: #e0e0e0;
466
+ }
467
+
468
+ .string-item.extracted .string-actions .action-btn:hover {
469
+ background: #5d5d5d;
470
+ }
471
+
472
+ /* Empty state */
473
+ .empty-state {
474
+ display: flex;
475
+ flex-direction: column;
476
+ align-items: center;
477
+ justify-content: center;
478
+ height: 100%;
479
+ color: #888;
480
+ text-align: center;
481
+ padding: 40px;
482
+ }
483
+
484
+ .empty-state-icon {
485
+ font-size: 48px;
486
+ margin-bottom: 16px;
487
+ opacity: 0.5;
488
+ }
489
+
490
+ .empty-state-title {
491
+ font-size: 14px;
492
+ font-weight: 600;
493
+ margin-bottom: 8px;
494
+ color: #aaa;
495
+ }
496
+
497
+ .empty-state-text {
498
+ font-size: 12px;
499
+ line-height: 1.5;
500
+ }
501
+
502
+ .kbd {
503
+ background: #3d3d3d;
504
+ padding: 2px 6px;
505
+ border-radius: 3px;
506
+ font-family: 'Monaco', 'Menlo', monospace;
507
+ font-size: 10px;
508
+ }
509
+
510
+ /* Scrollbar styling */
511
+ ::-webkit-scrollbar {
512
+ width: 8px;
513
+ height: 8px;
514
+ }
515
+
516
+ ::-webkit-scrollbar-track {
517
+ background: #1e1e1e;
518
+ }
519
+
520
+ ::-webkit-scrollbar-thumb {
521
+ background: #4d4d4d;
522
+ border-radius: 4px;
523
+ }
524
+
525
+ ::-webkit-scrollbar-thumb:hover {
526
+ background: #5d5d5d;
527
+ }
528
+
529
+ /* Hidden class */
530
+ .hidden {
531
+ display: none !important;
532
+ }
533
+ </style>
534
+ </head>
535
+ <body>
536
+ <div class="toolbar">
537
+ <span class="toolbar-title">GxP Component Inspector</span>
538
+ <div class="status-indicator" id="statusIndicator" title="Disconnected"></div>
539
+ <button class="toolbar-btn" id="selectBtn">
540
+ <span>Select Element</span>
541
+ </button>
542
+ <button class="toolbar-btn" id="refreshBtn">Refresh</button>
543
+ </div>
544
+
545
+ <div class="main-content">
546
+ <div class="inspector">
547
+ <!-- Empty state shown when no component selected -->
548
+ <div class="empty-state" id="emptyState">
549
+ <div class="empty-state-icon">&#128269;</div>
550
+ <div class="empty-state-title">No Component Selected</div>
551
+ <div class="empty-state-text">
552
+ Click <strong>"Select Element"</strong> then click on any element in the page to inspect it.<br><br>
553
+ Or use the keyboard shortcut <span class="kbd">Ctrl+Shift+I</span> to toggle element selection.
554
+ </div>
555
+ </div>
556
+
557
+ <!-- Inspector content shown when component is selected -->
558
+ <div class="hidden" id="inspectorContent">
559
+ <div class="inspector-header">
560
+ <div class="component-name" id="componentName">&lt;Unknown&gt;</div>
561
+ <div class="component-file" id="componentFile">No file info</div>
562
+ </div>
563
+
564
+ <div class="inspector-body">
565
+ <!-- Text content section -->
566
+ <div class="section" id="stringsSection">
567
+ <div class="section-title">
568
+ Text Content
569
+ <span class="count" id="stringsCount">0</span>
570
+ </div>
571
+ <div class="strings-list" id="stringsList"></div>
572
+ </div>
573
+
574
+ <!-- Extract form (hidden by default) -->
575
+ <div class="extract-form hidden" id="extractForm">
576
+ <div class="form-row">
577
+ <label class="form-label">Original Text</label>
578
+ <input type="text" class="form-input" id="extractText" readonly>
579
+ </div>
580
+ <div class="form-row">
581
+ <label class="form-label">String Key</label>
582
+ <input type="text" class="form-input" id="extractKey" placeholder="e.g., welcome_title">
583
+ </div>
584
+ <div class="form-row">
585
+ <label class="form-label">Source File</label>
586
+ <input type="text" class="form-input" id="extractFile" readonly>
587
+ </div>
588
+ <div class="form-actions">
589
+ <button class="btn-cancel" id="cancelExtract">Cancel</button>
590
+ <button class="btn-primary" id="doExtract">Extract to gxp-string</button>
591
+ </div>
592
+ <div class="status-message hidden" id="extractStatus"></div>
593
+ </div>
594
+
595
+ <!-- Edit form for existing gxp-string attributes (hidden by default) -->
596
+ <div class="extract-form hidden" id="editForm">
597
+ <div class="form-header">
598
+ <span class="form-badge getString">gxp-string</span>
599
+ <span class="form-subtitle">This text is already extracted</span>
600
+ </div>
601
+ <div class="form-row">
602
+ <label class="form-label">String Key</label>
603
+ <input type="text" class="form-input" id="editKey" placeholder="e.g., welcome_title">
604
+ </div>
605
+ <div class="form-row">
606
+ <label class="form-label">Default Value</label>
607
+ <input type="text" class="form-input" id="editValue" placeholder="The displayed text">
608
+ </div>
609
+ <div class="form-row">
610
+ <label class="form-label">Source File</label>
611
+ <input type="text" class="form-input" id="editFile" readonly>
612
+ </div>
613
+ <div class="form-actions">
614
+ <button class="btn-cancel" id="cancelEdit">Cancel</button>
615
+ <button class="btn-primary" id="doEdit">Update gxp-string</button>
616
+ </div>
617
+ <div class="status-message hidden" id="editStatus"></div>
618
+ </div>
619
+
620
+ <!-- Props section -->
621
+ <div class="section" id="propsSection">
622
+ <div class="section-title">Props</div>
623
+ <div class="props-tree" id="propsTree"></div>
624
+ </div>
625
+
626
+ <!-- Data section -->
627
+ <div class="section hidden" id="dataSection">
628
+ <div class="section-title">Data</div>
629
+ <div class="props-tree" id="dataTree"></div>
630
+ </div>
631
+ </div>
632
+ </div>
633
+ </div>
634
+ </div>
635
+
636
+ <script src="panel.js"></script>
637
+ </body>
638
+ </html>