Skip to content

Commit 77f4b4c

Browse files
Merge pull request #2 from OhFeel/master
Enhance emoji fetching and UI improvements
2 parents 195ab31 + dee4b33 commit 77f4b4c

File tree

4 files changed

+279
-108
lines changed

4 files changed

+279
-108
lines changed

source/background.js

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,53 @@
11
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
2-
if (message.type === "fetchEmojipedia") {
3-
fetch(message.url)
4-
.then(res => res.text())
5-
.then(html => sendResponse({ html }))
6-
.catch(err => sendResponse({ error: err.toString() }));
7-
return true;
8-
}
2+
if (message.type === "fetchEmojipedia") {
3+
if (!message.url || !message.url.startsWith("https://emojipedia.org")) {
4+
sendResponse({ error: "Invalid URL provided" });
5+
return true;
6+
}
7+
8+
const controller = new AbortController();
9+
const timeoutId = setTimeout(() => controller.abort(), 10000);
10+
11+
fetch(message.url, {
12+
signal: controller.signal,
13+
headers: {
14+
'User-Agent': 'Mozilla/5.0 (compatible; EmojiCopyExtension/1.0)'
15+
}
16+
})
17+
.then(response => {
18+
clearTimeout(timeoutId);
19+
20+
if (!response.ok) {
21+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
22+
}
23+
24+
const contentType = response.headers.get('content-type');
25+
if (!contentType || !contentType.includes('text/html')) {
26+
throw new Error('Response is not HTML content');
27+
}
28+
29+
return response.text();
30+
})
31+
.then(html => {
32+
if (!html || html.trim().length === 0) {
33+
throw new Error('Empty response received');
34+
}
35+
sendResponse({ html });
36+
})
37+
.catch(err => {
38+
clearTimeout(timeoutId);
39+
console.error('Fetch error:', err);
40+
41+
let errorMessage = 'Failed to fetch emoji data';
42+
if (err.name === 'AbortError') {
43+
errorMessage = 'Request timed out';
44+
} else if (err.message) {
45+
errorMessage = err.message;
46+
}
47+
48+
sendResponse({ error: errorMessage });
49+
});
50+
51+
return true;
52+
}
953
});

source/content.js

Lines changed: 213 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,234 @@
1+
const EMOJI_SELECTORS = [
2+
'.Emoji_emoji-large__GG4kj',
3+
'[data-emoji-char]',
4+
'.emoji-large'
5+
];
6+
7+
const STYLES = {
8+
box: {
9+
light: {
10+
backgroundColor: '#f8f9fa',
11+
borderColor: '#ccc',
12+
color: 'inherit'
13+
},
14+
dark: {
15+
backgroundColor: '#303134',
16+
borderColor: '#5f6368',
17+
color: '#e8eaed'
18+
}
19+
}
20+
};
21+
22+
function showUserError(searchResult, message) {
23+
if (searchResult.querySelector('.emoji-error-box')) return;
24+
25+
const errorBox = document.createElement('div');
26+
errorBox.className = 'emoji-error-box';
27+
28+
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
29+
const theme = isDark ? STYLES.box.dark : STYLES.box.light;
30+
31+
errorBox.style.cssText = `
32+
width: 100%;
33+
display: flex;
34+
justify-content: center;
35+
align-items: center;
36+
gap: 8px;
37+
padding: 8px 12px;
38+
margin-top: 0px;
39+
margin-bottom: 20px;
40+
border-radius: 6px;
41+
font-size: 14px;
42+
border: 1px solid #ea4335;
43+
background-color: ${isDark ? '#2d1b1e' : '#fce8e6'};
44+
color: #ea4335;
45+
box-sizing: border-box;
46+
`;
47+
48+
errorBox.innerHTML = `
49+
<span>⚠️</span>
50+
<span>${message}</span>
51+
`;
52+
53+
searchResult.appendChild(errorBox);
54+
55+
setTimeout(() => {
56+
if (errorBox.parentNode) {
57+
errorBox.parentNode.removeChild(errorBox);
58+
}
59+
}, 5000);
60+
}
61+
162
async function getEmojiFromEmojipedia(url) {
263
return new Promise((resolve, reject) => {
364
chrome.runtime.sendMessage({ type: "fetchEmojipedia", url }, (response) => {
4-
if (response.error) {
5-
reject(response.error);
65+
if (chrome.runtime.lastError) {
66+
reject(`Connection error: ${chrome.runtime.lastError.message}`);
67+
return;
68+
}
69+
70+
if (response?.error) {
71+
reject(`Fetch error: ${response.error}`);
672
return;
773
}
874

9-
const parser = new DOMParser();
10-
const doc = parser.parseFromString(response.html, 'text/html');
75+
if (!response?.html) {
76+
reject('No content received from Emojipedia');
77+
return;
78+
}
1179

12-
const emojiElement = doc.querySelector('.Emoji_emoji-large__GG4kj');
80+
try {
81+
const parser = new DOMParser();
82+
const doc = parser.parseFromString(response.html, 'text/html');
1383

14-
if (emojiElement) {
15-
resolve(emojiElement.innerText.trim());
16-
} else {
17-
reject('Emoji not found on page.');
84+
for (const selector of EMOJI_SELECTORS) {
85+
const emojiElement = doc.querySelector(selector);
86+
if (emojiElement?.textContent?.trim()) {
87+
resolve(emojiElement.textContent.trim());
88+
return;
89+
}
90+
}
91+
92+
reject('Emoji not found on page');
93+
} catch (error) {
94+
reject(`Page parsing failed: ${error.message}`);
1895
}
1996
});
2097
});
2198
}
2299

23100
function createBox(emoji, searchResult) {
24-
if (!searchResult) return;
25-
26-
if (searchResult.querySelector('.emoji-copy-box')) return;
27-
28-
const box = document.createElement('div');
29-
box.className = 'emoji-copy-box';
30-
box.style.cssText = `
31-
width: 100%;
32-
display: flex;
33-
justify-content: center;
34-
align-items: center;
35-
gap: 12px;
36-
padding: 8px 12px;
37-
margin-top: 0px;
38-
margin-bottom: 20px;
39-
border-radius: 6px;
40-
font-size: 26px;
41-
border: 1px solid #ccc;
42-
background-color: #f8f9fa;
43-
box-sizing: border-box;
44-
color: inherit;
45-
`;
46-
47-
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
48-
if (isDark) {
49-
box.style.backgroundColor = '#303134';
50-
box.style.borderColor = '#5f6368';
51-
box.style.color = '#e8eaed';
52-
}
53-
54-
const emojiSpan = document.createElement('span');
55-
emojiSpan.textContent = emoji;
56-
57-
const copyButton = document.createElement('button');
58-
copyButton.textContent = 'Copy';
59-
copyButton.style.cssText = `
60-
background-color: #1a73e8;
61-
border: none;
62-
color: white;
63-
padding: 6px 14px;
64-
border-radius: 6px;
65-
cursor: pointer;
66-
font-size: 16px;
67-
margin-left: 10px;
68-
`;
69-
70-
copyButton.addEventListener('click', () => {
71-
navigator.clipboard.writeText(emoji).then(() => {
72-
copyButton.textContent = 'Copied!';
73-
setTimeout(() => {
74-
copyButton.textContent = 'Copy';
75-
}, 1200);
76-
}).catch(err => {
77-
console.error('Clipboard error:', err);
78-
copyButton.textContent = 'Error';
101+
if (!searchResult || !emoji) return;
102+
103+
if (searchResult.querySelector('.emoji-copy-box')) return;
104+
105+
const box = document.createElement('div');
106+
box.className = 'emoji-copy-box';
107+
108+
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
109+
const theme = isDark ? STYLES.box.dark : STYLES.box.light;
110+
111+
box.style.cssText = `
112+
width: 100%;
113+
display: flex;
114+
justify-content: center;
115+
align-items: center;
116+
gap: 12px;
117+
padding: 8px 12px;
118+
margin-top: 0px;
119+
margin-bottom: 20px;
120+
border-radius: 6px;
121+
font-size: 26px;
122+
border: 1px solid ${theme.borderColor};
123+
background-color: ${theme.backgroundColor};
124+
box-sizing: border-box;
125+
color: ${theme.color};
126+
`;
127+
128+
const emojiSpan = document.createElement('span');
129+
emojiSpan.textContent = emoji;
130+
131+
const copyButton = document.createElement('button');
132+
copyButton.textContent = 'Copy';
133+
copyButton.style.cssText = `
134+
background-color: #1a73e8;
135+
border: none;
136+
color: white;
137+
padding: 6px 14px;
138+
border-radius: 6px;
139+
cursor: pointer;
140+
font-size: 16px;
141+
margin-left: 10px;
142+
transition: background-color 0.2s ease;
143+
`;
144+
145+
copyButton.addEventListener('mouseenter', () => {
146+
copyButton.style.backgroundColor = '#1557b0';
147+
});
148+
149+
copyButton.addEventListener('mouseleave', () => {
150+
copyButton.style.backgroundColor = '#1a73e8';
151+
});
152+
153+
copyButton.addEventListener('click', async () => {
154+
try {
155+
await navigator.clipboard.writeText(emoji);
156+
const originalText = copyButton.textContent;
157+
copyButton.textContent = 'Copied!';
158+
copyButton.style.backgroundColor = '#34a853';
159+
160+
setTimeout(() => {
161+
copyButton.textContent = originalText;
162+
copyButton.style.backgroundColor = '#1a73e8';
163+
}, 1200);
164+
} catch (err) {
165+
console.error('Clipboard error:', err);
166+
copyButton.textContent = 'Failed';
167+
copyButton.style.backgroundColor = '#ea4335';
168+
169+
setTimeout(() => {
170+
copyButton.textContent = 'Copy';
171+
copyButton.style.backgroundColor = '#1a73e8';
172+
}, 1200);
173+
}
79174
});
80-
});
81175

82-
box.appendChild(emojiSpan);
83-
box.appendChild(copyButton);
176+
box.appendChild(emojiSpan);
177+
box.appendChild(copyButton);
178+
searchResult.appendChild(box);
179+
}
84180

85-
searchResult.appendChild(box);
181+
function processSearchResults() {
182+
const resultLinks = document.querySelectorAll('.zReHs');
183+
184+
resultLinks.forEach((link) => {
185+
const searchResult = link.closest('.MjjYud');
186+
if (!searchResult) return;
187+
188+
const firstElement = searchResult.firstElementChild?.firstElementChild;
189+
if (!firstElement || firstElement.hasAttribute('data-initq')) return;
190+
191+
if (link.href?.startsWith("https://emojipedia.org")) {
192+
getEmojiFromEmojipedia(link.href)
193+
.then(emoji => createBox(emoji, searchResult))
194+
.catch(err => {
195+
console.warn('Failed to fetch emoji:', err);
196+
showUserError(searchResult, 'Failed to load emoji');
197+
});
198+
}
199+
});
86200
}
87201

88-
chrome.storage.local.get(['enabled'], (data) => {
89-
if (data.enabled !== false) {
90-
window.addEventListener('DOMContentLoaded', function() {
91-
// const prompt = document.getElementById('APjFqb').value.toLowerCase();
92-
// if (prompt.includes('emoji')) {
93-
const resultLinks = document.getElementsByClassName('zReHs');
94-
95-
Array.from(resultLinks).forEach((link) => {
96-
let searchResult = link.closest('.MjjYud');
97-
if (searchResult) {
98-
99-
let n = searchResult.firstElementChild?.firstElementChild
100-
if (n && !n.hasAttribute('data-initq')) {
101-
if (link.href.startsWith("https://emojipedia.org")) {
102-
getEmojiFromEmojipedia(link.href)
103-
.then(emoji => createBox(emoji, searchResult))
104-
.catch(err => console.error('Error:', err));
105-
}
106-
}
202+
function initializeExtension() {
203+
chrome.storage.local.get(['enabled'], (data) => {
204+
if (data.enabled !== false) {
205+
if (document.readyState === 'loading') {
206+
document.addEventListener('DOMContentLoaded', processSearchResults);
207+
} else {
208+
processSearchResults();
209+
}
210+
211+
const observer = new MutationObserver((mutations) => {
212+
let shouldProcess = false;
213+
mutations.forEach((mutation) => {
214+
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
215+
shouldProcess = true;
107216
}
108217
});
109-
// }
110-
});
111-
}
112-
});
218+
219+
if (shouldProcess) {
220+
setTimeout(processSearchResults, 100);
221+
}
222+
});
223+
224+
observer.observe(document.body, {
225+
childList: true,
226+
subtree: true
227+
});
228+
}
229+
});
230+
}
231+
232+
if (typeof chrome !== 'undefined' && chrome.storage) {
233+
initializeExtension();
234+
}

0 commit comments

Comments
 (0)