namish10 commited on
Commit
d8413b7
·
verified ·
1 Parent(s): eeae8cd

Upload frontend/src/BrowserLLMLauncher.js with huggingface_hub

Browse files
Files changed (1) hide show
  1. frontend/src/BrowserLLMLauncher.js +343 -0
frontend/src/BrowserLLMLauncher.js ADDED
@@ -0,0 +1,343 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BrowserLLMLauncher.js
3
+ *
4
+ * Opens AI chat interfaces directly in the browser.
5
+ * No API keys required - uses user's existing sessions.
6
+ *
7
+ * Supported:
8
+ * - ChatGPT (chat.openai.com)
9
+ * - Gemini (gemini.google.com)
10
+ * - Claude (claude.ai)
11
+ * - Perplexity (perplexity.ai)
12
+ * - Poe (poe.com)
13
+ * - Grok (x.ai/grok)
14
+ * - DeepSeek (chat.deepseek.com)
15
+ * - Local Ollama (localhost:11434)
16
+ */
17
+
18
+ class BrowserLLMLauncher {
19
+ constructor() {
20
+ this.providers = {
21
+ chatgpt: {
22
+ name: 'ChatGPT',
23
+ url: 'https://chat.openai.com',
24
+ searchUrl: 'https://chat.openai.com/?q=',
25
+ icon: '🤖',
26
+ color: '#10a37f',
27
+ promptMethod: 'clipboard'
28
+ },
29
+ gemini: {
30
+ name: 'Gemini',
31
+ url: 'https://gemini.google.com',
32
+ searchUrl: 'https://gemini.google.com/?query=',
33
+ icon: '✨',
34
+ color: '#4285f4',
35
+ promptMethod: 'clipboard'
36
+ },
37
+ claude: {
38
+ name: 'Claude',
39
+ url: 'https://claude.ai',
40
+ searchUrl: 'https://claude.ai/?q=',
41
+ icon: '🧠',
42
+ color: '#d4a574',
43
+ promptMethod: 'clipboard'
44
+ },
45
+ perplexity: {
46
+ name: 'Perplexity',
47
+ url: 'https://perplexity.ai',
48
+ searchUrl: 'https://perplexity.ai/?q=',
49
+ icon: '🔍',
50
+ color: '#20b2aa',
51
+ promptMethod: 'clipboard'
52
+ },
53
+ poe: {
54
+ name: 'Poe',
55
+ url: 'https://poe.com',
56
+ searchUrl: 'https://poe.com/?q=',
57
+ icon: '🦊',
58
+ color: '#6b5ce7',
59
+ promptMethod: 'clipboard'
60
+ },
61
+ grok: {
62
+ name: 'Grok',
63
+ url: 'https://x.ai/grok',
64
+ searchUrl: 'https://x.ai/grok?q=',
65
+ icon: '🚀',
66
+ color: '#000000',
67
+ promptMethod: 'clipboard'
68
+ },
69
+ deepseek: {
70
+ name: 'DeepSeek',
71
+ url: 'https://chat.deepseek.com',
72
+ searchUrl: 'https://chat.deepseek.com/?q=',
73
+ icon: '🔭',
74
+ color: '#0066cc',
75
+ promptMethod: 'clipboard'
76
+ },
77
+ ollama: {
78
+ name: 'Ollama (Local)',
79
+ url: 'http://localhost:11434',
80
+ searchUrl: 'http://localhost:11434/?q=',
81
+ icon: '💻',
82
+ color: '#9333ea',
83
+ promptMethod: 'web'
84
+ }
85
+ };
86
+
87
+ this.activeProviders = ['chatgpt', 'gemini'];
88
+ this.clipboardFallback = true;
89
+ this.showNotification = true;
90
+ }
91
+
92
+ setActiveProviders(providers) {
93
+ this.activeProviders = providers.filter(p => this.providers[p]);
94
+ }
95
+
96
+ getActiveProviders() {
97
+ return this.activeProviders.map(key => ({
98
+ key,
99
+ ...this.providers[key]
100
+ }));
101
+ }
102
+
103
+ async copyToClipboard(text) {
104
+ try {
105
+ await navigator.clipboard.writeText(text);
106
+ return true;
107
+ } catch (err) {
108
+ console.error('Failed to copy to clipboard:', err);
109
+ return false;
110
+ }
111
+ }
112
+
113
+ async launchProvider(providerKey, prompt, options = {}) {
114
+ const provider = this.providers[providerKey];
115
+ if (!provider) {
116
+ console.error(`Unknown provider: ${providerKey}`);
117
+ return { success: false, error: 'Unknown provider' };
118
+ }
119
+
120
+ const finalPrompt = options.customPrompt || prompt;
121
+ let url = provider.url;
122
+
123
+ if (finalPrompt && provider.promptMethod === 'clipboard') {
124
+ await this.copyToClipboard(finalPrompt);
125
+
126
+ if (this.showNotification) {
127
+ this.showBrowserNotification(
128
+ provider.name,
129
+ `Copied prompt! Paste in ${provider.name}`,
130
+ provider.icon
131
+ );
132
+ }
133
+ }
134
+
135
+ if (finalPrompt && provider.searchUrl) {
136
+ const encodedPrompt = encodeURIComponent(finalPrompt.substring(0, 500));
137
+ url = provider.searchUrl + encodedPrompt;
138
+ }
139
+
140
+ const features = 'width=1200,height=800,scrollbars=yes,resizable=yes';
141
+ const newWindow = window.open(url, `_blank`, features);
142
+
143
+ if (!newWindow) {
144
+ return {
145
+ success: false,
146
+ error: 'Popup blocked. Please allow popups for this site.'
147
+ };
148
+ }
149
+
150
+ return {
151
+ success: true,
152
+ provider: providerKey,
153
+ providerName: provider.name,
154
+ url: url,
155
+ prompt: finalPrompt,
156
+ method: provider.promptMethod
157
+ };
158
+ }
159
+
160
+ async launchAll(prompt, options = {}) {
161
+ const results = [];
162
+
163
+ for (const providerKey of this.activeProviders) {
164
+ const result = await this.launchProvider(providerKey, prompt, options);
165
+ results.push(result);
166
+
167
+ await new Promise(resolve => setTimeout(resolve, 500));
168
+ }
169
+
170
+ return results;
171
+ }
172
+
173
+ async launchWithGesture(gestureType, prompt, context = {}) {
174
+ const results = [];
175
+
176
+ switch (gestureType) {
177
+ case '2_finger_swipe_right':
178
+ results.push(...await this.launchAll(prompt, { gesture: gestureType }));
179
+ break;
180
+
181
+ case '2_finger_swipe_left':
182
+ const defaultProvider = this.activeProviders[0];
183
+ results.push(await this.launchProvider(defaultProvider, prompt, { gesture: gestureType }));
184
+ break;
185
+
186
+ case '1_finger_tap':
187
+ results.push(...await this.launchAll(prompt, { gesture: gestureType, rlMode: true }));
188
+ break;
189
+
190
+ case 'pinch':
191
+ const lastProvider = this.activeProviders[this.activeProviders.length - 1];
192
+ results.push(await this.launchProvider(lastProvider, prompt, { gesture: gestureType }));
193
+ break;
194
+
195
+ case 'swipe_up':
196
+ results.push(...await this.launchAll(prompt, { gesture: gestureType }));
197
+ break;
198
+
199
+ case 'swipe_down':
200
+ const focusedPrompt = `Focus on: ${context.topic || prompt}`;
201
+ results.push(...await this.launchAll(focusedPrompt, { gesture: gestureType }));
202
+ break;
203
+
204
+ default:
205
+ results.push(...await this.launchAll(prompt, { gesture: gestureType }));
206
+ }
207
+
208
+ return results;
209
+ }
210
+
211
+ showBrowserNotification(title, message, icon = '🤖') {
212
+ if ('Notification' in window) {
213
+ if (Notification.permission === 'granted') {
214
+ new Notification(`${icon} ${title}`, {
215
+ body: message,
216
+ icon: '/favicon.ico'
217
+ });
218
+ } else if (Notification.permission !== 'denied') {
219
+ Notification.requestPermission().then(permission => {
220
+ if (permission === 'granted') {
221
+ new Notification(`${icon} ${title}`, {
222
+ body: message,
223
+ icon: '/favicon.ico'
224
+ });
225
+ }
226
+ });
227
+ }
228
+ }
229
+
230
+ this.showToast(message, 'info');
231
+ }
232
+
233
+ showToast(message, type = 'info') {
234
+ const toast = document.createElement('div');
235
+ toast.className = `llm-toast llm-toast-${type}`;
236
+ toast.innerHTML = `
237
+ <span>${message}</span>
238
+ <button onclick="this.parentElement.remove()">×</button>
239
+ `;
240
+ document.body.appendChild(toast);
241
+
242
+ setTimeout(() => {
243
+ toast.classList.add('show');
244
+ }, 10);
245
+
246
+ setTimeout(() => {
247
+ toast.classList.remove('show');
248
+ setTimeout(() => toast.remove(), 300);
249
+ }, 3000);
250
+ }
251
+
252
+ checkProviderStatus() {
253
+ const status = {};
254
+
255
+ for (const [key, provider] of Object.entries(this.providers)) {
256
+ status[key] = {
257
+ available: true,
258
+ url: provider.url,
259
+ name: provider.name
260
+ };
261
+ }
262
+
263
+ return status;
264
+ }
265
+
266
+ getInstructions() {
267
+ return `
268
+ To use browser-based LLM launching:
269
+
270
+ 1. Make sure you're logged into the AI services you want to use
271
+ 2. Allow popups for this site
272
+ 3. Use hand gestures to trigger queries
273
+
274
+ When a gesture is detected:
275
+ - The prompt will be copied to your clipboard
276
+ - The AI chat interface will open in a new tab
277
+ - Just paste (Ctrl+V) to send the prompt!
278
+
279
+ Supported services:
280
+ ${Object.values(this.providers).map(p => `- ${p.name}: ${p.url}`).join('\n')}
281
+ `;
282
+ }
283
+ }
284
+
285
+ class GesturePromptBuilder {
286
+ constructor() {
287
+ this.templates = {
288
+ learning_explain: (context) => `Explain ${context.topic || 'this concept'} in simple terms.`,
289
+
290
+ doubt_resolution: (context) => {
291
+ let prompt = `I need help understanding: ${context.doubt || context.topic || 'this concept'}\n\n`;
292
+ prompt += `I've tried: ${context.attempted || 'reading the material'}\n`;
293
+ prompt += `What specifically confuses me is: ${context.confusion || 'the underlying concept'}`;
294
+ return prompt;
295
+ },
296
+
297
+ summarize: (context) => `Summarize the key points about ${context.topic || 'this topic'} in a way that's easy to understand.`,
298
+
299
+ practice: (context) => `Generate 5 practice questions about ${context.topic || 'this topic'} to test my understanding.`,
300
+
301
+ compare: (context) => `Compare and contrast ${context.concept_a || 'concept A'} and ${context.concept_b || 'concept B'}.`,
302
+
303
+ real_world: (context) => `Explain ${context.topic || 'this concept'} using real-world examples that a beginner would understand.`,
304
+
305
+ step_by_step: (context) => `Break down ${context.topic || 'this concept'} into simple, easy-to-follow steps.`,
306
+
307
+ common_mistakes: (context) => `What are the most common mistakes people make when learning about ${context.topic || 'this'}? How can I avoid them?`
308
+ };
309
+ }
310
+
311
+ build(gesture, context) {
312
+ const template = this.templates[context.template] || this.templates.learning_explain;
313
+ return template(context);
314
+ }
315
+
316
+ buildFromGestures(gestures, context) {
317
+ const prompts = [];
318
+
319
+ for (const gesture of gestures) {
320
+ const type = this.getGesturePromptType(gesture);
321
+ const prompt = this.build(type, context);
322
+ prompts.push({ gesture, prompt });
323
+ }
324
+
325
+ return prompts;
326
+ }
327
+
328
+ getGesturePromptType(gesture) {
329
+ if (gesture.includes('swipe_right')) return 'learning_explain';
330
+ if (gesture.includes('swipe_left')) return 'doubt_resolution';
331
+ if (gesture.includes('swipe_up')) return 'summarize';
332
+ if (gesture.includes('swipe_down')) return 'step_by_step';
333
+ if (gesture.includes('pinch')) return 'real_world';
334
+ if (gesture.includes('tap') || gesture.includes('1_finger')) return 'practice';
335
+ return 'learning_explain';
336
+ }
337
+ }
338
+
339
+ window.BrowserLLMLauncher = BrowserLLMLauncher;
340
+ window.GesturePromptBuilder = GesturePromptBuilder;
341
+
342
+ export default BrowserLLMLauncher;
343
+ export { BrowserLLMLauncher, GesturePromptBuilder };