vibecodingmachine-core 1.0.1 → 2025.11.2-7.1239

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 (49) hide show
  1. package/.babelrc +13 -13
  2. package/README.md +28 -28
  3. package/__tests__/applescript-manager-claude-fix.test.js +286 -286
  4. package/__tests__/requirement-2-auto-start-looping.test.js +69 -69
  5. package/__tests__/requirement-3-auto-start-looping.test.js +69 -69
  6. package/__tests__/requirement-4-auto-start-looping.test.js +69 -69
  7. package/__tests__/requirement-6-auto-start-looping.test.js +73 -73
  8. package/__tests__/requirement-7-status-tracking.test.js +332 -332
  9. package/jest.config.js +18 -18
  10. package/jest.setup.js +12 -12
  11. package/package.json +48 -48
  12. package/src/auth/access-denied.html +119 -119
  13. package/src/auth/shared-auth-storage.js +230 -230
  14. package/src/autonomous-mode/feature-implementer.cjs +70 -70
  15. package/src/autonomous-mode/feature-implementer.js +425 -425
  16. package/src/chat-management/chat-manager.cjs +71 -71
  17. package/src/chat-management/chat-manager.js +342 -342
  18. package/src/ide-integration/__tests__/applescript-manager-thread-closure.test.js +227 -227
  19. package/src/ide-integration/aider-cli-manager.cjs +850 -850
  20. package/src/ide-integration/applescript-manager.cjs +1088 -1088
  21. package/src/ide-integration/applescript-manager.js +2802 -2802
  22. package/src/ide-integration/applescript-utils.js +306 -306
  23. package/src/ide-integration/cdp-manager.cjs +221 -221
  24. package/src/ide-integration/cdp-manager.js +321 -321
  25. package/src/ide-integration/claude-code-cli-manager.cjs +301 -301
  26. package/src/ide-integration/cline-cli-manager.cjs +2252 -2252
  27. package/src/ide-integration/continue-cli-manager.js +431 -431
  28. package/src/ide-integration/provider-manager.cjs +354 -354
  29. package/src/ide-integration/quota-detector.cjs +34 -34
  30. package/src/ide-integration/quota-detector.js +349 -349
  31. package/src/ide-integration/windows-automation-manager.js +262 -262
  32. package/src/index.cjs +47 -43
  33. package/src/index.js +17 -17
  34. package/src/llm/direct-llm-manager.cjs +609 -609
  35. package/src/ui/ButtonComponents.js +247 -247
  36. package/src/ui/ChatInterface.js +499 -499
  37. package/src/ui/StateManager.js +259 -259
  38. package/src/utils/audit-logger.cjs +116 -116
  39. package/src/utils/config-helpers.cjs +94 -94
  40. package/src/utils/config-helpers.js +94 -94
  41. package/src/utils/electron-update-checker.js +113 -85
  42. package/src/utils/gcloud-auth.cjs +394 -394
  43. package/src/utils/logger.cjs +193 -193
  44. package/src/utils/logger.js +191 -191
  45. package/src/utils/repo-helpers.cjs +120 -120
  46. package/src/utils/repo-helpers.js +120 -120
  47. package/src/utils/requirement-helpers.js +432 -432
  48. package/src/utils/update-checker.js +227 -167
  49. package/src/utils/version-checker.js +169 -0
@@ -1,499 +1,499 @@
1
- /**
2
- * Shared Chat Interface Component
3
- * Provides a consistent UI across electron app and VSCode extension
4
- */
5
-
6
- import { stateManager } from './StateManager.js';
7
- import {
8
- createAutonomousButton,
9
- createPauseButton,
10
- createStopButton,
11
- createCheckQuotaButton,
12
- createOpenIdeButton,
13
- createIdeButton,
14
- createControlButtonContainer,
15
- updateAutonomousButton,
16
- updatePauseButton,
17
- updateStopButton,
18
- updateCheckQuotaButton,
19
- updateOpenIdeButton
20
- } from './ButtonComponents.js';
21
-
22
- export class ChatInterface {
23
- constructor(container, options = {}) {
24
- this.container = container;
25
- this.options = {
26
- showIdeSelector: true,
27
- showChatMessages: true,
28
- showInput: true,
29
- showControls: true,
30
- theme: 'default', // 'default', 'vscode', 'electron'
31
- ...options
32
- };
33
-
34
- this.elements = {};
35
- this.state = stateManager.getState();
36
- this.unsubscribe = null;
37
-
38
- this.init();
39
- }
40
-
41
- init() {
42
- this.render();
43
- this.bindEvents();
44
- this.subscribeToState();
45
- }
46
-
47
- render() {
48
- this.container.innerHTML = '';
49
-
50
- // Create header
51
- const header = this.createHeader();
52
- this.container.appendChild(header);
53
-
54
- // Create IDE selector
55
- if (this.options.showIdeSelector) {
56
- const ideSelector = this.createIdeSelector();
57
- this.container.appendChild(ideSelector);
58
- }
59
-
60
- // Create status
61
- const status = this.createStatus();
62
- this.container.appendChild(status);
63
-
64
- // Create chat messages
65
- if (this.options.showChatMessages) {
66
- const messages = this.createMessages();
67
- this.container.appendChild(messages);
68
- }
69
-
70
- // Create input area
71
- if (this.options.showInput) {
72
- const input = this.createInput();
73
- this.container.appendChild(input);
74
- }
75
-
76
- // Create autonomous button
77
- const autonomousButton = this.createAutonomousButton();
78
- this.container.appendChild(autonomousButton);
79
-
80
- // Create control buttons
81
- if (this.options.showControls) {
82
- const controls = this.createControlButtons();
83
- this.container.appendChild(controls);
84
- }
85
- }
86
-
87
- createHeader() {
88
- const header = document.createElement('div');
89
- header.className = 'chat-header';
90
- header.innerHTML = `
91
- <h1>🤖 VibeCodingMachine Chat</h1>
92
- `;
93
-
94
- Object.assign(header.style, {
95
- textAlign: 'center',
96
- marginBottom: '20px'
97
- });
98
-
99
- const title = header.querySelector('h1');
100
- Object.assign(title.style, {
101
- color: this.getThemeColor('primary'),
102
- margin: '0',
103
- fontSize: '24px',
104
- fontWeight: '600'
105
- });
106
-
107
- this.elements.header = header;
108
- return header;
109
- }
110
-
111
- createIdeSelector() {
112
- const container = document.createElement('div');
113
- container.className = 'ide-selector';
114
-
115
- Object.assign(container.style, {
116
- marginBottom: '20px',
117
- textAlign: 'center'
118
- });
119
-
120
- const ides = ['vscode', 'cursor', 'windsurf'];
121
-
122
- ides.forEach(ide => {
123
- const button = createIdeButton({
124
- ide,
125
- isActive: this.state.currentIde === ide,
126
- onClick: () => this.onIdeSelect(ide)
127
- });
128
- container.appendChild(button);
129
- });
130
-
131
- const openIdeButton = createOpenIdeButton({
132
- isOpeningIde: this.state.isOpeningIde,
133
- onClick: () => this.onOpenIde()
134
- });
135
- container.appendChild(openIdeButton);
136
-
137
- this.elements.ideSelector = container;
138
- this.elements.ideButtons = container.querySelectorAll('.ide-button');
139
- this.elements.openIdeButton = openIdeButton;
140
- return container;
141
- }
142
-
143
- createStatus() {
144
- const status = document.createElement('div');
145
- status.className = 'status';
146
- status.textContent = this.state.status;
147
-
148
- Object.assign(status.style, {
149
- textAlign: 'center',
150
- margin: '10px 0',
151
- fontStyle: 'italic',
152
- color: this.getThemeColor('secondary')
153
- });
154
-
155
- this.elements.status = status;
156
- return status;
157
- }
158
-
159
- createMessages() {
160
- const container = document.createElement('div');
161
- container.className = 'chat-messages';
162
-
163
- Object.assign(container.style, {
164
- height: '400px',
165
- overflowY: 'auto',
166
- border: `1px solid ${this.getThemeColor('border')}`,
167
- borderRadius: '4px',
168
- padding: '10px',
169
- marginBottom: '20px',
170
- backgroundColor: this.getThemeColor('background')
171
- });
172
-
173
- // Add welcome message
174
- const welcome = document.createElement('div');
175
- welcome.className = 'message assistant';
176
- welcome.textContent = 'Welcome to VibeCodingMachine! Select an IDE and start chatting.';
177
-
178
- Object.assign(welcome.style, {
179
- marginBottom: '15px',
180
- padding: '10px',
181
- borderRadius: '4px',
182
- backgroundColor: this.getThemeColor('messageBackground'),
183
- border: `1px solid ${this.getThemeColor('border')}`,
184
- marginRight: '20%'
185
- });
186
-
187
- container.appendChild(welcome);
188
-
189
- this.elements.messages = container;
190
- return container;
191
- }
192
-
193
- createInput() {
194
- const container = document.createElement('div');
195
- container.className = 'input-container';
196
-
197
- Object.assign(container.style, {
198
- display: 'flex',
199
- gap: '10px',
200
- marginBottom: '10px'
201
- });
202
-
203
- const input = document.createElement('input');
204
- input.type = 'text';
205
- input.className = 'message-input';
206
- input.placeholder = 'Type your message...';
207
-
208
- Object.assign(input.style, {
209
- flex: '1',
210
- padding: '10px',
211
- border: `1px solid ${this.getThemeColor('border')}`,
212
- borderRadius: '4px',
213
- backgroundColor: this.getThemeColor('background'),
214
- color: this.getThemeColor('text')
215
- });
216
-
217
- const sendButton = document.createElement('button');
218
- sendButton.className = 'send-button';
219
- sendButton.textContent = 'Send';
220
-
221
- Object.assign(sendButton.style, {
222
- background: this.getThemeColor('primary'),
223
- color: 'white',
224
- border: 'none',
225
- padding: '10px 20px',
226
- borderRadius: '4px',
227
- cursor: 'pointer'
228
- });
229
-
230
- container.appendChild(input);
231
- container.appendChild(sendButton);
232
-
233
- this.elements.input = input;
234
- this.elements.sendButton = sendButton;
235
- return container;
236
- }
237
-
238
- createAutonomousButton() {
239
- const button = createAutonomousButton({
240
- autonomousMode: this.state.autonomousMode,
241
- isPaused: this.state.isPaused,
242
- onClick: () => this.onAutonomousToggle()
243
- });
244
-
245
- Object.assign(button.style, {
246
- width: '100%',
247
- marginTop: '10px'
248
- });
249
-
250
- this.elements.autonomousButton = button;
251
- return button;
252
- }
253
-
254
- createControlButtons() {
255
- const container = createControlButtonContainer();
256
-
257
- const pauseButton = createPauseButton({
258
- isPaused: this.state.isPaused,
259
- onClick: () => this.onPauseToggle()
260
- });
261
-
262
- const stopButton = createStopButton({
263
- autonomousMode: this.state.autonomousMode,
264
- onClick: () => this.onStop()
265
- });
266
-
267
- const checkQuotaButton = createCheckQuotaButton({
268
- isCheckingQuota: this.state.isCheckingQuota,
269
- onClick: () => this.onCheckQuota()
270
- });
271
-
272
- container.appendChild(pauseButton);
273
- container.appendChild(stopButton);
274
- container.appendChild(checkQuotaButton);
275
-
276
- this.elements.pauseButton = pauseButton;
277
- this.elements.stopButton = stopButton;
278
- this.elements.checkQuotaButton = checkQuotaButton;
279
- this.elements.controlContainer = container;
280
-
281
- return container;
282
- }
283
-
284
- bindEvents() {
285
- // Input events
286
- if (this.elements.input) {
287
- this.elements.input.addEventListener('keypress', (e) => {
288
- if (e.key === 'Enter') {
289
- this.onSendMessage();
290
- }
291
- });
292
- }
293
-
294
- if (this.elements.sendButton) {
295
- this.elements.sendButton.addEventListener('click', () => {
296
- this.onSendMessage();
297
- });
298
- }
299
- }
300
-
301
- subscribeToState() {
302
- this.unsubscribe = stateManager.subscribe((newState, oldState) => {
303
- this.updateUI(newState, oldState);
304
- });
305
- }
306
-
307
- updateUI(newState, oldState) {
308
- this.state = newState;
309
-
310
- // Update status
311
- if (this.elements.status) {
312
- this.elements.status.textContent = newState.status;
313
- }
314
-
315
- // Update autonomous button
316
- if (this.elements.autonomousButton) {
317
- updateAutonomousButton(
318
- this.elements.autonomousButton,
319
- newState.autonomousMode,
320
- newState.isPaused
321
- );
322
- }
323
-
324
- // Update pause button
325
- if (this.elements.pauseButton) {
326
- updatePauseButton(this.elements.pauseButton, newState.isPaused);
327
- }
328
-
329
- // Update stop button
330
- if (this.elements.stopButton) {
331
- updateStopButton(this.elements.stopButton, newState.autonomousMode);
332
- }
333
-
334
- // Update check quota button
335
- if (this.elements.checkQuotaButton) {
336
- updateCheckQuotaButton(this.elements.checkQuotaButton, newState.isCheckingQuota);
337
- }
338
-
339
- // Update open IDE button
340
- if (this.elements.openIdeButton) {
341
- updateOpenIdeButton(this.elements.openIdeButton, newState.isOpeningIde);
342
- }
343
-
344
- // Update IDE buttons
345
- if (this.elements.ideButtons) {
346
- this.elements.ideButtons.forEach(button => {
347
- const ide = button.dataset.ide;
348
- button.classList.toggle('active', ide === newState.currentIde);
349
- button.style.backgroundColor = ide === newState.currentIde ?
350
- this.getThemeColor('primary') : this.getThemeColor('secondary');
351
- });
352
- }
353
-
354
- // Update messages
355
- if (this.elements.messages && newState.messages.length !== oldState.messages.length) {
356
- this.updateMessages(newState.messages);
357
- }
358
- }
359
-
360
- updateMessages(messages) {
361
- // Clear existing messages except welcome
362
- const welcome = this.elements.messages.querySelector('.message.assistant');
363
- this.elements.messages.innerHTML = '';
364
- if (welcome) {
365
- this.elements.messages.appendChild(welcome);
366
- }
367
-
368
- // Add new messages
369
- messages.forEach(message => {
370
- const messageEl = document.createElement('div');
371
- messageEl.className = `message ${message.role}`;
372
- messageEl.textContent = message.text;
373
-
374
- Object.assign(messageEl.style, {
375
- marginBottom: '15px',
376
- padding: '10px',
377
- borderRadius: '4px',
378
- backgroundColor: message.role === 'user' ?
379
- this.getThemeColor('primary') : this.getThemeColor('messageBackground'),
380
- color: message.role === 'user' ? 'white' : this.getThemeColor('text'),
381
- marginLeft: message.role === 'user' ? '20%' : '0',
382
- marginRight: message.role === 'user' ? '0' : '20%'
383
- });
384
-
385
- this.elements.messages.appendChild(messageEl);
386
- });
387
-
388
- // Scroll to bottom
389
- this.elements.messages.scrollTop = this.elements.messages.scrollHeight;
390
- }
391
-
392
- getThemeColor(type) {
393
- const themes = {
394
- default: {
395
- primary: '#68217a',
396
- secondary: '#007acc',
397
- background: '#ffffff',
398
- text: '#333333',
399
- border: '#cccccc',
400
- messageBackground: '#f8f9fa'
401
- },
402
- vscode: {
403
- primary: 'var(--vscode-textLink-foreground)',
404
- secondary: 'var(--vscode-button-secondaryBackground)',
405
- background: 'var(--vscode-editor-background)',
406
- text: 'var(--vscode-editor-foreground)',
407
- border: 'var(--vscode-input-border)',
408
- messageBackground: 'var(--vscode-input-background)'
409
- },
410
- electron: {
411
- primary: '#68217a',
412
- secondary: '#007acc',
413
- background: '#ffffff',
414
- text: '#333333',
415
- border: '#cccccc',
416
- messageBackground: '#f8f9fa'
417
- }
418
- };
419
-
420
- return themes[this.options.theme]?.[type] || themes.default[type];
421
- }
422
-
423
- // Event handlers
424
- onIdeSelect(ide) {
425
- stateManager.setState({ currentIde: ide });
426
- if (this.options.onIdeSelect) {
427
- this.options.onIdeSelect(ide);
428
- }
429
- }
430
-
431
- onOpenIde() {
432
- if (this.options.onOpenIde) {
433
- this.options.onOpenIde(this.state.currentIde);
434
- }
435
- }
436
-
437
- onSendMessage() {
438
- const text = this.elements.input?.value?.trim();
439
- if (!text) return;
440
-
441
- stateManager.addMessage('user', text);
442
- this.elements.input.value = '';
443
-
444
- if (this.options.onSendMessage) {
445
- this.options.onSendMessage(text, this.state.currentIde);
446
- }
447
- }
448
-
449
- onAutonomousToggle() {
450
- if (this.state.autonomousMode) {
451
- stateManager.stopAutonomousMode();
452
- } else {
453
- stateManager.startAutonomousMode(this.state.currentIde);
454
- }
455
-
456
- if (this.options.onAutonomousToggle) {
457
- this.options.onAutonomousToggle(this.state.autonomousMode);
458
- }
459
- }
460
-
461
- onPauseToggle() {
462
- stateManager.togglePause();
463
-
464
- if (this.options.onPauseToggle) {
465
- this.options.onPauseToggle(this.state.isPaused);
466
- }
467
- }
468
-
469
- onStop() {
470
- stateManager.stopAutonomousMode();
471
-
472
- if (this.options.onStop) {
473
- this.options.onStop();
474
- }
475
- }
476
-
477
- onCheckQuota() {
478
- stateManager.startQuotaCheck();
479
-
480
- if (this.options.onCheckQuota) {
481
- this.options.onCheckQuota(this.state.currentIde);
482
- }
483
- }
484
-
485
- // Public methods
486
- addMessage(role, text) {
487
- stateManager.addMessage(role, text);
488
- }
489
-
490
- updateStatus(status) {
491
- stateManager.updateStatus(status);
492
- }
493
-
494
- destroy() {
495
- if (this.unsubscribe) {
496
- this.unsubscribe();
497
- }
498
- }
499
- }
1
+ /**
2
+ * Shared Chat Interface Component
3
+ * Provides a consistent UI across electron app and VSCode extension
4
+ */
5
+
6
+ import { stateManager } from './StateManager.js';
7
+ import {
8
+ createAutonomousButton,
9
+ createPauseButton,
10
+ createStopButton,
11
+ createCheckQuotaButton,
12
+ createOpenIdeButton,
13
+ createIdeButton,
14
+ createControlButtonContainer,
15
+ updateAutonomousButton,
16
+ updatePauseButton,
17
+ updateStopButton,
18
+ updateCheckQuotaButton,
19
+ updateOpenIdeButton
20
+ } from './ButtonComponents.js';
21
+
22
+ export class ChatInterface {
23
+ constructor(container, options = {}) {
24
+ this.container = container;
25
+ this.options = {
26
+ showIdeSelector: true,
27
+ showChatMessages: true,
28
+ showInput: true,
29
+ showControls: true,
30
+ theme: 'default', // 'default', 'vscode', 'electron'
31
+ ...options
32
+ };
33
+
34
+ this.elements = {};
35
+ this.state = stateManager.getState();
36
+ this.unsubscribe = null;
37
+
38
+ this.init();
39
+ }
40
+
41
+ init() {
42
+ this.render();
43
+ this.bindEvents();
44
+ this.subscribeToState();
45
+ }
46
+
47
+ render() {
48
+ this.container.innerHTML = '';
49
+
50
+ // Create header
51
+ const header = this.createHeader();
52
+ this.container.appendChild(header);
53
+
54
+ // Create IDE selector
55
+ if (this.options.showIdeSelector) {
56
+ const ideSelector = this.createIdeSelector();
57
+ this.container.appendChild(ideSelector);
58
+ }
59
+
60
+ // Create status
61
+ const status = this.createStatus();
62
+ this.container.appendChild(status);
63
+
64
+ // Create chat messages
65
+ if (this.options.showChatMessages) {
66
+ const messages = this.createMessages();
67
+ this.container.appendChild(messages);
68
+ }
69
+
70
+ // Create input area
71
+ if (this.options.showInput) {
72
+ const input = this.createInput();
73
+ this.container.appendChild(input);
74
+ }
75
+
76
+ // Create autonomous button
77
+ const autonomousButton = this.createAutonomousButton();
78
+ this.container.appendChild(autonomousButton);
79
+
80
+ // Create control buttons
81
+ if (this.options.showControls) {
82
+ const controls = this.createControlButtons();
83
+ this.container.appendChild(controls);
84
+ }
85
+ }
86
+
87
+ createHeader() {
88
+ const header = document.createElement('div');
89
+ header.className = 'chat-header';
90
+ header.innerHTML = `
91
+ <h1>🤖 VibeCodingMachine Chat</h1>
92
+ `;
93
+
94
+ Object.assign(header.style, {
95
+ textAlign: 'center',
96
+ marginBottom: '20px'
97
+ });
98
+
99
+ const title = header.querySelector('h1');
100
+ Object.assign(title.style, {
101
+ color: this.getThemeColor('primary'),
102
+ margin: '0',
103
+ fontSize: '24px',
104
+ fontWeight: '600'
105
+ });
106
+
107
+ this.elements.header = header;
108
+ return header;
109
+ }
110
+
111
+ createIdeSelector() {
112
+ const container = document.createElement('div');
113
+ container.className = 'ide-selector';
114
+
115
+ Object.assign(container.style, {
116
+ marginBottom: '20px',
117
+ textAlign: 'center'
118
+ });
119
+
120
+ const ides = ['vscode', 'cursor', 'windsurf'];
121
+
122
+ ides.forEach(ide => {
123
+ const button = createIdeButton({
124
+ ide,
125
+ isActive: this.state.currentIde === ide,
126
+ onClick: () => this.onIdeSelect(ide)
127
+ });
128
+ container.appendChild(button);
129
+ });
130
+
131
+ const openIdeButton = createOpenIdeButton({
132
+ isOpeningIde: this.state.isOpeningIde,
133
+ onClick: () => this.onOpenIde()
134
+ });
135
+ container.appendChild(openIdeButton);
136
+
137
+ this.elements.ideSelector = container;
138
+ this.elements.ideButtons = container.querySelectorAll('.ide-button');
139
+ this.elements.openIdeButton = openIdeButton;
140
+ return container;
141
+ }
142
+
143
+ createStatus() {
144
+ const status = document.createElement('div');
145
+ status.className = 'status';
146
+ status.textContent = this.state.status;
147
+
148
+ Object.assign(status.style, {
149
+ textAlign: 'center',
150
+ margin: '10px 0',
151
+ fontStyle: 'italic',
152
+ color: this.getThemeColor('secondary')
153
+ });
154
+
155
+ this.elements.status = status;
156
+ return status;
157
+ }
158
+
159
+ createMessages() {
160
+ const container = document.createElement('div');
161
+ container.className = 'chat-messages';
162
+
163
+ Object.assign(container.style, {
164
+ height: '400px',
165
+ overflowY: 'auto',
166
+ border: `1px solid ${this.getThemeColor('border')}`,
167
+ borderRadius: '4px',
168
+ padding: '10px',
169
+ marginBottom: '20px',
170
+ backgroundColor: this.getThemeColor('background')
171
+ });
172
+
173
+ // Add welcome message
174
+ const welcome = document.createElement('div');
175
+ welcome.className = 'message assistant';
176
+ welcome.textContent = 'Welcome to VibeCodingMachine! Select an IDE and start chatting.';
177
+
178
+ Object.assign(welcome.style, {
179
+ marginBottom: '15px',
180
+ padding: '10px',
181
+ borderRadius: '4px',
182
+ backgroundColor: this.getThemeColor('messageBackground'),
183
+ border: `1px solid ${this.getThemeColor('border')}`,
184
+ marginRight: '20%'
185
+ });
186
+
187
+ container.appendChild(welcome);
188
+
189
+ this.elements.messages = container;
190
+ return container;
191
+ }
192
+
193
+ createInput() {
194
+ const container = document.createElement('div');
195
+ container.className = 'input-container';
196
+
197
+ Object.assign(container.style, {
198
+ display: 'flex',
199
+ gap: '10px',
200
+ marginBottom: '10px'
201
+ });
202
+
203
+ const input = document.createElement('input');
204
+ input.type = 'text';
205
+ input.className = 'message-input';
206
+ input.placeholder = 'Type your message...';
207
+
208
+ Object.assign(input.style, {
209
+ flex: '1',
210
+ padding: '10px',
211
+ border: `1px solid ${this.getThemeColor('border')}`,
212
+ borderRadius: '4px',
213
+ backgroundColor: this.getThemeColor('background'),
214
+ color: this.getThemeColor('text')
215
+ });
216
+
217
+ const sendButton = document.createElement('button');
218
+ sendButton.className = 'send-button';
219
+ sendButton.textContent = 'Send';
220
+
221
+ Object.assign(sendButton.style, {
222
+ background: this.getThemeColor('primary'),
223
+ color: 'white',
224
+ border: 'none',
225
+ padding: '10px 20px',
226
+ borderRadius: '4px',
227
+ cursor: 'pointer'
228
+ });
229
+
230
+ container.appendChild(input);
231
+ container.appendChild(sendButton);
232
+
233
+ this.elements.input = input;
234
+ this.elements.sendButton = sendButton;
235
+ return container;
236
+ }
237
+
238
+ createAutonomousButton() {
239
+ const button = createAutonomousButton({
240
+ autonomousMode: this.state.autonomousMode,
241
+ isPaused: this.state.isPaused,
242
+ onClick: () => this.onAutonomousToggle()
243
+ });
244
+
245
+ Object.assign(button.style, {
246
+ width: '100%',
247
+ marginTop: '10px'
248
+ });
249
+
250
+ this.elements.autonomousButton = button;
251
+ return button;
252
+ }
253
+
254
+ createControlButtons() {
255
+ const container = createControlButtonContainer();
256
+
257
+ const pauseButton = createPauseButton({
258
+ isPaused: this.state.isPaused,
259
+ onClick: () => this.onPauseToggle()
260
+ });
261
+
262
+ const stopButton = createStopButton({
263
+ autonomousMode: this.state.autonomousMode,
264
+ onClick: () => this.onStop()
265
+ });
266
+
267
+ const checkQuotaButton = createCheckQuotaButton({
268
+ isCheckingQuota: this.state.isCheckingQuota,
269
+ onClick: () => this.onCheckQuota()
270
+ });
271
+
272
+ container.appendChild(pauseButton);
273
+ container.appendChild(stopButton);
274
+ container.appendChild(checkQuotaButton);
275
+
276
+ this.elements.pauseButton = pauseButton;
277
+ this.elements.stopButton = stopButton;
278
+ this.elements.checkQuotaButton = checkQuotaButton;
279
+ this.elements.controlContainer = container;
280
+
281
+ return container;
282
+ }
283
+
284
+ bindEvents() {
285
+ // Input events
286
+ if (this.elements.input) {
287
+ this.elements.input.addEventListener('keypress', (e) => {
288
+ if (e.key === 'Enter') {
289
+ this.onSendMessage();
290
+ }
291
+ });
292
+ }
293
+
294
+ if (this.elements.sendButton) {
295
+ this.elements.sendButton.addEventListener('click', () => {
296
+ this.onSendMessage();
297
+ });
298
+ }
299
+ }
300
+
301
+ subscribeToState() {
302
+ this.unsubscribe = stateManager.subscribe((newState, oldState) => {
303
+ this.updateUI(newState, oldState);
304
+ });
305
+ }
306
+
307
+ updateUI(newState, oldState) {
308
+ this.state = newState;
309
+
310
+ // Update status
311
+ if (this.elements.status) {
312
+ this.elements.status.textContent = newState.status;
313
+ }
314
+
315
+ // Update autonomous button
316
+ if (this.elements.autonomousButton) {
317
+ updateAutonomousButton(
318
+ this.elements.autonomousButton,
319
+ newState.autonomousMode,
320
+ newState.isPaused
321
+ );
322
+ }
323
+
324
+ // Update pause button
325
+ if (this.elements.pauseButton) {
326
+ updatePauseButton(this.elements.pauseButton, newState.isPaused);
327
+ }
328
+
329
+ // Update stop button
330
+ if (this.elements.stopButton) {
331
+ updateStopButton(this.elements.stopButton, newState.autonomousMode);
332
+ }
333
+
334
+ // Update check quota button
335
+ if (this.elements.checkQuotaButton) {
336
+ updateCheckQuotaButton(this.elements.checkQuotaButton, newState.isCheckingQuota);
337
+ }
338
+
339
+ // Update open IDE button
340
+ if (this.elements.openIdeButton) {
341
+ updateOpenIdeButton(this.elements.openIdeButton, newState.isOpeningIde);
342
+ }
343
+
344
+ // Update IDE buttons
345
+ if (this.elements.ideButtons) {
346
+ this.elements.ideButtons.forEach(button => {
347
+ const ide = button.dataset.ide;
348
+ button.classList.toggle('active', ide === newState.currentIde);
349
+ button.style.backgroundColor = ide === newState.currentIde ?
350
+ this.getThemeColor('primary') : this.getThemeColor('secondary');
351
+ });
352
+ }
353
+
354
+ // Update messages
355
+ if (this.elements.messages && newState.messages.length !== oldState.messages.length) {
356
+ this.updateMessages(newState.messages);
357
+ }
358
+ }
359
+
360
+ updateMessages(messages) {
361
+ // Clear existing messages except welcome
362
+ const welcome = this.elements.messages.querySelector('.message.assistant');
363
+ this.elements.messages.innerHTML = '';
364
+ if (welcome) {
365
+ this.elements.messages.appendChild(welcome);
366
+ }
367
+
368
+ // Add new messages
369
+ messages.forEach(message => {
370
+ const messageEl = document.createElement('div');
371
+ messageEl.className = `message ${message.role}`;
372
+ messageEl.textContent = message.text;
373
+
374
+ Object.assign(messageEl.style, {
375
+ marginBottom: '15px',
376
+ padding: '10px',
377
+ borderRadius: '4px',
378
+ backgroundColor: message.role === 'user' ?
379
+ this.getThemeColor('primary') : this.getThemeColor('messageBackground'),
380
+ color: message.role === 'user' ? 'white' : this.getThemeColor('text'),
381
+ marginLeft: message.role === 'user' ? '20%' : '0',
382
+ marginRight: message.role === 'user' ? '0' : '20%'
383
+ });
384
+
385
+ this.elements.messages.appendChild(messageEl);
386
+ });
387
+
388
+ // Scroll to bottom
389
+ this.elements.messages.scrollTop = this.elements.messages.scrollHeight;
390
+ }
391
+
392
+ getThemeColor(type) {
393
+ const themes = {
394
+ default: {
395
+ primary: '#68217a',
396
+ secondary: '#007acc',
397
+ background: '#ffffff',
398
+ text: '#333333',
399
+ border: '#cccccc',
400
+ messageBackground: '#f8f9fa'
401
+ },
402
+ vscode: {
403
+ primary: 'var(--vscode-textLink-foreground)',
404
+ secondary: 'var(--vscode-button-secondaryBackground)',
405
+ background: 'var(--vscode-editor-background)',
406
+ text: 'var(--vscode-editor-foreground)',
407
+ border: 'var(--vscode-input-border)',
408
+ messageBackground: 'var(--vscode-input-background)'
409
+ },
410
+ electron: {
411
+ primary: '#68217a',
412
+ secondary: '#007acc',
413
+ background: '#ffffff',
414
+ text: '#333333',
415
+ border: '#cccccc',
416
+ messageBackground: '#f8f9fa'
417
+ }
418
+ };
419
+
420
+ return themes[this.options.theme]?.[type] || themes.default[type];
421
+ }
422
+
423
+ // Event handlers
424
+ onIdeSelect(ide) {
425
+ stateManager.setState({ currentIde: ide });
426
+ if (this.options.onIdeSelect) {
427
+ this.options.onIdeSelect(ide);
428
+ }
429
+ }
430
+
431
+ onOpenIde() {
432
+ if (this.options.onOpenIde) {
433
+ this.options.onOpenIde(this.state.currentIde);
434
+ }
435
+ }
436
+
437
+ onSendMessage() {
438
+ const text = this.elements.input?.value?.trim();
439
+ if (!text) return;
440
+
441
+ stateManager.addMessage('user', text);
442
+ this.elements.input.value = '';
443
+
444
+ if (this.options.onSendMessage) {
445
+ this.options.onSendMessage(text, this.state.currentIde);
446
+ }
447
+ }
448
+
449
+ onAutonomousToggle() {
450
+ if (this.state.autonomousMode) {
451
+ stateManager.stopAutonomousMode();
452
+ } else {
453
+ stateManager.startAutonomousMode(this.state.currentIde);
454
+ }
455
+
456
+ if (this.options.onAutonomousToggle) {
457
+ this.options.onAutonomousToggle(this.state.autonomousMode);
458
+ }
459
+ }
460
+
461
+ onPauseToggle() {
462
+ stateManager.togglePause();
463
+
464
+ if (this.options.onPauseToggle) {
465
+ this.options.onPauseToggle(this.state.isPaused);
466
+ }
467
+ }
468
+
469
+ onStop() {
470
+ stateManager.stopAutonomousMode();
471
+
472
+ if (this.options.onStop) {
473
+ this.options.onStop();
474
+ }
475
+ }
476
+
477
+ onCheckQuota() {
478
+ stateManager.startQuotaCheck();
479
+
480
+ if (this.options.onCheckQuota) {
481
+ this.options.onCheckQuota(this.state.currentIde);
482
+ }
483
+ }
484
+
485
+ // Public methods
486
+ addMessage(role, text) {
487
+ stateManager.addMessage(role, text);
488
+ }
489
+
490
+ updateStatus(status) {
491
+ stateManager.updateStatus(status);
492
+ }
493
+
494
+ destroy() {
495
+ if (this.unsubscribe) {
496
+ this.unsubscribe();
497
+ }
498
+ }
499
+ }