-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathideogram-handler.js
More file actions
315 lines (271 loc) · 10.7 KB
/
Copy pathideogram-handler.js
File metadata and controls
315 lines (271 loc) · 10.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
// Content script specifically for the Ideogram model page
// This handles receiving images and uploading them to the model
(function() {
// Prevent multiple injections
if (window.ideogramHandlerLoaded) return;
window.ideogramHandlerLoaded = true;
console.log('[Ideogram Ext] Handler loaded on:', window.location.href);
// Listen for upload requests
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
console.log('[Ideogram Ext] Received message:', message.action);
if (message.action === 'uploadImage') {
handleImageUpload(message.imageData, message.imageUrl, message.imageSrc)
.then(result => {
console.log('[Ideogram Ext] Upload result:', result);
sendResponse(result);
})
.catch(error => {
console.error('[Ideogram Ext] Upload error:', error);
sendResponse({ success: false, error: error.message });
});
return true; // Keep channel open for async response
}
if (message.action === 'ping') {
sendResponse({ success: true, message: 'Handler is ready' });
return true;
}
});
// Handle uploading an image to the model
async function handleImageUpload(imageData, imageUrl, fallbackSrc) {
console.log('[Ideogram Ext] Starting upload...', { hasImageData: !!imageData, imageUrl });
try {
let file;
// If we received image data (base64), convert it to a file
if (imageData) {
console.log('[Ideogram Ext] Converting base64 to file...');
const blob = base64ToBlob(imageData.data, imageData.type);
const fileName = `image_${Date.now()}.${imageData.type.split('/')[1] || 'jpg'}`;
file = new File([blob], fileName, { type: imageData.type });
console.log('[Ideogram Ext] Created file:', fileName, 'size:', file.size);
} else {
// Try to fetch directly (might work for same-origin or CORS-enabled images)
console.log('[Ideogram Ext] No image data, trying direct fetch...');
const urlToFetch = imageUrl || fallbackSrc;
try {
const response = await fetch(urlToFetch, { mode: 'cors', credentials: 'omit' });
if (!response.ok) throw new Error('Fetch failed: ' + response.status);
const blob = await response.blob();
const fileName = `image_${Date.now()}.${blob.type.split('/')[1] || 'jpg'}`;
file = new File([blob], fileName, { type: blob.type });
} catch (fetchError) {
console.error('[Ideogram Ext] Direct fetch failed:', fetchError);
// Fall back to copying URL to clipboard
return copyUrlFallback(imageUrl || fallbackSrc);
}
}
// Now try to upload the file
const uploadResult = await uploadFileToIdeogram(file);
return uploadResult;
} catch (error) {
console.error('[Ideogram Ext] Upload error:', error);
return { success: false, error: error.message };
}
}
// Convert base64 to blob
function base64ToBlob(base64, mimeType) {
const byteCharacters = atob(base64);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
return new Blob([byteArray], { type: mimeType });
}
// Upload file to Ideogram
async function uploadFileToIdeogram(file) {
console.log('[Ideogram Ext] Looking for upload mechanism...');
// Method 1: Find file input
const fileInput = findFileInput();
if (fileInput) {
console.log('[Ideogram Ext] Found file input:', fileInput);
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
fileInput.files = dataTransfer.files;
// Dispatch multiple events to ensure React/Vue picks it up
fileInput.dispatchEvent(new Event('change', { bubbles: true }));
fileInput.dispatchEvent(new Event('input', { bubbles: true }));
// Also try to trigger React's synthetic event
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'files').set;
if (nativeInputValueSetter) {
nativeInputValueSetter.call(fileInput, dataTransfer.files);
fileInput.dispatchEvent(new Event('input', { bubbles: true }));
}
showNotification('Image added!', 'success');
return { success: true, message: 'Image added to upload queue' };
}
// Method 2: Find drop zone and simulate drop
const dropZone = findDropZone();
if (dropZone) {
console.log('[Ideogram Ext] Found drop zone:', dropZone);
const success = simulateDrop(dropZone, file);
if (success) {
showNotification('Image dropped!', 'success');
return { success: true, message: 'Image dropped on upload area' };
}
}
// Method 3: Try to find and click an upload button
const clickResult = await tryClickUpload(file);
if (clickResult.success) {
showNotification('Image uploaded!', 'success');
return clickResult;
}
// Fallback: Copy URL
console.log('[Ideogram Ext] No upload mechanism found, falling back to notification');
showNotification('Could not auto-upload. Please drag the image manually.', 'info');
return { success: false, error: 'No upload mechanism found. Try dragging the image manually.' };
}
// Find file input element
function findFileInput() {
const selectors = [
'input[type="file"]',
'input[accept*="image"]',
'input[accept*=".png"]',
'input[accept*=".jpg"]',
'[data-testid*="file"]',
'[data-testid*="upload"]'
];
for (const selector of selectors) {
const inputs = document.querySelectorAll(selector);
for (const input of inputs) {
// Prefer visible inputs
const style = window.getComputedStyle(input);
if (style.display !== 'none' || input.offsetParent !== null) {
return input;
}
}
// Return hidden one if that's all we have
if (inputs.length > 0) return inputs[0];
}
return null;
}
// Find drop zone
function findDropZone() {
// Look for common drop zone patterns
const selectors = [
'[class*="dropzone"]',
'[class*="drop-zone"]',
'[class*="upload-area"]',
'[class*="upload-zone"]',
'[data-testid*="drop"]',
'[data-testid*="upload"]'
];
for (const selector of selectors) {
const el = document.querySelector(selector);
if (el) return el;
}
// Look for elements that mention "drop" or "drag" in their text
const allElements = document.querySelectorAll('div, section, label');
for (const el of allElements) {
const text = el.textContent?.toLowerCase() || '';
if ((text.includes('drop') || text.includes('drag')) && text.includes('image')) {
return el;
}
}
return null;
}
// Simulate drag and drop
function simulateDrop(dropZone, file) {
try {
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
// Create and dispatch drag events
const events = ['dragenter', 'dragover', 'drop'];
for (const eventType of events) {
const event = new DragEvent(eventType, {
bubbles: true,
cancelable: true,
dataTransfer: dataTransfer
});
dropZone.dispatchEvent(event);
}
return true;
} catch (e) {
console.error('[Ideogram Ext] Drop simulation failed:', e);
return false;
}
}
// Try to click upload button
async function tryClickUpload(file) {
const buttons = document.querySelectorAll('button, [role="button"], label');
for (const btn of buttons) {
const text = (btn.textContent || '').toLowerCase();
const ariaLabel = (btn.getAttribute('aria-label') || '').toLowerCase();
if (text.includes('upload') || text.includes('add image') ||
ariaLabel.includes('upload') || ariaLabel.includes('add')) {
console.log('[Ideogram Ext] Clicking upload button:', btn);
btn.click();
// Wait for file input to appear
await new Promise(r => setTimeout(r, 300));
const fileInput = findFileInput();
if (fileInput) {
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
fileInput.files = dataTransfer.files;
fileInput.dispatchEvent(new Event('change', { bubbles: true }));
return { success: true, message: 'Image uploaded via button' };
}
}
}
return { success: false };
}
// Fallback: copy URL to clipboard
async function copyUrlFallback(url) {
try {
await navigator.clipboard.writeText(url);
showNotification('Image URL copied to clipboard!', 'info');
return { success: true, message: 'Image URL copied to clipboard - paste manually' };
} catch (e) {
showNotification('Could not process image', 'error');
return { success: false, error: 'Could not fetch or copy image' };
}
}
// Show a notification on the page
function showNotification(message, type = 'success') {
// Remove existing notifications
document.querySelectorAll('.ideogram-ext-notification').forEach(n => n.remove());
const notification = document.createElement('div');
notification.className = 'ideogram-ext-notification';
notification.textContent = message;
const colors = {
success: '#10b981',
error: '#ef4444',
info: '#6366f1'
};
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
padding: 12px 24px;
background: ${colors[type] || colors.info};
color: white;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
z-index: 999999;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
animation: ideogramSlideIn 0.3s ease;
`;
// Add animation keyframes if not already added
if (!document.getElementById('ideogram-ext-styles')) {
const style = document.createElement('style');
style.id = 'ideogram-ext-styles';
style.textContent = `
@keyframes ideogramSlideIn {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
`;
document.head.appendChild(style);
}
document.body.appendChild(notification);
// Remove after 3 seconds
setTimeout(() => {
notification.style.opacity = '0';
notification.style.transform = 'translateX(100%)';
notification.style.transition = 'all 0.3s ease';
setTimeout(() => notification.remove(), 300);
}, 3000);
}
console.log('[Ideogram Ext] Handler ready and listening');
})();