@gxp-dev/tools 2.0.62 → 2.0.64

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 (182) hide show
  1. package/README.md +32 -31
  2. package/bin/gx-devtools.js +74 -54
  3. package/bin/lib/cli.js +23 -21
  4. package/bin/lib/commands/add-dependency.js +366 -325
  5. package/bin/lib/commands/assets.js +137 -139
  6. package/bin/lib/commands/build.js +169 -174
  7. package/bin/lib/commands/datastore.js +181 -183
  8. package/bin/lib/commands/dev.js +127 -131
  9. package/bin/lib/commands/extensions.js +147 -149
  10. package/bin/lib/commands/extract-config.js +73 -67
  11. package/bin/lib/commands/index.js +12 -12
  12. package/bin/lib/commands/init.js +342 -240
  13. package/bin/lib/commands/publish.js +69 -75
  14. package/bin/lib/commands/socket.js +69 -69
  15. package/bin/lib/commands/ssl.js +14 -14
  16. package/bin/lib/constants.js +10 -24
  17. package/bin/lib/tui/App.tsx +761 -705
  18. package/bin/lib/tui/components/AIPanel.tsx +191 -171
  19. package/bin/lib/tui/components/CommandInput.tsx +394 -343
  20. package/bin/lib/tui/components/GeminiPanel.tsx +175 -151
  21. package/bin/lib/tui/components/Header.tsx +23 -21
  22. package/bin/lib/tui/components/LogPanel.tsx +244 -220
  23. package/bin/lib/tui/components/TabBar.tsx +50 -48
  24. package/bin/lib/tui/components/WelcomeScreen.tsx +126 -71
  25. package/bin/lib/tui/index.tsx +37 -39
  26. package/bin/lib/tui/services/AIService.ts +518 -462
  27. package/bin/lib/tui/services/ExtensionService.ts +140 -129
  28. package/bin/lib/tui/services/GeminiService.ts +367 -337
  29. package/bin/lib/tui/services/ServiceManager.ts +344 -322
  30. package/bin/lib/tui/services/SocketService.ts +168 -168
  31. package/bin/lib/tui/services/ViteService.ts +88 -88
  32. package/bin/lib/tui/services/index.ts +47 -22
  33. package/bin/lib/utils/ai-scaffold.js +291 -280
  34. package/bin/lib/utils/extract-config.js +157 -140
  35. package/bin/lib/utils/files.js +82 -86
  36. package/bin/lib/utils/index.js +7 -7
  37. package/bin/lib/utils/paths.js +34 -34
  38. package/bin/lib/utils/prompts.js +194 -169
  39. package/bin/lib/utils/ssl.js +79 -81
  40. package/browser-extensions/README.md +0 -1
  41. package/browser-extensions/chrome/background.js +244 -237
  42. package/browser-extensions/chrome/content.js +32 -29
  43. package/browser-extensions/chrome/devtools.html +7 -7
  44. package/browser-extensions/chrome/devtools.js +19 -19
  45. package/browser-extensions/chrome/inspector.js +802 -767
  46. package/browser-extensions/chrome/manifest.json +71 -63
  47. package/browser-extensions/chrome/panel.html +674 -636
  48. package/browser-extensions/chrome/panel.js +722 -712
  49. package/browser-extensions/chrome/popup.html +586 -543
  50. package/browser-extensions/chrome/popup.js +282 -244
  51. package/browser-extensions/chrome/rules.json +1 -1
  52. package/browser-extensions/chrome/test-chrome.html +216 -136
  53. package/browser-extensions/chrome/test-mixed-content.html +284 -189
  54. package/browser-extensions/chrome/test-uri-pattern.html +221 -198
  55. package/browser-extensions/firefox/README.md +9 -6
  56. package/browser-extensions/firefox/background.js +221 -218
  57. package/browser-extensions/firefox/content.js +55 -52
  58. package/browser-extensions/firefox/debug-errors.html +386 -228
  59. package/browser-extensions/firefox/debug-https.html +153 -105
  60. package/browser-extensions/firefox/devtools.html +7 -7
  61. package/browser-extensions/firefox/devtools.js +23 -20
  62. package/browser-extensions/firefox/inspector.js +802 -767
  63. package/browser-extensions/firefox/manifest.json +68 -68
  64. package/browser-extensions/firefox/panel.html +674 -636
  65. package/browser-extensions/firefox/panel.js +722 -712
  66. package/browser-extensions/firefox/popup.html +572 -535
  67. package/browser-extensions/firefox/popup.js +281 -236
  68. package/browser-extensions/firefox/test-gramercy.html +170 -125
  69. package/browser-extensions/firefox/test-imports.html +59 -55
  70. package/browser-extensions/firefox/test-masking.html +231 -140
  71. package/browser-extensions/firefox/test-uri-pattern.html +221 -198
  72. package/dist/tui/App.d.ts +1 -1
  73. package/dist/tui/App.d.ts.map +1 -1
  74. package/dist/tui/App.js +154 -150
  75. package/dist/tui/App.js.map +1 -1
  76. package/dist/tui/components/AIPanel.d.ts.map +1 -1
  77. package/dist/tui/components/AIPanel.js +42 -35
  78. package/dist/tui/components/AIPanel.js.map +1 -1
  79. package/dist/tui/components/CommandInput.d.ts +1 -1
  80. package/dist/tui/components/CommandInput.d.ts.map +1 -1
  81. package/dist/tui/components/CommandInput.js +92 -62
  82. package/dist/tui/components/CommandInput.js.map +1 -1
  83. package/dist/tui/components/GeminiPanel.d.ts.map +1 -1
  84. package/dist/tui/components/GeminiPanel.js +37 -30
  85. package/dist/tui/components/GeminiPanel.js.map +1 -1
  86. package/dist/tui/components/Header.d.ts.map +1 -1
  87. package/dist/tui/components/Header.js +1 -1
  88. package/dist/tui/components/Header.js.map +1 -1
  89. package/dist/tui/components/LogPanel.d.ts +1 -1
  90. package/dist/tui/components/LogPanel.d.ts.map +1 -1
  91. package/dist/tui/components/LogPanel.js +26 -24
  92. package/dist/tui/components/LogPanel.js.map +1 -1
  93. package/dist/tui/components/TabBar.d.ts +2 -2
  94. package/dist/tui/components/TabBar.d.ts.map +1 -1
  95. package/dist/tui/components/TabBar.js +11 -11
  96. package/dist/tui/components/TabBar.js.map +1 -1
  97. package/dist/tui/components/WelcomeScreen.d.ts.map +1 -1
  98. package/dist/tui/components/WelcomeScreen.js +6 -6
  99. package/dist/tui/components/WelcomeScreen.js.map +1 -1
  100. package/dist/tui/index.d.ts.map +1 -1
  101. package/dist/tui/index.js +8 -8
  102. package/dist/tui/index.js.map +1 -1
  103. package/dist/tui/services/AIService.d.ts +2 -2
  104. package/dist/tui/services/AIService.d.ts.map +1 -1
  105. package/dist/tui/services/AIService.js +165 -125
  106. package/dist/tui/services/AIService.js.map +1 -1
  107. package/dist/tui/services/ExtensionService.d.ts +1 -1
  108. package/dist/tui/services/ExtensionService.d.ts.map +1 -1
  109. package/dist/tui/services/ExtensionService.js +33 -26
  110. package/dist/tui/services/ExtensionService.js.map +1 -1
  111. package/dist/tui/services/GeminiService.d.ts +1 -1
  112. package/dist/tui/services/GeminiService.d.ts.map +1 -1
  113. package/dist/tui/services/GeminiService.js +87 -76
  114. package/dist/tui/services/GeminiService.js.map +1 -1
  115. package/dist/tui/services/ServiceManager.d.ts +3 -3
  116. package/dist/tui/services/ServiceManager.d.ts.map +1 -1
  117. package/dist/tui/services/ServiceManager.js +72 -58
  118. package/dist/tui/services/ServiceManager.js.map +1 -1
  119. package/dist/tui/services/SocketService.d.ts.map +1 -1
  120. package/dist/tui/services/SocketService.js +32 -32
  121. package/dist/tui/services/SocketService.js.map +1 -1
  122. package/dist/tui/services/ViteService.d.ts.map +1 -1
  123. package/dist/tui/services/ViteService.js +26 -28
  124. package/dist/tui/services/ViteService.js.map +1 -1
  125. package/dist/tui/services/index.d.ts +6 -6
  126. package/dist/tui/services/index.d.ts.map +1 -1
  127. package/dist/tui/services/index.js +6 -6
  128. package/dist/tui/services/index.js.map +1 -1
  129. package/mcp/gxp-api-server.js +83 -81
  130. package/package.json +109 -93
  131. package/runtime/PortalContainer.vue +258 -234
  132. package/runtime/dev-tools/DevToolsModal.vue +153 -155
  133. package/runtime/dev-tools/LayoutSwitcher.vue +144 -140
  134. package/runtime/dev-tools/MockDataEditor.vue +456 -433
  135. package/runtime/dev-tools/SocketSimulator.vue +379 -371
  136. package/runtime/dev-tools/StoreInspector.vue +517 -455
  137. package/runtime/dev-tools/index.js +5 -5
  138. package/runtime/fallback-layouts/PrivateLayout.vue +2 -2
  139. package/runtime/fallback-layouts/PublicLayout.vue +2 -2
  140. package/runtime/fallback-layouts/SystemLayout.vue +2 -2
  141. package/runtime/gxpStringsPlugin.js +159 -134
  142. package/runtime/index.html +17 -19
  143. package/runtime/main.js +24 -22
  144. package/runtime/mock-api/auth-middleware.js +15 -15
  145. package/runtime/mock-api/image-generator.js +46 -46
  146. package/runtime/mock-api/index.js +55 -55
  147. package/runtime/mock-api/response-generator.js +116 -105
  148. package/runtime/mock-api/route-generator.js +107 -84
  149. package/runtime/mock-api/socket-triggers.js +94 -93
  150. package/runtime/mock-api/spec-loader.js +79 -80
  151. package/runtime/package.json +3 -0
  152. package/runtime/server.js +68 -68
  153. package/runtime/stores/gxpPortalConfigStore.js +204 -186
  154. package/runtime/stores/index.js +2 -2
  155. package/runtime/vite-inspector-plugin.js +858 -707
  156. package/runtime/vite-source-tracker-plugin.js +132 -113
  157. package/runtime/vite.config.js +207 -132
  158. package/scripts/launch-chrome.js +41 -41
  159. package/scripts/pack-chrome.js +38 -39
  160. package/socket-events/AiSessionMessageCreated.json +17 -17
  161. package/socket-events/SocialStreamPostCreated.json +23 -23
  162. package/socket-events/SocialStreamPostVariantCompleted.json +22 -22
  163. package/template/.claude/agents/gxp-developer.md +100 -99
  164. package/template/.claude/settings.json +7 -7
  165. package/template/AGENTS.md +30 -23
  166. package/template/GEMINI.md +20 -20
  167. package/template/README.md +70 -53
  168. package/template/app-manifest.json +2 -4
  169. package/template/configuration.json +10 -10
  170. package/template/default-styling.css +1 -1
  171. package/template/index.html +18 -20
  172. package/template/main.js +24 -22
  173. package/template/src/DemoPage.vue +415 -362
  174. package/template/src/Plugin.vue +76 -85
  175. package/template/src/stores/index.js +3 -3
  176. package/template/src/stores/test-data.json +164 -172
  177. package/template/theme-layouts/AdditionalStyling.css +50 -50
  178. package/template/theme-layouts/PrivateLayout.vue +8 -12
  179. package/template/theme-layouts/PublicLayout.vue +8 -12
  180. package/template/theme-layouts/SystemLayout.vue +8 -12
  181. package/template/vite.extend.js +45 -0
  182. package/template/vite.config.js +0 -409
@@ -1,331 +1,381 @@
1
1
  <template>
2
- <div class="store-inspector">
3
- <div class="inspector-section">
4
- <h3 class="section-title" @click="toggleSection('pluginVars')">
5
- <span class="toggle-icon">{{ expandedSections.pluginVars ? '▼' : '▶' }}</span>
6
- Plugin Variables
7
- <span class="item-count">{{ Object.keys(store.pluginVars || {}).length }}</span>
8
- </h3>
9
- <div v-if="expandedSections.pluginVars" class="section-content">
10
- <div v-if="Object.keys(store.pluginVars || {}).length === 0" class="empty-state">
11
- No plugin variables defined
12
- </div>
13
- <div v-else class="property-list">
14
- <div
15
- v-for="(value, key) in store.pluginVars"
16
- :key="key"
17
- class="property-item"
18
- @mouseenter="highlightElements('gxp-settings', key)"
19
- @mouseleave="clearHighlight()"
20
- >
21
- <span class="property-key">{{ key }}</span>
22
- <input
23
- v-if="editingKey === `pluginVars.${key}`"
24
- v-model="editValue"
25
- class="property-input"
26
- @blur="saveEdit('pluginVars', key)"
27
- @keydown.enter="saveEdit('pluginVars', key)"
28
- @keydown.escape="cancelEdit"
29
- ref="editInput"
30
- />
31
- <span
32
- v-else
33
- class="property-value"
34
- :class="getValueType(value)"
35
- @dblclick="startEdit('pluginVars', key, value)"
36
- :title="'Double-click to edit'"
37
- >
38
- {{ formatValue(value) }}
39
- </span>
40
- </div>
41
- </div>
42
- </div>
43
- </div>
44
-
45
- <div class="inspector-section">
46
- <h3 class="section-title" @click="toggleSection('stringsList')">
47
- <span class="toggle-icon">{{ expandedSections.stringsList ? '▼' : '▶' }}</span>
48
- Strings List
49
- <span class="item-count">{{ Object.keys(store.stringsList || {}).length }}</span>
50
- </h3>
51
- <div v-if="expandedSections.stringsList" class="section-content">
52
- <div v-if="Object.keys(store.stringsList || {}).length === 0" class="empty-state">
53
- No strings defined
54
- </div>
55
- <div v-else class="property-list">
56
- <div
57
- v-for="(value, key) in store.stringsList"
58
- :key="key"
59
- class="property-item"
60
- @mouseenter="highlightElements('gxp-string', key)"
61
- @mouseleave="clearHighlight()"
62
- >
63
- <span class="property-key">{{ key }}</span>
64
- <input
65
- v-if="editingKey === `stringsList.${key}`"
66
- v-model="editValue"
67
- class="property-input"
68
- @blur="saveEdit('stringsList', key)"
69
- @keydown.enter="saveEdit('stringsList', key)"
70
- @keydown.escape="cancelEdit"
71
- />
72
- <span
73
- v-else
74
- class="property-value string"
75
- @dblclick="startEdit('stringsList', key, value)"
76
- :title="'Double-click to edit'"
77
- >
78
- "{{ value }}"
79
- </span>
80
- </div>
81
- </div>
82
- </div>
83
- </div>
84
-
85
- <div class="inspector-section">
86
- <h3 class="section-title" @click="toggleSection('assetList')">
87
- <span class="toggle-icon">{{ expandedSections.assetList ? '▼' : '▶' }}</span>
88
- Asset List
89
- <span class="item-count">{{ Object.keys(store.assetList || {}).length }}</span>
90
- </h3>
91
- <div v-if="expandedSections.assetList" class="section-content">
92
- <div v-if="Object.keys(store.assetList || {}).length === 0" class="empty-state">
93
- No assets defined
94
- </div>
95
- <div v-else class="property-list">
96
- <div
97
- v-for="(value, key) in store.assetList"
98
- :key="key"
99
- class="property-item asset-item"
100
- @mouseenter="highlightElements('gxp-src', key)"
101
- @mouseleave="clearHighlight()"
102
- >
103
- <span class="property-key">{{ key }}</span>
104
- <div class="asset-preview">
105
- <img
106
- v-if="isImageUrl(value)"
107
- :src="value"
108
- class="asset-thumbnail"
109
- @error="(e) => e.target.style.display = 'none'"
110
- />
111
- <span class="property-value string" :title="value">
112
- {{ truncateUrl(value) }}
113
- </span>
114
- </div>
115
- </div>
116
- </div>
117
- </div>
118
- </div>
119
-
120
- <div class="inspector-section">
121
- <h3 class="section-title" @click="toggleSection('triggerState')">
122
- <span class="toggle-icon">{{ expandedSections.triggerState ? '▼' : '▶' }}</span>
123
- Trigger State
124
- <span class="item-count">{{ Object.keys(store.triggerState || {}).length }}</span>
125
- </h3>
126
- <div v-if="expandedSections.triggerState" class="section-content">
127
- <div v-if="Object.keys(store.triggerState || {}).length === 0" class="empty-state">
128
- No trigger state defined
129
- </div>
130
- <div v-else class="property-list">
131
- <div
132
- v-for="(value, key) in store.triggerState"
133
- :key="key"
134
- class="property-item"
135
- @mouseenter="highlightElements('gxp-state', key)"
136
- @mouseleave="clearHighlight()"
137
- >
138
- <span class="property-key">{{ key }}</span>
139
- <span class="property-value" :class="getValueType(value)">
140
- {{ formatValue(value) }}
141
- </span>
142
- </div>
143
- </div>
144
- </div>
145
- </div>
146
-
147
- <div class="inspector-section">
148
- <h3 class="section-title" @click="toggleSection('dependencyList')">
149
- <span class="toggle-icon">{{ expandedSections.dependencyList ? '▼' : '▶' }}</span>
150
- Dependencies
151
- <span class="item-count">{{ getDependencyCount() }}</span>
152
- </h3>
153
- <div v-if="expandedSections.dependencyList" class="section-content">
154
- <div v-if="getDependencyCount() === 0" class="empty-state">
155
- No dependencies defined
156
- </div>
157
- <div v-else class="property-list">
158
- <div
159
- v-for="(value, key) in store.dependencyList"
160
- :key="key"
161
- class="property-item"
162
- >
163
- <span class="property-key">{{ key }}</span>
164
- <input
165
- v-if="editingKey === `dependencyList.${key}`"
166
- v-model="editValue"
167
- class="property-input"
168
- @blur="saveEdit('dependencyList', key)"
169
- @keydown.enter="saveEdit('dependencyList', key)"
170
- @keydown.escape="cancelEdit"
171
- />
172
- <span
173
- v-else
174
- class="property-value string"
175
- @dblclick="startEdit('dependencyList', key, value)"
176
- :title="'Double-click to edit'"
177
- >
178
- "{{ value }}"
179
- </span>
180
- </div>
181
- </div>
182
- </div>
183
- </div>
184
-
185
- <div class="inspector-actions">
186
- <button class="action-btn" @click="refreshStore" title="Refresh store data">
187
- Refresh
188
- </button>
189
- <button class="action-btn" @click="copyStoreToClipboard" title="Copy store state to clipboard">
190
- Copy JSON
191
- </button>
192
- </div>
193
- </div>
2
+ <div class="store-inspector">
3
+ <div class="inspector-section">
4
+ <h3 class="section-title" @click="toggleSection('pluginVars')">
5
+ <span class="toggle-icon">{{
6
+ expandedSections.pluginVars ? "▼" : "▶"
7
+ }}</span>
8
+ Plugin Variables
9
+ <span class="item-count">{{
10
+ Object.keys(store.pluginVars || {}).length
11
+ }}</span>
12
+ </h3>
13
+ <div v-if="expandedSections.pluginVars" class="section-content">
14
+ <div
15
+ v-if="Object.keys(store.pluginVars || {}).length === 0"
16
+ class="empty-state"
17
+ >
18
+ No plugin variables defined
19
+ </div>
20
+ <div v-else class="property-list">
21
+ <div
22
+ v-for="(value, key) in store.pluginVars"
23
+ :key="key"
24
+ class="property-item"
25
+ @mouseenter="highlightElements('gxp-settings', key)"
26
+ @mouseleave="clearHighlight()"
27
+ >
28
+ <span class="property-key">{{ key }}</span>
29
+ <input
30
+ v-if="editingKey === `pluginVars.${key}`"
31
+ v-model="editValue"
32
+ class="property-input"
33
+ @blur="saveEdit('pluginVars', key)"
34
+ @keydown.enter="saveEdit('pluginVars', key)"
35
+ @keydown.escape="cancelEdit"
36
+ ref="editInput"
37
+ />
38
+ <span
39
+ v-else
40
+ class="property-value"
41
+ :class="getValueType(value)"
42
+ @dblclick="startEdit('pluginVars', key, value)"
43
+ :title="'Double-click to edit'"
44
+ >
45
+ {{ formatValue(value) }}
46
+ </span>
47
+ </div>
48
+ </div>
49
+ </div>
50
+ </div>
51
+
52
+ <div class="inspector-section">
53
+ <h3 class="section-title" @click="toggleSection('stringsList')">
54
+ <span class="toggle-icon">{{
55
+ expandedSections.stringsList ? "" : "▶"
56
+ }}</span>
57
+ Strings List
58
+ <span class="item-count">{{
59
+ Object.keys(store.stringsList || {}).length
60
+ }}</span>
61
+ </h3>
62
+ <div v-if="expandedSections.stringsList" class="section-content">
63
+ <div
64
+ v-if="Object.keys(store.stringsList || {}).length === 0"
65
+ class="empty-state"
66
+ >
67
+ No strings defined
68
+ </div>
69
+ <div v-else class="property-list">
70
+ <div
71
+ v-for="(value, key) in store.stringsList"
72
+ :key="key"
73
+ class="property-item"
74
+ @mouseenter="highlightElements('gxp-string', key)"
75
+ @mouseleave="clearHighlight()"
76
+ >
77
+ <span class="property-key">{{ key }}</span>
78
+ <input
79
+ v-if="editingKey === `stringsList.${key}`"
80
+ v-model="editValue"
81
+ class="property-input"
82
+ @blur="saveEdit('stringsList', key)"
83
+ @keydown.enter="saveEdit('stringsList', key)"
84
+ @keydown.escape="cancelEdit"
85
+ />
86
+ <span
87
+ v-else
88
+ class="property-value string"
89
+ @dblclick="startEdit('stringsList', key, value)"
90
+ :title="'Double-click to edit'"
91
+ >
92
+ "{{ value }}"
93
+ </span>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ </div>
98
+
99
+ <div class="inspector-section">
100
+ <h3 class="section-title" @click="toggleSection('assetList')">
101
+ <span class="toggle-icon">{{
102
+ expandedSections.assetList ? "▼" : "▶"
103
+ }}</span>
104
+ Asset List
105
+ <span class="item-count">{{
106
+ Object.keys(store.assetList || {}).length
107
+ }}</span>
108
+ </h3>
109
+ <div v-if="expandedSections.assetList" class="section-content">
110
+ <div
111
+ v-if="Object.keys(store.assetList || {}).length === 0"
112
+ class="empty-state"
113
+ >
114
+ No assets defined
115
+ </div>
116
+ <div v-else class="property-list">
117
+ <div
118
+ v-for="(value, key) in store.assetList"
119
+ :key="key"
120
+ class="property-item asset-item"
121
+ @mouseenter="highlightElements('gxp-src', key)"
122
+ @mouseleave="clearHighlight()"
123
+ >
124
+ <span class="property-key">{{ key }}</span>
125
+ <div class="asset-preview">
126
+ <img
127
+ v-if="isImageUrl(value)"
128
+ :src="value"
129
+ class="asset-thumbnail"
130
+ @error="(e) => (e.target.style.display = 'none')"
131
+ />
132
+ <span class="property-value string" :title="value">
133
+ {{ truncateUrl(value) }}
134
+ </span>
135
+ </div>
136
+ </div>
137
+ </div>
138
+ </div>
139
+ </div>
140
+
141
+ <div class="inspector-section">
142
+ <h3 class="section-title" @click="toggleSection('triggerState')">
143
+ <span class="toggle-icon">{{
144
+ expandedSections.triggerState ? "▼" : "▶"
145
+ }}</span>
146
+ Trigger State
147
+ <span class="item-count">{{
148
+ Object.keys(store.triggerState || {}).length
149
+ }}</span>
150
+ </h3>
151
+ <div v-if="expandedSections.triggerState" class="section-content">
152
+ <div
153
+ v-if="Object.keys(store.triggerState || {}).length === 0"
154
+ class="empty-state"
155
+ >
156
+ No trigger state defined
157
+ </div>
158
+ <div v-else class="property-list">
159
+ <div
160
+ v-for="(value, key) in store.triggerState"
161
+ :key="key"
162
+ class="property-item"
163
+ @mouseenter="highlightElements('gxp-state', key)"
164
+ @mouseleave="clearHighlight()"
165
+ >
166
+ <span class="property-key">{{ key }}</span>
167
+ <span class="property-value" :class="getValueType(value)">
168
+ {{ formatValue(value) }}
169
+ </span>
170
+ </div>
171
+ </div>
172
+ </div>
173
+ </div>
174
+
175
+ <div class="inspector-section">
176
+ <h3 class="section-title" @click="toggleSection('dependencyList')">
177
+ <span class="toggle-icon">{{
178
+ expandedSections.dependencyList ? "▼" : "▶"
179
+ }}</span>
180
+ Dependencies
181
+ <span class="item-count">{{ getDependencyCount() }}</span>
182
+ </h3>
183
+ <div v-if="expandedSections.dependencyList" class="section-content">
184
+ <div v-if="getDependencyCount() === 0" class="empty-state">
185
+ No dependencies defined
186
+ </div>
187
+ <div v-else class="property-list">
188
+ <div
189
+ v-for="(value, key) in store.dependencyList"
190
+ :key="key"
191
+ class="property-item"
192
+ >
193
+ <span class="property-key">{{ key }}</span>
194
+ <input
195
+ v-if="editingKey === `dependencyList.${key}`"
196
+ v-model="editValue"
197
+ class="property-input"
198
+ @blur="saveEdit('dependencyList', key)"
199
+ @keydown.enter="saveEdit('dependencyList', key)"
200
+ @keydown.escape="cancelEdit"
201
+ />
202
+ <span
203
+ v-else
204
+ class="property-value string"
205
+ @dblclick="startEdit('dependencyList', key, value)"
206
+ :title="'Double-click to edit'"
207
+ >
208
+ "{{ value }}"
209
+ </span>
210
+ </div>
211
+ </div>
212
+ </div>
213
+ </div>
214
+
215
+ <div class="inspector-actions">
216
+ <button
217
+ class="action-btn"
218
+ @click="refreshStore"
219
+ title="Refresh store data"
220
+ >
221
+ Refresh
222
+ </button>
223
+ <button
224
+ class="action-btn"
225
+ @click="copyStoreToClipboard"
226
+ title="Copy store state to clipboard"
227
+ >
228
+ Copy JSON
229
+ </button>
230
+ </div>
231
+ </div>
194
232
  </template>
195
233
 
196
234
  <script setup>
197
- import { ref, reactive, nextTick, onUnmounted } from 'vue';
235
+ import { ref, reactive, nextTick, onUnmounted } from "vue"
198
236
 
199
237
  const props = defineProps({
200
- store: {
201
- type: Object,
202
- required: true
203
- }
204
- });
238
+ store: {
239
+ type: Object,
240
+ required: true,
241
+ },
242
+ })
205
243
 
206
244
  // Element highlighting
207
- const highlightedElements = ref([]);
208
- const highlightOverlays = ref([]);
245
+ const highlightedElements = ref([])
246
+ const highlightOverlays = ref([])
209
247
 
210
248
  const expandedSections = reactive({
211
- pluginVars: true,
212
- stringsList: false,
213
- assetList: false,
214
- triggerState: false,
215
- dependencyList: false
216
- });
249
+ pluginVars: true,
250
+ stringsList: false,
251
+ assetList: false,
252
+ triggerState: false,
253
+ dependencyList: false,
254
+ })
217
255
 
218
- const editingKey = ref(null);
219
- const editValue = ref('');
256
+ const editingKey = ref(null)
257
+ const editValue = ref("")
220
258
 
221
259
  function toggleSection(section) {
222
- expandedSections[section] = !expandedSections[section];
260
+ expandedSections[section] = !expandedSections[section]
223
261
  }
224
262
 
225
263
  function getValueType(value) {
226
- if (value === null) return 'null';
227
- if (value === undefined) return 'undefined';
228
- if (typeof value === 'boolean') return 'boolean';
229
- if (typeof value === 'number') return 'number';
230
- if (typeof value === 'string') return 'string';
231
- if (Array.isArray(value)) return 'array';
232
- if (typeof value === 'object') return 'object';
233
- return 'unknown';
264
+ if (value === null) return "null"
265
+ if (value === undefined) return "undefined"
266
+ if (typeof value === "boolean") return "boolean"
267
+ if (typeof value === "number") return "number"
268
+ if (typeof value === "string") return "string"
269
+ if (Array.isArray(value)) return "array"
270
+ if (typeof value === "object") return "object"
271
+ return "unknown"
234
272
  }
235
273
 
236
274
  function formatValue(value) {
237
- if (value === null) return 'null';
238
- if (value === undefined) return 'undefined';
239
- if (typeof value === 'boolean') return value ? 'true' : 'false';
240
- if (typeof value === 'number') return value.toString();
241
- if (typeof value === 'string') return `"${value}"`;
242
- if (Array.isArray(value)) return `Array(${value.length})`;
243
- if (typeof value === 'object') return `Object(${Object.keys(value).length})`;
244
- return String(value);
275
+ if (value === null) return "null"
276
+ if (value === undefined) return "undefined"
277
+ if (typeof value === "boolean") return value ? "true" : "false"
278
+ if (typeof value === "number") return value.toString()
279
+ if (typeof value === "string") return `"${value}"`
280
+ if (Array.isArray(value)) return `Array(${value.length})`
281
+ if (typeof value === "object") return `Object(${Object.keys(value).length})`
282
+ return String(value)
245
283
  }
246
284
 
247
285
  function isImageUrl(url) {
248
- if (typeof url !== 'string') return false;
249
- return /\.(jpg|jpeg|png|gif|webp|svg)(\?.*)?$/i.test(url);
286
+ if (typeof url !== "string") return false
287
+ return /\.(jpg|jpeg|png|gif|webp|svg)(\?.*)?$/i.test(url)
250
288
  }
251
289
 
252
290
  function truncateUrl(url, maxLength = 50) {
253
- if (typeof url !== 'string') return url;
254
- if (url.length <= maxLength) return url;
255
- return url.substring(0, maxLength) + '...';
291
+ if (typeof url !== "string") return url
292
+ if (url.length <= maxLength) return url
293
+ return url.substring(0, maxLength) + "..."
256
294
  }
257
295
 
258
296
  function getDependencyCount() {
259
- const deps = props.store.dependencyList;
260
- if (!deps) return 0;
261
- if (Array.isArray(deps)) return deps.length;
262
- return Object.keys(deps).length;
297
+ const deps = props.store.dependencyList
298
+ if (!deps) return 0
299
+ if (Array.isArray(deps)) return deps.length
300
+ return Object.keys(deps).length
263
301
  }
264
302
 
265
303
  function startEdit(section, key, value) {
266
- editingKey.value = `${section}.${key}`;
267
- editValue.value = typeof value === 'string' ? value : JSON.stringify(value);
268
- nextTick(() => {
269
- const input = document.querySelector('.property-input');
270
- if (input) input.focus();
271
- });
304
+ editingKey.value = `${section}.${key}`
305
+ editValue.value = typeof value === "string" ? value : JSON.stringify(value)
306
+ nextTick(() => {
307
+ const input = document.querySelector(".property-input")
308
+ if (input) input.focus()
309
+ })
272
310
  }
273
311
 
274
312
  function saveEdit(section, key) {
275
- if (editingKey.value && props.store[section]) {
276
- let newValue = editValue.value;
277
- // Try to parse as JSON for non-string values
278
- try {
279
- const parsed = JSON.parse(newValue);
280
- newValue = parsed;
281
- } catch {
282
- // Keep as string if not valid JSON
283
- }
284
-
285
- // Use the store's update methods to ensure Vue reactivity triggers properly
286
- // This will update any gxp-string/gxp-src directives that depend on these values
287
- if (section === 'stringsList' && typeof props.store.updateString === 'function') {
288
- props.store.updateString(key, newValue);
289
- } else if (section === 'pluginVars' && typeof props.store.updateSetting === 'function') {
290
- props.store.updateSetting(key, newValue);
291
- } else if (section === 'assetList' && typeof props.store.updateAsset === 'function') {
292
- props.store.updateAsset(key, newValue);
293
- } else if (section === 'triggerState' && typeof props.store.updateState === 'function') {
294
- props.store.updateState(key, newValue);
295
- } else {
296
- // Fallback for older stores without update methods
297
- props.store[section][key] = newValue;
298
- }
299
- console.log(`[DevTools] Updated ${section}.${key}:`, newValue);
300
- }
301
- cancelEdit();
313
+ if (editingKey.value && props.store[section]) {
314
+ let newValue = editValue.value
315
+ // Try to parse as JSON for non-string values
316
+ try {
317
+ const parsed = JSON.parse(newValue)
318
+ newValue = parsed
319
+ } catch {
320
+ // Keep as string if not valid JSON
321
+ }
322
+
323
+ // Use the store's update methods to ensure Vue reactivity triggers properly
324
+ // This will update any gxp-string/gxp-src directives that depend on these values
325
+ if (
326
+ section === "stringsList" &&
327
+ typeof props.store.updateString === "function"
328
+ ) {
329
+ props.store.updateString(key, newValue)
330
+ } else if (
331
+ section === "pluginVars" &&
332
+ typeof props.store.updateSetting === "function"
333
+ ) {
334
+ props.store.updateSetting(key, newValue)
335
+ } else if (
336
+ section === "assetList" &&
337
+ typeof props.store.updateAsset === "function"
338
+ ) {
339
+ props.store.updateAsset(key, newValue)
340
+ } else if (
341
+ section === "triggerState" &&
342
+ typeof props.store.updateState === "function"
343
+ ) {
344
+ props.store.updateState(key, newValue)
345
+ } else {
346
+ // Fallback for older stores without update methods
347
+ props.store[section][key] = newValue
348
+ }
349
+ console.log(`[DevTools] Updated ${section}.${key}:`, newValue)
350
+ }
351
+ cancelEdit()
302
352
  }
303
353
 
304
354
  function cancelEdit() {
305
- editingKey.value = null;
306
- editValue.value = '';
355
+ editingKey.value = null
356
+ editValue.value = ""
307
357
  }
308
358
 
309
359
  function refreshStore() {
310
- // Force reactivity update
311
- console.log('[DevTools] Store refreshed');
360
+ // Force reactivity update
361
+ console.log("[DevTools] Store refreshed")
312
362
  }
313
363
 
314
364
  async function copyStoreToClipboard() {
315
- const storeData = {
316
- pluginVars: props.store.pluginVars,
317
- stringsList: props.store.stringsList,
318
- assetList: props.store.assetList,
319
- triggerState: props.store.triggerState,
320
- dependencyList: props.store.dependencyList
321
- };
322
-
323
- try {
324
- await navigator.clipboard.writeText(JSON.stringify(storeData, null, 2));
325
- console.log('[DevTools] Store data copied to clipboard');
326
- } catch (err) {
327
- console.error('[DevTools] Failed to copy:', err);
328
- }
365
+ const storeData = {
366
+ pluginVars: props.store.pluginVars,
367
+ stringsList: props.store.stringsList,
368
+ assetList: props.store.assetList,
369
+ triggerState: props.store.triggerState,
370
+ dependencyList: props.store.dependencyList,
371
+ }
372
+
373
+ try {
374
+ await navigator.clipboard.writeText(JSON.stringify(storeData, null, 2))
375
+ console.log("[DevTools] Store data copied to clipboard")
376
+ } catch (err) {
377
+ console.error("[DevTools] Failed to copy:", err)
378
+ }
329
379
  }
330
380
 
331
381
  /**
@@ -334,15 +384,15 @@ async function copyStoreToClipboard() {
334
384
  */
335
385
 
336
386
  // CSS class name for our highlight overlay
337
- const HIGHLIGHT_CLASS = 'gxp-devtools-highlight-overlay';
387
+ const HIGHLIGHT_CLASS = "gxp-devtools-highlight-overlay"
338
388
 
339
389
  // Inject highlight styles into the document if not already present
340
390
  function ensureHighlightStyles() {
341
- if (document.getElementById('gxp-devtools-highlight-styles')) return;
391
+ if (document.getElementById("gxp-devtools-highlight-styles")) return
342
392
 
343
- const style = document.createElement('style');
344
- style.id = 'gxp-devtools-highlight-styles';
345
- style.textContent = `
393
+ const style = document.createElement("style")
394
+ style.id = "gxp-devtools-highlight-styles"
395
+ style.textContent = `
346
396
  .${HIGHLIGHT_CLASS} {
347
397
  position: fixed;
348
398
  pointer-events: none;
@@ -371,274 +421,286 @@ function ensureHighlightStyles() {
371
421
  0%, 100% { box-shadow: 0 0 10px rgba(97, 218, 251, 0.5), inset 0 0 10px rgba(97, 218, 251, 0.1); }
372
422
  50% { box-shadow: 0 0 20px rgba(97, 218, 251, 0.8), inset 0 0 15px rgba(97, 218, 251, 0.2); }
373
423
  }
374
- `;
375
- document.head.appendChild(style);
424
+ `
425
+ document.head.appendChild(style)
376
426
  }
377
427
 
378
428
  // Find elements that match a gxp attribute and key
379
429
  function findMatchingElements(attribute, key) {
380
- const elements = [];
381
-
382
- // For stringsList, look for gxp-string attribute (without gxp-settings, gxp-assets, gxp-state)
383
- // For pluginVars (gxp-settings), look for gxp-string with gxp-settings modifier
384
- // For assetList (gxp-src), look for gxp-src attribute
385
- // For triggerState (gxp-state), look for gxp-string or gxp-src with gxp-state modifier
386
-
387
- if (attribute === 'gxp-string') {
388
- // Find elements with gxp-string="key" that don't have modifiers
389
- document.querySelectorAll(`[gxp-string="${key}"]`).forEach(el => {
390
- if (!el.hasAttribute('gxp-settings') && !el.hasAttribute('gxp-assets') && !el.hasAttribute('gxp-state')) {
391
- elements.push(el);
392
- }
393
- });
394
- } else if (attribute === 'gxp-settings') {
395
- // Find elements with gxp-string="key" AND gxp-settings attribute
396
- document.querySelectorAll(`[gxp-string="${key}"][gxp-settings]`).forEach(el => {
397
- elements.push(el);
398
- });
399
- } else if (attribute === 'gxp-src') {
400
- // Find elements with gxp-src="key" that don't have gxp-state modifier
401
- document.querySelectorAll(`[gxp-src="${key}"]`).forEach(el => {
402
- if (!el.hasAttribute('gxp-state')) {
403
- elements.push(el);
404
- }
405
- });
406
- // Also check for gxp-string with gxp-assets modifier
407
- document.querySelectorAll(`[gxp-string="${key}"][gxp-assets]`).forEach(el => {
408
- elements.push(el);
409
- });
410
- } else if (attribute === 'gxp-state') {
411
- // Find elements with gxp-state modifier on either gxp-string or gxp-src
412
- document.querySelectorAll(`[gxp-string="${key}"][gxp-state]`).forEach(el => {
413
- elements.push(el);
414
- });
415
- document.querySelectorAll(`[gxp-src="${key}"][gxp-state]`).forEach(el => {
416
- elements.push(el);
417
- });
418
- }
419
-
420
- return elements;
430
+ const elements = []
431
+
432
+ // For stringsList, look for gxp-string attribute (without gxp-settings, gxp-assets, gxp-state)
433
+ // For pluginVars (gxp-settings), look for gxp-string with gxp-settings modifier
434
+ // For assetList (gxp-src), look for gxp-src attribute
435
+ // For triggerState (gxp-state), look for gxp-string or gxp-src with gxp-state modifier
436
+
437
+ if (attribute === "gxp-string") {
438
+ // Find elements with gxp-string="key" that don't have modifiers
439
+ document.querySelectorAll(`[gxp-string="${key}"]`).forEach((el) => {
440
+ if (
441
+ !el.hasAttribute("gxp-settings") &&
442
+ !el.hasAttribute("gxp-assets") &&
443
+ !el.hasAttribute("gxp-state")
444
+ ) {
445
+ elements.push(el)
446
+ }
447
+ })
448
+ } else if (attribute === "gxp-settings") {
449
+ // Find elements with gxp-string="key" AND gxp-settings attribute
450
+ document
451
+ .querySelectorAll(`[gxp-string="${key}"][gxp-settings]`)
452
+ .forEach((el) => {
453
+ elements.push(el)
454
+ })
455
+ } else if (attribute === "gxp-src") {
456
+ // Find elements with gxp-src="key" that don't have gxp-state modifier
457
+ document.querySelectorAll(`[gxp-src="${key}"]`).forEach((el) => {
458
+ if (!el.hasAttribute("gxp-state")) {
459
+ elements.push(el)
460
+ }
461
+ })
462
+ // Also check for gxp-string with gxp-assets modifier
463
+ document
464
+ .querySelectorAll(`[gxp-string="${key}"][gxp-assets]`)
465
+ .forEach((el) => {
466
+ elements.push(el)
467
+ })
468
+ } else if (attribute === "gxp-state") {
469
+ // Find elements with gxp-state modifier on either gxp-string or gxp-src
470
+ document
471
+ .querySelectorAll(`[gxp-string="${key}"][gxp-state]`)
472
+ .forEach((el) => {
473
+ elements.push(el)
474
+ })
475
+ document.querySelectorAll(`[gxp-src="${key}"][gxp-state]`).forEach((el) => {
476
+ elements.push(el)
477
+ })
478
+ }
479
+
480
+ return elements
421
481
  }
422
482
 
423
483
  // Create overlay for an element
424
484
  function createOverlay(element, key) {
425
- const rect = element.getBoundingClientRect();
426
- const overlay = document.createElement('div');
427
- overlay.className = HIGHLIGHT_CLASS;
428
- overlay.setAttribute('data-gxp-key', key);
429
- overlay.style.top = `${rect.top}px`;
430
- overlay.style.left = `${rect.left}px`;
431
- overlay.style.width = `${rect.width}px`;
432
- overlay.style.height = `${rect.height}px`;
433
- document.body.appendChild(overlay);
434
- return overlay;
485
+ const rect = element.getBoundingClientRect()
486
+ const overlay = document.createElement("div")
487
+ overlay.className = HIGHLIGHT_CLASS
488
+ overlay.setAttribute("data-gxp-key", key)
489
+ overlay.style.top = `${rect.top}px`
490
+ overlay.style.left = `${rect.left}px`
491
+ overlay.style.width = `${rect.width}px`
492
+ overlay.style.height = `${rect.height}px`
493
+ document.body.appendChild(overlay)
494
+ return overlay
435
495
  }
436
496
 
437
497
  // Highlight elements matching a key
438
498
  function highlightElements(attribute, key) {
439
- ensureHighlightStyles();
440
- clearHighlight();
499
+ ensureHighlightStyles()
500
+ clearHighlight()
441
501
 
442
- const elements = findMatchingElements(attribute, key);
443
- highlightedElements.value = elements;
502
+ const elements = findMatchingElements(attribute, key)
503
+ highlightedElements.value = elements
444
504
 
445
- elements.forEach(el => {
446
- const overlay = createOverlay(el, key);
447
- highlightOverlays.value.push(overlay);
448
- });
505
+ elements.forEach((el) => {
506
+ const overlay = createOverlay(el, key)
507
+ highlightOverlays.value.push(overlay)
508
+ })
449
509
 
450
- if (elements.length > 0) {
451
- console.log(`[DevTools] Highlighting ${elements.length} element(s) with ${attribute}="${key}"`);
452
- }
510
+ if (elements.length > 0) {
511
+ console.log(
512
+ `[DevTools] Highlighting ${elements.length} element(s) with ${attribute}="${key}"`,
513
+ )
514
+ }
453
515
  }
454
516
 
455
517
  // Clear all highlight overlays
456
518
  function clearHighlight() {
457
- highlightOverlays.value.forEach(overlay => {
458
- if (overlay && overlay.parentNode) {
459
- overlay.parentNode.removeChild(overlay);
460
- }
461
- });
462
- highlightOverlays.value = [];
463
- highlightedElements.value = [];
519
+ highlightOverlays.value.forEach((overlay) => {
520
+ if (overlay && overlay.parentNode) {
521
+ overlay.parentNode.removeChild(overlay)
522
+ }
523
+ })
524
+ highlightOverlays.value = []
525
+ highlightedElements.value = []
464
526
  }
465
527
 
466
528
  // Clean up on unmount
467
529
  onUnmounted(() => {
468
- clearHighlight();
469
- });
530
+ clearHighlight()
531
+ })
470
532
  </script>
471
533
 
472
534
  <style scoped>
473
535
  .store-inspector {
474
- display: flex;
475
- flex-direction: column;
476
- gap: 8px;
536
+ display: flex;
537
+ flex-direction: column;
538
+ gap: 8px;
477
539
  }
478
540
 
479
541
  .inspector-section {
480
- background: #2d2d2d;
481
- border-radius: 6px;
482
- overflow: hidden;
542
+ background: #2d2d2d;
543
+ border-radius: 6px;
544
+ overflow: hidden;
483
545
  }
484
546
 
485
547
  .section-title {
486
- display: flex;
487
- align-items: center;
488
- gap: 8px;
489
- margin: 0;
490
- padding: 10px 12px;
491
- font-size: 13px;
492
- font-weight: 500;
493
- cursor: pointer;
494
- user-select: none;
495
- transition: background 0.2s;
548
+ display: flex;
549
+ align-items: center;
550
+ gap: 8px;
551
+ margin: 0;
552
+ padding: 10px 12px;
553
+ font-size: 13px;
554
+ font-weight: 500;
555
+ cursor: pointer;
556
+ user-select: none;
557
+ transition: background 0.2s;
496
558
  }
497
559
 
498
560
  .section-title:hover {
499
- background: #3d3d3d;
561
+ background: #3d3d3d;
500
562
  }
501
563
 
502
564
  .toggle-icon {
503
- font-size: 10px;
504
- color: #888;
505
- width: 12px;
565
+ font-size: 10px;
566
+ color: #888;
567
+ width: 12px;
506
568
  }
507
569
 
508
570
  .item-count {
509
- margin-left: auto;
510
- background: #3d3d3d;
511
- padding: 2px 8px;
512
- border-radius: 10px;
513
- font-size: 11px;
514
- color: #888;
571
+ margin-left: auto;
572
+ background: #3d3d3d;
573
+ padding: 2px 8px;
574
+ border-radius: 10px;
575
+ font-size: 11px;
576
+ color: #888;
515
577
  }
516
578
 
517
579
  .section-content {
518
- padding: 8px 12px 12px;
519
- border-top: 1px solid #3d3d3d;
580
+ padding: 8px 12px 12px;
581
+ border-top: 1px solid #3d3d3d;
520
582
  }
521
583
 
522
584
  .empty-state {
523
- color: #666;
524
- font-size: 12px;
525
- font-style: italic;
526
- padding: 8px 0;
585
+ color: #666;
586
+ font-size: 12px;
587
+ font-style: italic;
588
+ padding: 8px 0;
527
589
  }
528
590
 
529
591
  .property-list {
530
- display: flex;
531
- flex-direction: column;
532
- gap: 6px;
592
+ display: flex;
593
+ flex-direction: column;
594
+ gap: 6px;
533
595
  }
534
596
 
535
597
  .property-item {
536
- display: flex;
537
- align-items: flex-start;
538
- gap: 8px;
539
- font-size: 12px;
540
- font-family: 'SF Mono', Monaco, 'Courier New', monospace;
598
+ display: flex;
599
+ align-items: flex-start;
600
+ gap: 8px;
601
+ font-size: 12px;
602
+ font-family: "SF Mono", Monaco, "Courier New", monospace;
541
603
  }
542
604
 
543
605
  .property-key {
544
- color: #9cdcfe;
545
- min-width: 140px;
546
- flex-shrink: 0;
606
+ color: #9cdcfe;
607
+ min-width: 140px;
608
+ flex-shrink: 0;
547
609
  }
548
610
 
549
611
  .property-key::after {
550
- content: ':';
551
- color: #888;
612
+ content: ":";
613
+ color: #888;
552
614
  }
553
615
 
554
616
  .property-value {
555
- word-break: break-all;
556
- cursor: pointer;
557
- padding: 2px 4px;
558
- border-radius: 3px;
559
- transition: background 0.2s;
617
+ word-break: break-all;
618
+ cursor: pointer;
619
+ padding: 2px 4px;
620
+ border-radius: 3px;
621
+ transition: background 0.2s;
560
622
  }
561
623
 
562
624
  .property-value:hover {
563
- background: #3d3d3d;
625
+ background: #3d3d3d;
564
626
  }
565
627
 
566
628
  .property-value.string {
567
- color: #ce9178;
629
+ color: #ce9178;
568
630
  }
569
631
 
570
632
  .property-value.number {
571
- color: #b5cea8;
633
+ color: #b5cea8;
572
634
  }
573
635
 
574
636
  .property-value.boolean {
575
- color: #569cd6;
637
+ color: #569cd6;
576
638
  }
577
639
 
578
640
  .property-value.null,
579
641
  .property-value.undefined {
580
- color: #808080;
581
- font-style: italic;
642
+ color: #808080;
643
+ font-style: italic;
582
644
  }
583
645
 
584
646
  .property-value.object,
585
647
  .property-value.array {
586
- color: #dcdcaa;
648
+ color: #dcdcaa;
587
649
  }
588
650
 
589
651
  .property-input {
590
- flex: 1;
591
- background: #3d3d3d;
592
- border: 1px solid #61dafb;
593
- color: #e0e0e0;
594
- padding: 4px 8px;
595
- border-radius: 3px;
596
- font-family: inherit;
597
- font-size: 12px;
598
- outline: none;
652
+ flex: 1;
653
+ background: #3d3d3d;
654
+ border: 1px solid #61dafb;
655
+ color: #e0e0e0;
656
+ padding: 4px 8px;
657
+ border-radius: 3px;
658
+ font-family: inherit;
659
+ font-size: 12px;
660
+ outline: none;
599
661
  }
600
662
 
601
663
  .asset-item {
602
- flex-direction: column;
603
- gap: 4px;
664
+ flex-direction: column;
665
+ gap: 4px;
604
666
  }
605
667
 
606
668
  .asset-preview {
607
- display: flex;
608
- align-items: center;
609
- gap: 8px;
610
- padding-left: 148px;
669
+ display: flex;
670
+ align-items: center;
671
+ gap: 8px;
672
+ padding-left: 148px;
611
673
  }
612
674
 
613
675
  .asset-thumbnail {
614
- width: 32px;
615
- height: 32px;
616
- object-fit: cover;
617
- border-radius: 4px;
618
- background: #3d3d3d;
676
+ width: 32px;
677
+ height: 32px;
678
+ object-fit: cover;
679
+ border-radius: 4px;
680
+ background: #3d3d3d;
619
681
  }
620
682
 
621
683
  .inspector-actions {
622
- display: flex;
623
- gap: 8px;
624
- margin-top: 12px;
625
- padding-top: 12px;
626
- border-top: 1px solid #3d3d3d;
684
+ display: flex;
685
+ gap: 8px;
686
+ margin-top: 12px;
687
+ padding-top: 12px;
688
+ border-top: 1px solid #3d3d3d;
627
689
  }
628
690
 
629
691
  .action-btn {
630
- background: #3d3d3d;
631
- border: none;
632
- color: #e0e0e0;
633
- padding: 8px 16px;
634
- border-radius: 4px;
635
- cursor: pointer;
636
- font-size: 12px;
637
- transition: all 0.2s;
692
+ background: #3d3d3d;
693
+ border: none;
694
+ color: #e0e0e0;
695
+ padding: 8px 16px;
696
+ border-radius: 4px;
697
+ cursor: pointer;
698
+ font-size: 12px;
699
+ transition: all 0.2s;
638
700
  }
639
701
 
640
702
  .action-btn:hover {
641
- background: #4d4d4d;
642
- color: #61dafb;
703
+ background: #4d4d4d;
704
+ color: #61dafb;
643
705
  }
644
706
  </style>