Skip to content

Commit 5f7f974

Browse files
committed
Added copy button to code translation panel
1 parent f3f52d4 commit 5f7f974

File tree

1 file changed

+118
-64
lines changed

1 file changed

+118
-64
lines changed

frontend/scripts/ui.js

Lines changed: 118 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
export function injectOrUpdateTranslations(translations, originalElement, width) {
2-
const componentStyles = `
1+
export function injectOrUpdateTranslations(
2+
translations,
3+
originalElement,
4+
width
5+
) {
6+
const componentStyles = `
37
.tab-nav {
48
display: flex;
59
border-bottom: 1px solid #ccc;
@@ -29,6 +33,31 @@ export function injectOrUpdateTranslations(translations, originalElement, width)
2933
.tab-content.active {
3034
display: block;
3135
}
36+
.code-wrapper{
37+
position:relative
38+
}
39+
.copy-button {
40+
position: absolute;
41+
top: 8px;
42+
right: 8px;
43+
padding: 6px 12px;
44+
font-size: 14px;
45+
background-color: rgba(255, 255, 255, 0.08); /* soft overlay */
46+
border: 1px solid rgba(255, 255, 255, 0.2);
47+
border-radius: 4px;
48+
color: #f0f0f0;
49+
cursor: pointer;
50+
transition: background-color 0.3s, border-color 0.3s, color 0.3s;
51+
z-index: 10;
52+
}
53+
.copy-button:hover {
54+
background-color: rgba(255, 255, 255, 0.15);
55+
border-color: rgba(255, 255, 255, 0.3);
56+
color: #ffffff;
57+
}
58+
.copy-button:active {
59+
background-color: rgba(255, 255, 255, 0.2);
60+
}
3261
pre {
3362
margin: 0;
3463
white-space: pre-wrap;
@@ -40,71 +69,96 @@ export function injectOrUpdateTranslations(translations, originalElement, width)
4069
}
4170
`;
4271

43-
let container = originalElement.nextElementSibling;
72+
let container = originalElement.nextElementSibling;
4473

45-
if (!container || container.id !== 'my-code-translator-container') {
46-
container = document.createElement('div');
47-
container.id = 'my-code-translator-container';
48-
const shadowRoot = container.attachShadow({ mode: 'open' });
49-
const prismTheme = document.createElement('link');
50-
prismTheme.rel = 'stylesheet';
51-
prismTheme.href = chrome.runtime.getURL('packages/prism.css');
52-
shadowRoot.appendChild(prismTheme);
53-
const styleElement = document.createElement('style');
54-
styleElement.textContent = componentStyles;
55-
shadowRoot.appendChild(styleElement);
56-
const uiWrapper = document.createElement('div');
57-
uiWrapper.className = 'ui-wrapper';
58-
shadowRoot.appendChild(uiWrapper);
59-
originalElement.parentNode.insertBefore(container, originalElement.nextSibling);
60-
}
74+
if (!container || container.id !== "my-code-translator-container") {
75+
container = document.createElement("div");
76+
container.id = "my-code-translator-container";
77+
const shadowRoot = container.attachShadow({ mode: "open" });
78+
const prismTheme = document.createElement("link");
79+
prismTheme.rel = "stylesheet";
80+
prismTheme.href = chrome.runtime.getURL("packages/prism.css");
81+
shadowRoot.appendChild(prismTheme);
82+
const styleElement = document.createElement("style");
83+
styleElement.textContent = componentStyles;
84+
shadowRoot.appendChild(styleElement);
85+
const uiWrapper = document.createElement("div");
86+
uiWrapper.className = "ui-wrapper";
87+
shadowRoot.appendChild(uiWrapper);
88+
originalElement.parentNode.insertBefore(
89+
container,
90+
originalElement.nextSibling
91+
);
92+
}
6193

62-
container.style.width = `${width}px`;
63-
container.style.boxSizing = 'border-box';
64-
const shadowRoot = container.shadowRoot;
65-
const uiWrapper = shadowRoot.querySelector('.ui-wrapper');
66-
uiWrapper.innerHTML = '';
67-
const tabNav = document.createElement('div');
68-
tabNav.className = 'tab-nav';
69-
const contentArea = document.createElement('div');
70-
contentArea.className = 'tab-content-area';
71-
uiWrapper.appendChild(tabNav);
72-
uiWrapper.appendChild(contentArea);
73-
Object.keys(translations).forEach(lang => {
74-
const contentPanel = document.createElement('div');
75-
contentPanel.className = 'tab-content';
76-
contentPanel.dataset.lang = lang;
77-
const langClass = `language-${lang.toLowerCase()}`;
78-
const pre = document.createElement('pre');
79-
pre.className = langClass;
80-
const code = document.createElement('code');
81-
code.className = langClass;
82-
code.textContent = translations[lang];
83-
pre.appendChild(code);
84-
contentPanel.appendChild(pre);
85-
contentArea.appendChild(contentPanel);
94+
container.style.width = `${width}px`;
95+
container.style.boxSizing = "border-box";
96+
const shadowRoot = container.shadowRoot;
97+
const uiWrapper = shadowRoot.querySelector(".ui-wrapper");
98+
uiWrapper.innerHTML = "";
99+
const tabNav = document.createElement("div");
100+
tabNav.className = "tab-nav";
101+
const contentArea = document.createElement("div");
102+
contentArea.className = "tab-content-area";
103+
uiWrapper.appendChild(tabNav);
104+
uiWrapper.appendChild(contentArea);
105+
Object.keys(translations).forEach((lang) => {
106+
const contentPanel = document.createElement("div");
107+
contentPanel.className = "tab-content";
108+
contentPanel.dataset.lang = lang;
109+
const codeWrapper = document.createElement("div");
110+
codeWrapper.className = "code-wrapper";
111+
const copyButton = document.createElement("div");
112+
copyButton.className = "copy-button";
113+
copyButton.innerText = "copy";
114+
copyButton.addEventListener("click", () => {
115+
navigator.clipboard.writeText(translations[lang]).then(() => {
116+
copyButton.innerText = "Copied!";
117+
setTimeout(() => (copyButton.innerText = "Copy"), 2000);
118+
});
86119
});
120+
const langClass = `language-${lang.toLowerCase()}`;
121+
const pre = document.createElement("pre");
122+
pre.className = langClass;
123+
const code = document.createElement("code");
124+
code.className = langClass;
125+
code.textContent = translations[lang];
87126

88-
Object.keys(translations).forEach((lang, index) => {
89-
const tabButton = document.createElement('button');
90-
tabButton.className = 'tab-link';
91-
tabButton.textContent = lang;
92-
tabButton.addEventListener('click', () => {
93-
shadowRoot.querySelectorAll('.tab-link').forEach(btn => btn.classList.remove('active'));
94-
shadowRoot.querySelectorAll('.tab-content').forEach(panel => panel.classList.remove('active'));
95-
tabButton.classList.add('active');
96-
shadowRoot.querySelector(`.tab-content[data-lang="${lang}"]`).classList.add('active');
97-
});
98-
tabNav.appendChild(tabButton);
99-
if (index === 0) {
100-
tabButton.click();
101-
}
127+
pre.appendChild(code);
128+
codeWrapper.appendChild(copyButton);
129+
codeWrapper.appendChild(pre);
130+
contentPanel.appendChild(codeWrapper);
131+
contentArea.appendChild(contentPanel);
132+
});
133+
134+
Object.keys(translations).forEach((lang, index) => {
135+
const tabButton = document.createElement("button");
136+
tabButton.className = "tab-link";
137+
tabButton.textContent = lang;
138+
tabButton.addEventListener("click", () => {
139+
shadowRoot
140+
.querySelectorAll(".tab-link")
141+
.forEach((btn) => btn.classList.remove("active"));
142+
shadowRoot
143+
.querySelectorAll(".tab-content")
144+
.forEach((panel) => panel.classList.remove("active"));
145+
tabButton.classList.add("active");
146+
shadowRoot
147+
.querySelector(`.tab-content[data-lang="${lang}"]`)
148+
.classList.add("active");
102149
});
103-
try {
104-
if (window.Prism) {
105-
contentArea.querySelectorAll(`pre[class*="language-"]`).forEach(element => window.Prism.highlightElement(element));
106-
}
107-
} catch (e) {
108-
console.error('CodeTranslateAI: Error highlighting syntax.', e);
150+
tabNav.appendChild(tabButton);
151+
if (index === 0) {
152+
tabButton.click();
153+
}
154+
});
155+
try {
156+
if (window.Prism) {
157+
contentArea
158+
.querySelectorAll(`pre[class*="language-"]`)
159+
.forEach((element) => window.Prism.highlightElement(element));
109160
}
110-
}
161+
} catch (e) {
162+
console.error("CodeTranslateAI: Error highlighting syntax.", e);
163+
}
164+
}

0 commit comments

Comments
 (0)