New:Socket for Asana Is Now Available.Learn more
Get Started

Screenshot for YouTube™ | 4K Frame & Thumbnail Cap

Package Overview
Versions
2
Alerts
File Explorer
Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

youtube-frame-extractor@example.com - firefox Package Compare versions

Comparing version
1.2.2
to
1.2.3
+13
-1
_locales/ar/messages.json

@@ -73,3 +73,15 @@ {

"description": "Reset settings button"
},
"copyBtn": {
"message": "📋 نسخ للحافظة",
"description": "Button to copy frame to clipboard"
},
"copySuccess": {
"message": "تم نسخ الإطار للحافظة!",
"description": "Success message after copying a frame"
},
"showPreviewLabel": {
"message": "تفعيل معاينة الصورة",
"description": "Setting to toggle thumbnail preview"
}
}
}

@@ -73,3 +73,15 @@ {

"description": "Reset settings button"
},
"copyBtn": {
"message": "📋 Copy to Clipboard",
"description": "Button to copy frame to clipboard"
},
"copySuccess": {
"message": "Frame copied to clipboard!",
"description": "Success message after copying a frame"
},
"showPreviewLabel": {
"message": "Show Thumbnail Preview",
"description": "Setting to toggle thumbnail preview"
}
}
}
+157
-126

@@ -6,3 +6,3 @@ :root {

--gold-gradient: linear-gradient(135deg, #f6d365 0%, #fda085 100%);
--bg-color: #ffffff;

@@ -13,7 +13,7 @@ --surface-color: #f8f9fa;

--border-color: #e2e8f0;
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
--shadow-colored: 0 4px 14px 0 rgba(0, 118, 255, 0.39);
--radius-md: 8px;

@@ -32,3 +32,3 @@ --radius-lg: 12px;

body {
width: 350px;
width: 380px;
background-color: var(--bg-color);

@@ -40,6 +40,6 @@ color: var(--text-primary);

.popup-container {
padding: 16px;
padding: 14px;
display: flex;
flex-direction: column;
gap: 16px;
gap: 12px;
}

@@ -58,3 +58,3 @@

font-weight: 700;
font-size: 16px;
font-size: 15px;
display: flex;

@@ -65,2 +65,3 @@ align-items: center;

-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;

@@ -71,3 +72,4 @@ }

font-size: 18px;
-webkit-text-fill-color: initial; /* Don't apply gradient to emoji */
-webkit-text-fill-color: initial;
/* Don't apply gradient to emoji */
}

@@ -107,11 +109,21 @@

.action-buttons-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.primary-action-btn.copy-btn {
background: var(--secondary-gradient);
}
.primary-action-btn {
width: 100%;
padding: 14px;
padding: 10px 14px;
background: var(--primary-gradient);
border: none;
border-radius: var(--radius-lg);
border-radius: var(--radius-md);
color: white;
font-weight: 700;
font-size: 16px;
font-weight: 600;
font-size: 14px;
cursor: pointer;

@@ -122,3 +134,3 @@ transition: transform 0.2s, box-shadow 0.2s;

justify-content: center;
gap: 8px;
gap: 6px;
box-shadow: var(--shadow-colored);

@@ -139,3 +151,3 @@ }

display: flex;
gap: 10px;
gap: 8px;
}

@@ -145,3 +157,3 @@

flex: 1;
padding: 10px;
padding: 8px;
border: 1px solid var(--border-color);

@@ -154,5 +166,5 @@ background: white;

justify-content: center;
gap: 6px;
gap: 5px;
font-weight: 600;
font-size: 13px;
font-size: 12px;
transition: all 0.2s;

@@ -180,2 +192,3 @@ color: var(--text-primary);

-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;

@@ -185,3 +198,4 @@ }

.rate-btn .btn-icon {
color: orange; /* Fallback */
color: orange;
/* Fallback */
}

@@ -192,3 +206,3 @@

background: var(--surface-color);
padding: 10px 12px;
padding: 8px 10px;
border-radius: var(--radius-md);

@@ -198,3 +212,3 @@ display: flex;

align-items: center;
font-size: 13px;
font-size: 12px;
}

@@ -234,145 +248,156 @@

.tools-section {
border-top: 1px solid var(--border-color);
padding-top: 12px;
border-top: 1px solid var(--border-color);
padding-top: 10px;
}
.tools-header {
margin-bottom: 8px;
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
color: var(--text-secondary);
letter-spacing: 0.5px;
margin-bottom: 8px;
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
color: var(--text-secondary);
letter-spacing: 0.5px;
}
.thumbnail-tool-container {
background: var(--surface-color);
border-radius: var(--radius-md);
padding: 10px;
margin-bottom: 10px;
background: var(--surface-color);
border-radius: var(--radius-md);
padding: 8px;
margin-bottom: 8px;
}
.secondary-btn {
width: 100%;
padding: 8px;
background: white;
border: 1px solid var(--border-color);
border-radius: 6px;
color: var(--text-primary);
font-weight: 600;
cursor: pointer;
font-size: 13px;
transition: all 0.2s;
width: 100%;
padding: 7px 10px;
background: white;
border: 1px solid var(--border-color);
border-radius: 6px;
color: var(--text-primary);
font-weight: 600;
cursor: pointer;
font-size: 12px;
transition: all 0.2s;
}
.secondary-btn:hover {
border-color: #4facfe;
color: #4facfe;
border-color: #4facfe;
color: #4facfe;
}
.secondary-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
border-color: var(--border-color);
color: var(--text-secondary);
opacity: 0.6;
cursor: not-allowed;
border-color: var(--border-color);
color: var(--text-secondary);
}
.video-status {
margin-top: 6px;
font-size: 11px;
color: var(--text-secondary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-top: 6px;
font-size: 11px;
color: var(--text-secondary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.settings-toggle-container {
display: flex;
justify-content: flex-end;
display: flex;
justify-content: flex-end;
}
.icon-btn {
background: none;
border: none;
cursor: pointer;
font-size: 18px;
opacity: 0.7;
transition: opacity 0.2s;
padding: 4px;
background: none;
border: none;
cursor: pointer;
font-size: 18px;
opacity: 0.7;
transition: opacity 0.2s;
padding: 4px;
}
.icon-btn:hover {
opacity: 1;
opacity: 1;
}
.settings-content {
display: none;
background: var(--surface-color);
margin-top: 10px;
padding: 12px;
border-radius: var(--radius-md);
border: 1px solid var(--border-color);
animation: slideDown 0.2s ease-out;
display: none;
background: var(--surface-color);
margin-top: 8px;
padding: 10px;
border-radius: var(--radius-md);
border: 1px solid var(--border-color);
animation: slideDown 0.2s ease-out;
}
@keyframes slideDown {
from { opacity: 0; transform: translateY(-5px); }
to { opacity: 1; transform: translateY(0); }
from {
opacity: 0;
transform: translateY(-5px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.form-group {
margin-bottom: 10px;
margin-bottom: 8px;
}
.form-group label {
display: block;
font-size: 12px;
font-weight: 600;
margin-bottom: 4px;
color: var(--text-secondary);
display: block;
font-size: 12px;
font-weight: 600;
margin-bottom: 4px;
color: var(--text-secondary);
}
.form-group select, .form-group input {
width: 100%;
padding: 8px;
border: 1px solid var(--border-color);
border-radius: 6px;
font-size: 13px;
outline: none;
transition: border-color 0.2s;
.form-group select,
.form-group input {
width: 100%;
padding: 7px;
border: 1px solid var(--border-color);
border-radius: 6px;
font-size: 12px;
outline: none;
transition: border-color 0.2s;
}
.form-group select:focus, .form-group input:focus {
border-color: #4facfe;
.form-group select:focus,
.form-group input:focus {
border-color: #4facfe;
}
.action-buttons {
display: flex;
gap: 8px;
margin-top: 12px;
display: flex;
gap: 8px;
margin-top: 10px;
}
.save-btn, .reset-btn {
flex: 1;
padding: 8px;
border: none;
border-radius: 6px;
font-size: 12px;
font-weight: 600;
cursor: pointer;
transition: opacity 0.2s;
.save-btn,
.reset-btn {
flex: 1;
padding: 7px;
border: none;
border-radius: 6px;
font-size: 11px;
font-weight: 600;
cursor: pointer;
transition: opacity 0.2s;
}
.save-btn {
background: var(--text-primary);
color: white;
background: var(--text-primary);
color: white;
}
.reset-btn {
background: #e2e8f0;
color: var(--text-primary);
background: #e2e8f0;
color: var(--text-primary);
}
.save-btn:hover, .reset-btn:hover {
opacity: 0.9;
.save-btn:hover,
.reset-btn:hover {
opacity: 0.9;
}

@@ -382,29 +407,35 @@

.status-message {
padding: 10px;
border-radius: var(--radius-md);
font-size: 13px;
text-align: center;
font-weight: 500;
animation: fadeIn 0.3s;
display: none; /* Controlled by JS */
padding: 8px;
border-radius: var(--radius-md);
font-size: 12px;
text-align: center;
font-weight: 500;
animation: fadeIn 0.3s;
display: none;
/* Controlled by JS */
}
.status-message.success {
background-color: #d1fae5;
color: #065f46;
background-color: #d1fae5;
color: #065f46;
}
.status-message.error {
background-color: #fee2e2;
color: #991b1b;
background-color: #fee2e2;
color: #991b1b;
}
.status-message.info {
background-color: #eff6ff;
color: #1e40af;
background-color: #eff6ff;
color: #1e40af;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
from {
opacity: 0;
}
to {
opacity: 1;
}
}

@@ -414,13 +445,13 @@

.rtl {
direction: rtl;
direction: rtl;
}
.rtl .popup-title {
flex-direction: row-reverse;
flex-direction: row-reverse;
}
.rtl .primary-action-btn,
.rtl .primary-action-btn,
.rtl .growth-btn,
.rtl .shortcut-info {
flex-direction: row-reverse;
flex-direction: row-reverse;
}
{
"manifest_version": 2,
"name": "YouTube Screenshot Tool",
"version": "1.2.2",
"version": "1.2.3",
"description": "Extract high-quality frames from YouTube videos without UI elements",

@@ -10,2 +10,3 @@ "default_locale": "en",

"storage",
"clipboardWrite",
"*://*.youtube.com/*"

@@ -12,0 +13,0 @@ ],

@@ -25,3 +25,3 @@ <!DOCTYPE html>

<!-- Main Action Section -->
<div class="hero-section">
<div class="hero-section action-buttons-grid">
<button id="capture" class="primary-action-btn">

@@ -31,2 +31,6 @@ <span class="btn-icon">📸</span>

</button>
<button id="copyFrame" class="primary-action-btn copy-btn">
<span class="btn-icon">📋</span>
<span class="btn-text">Copy to Clipboard</span>
</button>
</div>

@@ -57,22 +61,27 @@

<div class="tools-section">
<div class="tools-header">
<span id="toolsLabel">Tools & Settings</span>
<div class="tools-header">
<span id="toolsLabel">Tools & Settings</span>
</div>
<div class="thumbnail-tool-container">
<div class="form-group" style="margin-bottom: 8px;">
<input type="text" id="videoUrlInput" placeholder="Enter YouTube URL or ID...">
</div>
<div class="thumbnail-tool-container">
<button id="openThumbnailTool" class="secondary-btn">
<span class="btn-text">Open Thumbnail Tool</span>
</button>
<div class="video-status">
<span id="videoUrlDisplay">No video detected</span>
</div>
<button id="downloadThumbnail" class="secondary-btn">
<span class="btn-text">Download HD Thumbnail</span>
</button>
<div id="thumbPreview" style="margin-top: 10px; display: none; text-align: center;">
<img id="thumbImg" src=""
style="width: 100%; border-radius: 4px; box-shadow: var(--shadow-sm); cursor: pointer;"
title="Click to view full size">
</div>
</div>
<div class="settings-toggle-container">
<button id="settingsToggle" class="icon-btn" aria-label="Settings">
⚙️
</button>
</div>
<div class="settings-toggle-container">
<button id="settingsToggle" class="icon-btn" aria-label="Settings">
⚙️
</button>
</div>
</div>
<!-- Collapsible Settings Panel -->

@@ -103,2 +112,7 @@ <div class="settings-content" id="settingsContent">

<div class="form-group" style="display: flex; align-items: center; gap: 8px;">
<input type="checkbox" id="showThumbnailPreview" style="width: auto;">
<label for="showThumbnailPreview" id="showPreviewLabel" style="margin-bottom: 0;">Show Thumbnail Preview</label>
</div>
<div class="action-buttons">

@@ -105,0 +119,0 @@ <button id="saveSettings" class="save-btn">Save</button>

@@ -12,3 +12,4 @@ // YouTube Screenshot Tool - Background Script

filename: 'youtube_frame',
language: 'en'
language: 'en',
showThumbnailPreview: false
};

@@ -15,0 +16,0 @@

@@ -8,5 +8,4 @@ /**

// استخدام الواجهة الموحدة لتحقيق التوافق مع Chrome و Firefox
// Unified approach for Firefox and Chrome
const isBrowser = typeof browser !== 'undefined';
const isChrome = typeof chrome !== 'undefined';
const browserAPI = isBrowser ? browser : chrome;

@@ -20,2 +19,9 @@

return true; // Keep the message channel open for async response
} else if (message.action === 'copyFrame') {
copyFrame(message.settings).then(result => {
sendResponse(result);
}).catch(err => {
sendResponse(err.toString());
});
return true;
} else if (message.action === 'ping' || message.action === 'injected_check') {

@@ -48,3 +54,3 @@ // Respond to ping or injected_check message to confirm content script is loaded

const lang = settings.language || 'en';
const video = document.querySelector('video');

@@ -57,3 +63,3 @@ if (!video) {

const canvas = document.createElement('canvas');
// Set dimensions based on quality

@@ -66,3 +72,3 @@ let scaleFactor = 1;

}
canvas.width = video.videoWidth * scaleFactor;

@@ -78,3 +84,3 @@ canvas.height = video.videoHeight * scaleFactor;

let quality = 1;
if (settings.format === 'jpeg') {

@@ -87,3 +93,3 @@ mimeType = 'image/jpeg';

}
// Get video title for filename

@@ -95,3 +101,3 @@ const videoTitle = document.querySelector('h1.title')?.textContent?.trim() || 'youtube_video';

const timeString = `${minutes}_${seconds.toString().padStart(2, '0')}`;
// Create a safe filename

@@ -105,3 +111,3 @@ let filename = settings.filename;

.substring(0, 50); // Limit length
filename = `${filename}_${timeString}.${settings.format}`;

@@ -117,3 +123,3 @@ } else {

link.click();
return true;

@@ -123,2 +129,42 @@ } catch (error) {

}
}
/**
* Captures a frame and copies it to the clipboard
* @param {Object} settings - The capture settings
* @returns {Promise<boolean|string>} - True on success, error message on failure
*/
async function copyFrame(settings) {
try {
const lang = settings.language || 'en';
const video = document.querySelector('video');
if (!video) {
return messages[lang].videoNotFound;
}
const canvas = document.createElement('canvas');
let scaleFactor = 1;
if (settings.quality === 'medium') scaleFactor = 0.75;
else if (settings.quality === 'low') scaleFactor = 0.5;
canvas.width = video.videoWidth * scaleFactor;
canvas.height = video.videoHeight * scaleFactor;
const ctx = canvas.getContext('2d');
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'));
if (!blob) {
throw new Error('Failed to create image blob');
}
const data = [new ClipboardItem({ 'image/png': blob })];
await navigator.clipboard.write(data);
return true;
} catch (error) {
console.error('Copy frame error:', error);
return error.toString();
}
}

@@ -5,3 +5,2 @@ // YouTube Screenshot Tool - Popup Script

const isBrowser = typeof browser !== 'undefined';
const isChrome = typeof chrome !== 'undefined';
const browserAPI = isBrowser ? browser : chrome;

@@ -17,3 +16,4 @@

filename: 'youtube_frame',
language: 'en'
language: 'en',
showThumbnailPreview: false
};

@@ -37,2 +37,4 @@

const lowQualityOption = document.getElementById('lowQualityOption');
const copyFrameBtn = document.getElementById('copyFrame');
const showPreviewToggle = document.getElementById('showThumbnailPreview');

@@ -64,3 +66,6 @@ // Messages cache

toolsLabel: "Tools & Settings",
openThumbnailTool: "Open Thumbnail",
downloadThumbnail: "Download HD Thumbnail",
showPreviewLabel: "Show Thumbnail Preview",
copyBtn: "📋 Copy to Clipboard",
copySuccess: "Frame copied to clipboard!",
shareMessage: "I'm loving this YouTube Screenshot Tool! It helps me extract high-quality frames. Try it: " + STORE_URL,

@@ -86,3 +91,6 @@ copied: "Copied! ✅"

toolsLabel: "الأدوات والإعدادات",
openThumbnailTool: "فتح الصورة المصغرة",
downloadThumbnail: "تحميل الصورة المصغرة",
showPreviewLabel: "تفعيل معاينة الصورة",
copyBtn: "📋 نسخ للحافظة",
copySuccess: "تم نسخ الإطار للحافظة!",
shareMessage: "أداة رائعة لالتقاط الصور من يوتيوب بجودة عالية! جربها: " + STORE_URL,

@@ -104,2 +112,3 @@ copied: "تم النسخ! ✅"

if (filenameInput) filenameInput.value = settings.filename;
if (showPreviewToggle) showPreviewToggle.checked = settings.showThumbnailPreview || false;

@@ -129,6 +138,8 @@ // Set language

async function initThumbnailTool() {
const videoUrlDisplay = document.getElementById('videoUrlDisplay');
const openThumbnailToolBtn = document.getElementById('openThumbnailTool');
const videoUrlInput = document.getElementById('videoUrlInput');
const downloadThumbnailBtn = document.getElementById('downloadThumbnail');
const thumbPreview = document.getElementById('thumbPreview');
const thumbImg = document.getElementById('thumbImg');
if (!videoUrlDisplay || !openThumbnailToolBtn) return;
if (!videoUrlInput || !downloadThumbnailBtn) return;

@@ -138,21 +149,15 @@ try {

if (tabs.length > 0 && tabs[0].url && (tabs[0].url.includes('youtube.com/watch') || tabs[0].url.includes('youtu.be/'))) {
videoUrlDisplay.textContent = tabs[0].url;
openThumbnailToolBtn.disabled = false;
openThumbnailToolBtn.classList.remove('disabled');
} else {
videoUrlDisplay.textContent = "No YouTube video detected";
openThumbnailToolBtn.disabled = true;
openThumbnailToolBtn.classList.add('disabled');
videoUrlInput.value = tabs[0].url;
updateThumbPreview(tabs[0].url);
}
// Remove old listeners to avoid duplicates
const newBtn = openThumbnailToolBtn.cloneNode(true);
openThumbnailToolBtn.parentNode.replaceChild(newBtn, openThumbnailToolBtn);
videoUrlInput.addEventListener('input', () => {
updateThumbPreview(videoUrlInput.value);
});
newBtn.addEventListener('click', () => {
const url = videoUrlDisplay.textContent;
downloadThumbnailBtn.addEventListener('click', () => {
const url = videoUrlInput.value;
const videoId = extractVideoId(url);
if (videoId) {
// Open the maximum resolution thumbnail directly
const thumbUrl = `https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`;

@@ -165,2 +170,31 @@ browserAPI.tabs.create({ url: thumbUrl });

thumbImg.addEventListener('click', () => {
const videoId = extractVideoId(videoUrlInput.value);
if (videoId) {
const thumbUrl = `https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`;
browserAPI.tabs.create({ url: thumbUrl });
}
});
function updateThumbPreview(url) {
const videoId = extractVideoId(url);
const isEnabled = showPreviewToggle ? showPreviewToggle.checked : false;
if (videoId) {
if (isEnabled) {
const thumbUrl = `https://img.youtube.com/vi/${videoId}/mqdefault.jpg`;
thumbImg.src = thumbUrl;
thumbPreview.style.display = 'block';
} else {
thumbPreview.style.display = 'none';
}
downloadThumbnailBtn.disabled = false;
downloadThumbnailBtn.classList.remove('disabled');
} else {
thumbPreview.style.display = 'none';
downloadThumbnailBtn.disabled = true;
downloadThumbnailBtn.classList.add('disabled');
}
}
} catch (error) {

@@ -296,2 +330,57 @@ console.error('Error initializing thumbnail tool:', error);

// Copy button click
if (copyFrameBtn) {
copyFrameBtn.addEventListener('click', async () => {
const settings = await loadSettings();
showStatus('Copying...', 'info');
try {
const tabs = await browserAPI.tabs.query({ active: true, currentWindow: true });
if (tabs[0].url.includes('youtube.com/watch')) {
try {
await sendMessageToTab(tabs[0].id, { action: 'ping' });
await handleCopyMessage(tabs[0].id, settings);
} catch (err) {
console.log('Content script not loaded, injecting:', err);
try {
await injectContentScript(tabs[0].id);
await handleCopyMessage(tabs[0].id, settings);
} catch (injectError) {
showStatus('Error injecting script: ' + injectError.message, 'error');
}
}
} else {
const currentLang = document.querySelector('.language-btn.active').dataset.lang || 'en';
const messages = {
en: 'Please navigate to a YouTube video page first',
ar: 'يرجى الانتقال إلى صفحة فيديو YouTube أولاً'
};
showStatus(messages[currentLang], 'error');
}
} catch (error) {
showStatus('Error: ' + (error.message || 'Unknown error'), 'error');
}
});
}
// Handle copy message
async function handleCopyMessage(tabId, settings) {
try {
const response = await sendMessageToTab(tabId, {
action: 'copyFrame',
settings: settings
});
if (response === true) {
const successMsg = getMessage('copySuccess');
showStatus(successMsg, 'success');
} else {
showStatus('Error: ' + (response || 'Unknown error'), 'error');
}
} catch (error) {
showStatus('Error: ' + (error.message || 'Unknown error'), 'error');
}
}
// Inject content script

@@ -362,3 +451,4 @@ async function injectContentScript(tabId) {

filename: filenameInput.value,
language: document.querySelector('.language-btn.active').dataset.lang
language: document.querySelector('.language-btn.active').dataset.lang,
showThumbnailPreview: showPreviewToggle.checked
};

@@ -380,2 +470,3 @@

filenameInput.value = DEFAULT_SETTINGS.filename;
showPreviewToggle.checked = DEFAULT_SETTINGS.showThumbnailPreview;

@@ -478,3 +569,4 @@ // Update language

'changeShortcut': fallbackMessages[lang].changeShortcut,
'toolsLabel': fallbackMessages[lang].toolsLabel
'toolsLabel': fallbackMessages[lang].toolsLabel,
'showPreviewLabel': fallbackMessages[lang].showPreviewLabel
};

@@ -494,7 +586,10 @@

}
if (copyFrameBtn) {
copyFrameBtn.querySelector('.btn-text').textContent = fallbackMessages[lang].copyBtn.replace("📋 ", "");
}
if (rateBtn) {
rateBtn.querySelector('.btn-text').textContent = fallbackMessages[lang].rateText;
}
if (document.getElementById('openThumbnailTool')) {
document.getElementById('openThumbnailTool').querySelector('.btn-text').textContent = fallbackMessages[lang].openThumbnailTool;
if (document.getElementById('downloadThumbnail')) {
document.getElementById('downloadThumbnail').querySelector('.btn-text').textContent = fallbackMessages[lang].downloadThumbnail;
}

@@ -517,3 +612,4 @@ }

'Settings reset to default': 'تم إعادة الإعدادات للوضع الافتراضي',
'Failed to copy to clipboard': 'فشل نسخ النص'
'Failed to copy to clipboard': 'فشل نسخ النص',
'Copying...': 'جاري النسخ...'
}

@@ -520,0 +616,0 @@ };

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet