diff --git a/index.html b/index.html new file mode 100644 index 0000000..8969530 --- /dev/null +++ b/index.html @@ -0,0 +1,115 @@ + + + + + + New JSTAR Tab + + + + + +
+
+ + + + + + + + +
+
+ + + + + + + +
+
+ Edit +
+
+ Delete +
+
+ + + + \ No newline at end of file diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..c708705 --- /dev/null +++ b/manifest.json @@ -0,0 +1,13 @@ +{ + "manifest_version": 3, + "name": "JSTAR Tab", + "version": "1.0", + "description": "A sleek modern custom new tab with search, shortcuts, and settings.", + "chrome_url_overrides": { + "newtab": "index.html" + }, + "permissions": ["storage"], + "action": { + "default_title": "New JSTAR Tab" + } +} \ No newline at end of file diff --git a/script.js b/script.js new file mode 100644 index 0000000..c5ab65d --- /dev/null +++ b/script.js @@ -0,0 +1,380 @@ +document.addEventListener('DOMContentLoaded', () => { + applyTheme(); + updateGreeting(); + loadAnonymizationState(); // Load anonymization state on page load + loadShortcuts(); // Load shortcuts on page load + + document.getElementById('theme-switch').addEventListener('change', toggleTheme); + document.getElementById('settings-btn').addEventListener('click', toggleSettings); + document.getElementById('search-btn').addEventListener('click', searchBrave); + document.getElementById('search').addEventListener('keypress', function(event) { + if (event.key === 'Enter') { + searchBrave(); + } + }); + + // Add event listener for the anonymize button + const anonymizeBtn = document.getElementById('anonymize-btn'); + anonymizeBtn.addEventListener('click', toggleAnonymize); + + const modal = document.getElementById('settings-modal'); + modal.addEventListener('click', closeOnClickOutside); + document.querySelector('.close-btn').addEventListener('click', closeSettings); + + // Add shortcut button listeners + document.getElementById('add-shortcut').addEventListener('click', addShortcut); + document.getElementById('import-shortcuts').addEventListener('click', importShortcuts); + document.getElementById('export-shortcuts').addEventListener('click', exportShortcuts); + document.getElementById('reset-shortcuts').addEventListener('click', resetShortcuts); + + // Custom context menu for shortcuts + const contextMenu = document.createElement('div'); + contextMenu.id = 'context-menu'; + contextMenu.innerHTML = ` + + + `; + contextMenu.style.display = 'none'; + document.body.appendChild(contextMenu); + + document.addEventListener('click', (event) => { + const contextMenu = document.getElementById('context-menu'); + // Hide context menu if the click is outside of it + if (event.target !== contextMenu && !contextMenu.contains(event.target)) { + contextMenu.style.display = 'none'; // Hide menu on click outside + } + }); + + // Close context menu on Escape key + document.addEventListener('keydown', (event) => { + if (event.key === 'Escape') { + contextMenu.style.display = 'none'; // Hide context menu + } + }); + + // Detect Shift + A to toggle anonymization, but ensure no other keys are pressed + document.addEventListener('keydown', (event) => { + if (event.key === 'A' && event.shiftKey && !event.ctrlKey && !event.altKey && !event.metaKey) { + toggleAnonymize(); + } + }); + + // Detect Shift + T to toggle theme + document.addEventListener('keydown', (event) => { + if (event.key === 'T' && event.shiftKey) { + toggleThemeShortcut(); // Toggle theme when Shift + T is pressed + } + }); + + // Detect Shift + S to toggle settings menu + document.addEventListener('keydown', (event) => { + if (event.key === 'S' && event.shiftKey) { + toggleSettings(); // Open or close settings modal + } + }); +}); + +let isAnonymized = false; // Track the anonymization state +let currentShortcutIndex = -1; // To keep track of the current shortcut for editing or deleting + +function updateGreeting() { + const greeting = document.getElementById('greeting'); + const date = new Date(); + const hours = date.getUTCHours() + 5; // Adjust for timezone + + let message = `Good ${getTimeOfDay(hours)}, ${isAnonymized ? 'JSTAR' : 'Junaid'}!`; + greeting.textContent = message; +} + +function getTimeOfDay(hours) { + if (hours >= 5 && hours < 12) { + return "Morning"; + } else if (hours >= 12 && hours < 17) { + return "Afternoon"; + } else if (hours >= 17 && hours < 22) { + return "Evening"; + } else { + return "Night"; + } +} + +function searchBrave() { + const query = document.getElementById('search').value.trim(); + if (query) { + window.location.href = `https://search.brave.com/search?q=${encodeURIComponent(query)}`; + } +} + +function toggleTheme() { + const isDark = document.getElementById('theme-switch').checked; + document.body.classList.toggle('dark-theme', isDark); + localStorage.setItem('theme', isDark ? 'dark' : 'light'); + + const modalContent = document.querySelector('.modal-content'); + modalContent.style.backgroundColor = isDark ? '#1a1a1a' : 'white'; +} + +// Shortcut to toggle theme +function toggleThemeShortcut() { + const themeSwitch = document.getElementById('theme-switch'); + themeSwitch.checked = !themeSwitch.checked; + toggleTheme(); +} + +function applyTheme() { + const savedTheme = localStorage.getItem('theme') || 'light'; + document.body.classList.toggle('dark-theme', savedTheme === 'dark'); + document.getElementById('theme-switch').checked = savedTheme === 'dark'; + document.querySelector('.modal-content').style.backgroundColor = savedTheme === 'dark' ? '#1a1a1a' : 'white'; +} + +// Load the anonymization state from localStorage +function loadAnonymizationState() { + const savedAnonymization = localStorage.getItem('anonymization'); + isAnonymized = savedAnonymization === 'true'; // Convert string to boolean + + updateAnonymizeButton(); + updateGreeting(); +} + +// Load shortcuts from localStorage and display them +function loadShortcuts() { + const shortcutsContainer = document.getElementById('shortcuts'); + shortcutsContainer.innerHTML = ''; // Clear existing shortcuts + + const savedShortcuts = JSON.parse(localStorage.getItem('shortcuts')) || []; + savedShortcuts.forEach((shortcut, index) => { + const shortcutButton = document.createElement('div'); + shortcutButton.className = 'shortcut'; + + const favicon = document.createElement('img'); + favicon.src = `https://www.google.com/s2/favicons?domain=${new URL(shortcut.url).hostname}`; // Fetch favicon + const shortcutName = document.createElement('span'); + shortcutName.textContent = shortcut.name.length > 10 ? shortcut.name.slice(0, 10) + '...' : shortcut.name; // Truncate long names + + // Add a click event to open the shortcut link + shortcutButton.addEventListener('click', (event) => { + if (event.ctrlKey) { + window.open(shortcut.url, '_blank'); // Open in a new tab if Ctrl is held + } else { + window.location.href = shortcut.url; // Open in the same tab + } + }); + + // Add context menu for right-click + shortcutButton.addEventListener('contextmenu', (event) => { + event.preventDefault(); + showContextMenu(event.clientX, event.clientY, index); + }); + + shortcutButton.appendChild(favicon); + shortcutButton.appendChild(shortcutName); + shortcutsContainer.appendChild(shortcutButton); + }); +} + +// Show custom context menu +function showContextMenu(x, y, index) { + currentShortcutIndex = index; // Store the index of the current shortcut + const contextMenu = document.getElementById('context-menu'); + contextMenu.style.display = 'block'; + contextMenu.style.left = `${x}px`; + contextMenu.style.top = `${y}px`; + + // Clear previous event handlers (important to prevent multiple listeners) + const editButton = contextMenu.querySelector('#edit-shortcut'); + const deleteButton = contextMenu.querySelector('#delete-shortcut'); + + if (editButton) { + editButton.onclick = () => { + editShortcut(); // Trigger edit shortcut + contextMenu.style.display = 'none'; // Hide context menu + }; + } else { + const editBtn = document.createElement('button'); + editBtn.id = 'edit-shortcut'; + editBtn.textContent = 'Edit'; + editBtn.onclick = () => { + editShortcut(); // Trigger edit shortcut + contextMenu.style.display = 'none'; // Hide context menu + }; + contextMenu.appendChild(editBtn); + } + + if (deleteButton) { + deleteButton.onclick = () => { + deleteShortcut(); // Trigger delete shortcut + contextMenu.style.display = 'none'; // Hide context menu + }; + } else { + const deleteBtn = document.createElement('button'); + deleteBtn.id = 'delete-shortcut'; + deleteBtn.textContent = 'Delete'; + deleteBtn.onclick = () => { + deleteShortcut(); // Trigger delete shortcut + contextMenu.style.display = 'none'; // Hide context menu + }; + contextMenu.appendChild(deleteBtn); + } +} + +// Toggle anonymization state +function toggleAnonymize() { + isAnonymized = !isAnonymized; // Toggle the anonymized state + updateAnonymizeButton(); + updateGreeting(); + + // Save the current anonymization state to localStorage + localStorage.setItem('anonymization', isAnonymized); +} + +function updateAnonymizeButton() { + const anonymizeBtn = document.getElementById('anonymize-btn'); + if (isAnonymized) { + anonymizeBtn.classList.add('active'); + anonymizeBtn.setAttribute('data-tooltip', 'Unanonymize'); + anonymizeBtn.innerHTML = ''; // Change icon to un-anonymize + } else { + anonymizeBtn.classList.remove('active'); + anonymizeBtn.setAttribute('data-tooltip', 'Anonymize'); + anonymizeBtn.innerHTML = ''; // Change icon to anonymize + } +} + +function toggleSettings() { + const modal = document.getElementById('settings-modal'); + modal.classList.contains('active') ? closeSettings() : openSettings(); +} + +function openSettings() { + const modal = document.getElementById('settings-modal'); + modal.style.display = 'flex'; + setTimeout(() => { + modal.classList.add('active'); + modal.querySelector('.modal-content').classList.add('active'); + }, 50); +} + +function closeSettings() { + const modal = document.getElementById('settings-modal'); + modal.querySelector('.modal-content').classList.remove('active'); + modal.classList.remove('active'); + + setTimeout(() => { + modal.style.display = 'none'; + }, 300); +} + +function closeOnClickOutside(event) { + const modal = document.getElementById('settings-modal'); + if (event.target === modal) { + closeSettings(); + } +} + +// Validate if the URL is valid +function isValidURL(url) { + try { + new URL(url); + return true; + } catch { + return false; + } +} + +// Add a new shortcut +function addShortcut() { + const name = prompt("Enter the shortcut name:"); + const url = prompt("Enter the shortcut URL:"); + + if (!name || !url || !isValidURL(url)) { + alert("Invalid input. Please provide a valid name and URL."); + return; + } + + const shortcuts = JSON.parse(localStorage.getItem('shortcuts')) || []; + shortcuts.push({ name, url }); + localStorage.setItem('shortcuts', JSON.stringify(shortcuts)); + loadShortcuts(); +} + +// Edit the current shortcut +function editShortcut() { + if (currentShortcutIndex < 0) return; + + const shortcuts = JSON.parse(localStorage.getItem('shortcuts')); + const currentShortcut = shortcuts[currentShortcutIndex]; + + const newName = prompt("Edit the shortcut name:", currentShortcut.name); + const newUrl = prompt("Edit the shortcut URL:", currentShortcut.url); + + if (!newName || !newUrl || !isValidURL(newUrl)) { + alert("Invalid input. Please provide a valid name and URL."); + return; + } + + shortcuts[currentShortcutIndex] = { name: newName, url: newUrl }; + localStorage.setItem('shortcuts', JSON.stringify(shortcuts)); + loadShortcuts(); +} + +// Delete the current shortcut +function deleteShortcut() { + if (currentShortcutIndex < 0) return; + + const shortcuts = JSON.parse(localStorage.getItem('shortcuts')); + shortcuts.splice(currentShortcutIndex, 1); + localStorage.setItem('shortcuts', JSON.stringify(shortcuts)); + loadShortcuts(); +} + +// Import shortcuts from a JSON file +function importShortcuts() { + const input = document.createElement('input'); + input.type = 'file'; + input.accept = '.json'; + + input.onchange = (event) => { + const file = event.target.files[0]; + const reader = new FileReader(); + + reader.onload = (e) => { + try { + const shortcuts = JSON.parse(e.target.result); + if (Array.isArray(shortcuts)) { + localStorage.setItem('shortcuts', JSON.stringify(shortcuts)); + loadShortcuts(); + } else { + alert("Invalid file format. Please upload a valid JSON file."); + } + } catch (error) { + alert("Error parsing JSON. Please ensure the file is valid."); + } + }; + reader.readAsText(file); + }; + + input.click(); +} + +// Export shortcuts to a JSON file +function exportShortcuts() { + const shortcuts = JSON.parse(localStorage.getItem('shortcuts')) || []; + const dataStr = JSON.stringify(shortcuts, null, 2); + const blob = new Blob([dataStr], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = 'shortcuts.json'; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); +} + +// Reset shortcuts to the default state +function resetShortcuts() { + if (confirm("Are you sure you want to reset the shortcuts? This action cannot be undone.")) { + localStorage.removeItem('shortcuts'); + loadShortcuts(); + } +} diff --git a/style.css b/style.css new file mode 100644 index 0000000..28c38db --- /dev/null +++ b/style.css @@ -0,0 +1,399 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Poppins', sans-serif; + background-color: #ffffff; /* Light mode background */ + color: #000000; /* Light mode text color */ + display: flex; + justify-content: center; + align-items: center; + height: 100vh; +} + +#container { + text-align: center; +} + +#greeting { + font-size: 36px; + font-weight: 600; + margin-bottom: 30px; +} + +#search-bar { + display: flex; + justify-content: center; + align-items: center; + margin-bottom: 10px; + background-color: #f0f0f0; /* Light mode input background */ + border-radius: 30px; + padding: 5px; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); +} + +#search-bar input { + font-size: 18px; + padding: 10px; + width: 400px; + border: none; + border-radius: 20px; + outline: none; + background-color: transparent; + color: #000000; /* Light mode input text color */ +} + +#search-bar button { + font-size: 18px; + padding: 10px; + cursor: pointer; + border: none; + background-color: transparent; + color: #000; /* Light mode icon color */ +} + +.grid-container { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 15px; +} + +.shortcut { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 10px; + border-radius: 10px; + background-color: #e0e0e0; /* Light mode button background */ + transition: background-color 0.3s; + cursor: pointer; +} + +.shortcut img { + width: 24px; /* Size of favicon */ + height: 24px; /* Size of favicon */ +} + +.shortcut:hover { + background-color: #ccc; /* Light mode button hover background */ +} + +.settings-icon { + position: fixed; + bottom: 20px; + right: 20px; + background-color: transparent; + border: none; + font-size: 32px; + cursor: pointer; + color: #000000; /* Light mode settings icon color */ + z-index: 10; +} + +.modal { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.8); /* Modal background color */ + justify-content: center; + align-items: center; + z-index: 1000; + opacity: 0; + transition: opacity 0.5s ease; +} + +.modal-content { + background-color: #1A1A1A; /* Dark mode modal background */ + padding: 20px; + border-radius: 10px; + width: 400px; + position: relative; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); + opacity: 0; + transform: translateY(-20px); + transition: opacity 0.3s ease, transform 0.3s ease; +} + +.close-btn { + position: absolute; + right: 20px; + top: 20px; + cursor: pointer; + font-size: 24px; + color: #000000; /* Close button color in dark mode */ +} + +.switch { + position: relative; + display: inline-block; + width: 60px; + height: 34px; +} + +.switch input { + opacity: 0; + width: 0; + height: 0; +} + +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; /* Light mode toggle background */ + transition: 0.4s; + border-radius: 34px; +} + +.slider:before { + position: absolute; + content: ""; + height: 26px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: white; /* Light mode toggle circle color */ + transition: 0.4s; + border-radius: 50%; +} + +input:checked + .slider { + background-color: rgb(100, 100, 100); /* Light mode toggle active color */ +} + +input:checked + .slider:before { + background-color: rgb(133, 133, 133); + transform: translateX(26px); +} + +body.dark-theme { + background-color: #000000; /* Dark mode background */ + color: #ffffff; /* Dark mode text color */ +} + +body.dark-theme #search-bar { + background-color: #111111; /* Dark mode input background */ +} + +body.dark-theme #search-bar input { + color: #ffffff; /* Dark mode input text color */ +} + +body.dark-theme .settings-icon { + color: #ffffff; /* Dark mode settings icon color */ +} + +body.dark-theme .modal-content { + background-color: #1A1A1A; /* Dark mode modal background */ + color: #ffffff; /* Dark mode modal text color */ +} + +body.dark-theme .close-btn { + color: #ffffff; +} + +body.dark-theme #search-bar button { + color: #ffffff; /* Dark mode button text color */ +} + +body.dark-theme #search-bar button:hover { + color: #ffffff; /* Dark mode button hover color */ +} + +.section { + margin: 20px 0; +} + +h2, h3 { + margin-bottom: 10px; + color: #000000; /* Light mode heading color */ +} + +body.dark-theme h2, body.dark-theme h3 { + color: #ffffff; /* Dark mode heading color */ +} + +.button-group { + display: flex; + justify-content: flex-start; + gap: 10px; +} + +.icon-btn { + display: flex; + align-items: center; + justify-content: center; + padding: 15px; + border: none; + background-color: #e0e0e0; /* Light mode button background */ + border-radius: 5px; + color: #000000; /* Light mode button text color */ + cursor: pointer; + position: relative; + transition: background-color 0.3s; + font-size: 18px; +} + +.icon-btn:hover { + background-color: #ccc; /* Light mode button hover background */ +} + +body.dark-theme .icon-btn { + background-color: #333333; /* Dark mode button background */ + color: #ffffff; /* Dark mode button text color */ +} + +body.dark-theme .icon-btn:hover { + background-color: #444444; /* Dark mode button hover background */ +} + +.icon-btn[data-tooltip]::after { + content: attr(data-tooltip); + position: absolute; + bottom: calc(100% + 6px); + left: 50%; + transform: translateX(-50%); + background: #333; + color: white; + padding: 10px 15px; + border-radius: 5px; + white-space: nowrap; + font-size: 14px; + opacity: 0; + transition: opacity 0.3s ease-in; + pointer-events: none; + z-index: 100; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); +} + +.icon-btn[data-tooltip]:hover::after { + opacity: 1; +} + +.modal.active { + display: flex; + opacity: 1; +} + +.modal-content.active { + opacity: 1; + transform: translateY(0); +} + +.modal-content.hide { + opacity: 0; + transform: translateY(-20px); +} + +.modal.hide { + opacity: 0; +} + +/* Additional styles for the shortcut buttons */ +.shortcut { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 10px; + border-radius: 10px; + background-color: #e0e0e0; /* Light mode button background */ + transition: background-color 0.3s; + cursor: pointer; +} + +.shortcut img { + width: 24px; /* Size of favicon */ + height: 24px; /* Size of favicon */ +} + +.shortcut:hover { + background-color: #ccc; /* Light mode button hover background */ +} + +/* Dark mode styles for shortcuts */ +body.dark-theme .shortcut { + background-color: #444; /* Dark mode button background */ +} + +body.dark-theme .shortcut:hover { + background-color: #555; /* Dark mode button hover background */ +} + +/* Custom context menu styles */ +#context-menu { + position: absolute; + background-color: #fff; /* Light mode menu background */ + border-radius: 5px; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4); + z-index: 1000; + padding: 0; /* Remove padding from menu itself */ + display: none; /* Hidden by default */ + width: 150px; /* Set width for the context menu */ +} + +.context-menu-item { + display: flex; + align-items: center; + padding: 10px; /* Increased padding for larger button size */ + color: #000; /* Default text color */ + cursor: pointer; + border-bottom: 1px solid #fff; /* Divider between items */ + transition: background-color 0.3s; + width: 100%; /* Full width buttons */ + text-align: left; /* Align text to the left */ +} + +.context-menu-item i { + margin-right: 8px; /* Space between icon and text */ + font-size: 16px; /* Icon size */ +} + +.context-menu-item:last-child { + border-bottom: none; /* No divider after last item */ + border-bottom-right-radius: 5px; + border-bottom-left-radius: 5px; +} + +.context-menu-item:hover { + background-color: #f0f0f0; /* Menu item hover background */ +} + +.context-menu-item.delete { + color: #000; /* Set delete button text color to red */ +} + +.context-menu-item.delete:hover { + color: red; /* Set delete button text color to red */ +} + +body.dark-theme #context-menu { + background-color: #222; /* Dark mode menu background */ +} + +body.dark-theme .context-menu-item { + color: #ffffff; /* Dark mode menu text color */ + border-bottom: 1px solid #222222; +} + +body.dark-theme .context-menu-item.delete { + color: #fff; /* Dark mode delete text color */ +} + +body.dark-theme .context-menu-item.delete:hover { + color: red; /* Dark mode delete text color */ +} + +body.dark-theme .context-menu-item:hover { + background-color: #333; /* Dark mode item hover background */ +} \ No newline at end of file