diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..95a4cfd --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,31 @@ +--- +name: Bug report +about: Create a bug report +title: '' +labels: "\U0001F41B bug" +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - Plugin Version [e.g. 1.2.0] + - Obsidian Version [e.g. 0.8.12] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..5b0de39 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: "✨ feature" +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/premerge.yml b/.github/workflows/premerge.yml new file mode 100644 index 0000000..9eb083c --- /dev/null +++ b/.github/workflows/premerge.yml @@ -0,0 +1,15 @@ +name: "premerge" +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout source + uses: actions/checkout@v2 + + - name: Install NPM dependencies + run: npm install + + - name: Build + run: npm run build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..64911f1 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,57 @@ +name: "Create Release" +on: + push: + tags: + - "*" + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout source + uses: actions/checkout@v2 + + - name: Install NPM dependencies + run: npm install + + - name: Build + run: NODE_ENV=production npm run build + + - name: Create release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + + - name: Upload release assets (1/3) + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./dist/main.js + asset_name: main.js + asset_content_type: application/javascript + + - name: Upload release assets (2/3) + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./dist/manifest.json + asset_name: manifest.json + asset_content_type: application/json + + - name: Upload release assets (3/3) + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./dist/styles.css + asset_name: styles.css + asset_content_type: text/css diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5be20b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# Intellij +*.iml +.idea + +# npm +node_modules/ +package-lock.json + +# build +main.js +*.js.map + +# obsidian +data.json + + +dist/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..3162355 --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +# Hover External Link Obsidian Plugin + +An [Obsidian](https://obsidian.md/) plugin that adds hover URL previews to external links in your rendered Markdown. + +## Installation + +### From in Obsidian + +🚧 Coming Soon! 🚧 + +### Manually + +1. Download `main.js`, `manifest.json`, and `styles.css` from the [latest release](https://github.com/jamiebrynes7/obsidian-hover-external-link/releases/latest). +2. Place them in `/.obsidian/plugins/hover-external-link/` folder (you'll need to create this). + +## Usage + +This plugin will add on-hover tooltips to all external links _if_ the text of the link is not the same as the URL. This only works in the preview mode. + +```md +This link will have a hover tooltip: [Google](www.google.com). + +This link won't: [www.google.com](www.google.com). +``` + +## Customization + +This plugin exposes two CSS variables that you can use to set the tooltip background and text color. + +```css +.theme-light { + --link-tooltip-background: #333; + --link-tooltip-text: #fff; +} + +.theme-dark { + --link-tooltip-background: #ccc; + --link-tooltip-text: #000; +} +``` \ No newline at end of file diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..1df567c --- /dev/null +++ b/manifest.json @@ -0,0 +1,10 @@ +{ + "id": "hover-external-link", + "name": "Hover External Link Plugin", + "version": "1.0.0", + "minAppVersion": "0.10.2", + "description": "Hover on external links to see the destination URL.", + "author": "Jamie Brynes", + "authorUrl": "https://obsidian.md/about", + "isDesktopOnly": true +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..69b4c30 --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "obsidian-sample-plugin", + "version": "0.12.0", + "description": "This is a sample plugin for Obsidian (https://obsidian.md)", + "main": "main.js", + "scripts": { + "dev": "rollup --config rollup.config.js -w", + "build": "rollup --config rollup.config.js --environment BUILD:production" + }, + "keywords": [], + "author": "", + "license": "MIT", + "devDependencies": { + "@rollup/plugin-commonjs": "^18.0.0", + "@rollup/plugin-node-resolve": "^11.2.1", + "@rollup/plugin-typescript": "^8.2.1", + "@types/node": "^14.14.37", + "obsidian": "^0.12.0", + "postcss": "^8.3.6", + "rollup": "^2.32.1", + "rollup-plugin-copy": "^3.4.0", + "rollup-plugin-postcss": "^4.0.1", + "tslib": "^2.2.0", + "typescript": "^4.2.4" + }, + "dependencies": { + "tippy.js": "^6.3.1" + } +} diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..50942f0 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,35 @@ +import typescript from "@rollup/plugin-typescript"; +import { nodeResolve } from "@rollup/plugin-node-resolve"; +import commonjs from "@rollup/plugin-commonjs"; +import copy from "rollup-plugin-copy"; + +const isProd = process.env.NODE_ENV === "production"; + +export default { + input: "src/main.ts", + output: { + dir: "./dist", + sourcemap: "inline", + sourcemapExcludeSources: isProd, + format: "cjs", + exports: "default", + }, + external: ["obsidian"], + plugins: [ + typescript(), + nodeResolve({ browser: true }), + commonjs(), + copy({ + targets: [ + { + src: "manifest.json", + dest: "dist/", + }, + { + src: "styles.css", + dest: "dist/", + }, + ], + }), + ], +}; diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..80ea286 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,23 @@ +import { Plugin } from "obsidian"; +import tippy from "tippy.js"; + +export default class HoverLinkPlugin extends Plugin { + async onload() { + this.registerMarkdownPostProcessor((element) => { + // We only want to add tooltips to: + // 1. external links + // 2. links which don't already show the href + const targetLinks = Array.from(element.getElementsByTagName("a")).filter( + (link) => + link.classList.contains("external-link") && + link.href !== link.innerHTML + ); + + for (const link of targetLinks) { + tippy(link, { + content: link.href, + }); + } + }); + } +} diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..e09a1c2 --- /dev/null +++ b/styles.css @@ -0,0 +1,105 @@ +/* + * Originally copied from dist/tippy.css in the tippy.js package. + * + * Modifications were made to implement compatibility with + * Obsidian themes. + */ + +.theme-light { + --link-tooltip-background: #333; + --link-tooltip-text: #fff; +} + +.theme-dark { + --link-tooltip-background: #ccc; + --link-tooltip-text: #000; +} + +.tippy-box[data-animation="fade"][data-state="hidden"] { + opacity: 0; +} + +[data-tippy-root] { + max-width: calc(100vw - 10px); +} + +.tippy-box { + position: relative; + background-color: var(--link-tooltip-background); + color: var(--link-tooltip-text); + border-radius: 4px; + font-size: 14px; + line-height: 1.4; + outline: 0; + transition-property: transform, visibility, opacity; +} + +.tippy-box[data-placement^="top"] > .tippy-arrow { + bottom: 0; +} + +.tippy-box[data-placement^="top"] > .tippy-arrow:before { + bottom: -7px; + left: 0; + border-width: 8px 8px 0; + border-top-color: initial; + transform-origin: center top; +} + +.tippy-box[data-placement^="bottom"] > .tippy-arrow { + top: 0; +} + +.tippy-box[data-placement^="bottom"] > .tippy-arrow:before { + top: -7px; + left: 0; + border-width: 0 8px 8px; + border-bottom-color: initial; + transform-origin: center bottom; +} + +.tippy-box[data-placement^="left"] > .tippy-arrow { + right: 0; +} + +.tippy-box[data-placement^="left"] > .tippy-arrow:before { + border-width: 8px 0 8px 8px; + border-left-color: initial; + right: -7px; + transform-origin: center left; +} + +.tippy-box[data-placement^="right"] > .tippy-arrow { + left: 0; +} + +.tippy-box[data-placement^="right"] > .tippy-arrow:before { + left: -7px; + border-width: 8px 8px 8px 0; + border-right-color: initial; + transform-origin: center right; +} + +.tippy-box[data-inertia][data-state="visible"] { + transition-timing-function: cubic-bezier(0.54, 1.5, 0.38, 1.11); +} + +.tippy-arrow { + width: 16px; + height: 16px; + color: var(--link-tooltip-background); +} + +.tippy-arrow:before { + content: ""; + position: absolute; + border-color: transparent; + border-style: solid; +} + +.tippy-content { + position: relative; + padding: 10px 15px; + z-index: 1; + overflow-wrap: anywhere; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..604351d --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "inlineSourceMap": true, + "inlineSources": true, + "module": "ESNext", + "target": "es6", + "allowJs": true, + "noImplicitAny": true, + "moduleResolution": "node", + "importHelpers": true, + "lib": [ + "dom", + "es5", + "scripthost", + "es2015" + ] + }, + "include": [ + "**/*.ts" + ] +}