├── .github └── workflows │ └── main.yml ├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── icon.png ├── image ├── demo.gif ├── linked.png ├── marketplace.png └── screenshot.png ├── package.json ├── public ├── highlightLinked.ts ├── index.html └── index.ts ├── tsconfig.json ├── wmr.config.mjs └── yarn.lock /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Release 4 | 5 | env: 6 | PLUGIN_NAME: logseq-plugin-link-unlinked 7 | 8 | # Controls when the workflow will run 9 | on: 10 | push: 11 | tags: 12 | - "*" # Push events to matching any tag format, i.e. 1.0, 20.15.10 13 | 14 | # Allows you to run this workflow manually from the Actions tab 15 | workflow_dispatch: 16 | 17 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 18 | jobs: 19 | release: 20 | # The type of runner that the job will run on 21 | runs-on: ubuntu-latest 22 | 23 | # Steps represent a sequence of tasks that will be executed as part of the job 24 | steps: 25 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 26 | - uses: actions/checkout@v2 27 | - uses: actions/setup-node@v2 28 | with: 29 | node-version: "14.x" 30 | 31 | - name: Build 32 | id: build 33 | run: | 34 | yarn 35 | yarn build 36 | mkdir ${{ env.PLUGIN_NAME }} 37 | cp README.md package.json icon.png ${{ env.PLUGIN_NAME }} 38 | mv dist ${{ env.PLUGIN_NAME }} 39 | zip -r ${{ env.PLUGIN_NAME }}.zip ${{ env.PLUGIN_NAME }} 40 | ls 41 | echo "::set-output name=tag_name::$(git tag --sort version:refname | tail -n 1)" 42 | 43 | - name: Create Release 44 | id: create_release 45 | uses: actions/create-release@v1 46 | env: 47 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 48 | VERSION: ${{ github.ref }} 49 | with: 50 | tag_name: ${{ github.ref }} 51 | release_name: ${{ github.ref }} 52 | draft: false 53 | prerelease: false 54 | 55 | - name: Upload zip file 56 | id: upload_zip 57 | uses: actions/upload-release-asset@v1 58 | env: 59 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 60 | with: 61 | upload_url: ${{ steps.create_release.outputs.upload_url }} 62 | asset_path: ./${{ env.PLUGIN_NAME }}.zip 63 | asset_name: ${{ env.PLUGIN_NAME }}-${{ steps.build.outputs.tag_name }}.zip 64 | asset_content_type: application/zip 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .cache 2 | node_modules 3 | dist 4 | stats.html 5 | yarn-error.log 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "endOfLine": "lf", 4 | "semi": false, 5 | "singleQuote": false, 6 | "trailingComma": "all" 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Seth Yuan 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Link Unlinked References 2 | 3 | A plugin for logseq to highlight the keyword in unlinked reference, and provide a button to automatically link them. 4 | 5 | For the 'link-all' button, you may need to click it several times to complete the "real" link-all action, as it only simulates this by clicking all visible link buttons. 6 | 7 | You can also highlight the refs and tags in linked references (**need to enable manually in the settings**). 8 | 9 | #### Screenshot 10 | ![](image/screenshot.png) 11 | 12 | Enable highlight in linked references. 13 | 14 | drawing 15 | 16 | #### Demo 17 | ![Demo](image/demo.gif) 18 | 19 | #### Marketplace 20 | drawing 21 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usoonees/logseq-plugin-link-unlink/205a6464ae3f38dea5d3595d6bce276fea9bb093/icon.png -------------------------------------------------------------------------------- /image/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usoonees/logseq-plugin-link-unlink/205a6464ae3f38dea5d3595d6bce276fea9bb093/image/demo.gif -------------------------------------------------------------------------------- /image/linked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usoonees/logseq-plugin-link-unlink/205a6464ae3f38dea5d3595d6bce276fea9bb093/image/linked.png -------------------------------------------------------------------------------- /image/marketplace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usoonees/logseq-plugin-link-unlink/205a6464ae3f38dea5d3595d6bce276fea9bb093/image/marketplace.png -------------------------------------------------------------------------------- /image/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usoonees/logseq-plugin-link-unlink/205a6464ae3f38dea5d3595d6bce276fea9bb093/image/screenshot.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "logseq-plugin-link-unlinked", 3 | "version": "1.5.1", 4 | "main": "dist/index.html", 5 | "logseq": { 6 | "title": "Link Unlinked References", 7 | "id": "_usoon-logseq-link-unlinked", 8 | "icon": "./icon.png" 9 | }, 10 | "scripts": { 11 | "start": "wmr", 12 | "build": "wmr build", 13 | "serve": "wmr serve" 14 | }, 15 | "dependencies": { 16 | "@logseq/libs": "^0.0.10" 17 | }, 18 | "devDependencies": { 19 | "wmr": "^3.7.2" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /public/highlightLinked.ts: -------------------------------------------------------------------------------- 1 | const selectRef = (name) => `.page-linked .page-ref[data-ref^="${name}"]` 2 | const selectDarkRef = (name) => 3 | `.dark .page-linked .page-ref[data-ref^="${name}"]` 4 | 5 | function unHighlightRef(preNames) { 6 | const unsetSelector = preNames.map(selectRef).join(",") 7 | const unsetDarkSelector = preNames.map(selectDarkRef).join(",") 8 | logseq.provideStyle(` 9 | ${unsetSelector} { 10 | background-color: unset; 11 | } 12 | 13 | ${unsetDarkSelector} { 14 | background-color: unset; 15 | } 16 | `) 17 | } 18 | 19 | function highlightLinkRef(preNames, curNames) { 20 | unHighlightRef(preNames) 21 | const setSelector = curNames.map(selectRef).join(",") 22 | const setDarkSelector = curNames.map(selectDarkRef).join(",") 23 | logseq.provideStyle(` 24 | ${setSelector} { 25 | background-color: ${logseq.settings.highlightColor}; 26 | } 27 | 28 | ${setDarkSelector} { 29 | background-color: ${logseq.settings.highlightColorDarkMode}; 30 | } 31 | `) 32 | } 33 | 34 | const selectTag = (name) => `.page-linked a.tag[data-ref^="${name}"]` 35 | const selectDarkTag = (name) => `.dark .page-linked a.tag[data-ref^="${name}"]` 36 | 37 | // TODO: set tag color and background to previous 38 | function unHighlightTag(preNames) { 39 | const unsetTagSelector = preNames.map(selectTag).join(",") 40 | const unsetTagDarkSelector = preNames.map(selectDarkTag).join(",") 41 | logseq.provideStyle(` 42 | ${unsetTagSelector} { 43 | background-color: unset; 44 | color: unset; 45 | } 46 | 47 | ${unsetTagDarkSelector} { 48 | background-color: unset; 49 | color: unset; 50 | }`) 51 | } 52 | 53 | function highlighTagRef(preNames, curNames) { 54 | unHighlightTag(preNames) 55 | const setTagSelector = curNames.map(selectTag).join(",") 56 | const setTagDarkSelector = curNames.map(selectDarkTag).join(",") 57 | logseq.provideStyle(` 58 | ${setTagSelector} { 59 | background-color: yellow; 60 | color: #484576; 61 | } 62 | 63 | ${setTagDarkSelector} { 64 | background-color: #ffff0030; 65 | color: #ebdddd; 66 | } 67 | `) 68 | } 69 | 70 | export {highlighTagRef, unHighlightTag, highlightLinkRef, unHighlightRef} -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | logseq-plugin-paste-more 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /public/index.ts: -------------------------------------------------------------------------------- 1 | import "@logseq/libs" 2 | import {highlighTagRef, unHighlightTag, highlightLinkRef, unHighlightRef} from './highlightLinked' 3 | 4 | const doc = parent.document 5 | const highlightClass = "unlink-highlight" 6 | 7 | async function getPageNames() { 8 | const page = await logseq.Editor.getCurrentPage() 9 | if(!page){ 10 | return 11 | } 12 | let pageNames = [page.name] 13 | if (page.alias) { 14 | for(const alias of page.alias){ 15 | const aliasPage = await logseq.Editor.getPage(alias.id) 16 | pageNames.push(aliasPage.name) 17 | } 18 | } 19 | 20 | // return lowercase page names, and sort by page length 21 | return pageNames.sort((a, b) => b.length - a.length).map((name) => name.toLowerCase()) 22 | } 23 | 24 | let prePageNames = [] 25 | async function highlightLinked() { 26 | const curPageNames = await getPageNames() 27 | if(!curPageNames){ 28 | return 29 | } 30 | // references 31 | if (logseq.settings.highlightLinkedRefs) { 32 | highlightLinkRef(prePageNames, curPageNames) 33 | } else { 34 | unHighlightRef(prePageNames) 35 | } 36 | 37 | // tags 38 | // if (logseq.settings.highlightLinkedTags) { 39 | // highlighTagRef(prePageNames, curPageNames) 40 | // } else { 41 | // unHighlightTag(prePageNames) 42 | // } 43 | 44 | prePageNames = curPageNames 45 | } 46 | 47 | 48 | logseq.useSettingsSchema([ 49 | { 50 | key: "highlightColor", 51 | title: "Highlight Color", 52 | type: "string", 53 | default: "yellow", // default to false 54 | description: "", 55 | }, 56 | { 57 | key: "highlightColorDarkMode", 58 | title: "Highlight Color in Dark Mode", 59 | type: "string", 60 | default: "#ffff0030", 61 | description: "", 62 | }, 63 | { 64 | key: "highlightLinkedRefs", 65 | title: "Whether to highlight references in linked references", 66 | type: "boolean", 67 | default: false, // default to false 68 | description: "", 69 | }, 70 | // { 71 | // key: "highlightLinkedTags", 72 | // title: "Whether to highlight tag in linked references", 73 | // type: "boolean", 74 | // default: false, // default to false 75 | // description: "", 76 | // }, 77 | ]) 78 | 79 | function provideStyle() { 80 | logseq.provideStyle(` 81 | button.link-button, button.link-all-button { 82 | float: right; 83 | padding-left: 5px; 84 | padding-right: 5px; 85 | border: 1px solid var(--ls-border-color); 86 | border-radius: 4px; 87 | opacity: 0.6; 88 | transition: .3s; 89 | } 90 | button.link-button:hover, button.link-all-button:hover { 91 | opacity: 1; 92 | } 93 | button.link-button::before, button.link-all-button::before { 94 | content: "\\eade"; 95 | width: 16px; 96 | height: 16px; 97 | margin-right: 4px; 98 | display: inline-block; 99 | line-height: 1em; 100 | font-family: tabler-icons; 101 | } 102 | button.link-all-button { 103 | margin-left: auto; 104 | } 105 | span.${highlightClass} { 106 | background-color: ${logseq.settings.highlightColor}; 107 | } 108 | .dark span.${highlightClass} { 109 | background-color: ${logseq.settings.highlightColorDarkMode}; 110 | } 111 | `) 112 | } 113 | 114 | async function main() { 115 | provideStyle() 116 | 117 | logseq.onSettingsChanged(async () => { 118 | provideStyle() 119 | await highlightLinked() 120 | }) 121 | 122 | logseq.App.onRouteChanged(() => { 123 | setTimeout(async () => { 124 | if(logseq.settings.highlightLinkedRefs){ 125 | await highlightLinked() 126 | } 127 | }, 100) 128 | }) 129 | 130 | 131 | let unlinkObserver, unlinkedRefsContainer 132 | 133 | const unlinkCallback = function (mutationsList, observer) { 134 | for (let i = 0; i < mutationsList.length; i++) { 135 | const addedNode = mutationsList[i].addedNodes[0] 136 | if (addedNode && addedNode.childNodes.length) { 137 | const blocks = addedNode.querySelectorAll(".block-content") 138 | if (blocks.length) { 139 | unlinkObserver.disconnect() 140 | for (let i = 0; i < blocks.length; i++) { 141 | addHighlight(blocks[i]) 142 | } 143 | unlinkObserver.observe(unlinkedRefsContainer, obConfig) 144 | } 145 | } 146 | } 147 | } 148 | 149 | const obConfig = { 150 | childList: true, 151 | subtree: true, 152 | } 153 | unlinkObserver = new MutationObserver(unlinkCallback) 154 | 155 | function addObserverIfDesiredNodeAvailable() { 156 | unlinkedRefsContainer = doc.querySelector(".page-unlinked.references") 157 | if (!unlinkedRefsContainer) { 158 | setTimeout(addObserverIfDesiredNodeAvailable, 200) 159 | return 160 | } 161 | unlinkObserver.observe(unlinkedRefsContainer, obConfig) 162 | } 163 | addObserverIfDesiredNodeAvailable() 164 | 165 | logseq.App.onRouteChanged(() => { 166 | setTimeout(() => { 167 | addObserverIfDesiredNodeAvailable() 168 | }, 50) // wait for page load, otherwise would observer the previous page 169 | }) 170 | 171 | logseq.beforeunload(async () => { 172 | unlinkObserver.disconnect() 173 | }) 174 | } 175 | 176 | 177 | function addLinkAllButton() { 178 | let linkAllButton = doc.querySelector(".link-all-button"); 179 | if (linkAllButton) { 180 | linkAllButton.style.display = "inline-block" 181 | return 182 | } 183 | // linkAllButton.addEventListener("click", linkAll); 184 | let unLinkTitle = doc.querySelector(".references.page-unlinked > .content > .flex > .content") 185 | let extended = unLinkTitle.nextElementSibling 186 | 187 | linkAllButton = doc.createElement("button"); 188 | linkAllButton.setAttribute("class", "link-all-button"); 189 | linkAllButton.innerHTML = "Link All"; 190 | if(extended.className == "hidden"){ 191 | linkAllButton.style.display = "none" 192 | } 193 | linkAllButton.addEventListener("click", (e) => { 194 | e.stopImmediatePropagation(); 195 | const allUnlinkedButtons = doc.querySelectorAll(".link-button"); 196 | allUnlinkedButtons.forEach((button) => { 197 | button.click(); 198 | }); 199 | }); 200 | // 🌟 Create a span element 201 | let wrapperSpan = doc.createElement("span"); 202 | 203 | // 🌟 Append the button to the span 204 | wrapperSpan.appendChild(linkAllButton); 205 | 206 | if (unLinkTitle) { 207 | unLinkTitle.insertBefore(wrapperSpan, unLinkTitle.firstChild); 208 | unLinkTitle.addEventListener("click", (e) => { 209 | setTimeout(() => { 210 | if(extended.className == "hidden"){ 211 | linkAllButton.style.display = "none" 212 | return 213 | } 214 | if(!doc.querySelector(".link-button")) { 215 | console.log("no link button") 216 | return 217 | } 218 | 219 | linkAllButton.style.display = "inline-block" 220 | }, 100); 221 | 222 | }); 223 | } 224 | } 225 | 226 | const contentSelector = ".inline, .is-paragraph, h1, h2, h3, h4, h5, h6" 227 | function addButton(blockEl, pageNames) { 228 | addLinkAllButton() 229 | let linkButton = blockEl.querySelector(".link-button") 230 | if (linkButton) { 231 | return 232 | } 233 | const blockID = blockEl.getAttribute("blockid") 234 | linkButton = doc.createElement("button") 235 | linkButton.setAttribute("class", "link-button") 236 | linkButton.innerHTML = "link" 237 | linkButton.addEventListener("click", async (e) => { 238 | const block = await logseq.Editor.getBlock(blockID) 239 | const content = block.content 240 | const reStr = "(" + pageNames.join("|") + ")" 241 | const re = new RegExp(reStr, "ig") 242 | 243 | /* 244 | page = cu 245 | 'cu #focus #f/ocus #cu cus focus [[cu]] [[focus]]' 246 | '[[cu]] #focus #f/ocus #cu [[cu]]s fo[[cu]]s [[cu]] [[focus]]' 247 | */ 248 | const newContent = content.replace(re, (match, _, i) => { 249 | while (i >= 0) { 250 | if (/\s/.test(content[i]) || content[i] === "]") { 251 | break 252 | } else if (content[i] == "[" || content[i] == "#") { 253 | return match 254 | } 255 | i -= 1 256 | } 257 | 258 | return `[[${match}]]` 259 | }) 260 | console.log("unlinked content: ", content, pageNames) 261 | console.log("linked content: ", newContent) 262 | 263 | // sometimes header and paragraph would cause block render error 264 | // so we need to step into edit mode first, and exit after 100ms 265 | await logseq.Editor.editBlock(blockID) 266 | await logseq.Editor.updateBlock(blockID, newContent) 267 | 268 | setTimeout(() => { 269 | logseq.Editor.exitEditingMode() 270 | }, 100); 271 | 272 | let highlights = blockEl.querySelectorAll(`.${highlightClass}`) 273 | for (let i = 0; i < highlights.length; i++) { 274 | // highlights[i].style.display = 'none' 275 | highlights[i].remove() 276 | } 277 | linkButton.style.display = "none" 278 | }) 279 | 280 | blockEl.querySelector(contentSelector).appendChild(linkButton) 281 | } 282 | 283 | async function addHighlight(blockEl) { 284 | const contentElements = blockEl.querySelectorAll(contentSelector) 285 | const pageNames = await getPageNames() 286 | const reStr = "(" + pageNames.join("|") + ")" 287 | const re = new RegExp(reStr, "ig") 288 | 289 | contentElements.forEach((content) => { 290 | let child = content.firstChild 291 | let pureText = [] // pure text in first level of inline 292 | let formatText = [] // bold, italic, mark text in second level of inline 293 | 294 | while (child) { 295 | if (child.nodeType == 3) { 296 | pureText.push(child) 297 | } 298 | 299 | if (["B", "I", "EM", "STRONG", "MARK", "DEL"].includes(child.tagName)) { 300 | formatText.push({ 301 | parent: child, 302 | children: [], 303 | }) 304 | } 305 | 306 | child = child.nextSibling 307 | } 308 | 309 | for (let i = 0; i < formatText.length; i++) { 310 | child = formatText[i]["parent"].firstChild 311 | while (child) { 312 | if (child.nodeType == 3) { 313 | formatText[i]["children"].push(child) 314 | } 315 | child = child.nextSibling 316 | } 317 | } 318 | 319 | function newHighlightNode(child) { 320 | const text = child.textContent 321 | if (re.test(text)) { 322 | // const isPureText = content.tagName == "SPAN" 323 | addButton(blockEl, pageNames) 324 | } 325 | let domText = text.replace( 326 | re, 327 | `$1`, 328 | ) 329 | let newDom = document.createElement("span") 330 | newDom.innerHTML = domText 331 | return newDom 332 | } 333 | 334 | pureText.forEach((child) => { 335 | content.replaceChild(newHighlightNode(child), child) 336 | }) 337 | 338 | formatText.forEach((boldNode) => { 339 | boldNode["children"].forEach((c) => { 340 | boldNode["parent"].replaceChild(newHighlightNode(c), c) 341 | }) 342 | }) 343 | }) 344 | } 345 | 346 | logseq.ready(main).catch(console.error) 347 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "jsx": "preserve", 5 | "jsxFactory": "preact.h", 6 | "jsxFragmentFactory": "preact.Fragment", 7 | "allowJs": true, 8 | "checkJs": true, 9 | // "strict": true, 10 | "noEmit": true, 11 | "moduleResolution": "node", 12 | "target": "ESNext", 13 | "module": "esnext", 14 | "resolveJsonModule": true, 15 | "allowSyntheticDefaultImports": true, 16 | "downlevelIteration": true 17 | }, 18 | "include": ["node_modules/wmr/types.d.ts", "**/*"], 19 | "typeAcquisition": { 20 | "enable": true 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /wmr.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "wmr" 2 | 3 | // Full list of options: https://wmr.dev/docs/configuration 4 | export default defineConfig({ 5 | /* Your configuration here */ 6 | alias: { 7 | react: "preact/compat", 8 | "react-dom": "preact/compat", 9 | }, 10 | publicPath: "", 11 | }) 12 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@logseq/libs@^0.0.10": 6 | version "0.0.10" 7 | resolved "https://registry.npmmirror.com/@logseq/libs/-/libs-0.0.10.tgz#305d65b733ed16d157f57a1a0759e0233d1b1a81" 8 | integrity sha512-Ah5wAhLcgrlOtk/9nEe11UsqOzvpcEaEWDV+oFD3TIGTeP4mfv2HjDiPYPBGXuMZRgvsW6klQLzzaxMieksXXg== 9 | dependencies: 10 | csstype "3.1.0" 11 | debug "4.3.4" 12 | dompurify "2.3.8" 13 | eventemitter3 "4.0.7" 14 | fast-deep-equal "3.1.3" 15 | lodash-es "4.17.21" 16 | path "0.12.7" 17 | snake-case "3.0.4" 18 | 19 | csstype@3.1.0: 20 | version "3.1.0" 21 | resolved "https://registry.npmmirror.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2" 22 | integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA== 23 | 24 | debug@4.3.4: 25 | version "4.3.4" 26 | resolved "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" 27 | integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== 28 | dependencies: 29 | ms "2.1.2" 30 | 31 | dompurify@2.3.8: 32 | version "2.3.8" 33 | resolved "https://registry.npmmirror.com/dompurify/-/dompurify-2.3.8.tgz#224fe9ae57d7ebd9a1ae1ac18c1c1ca3f532226f" 34 | integrity sha512-eVhaWoVibIzqdGYjwsBWodIQIaXFSB+cKDf4cfxLMsK0xiud6SE+/WCVx/Xw/UwQsa4cS3T2eITcdtmTg2UKcw== 35 | 36 | dot-case@^3.0.4: 37 | version "3.0.4" 38 | resolved "https://registry.npmmirror.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" 39 | integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== 40 | dependencies: 41 | no-case "^3.0.4" 42 | tslib "^2.0.3" 43 | 44 | eventemitter3@4.0.7: 45 | version "4.0.7" 46 | resolved "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" 47 | integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== 48 | 49 | fast-deep-equal@3.1.3: 50 | version "3.1.3" 51 | resolved "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" 52 | integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== 53 | 54 | inherits@2.0.3: 55 | version "2.0.3" 56 | resolved "https://registry.npmmirror.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 57 | integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== 58 | 59 | lodash-es@4.17.21: 60 | version "4.17.21" 61 | resolved "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" 62 | integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== 63 | 64 | lower-case@^2.0.2: 65 | version "2.0.2" 66 | resolved "https://registry.npmmirror.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" 67 | integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== 68 | dependencies: 69 | tslib "^2.0.3" 70 | 71 | ms@2.1.2: 72 | version "2.1.2" 73 | resolved "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 74 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 75 | 76 | no-case@^3.0.4: 77 | version "3.0.4" 78 | resolved "https://registry.npmmirror.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" 79 | integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== 80 | dependencies: 81 | lower-case "^2.0.2" 82 | tslib "^2.0.3" 83 | 84 | path@0.12.7: 85 | version "0.12.7" 86 | resolved "https://registry.npmmirror.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f" 87 | integrity sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q== 88 | dependencies: 89 | process "^0.11.1" 90 | util "^0.10.3" 91 | 92 | process@^0.11.1: 93 | version "0.11.10" 94 | resolved "https://registry.npmmirror.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" 95 | integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== 96 | 97 | snake-case@3.0.4: 98 | version "3.0.4" 99 | resolved "https://registry.npmmirror.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" 100 | integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg== 101 | dependencies: 102 | dot-case "^3.0.4" 103 | tslib "^2.0.3" 104 | 105 | tslib@^2.0.3: 106 | version "2.3.1" 107 | resolved "https://registry.npmmirror.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" 108 | integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== 109 | 110 | util@^0.10.3: 111 | version "0.10.4" 112 | resolved "https://registry.npmmirror.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" 113 | integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== 114 | dependencies: 115 | inherits "2.0.3" 116 | 117 | wmr@^3.7.2: 118 | version "3.7.2" 119 | resolved "https://registry.npmmirror.com/wmr/-/wmr-3.7.2.tgz#3d6dfb771fafc0e2119d4502aaea26ecd5963c19" 120 | integrity sha512-i+e/T8J067qP2pBgq6E2gaW9qMGK+D9eGue85YM0Vflc2UuzXBP3fSS5PzvyHEKuPHZWmcFBmZRs6DU9Lkr9og== 121 | --------------------------------------------------------------------------------