diff --git a/CSS/style.css b/CSS/style.css index aefdea7..8f77418 100644 --- a/CSS/style.css +++ b/CSS/style.css @@ -20,3 +20,24 @@ webview { #tabwebview { display: block !important; } + +#contextMenu { + color: white; + display: none; + background-color: #000000; + border: 1px solid #ccc; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); + padding: 8px 0; + width: 150px; + position: absolute; + z-index: 999999999999999; +} + +.context-menu-item { + padding: 8px 16px; + cursor: pointer; +} + +.context-menu-item:hover { + background-color: #242424; +} \ No newline at end of file diff --git a/JS/contextmenu.js b/JS/contextmenu.js new file mode 100644 index 0000000..1c63f31 --- /dev/null +++ b/JS/contextmenu.js @@ -0,0 +1,55 @@ + +const findLink = (el) => { + if (el.href) return el.href; + else if (el.parentElement && el.parentElement !== document.body) return findLink(el.parentElement); + else return null; +} + + +function showContextMenu(e) { + document.querySelectorAll('.context-menu').forEach(el => el.remove()); + + const contextMenuActions = { + 'open in new tab': (url) => window.tabAPI.newTab(url), + 'test': () => console.log('test') + } + + // create the main context menu container + const contextMenu = document.createElement('div'); + contextMenu.classList.add('context-menu'); + contextMenu.id = 'contextMenu'; + + // loop through menu options and create each item + const l = findLink(e.target); + + for (const key in contextMenuActions) { + if (!l && key === 'open in new tab') continue; + + const menuItem = document.createElement('div'); + menuItem.classList.add('context-menu-item'); + menuItem.textContent = key; + + menuItem.onclick = (_) => { + if (l && key === 'open in new tab') contextMenuActions[key](l); + else contextMenuActions[key](e.target); + } + contextMenu.appendChild(menuItem); + } + + // window.safeHTML.write('body', contextMenu.outerHTML); + document.body.appendChild(contextMenu); + + // position the menu at the cursor position + contextMenu.style.display = 'block'; + contextMenu.style.left = `${e.pageX}px`; + contextMenu.style.top = `${e.pageY}px`; + + // event listener to hide the context menu on click + document.addEventListener('click', (e) => { + if (!contextMenu.contains(e.target)) contextMenu.remove(); + }); +} + + +document.removeEventListener('contextmenu', showContextMenu); +document.addEventListener('contextmenu', showContextMenu); \ No newline at end of file diff --git a/JS/preload.cjs b/JS/preload.cjs index a65ff77..3da2009 100644 --- a/JS/preload.cjs +++ b/JS/preload.cjs @@ -89,7 +89,8 @@ ipcRenderer.on('tab-created', (ev, id, url = 'https://duckduckgo.com') => create contextBridge.exposeInMainWorld('tabAPI', { ping: () => console.info('pong'), - addTab: (url) => ipcRenderer.send('add-tab', url || 'about:blank') + addTab: (url) => ipcRenderer.send('add-tab', url || 'about:blank'), + newTab: (url) => ipcRenderer.send('add-tab-external', url) }); const load = async () => { diff --git a/addon/addonmanager.js b/addon/addonmanager.js index b9ffe34..9dae993 100644 --- a/addon/addonmanager.js +++ b/addon/addonmanager.js @@ -3,7 +3,8 @@ import { findPath } from "../utils/paths.js"; import fs from 'fs'; -const youtube = fs.readFileSync(await findPath('youtubeutils.js')); +const youtube = fs.readFileSync(await findPath('youtubeutils.js')), + contextmenu = fs.readFileSync(await findPath('contextmenu.js')); /** * @param {Electron.WebContents} contents @@ -19,15 +20,22 @@ const youtubeinject = (contents) => { } +/** + * @param {Electron.WebContents} contents + */ +const contextMenuInject = (contents) => contents.executeJavaScript(contextmenu).catch(console.error); + + /** * @param {Electron.WebContents} contents */ export default async function addonManager(contents) { try { - const hostname = await contents.executeJavaScript('window.location.hostname'); + contextMenuInject(contents); + const hostname = await contents.executeJavaScript('window.location.hostname'); if (hostname === 'www.youtube.com') return youtubeinject(contents); - + return {}; } catch (err) { diff --git a/plugins/bluesky.js b/plugins/bluesky.js deleted file mode 100644 index 3047352..0000000 --- a/plugins/bluesky.js +++ /dev/null @@ -1,17 +0,0 @@ -import { Agent, CredentialSession } from '@atproto/api'; -import { CronJob } from 'cron'; -import json from '../secrets/config.json' with { type: 'json' }; -const { uname, upass } = json.bluesky; - -// Create a Bluesky Agent -const session = new CredentialSession(new URL('https://bsky.social')); - -const agent = new Agent(session); - -async function main() { - await session.login({ identifier: uname, password: upass }); - const { data: accountData } = await agent.getProfile({ actor: session.did }); - console.log(accountData); -} - -main(); \ No newline at end of file diff --git a/serverJS/shortcuts.js b/serverJS/shortcuts.js index e54f5f4..a793415 100644 --- a/serverJS/shortcuts.js +++ b/serverJS/shortcuts.js @@ -1,6 +1,6 @@ import { session, globalShortcut } from "electron"; import { changeZoom } from "../JS/display.js"; -import { getCurrentTab, getCurrentWindow } from "./tabs_server.js"; +import { closeTab, getCurrentTab, getCurrentWindow } from "./tabs_server.js"; import { logger } from "./imports.js"; @@ -23,7 +23,11 @@ export default async function setUpShortcuts(uid) { globalShortcut.register('Control+Plus', () => changeZoom(getCurrentTab(), false, true)); - globalShortcut.register('Control+T', () => window.webContents.executeJavaScript('window.tabAPI.addTab()')) + globalShortcut.register('Control+T', () => getCurrentTab()?.webContents.executeJavaScript('window.tabAPI.newTab()')); + globalShortcut.register('Control+W', (e) => { + console.log(getCurrentTab()); + closeTab(e, getCurrentTab()?.id) + }); // window.webContents.on('did-navigate', async (_, url, code, stat) => { // if (isValidURL(url)?.hostname === 'lite.duckduckgo.com') return; diff --git a/serverJS/tabs_server.js b/serverJS/tabs_server.js index 871ef15..54660b5 100644 --- a/serverJS/tabs_server.js +++ b/serverJS/tabs_server.js @@ -12,7 +12,7 @@ const webViewContentsMap = {}; // Memory storage for active tabs /** * returns the focused window, if there is no focused window, returns the first one spawned */ -const getCurrentWindow = () => { +const getCurrentWindow = () => { const allWins = BaseWindow.getAllWindows(), w = allWins.find((win) => win.isFocused()); if (!w && allWins.length > 0) return allWins?.at(0); @@ -26,11 +26,15 @@ const getCurrentTab = () => { return cw?.currentView; } -const settabqual = (tabId) => getCurrentTab().webContents.executeJavaScript('setQuality()').catch(err => logger.warn(`setting quality for window ${tabId} failed with reason:\`\`\`${err}\`\`\``)); +const settabqual = (tabId) => { + const t = (tabId) ? getCurrentWindow()?.contentView.children.find(v => v.id === tabId) : getCurrentTab(); + const r = t.webContents.executeJavaScript('setQuality()').catch(err => logger.warn(`setting quality for window ${tabId} failed with reason:\`\`\`${err}\`\`\``)); + return r; +} /** - * Switch to the specified view by its ID. + * Switch to the specified view by its ID * @param {string | Electron.WebContentsView} tabId */ async function switchToView(tabId) { @@ -73,11 +77,11 @@ async function switchToView(tabId) { // Set the new view as active and add it to the window // viewData.webContents.setBackgroundThrottling(true); currentWindow.contentView.addChildView(viewData); - await viewData.webContents.loadURL('https://start.duckduckgo.com'); + // await viewData.webContents.loadURL('https://start.duckduckgo.com'); currentWindow.contentView.children.map(c => c.setVisible((c.id === id) || c.id < 0)); currentWindow.contentView.children.map(c => console.log(c.id, c.webContents.isCurrentlyAudible())); - settabqual(tabId); + settabqual(id); } @@ -96,7 +100,7 @@ async function shiftTabToBK(currentWindow, tabId, oldView, customSession) { // TODO: optimize the page more settabqual(tabId); - + const newView = await createWebview(tabId, currentWindow, customSession); webViewContentsMap[tabId] = newView; @@ -116,6 +120,8 @@ async function shiftTabToBK(currentWindow, tabId, oldView, customSession) { * @param {string} [url] */ async function addTab(event, tabId, customSession, url = 'https://duckduckgo.com', isOpen = false) { + console.log('opening', url, typeof url); + const currentWindow = getCurrentWindow(); const tabPath = getLoadPath(tabId), currentTab = getCurrentTab(); @@ -128,17 +134,19 @@ async function addTab(event, tabId, customSession, url = 'https://duckduckgo.com if (tabPath && fs.existsSync(tabPath)) newView.webContents.loadFile(tabPath); else newView.webContents.loadURL(url); - switchToView(newView); + switchToView(newView, url); } /** * Close a tab and save its state to disk. * @param {string} tabId */ -async function closeTab(event, tabId) { +async function closeTab(_, tabId) { const currentWindow = getCurrentWindow(); const view = webViewContentsMap[tabId]; + console.log(tabId); + if (view && view.id >= 0) { await saveTabState(tabId, view.webContents); currentWindow.contentView.removeChildView(view); @@ -174,4 +182,11 @@ function organizeTabIds() { } -export { closeTab, addTab, openTab, getCurrentWindow, getCurrentTab, organizeTabIds }; +async function addTabExternal(url = 'https://start.duckduckgo.com') { + const tabview = getCurrentWindow()?.contentView.children.find(o => (o.id === -1)); + await tabview?.webContents.executeJavaScript(`sessionStorage.setItem('templock', "${url}")`); + await tabview?.webContents.executeJavaScript("document.querySelector('#addtabbtn')?.click()"); + await tabview?.webContents.executeJavaScript(`sessionStorage.removeItem('templock')`); +} + +export { closeTab, addTab, openTab, getCurrentWindow, getCurrentTab, organizeTabIds, addTabExternal }; diff --git a/utils/ipc.js b/utils/ipc.js index bae75d9..246a107 100644 --- a/utils/ipc.js +++ b/utils/ipc.js @@ -24,6 +24,7 @@ export default function init(customSession) { ipcMain.on('tab-open', (e, id) => tabModule.openTab(e, id, customSession)); ipcMain.on('tab-close', (e, id) => tabModule.closeTab(e, id, customSession)); ipcMain.on('tab-new', (e, id, url) => tabModule.addTab(e, id, customSession, url)); + ipcMain.on('add-tab-external', (e, url) => tabModule.addTabExternal(url, customSession)); ipcMain.on('set-site-perms', (e, sitehostname, id, value) => setSitePerms(e, sitehostname, id, value)); ipcMain.on('set-site-perms-all', (e, sitehostname, id, value) => setSitePerms(e, sitehostname, id, value, true)); diff --git a/utils/misc.js b/utils/misc.js index 7cd3ecd..8ba4ec2 100644 --- a/utils/misc.js +++ b/utils/misc.js @@ -4,7 +4,8 @@ import dns from 'dns'; import path from 'path'; const history = (fs.readFileSync(path.resolve(import.meta.dirname, '../CSS', 'history.css')).toString()), - tabs = (fs.readFileSync(path.resolve(import.meta.dirname, '../CSS', 'tabs.css')).toString()); + tabs = (fs.readFileSync(path.resolve(import.meta.dirname, '../CSS', 'tabs.css')).toString()), + contextmenu = (fs.readFileSync(path.resolve(import.meta.dirname, '../CSS', 'style.css')).toString()); export const isValidURL = (u) => { try { return new URL(u); } @@ -39,6 +40,7 @@ export async function addEl(window, hostname) { window.webContents.insertCSS(history); window.webContents.insertCSS(tabs); + window.webContents.insertCSS(contextmenu); // window.safdocument.addEventListener('')eHTML.addStylesheet(srccontent, `https://ion-local.${window.location.hostname}/${src}`); // window.safeHTML.addStylesheet(history, `https://ion-local.${window.location.hostname}/history.css`);