@gxp-dev/tools 2.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/.github/workflows/npm-publish.yml +48 -0
  2. package/CLAUDE.md +400 -0
  3. package/README.md +247 -0
  4. package/REFACTOR_PLAN.md +194 -0
  5. package/bin/gx-devtools.js +87 -0
  6. package/bin/lib/cli.js +251 -0
  7. package/bin/lib/commands/assets.js +337 -0
  8. package/bin/lib/commands/build.js +259 -0
  9. package/bin/lib/commands/datastore.js +433 -0
  10. package/bin/lib/commands/dev.js +328 -0
  11. package/bin/lib/commands/extensions.js +298 -0
  12. package/bin/lib/commands/index.js +35 -0
  13. package/bin/lib/commands/init.js +307 -0
  14. package/bin/lib/commands/publish.js +189 -0
  15. package/bin/lib/commands/socket.js +158 -0
  16. package/bin/lib/commands/ssl.js +47 -0
  17. package/bin/lib/constants.js +120 -0
  18. package/bin/lib/tui/App.tsx +600 -0
  19. package/bin/lib/tui/components/CommandInput.tsx +278 -0
  20. package/bin/lib/tui/components/GeminiPanel.tsx +161 -0
  21. package/bin/lib/tui/components/Header.tsx +27 -0
  22. package/bin/lib/tui/components/LogPanel.tsx +122 -0
  23. package/bin/lib/tui/components/TabBar.tsx +56 -0
  24. package/bin/lib/tui/components/WelcomeScreen.tsx +80 -0
  25. package/bin/lib/tui/index.tsx +63 -0
  26. package/bin/lib/tui/services/ExtensionService.ts +122 -0
  27. package/bin/lib/tui/services/GeminiService.ts +395 -0
  28. package/bin/lib/tui/services/ServiceManager.ts +336 -0
  29. package/bin/lib/tui/services/SocketService.ts +204 -0
  30. package/bin/lib/tui/services/ViteService.ts +107 -0
  31. package/bin/lib/tui/services/index.ts +13 -0
  32. package/bin/lib/utils/files.js +180 -0
  33. package/bin/lib/utils/index.js +17 -0
  34. package/bin/lib/utils/paths.js +138 -0
  35. package/bin/lib/utils/prompts.js +71 -0
  36. package/bin/lib/utils/ssl.js +233 -0
  37. package/browser-extensions/README.md +1 -0
  38. package/browser-extensions/chrome/background.js +857 -0
  39. package/browser-extensions/chrome/content.js +51 -0
  40. package/browser-extensions/chrome/devtools.html +9 -0
  41. package/browser-extensions/chrome/devtools.js +23 -0
  42. package/browser-extensions/chrome/icons/gx_off_128.png +0 -0
  43. package/browser-extensions/chrome/icons/gx_off_16.png +0 -0
  44. package/browser-extensions/chrome/icons/gx_off_32.png +0 -0
  45. package/browser-extensions/chrome/icons/gx_off_64.png +0 -0
  46. package/browser-extensions/chrome/icons/gx_on_128.png +0 -0
  47. package/browser-extensions/chrome/icons/gx_on_16.png +0 -0
  48. package/browser-extensions/chrome/icons/gx_on_32.png +0 -0
  49. package/browser-extensions/chrome/icons/gx_on_64.png +0 -0
  50. package/browser-extensions/chrome/inspector.js +1087 -0
  51. package/browser-extensions/chrome/manifest.json +70 -0
  52. package/browser-extensions/chrome/panel.html +638 -0
  53. package/browser-extensions/chrome/panel.js +862 -0
  54. package/browser-extensions/chrome/popup.html +399 -0
  55. package/browser-extensions/chrome/popup.js +515 -0
  56. package/browser-extensions/chrome/rules.json +1 -0
  57. package/browser-extensions/chrome/test-chrome.html +145 -0
  58. package/browser-extensions/chrome/test-mixed-content.html +190 -0
  59. package/browser-extensions/chrome/test-uri-pattern.html +199 -0
  60. package/browser-extensions/firefox/README.md +134 -0
  61. package/browser-extensions/firefox/background.js +804 -0
  62. package/browser-extensions/firefox/content.js +120 -0
  63. package/browser-extensions/firefox/debug-errors.html +229 -0
  64. package/browser-extensions/firefox/debug-https.html +113 -0
  65. package/browser-extensions/firefox/devtools.html +9 -0
  66. package/browser-extensions/firefox/devtools.js +24 -0
  67. package/browser-extensions/firefox/icons/gx_off_128.png +0 -0
  68. package/browser-extensions/firefox/icons/gx_off_16.png +0 -0
  69. package/browser-extensions/firefox/icons/gx_off_32.png +0 -0
  70. package/browser-extensions/firefox/icons/gx_off_64.png +0 -0
  71. package/browser-extensions/firefox/icons/gx_on_128.png +0 -0
  72. package/browser-extensions/firefox/icons/gx_on_16.png +0 -0
  73. package/browser-extensions/firefox/icons/gx_on_32.png +0 -0
  74. package/browser-extensions/firefox/icons/gx_on_64.png +0 -0
  75. package/browser-extensions/firefox/inspector.js +1087 -0
  76. package/browser-extensions/firefox/manifest.json +67 -0
  77. package/browser-extensions/firefox/panel.html +638 -0
  78. package/browser-extensions/firefox/panel.js +862 -0
  79. package/browser-extensions/firefox/popup.html +525 -0
  80. package/browser-extensions/firefox/popup.js +536 -0
  81. package/browser-extensions/firefox/test-gramercy.html +126 -0
  82. package/browser-extensions/firefox/test-imports.html +58 -0
  83. package/browser-extensions/firefox/test-masking.html +147 -0
  84. package/browser-extensions/firefox/test-uri-pattern.html +199 -0
  85. package/docs/DOCUSAURUS_IMPORT.md +378 -0
  86. package/docs/_category_.json +8 -0
  87. package/docs/app-manifest.md +272 -0
  88. package/docs/building-for-platform.md +315 -0
  89. package/docs/dev-tools.md +291 -0
  90. package/docs/getting-started.md +180 -0
  91. package/docs/gxp-store.md +305 -0
  92. package/docs/index.md +44 -0
  93. package/package.json +77 -0
  94. package/runtime/PortalContainer.vue +326 -0
  95. package/runtime/dev-tools/DevToolsModal.vue +217 -0
  96. package/runtime/dev-tools/LayoutSwitcher.vue +221 -0
  97. package/runtime/dev-tools/MockDataEditor.vue +621 -0
  98. package/runtime/dev-tools/SocketSimulator.vue +562 -0
  99. package/runtime/dev-tools/StoreInspector.vue +644 -0
  100. package/runtime/dev-tools/index.js +6 -0
  101. package/runtime/gxpStringsPlugin.js +428 -0
  102. package/runtime/index.html +22 -0
  103. package/runtime/main.js +32 -0
  104. package/runtime/mock-api/auth-middleware.js +97 -0
  105. package/runtime/mock-api/image-generator.js +221 -0
  106. package/runtime/mock-api/index.js +197 -0
  107. package/runtime/mock-api/response-generator.js +394 -0
  108. package/runtime/mock-api/route-generator.js +323 -0
  109. package/runtime/mock-api/socket-triggers.js +371 -0
  110. package/runtime/mock-api/spec-loader.js +300 -0
  111. package/runtime/server.js +180 -0
  112. package/runtime/stores/gxpPortalConfigStore.js +554 -0
  113. package/runtime/stores/index.js +6 -0
  114. package/runtime/vite-inspector-plugin.js +749 -0
  115. package/runtime/vite-source-tracker-plugin.js +232 -0
  116. package/runtime/vite.config.js +402 -0
  117. package/scripts/launch-chrome.js +90 -0
  118. package/scripts/pack-chrome.js +91 -0
  119. package/socket-events/AiSessionMessageCreated.json +18 -0
  120. package/socket-events/SocialStreamPostCreated.json +24 -0
  121. package/socket-events/SocialStreamPostVariantCompleted.json +23 -0
  122. package/template/README.md +332 -0
  123. package/template/app-manifest.json +32 -0
  124. package/template/dev-assets/images/avatar-placeholder.png +0 -0
  125. package/template/dev-assets/images/background-placeholder.jpg +0 -0
  126. package/template/dev-assets/images/banner-placeholder.jpg +0 -0
  127. package/template/dev-assets/images/icon-placeholder.png +0 -0
  128. package/template/dev-assets/images/logo-placeholder.png +0 -0
  129. package/template/dev-assets/images/product-placeholder.jpg +0 -0
  130. package/template/dev-assets/images/thumbnail-placeholder.jpg +0 -0
  131. package/template/env.example +51 -0
  132. package/template/gitignore +53 -0
  133. package/template/index.html +22 -0
  134. package/template/main.js +28 -0
  135. package/template/src/DemoPage.vue +459 -0
  136. package/template/src/Plugin.vue +38 -0
  137. package/template/src/stores/index.js +9 -0
  138. package/template/src/stores/test-data.json +173 -0
  139. package/template/theme-layouts/AdditionalStyling.css +0 -0
  140. package/template/theme-layouts/PrivateLayout.vue +39 -0
  141. package/template/theme-layouts/PublicLayout.vue +39 -0
  142. package/template/theme-layouts/SystemLayout.vue +39 -0
  143. package/template/vite.config.js +333 -0
  144. package/tsconfig.tui.json +21 -0
  145. package/vite.config.js +164 -0
@@ -0,0 +1,515 @@
1
+ document.addEventListener("DOMContentLoaded", async function () {
2
+ // Get all DOM elements
3
+ const toggleButton = document.getElementById("toggleButton");
4
+ const toggleText = document.getElementById("toggleText");
5
+ const saveButton = document.getElementById("saveButton");
6
+ const statusDiv = document.getElementById("status");
7
+ const maskingModeCheckbox = document.getElementById("maskingMode");
8
+ const clearCacheOnEnableCheckbox =
9
+ document.getElementById("clearCacheOnEnable");
10
+ const disableCacheForRedirectsCheckbox = document.getElementById(
11
+ "disableCacheForRedirects"
12
+ );
13
+ const clearCacheButton = document.getElementById("clearCacheButton");
14
+
15
+ // Inspector elements
16
+ const inspectorToggle = document.getElementById("inspectorToggle");
17
+ const inspectorText = document.getElementById("inspectorText");
18
+
19
+ // Inspector state
20
+ let inspectorEnabled = false;
21
+
22
+ // JS Rule elements
23
+ const jsRuleEnabled = document.getElementById("jsRuleEnabled");
24
+ const jsRuleContent = document.getElementById("jsRuleContent");
25
+ const jsRedirectUrl = document.getElementById("jsRedirectUrl");
26
+ const jsCustomPattern = document.getElementById("jsCustomPattern");
27
+ const jsPatternDisplay = document.getElementById("jsPatternDisplay");
28
+ const jsCustomPatternInput = document.getElementById("jsCustomPatternInput");
29
+
30
+ // CSS Rule elements
31
+ const cssRuleEnabled = document.getElementById("cssRuleEnabled");
32
+ const cssRuleContent = document.getElementById("cssRuleContent");
33
+ const cssRedirectUrl = document.getElementById("cssRedirectUrl");
34
+ const cssReturnBlank = document.getElementById("cssReturnBlank");
35
+ const cssRedirectSection = document.getElementById("cssRedirectSection");
36
+ const cssCustomPattern = document.getElementById("cssCustomPattern");
37
+ const cssPatternDisplay = document.getElementById("cssPatternDisplay");
38
+ const cssCustomPatternInput = document.getElementById(
39
+ "cssCustomPatternInput"
40
+ );
41
+
42
+ // Default configuration
43
+ const defaultConfig = {
44
+ enabled: false,
45
+ // Legacy fields for backward compatibility
46
+ redirectUrl: "https://localhost:3060/src/Plugin.vue",
47
+ urlPattern: "uploads\\/plugin-version\\/\\d+\\/file_name\\/.*\\.js(\\?.*)?",
48
+ useCustomPattern: false,
49
+ // New rules-based configuration
50
+ rules: {
51
+ js: {
52
+ enabled: true,
53
+ pattern:
54
+ "uploads\\/plugin-version\\/\\d+\\/file_name\\/.*\\.js(\\?.*)?",
55
+ redirectUrl: "https://localhost:3060/src/Plugin.vue",
56
+ useCustomPattern: false,
57
+ },
58
+ css: {
59
+ enabled: true,
60
+ pattern:
61
+ "uploads\\/plugin-version\\/\\d+\\/style_file_name\\/.*\\.css(\\?.*)?",
62
+ redirectUrl: "",
63
+ returnBlank: true,
64
+ useCustomPattern: false,
65
+ },
66
+ },
67
+ maskingMode: false,
68
+ clearCacheOnEnable: true,
69
+ disableCacheForRedirects: true,
70
+ };
71
+
72
+ // Load current configuration
73
+ let config = {};
74
+ try {
75
+ const result = await chrome.storage.sync.get(defaultConfig);
76
+ config = result;
77
+
78
+ // Migrate legacy config if needed
79
+ config = migrateConfig(config);
80
+ } catch (error) {
81
+ console.error("Error loading config:", error);
82
+ config = defaultConfig;
83
+ }
84
+
85
+ // Initialize UI with loaded config
86
+ updateUI();
87
+ updatePatternDisplays();
88
+
89
+ function migrateConfig(config) {
90
+ // If rules don't exist, create them from legacy config
91
+ if (!config.rules) {
92
+ config.rules = {
93
+ js: {
94
+ enabled: true,
95
+ pattern: config.urlPattern || defaultConfig.rules.js.pattern,
96
+ redirectUrl: config.redirectUrl || defaultConfig.rules.js.redirectUrl,
97
+ useCustomPattern: config.useCustomPattern || false,
98
+ },
99
+ css: {
100
+ enabled: true,
101
+ pattern: defaultConfig.rules.css.pattern,
102
+ redirectUrl: "",
103
+ returnBlank: true,
104
+ useCustomPattern: false,
105
+ },
106
+ };
107
+ }
108
+ return config;
109
+ }
110
+
111
+ function updateUI() {
112
+ // Update toggle button
113
+ if (config.enabled) {
114
+ toggleButton.classList.add("enabled");
115
+ toggleText.textContent = "ON";
116
+ } else {
117
+ toggleButton.classList.remove("enabled");
118
+ toggleText.textContent = "OFF";
119
+ }
120
+
121
+ // Update global settings
122
+ maskingModeCheckbox.checked = config.maskingMode || false;
123
+ clearCacheOnEnableCheckbox.checked = config.clearCacheOnEnable !== false;
124
+ disableCacheForRedirectsCheckbox.checked =
125
+ config.disableCacheForRedirects !== false;
126
+
127
+ // Update JS rule
128
+ if (config.rules && config.rules.js) {
129
+ jsRuleEnabled.checked = config.rules.js.enabled;
130
+ jsRedirectUrl.value = config.rules.js.redirectUrl || "";
131
+ jsCustomPattern.checked = config.rules.js.useCustomPattern || false;
132
+ jsCustomPatternInput.value = config.rules.js.pattern || "";
133
+
134
+ // Toggle JS rule content visibility
135
+ if (config.rules.js.enabled) {
136
+ jsRuleContent.classList.remove("rule-disabled");
137
+ } else {
138
+ jsRuleContent.classList.add("rule-disabled");
139
+ }
140
+
141
+ // Toggle custom pattern input visibility
142
+ if (config.rules.js.useCustomPattern) {
143
+ jsCustomPatternInput.classList.add("visible");
144
+ jsPatternDisplay.style.display = "none";
145
+ } else {
146
+ jsCustomPatternInput.classList.remove("visible");
147
+ jsPatternDisplay.style.display = "block";
148
+ }
149
+ }
150
+
151
+ // Update CSS rule
152
+ if (config.rules && config.rules.css) {
153
+ cssRuleEnabled.checked = config.rules.css.enabled;
154
+ cssRedirectUrl.value = config.rules.css.redirectUrl || "";
155
+ cssReturnBlank.checked = config.rules.css.returnBlank || false;
156
+ cssCustomPattern.checked = config.rules.css.useCustomPattern || false;
157
+ cssCustomPatternInput.value = config.rules.css.pattern || "";
158
+
159
+ // Toggle CSS rule content visibility
160
+ if (config.rules.css.enabled) {
161
+ cssRuleContent.classList.remove("rule-disabled");
162
+ } else {
163
+ cssRuleContent.classList.add("rule-disabled");
164
+ }
165
+
166
+ // Toggle redirect section based on blank return setting
167
+ if (config.rules.css.returnBlank) {
168
+ cssRedirectSection.style.display = "none";
169
+ } else {
170
+ cssRedirectSection.style.display = "block";
171
+ }
172
+
173
+ // Toggle custom pattern input visibility
174
+ if (config.rules.css.useCustomPattern) {
175
+ cssCustomPatternInput.classList.add("visible");
176
+ cssPatternDisplay.style.display = "none";
177
+ } else {
178
+ cssCustomPatternInput.classList.remove("visible");
179
+ cssPatternDisplay.style.display = "block";
180
+ }
181
+ }
182
+ }
183
+
184
+ function updatePatternDisplays() {
185
+ // Update JS pattern display
186
+ if (config.rules && config.rules.js) {
187
+ const jsPattern = config.rules.js.useCustomPattern
188
+ ? config.rules.js.pattern
189
+ : defaultConfig.rules.js.pattern;
190
+ jsPatternDisplay.textContent = jsPattern;
191
+ }
192
+
193
+ // Update CSS pattern display
194
+ if (config.rules && config.rules.css) {
195
+ const cssPattern = config.rules.css.useCustomPattern
196
+ ? config.rules.css.pattern
197
+ : defaultConfig.rules.css.pattern;
198
+ cssPatternDisplay.textContent = cssPattern;
199
+ }
200
+ }
201
+
202
+ function showStatus(message, isSuccess = true) {
203
+ statusDiv.textContent = message;
204
+ statusDiv.className = `status ${isSuccess ? "success" : "error"}`;
205
+ statusDiv.style.display = "block";
206
+
207
+ setTimeout(() => {
208
+ statusDiv.style.display = "none";
209
+ }, 3000);
210
+ }
211
+
212
+ function validateRedirectUrl(url) {
213
+ if (!url || url.trim() === "") {
214
+ return null; // Empty URL is valid for optional fields
215
+ }
216
+
217
+ try {
218
+ new URL(url);
219
+ return null; // Valid
220
+ } catch {
221
+ // Check if it's a relative-style URL like localhost:3060/path
222
+ if (url.includes("://")) {
223
+ return "Invalid URL format";
224
+ }
225
+
226
+ // Try to parse as localhost-style URL
227
+ try {
228
+ new URL("https://" + url);
229
+ return null; // Valid
230
+ } catch {
231
+ return "Invalid URL format";
232
+ }
233
+ }
234
+ }
235
+
236
+ function validatePattern(pattern) {
237
+ if (!pattern || pattern.trim() === "") {
238
+ return "URL pattern is required";
239
+ }
240
+
241
+ try {
242
+ new RegExp(pattern);
243
+ return null; // Valid
244
+ } catch {
245
+ return "Invalid regular expression pattern";
246
+ }
247
+ }
248
+
249
+ function normalizeUrl(url) {
250
+ if (!url || url.trim() === "") return "";
251
+ if (!url.includes("://")) {
252
+ return "https://" + url;
253
+ }
254
+ return url;
255
+ }
256
+
257
+ // Event listeners
258
+ toggleButton.addEventListener("click", async function () {
259
+ config.enabled = !config.enabled;
260
+
261
+ try {
262
+ await chrome.storage.sync.set({ enabled: config.enabled });
263
+ updateUI();
264
+
265
+ // Send message to background script
266
+ chrome.runtime.sendMessage({
267
+ action: "toggleProxy",
268
+ enabled: config.enabled,
269
+ });
270
+
271
+ showStatus(config.enabled ? "Proxy enabled" : "Proxy disabled");
272
+ } catch (error) {
273
+ console.error("Error toggling proxy:", error);
274
+ showStatus("Error toggling proxy", false);
275
+ }
276
+ });
277
+
278
+ // Global settings event listeners
279
+ maskingModeCheckbox.addEventListener("change", function () {
280
+ config.maskingMode = this.checked;
281
+ });
282
+
283
+ clearCacheOnEnableCheckbox.addEventListener("change", function () {
284
+ config.clearCacheOnEnable = this.checked;
285
+ });
286
+
287
+ disableCacheForRedirectsCheckbox.addEventListener("change", function () {
288
+ config.disableCacheForRedirects = this.checked;
289
+ });
290
+
291
+ // JS rule event listeners
292
+ jsRuleEnabled.addEventListener("change", function () {
293
+ if (!config.rules) config.rules = {};
294
+ if (!config.rules.js) config.rules.js = { ...defaultConfig.rules.js };
295
+ config.rules.js.enabled = this.checked;
296
+ updateUI();
297
+ });
298
+
299
+ jsCustomPattern.addEventListener("change", function () {
300
+ if (!config.rules) config.rules = {};
301
+ if (!config.rules.js) config.rules.js = { ...defaultConfig.rules.js };
302
+ config.rules.js.useCustomPattern = this.checked;
303
+ updateUI();
304
+ updatePatternDisplays();
305
+ });
306
+
307
+ // CSS rule event listeners
308
+ cssRuleEnabled.addEventListener("change", function () {
309
+ if (!config.rules) config.rules = {};
310
+ if (!config.rules.css) config.rules.css = { ...defaultConfig.rules.css };
311
+ config.rules.css.enabled = this.checked;
312
+ updateUI();
313
+ });
314
+
315
+ cssReturnBlank.addEventListener("change", function () {
316
+ if (!config.rules) config.rules = {};
317
+ if (!config.rules.css) config.rules.css = { ...defaultConfig.rules.css };
318
+ config.rules.css.returnBlank = this.checked;
319
+ updateUI();
320
+ });
321
+
322
+ cssCustomPattern.addEventListener("change", function () {
323
+ if (!config.rules) config.rules = {};
324
+ if (!config.rules.css) config.rules.css = { ...defaultConfig.rules.css };
325
+ config.rules.css.useCustomPattern = this.checked;
326
+ updateUI();
327
+ updatePatternDisplays();
328
+ });
329
+
330
+ clearCacheButton.addEventListener("click", async function () {
331
+ try {
332
+ this.textContent = "Clearing...";
333
+ this.disabled = true;
334
+
335
+ const response = await chrome.runtime.sendMessage({
336
+ action: "clearCache",
337
+ });
338
+
339
+ if (response.success) {
340
+ showStatus("Cache cleared successfully");
341
+ } else {
342
+ showStatus(
343
+ "Error clearing cache: " + (response.error || "Unknown error"),
344
+ false
345
+ );
346
+ }
347
+ } catch (error) {
348
+ console.error("Error clearing cache:", error);
349
+ showStatus("Error clearing cache", false);
350
+ } finally {
351
+ this.textContent = "Clear Cache Now";
352
+ this.disabled = false;
353
+ }
354
+ });
355
+
356
+ saveButton.addEventListener("click", async function () {
357
+ // Ensure rules structure exists
358
+ if (!config.rules) config.rules = {};
359
+ if (!config.rules.js) config.rules.js = { ...defaultConfig.rules.js };
360
+ if (!config.rules.css) config.rules.css = { ...defaultConfig.rules.css };
361
+
362
+ // Validate JS rule if enabled
363
+ if (config.rules.js.enabled) {
364
+ const jsRedirectUrlValue = jsRedirectUrl.value.trim();
365
+ const jsPatternValue = config.rules.js.useCustomPattern
366
+ ? jsCustomPatternInput.value.trim()
367
+ : defaultConfig.rules.js.pattern;
368
+
369
+ if (!jsRedirectUrlValue) {
370
+ showStatus(
371
+ "JavaScript redirect URL is required when JS rule is enabled",
372
+ false
373
+ );
374
+ return;
375
+ }
376
+
377
+ const jsUrlError = validateRedirectUrl(jsRedirectUrlValue);
378
+ if (jsUrlError) {
379
+ showStatus("JavaScript rule: " + jsUrlError, false);
380
+ return;
381
+ }
382
+
383
+ const jsPatternError = validatePattern(jsPatternValue);
384
+ if (jsPatternError) {
385
+ showStatus("JavaScript rule: " + jsPatternError, false);
386
+ return;
387
+ }
388
+
389
+ // Update JS rule config
390
+ config.rules.js.redirectUrl = normalizeUrl(jsRedirectUrlValue);
391
+ config.rules.js.pattern = jsPatternValue;
392
+ }
393
+
394
+ // Validate CSS rule if enabled
395
+ if (config.rules.css.enabled) {
396
+ const cssRedirectUrlValue = cssRedirectUrl.value.trim();
397
+ const cssPatternValue = config.rules.css.useCustomPattern
398
+ ? cssCustomPatternInput.value.trim()
399
+ : defaultConfig.rules.css.pattern;
400
+
401
+ // Only validate redirect URL if not returning blank
402
+ if (!config.rules.css.returnBlank && !cssRedirectUrlValue) {
403
+ showStatus(
404
+ "CSS redirect URL is required when CSS rule is enabled and not returning blank",
405
+ false
406
+ );
407
+ return;
408
+ }
409
+
410
+ if (!config.rules.css.returnBlank) {
411
+ const cssUrlError = validateRedirectUrl(cssRedirectUrlValue);
412
+ if (cssUrlError) {
413
+ showStatus("CSS rule: " + cssUrlError, false);
414
+ return;
415
+ }
416
+ }
417
+
418
+ const cssPatternError = validatePattern(cssPatternValue);
419
+ if (cssPatternError) {
420
+ showStatus("CSS rule: " + cssPatternError, false);
421
+ return;
422
+ }
423
+
424
+ // Update CSS rule config
425
+ config.rules.css.redirectUrl = normalizeUrl(cssRedirectUrlValue);
426
+ config.rules.css.pattern = cssPatternValue;
427
+ }
428
+
429
+ try {
430
+ await chrome.storage.sync.set(config);
431
+
432
+ // Send updated config to background script
433
+ chrome.runtime.sendMessage({
434
+ action: "updateConfig",
435
+ config: config,
436
+ });
437
+
438
+ updateUI();
439
+ updatePatternDisplays();
440
+ showStatus("Configuration saved successfully");
441
+ } catch (error) {
442
+ console.error("Error saving config:", error);
443
+ showStatus("Error saving configuration", false);
444
+ }
445
+ });
446
+
447
+ // Send initial config to background script
448
+ chrome.runtime.sendMessage({
449
+ action: "updateConfig",
450
+ config: config,
451
+ });
452
+
453
+ // ============================================================
454
+ // Component Inspector
455
+ // ============================================================
456
+
457
+ function updateInspectorUI() {
458
+ if (inspectorEnabled) {
459
+ inspectorToggle.classList.add("enabled");
460
+ inspectorText.textContent = "ON";
461
+ } else {
462
+ inspectorToggle.classList.remove("enabled");
463
+ inspectorText.textContent = "OFF";
464
+ }
465
+ }
466
+
467
+ // Get initial inspector state from content script
468
+ async function getInspectorState() {
469
+ try {
470
+ const [tab] = await chrome.tabs.query({
471
+ active: true,
472
+ currentWindow: true,
473
+ });
474
+ if (tab?.id) {
475
+ const response = await chrome.tabs.sendMessage(tab.id, {
476
+ action: "getInspectorState",
477
+ });
478
+ if (response) {
479
+ inspectorEnabled = response.enabled;
480
+ updateInspectorUI();
481
+ }
482
+ }
483
+ } catch (error) {
484
+ // Content script might not be loaded yet
485
+ console.log("Could not get inspector state:", error);
486
+ }
487
+ }
488
+
489
+ // Toggle inspector
490
+ inspectorToggle.addEventListener("click", async function () {
491
+ try {
492
+ const [tab] = await chrome.tabs.query({
493
+ active: true,
494
+ currentWindow: true,
495
+ });
496
+ if (tab?.id) {
497
+ const response = await chrome.tabs.sendMessage(tab.id, {
498
+ action: "toggleInspector",
499
+ });
500
+ if (response) {
501
+ inspectorEnabled = response.enabled;
502
+ updateInspectorUI();
503
+ showStatus(
504
+ inspectorEnabled ? "Inspector enabled" : "Inspector disabled"
505
+ );
506
+ }
507
+ }
508
+ } catch (error) {
509
+ showStatus("Could not toggle inspector. Make sure you're on a web page.", false);
510
+ }
511
+ });
512
+
513
+ // Initialize inspector state
514
+ getInspectorState();
515
+ });
@@ -0,0 +1 @@
1
+ []
@@ -0,0 +1,145 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Test Chrome Extension</title>
5
+ <style>
6
+ body { font-family: Arial, sans-serif; padding: 20px; max-width: 800px; }
7
+ .test-section { margin: 20px 0; padding: 20px; border: 2px solid #ddd; border-radius: 8px; }
8
+ .success { border-color: #28a745; background: #f8fff8; }
9
+ .warning { border-color: #ffc107; background: #fffbf0; }
10
+ .info { border-color: #17a2b8; background: #f0f9ff; }
11
+ .code { background: #e9ecef; padding: 8px; font-family: monospace; margin: 8px 0; word-break: break-all; }
12
+ .results { background: #f5f5f5; padding: 10px; margin: 10px 0; border-radius: 4px; max-height: 300px; overflow-y: auto; }
13
+ button { padding: 10px 15px; margin: 5px; cursor: pointer; background: #007bff; color: white; border: none; border-radius: 4px; }
14
+ </style>
15
+ </head>
16
+ <body>
17
+ <h1>๐Ÿ”ง Chrome Extension Test</h1>
18
+
19
+ <div class="test-section info">
20
+ <h2>๐Ÿ“‹ Chrome Extension Differences</h2>
21
+ <ul>
22
+ <li><strong>Manifest V3:</strong> Uses service workers instead of background scripts</li>
23
+ <li><strong>declarativeNetRequest:</strong> Replaces webRequest API for better performance</li>
24
+ <li><strong>chrome API:</strong> Uses <code>chrome.*</code> instead of <code>browser.*</code></li>
25
+ <li><strong>Limited Regex:</strong> Chrome's declarativeNetRequest has more limited pattern matching</li>
26
+ </ul>
27
+ </div>
28
+
29
+ <div class="test-section warning">
30
+ <h2>โš ๏ธ Installation Steps for Chrome</h2>
31
+ <ol>
32
+ <li>Open Chrome and go to <code>chrome://extensions/</code></li>
33
+ <li>Enable "Developer mode" (toggle in top right)</li>
34
+ <li>Click "Load unpacked"</li>
35
+ <li>Select the <code>chrome</code> folder</li>
36
+ <li>The extension should appear in your toolbar</li>
37
+ </ol>
38
+ </div>
39
+
40
+ <div class="test-section success">
41
+ <h2>๐Ÿงช Chrome Extension Tests</h2>
42
+ <button onclick="checkChromeExtension()">Check Chrome Extension</button>
43
+ <button onclick="testChromeRequest()">Test Chrome Request</button>
44
+ <button onclick="testDeclarativeRules()">Test Declarative Rules</button>
45
+ <div id="chromeResults" class="results"></div>
46
+ </div>
47
+
48
+ <div class="test-section">
49
+ <h2>๐Ÿ“Š Chrome vs Firefox Comparison</h2>
50
+ <table style="width: 100%; border-collapse: collapse;">
51
+ <tr style="background: #f8f9fa;">
52
+ <th style="border: 1px solid #ddd; padding: 8px;">Feature</th>
53
+ <th style="border: 1px solid #ddd; padding: 8px;">Firefox</th>
54
+ <th style="border: 1px solid #ddd; padding: 8px;">Chrome</th>
55
+ </tr>
56
+ <tr>
57
+ <td style="border: 1px solid #ddd; padding: 8px;">API</td>
58
+ <td style="border: 1px solid #ddd; padding: 8px;">browser.*</td>
59
+ <td style="border: 1px solid #ddd; padding: 8px;">chrome.*</td>
60
+ </tr>
61
+ <tr>
62
+ <td style="border: 1px solid #ddd; padding: 8px;">Manifest</td>
63
+ <td style="border: 1px solid #ddd; padding: 8px;">v2</td>
64
+ <td style="border: 1px solid #ddd; padding: 8px;">v3</td>
65
+ </tr>
66
+ <tr>
67
+ <td style="border: 1px solid #ddd; padding: 8px;">Background</td>
68
+ <td style="border: 1px solid #ddd; padding: 8px;">Persistent scripts</td>
69
+ <td style="border: 1px solid #ddd; padding: 8px;">Service worker</td>
70
+ </tr>
71
+ <tr>
72
+ <td style="border: 1px solid #ddd; padding: 8px;">Network API</td>
73
+ <td style="border: 1px solid #ddd; padding: 8px;">webRequest (full regex)</td>
74
+ <td style="border: 1px solid #ddd; padding: 8px;">declarativeNetRequest (limited patterns)</td>
75
+ </tr>
76
+ </table>
77
+ </div>
78
+
79
+ <script>
80
+ function log(message, type = 'info') {
81
+ const results = document.getElementById('chromeResults');
82
+ const timestamp = new Date().toLocaleTimeString();
83
+ const icon = type === 'error' ? 'โŒ' : type === 'success' ? 'โœ…' : type === 'warning' ? 'โš ๏ธ' : 'โ„น๏ธ';
84
+ results.innerHTML += `<div>${timestamp} ${icon} ${message}</div>`;
85
+ }
86
+
87
+ async function checkChromeExtension() {
88
+ log('Checking Chrome extension status...');
89
+
90
+ try {
91
+ if (typeof chrome === 'undefined' || !chrome.runtime) {
92
+ log('Chrome extension API not available', 'error');
93
+ return;
94
+ }
95
+
96
+ const response = await chrome.runtime.sendMessage({ action: 'getState' });
97
+ log(`โœ… Chrome extension responding: Enabled=${response.enabled}, Rules=${response.rules.length}`, 'success');
98
+
99
+ response.rules.forEach((rule, index) => {
100
+ log(`Rule ${index + 1}: ${rule.pattern} โ†’ ${rule.redirect} (Mask: ${rule.maskUrl || false})`, 'info');
101
+ });
102
+
103
+ } catch (error) {
104
+ log(`Chrome extension communication failed: ${error.message}`, 'error');
105
+ }
106
+ }
107
+
108
+ async function testChromeRequest() {
109
+ log('Testing request with Chrome extension...');
110
+
111
+ try {
112
+ const response = await fetch('https://httpbin.org/get?test=chrome');
113
+ const data = await response.json();
114
+ log(`Request successful: ${response.status}`, 'success');
115
+ log(`Response URL: ${data.url}`, 'info');
116
+ log('Check Chrome DevTools Network tab to see if request was intercepted', 'info');
117
+ } catch (error) {
118
+ log(`Request failed: ${error.message}`, 'error');
119
+ }
120
+ }
121
+
122
+ async function testDeclarativeRules() {
123
+ log('Testing Chrome declarativeNetRequest rules...');
124
+
125
+ try {
126
+ if (chrome.declarativeNetRequest) {
127
+ const rules = await chrome.declarativeNetRequest.getDynamicRules();
128
+ log(`Found ${rules.length} declarative rules`, 'success');
129
+
130
+ rules.forEach((rule, index) => {
131
+ log(`Rule ${rule.id}: ${rule.condition.regexFilter || rule.condition.urlFilter} โ†’ ${rule.action.type}`, 'info');
132
+ });
133
+ } else {
134
+ log('declarativeNetRequest API not available', 'warning');
135
+ }
136
+ } catch (error) {
137
+ log(`Error checking declarative rules: ${error.message}`, 'error');
138
+ }
139
+ }
140
+
141
+ // Auto-run extension check on page load
142
+ window.addEventListener('load', checkChromeExtension);
143
+ </script>
144
+ </body>
145
+ </html>