vg-coder-cli 2.0.25 → 2.0.26

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.
@@ -1,4 +1,6 @@
1
1
  import { ScriptInjector } from './script-injector';
2
+ import { isAIChatDomain } from './utils/ai-domains';
3
+ import { VG_CODER_INJECTOR_SCRIPT } from './utils/injector-script';
2
4
 
3
5
  const addScript = async (script: string, actionType: string) => {
4
6
  try {
@@ -21,11 +23,83 @@ const waitForDOM = (): Promise<void> => {
21
23
  });
22
24
  };
23
25
 
26
+ /**
27
+ * Multi-layer detection to check if current page is loaded inside VG Coder's iframe
28
+ * Returns true if this is a nested context (should skip injection)
29
+ */
30
+ const detectVGCoderContext = async (): Promise<boolean> => {
31
+ // Layer 1: URL parameter check (fastest)
32
+ const urlParams = new URLSearchParams(window.location.search);
33
+ if (urlParams.has('vg_coder_context')) {
34
+ console.log('⚡ VG Coder context detected: URL parameter');
35
+ return true;
36
+ }
37
+
38
+ // Layer 2: Referrer check (backup)
39
+ const referrer = document.referrer;
40
+ if (referrer && (referrer.includes(':6868') || referrer.includes('vg-coder'))) {
41
+ console.log('⚡ VG Coder context detected: Referrer contains :6868');
42
+ return true;
43
+ }
44
+
45
+ // Layer 3: PostMessage ping (most reliable for iframe detection)
46
+ if (window.self !== window.top) {
47
+ try {
48
+ const isVGCoder = await new Promise<boolean>((resolve) => {
49
+ const timeout = setTimeout(() => resolve(false), 200);
50
+
51
+ const handler = (event: MessageEvent) => {
52
+ if (event.data?.type === 'VG_CODER_PARENT') {
53
+ clearTimeout(timeout);
54
+ window.removeEventListener('message', handler);
55
+ resolve(true);
56
+ }
57
+ };
58
+
59
+ window.addEventListener('message', handler);
60
+ window.parent.postMessage({ type: 'VG_CODER_PING' }, '*');
61
+ });
62
+
63
+ if (isVGCoder) {
64
+ console.log('⚡ VG Coder context detected: PostMessage confirmation');
65
+ return true;
66
+ }
67
+ } catch (e) {
68
+ console.log('PostMessage detection failed (expected for cross-origin):', e);
69
+ }
70
+ }
71
+
72
+ console.log('✅ Normal context - not inside VG Coder iframe');
73
+ return false;
74
+ };
75
+
24
76
  const initializeController = async () => {
25
77
  try {
78
+ // Check if we're in VG Coder's iframe context
79
+ const isVGCoderNested = await detectVGCoderContext();
80
+
81
+ if (isVGCoderNested) {
82
+ console.log('🚫 VG Coder nested iframe context detected - skipping all script injections');
83
+ sessionStorage.setItem('VG_CODER_NESTED', 'true');
84
+ return; // Early exit - don't inject any scripts
85
+ }
86
+
26
87
  // Ensure DOM is ready
27
88
  await waitForDOM();
28
89
 
90
+ const currentHostname = window.location.hostname.replace(/(https?:\/\/)?(www.)?/i, '');
91
+
92
+ // Check if this is an AI chat domain - use bundled injector
93
+ if (isAIChatDomain(currentHostname)) {
94
+ console.log('🤖 AI chat domain detected:', currentHostname);
95
+ console.log('📦 Using bundled VG Coder injector (no Firebase needed)');
96
+
97
+ // Execute bundled injector script
98
+ await addScript(VG_CODER_INJECTOR_SCRIPT, 'VG_CODER_INJECTOR');
99
+ return; // Done - no need for Firebase script
100
+ }
101
+
102
+ // For non-AI domains, continue with Firebase script loading
29
103
  const actionType = new URL(window.location.href).searchParams.get("actionType") || "MAIN";
30
104
  const cache = sessionStorage.getItem(actionType);
31
105
 
@@ -34,7 +108,7 @@ const initializeController = async () => {
34
108
  } else {
35
109
  chrome.runtime.sendMessage({
36
110
  action: "CONTROLLER",
37
- domain: window.location.hostname.replace(/(https?:\/\/)?(www.)?/i, ''),
111
+ domain: currentHostname,
38
112
  actionType: actionType
39
113
  }, async (response) => {
40
114
  if (chrome.runtime.lastError) {
@@ -0,0 +1,33 @@
1
+ // AI Chat Providers Configuration
2
+ // Domains for automatic VG Coder iframe injection
3
+
4
+ export const AI_DOMAINS = [
5
+ 'chat.openai.com', // ChatGPT (old URL)
6
+ 'chatgpt.com', // ChatGPT (new URL)
7
+ 'gemini.google.com', // Google Gemini
8
+ 'aistudio.google.com', // Google AI Studio
9
+ 'chat.deepseek.com', // DeepSeek
10
+ 'kimi.com', // Kimi AI
11
+ 'www.kimi.com', // Kimi AI (www)
12
+ 'grok.com', // Grok
13
+ 'claude.ai', // Claude (Anthropic)
14
+ 'poe.com', // Poe
15
+ 'perplexity.ai', // Perplexity
16
+ 'www.perplexity.ai', // Perplexity (www)
17
+ ];
18
+
19
+ /**
20
+ * Check if domain should have VG Coder iframe injected
21
+ */
22
+ export function isAIChatDomain(hostname: string): boolean {
23
+ const cleanHostname = hostname.toLowerCase().replace(/^www\./, '');
24
+ console.log('🔍 Checking AI domain:', cleanHostname);
25
+
26
+ const isMatch = AI_DOMAINS.some(domain => {
27
+ const cleanDomain = domain.toLowerCase().replace(/^www\./, '');
28
+ return cleanHostname === cleanDomain || cleanHostname.endsWith('.' + cleanDomain);
29
+ });
30
+
31
+ console.log('🎯 AI domain match:', isMatch);
32
+ return isMatch;
33
+ }
@@ -0,0 +1,251 @@
1
+ // VG Coder Iframe Injector Script (Bundled for Extension)
2
+ // Two-Button Toggle with hidden fullscreen when collapsed
3
+
4
+ export const VG_CODER_INJECTOR_SCRIPT = `
5
+ (function() {
6
+ 'use strict';
7
+
8
+ const CONFIG = {
9
+ VG_CODER_URL: 'http://localhost:6868?embedded=true',
10
+ IFRAME_ID: 'vg-coder-side-iframe',
11
+ CONTAINER_ID: 'vg-coder-container',
12
+ CONTROLS_ID: 'vg-coder-controls',
13
+ RESIZE_HANDLE_ID: 'vg-coder-resize-handle',
14
+ DEFAULT_WIDTH: '420px',
15
+ FULLSCREEN_WIDTH: '100%',
16
+ MIN_WIDTH: 300,
17
+ MAX_WIDTH_PERCENT: 70,
18
+ STATE_DEFAULT: 'default',
19
+ STATE_FULLSCREEN: 'fullscreen',
20
+ STATE_COLLAPSED: 'collapsed',
21
+ Z_INDEX: 9999,
22
+ STORAGE_KEY_WIDTH: 'vg_coder_width',
23
+ STORAGE_KEY_STATE: 'vg_coder_state',
24
+ };
25
+
26
+ if (sessionStorage.getItem('VG_CODER_NESTED') === 'true') {
27
+ console.log('🚫 VG Coder iframe injection skipped - nested context');
28
+ return;
29
+ }
30
+
31
+ if (window.self !== window.top && document.referrer.includes(':6868')) {
32
+ console.log('🚫 VG Coder iframe injection skipped - parent is :6868');
33
+ return;
34
+ }
35
+
36
+ if (document.getElementById(CONFIG.CONTAINER_ID)) {
37
+ console.log('⚠️ VG Coder iframe already injected');
38
+ return;
39
+ }
40
+
41
+ function getState() {
42
+ return localStorage.getItem(CONFIG.STORAGE_KEY_STATE) || CONFIG.STATE_DEFAULT;
43
+ }
44
+
45
+ function getWidth() {
46
+ return localStorage.getItem(CONFIG.STORAGE_KEY_WIDTH) || CONFIG.DEFAULT_WIDTH;
47
+ }
48
+
49
+ function setState(state) {
50
+ localStorage.setItem(CONFIG.STORAGE_KEY_STATE, state);
51
+ }
52
+
53
+ function setWidth(width) {
54
+ localStorage.setItem(CONFIG.STORAGE_KEY_WIDTH, width);
55
+ }
56
+
57
+ function injectStyles() {
58
+ const style = document.createElement('style');
59
+ style.textContent = '#' + CONFIG.CONTAINER_ID + ' {' +
60
+ 'position: fixed; top: 0; left: 0; bottom: 0;' +
61
+ 'width: ' + getWidth() + ';' +
62
+ 'background: #1a1a1a; z-index: ' + CONFIG.Z_INDEX + ';' +
63
+ 'display: flex; flex-direction: column;' +
64
+ 'box-shadow: 4px 0 12px rgba(0, 0, 0, 0.3);' +
65
+ 'transition: width 0.3s ease, transform 0.3s ease;' +
66
+ '}' +
67
+ '#' + CONFIG.CONTAINER_ID + '.collapsed { transform: translateX(-100%); }' +
68
+ '#' + CONFIG.IFRAME_ID + ' {' +
69
+ 'flex: 1; border: none; width: 100%; height: 100%; background: white;' +
70
+ '}' +
71
+ '#' + CONFIG.CONTROLS_ID + ' {' +
72
+ 'position: absolute; top: 50%; right: -36px; transform: translateY(-50%);' +
73
+ 'z-index: ' + (CONFIG.Z_INDEX + 1) + '; display: flex; flex-direction: column; gap: 4px;' +
74
+ 'transition: right 0.3s ease;' +
75
+ '}' +
76
+ '#' + CONFIG.CONTROLS_ID + '.fullscreen {' +
77
+ 'right: 12px;' +
78
+ '}' +
79
+ '#' + CONFIG.CONTROLS_ID + ' button {' +
80
+ 'background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);' +
81
+ 'color: white; border: none; border-radius: 0 6px 6px 0;' +
82
+ 'padding: 8px 6px; cursor: pointer; font-size: 18px;' +
83
+ 'box-shadow: -2px 2px 6px rgba(0, 0, 0, 0.2);' +
84
+ 'transition: all 0.2s ease; width: 32px; height: 32px;' +
85
+ 'display: flex; align-items: center; justify-content: center;' +
86
+ '}' +
87
+ '#' + CONFIG.CONTROLS_ID + ' button.hidden {' +
88
+ 'display: none;' +
89
+ '}' +
90
+ '#' + CONFIG.CONTROLS_ID + ' button:hover {' +
91
+ 'background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);' +
92
+ 'box-shadow: -3px 3px 8px rgba(0, 0, 0, 0.3);' +
93
+ '}' +
94
+ '#' + CONFIG.RESIZE_HANDLE_ID + ' {' +
95
+ 'position: absolute; right: 0; top: 0; bottom: 0; width: 6px;' +
96
+ 'cursor: ew-resize; background: transparent; transition: background 0.2s ease;' +
97
+ '}' +
98
+ '#' + CONFIG.RESIZE_HANDLE_ID + ':hover { background: rgba(102, 126, 234, 0.5); }' +
99
+ '#' + CONFIG.RESIZE_HANDLE_ID + '.resizing { background: rgba(102, 126, 234, 0.8); }';
100
+ document.head.appendChild(style);
101
+ }
102
+
103
+ function createContainer() {
104
+ const container = document.createElement('div');
105
+ container.id = CONFIG.CONTAINER_ID;
106
+ const currentState = getState();
107
+ if (currentState === CONFIG.STATE_COLLAPSED) {
108
+ container.classList.add('collapsed');
109
+ } else if (currentState === CONFIG.STATE_FULLSCREEN) {
110
+ container.style.width = CONFIG.FULLSCREEN_WIDTH;
111
+ }
112
+ return container;
113
+ }
114
+
115
+ function createIframe() {
116
+ const iframe = document.createElement('iframe');
117
+ iframe.id = CONFIG.IFRAME_ID;
118
+ iframe.src = CONFIG.VG_CODER_URL;
119
+ iframe.title = 'VG Coder Dashboard';
120
+ iframe.allow = 'clipboard-read; clipboard-write';
121
+ return iframe;
122
+ }
123
+
124
+ function createResizeHandle() {
125
+ const handle = document.createElement('div');
126
+ handle.id = CONFIG.RESIZE_HANDLE_ID;
127
+ return handle;
128
+ }
129
+
130
+ function createControls() {
131
+ const controls = document.createElement('div');
132
+ controls.id = CONFIG.CONTROLS_ID;
133
+ const currentState = getState();
134
+ if (currentState === CONFIG.STATE_FULLSCREEN) {
135
+ controls.classList.add('fullscreen');
136
+ }
137
+ const btnFullscreen = document.createElement('button');
138
+ btnFullscreen.innerHTML = currentState === CONFIG.STATE_FULLSCREEN ? '◧' : '▣';
139
+ btnFullscreen.title = currentState === CONFIG.STATE_FULLSCREEN ? 'Default (45%)' : 'Fullscreen (100%)';
140
+ if (currentState === CONFIG.STATE_COLLAPSED) {
141
+ btnFullscreen.classList.add('hidden');
142
+ }
143
+ const btnCollapse = document.createElement('button');
144
+ btnCollapse.innerHTML = currentState === CONFIG.STATE_COLLAPSED ? '▶' : '◄';
145
+ btnCollapse.title = currentState === CONFIG.STATE_COLLAPSED ? 'Expand' : 'Collapse';
146
+ controls.appendChild(btnFullscreen);
147
+ controls.appendChild(btnCollapse);
148
+ return controls;
149
+ }
150
+
151
+ function initResize(container, handle, controls) {
152
+ let isResizing = false, startX = 0, startWidth = 0;
153
+ handle.addEventListener('mousedown', (e) => {
154
+ isResizing = true; startX = e.clientX; startWidth = container.offsetWidth;
155
+ handle.classList.add('resizing');
156
+ document.body.style.cursor = 'ew-resize';
157
+ document.body.style.userSelect = 'none';
158
+ e.preventDefault();
159
+ });
160
+ document.addEventListener('mousemove', (e) => {
161
+ if (!isResizing) return;
162
+ const deltaX = e.clientX - startX;
163
+ const newWidth = startWidth + deltaX;
164
+ const maxWidth = window.innerWidth * (CONFIG.MAX_WIDTH_PERCENT / 100);
165
+ if (newWidth >= CONFIG.MIN_WIDTH && newWidth <= maxWidth) {
166
+ const widthPercent = (newWidth / window.innerWidth) * 100;
167
+ container.style.width = widthPercent + '%';
168
+ setWidth(widthPercent + '%');
169
+ }
170
+ });
171
+ document.addEventListener('mouseup', () => {
172
+ if (isResizing) {
173
+ isResizing = false; handle.classList.remove('resizing');
174
+ document.body.style.cursor = '';
175
+ document.body.style.userSelect = '';
176
+ }
177
+ });
178
+ }
179
+
180
+ function initControls(container, controls) {
181
+ const buttons = controls.querySelectorAll('button');
182
+ const btnFullscreen = buttons[0];
183
+ const btnCollapse = buttons[1];
184
+ btnFullscreen.addEventListener('click', () => {
185
+ const currentState = getState();
186
+ if (currentState === CONFIG.STATE_FULLSCREEN) {
187
+ setState(CONFIG.STATE_DEFAULT);
188
+ container.style.width = CONFIG.DEFAULT_WIDTH;
189
+ controls.classList.remove('fullscreen');
190
+ btnFullscreen.innerHTML = '▣';
191
+ btnFullscreen.title = 'Fullscreen (100%)';
192
+ } else {
193
+ setState(CONFIG.STATE_FULLSCREEN);
194
+ container.style.width = CONFIG.FULLSCREEN_WIDTH;
195
+ controls.classList.add('fullscreen');
196
+ btnFullscreen.innerHTML = '◧';
197
+ btnFullscreen.title = 'Default (45%)';
198
+ }
199
+ });
200
+ btnCollapse.addEventListener('click', () => {
201
+ const currentState = getState();
202
+ if (currentState === CONFIG.STATE_COLLAPSED) {
203
+ setState(CONFIG.STATE_DEFAULT);
204
+ container.classList.remove('collapsed');
205
+ container.style.width = CONFIG.DEFAULT_WIDTH;
206
+ controls.classList.remove('fullscreen');
207
+ btnCollapse.innerHTML = '◄';
208
+ btnCollapse.title = 'Collapse';
209
+ btnFullscreen.classList.remove('hidden');
210
+ btnFullscreen.innerHTML = '▣';
211
+ btnFullscreen.title = 'Fullscreen (100%)';
212
+ } else {
213
+ const wasFullscreen = currentState === CONFIG.STATE_FULLSCREEN;
214
+ setState(CONFIG.STATE_COLLAPSED);
215
+ container.classList.add('collapsed');
216
+ if (wasFullscreen) {
217
+ controls.classList.remove('fullscreen');
218
+ }
219
+ btnFullscreen.classList.add('hidden');
220
+ btnCollapse.innerHTML = '▶';
221
+ btnCollapse.title = 'Expand';
222
+ }
223
+ });
224
+ }
225
+
226
+ function init() {
227
+ if (!document.body) {
228
+ setTimeout(init, 100);
229
+ return;
230
+ }
231
+ console.log('🚀 Initializing VG Coder iframe injection...');
232
+ injectStyles();
233
+ const container = createContainer();
234
+ const iframe = createIframe();
235
+ const resizeHandle = createResizeHandle();
236
+ const controls = createControls();
237
+ container.appendChild(resizeHandle);
238
+ container.appendChild(iframe);
239
+ container.appendChild(controls);
240
+ document.body.appendChild(container);
241
+ initResize(container, resizeHandle, controls);
242
+ initControls(container, controls);
243
+ console.log('✅ VG Coder iframe injected successfully');
244
+ }
245
+ if (document.readyState === 'loading') {
246
+ document.addEventListener('DOMContentLoaded', init);
247
+ } else {
248
+ init();
249
+ }
250
+ })();
251
+ `;
Binary file
Binary file
Binary file
Binary file
Binary file