@gxp-dev/tools 2.0.12 → 2.0.14

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.
@@ -5,13 +5,16 @@ document.addEventListener("DOMContentLoaded", async function () {
5
5
  const saveButton = document.getElementById("saveButton");
6
6
  const statusDiv = document.getElementById("status");
7
7
  const maskingModeCheckbox = document.getElementById("maskingMode");
8
- const clearCacheOnEnableCheckbox =
9
- document.getElementById("clearCacheOnEnable");
10
- const disableCacheForRedirectsCheckbox = document.getElementById(
11
- "disableCacheForRedirects"
12
- );
8
+ const clearCacheOnEnableCheckbox = document.getElementById("clearCacheOnEnable");
9
+ const disableCacheForRedirectsCheckbox = document.getElementById("disableCacheForRedirects");
13
10
  const clearCacheButton = document.getElementById("clearCacheButton");
14
11
 
12
+ // Tab elements
13
+ const tabs = document.querySelectorAll(".tab");
14
+ const tabContents = document.querySelectorAll(".tab-content");
15
+ const jsBadge = document.getElementById("jsBadge");
16
+ const cssBadge = document.getElementById("cssBadge");
17
+
15
18
  // Inspector elements
16
19
  const inspectorToggle = document.getElementById("inspectorToggle");
17
20
  const inspectorText = document.getElementById("inspectorText");
@@ -35,79 +38,113 @@ document.addEventListener("DOMContentLoaded", async function () {
35
38
  const cssRedirectSection = document.getElementById("cssRedirectSection");
36
39
  const cssCustomPattern = document.getElementById("cssCustomPattern");
37
40
  const cssPatternDisplay = document.getElementById("cssPatternDisplay");
38
- const cssCustomPatternInput = document.getElementById(
39
- "cssCustomPatternInput"
40
- );
41
+ const cssCustomPatternInput = document.getElementById("cssCustomPatternInput");
42
+
43
+ // Load environment-based defaults from defaults.json (generated by launch script)
44
+ let envDefaults = {};
45
+ try {
46
+ const response = await fetch(chrome.runtime.getURL("defaults.json"));
47
+ if (response.ok) {
48
+ envDefaults = await response.json();
49
+ console.log("Loaded environment defaults:", envDefaults);
50
+ }
51
+ } catch (error) {
52
+ console.log("No environment defaults found, using hardcoded defaults");
53
+ }
54
+
55
+ // Build default redirect URL from env defaults or fallback
56
+ const defaultRedirectUrl = envDefaults.jsRedirectUrl || "https://localhost:3060/src/Plugin.vue";
41
57
 
42
- // Default configuration
58
+ // Default configuration (merged with environment defaults)
43
59
  const defaultConfig = {
44
- enabled: false,
45
- // Legacy fields for backward compatibility
46
- redirectUrl: "https://localhost:3060/src/Plugin.vue",
60
+ enabled: envDefaults.enabled || false,
61
+ redirectUrl: defaultRedirectUrl,
47
62
  urlPattern: "uploads\\/plugin-version\\/\\d+\\/file_name\\/.*\\.js(\\?.*)?",
48
- useCustomPattern: false,
49
- // New rules-based configuration
63
+ useCustomPattern: envDefaults.jsUseCustomPattern || false,
50
64
  rules: {
51
65
  js: {
52
66
  enabled: true,
53
- pattern:
54
- "uploads\\/plugin-version\\/\\d+\\/file_name\\/.*\\.js(\\?.*)?",
55
- redirectUrl: "https://localhost:3060/src/Plugin.vue",
56
- useCustomPattern: false,
67
+ pattern: "uploads\\/plugin-version\\/\\d+\\/file_name\\/.*\\.js(\\?.*)?",
68
+ redirectUrl: defaultRedirectUrl,
69
+ useCustomPattern: envDefaults.jsUseCustomPattern || false,
57
70
  },
58
71
  css: {
59
- enabled: true,
60
- pattern:
61
- "uploads\\/plugin-version\\/\\d+\\/style_file_name\\/.*\\.css(\\?.*)?",
62
- redirectUrl: "",
63
- returnBlank: true,
64
- useCustomPattern: false,
72
+ enabled: envDefaults.cssRuleEnabled !== false,
73
+ pattern: "uploads\\/plugin-version\\/\\d+\\/style_file_name\\/.*\\.css(\\?.*)?",
74
+ redirectUrl: envDefaults.cssRedirectUrl || "",
75
+ returnBlank: envDefaults.cssReturnBlank !== false,
76
+ useCustomPattern: envDefaults.cssUseCustomPattern || false,
65
77
  },
66
78
  },
67
79
  maskingMode: false,
68
- clearCacheOnEnable: true,
69
- disableCacheForRedirects: true,
80
+ clearCacheOnEnable: envDefaults.clearCacheOnEnable !== false,
81
+ disableCacheForRedirects: envDefaults.disableCacheForRedirects !== false,
70
82
  };
71
83
 
72
84
  // Load current configuration
73
85
  let config = {};
74
86
  try {
75
- const result = await chrome.storage.sync.get(defaultConfig);
76
- config = result;
87
+ const result = await chrome.storage.sync.get(null);
77
88
 
78
- // Migrate legacy config if needed
79
- config = migrateConfig(config);
89
+ if (!result || Object.keys(result).length === 0) {
90
+ config = defaultConfig;
91
+ await chrome.storage.sync.set(config);
92
+ console.log("Initialized with environment defaults");
93
+ } else {
94
+ config = { ...defaultConfig, ...result };
95
+ config = migrateConfig(config, defaultConfig);
96
+ }
80
97
  } catch (error) {
81
98
  console.error("Error loading config:", error);
82
99
  config = defaultConfig;
83
100
  }
84
101
 
85
- // Initialize UI with loaded config
102
+ // Initialize UI
86
103
  updateUI();
87
104
  updatePatternDisplays();
105
+ setupTabNavigation();
88
106
 
89
- function migrateConfig(config) {
90
- // If rules don't exist, create them from legacy config
107
+ function migrateConfig(config, defaults = defaultConfig) {
91
108
  if (!config.rules) {
92
109
  config.rules = {
93
110
  js: {
94
111
  enabled: true,
95
- pattern: config.urlPattern || defaultConfig.rules.js.pattern,
96
- redirectUrl: config.redirectUrl || defaultConfig.rules.js.redirectUrl,
97
- useCustomPattern: config.useCustomPattern || false,
112
+ pattern: config.urlPattern || defaults.rules.js.pattern,
113
+ redirectUrl: config.redirectUrl || defaults.rules.js.redirectUrl,
114
+ useCustomPattern: config.useCustomPattern || defaults.rules.js.useCustomPattern,
98
115
  },
99
116
  css: {
100
- enabled: true,
101
- pattern: defaultConfig.rules.css.pattern,
102
- redirectUrl: "",
103
- returnBlank: true,
104
- useCustomPattern: false,
117
+ enabled: defaults.rules.css.enabled,
118
+ pattern: defaults.rules.css.pattern,
119
+ redirectUrl: defaults.rules.css.redirectUrl,
120
+ returnBlank: defaults.rules.css.returnBlank,
121
+ useCustomPattern: defaults.rules.css.useCustomPattern,
105
122
  },
106
123
  };
107
124
  }
108
125
  return config;
109
126
  }
110
127
 
128
+ function setupTabNavigation() {
129
+ tabs.forEach((tab) => {
130
+ tab.addEventListener("click", () => {
131
+ const tabName = tab.dataset.tab;
132
+
133
+ // Update active tab
134
+ tabs.forEach((t) => t.classList.remove("active"));
135
+ tab.classList.add("active");
136
+
137
+ // Show corresponding content
138
+ tabContents.forEach((content) => {
139
+ content.classList.remove("active");
140
+ if (content.id === `${tabName}Tab`) {
141
+ content.classList.add("active");
142
+ }
143
+ });
144
+ });
145
+ });
146
+ }
147
+
111
148
  function updateUI() {
112
149
  // Update toggle button
113
150
  if (config.enabled) {
@@ -121,8 +158,7 @@ document.addEventListener("DOMContentLoaded", async function () {
121
158
  // Update global settings
122
159
  maskingModeCheckbox.checked = config.maskingMode || false;
123
160
  clearCacheOnEnableCheckbox.checked = config.clearCacheOnEnable !== false;
124
- disableCacheForRedirectsCheckbox.checked =
125
- config.disableCacheForRedirects !== false;
161
+ disableCacheForRedirectsCheckbox.checked = config.disableCacheForRedirects !== false;
126
162
 
127
163
  // Update JS rule
128
164
  if (config.rules && config.rules.js) {
@@ -131,10 +167,16 @@ document.addEventListener("DOMContentLoaded", async function () {
131
167
  jsCustomPattern.checked = config.rules.js.useCustomPattern || false;
132
168
  jsCustomPatternInput.value = config.rules.js.pattern || "";
133
169
 
134
- // Toggle JS rule content visibility
170
+ // Update JS badge
135
171
  if (config.rules.js.enabled) {
172
+ jsBadge.textContent = "ON";
173
+ jsBadge.classList.remove("disabled");
174
+ jsBadge.classList.add("enabled");
136
175
  jsRuleContent.classList.remove("rule-disabled");
137
176
  } else {
177
+ jsBadge.textContent = "OFF";
178
+ jsBadge.classList.remove("enabled");
179
+ jsBadge.classList.add("disabled");
138
180
  jsRuleContent.classList.add("rule-disabled");
139
181
  }
140
182
 
@@ -156,10 +198,16 @@ document.addEventListener("DOMContentLoaded", async function () {
156
198
  cssCustomPattern.checked = config.rules.css.useCustomPattern || false;
157
199
  cssCustomPatternInput.value = config.rules.css.pattern || "";
158
200
 
159
- // Toggle CSS rule content visibility
201
+ // Update CSS badge
160
202
  if (config.rules.css.enabled) {
203
+ cssBadge.textContent = "ON";
204
+ cssBadge.classList.remove("disabled");
205
+ cssBadge.classList.add("enabled");
161
206
  cssRuleContent.classList.remove("rule-disabled");
162
207
  } else {
208
+ cssBadge.textContent = "OFF";
209
+ cssBadge.classList.remove("enabled");
210
+ cssBadge.classList.add("disabled");
163
211
  cssRuleContent.classList.add("rule-disabled");
164
212
  }
165
213
 
@@ -182,7 +230,6 @@ document.addEventListener("DOMContentLoaded", async function () {
182
230
  }
183
231
 
184
232
  function updatePatternDisplays() {
185
- // Update JS pattern display
186
233
  if (config.rules && config.rules.js) {
187
234
  const jsPattern = config.rules.js.useCustomPattern
188
235
  ? config.rules.js.pattern
@@ -190,7 +237,6 @@ document.addEventListener("DOMContentLoaded", async function () {
190
237
  jsPatternDisplay.textContent = jsPattern;
191
238
  }
192
239
 
193
- // Update CSS pattern display
194
240
  if (config.rules && config.rules.css) {
195
241
  const cssPattern = config.rules.css.useCustomPattern
196
242
  ? config.rules.css.pattern
@@ -210,23 +256,17 @@ document.addEventListener("DOMContentLoaded", async function () {
210
256
  }
211
257
 
212
258
  function validateRedirectUrl(url) {
213
- if (!url || url.trim() === "") {
214
- return null; // Empty URL is valid for optional fields
215
- }
259
+ if (!url || url.trim() === "") return null;
216
260
 
217
261
  try {
218
262
  new URL(url);
219
- return null; // Valid
263
+ return null;
220
264
  } catch {
221
- // Check if it's a relative-style URL like localhost:3060/path
222
- if (url.includes("://")) {
223
- return "Invalid URL format";
224
- }
265
+ if (url.includes("://")) return "Invalid URL format";
225
266
 
226
- // Try to parse as localhost-style URL
227
267
  try {
228
268
  new URL("https://" + url);
229
- return null; // Valid
269
+ return null;
230
270
  } catch {
231
271
  return "Invalid URL format";
232
272
  }
@@ -234,13 +274,11 @@ document.addEventListener("DOMContentLoaded", async function () {
234
274
  }
235
275
 
236
276
  function validatePattern(pattern) {
237
- if (!pattern || pattern.trim() === "") {
238
- return "URL pattern is required";
239
- }
277
+ if (!pattern || pattern.trim() === "") return "URL pattern is required";
240
278
 
241
279
  try {
242
280
  new RegExp(pattern);
243
- return null; // Valid
281
+ return null;
244
282
  } catch {
245
283
  return "Invalid regular expression pattern";
246
284
  }
@@ -248,9 +286,7 @@ document.addEventListener("DOMContentLoaded", async function () {
248
286
 
249
287
  function normalizeUrl(url) {
250
288
  if (!url || url.trim() === "") return "";
251
- if (!url.includes("://")) {
252
- return "https://" + url;
253
- }
289
+ if (!url.includes("://")) return "https://" + url;
254
290
  return url;
255
291
  }
256
292
 
@@ -262,7 +298,6 @@ document.addEventListener("DOMContentLoaded", async function () {
262
298
  await chrome.storage.sync.set({ enabled: config.enabled });
263
299
  updateUI();
264
300
 
265
- // Send message to background script
266
301
  chrome.runtime.sendMessage({
267
302
  action: "toggleProxy",
268
303
  enabled: config.enabled,
@@ -332,29 +367,23 @@ document.addEventListener("DOMContentLoaded", async function () {
332
367
  this.textContent = "Clearing...";
333
368
  this.disabled = true;
334
369
 
335
- const response = await chrome.runtime.sendMessage({
336
- action: "clearCache",
337
- });
370
+ const response = await chrome.runtime.sendMessage({ action: "clearCache" });
338
371
 
339
372
  if (response.success) {
340
373
  showStatus("Cache cleared successfully");
341
374
  } else {
342
- showStatus(
343
- "Error clearing cache: " + (response.error || "Unknown error"),
344
- false
345
- );
375
+ showStatus("Error clearing cache: " + (response.error || "Unknown error"), false);
346
376
  }
347
377
  } catch (error) {
348
378
  console.error("Error clearing cache:", error);
349
379
  showStatus("Error clearing cache", false);
350
380
  } finally {
351
- this.textContent = "Clear Cache Now";
381
+ this.textContent = "Clear Cache";
352
382
  this.disabled = false;
353
383
  }
354
384
  });
355
385
 
356
386
  saveButton.addEventListener("click", async function () {
357
- // Ensure rules structure exists
358
387
  if (!config.rules) config.rules = {};
359
388
  if (!config.rules.js) config.rules.js = { ...defaultConfig.rules.js };
360
389
  if (!config.rules.css) config.rules.css = { ...defaultConfig.rules.css };
@@ -367,10 +396,7 @@ document.addEventListener("DOMContentLoaded", async function () {
367
396
  : defaultConfig.rules.js.pattern;
368
397
 
369
398
  if (!jsRedirectUrlValue) {
370
- showStatus(
371
- "JavaScript redirect URL is required when JS rule is enabled",
372
- false
373
- );
399
+ showStatus("JavaScript redirect URL is required when JS rule is enabled", false);
374
400
  return;
375
401
  }
376
402
 
@@ -386,7 +412,6 @@ document.addEventListener("DOMContentLoaded", async function () {
386
412
  return;
387
413
  }
388
414
 
389
- // Update JS rule config
390
415
  config.rules.js.redirectUrl = normalizeUrl(jsRedirectUrlValue);
391
416
  config.rules.js.pattern = jsPatternValue;
392
417
  }
@@ -398,12 +423,8 @@ document.addEventListener("DOMContentLoaded", async function () {
398
423
  ? cssCustomPatternInput.value.trim()
399
424
  : defaultConfig.rules.css.pattern;
400
425
 
401
- // Only validate redirect URL if not returning blank
402
426
  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
- );
427
+ showStatus("CSS redirect URL is required when not returning blank", false);
407
428
  return;
408
429
  }
409
430
 
@@ -421,7 +442,6 @@ document.addEventListener("DOMContentLoaded", async function () {
421
442
  return;
422
443
  }
423
444
 
424
- // Update CSS rule config
425
445
  config.rules.css.redirectUrl = normalizeUrl(cssRedirectUrlValue);
426
446
  config.rules.css.pattern = cssPatternValue;
427
447
  }
@@ -429,7 +449,6 @@ document.addEventListener("DOMContentLoaded", async function () {
429
449
  try {
430
450
  await chrome.storage.sync.set(config);
431
451
 
432
- // Send updated config to background script
433
452
  chrome.runtime.sendMessage({
434
453
  action: "updateConfig",
435
454
  config: config,
@@ -450,10 +469,7 @@ document.addEventListener("DOMContentLoaded", async function () {
450
469
  config: config,
451
470
  });
452
471
 
453
- // ============================================================
454
472
  // Component Inspector
455
- // ============================================================
456
-
457
473
  function updateInspectorUI() {
458
474
  if (inspectorEnabled) {
459
475
  inspectorToggle.classList.add("enabled");
@@ -464,45 +480,30 @@ document.addEventListener("DOMContentLoaded", async function () {
464
480
  }
465
481
  }
466
482
 
467
- // Get initial inspector state from content script
468
483
  async function getInspectorState() {
469
484
  try {
470
- const [tab] = await chrome.tabs.query({
471
- active: true,
472
- currentWindow: true,
473
- });
485
+ const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
474
486
  if (tab?.id) {
475
- const response = await chrome.tabs.sendMessage(tab.id, {
476
- action: "getInspectorState",
477
- });
487
+ const response = await chrome.tabs.sendMessage(tab.id, { action: "getInspectorState" });
478
488
  if (response) {
479
489
  inspectorEnabled = response.enabled;
480
490
  updateInspectorUI();
481
491
  }
482
492
  }
483
493
  } catch (error) {
484
- // Content script might not be loaded yet
485
494
  console.log("Could not get inspector state:", error);
486
495
  }
487
496
  }
488
497
 
489
- // Toggle inspector
490
498
  inspectorToggle.addEventListener("click", async function () {
491
499
  try {
492
- const [tab] = await chrome.tabs.query({
493
- active: true,
494
- currentWindow: true,
495
- });
500
+ const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
496
501
  if (tab?.id) {
497
- const response = await chrome.tabs.sendMessage(tab.id, {
498
- action: "toggleInspector",
499
- });
502
+ const response = await chrome.tabs.sendMessage(tab.id, { action: "toggleInspector" });
500
503
  if (response) {
501
504
  inspectorEnabled = response.enabled;
502
505
  updateInspectorUI();
503
- showStatus(
504
- inspectorEnabled ? "Inspector enabled" : "Inspector disabled"
505
- );
506
+ showStatus(inspectorEnabled ? "Inspector enabled" : "Inspector disabled");
506
507
  }
507
508
  }
508
509
  } catch (error) {
@@ -510,6 +511,5 @@ document.addEventListener("DOMContentLoaded", async function () {
510
511
  }
511
512
  });
512
513
 
513
- // Initialize inspector state
514
514
  getInspectorState();
515
515
  });
@@ -62,6 +62,7 @@
62
62
  "content.js",
63
63
  "inspector.js",
64
64
  "panel.html",
65
- "panel.js"
65
+ "panel.js",
66
+ "defaults.json"
66
67
  ]
67
68
  }
@@ -1,30 +1,49 @@
1
- // Configuration defaults
1
+ // Environment defaults loaded from defaults.json (generated by launch script)
2
+ let envDefaults = {};
3
+
4
+ // Load environment defaults synchronously via XHR (needed before DEFAULT_CONFIG)
5
+ try {
6
+ const xhr = new XMLHttpRequest();
7
+ xhr.open('GET', browser.runtime.getURL('defaults.json'), false);
8
+ xhr.send();
9
+ if (xhr.status === 200) {
10
+ envDefaults = JSON.parse(xhr.responseText);
11
+ console.log('[JavaScript Proxy] Loaded environment defaults:', envDefaults);
12
+ }
13
+ } catch (error) {
14
+ console.log('[JavaScript Proxy] No environment defaults found, using hardcoded defaults');
15
+ }
16
+
17
+ // Build default redirect URL from env defaults or fallback
18
+ const defaultRedirectUrl = envDefaults.jsRedirectUrl || "https://localhost:3060/src/Plugin.vue";
19
+
20
+ // Configuration defaults (merged with environment defaults)
2
21
  const DEFAULT_CONFIG = {
3
- enabled: false,
22
+ enabled: envDefaults.enabled || false,
4
23
  // Legacy fields for backward compatibility
5
- redirectUrl: "https://localhost:3060/src/Plugin.vue",
24
+ redirectUrl: defaultRedirectUrl,
6
25
  urlPattern: "uploads\\/plugin-version\\/\\d+\\/file_name\\/.*\\.js(\\?.*)?",
7
- useCustomPattern: false,
26
+ useCustomPattern: envDefaults.jsUseCustomPattern || false,
8
27
  // New rules-based configuration
9
28
  rules: {
10
29
  js: {
11
30
  enabled: true,
12
31
  pattern: "uploads\\/plugin-version\\/\\d+\\/file_name\\/.*\\.js(\\?.*)?",
13
- redirectUrl: "https://localhost:3060/src/Plugin.vue",
14
- useCustomPattern: false,
32
+ redirectUrl: defaultRedirectUrl,
33
+ useCustomPattern: envDefaults.jsUseCustomPattern || false,
15
34
  },
16
35
  css: {
17
- enabled: true,
36
+ enabled: envDefaults.cssRuleEnabled !== false,
18
37
  pattern:
19
38
  "uploads\\/plugin-version\\/\\d+\\/style_file_name\\/.*\\.css(\\?.*)?",
20
- redirectUrl: "",
21
- returnBlank: true,
22
- useCustomPattern: false,
39
+ redirectUrl: envDefaults.cssRedirectUrl || "",
40
+ returnBlank: envDefaults.cssReturnBlank !== false,
41
+ useCustomPattern: envDefaults.cssUseCustomPattern || false,
23
42
  },
24
43
  },
25
44
  maskingMode: false,
26
- clearCacheOnEnable: true,
27
- disableCacheForRedirects: true,
45
+ clearCacheOnEnable: envDefaults.clearCacheOnEnable !== false,
46
+ disableCacheForRedirects: envDefaults.disableCacheForRedirects !== false,
28
47
  };
29
48
 
30
49
  // Global state
@@ -1 +1 @@
1
- {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../bin/lib/tui/App.tsx"],"names":[],"mappings":"AAQA,OAAO,EAUL,aAAa,EAKd,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,aAAa,CAAC;IACtB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAUD,MAAM,WAAW,QAAQ;IACvB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,CAAC,OAAO,UAAU,GAAG,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,QAAQ,2CAqpBxD"}
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../bin/lib/tui/App.tsx"],"names":[],"mappings":"AAQA,OAAO,EAUL,aAAa,EAKd,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,aAAa,CAAC;IACtB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAUD,MAAM,WAAW,QAAQ;IACvB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,CAAC,OAAO,UAAU,GAAG,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,QAAQ,2CAyqBxD"}
package/dist/tui/App.js CHANGED
@@ -82,15 +82,7 @@ export default function App({ autoStart, args }) {
82
82
  }
83
83
  return;
84
84
  }
85
- // Tab to cycle through tabs (when not in input)
86
- if (key.tab && services.length > 0) {
87
- const nextTab = key.shift
88
- ? (activeTab - 1 + services.length) % services.length
89
- : (activeTab + 1) % services.length;
90
- setActiveTab(nextTab);
91
- return;
92
- }
93
- // Left/Right arrow to switch tabs
85
+ // Left/Right arrow to switch tabs (Tab is reserved for command autocomplete)
94
86
  if (key.leftArrow && services.length > 0) {
95
87
  setActiveTab((activeTab - 1 + services.length) % services.length);
96
88
  return;
@@ -99,7 +91,7 @@ export default function App({ autoStart, args }) {
99
91
  setActiveTab((activeTab + 1) % services.length);
100
92
  return;
101
93
  }
102
- // Ctrl+1-9 or Cmd+1-9 to switch tabs (for compatibility)
94
+ // Ctrl+1-9 or Cmd+1-9 to switch tabs directly
103
95
  if ((key.ctrl || key.meta) && /^[1-9]$/.test(input)) {
104
96
  const tabIndex = parseInt(input) - 1;
105
97
  if (tabIndex < services.length) {
@@ -433,11 +425,21 @@ export default function App({ autoStart, args }) {
433
425
  const overwrite = cmdArgs.includes('--overwrite') || cmdArgs.includes('-o');
434
426
  addSystemLog('Scanning source files for GxP configuration...');
435
427
  try {
436
- // Use require for CommonJS modules
437
- const path = require('path');
438
- const fs = require('fs');
439
- // eslint-disable-next-line @typescript-eslint/no-var-requires
440
- const extractConfigUtils = require('../utils/extract-config.js');
428
+ // Use dynamic imports for ES modules
429
+ const path = await import('path');
430
+ const fs = await import('fs');
431
+ const url = await import('url');
432
+ const { createRequire } = await import('module');
433
+ // Get the directory of this file and resolve to the utils directory
434
+ const __filename = url.fileURLToPath(import.meta.url);
435
+ const __dirname = path.dirname(__filename);
436
+ // The compiled JS is in dist/tui/, utils is in bin/lib/utils/
437
+ // From dist/tui/ we need to go up to package root, then into bin/lib/utils/
438
+ const packageRoot = path.resolve(__dirname, '..', '..');
439
+ const utilsPath = path.join(packageRoot, 'bin', 'lib', 'utils', 'extract-config.js');
440
+ // Create a require function to load CommonJS modules
441
+ const requireCjs = createRequire(import.meta.url);
442
+ const extractConfigUtils = requireCjs(utilsPath);
441
443
  const projectPath = process.cwd();
442
444
  const srcDir = path.join(projectPath, 'src');
443
445
  const manifestPath = path.join(projectPath, 'app-manifest.json');
@@ -507,43 +509,59 @@ export default function App({ autoStart, args }) {
507
509
  });
508
510
  const getHelpText = () => `
509
511
  Available commands:
510
- /dev Start Vite (+ Socket if SOCKET_IO_ENABLED=true)
511
- /dev --with-socket Start Vite + Socket.IO together
512
- /dev --with-mock Start Vite + Socket.IO + Mock API server
513
- /dev --no-socket Start Vite only (skip Socket.IO)
514
- /dev --no-https Start Vite without SSL
515
- /dev --firefox Start Vite + Firefox extension
516
- /dev --chrome Start Vite + Chrome extension
517
- /socket Start Socket.IO server
518
- /socket --with-mock Start Socket.IO with Mock API enabled
519
- /socket send <event> Send socket event
520
- /socket list List available events
521
- /mock Start Socket.IO + Mock API (shorthand)
522
- /ext chrome Launch Chrome extension
523
- /ext firefox Launch Firefox extension
524
- /extract-config Extract GxP config from source to manifest
525
- /extract-config -d Dry run (show what would be extracted)
526
- /extract-config -o Overwrite existing values in manifest
527
- /stop [service] Stop a running service
528
- /restart [service] Restart a service
529
- /clear Clear current log panel
530
- /gemini Open Gemini AI chat panel
531
- /gemini enable Set up Google authentication
532
- /gemini ask <query> Quick question to Gemini
533
- /gemini status Check authentication status
534
- /gemini logout Log out from Gemini
535
- /help Show this help message
536
- /quit Exit the application
512
+
513
+ Development Server:
514
+ /dev Start Vite dev server
515
+ /dev --with-socket Start Vite + Socket.IO
516
+ /dev --with-mock Start Vite + Socket.IO + Mock API
517
+ /dev --no-https Start without SSL
518
+ /dev --no-socket Start without Socket.IO
519
+ /dev --chrome Start + launch Chrome extension
520
+ /dev --firefox Start + launch Firefox extension
521
+
522
+ Socket.IO:
523
+ /socket Start Socket.IO server
524
+ /socket --with-mock Start with Mock API enabled
525
+ /socket send <event> Send a socket event
526
+ /socket list List available events
527
+ /mock Shorthand for /socket --with-mock
528
+
529
+ Browser Extensions:
530
+ /ext chrome Launch Chrome with GxP extension
531
+ /ext firefox Launch Firefox with GxP extension
532
+
533
+ Config Extraction:
534
+ /extract-config Extract GxP config from source
535
+ /extract-config -d Dry run (preview changes)
536
+ /extract-config -o Overwrite existing values
537
+
538
+ AI Assistant:
539
+ /gemini Open Gemini AI chat panel
540
+ /gemini enable Set up Google authentication
541
+ /gemini ask <query> Quick question to AI
542
+ /gemini status Check auth status
543
+ /gemini logout Log out from Gemini
544
+ /gemini clear Clear conversation history
545
+ /ai Alias for /gemini
546
+
547
+ Service Management:
548
+ /stop [service] Stop current or specified service
549
+ /restart [service] Restart a service
550
+ /clear Clear current log panel
551
+ /help Show this help
552
+ /quit Exit application
537
553
 
538
554
  Keyboard shortcuts:
539
- Tab / Shift+Tab Cycle through tabs
540
- Left/Right Switch tabs
541
- Cmd+1/2/3... Jump to tab (Mac)
542
- Shift+Up/Down Scroll logs
543
- Cmd+Up/Down Jump to top/bottom of logs
555
+ ←/→ Switch tabs
556
+ Ctrl+1/2/3... Jump to tab directly
557
+ Shift+↑/↓ Scroll logs
558
+ Ctrl+↑/↓ Jump to top/bottom of logs
544
559
  Ctrl+L Clear current log
560
+ Ctrl+K Stop current service
545
561
  Ctrl+C Exit application
546
- Up/Down Command history (in input)
562
+ Tab Autocomplete command
563
+ ↑/↓ Navigate suggestions or command history
564
+ Esc Clear input
547
565
  `;
548
566
  // Show Gemini panel
549
567
  if (showGemini) {