feat: context menu

This commit is contained in:
2024-10-06 03:03:10 +03:00
parent 4095f02823
commit b5725bf2c5
4 changed files with 63 additions and 39 deletions

View File

@@ -4,7 +4,7 @@
"version": "0.0.1",
"type": "module",
"description": "Easy shortcuts for copying tab URL with or without title, with customizable format.",
"packageManager": "pnpm@9.9.0",
"packageManager": "pnpm@9.12.0",
"private": true,
"scripts": {
"dev": "npm run clear && cross-env NODE_ENV=development run-p 'dev:*'",
@@ -22,6 +22,7 @@
"pack:xpi": "cross-env WEB_EXT_ARTIFACTS_DIR=./ web-ext build --source-dir ./extension --filename extension.xpi --overwrite-dest",
"start:chromium": "web-ext run --source-dir ./extension --target=chromium",
"start:firefox": "web-ext run --source-dir ./extension --target=firefox-desktop",
"start:zed": "web-ext run --source-dir ./extension --target=firefox-desktop --firefox='/Applications/Zen Browser.app/Contents/MacOS/zen'",
"clear": "rimraf extension/dist extension/manifest.json 'extension.*'",
"lint": "eslint --ignore-path .gitignore './**/*.{ts,tsx,js,jsx}'"
},

View File

@@ -11,31 +11,51 @@ if (import.meta.hot) {
import('./contentScriptHMR')
}
browser.runtime.onInstalled.addListener((): void => {
logger.log('Extension installed')
})
logger.log('Hello from background script')
async function getCurrentTab() {
return browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => tabs[0])
function init() {
browser.runtime.onInstalled.addListener((): void => {
logger.log('Extension installed')
})
// Commands (keyboard shortcuts)
browser.commands.onCommand.addListener(async (cmd) => {
const tab = await getCurrentTab()
logger.debug('command received', cmd, tab)
const resp = getResponse(cmd, tab)
if (!resp || !tab) return
logger.debug('sending message', resp, 'to tab', tab.id)
return sendMessage(resp.cmd, resp.payload!, {
context: 'content-script',
tabId: tab.id!,
})
})
// Context menu
browser.contextMenus.create({
id: 'copy-tab-url',
title: 'Copy Tab URL',
contexts: ['all'],
})
browser.contextMenus.create({
id: 'copy-tab-markdown',
title: 'Copy Tab Markdown',
contexts: ['all'],
})
browser.contextMenus.onClicked.addListener(async (info) => {
const tab = await getCurrentTab()
const resp = getResponse(info.menuItemId as string, tab)
if (!resp || !tab) return
return sendMessage(resp.cmd, resp.payload!, { context: 'content-script', tabId: tab.id! })
})
// Popup listeners
onMessage('copy-tab-url', responder('copy-tab-url'))
onMessage('copy-tab-markdown', responder('copy-tab-markdown'))
}
browser.commands.onCommand.addListener(async (cmd) => {
const tab = await getCurrentTab()
logger.debug('command received', cmd, tab)
const resp = getResponse(cmd, tab)
if (!resp || !tab) return
logger.debug('sending message', resp, 'to tab', tab.id)
return sendMessage(resp.cmd, resp.payload!, {
context: 'content-script',
tabId: tab.id!,
})
})
onMessage('copy-tab-url', responder('copy-tab-url'))
onMessage('copy-tab-markdown', responder('copy-tab-markdown'))
init()
function responder(cmd: string) {
return async (payload: BridgeMessage<string>) => {
@@ -54,13 +74,17 @@ function getResponse(msg: string, tab: Tabs.Tab) {
switch (msg) {
case 'copy-tab-url':
// copy tab url to clipboard
return { cmd: 'copy-text', payload: tab?.url }
return { cmd: msg, payload: tab?.url }
case 'copy-tab-markdown':
// copy tab url and title in markdown format
return {
cmd: 'copy-text',
cmd: msg,
payload: `[${tab?.title}](${tab?.url})`,
}
}
return null
}
async function getCurrentTab() {
return browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => tabs[0])
}

View File

@@ -30,7 +30,15 @@ export async function getManifest() {
48: './assets/icon-512.png',
128: './assets/icon-512.png',
},
permissions: ['tabs', 'storage', 'activeTab', 'clipboardWrite', 'http://*/', 'https://*/'],
permissions: [
'tabs',
'storage',
'activeTab',
'clipboardWrite',
'contextMenus',
'http://*/',
'https://*/',
],
content_scripts: [
{
matches: ['http://*/*', 'https://*/*'],

View File

@@ -1,13 +1,13 @@
html,
body {
overflow: hidden;
}
.App {
width: 300px;
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
@@ -29,15 +29,6 @@
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
button {
font-size: calc(10px + 2vmin);
}