├── src ├── style.css ├── vite-env.d.ts └── main.ts ├── images ├── icon16.png ├── icon32.png ├── icon48.png ├── icon128.png ├── screenshot1.png ├── screenshot2.png ├── screenshot-copied.png └── screenshot-pasted.png ├── CHANGELOG.md ├── scripts ├── resize_screenshot ├── generate_icons └── generate_icon ├── vite.config.ts ├── .github └── workflows │ ├── release.yml │ └── tag.yml ├── .gitignore ├── index.html ├── package.json ├── tsconfig.json ├── docs └── chrome_web_store_description.txt ├── manifest.json ├── README.md ├── LICENSE └── favicon.svg /src/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: sans-serif; 3 | } 4 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /images/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r7kamura/copy-rich-link/HEAD/images/icon16.png -------------------------------------------------------------------------------- /images/icon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r7kamura/copy-rich-link/HEAD/images/icon32.png -------------------------------------------------------------------------------- /images/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r7kamura/copy-rich-link/HEAD/images/icon48.png -------------------------------------------------------------------------------- /images/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r7kamura/copy-rich-link/HEAD/images/icon128.png -------------------------------------------------------------------------------- /images/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r7kamura/copy-rich-link/HEAD/images/screenshot1.png -------------------------------------------------------------------------------- /images/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r7kamura/copy-rich-link/HEAD/images/screenshot2.png -------------------------------------------------------------------------------- /images/screenshot-copied.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r7kamura/copy-rich-link/HEAD/images/screenshot-copied.png -------------------------------------------------------------------------------- /images/screenshot-pasted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r7kamura/copy-rich-link/HEAD/images/screenshot-pasted.png -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Unreleased 4 | 5 | ## 0.1.0 6 | 7 | ### Added 8 | 9 | - Initial release. 10 | -------------------------------------------------------------------------------- /scripts/resize_screenshot: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | # Chrome web store requires screenshots to be 1280x800 size. 4 | mogrify -resize '1280x800!' images/screenshot*.png 5 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { crx } from "@crxjs/vite-plugin"; 2 | import manifest from "./manifest.json"; 3 | 4 | export default { 5 | plugins: [crx({ manifest })], 6 | }; 7 | -------------------------------------------------------------------------------- /scripts/generate_icons: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | set -euo pipefail 3 | 4 | widths=(16 32 48 128) 5 | for width in "${widths[@]}" 6 | do 7 | ./scripts/generate_icon "${width}" 8 | done 9 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*.*.*" 7 | workflow_dispatch: 8 | 9 | jobs: 10 | release: 11 | uses: r7kamura/workflows/.github/workflows/github-release.yml@main 12 | secrets: 13 | github-token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} 14 | -------------------------------------------------------------------------------- /.github/workflows/tag.yml: -------------------------------------------------------------------------------- 1 | name: tag 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - CHANGELOG.md 9 | workflow_dispatch: 10 | 11 | jobs: 12 | tag: 13 | uses: r7kamura/workflows/.github/workflows/changelog-tag.yml@main 14 | secrets: 15 | github-token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} 16 | -------------------------------------------------------------------------------- /scripts/generate_icon: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | set -euo pipefail 3 | 4 | width=$1 5 | height=${width} 6 | 7 | mkdir -p images 8 | convert \ 9 | -gravity center \ 10 | -font Helvetica \ 11 | -pointsize "$((width / 4 * 3))" \ 12 | -background "#36393F" \ 13 | -fill "#FFFFFF" \ 14 | -size "${width}x${height}" \ 15 | label:c \ 16 | "images/icon${width}.png" 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | /dist.zip 27 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | copy-rich-link popup 8 | 9 | 10 | Copied 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "engines": { 4 | "node": "18" 5 | }, 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview", 10 | "lint": "prettier --write ." 11 | }, 12 | "devDependencies": { 13 | "@crxjs/vite-plugin": "^1.0.3", 14 | "@types/chrome": "^0.0.184", 15 | "prettier": "^2.6.2", 16 | "typescript": "^4.5.4", 17 | "vite": "^2.9.7" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ESNext", "DOM"], 7 | "moduleResolution": "Node", 8 | "strict": true, 9 | "sourceMap": true, 10 | "resolveJsonModule": true, 11 | "isolatedModules": true, 12 | "esModuleInterop": true, 13 | "noEmit": true, 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": true, 16 | "noImplicitReturns": true, 17 | "skipLibCheck": true 18 | }, 19 | "include": ["src"] 20 | } 21 | -------------------------------------------------------------------------------- /docs/chrome_web_store_description.txt: -------------------------------------------------------------------------------- 1 | Useful for pasting links to Slack, Google Docs, etc. 2 | 3 | ## Usage 4 | 5 | Click the extension icon or execute the keyboard shortcut to copy the link to the page as rich text. 6 | 7 | ## Keyboard shortcut 8 | 9 | The default keyboard shortcut is Ctrl + Shift + L, and Command + Shift + L on Mac. 10 | This can be changed from the browser's extension settings. 11 | 12 | ## Plain text fallback 13 | 14 | As a fallback, we also copy in plain text format in the form `"{title}" {url}`, 15 | so that if you paste where rich text is not available or paste explicitly as plain text (Ctrl + Shift + V on Windows), this plain text format will be pasted. 16 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import "./style.css"; 2 | 3 | function copy({ html, plain }: { html: string; plain: string }) { 4 | const listener = (event: any) => { 5 | event.clipboardData.setData("text/html", html); 6 | event.clipboardData.setData("text/plain", plain); 7 | event.preventDefault(); 8 | }; 9 | document.addEventListener("copy", listener); 10 | document.execCommand("copy"); 11 | document.removeEventListener("copy", listener); 12 | } 13 | 14 | chrome.tabs.query( 15 | { 16 | active: true, 17 | currentWindow: true, 18 | }, 19 | (tabs) => { 20 | const { title, url } = tabs[0]; 21 | copy({ 22 | html: `${title}`, 23 | plain: `"${title}" ${url}`, 24 | }); 25 | } 26 | ); 27 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Copy Rich Link", 3 | "description": "Copy the page title and URL as rich text.", 4 | "version": "0.1.0", 5 | "manifest_version": 3, 6 | "action": { 7 | "default_popup": "index.html", 8 | "default_icon": { 9 | "16": "images/icon16.png", 10 | "32": "images/icon32.png", 11 | "48": "images/icon48.png", 12 | "128": "images/icon128.png" 13 | } 14 | }, 15 | "icons": { 16 | "16": "images/icon16.png", 17 | "32": "images/icon32.png", 18 | "48": "images/icon48.png", 19 | "128": "images/icon128.png" 20 | }, 21 | "commands": { 22 | "_execute_action": { 23 | "suggested_key": { 24 | "default": "Ctrl+Shift+L" 25 | } 26 | } 27 | }, 28 | "permissions": ["activeTab", "clipboardWrite"] 29 | } 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Copy Rich Link 2 | 3 | Browser extension to copy the page title and URL as rich text. 4 | 5 | Useful for pasting links to Slack, Google Docs, etc. 6 | 7 | ![](images/screenshot-copied.png) 8 | 9 | ![](images/screenshot-pasted.png) 10 | 11 | ## Usage 12 | 13 | ### Install 14 | 15 | Install from [Chrome Web Store](https://chrome.google.com/webstore/detail/copy-rich-link/hikiamlgpdcabppakpmemaofmkgknpea). 16 | 17 | ### Copy 18 | 19 | Click the extension icon or execute the keyboard shortcut to copy the link to the page as rich text. 20 | 21 | ### Keyboard shortcut 22 | 23 | The default keyboard shortcut is Ctrl + Shift + L, and Command + Shift + L on Mac. 24 | This can be changed from the browser's extension settings. 25 | 26 | ### Plain text fallback 27 | 28 | As a fallback, we also copy in plain text format in the form `"{title}" {url}`, 29 | so that if you paste where rich text is not available or paste explicitly as plain text (Ctrl + Shift + V on Windows), this plain text format will be pasted. 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Ryo Nakamura 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | --------------------------------------------------------------------------------