@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.
- package/.github/workflows/npm-publish.yml +48 -0
- package/CLAUDE.md +400 -0
- package/README.md +247 -0
- package/REFACTOR_PLAN.md +194 -0
- package/bin/gx-devtools.js +87 -0
- package/bin/lib/cli.js +251 -0
- package/bin/lib/commands/assets.js +337 -0
- package/bin/lib/commands/build.js +259 -0
- package/bin/lib/commands/datastore.js +433 -0
- package/bin/lib/commands/dev.js +328 -0
- package/bin/lib/commands/extensions.js +298 -0
- package/bin/lib/commands/index.js +35 -0
- package/bin/lib/commands/init.js +307 -0
- package/bin/lib/commands/publish.js +189 -0
- package/bin/lib/commands/socket.js +158 -0
- package/bin/lib/commands/ssl.js +47 -0
- package/bin/lib/constants.js +120 -0
- package/bin/lib/tui/App.tsx +600 -0
- package/bin/lib/tui/components/CommandInput.tsx +278 -0
- package/bin/lib/tui/components/GeminiPanel.tsx +161 -0
- package/bin/lib/tui/components/Header.tsx +27 -0
- package/bin/lib/tui/components/LogPanel.tsx +122 -0
- package/bin/lib/tui/components/TabBar.tsx +56 -0
- package/bin/lib/tui/components/WelcomeScreen.tsx +80 -0
- package/bin/lib/tui/index.tsx +63 -0
- package/bin/lib/tui/services/ExtensionService.ts +122 -0
- package/bin/lib/tui/services/GeminiService.ts +395 -0
- package/bin/lib/tui/services/ServiceManager.ts +336 -0
- package/bin/lib/tui/services/SocketService.ts +204 -0
- package/bin/lib/tui/services/ViteService.ts +107 -0
- package/bin/lib/tui/services/index.ts +13 -0
- package/bin/lib/utils/files.js +180 -0
- package/bin/lib/utils/index.js +17 -0
- package/bin/lib/utils/paths.js +138 -0
- package/bin/lib/utils/prompts.js +71 -0
- package/bin/lib/utils/ssl.js +233 -0
- package/browser-extensions/README.md +1 -0
- package/browser-extensions/chrome/background.js +857 -0
- package/browser-extensions/chrome/content.js +51 -0
- package/browser-extensions/chrome/devtools.html +9 -0
- package/browser-extensions/chrome/devtools.js +23 -0
- package/browser-extensions/chrome/icons/gx_off_128.png +0 -0
- package/browser-extensions/chrome/icons/gx_off_16.png +0 -0
- package/browser-extensions/chrome/icons/gx_off_32.png +0 -0
- package/browser-extensions/chrome/icons/gx_off_64.png +0 -0
- package/browser-extensions/chrome/icons/gx_on_128.png +0 -0
- package/browser-extensions/chrome/icons/gx_on_16.png +0 -0
- package/browser-extensions/chrome/icons/gx_on_32.png +0 -0
- package/browser-extensions/chrome/icons/gx_on_64.png +0 -0
- package/browser-extensions/chrome/inspector.js +1087 -0
- package/browser-extensions/chrome/manifest.json +70 -0
- package/browser-extensions/chrome/panel.html +638 -0
- package/browser-extensions/chrome/panel.js +862 -0
- package/browser-extensions/chrome/popup.html +399 -0
- package/browser-extensions/chrome/popup.js +515 -0
- package/browser-extensions/chrome/rules.json +1 -0
- package/browser-extensions/chrome/test-chrome.html +145 -0
- package/browser-extensions/chrome/test-mixed-content.html +190 -0
- package/browser-extensions/chrome/test-uri-pattern.html +199 -0
- package/browser-extensions/firefox/README.md +134 -0
- package/browser-extensions/firefox/background.js +804 -0
- package/browser-extensions/firefox/content.js +120 -0
- package/browser-extensions/firefox/debug-errors.html +229 -0
- package/browser-extensions/firefox/debug-https.html +113 -0
- package/browser-extensions/firefox/devtools.html +9 -0
- package/browser-extensions/firefox/devtools.js +24 -0
- package/browser-extensions/firefox/icons/gx_off_128.png +0 -0
- package/browser-extensions/firefox/icons/gx_off_16.png +0 -0
- package/browser-extensions/firefox/icons/gx_off_32.png +0 -0
- package/browser-extensions/firefox/icons/gx_off_64.png +0 -0
- package/browser-extensions/firefox/icons/gx_on_128.png +0 -0
- package/browser-extensions/firefox/icons/gx_on_16.png +0 -0
- package/browser-extensions/firefox/icons/gx_on_32.png +0 -0
- package/browser-extensions/firefox/icons/gx_on_64.png +0 -0
- package/browser-extensions/firefox/inspector.js +1087 -0
- package/browser-extensions/firefox/manifest.json +67 -0
- package/browser-extensions/firefox/panel.html +638 -0
- package/browser-extensions/firefox/panel.js +862 -0
- package/browser-extensions/firefox/popup.html +525 -0
- package/browser-extensions/firefox/popup.js +536 -0
- package/browser-extensions/firefox/test-gramercy.html +126 -0
- package/browser-extensions/firefox/test-imports.html +58 -0
- package/browser-extensions/firefox/test-masking.html +147 -0
- package/browser-extensions/firefox/test-uri-pattern.html +199 -0
- package/docs/DOCUSAURUS_IMPORT.md +378 -0
- package/docs/_category_.json +8 -0
- package/docs/app-manifest.md +272 -0
- package/docs/building-for-platform.md +315 -0
- package/docs/dev-tools.md +291 -0
- package/docs/getting-started.md +180 -0
- package/docs/gxp-store.md +305 -0
- package/docs/index.md +44 -0
- package/package.json +77 -0
- package/runtime/PortalContainer.vue +326 -0
- package/runtime/dev-tools/DevToolsModal.vue +217 -0
- package/runtime/dev-tools/LayoutSwitcher.vue +221 -0
- package/runtime/dev-tools/MockDataEditor.vue +621 -0
- package/runtime/dev-tools/SocketSimulator.vue +562 -0
- package/runtime/dev-tools/StoreInspector.vue +644 -0
- package/runtime/dev-tools/index.js +6 -0
- package/runtime/gxpStringsPlugin.js +428 -0
- package/runtime/index.html +22 -0
- package/runtime/main.js +32 -0
- package/runtime/mock-api/auth-middleware.js +97 -0
- package/runtime/mock-api/image-generator.js +221 -0
- package/runtime/mock-api/index.js +197 -0
- package/runtime/mock-api/response-generator.js +394 -0
- package/runtime/mock-api/route-generator.js +323 -0
- package/runtime/mock-api/socket-triggers.js +371 -0
- package/runtime/mock-api/spec-loader.js +300 -0
- package/runtime/server.js +180 -0
- package/runtime/stores/gxpPortalConfigStore.js +554 -0
- package/runtime/stores/index.js +6 -0
- package/runtime/vite-inspector-plugin.js +749 -0
- package/runtime/vite-source-tracker-plugin.js +232 -0
- package/runtime/vite.config.js +402 -0
- package/scripts/launch-chrome.js +90 -0
- package/scripts/pack-chrome.js +91 -0
- package/socket-events/AiSessionMessageCreated.json +18 -0
- package/socket-events/SocialStreamPostCreated.json +24 -0
- package/socket-events/SocialStreamPostVariantCompleted.json +23 -0
- package/template/README.md +332 -0
- package/template/app-manifest.json +32 -0
- package/template/dev-assets/images/avatar-placeholder.png +0 -0
- package/template/dev-assets/images/background-placeholder.jpg +0 -0
- package/template/dev-assets/images/banner-placeholder.jpg +0 -0
- package/template/dev-assets/images/icon-placeholder.png +0 -0
- package/template/dev-assets/images/logo-placeholder.png +0 -0
- package/template/dev-assets/images/product-placeholder.jpg +0 -0
- package/template/dev-assets/images/thumbnail-placeholder.jpg +0 -0
- package/template/env.example +51 -0
- package/template/gitignore +53 -0
- package/template/index.html +22 -0
- package/template/main.js +28 -0
- package/template/src/DemoPage.vue +459 -0
- package/template/src/Plugin.vue +38 -0
- package/template/src/stores/index.js +9 -0
- package/template/src/stores/test-data.json +173 -0
- package/template/theme-layouts/AdditionalStyling.css +0 -0
- package/template/theme-layouts/PrivateLayout.vue +39 -0
- package/template/theme-layouts/PublicLayout.vue +39 -0
- package/template/theme-layouts/SystemLayout.vue +39 -0
- package/template/vite.config.js +333 -0
- package/tsconfig.tui.json +21 -0
- package/vite.config.js +164 -0
|
@@ -0,0 +1,644 @@
|
|
|
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>
|
|
194
|
+
</template>
|
|
195
|
+
|
|
196
|
+
<script setup>
|
|
197
|
+
import { ref, reactive, nextTick, onUnmounted } from 'vue';
|
|
198
|
+
|
|
199
|
+
const props = defineProps({
|
|
200
|
+
store: {
|
|
201
|
+
type: Object,
|
|
202
|
+
required: true
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Element highlighting
|
|
207
|
+
const highlightedElements = ref([]);
|
|
208
|
+
const highlightOverlays = ref([]);
|
|
209
|
+
|
|
210
|
+
const expandedSections = reactive({
|
|
211
|
+
pluginVars: true,
|
|
212
|
+
stringsList: false,
|
|
213
|
+
assetList: false,
|
|
214
|
+
triggerState: false,
|
|
215
|
+
dependencyList: false
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
const editingKey = ref(null);
|
|
219
|
+
const editValue = ref('');
|
|
220
|
+
|
|
221
|
+
function toggleSection(section) {
|
|
222
|
+
expandedSections[section] = !expandedSections[section];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
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';
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
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);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function isImageUrl(url) {
|
|
248
|
+
if (typeof url !== 'string') return false;
|
|
249
|
+
return /\.(jpg|jpeg|png|gif|webp|svg)(\?.*)?$/i.test(url);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
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) + '...';
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
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;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
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
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
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();
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function cancelEdit() {
|
|
305
|
+
editingKey.value = null;
|
|
306
|
+
editValue.value = '';
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function refreshStore() {
|
|
310
|
+
// Force reactivity update
|
|
311
|
+
console.log('[DevTools] Store refreshed');
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
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
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Element highlighting functions
|
|
333
|
+
* Creates overlay boxes that highlight elements using gxp-* attributes
|
|
334
|
+
*/
|
|
335
|
+
|
|
336
|
+
// CSS class name for our highlight overlay
|
|
337
|
+
const HIGHLIGHT_CLASS = 'gxp-devtools-highlight-overlay';
|
|
338
|
+
|
|
339
|
+
// Inject highlight styles into the document if not already present
|
|
340
|
+
function ensureHighlightStyles() {
|
|
341
|
+
if (document.getElementById('gxp-devtools-highlight-styles')) return;
|
|
342
|
+
|
|
343
|
+
const style = document.createElement('style');
|
|
344
|
+
style.id = 'gxp-devtools-highlight-styles';
|
|
345
|
+
style.textContent = `
|
|
346
|
+
.${HIGHLIGHT_CLASS} {
|
|
347
|
+
position: fixed;
|
|
348
|
+
pointer-events: none;
|
|
349
|
+
z-index: 99998;
|
|
350
|
+
background: rgba(97, 218, 251, 0.15);
|
|
351
|
+
border: 2px solid #61dafb;
|
|
352
|
+
border-radius: 4px;
|
|
353
|
+
box-shadow: 0 0 10px rgba(97, 218, 251, 0.5), inset 0 0 10px rgba(97, 218, 251, 0.1);
|
|
354
|
+
animation: gxp-highlight-pulse 1.5s ease-in-out infinite;
|
|
355
|
+
}
|
|
356
|
+
.${HIGHLIGHT_CLASS}::before {
|
|
357
|
+
content: attr(data-gxp-key);
|
|
358
|
+
position: absolute;
|
|
359
|
+
top: -22px;
|
|
360
|
+
left: -2px;
|
|
361
|
+
background: #61dafb;
|
|
362
|
+
color: #1e1e1e;
|
|
363
|
+
font-size: 10px;
|
|
364
|
+
font-weight: 600;
|
|
365
|
+
padding: 2px 6px;
|
|
366
|
+
border-radius: 3px 3px 0 0;
|
|
367
|
+
font-family: 'SF Mono', Monaco, 'Courier New', monospace;
|
|
368
|
+
white-space: nowrap;
|
|
369
|
+
}
|
|
370
|
+
@keyframes gxp-highlight-pulse {
|
|
371
|
+
0%, 100% { box-shadow: 0 0 10px rgba(97, 218, 251, 0.5), inset 0 0 10px rgba(97, 218, 251, 0.1); }
|
|
372
|
+
50% { box-shadow: 0 0 20px rgba(97, 218, 251, 0.8), inset 0 0 15px rgba(97, 218, 251, 0.2); }
|
|
373
|
+
}
|
|
374
|
+
`;
|
|
375
|
+
document.head.appendChild(style);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Find elements that match a gxp attribute and key
|
|
379
|
+
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;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Create overlay for an element
|
|
424
|
+
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;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Highlight elements matching a key
|
|
438
|
+
function highlightElements(attribute, key) {
|
|
439
|
+
ensureHighlightStyles();
|
|
440
|
+
clearHighlight();
|
|
441
|
+
|
|
442
|
+
const elements = findMatchingElements(attribute, key);
|
|
443
|
+
highlightedElements.value = elements;
|
|
444
|
+
|
|
445
|
+
elements.forEach(el => {
|
|
446
|
+
const overlay = createOverlay(el, key);
|
|
447
|
+
highlightOverlays.value.push(overlay);
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
if (elements.length > 0) {
|
|
451
|
+
console.log(`[DevTools] Highlighting ${elements.length} element(s) with ${attribute}="${key}"`);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Clear all highlight overlays
|
|
456
|
+
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 = [];
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Clean up on unmount
|
|
467
|
+
onUnmounted(() => {
|
|
468
|
+
clearHighlight();
|
|
469
|
+
});
|
|
470
|
+
</script>
|
|
471
|
+
|
|
472
|
+
<style scoped>
|
|
473
|
+
.store-inspector {
|
|
474
|
+
display: flex;
|
|
475
|
+
flex-direction: column;
|
|
476
|
+
gap: 8px;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.inspector-section {
|
|
480
|
+
background: #2d2d2d;
|
|
481
|
+
border-radius: 6px;
|
|
482
|
+
overflow: hidden;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
.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;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
.section-title:hover {
|
|
499
|
+
background: #3d3d3d;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.toggle-icon {
|
|
503
|
+
font-size: 10px;
|
|
504
|
+
color: #888;
|
|
505
|
+
width: 12px;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
.item-count {
|
|
509
|
+
margin-left: auto;
|
|
510
|
+
background: #3d3d3d;
|
|
511
|
+
padding: 2px 8px;
|
|
512
|
+
border-radius: 10px;
|
|
513
|
+
font-size: 11px;
|
|
514
|
+
color: #888;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
.section-content {
|
|
518
|
+
padding: 8px 12px 12px;
|
|
519
|
+
border-top: 1px solid #3d3d3d;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
.empty-state {
|
|
523
|
+
color: #666;
|
|
524
|
+
font-size: 12px;
|
|
525
|
+
font-style: italic;
|
|
526
|
+
padding: 8px 0;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
.property-list {
|
|
530
|
+
display: flex;
|
|
531
|
+
flex-direction: column;
|
|
532
|
+
gap: 6px;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
.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;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
.property-key {
|
|
544
|
+
color: #9cdcfe;
|
|
545
|
+
min-width: 140px;
|
|
546
|
+
flex-shrink: 0;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
.property-key::after {
|
|
550
|
+
content: ':';
|
|
551
|
+
color: #888;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
.property-value {
|
|
555
|
+
word-break: break-all;
|
|
556
|
+
cursor: pointer;
|
|
557
|
+
padding: 2px 4px;
|
|
558
|
+
border-radius: 3px;
|
|
559
|
+
transition: background 0.2s;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
.property-value:hover {
|
|
563
|
+
background: #3d3d3d;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
.property-value.string {
|
|
567
|
+
color: #ce9178;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
.property-value.number {
|
|
571
|
+
color: #b5cea8;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
.property-value.boolean {
|
|
575
|
+
color: #569cd6;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
.property-value.null,
|
|
579
|
+
.property-value.undefined {
|
|
580
|
+
color: #808080;
|
|
581
|
+
font-style: italic;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
.property-value.object,
|
|
585
|
+
.property-value.array {
|
|
586
|
+
color: #dcdcaa;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
.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;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
.asset-item {
|
|
602
|
+
flex-direction: column;
|
|
603
|
+
gap: 4px;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
.asset-preview {
|
|
607
|
+
display: flex;
|
|
608
|
+
align-items: center;
|
|
609
|
+
gap: 8px;
|
|
610
|
+
padding-left: 148px;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
.asset-thumbnail {
|
|
614
|
+
width: 32px;
|
|
615
|
+
height: 32px;
|
|
616
|
+
object-fit: cover;
|
|
617
|
+
border-radius: 4px;
|
|
618
|
+
background: #3d3d3d;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
.inspector-actions {
|
|
622
|
+
display: flex;
|
|
623
|
+
gap: 8px;
|
|
624
|
+
margin-top: 12px;
|
|
625
|
+
padding-top: 12px;
|
|
626
|
+
border-top: 1px solid #3d3d3d;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
.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;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
.action-btn:hover {
|
|
641
|
+
background: #4d4d4d;
|
|
642
|
+
color: #61dafb;
|
|
643
|
+
}
|
|
644
|
+
</style>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// GxP Dev Tools Components
|
|
2
|
+
export { default as DevToolsModal } from './DevToolsModal.vue';
|
|
3
|
+
export { default as StoreInspector } from './StoreInspector.vue';
|
|
4
|
+
export { default as LayoutSwitcher } from './LayoutSwitcher.vue';
|
|
5
|
+
export { default as SocketSimulator } from './SocketSimulator.vue';
|
|
6
|
+
export { default as MockDataEditor } from './MockDataEditor.vue';
|