contextflow-rl / frontend /src /BrowserLLMLauncher.js
namish10's picture
Upload frontend/src/BrowserLLMLauncher.js with huggingface_hub
d8413b7 verified
/**
* BrowserLLMLauncher.js
*
* Opens AI chat interfaces directly in the browser.
* No API keys required - uses user's existing sessions.
*
* Supported:
* - ChatGPT (chat.openai.com)
* - Gemini (gemini.google.com)
* - Claude (claude.ai)
* - Perplexity (perplexity.ai)
* - Poe (poe.com)
* - Grok (x.ai/grok)
* - DeepSeek (chat.deepseek.com)
* - Local Ollama (localhost:11434)
*/
class BrowserLLMLauncher {
constructor() {
this.providers = {
chatgpt: {
name: 'ChatGPT',
url: 'https://chat.openai.com',
searchUrl: 'https://chat.openai.com/?q=',
icon: '🤖',
color: '#10a37f',
promptMethod: 'clipboard'
},
gemini: {
name: 'Gemini',
url: 'https://gemini.google.com',
searchUrl: 'https://gemini.google.com/?query=',
icon: '✨',
color: '#4285f4',
promptMethod: 'clipboard'
},
claude: {
name: 'Claude',
url: 'https://claude.ai',
searchUrl: 'https://claude.ai/?q=',
icon: '🧠',
color: '#d4a574',
promptMethod: 'clipboard'
},
perplexity: {
name: 'Perplexity',
url: 'https://perplexity.ai',
searchUrl: 'https://perplexity.ai/?q=',
icon: '🔍',
color: '#20b2aa',
promptMethod: 'clipboard'
},
poe: {
name: 'Poe',
url: 'https://poe.com',
searchUrl: 'https://poe.com/?q=',
icon: '🦊',
color: '#6b5ce7',
promptMethod: 'clipboard'
},
grok: {
name: 'Grok',
url: 'https://x.ai/grok',
searchUrl: 'https://x.ai/grok?q=',
icon: '🚀',
color: '#000000',
promptMethod: 'clipboard'
},
deepseek: {
name: 'DeepSeek',
url: 'https://chat.deepseek.com',
searchUrl: 'https://chat.deepseek.com/?q=',
icon: '🔭',
color: '#0066cc',
promptMethod: 'clipboard'
},
ollama: {
name: 'Ollama (Local)',
url: 'http://localhost:11434',
searchUrl: 'http://localhost:11434/?q=',
icon: '💻',
color: '#9333ea',
promptMethod: 'web'
}
};
this.activeProviders = ['chatgpt', 'gemini'];
this.clipboardFallback = true;
this.showNotification = true;
}
setActiveProviders(providers) {
this.activeProviders = providers.filter(p => this.providers[p]);
}
getActiveProviders() {
return this.activeProviders.map(key => ({
key,
...this.providers[key]
}));
}
async copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
return true;
} catch (err) {
console.error('Failed to copy to clipboard:', err);
return false;
}
}
async launchProvider(providerKey, prompt, options = {}) {
const provider = this.providers[providerKey];
if (!provider) {
console.error(`Unknown provider: ${providerKey}`);
return { success: false, error: 'Unknown provider' };
}
const finalPrompt = options.customPrompt || prompt;
let url = provider.url;
if (finalPrompt && provider.promptMethod === 'clipboard') {
await this.copyToClipboard(finalPrompt);
if (this.showNotification) {
this.showBrowserNotification(
provider.name,
`Copied prompt! Paste in ${provider.name}`,
provider.icon
);
}
}
if (finalPrompt && provider.searchUrl) {
const encodedPrompt = encodeURIComponent(finalPrompt.substring(0, 500));
url = provider.searchUrl + encodedPrompt;
}
const features = 'width=1200,height=800,scrollbars=yes,resizable=yes';
const newWindow = window.open(url, `_blank`, features);
if (!newWindow) {
return {
success: false,
error: 'Popup blocked. Please allow popups for this site.'
};
}
return {
success: true,
provider: providerKey,
providerName: provider.name,
url: url,
prompt: finalPrompt,
method: provider.promptMethod
};
}
async launchAll(prompt, options = {}) {
const results = [];
for (const providerKey of this.activeProviders) {
const result = await this.launchProvider(providerKey, prompt, options);
results.push(result);
await new Promise(resolve => setTimeout(resolve, 500));
}
return results;
}
async launchWithGesture(gestureType, prompt, context = {}) {
const results = [];
switch (gestureType) {
case '2_finger_swipe_right':
results.push(...await this.launchAll(prompt, { gesture: gestureType }));
break;
case '2_finger_swipe_left':
const defaultProvider = this.activeProviders[0];
results.push(await this.launchProvider(defaultProvider, prompt, { gesture: gestureType }));
break;
case '1_finger_tap':
results.push(...await this.launchAll(prompt, { gesture: gestureType, rlMode: true }));
break;
case 'pinch':
const lastProvider = this.activeProviders[this.activeProviders.length - 1];
results.push(await this.launchProvider(lastProvider, prompt, { gesture: gestureType }));
break;
case 'swipe_up':
results.push(...await this.launchAll(prompt, { gesture: gestureType }));
break;
case 'swipe_down':
const focusedPrompt = `Focus on: ${context.topic || prompt}`;
results.push(...await this.launchAll(focusedPrompt, { gesture: gestureType }));
break;
default:
results.push(...await this.launchAll(prompt, { gesture: gestureType }));
}
return results;
}
showBrowserNotification(title, message, icon = '🤖') {
if ('Notification' in window) {
if (Notification.permission === 'granted') {
new Notification(`${icon} ${title}`, {
body: message,
icon: '/favicon.ico'
});
} else if (Notification.permission !== 'denied') {
Notification.requestPermission().then(permission => {
if (permission === 'granted') {
new Notification(`${icon} ${title}`, {
body: message,
icon: '/favicon.ico'
});
}
});
}
}
this.showToast(message, 'info');
}
showToast(message, type = 'info') {
const toast = document.createElement('div');
toast.className = `llm-toast llm-toast-${type}`;
toast.innerHTML = `
<span>${message}</span>
<button onclick="this.parentElement.remove()">×</button>
`;
document.body.appendChild(toast);
setTimeout(() => {
toast.classList.add('show');
}, 10);
setTimeout(() => {
toast.classList.remove('show');
setTimeout(() => toast.remove(), 300);
}, 3000);
}
checkProviderStatus() {
const status = {};
for (const [key, provider] of Object.entries(this.providers)) {
status[key] = {
available: true,
url: provider.url,
name: provider.name
};
}
return status;
}
getInstructions() {
return `
To use browser-based LLM launching:
1. Make sure you're logged into the AI services you want to use
2. Allow popups for this site
3. Use hand gestures to trigger queries
When a gesture is detected:
- The prompt will be copied to your clipboard
- The AI chat interface will open in a new tab
- Just paste (Ctrl+V) to send the prompt!
Supported services:
${Object.values(this.providers).map(p => `- ${p.name}: ${p.url}`).join('\n')}
`;
}
}
class GesturePromptBuilder {
constructor() {
this.templates = {
learning_explain: (context) => `Explain ${context.topic || 'this concept'} in simple terms.`,
doubt_resolution: (context) => {
let prompt = `I need help understanding: ${context.doubt || context.topic || 'this concept'}\n\n`;
prompt += `I've tried: ${context.attempted || 'reading the material'}\n`;
prompt += `What specifically confuses me is: ${context.confusion || 'the underlying concept'}`;
return prompt;
},
summarize: (context) => `Summarize the key points about ${context.topic || 'this topic'} in a way that's easy to understand.`,
practice: (context) => `Generate 5 practice questions about ${context.topic || 'this topic'} to test my understanding.`,
compare: (context) => `Compare and contrast ${context.concept_a || 'concept A'} and ${context.concept_b || 'concept B'}.`,
real_world: (context) => `Explain ${context.topic || 'this concept'} using real-world examples that a beginner would understand.`,
step_by_step: (context) => `Break down ${context.topic || 'this concept'} into simple, easy-to-follow steps.`,
common_mistakes: (context) => `What are the most common mistakes people make when learning about ${context.topic || 'this'}? How can I avoid them?`
};
}
build(gesture, context) {
const template = this.templates[context.template] || this.templates.learning_explain;
return template(context);
}
buildFromGestures(gestures, context) {
const prompts = [];
for (const gesture of gestures) {
const type = this.getGesturePromptType(gesture);
const prompt = this.build(type, context);
prompts.push({ gesture, prompt });
}
return prompts;
}
getGesturePromptType(gesture) {
if (gesture.includes('swipe_right')) return 'learning_explain';
if (gesture.includes('swipe_left')) return 'doubt_resolution';
if (gesture.includes('swipe_up')) return 'summarize';
if (gesture.includes('swipe_down')) return 'step_by_step';
if (gesture.includes('pinch')) return 'real_world';
if (gesture.includes('tap') || gesture.includes('1_finger')) return 'practice';
return 'learning_explain';
}
}
window.BrowserLLMLauncher = BrowserLLMLauncher;
window.GesturePromptBuilder = GesturePromptBuilder;
export default BrowserLLMLauncher;
export { BrowserLLMLauncher, GesturePromptBuilder };