Skip to content

Commit 84ffc30

Browse files
committed
fix: adjust of content manupulation
1 parent 7bd7096 commit 84ffc30

File tree

2 files changed

+175
-54
lines changed

2 files changed

+175
-54
lines changed

frontend/content.js

Lines changed: 165 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
let isPickerEnabled = false;
22
let hoveredElement = null;
33

4-
// The main listener for messages from the popup
54
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
65
if (message.type === "ENABLE_PICKER") {
76
if (!isPickerEnabled) enablePicker();
@@ -35,45 +34,193 @@ function onMouseOut(e) {
3534
hoveredElement = null;
3635
}
3736

38-
function onClick(e) {
37+
async function onClick(e) {
3938
e.preventDefault();
4039
e.stopPropagation();
4140

4241
const clickedElement = e.target;
4342
const selectedCode = clickedElement.textContent;
43+
const cacheKey = `translation_${hashCode(selectedCode)}`;
44+
const originalWidth = clickedElement.getBoundingClientRect().width;
4445
disablePicker();
4546

47+
const { targetLanguage } = await chrome.storage.sync.get('targetLanguage');
48+
const lang = targetLanguage || 'Java';
49+
50+
const cachedData = await getFromCache(cacheKey);
51+
if (cachedData && cachedData[lang]) {
52+
injectOrUpdateTranslations(cachedData, clickedElement, originalWidth);
53+
return;
54+
}
55+
4656
const loadingDiv = document.createElement('div');
4757
loadingDiv.className = 'translator-loading';
48-
loadingDiv.textContent = 'Translating...';
58+
loadingDiv.textContent = `Translating to ${lang}...`;
4959
clickedElement.parentNode.insertBefore(loadingDiv, clickedElement.nextSibling);
5060

51-
chrome.runtime.sendMessage({ type: "TRANSLATE_CODE", code: selectedCode }, (response) => {
61+
chrome.runtime.sendMessage({ type: "TRANSLATE_CODE", code: selectedCode }, async (response) => {
5262
loadingDiv.remove();
5363

5464
if (response.error) {
5565
alert(`Error: ${response.error}`);
5666
} else if (response.translation) {
57-
injectTranslatedCode(response.translation, clickedElement);
67+
const cleanedTranslation = response.translation.replace(/```[a-z]*\n/g, '').replace(/```/g, '').trim();
68+
69+
const newData = cachedData || {};
70+
newData[lang] = cleanedTranslation;
71+
await saveToCache(cacheKey, newData, 10);
72+
73+
injectOrUpdateTranslations(newData, clickedElement, originalWidth);
74+
}
75+
});
76+
}
77+
78+
function injectOrUpdateTranslations(translations, originalElement, width) {
79+
const componentStyles = `
80+
.tab-nav {
81+
display: flex;
82+
border-bottom: 1px solid #ccc;
83+
background-color: #f0f0f0;
84+
}
85+
.tab-link {
86+
padding: 10px 15px;
87+
cursor: pointer;
88+
border: none;
89+
background-color: transparent;
90+
font-size: 1em;
91+
font-weight: 500;
92+
color: #333;
93+
border-bottom: 3px solid transparent;
94+
}
95+
.tab-link:hover {
96+
background-color: #e5e5e5;
97+
}
98+
.tab-link.active {
99+
color: #007bff;
100+
border-bottom: 3px solid #007bff;
101+
}
102+
.tab-content-area {
103+
background-color: #fff;
104+
}
105+
.tab-content {
106+
display: none;
107+
}
108+
.tab-content.active {
109+
display: block;
110+
}
111+
pre {
112+
margin: 0;
113+
padding: 16px;
114+
white-space: pre-wrap;
115+
word-wrap: break-word;
116+
background-color: #f6f8fa;
117+
border-top: 1px solid #ddd;
118+
margin-bottom: 10px;
119+
}
120+
code {
121+
font-family: monospace;
122+
font-size: 0.65em;
123+
color: #24292e;
124+
line-height: 1;
125+
}
126+
`;
127+
128+
let container = originalElement.nextElementSibling;
129+
if (!container || container.id !== 'my-code-translator-container') {
130+
container = document.createElement('div');
131+
container.id = 'my-code-translator-container';
132+
133+
const shadowRoot = container.attachShadow({ mode: 'open' });
134+
135+
const styleElement = document.createElement('style');
136+
styleElement.textContent = componentStyles;
137+
shadowRoot.appendChild(styleElement);
138+
139+
const uiWrapper = document.createElement('div');
140+
shadowRoot.appendChild(uiWrapper);
141+
142+
originalElement.parentNode.insertBefore(container, originalElement.nextSibling);
143+
}
144+
145+
container.style.width = `${width}px`;
146+
container.style.boxSizing = 'border-box';
147+
148+
const shadowRoot = container.shadowRoot;
149+
const uiWrapper = shadowRoot.querySelector('div');
150+
uiWrapper.innerHTML = '';
151+
152+
const tabNav = document.createElement('div');
153+
tabNav.className = 'tab-nav';
154+
155+
const contentArea = document.createElement('div');
156+
contentArea.className = 'tab-content-area';
157+
158+
uiWrapper.appendChild(tabNav);
159+
uiWrapper.appendChild(contentArea);
160+
161+
Object.keys(translations).forEach((lang, index) => {
162+
const tabButton = document.createElement('button');
163+
tabButton.className = 'tab-link';
164+
tabButton.textContent = lang;
165+
166+
const contentPanel = document.createElement('div');
167+
contentPanel.className = 'tab-content';
168+
169+
const pre = document.createElement('pre');
170+
const code = document.createElement('code');
171+
code.textContent = translations[lang];
172+
pre.appendChild(code);
173+
contentPanel.appendChild(pre);
174+
175+
tabNav.appendChild(tabButton);
176+
contentArea.appendChild(contentPanel);
177+
178+
if (index === 0) {
179+
tabButton.classList.add('active');
180+
contentPanel.classList.add('active');
58181
}
182+
183+
tabButton.addEventListener('click', () => {
184+
shadowRoot.querySelectorAll('.tab-link').forEach(btn => btn.classList.remove('active'));
185+
shadowRoot.querySelectorAll('.tab-content').forEach(panel => panel.classList.remove('active'));
186+
187+
tabButton.classList.add('active');
188+
contentPanel.classList.add('active');
189+
});
59190
});
60191
}
61192

62-
function injectTranslatedCode(translatedCode, originalElement) {
63-
const container = document.createElement('div');
64-
container.className = 'translation-container';
193+
function hashCode(str) {
194+
let hash = 0;
195+
for (let i = 0, len = str.length; i < len; i++) {
196+
let chr = str.charCodeAt(i);
197+
hash = (hash << 5) - hash + chr;
198+
hash |= 0;
199+
}
200+
return hash;
201+
}
202+
203+
async function saveToCache(key, data, daysToExpire) {
204+
const expirationMs = daysToExpire * 24 * 60 * 60 * 1000;
205+
const cacheItem = {
206+
data: data,
207+
expiresAt: Date.now() + expirationMs,
208+
};
209+
await chrome.storage.local.set({ [key]: cacheItem });
210+
}
65211

66-
const header = document.createElement('h4');
67-
header.textContent = 'AI-Generated Translation:';
212+
async function getFromCache(key) {
213+
const result = await chrome.storage.local.get(key);
214+
const cacheItem = result[key];
68215

69-
const pre = document.createElement('pre');
70-
const code = document.createElement('code');
71-
code.textContent = translatedCode;
72-
pre.appendChild(code);
216+
if (!cacheItem) {
217+
return null;
218+
}
73219

74-
container.appendChild(header);
75-
container.appendChild(pre);
220+
if (Date.now() > cacheItem.expiresAt) {
221+
await chrome.storage.local.remove(key);
222+
return null;
223+
}
76224

77-
// Insert the container right after the original element
78-
originalElement.parentNode.insertBefore(container, originalElement.nextSibling);
225+
return cacheItem.data;
79226
}

frontend/styles.css

Lines changed: 10 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,15 @@
1-
body {
2-
font-family: sans-serif;
3-
width: 250px;
4-
padding: 10px;
5-
}
6-
.container {
7-
text-align: center;
8-
}
9-
h1 {
10-
font-size: 1.2em;
11-
margin-bottom: 5px;
12-
}
13-
p {
14-
font-size: 0.9em;
15-
color: #555;
16-
margin-top: 0;
17-
}
18-
select,
19-
button {
20-
width: 100%;
21-
padding: 8px;
22-
margin-top: 10px;
23-
box-sizing: border-box;
24-
}
25-
button {
26-
background-color: #007bff;
27-
color: white;
28-
border: none;
29-
cursor: pointer;
30-
font-weight: bold;
31-
}
32-
button:hover {
33-
background-color: #0056b3;
34-
}
35-
36-
/* Style for the element picker highlight (SAFE to inject on any page) */
371
.translator-highlight {
382
outline: 2px dashed #007bff !important;
393
background-color: rgba(0, 123, 255, 0.1) !important;
404
cursor: crosshair !important;
415
}
6+
7+
.translator-loading {
8+
margin-top: 10px;
9+
padding: 15px;
10+
background-color: #e9f5ff;
11+
border: 1px solid #b3d7ff;
12+
border-radius: 4px;
13+
color: #0056b3;
14+
font-family: sans-serif;
15+
}

0 commit comments

Comments
 (0)