Skip to content

Commit 332d3a1

Browse files
committed
Send prompt data with http endpoint instead of through the electron shell
1 parent de7a5e5 commit 332d3a1

1 file changed

Lines changed: 64 additions & 44 deletions

File tree

front_end/panels/livemate/LivematePanel.ts

Lines changed: 64 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
import * as Host from '../../core/host/host.js';
65
import * as i18n from '../../core/i18n/i18n.js';
76
import { ReactDevToolsViewBase } from '../react_devtools/ReactDevToolsViewBase.js';
87

@@ -129,62 +128,25 @@ export class LivematePanel extends ReactDevToolsViewBase {
129128
bottomRow.setAttribute('style', 'display: flex; align-items: center; gap: 8px;');
130129

131130
// AI query text box
132-
const queryInput = document.createElement('textarea');
131+
const queryInput: HTMLTextAreaElement = document.createElement('textarea');
133132
queryInput.setAttribute('placeholder', 'Query to modify component...');
134133
queryInput.setAttribute('style', 'flex: 1; padding: 12px 16px; border: 1px solid var(--sys-color-divider); border-radius: 4px; background: var(--sys-color-cdt-base-container); color: var(--sys-color-on-surface); font-size: 14px; min-height: 100px; resize: vertical; font-family: inherit;');
135134

136-
// Function to send query to Devmate
137-
const sendQueryToDevmate = (): void => {
138-
const query = queryInput.value;
139-
if (query.trim()) {
140-
// Build the prompt with focused component and hierarchy information
141-
let prompt = query;
142-
if (currentHierarchy.length > 0) {
143-
// The focused component is the last item in the hierarchy (leaf node)
144-
const focusedComponent = currentHierarchy[currentHierarchy.length - 1].name;
145-
const hierarchyStr = currentHierarchy.map(c => c.name).join(' > ');
146-
prompt = `Focused component: ${focusedComponent}\nComponent hierarchy: ${hierarchyStr}\n\nQuery: ${query}`;
147-
}
148-
(
149-
Host.InspectorFrontendHost.InspectorFrontendHostInstance as unknown as {
150-
sendToDevmate: (prompt: string) => void,
151-
}
152-
).sendToDevmate(prompt);
153-
154-
// Disable input and button with grayed out appearance
155-
queryInput.disabled = true;
156-
queryInput.style.opacity = '0.5';
157-
queryInput.style.cursor = 'not-allowed';
158-
sendButton.disabled = true;
159-
sendButton.style.opacity = '0.5';
160-
sendButton.style.cursor = 'not-allowed';
161-
162-
// Re-enable and clear after 3 seconds
163-
setTimeout(() => {
164-
queryInput.value = '';
165-
queryInput.disabled = false;
166-
queryInput.style.opacity = '1';
167-
queryInput.style.cursor = 'text';
168-
sendButton.disabled = false;
169-
sendButton.style.opacity = '1';
170-
sendButton.style.cursor = 'pointer';
171-
}, 3000);
172-
}
173-
};
174-
175135
// Handle Enter key to send prompt (Shift+Enter for newline)
176-
queryInput.addEventListener('keydown', (event: KeyboardEvent) => {
136+
queryInput.addEventListener('keydown', async (event: KeyboardEvent) => {
177137
if (event.key === 'Enter' && !event.shiftKey) {
178138
event.preventDefault();
179-
sendQueryToDevmate();
139+
await sendCommandToMetro(queryInput, currentHierarchy);
180140
}
181141
});
182142

183143
// Send to devmate button
184144
const sendButton = document.createElement('button');
185145
sendButton.textContent = 'Send to Devmate';
186146
sendButton.setAttribute('style', 'padding: 4px 12px; cursor: pointer; align-self: flex-end;');
187-
sendButton.addEventListener('click', sendQueryToDevmate);
147+
sendButton.addEventListener('click', async () => {
148+
await sendCommandToMetro(queryInput, currentHierarchy);
149+
});
188150

189151
bottomRow.appendChild(queryInput);
190152
bottomRow.appendChild(sendButton);
@@ -194,8 +156,66 @@ export class LivematePanel extends ReactDevToolsViewBase {
194156

195157
outerWrapper.appendChild(toolbarContainer);
196158
this.contentElement.appendChild(outerWrapper);
159+
}
160+
}
161+
162+
async function sendCommandToMetro(
163+
input: HTMLTextAreaElement,
164+
currentHierarchy: Array<{name: string}> = [],
165+
timeoutMs = 5000
166+
): Promise<{success: boolean; output?: string; error?: string}> {
167+
const query = input.value;
168+
let prompt;
169+
if (query.trim()) {
170+
prompt = query;
171+
if (currentHierarchy.length > 0) {
172+
const focusedComponent = currentHierarchy[currentHierarchy.length - 1].name;
173+
const hierarchyStr = currentHierarchy.map(c => c.name).join(' > ');
174+
prompt = `Focused component: ${focusedComponent}\nComponent hierarchy: ${hierarchyStr}\n\nQuery: ${query}`;
175+
}
176+
}
177+
178+
const controller = new AbortController();
179+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
180+
181+
try {
182+
const response = await fetch('http://localhost:8081/livemate', {
183+
method: 'POST',
184+
headers: {
185+
'Content-Type': 'application/json',
186+
},
187+
body: JSON.stringify({prompt}),
188+
signal: controller.signal,
189+
});
197190

191+
clearTimeout(timeoutId);
198192

193+
if (!response.ok) {
194+
const errorText = await response.text();
195+
input.value = '';
196+
return {
197+
success: false,
198+
error: `HTTP ${response.status}: ${errorText}`,
199+
};
200+
}
201+
202+
const result = await response.json();
203+
return result;
204+
} catch (e) {
205+
clearTimeout(timeoutId);
206+
207+
if (e instanceof Error && e.name === 'AbortError') {
208+
input.value = '';
209+
return {
210+
success: false,
211+
error: 'Request timeout',
212+
};
213+
}
199214

215+
input.value = '';
216+
return {
217+
success: false,
218+
error: e instanceof Error ? e.message : 'Unknown error',
219+
};
200220
}
201221
}

0 commit comments

Comments
 (0)