Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
ignoredBuiltDependencies:
- '@swc/core'
- esbuild
allowBuilds:
'@swc/core': true
esbuild: true
14 changes: 7 additions & 7 deletions public/background/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ chrome.runtime.onInstalled.addListener((details) => {
chrome.storage.sync.set({
enabled: true,
autoHide: true,
hideDelay: 5000,
hideDelay: 5,
});
} else if (details.reason === "update") {
console.log("SuperBook extension updated");
Expand All @@ -22,8 +22,8 @@ chrome.runtime.onInstalled.addListener((details) => {
// Handle extension icon click
chrome.action.onClicked.addListener((tab) => {
// Toggle extension on/off for current tab
chrome.storage.sync.get(["enabled"], (result) => {
const newState = !result.enabled;
chrome.storage.sync.get(["enabled"], (result) => {
const newState = !(result && result.enabled);
chrome.storage.sync.set({ enabled: newState });

// Update icon to reflect state
Expand Down Expand Up @@ -79,9 +79,9 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === "getSettings") {
chrome.storage.sync.get(["enabled", "autoHide", "hideDelay"], (result) => {
sendResponse({
enabled: result.enabled !== false, // Default to true
autoHide: result.autoHide !== false, // Default to true
hideDelay: result.hideDelay || 5000, // Default to 5 seconds
enabled: result ? result.enabled !== false : true,
autoHide: result ? result.autoHide !== false : true,
hideDelay: result && result.hideDelay ? result.hideDelay : 5,
});
});
return true; // Keep message channel open for async response
Expand All @@ -90,5 +90,5 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {

// Initialize icon state on startup
chrome.storage.sync.get(["enabled"], (result) => {
updateIcon(result.enabled !== false);
updateIcon(result ? result.enabled !== false : true);
});
290 changes: 288 additions & 2 deletions public/content/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ let tooltipEl = null;
let hideHoverTimeout = null;
let isInteracting = false;
let enabled = true;
let autoHide = true;
let hideDelay = 5;
let hideTooltipTimeout = null;
let lastSelectionRect = null;
let isInitialized = false;

Expand All @@ -34,8 +37,10 @@ function initializeSuperBook() {

try {
chrome.runtime.sendMessage({ action: "getSettings" }, (res) => {
if (res && typeof res.enabled !== "undefined") {
enabled = !!res.enabled;
if (res) {
if (typeof res.enabled !== "undefined") enabled = !!res.enabled;
if (typeof res.autoHide !== "undefined") autoHide = !!res.autoHide;
if (typeof res.hideDelay !== "undefined") hideDelay = res.hideDelay;
}
});
} catch (_) {}
Expand All @@ -49,6 +54,23 @@ function initializeSuperBook() {
removeTooltip();
}
}
if (message && message.action === "showSettings") {
showSettingsPanel();
}
});
} catch (_) {}

try {
chrome.storage.onChanged.addListener((changes, area) => {
if (area === "sync") {
if (changes.enabled) enabled = !!changes.enabled.newValue;
if (changes.autoHide) autoHide = !!changes.autoHide.newValue;
if (changes.hideDelay) hideDelay = changes.hideDelay.newValue;
if (!enabled) {
hideHoverButton();
removeTooltip();
}
}
});
} catch (_) {}
}
Expand Down Expand Up @@ -163,6 +185,16 @@ function createHoverButton() {
return btn;
}

//Auto hide function
function startAutoHide() {
clearTimeout(hideTooltipTimeout);
if (autoHide && hideDelay > 0) {
hideTooltipTimeout = setTimeout(function() {
removeTooltip();
}, hideDelay * 1000);
}
}

function showHoverButton(position, word) {
if (!hoverButtonEl) {
hoverButtonEl = createHoverButton();
Expand All @@ -187,6 +219,7 @@ function hideHoverButton() {
}

function removeTooltip() {
clearTimeout(hideTooltipTimeout);
if (tooltipEl && tooltipEl.parentNode) {
tooltipEl.parentNode.removeChild(tooltipEl);
}
Expand Down Expand Up @@ -282,6 +315,7 @@ async function showTooltip(word, position) {

content.innerHTML = parts.join("");
tooltipEl.classList.add("show");
startAutoHide();
} catch (err) {
clearTimeout(timeout);
console.error(err);
Expand Down Expand Up @@ -324,6 +358,7 @@ async function showTooltip(word, position) {
}

tooltipEl.classList.add("show");
startAutoHide();
}
};

Expand All @@ -343,6 +378,257 @@ async function showTooltip(word, position) {
document.addEventListener("click", onDocClick, true);
}

// Settings overlay panel
let settingsPanelEl = null;

function createSettingsPanel() {
if (settingsPanelEl) {
settingsPanelEl.style.display = 'block';
return settingsPanelEl;
}

const panel = document.createElement('div');
panel.className = 'superbook-settings-overlay';
panel.innerHTML = `
<div class="superbook-settings-panel">
<div class="superbook-settings-header">
<span class="superbook-settings-title">SuperBook Settings</span>
<button class="superbook-settings-close">&times;</button>
</div>
<div class="superbook-settings-body">
<div class="superbook-settings-section">
<h3>General</h3>
<div class="superbook-settings-row">
<div>
<label>Enable Extension</label>
<span class="superbook-settings-desc">Turn SuperBook on or off</span>
</div>
<label class="superbook-toggle">
<input type="checkbox" id="sb-settings-enabled">
<span class="superbook-toggle-slider"></span>
</label>
</div>
</div>
<div class="superbook-settings-section">
<h3>Tooltip</h3>
<div class="superbook-settings-row">
<div>
<label>Auto-Hide Tooltip</label>
<span class="superbook-settings-desc">Automatically hide after a few seconds</span>
</div>
<label class="superbook-toggle">
<input type="checkbox" id="sb-settings-autohide">
<span class="superbook-toggle-slider"></span>
</label>
</div>
<div class="superbook-settings-row">
<div>
<label>Hide Delay</label>
<span class="superbook-settings-desc">How long before the tooltip hides</span>
</div>
<input type="number" id="sb-settings-delay" min="1" max="15" value="5">
</div>
</div>
<button id="sb-settings-save">Save Settings</button>
</div>
</div>
`;

panel.querySelector('.superbook-settings-close').addEventListener('click', hideSettingsPanel);
panel.addEventListener('click', (e) => {
if (e.target === panel) hideSettingsPanel();
});
panel.querySelector('#sb-settings-save').addEventListener('click', saveSettingsPanel);

document.documentElement.appendChild(panel);
settingsPanelEl = panel;

// Inject styles
if (!document.getElementById('superbook-settings-styles')) {
const style = document.createElement('style');
style.id = 'superbook-settings-styles';
style.textContent = `
.superbook-settings-overlay {
position: fixed;
top: 0;
right: 0;
width: 300px;
height: 400px;
z-index: 2147483647;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
.superbook-settings-panel {
background: #1a1a1a;
border: 1px solid #333;
border-radius: 0 0 0 12px;
box-shadow: -4px 4px 24px rgba(0,0,0,0.5);
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
.superbook-settings-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
border-bottom: 1px solid #2a2a2a;
}
.superbook-settings-title {
font-size: 14px;
font-weight: 600;
color: #fff;
}
.superbook-settings-close {
background: none;
border: none;
color: #6b7280;
font-size: 20px;
cursor: pointer;
padding: 0 4px;
line-height: 1;
}
.superbook-settings-close:hover { color: #fff; }
.superbook-settings-body {
flex: 1;
padding: 16px;
overflow-y: auto;
}
.superbook-settings-section { margin-bottom: 16px; }
.superbook-settings-section h3 {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
color: #9ca3af;
margin: 0 0 12px 0;
}
.superbook-settings-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 0;
border-bottom: 1px solid #2a2a2a;
gap: 12px;
}
.superbook-settings-row:last-child { border-bottom: none; padding-bottom: 0; }
.superbook-settings-row:first-child { padding-top: 0; }
.superbook-settings-row label {
font-size: 13px;
font-weight: 500;
color: #e5e5e5;
display: block;
}
.superbook-settings-desc {
font-size: 11px;
color: #6b7280;
display: block;
margin-top: 2px;
}
.superbook-settings-row input[type="number"] {
width: 60px;
padding: 4px 8px;
background: #2a2a2a;
color: #fff;
border: 1px solid #3a3a3a;
border-radius: 6px;
font-size: 13px;
text-align: center;
outline: none;
flex-shrink: 0;
}
.superbook-settings-row input[type="number"]:focus { border-color: #4ade80; }
.superbook-toggle {
position: relative;
width: 36px;
height: 20px;
flex-shrink: 0;
}
.superbook-toggle input { opacity: 0; width: 0; height: 0; }
.superbook-toggle-slider {
position: absolute;
cursor: pointer;
inset: 0;
background: #374151;
border-radius: 20px;
transition: 0.2s;
}
.superbook-toggle-slider::before {
content: "";
position: absolute;
width: 14px;
height: 14px;
left: 3px;
bottom: 3px;
background: #fff;
border-radius: 50%;
transition: 0.2s;
}
.superbook-toggle input:checked + .superbook-toggle-slider { background: #4ade80; }
.superbook-toggle input:checked + .superbook-toggle-slider::before { transform: translateX(16px); }
#sb-settings-save {
width: 100%;
background: #4ade80;
color: #000;
border: none;
padding: 8px;
border-radius: 8px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: 0.2s;
margin-top: 8px;
}
#sb-settings-save:hover { background: #22c55e; }
`;
document.head.appendChild(style);
}

return panel;
}

function loadSettingsPanel() {
try {
chrome.storage.sync.get(['enabled', 'autoHide', 'hideDelay'], (res) => {
const enabled = res && res.enabled !== false;
const autoHide = res && res.autoHide !== false;
const hideDelay = res && res.hideDelay ? res.hideDelay : 5;
const enabledEl = document.getElementById('sb-settings-enabled');
const autoHideEl = document.getElementById('sb-settings-autohide');
const delayEl = document.getElementById('sb-settings-delay');
if (enabledEl) enabledEl.checked = enabled;
if (autoHideEl) autoHideEl.checked = autoHide;
if (delayEl) delayEl.value = hideDelay;
});
} catch (_) {}
}

function saveSettingsPanel() {
const enabled = document.getElementById('sb-settings-enabled')?.checked ?? true;
const autoHide = document.getElementById('sb-settings-autohide')?.checked ?? true;
const hideDelay = parseInt(document.getElementById('sb-settings-delay')?.value || '5', 10);
try {
chrome.storage.sync.set({ enabled, autoHide, hideDelay }, () => {
const btn = document.getElementById('sb-settings-save');
if (btn) {
const orig = btn.textContent;
btn.textContent = 'Saved!';
setTimeout(() => { btn.textContent = orig; }, 1500);
}
});
} catch (_) {}
}

function showSettingsPanel() {
const panel = createSettingsPanel();
panel.style.display = 'block';
loadSettingsPanel();
}

function hideSettingsPanel() {
if (settingsPanelEl) settingsPanelEl.style.display = 'none';
}

function escapeHtml(str) {
return String(str)
.replace(/&/g, "&amp;")
Expand Down
Loading