commit 39eda568e9c8dd9ac6ee17f656fd4f9b5b365a6b Author: Seth Date: Sat Aug 19 10:25:15 2023 +0800 chore: initial commit diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..3d082dd --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,67 @@ +# This is a basic workflow to help you get started with Actions + +name: Release + +env: + PLUGIN_NAME: logseq-plugin-favorite-tree + +# Controls when the workflow will run +on: + push: + tags: + - "*" # Push events to matching any tag format, i.e. 1.0, 20.15.10 + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + release: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: "16.x" + - uses: pnpm/action-setup@v2.2.4 + with: + version: 6.0.2 + + - name: Build + id: build + run: | + pnpm install + pnpm build + mkdir ${{ env.PLUGIN_NAME }} + cp README.md package.json icon.png ${{ env.PLUGIN_NAME }} + mv dist ${{ env.PLUGIN_NAME }} + zip -r ${{ env.PLUGIN_NAME }}.zip ${{ env.PLUGIN_NAME }} + ls + echo "::set-output name=tag_name::$(git tag --sort version:refname | tail -n 1)" + + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ github.ref }} + with: + tag_name: ${{ github.ref }} + release_name: ${{ github.ref }} + draft: false + prerelease: false + + - name: Upload zip file + id: upload_zip + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./${{ env.PLUGIN_NAME }}.zip + asset_name: ${{ env.PLUGIN_NAME }}-${{ steps.build.outputs.tag_name }}.zip + asset_content_type: application/zip diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..670435c --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +*.zip diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..c03d14f --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "arrowParens": "always", + "endOfLine": "lf", + "semi": false, + "singleQuote": false, + "trailingComma": "all" +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b86115f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Seth Yuan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6dcfbf6 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +English | [中文](README.zh.md) + +# logseq-plugin-favorite-tree + +## Feature Highlights + +- Tree structure for "Favorites" and "Recents" based on namespaces and/or page properties. You can affect the order of display by writing a `fixed` property on the page you want to adjust, e.g, `fixed:: 100`. Smaller the number, closer to the top it will be. +- Set combination of filters through page property and have them displayed in the tree structure. Please refer the demo video below. +- Slider to adjust the left sidebar's width. + +## Usage + +https://github.com/sethyuan/logseq-plugin-another-embed/assets/3410293/32b2a19e-19b3-4113-8fee-f2a445d151cc + +https://github.com/sethyuan/logseq-plugin-another-embed/assets/3410293/d586158a-6781-44fd-931b-1eca8c4df780 diff --git a/README.zh.md b/README.zh.md new file mode 100644 index 0000000..ef6fcfd --- /dev/null +++ b/README.zh.md @@ -0,0 +1,15 @@ +[English](README.md) | 中文 + +# logseq-plugin-favorite-tree + +## 功能 + +- 基于 namespace 或者页面属性实现树形“收藏”与“最近使用”。可通过`fixed`页面属性来调整展示顺序,例如 `fixed:: 100`,数值越小位置越靠前。 +- 可在页面上通过属性设置组合过滤器,会在树形收藏上展示。可参见下方演示视频。 +- 通过拖拽来调整左侧边栏宽度。 + +## 使用展示 + +https://github.com/sethyuan/logseq-plugin-another-embed/assets/3410293/32b2a19e-19b3-4113-8fee-f2a445d151cc + +https://github.com/sethyuan/logseq-plugin-another-embed/assets/3410293/d586158a-6781-44fd-931b-1eca8c4df780 diff --git a/index.html b/index.html new file mode 100644 index 0000000..ed906ca --- /dev/null +++ b/index.html @@ -0,0 +1,15 @@ + + + + + + + + Favorite Tree + + + + + + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..f527ec4 --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "logseq-plugin-favorite-tree", + "version": "1.0.0", + "main": "dist/index.html", + "logseq": { + "id": "_sethyuan-logseq-favorite-tree", + "icon": "./icon.png" + }, + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@logseq/libs": "^0.0.15", + "immer": "^9.0.19", + "logseq-l10n": "^0.2.0", + "preact": "^10.14.1", + "rambdax": "^10.0.0", + "reactutils": "^5.13.0" + }, + "devDependencies": { + "@preact/preset-vite": "^2.5.0", + "@types/node": "^18.15.11", + "typescript": "^5.1.6", + "vite": "^4.4.8", + "vite-plugin-logseq": "^1.1.2" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..161490a --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,1043 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + '@logseq/libs': + specifier: ^0.0.15 + version: 0.0.15 + immer: + specifier: ^9.0.19 + version: 9.0.19 + logseq-l10n: + specifier: ^0.2.0 + version: 0.2.0(@logseq/libs@0.0.15) + preact: + specifier: ^10.14.1 + version: 10.17.0 + rambdax: + specifier: ^10.0.0 + version: 10.0.0 + reactutils: + specifier: ^5.13.0 + version: 5.13.0(react@18.2.0) + +devDependencies: + '@preact/preset-vite': + specifier: ^2.5.0 + version: 2.5.0(@babel/core@7.22.10)(preact@10.17.0)(vite@4.4.8) + '@types/node': + specifier: ^18.15.11 + version: 18.15.11 + typescript: + specifier: ^5.1.6 + version: 5.1.6 + vite: + specifier: ^4.4.8 + version: 4.4.8(@types/node@18.15.11) + vite-plugin-logseq: + specifier: ^1.1.2 + version: 1.1.2 + +packages: + + /@ampproject/remapping@2.2.1: + resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.19 + dev: true + + /@babel/code-frame@7.22.10: + resolution: {integrity: sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.22.10 + chalk: 2.4.2 + dev: true + + /@babel/compat-data@7.22.9: + resolution: {integrity: sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/core@7.22.10: + resolution: {integrity: sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.1 + '@babel/code-frame': 7.22.10 + '@babel/generator': 7.22.10 + '@babel/helper-compilation-targets': 7.22.10 + '@babel/helper-module-transforms': 7.22.9(@babel/core@7.22.10) + '@babel/helpers': 7.22.10 + '@babel/parser': 7.22.10 + '@babel/template': 7.22.5 + '@babel/traverse': 7.22.10 + '@babel/types': 7.22.10 + convert-source-map: 1.9.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/generator@7.22.10: + resolution: {integrity: sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.22.10 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.19 + jsesc: 2.5.2 + dev: true + + /@babel/helper-annotate-as-pure@7.22.5: + resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.22.10 + dev: true + + /@babel/helper-compilation-targets@7.22.10: + resolution: {integrity: sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/compat-data': 7.22.9 + '@babel/helper-validator-option': 7.22.5 + browserslist: 4.21.10 + lru-cache: 5.1.1 + semver: 6.3.1 + dev: true + + /@babel/helper-environment-visitor@7.22.5: + resolution: {integrity: sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-function-name@7.22.5: + resolution: {integrity: sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.5 + '@babel/types': 7.22.10 + dev: true + + /@babel/helper-hoist-variables@7.22.5: + resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.22.10 + dev: true + + /@babel/helper-module-imports@7.22.5: + resolution: {integrity: sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.22.10 + dev: true + + /@babel/helper-module-transforms@7.22.9(@babel/core@7.22.10): + resolution: {integrity: sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.22.10 + '@babel/helper-environment-visitor': 7.22.5 + '@babel/helper-module-imports': 7.22.5 + '@babel/helper-simple-access': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/helper-validator-identifier': 7.22.5 + dev: true + + /@babel/helper-plugin-utils@7.22.5: + resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-simple-access@7.22.5: + resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.22.10 + dev: true + + /@babel/helper-split-export-declaration@7.22.6: + resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.22.10 + dev: true + + /@babel/helper-string-parser@7.22.5: + resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-identifier@7.22.5: + resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-option@7.22.5: + resolution: {integrity: sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helpers@7.22.10: + resolution: {integrity: sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.5 + '@babel/traverse': 7.22.10 + '@babel/types': 7.22.10 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/highlight@7.22.10: + resolution: {integrity: sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.5 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + + /@babel/parser@7.22.10: + resolution: {integrity: sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.22.10 + dev: true + + /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.22.10): + resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.10 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-react-jsx-development@7.22.5(@babel/core@7.22.10): + resolution: {integrity: sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.10 + '@babel/plugin-transform-react-jsx': 7.22.5(@babel/core@7.22.10) + dev: true + + /@babel/plugin-transform-react-jsx@7.22.5(@babel/core@7.22.10): + resolution: {integrity: sha512-rog5gZaVbUip5iWDMTYbVM15XQq+RkUKhET/IHR6oizR+JEoN6CAfTTuHcK4vwUyzca30qqHqEpzBOnaRMWYMA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.10 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-module-imports': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.10) + '@babel/types': 7.22.10 + dev: true + + /@babel/template@7.22.5: + resolution: {integrity: sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.22.10 + '@babel/parser': 7.22.10 + '@babel/types': 7.22.10 + dev: true + + /@babel/traverse@7.22.10: + resolution: {integrity: sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.22.10 + '@babel/generator': 7.22.10 + '@babel/helper-environment-visitor': 7.22.5 + '@babel/helper-function-name': 7.22.5 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/parser': 7.22.10 + '@babel/types': 7.22.10 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/types@7.22.10: + resolution: {integrity: sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.22.5 + '@babel/helper-validator-identifier': 7.22.5 + to-fast-properties: 2.0.0 + dev: true + + /@esbuild/android-arm64@0.18.20: + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.18.20: + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.18.20: + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.18.20: + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.18.20: + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.18.20: + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.18.20: + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.18.20: + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.18.20: + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.18.20: + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.18.20: + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.18.20: + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.18.20: + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.18.20: + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.18.20: + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.18.20: + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.18.20: + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.18.20: + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.18.20: + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.18.20: + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.18.20: + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.18.20: + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@jridgewell/gen-mapping@0.3.3: + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.19 + dev: true + + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/set-array@1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@jridgewell/trace-mapping@0.3.19: + resolution: {integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@logseq/libs@0.0.15: + resolution: {integrity: sha512-Z4YrYGfu8Y3s9LTqVnPGkxlO+KZtP1YalH/A63zYgxP61cV5QtKlnHWNcjsKxsD5CkaSL4MlSN4mf7wNx/Fm0A==} + dependencies: + csstype: 3.1.0 + debug: 4.3.4 + dompurify: 2.3.8 + eventemitter3: 4.0.7 + fast-deep-equal: 3.1.3 + lodash-es: 4.17.21 + path: 0.12.7 + snake-case: 3.0.4 + transitivePeerDependencies: + - supports-color + dev: false + + /@preact/preset-vite@2.5.0(@babel/core@7.22.10)(preact@10.17.0)(vite@4.4.8): + resolution: {integrity: sha512-BUhfB2xQ6ex0yPkrT1Z3LbfPzjpJecOZwQ/xJrXGFSZD84+ObyS//41RdEoQCMWsM0t7UHGaujUxUBub7WM1Jw==} + peerDependencies: + '@babel/core': 7.x + vite: 2.x || 3.x || 4.x + dependencies: + '@babel/core': 7.22.10 + '@babel/plugin-transform-react-jsx': 7.22.5(@babel/core@7.22.10) + '@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.22.10) + '@prefresh/vite': 2.4.1(preact@10.17.0)(vite@4.4.8) + '@rollup/pluginutils': 4.2.1 + babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.22.10) + debug: 4.3.4 + kolorist: 1.8.0 + resolve: 1.22.4 + vite: 4.4.8(@types/node@18.15.11) + transitivePeerDependencies: + - preact + - supports-color + dev: true + + /@prefresh/babel-plugin@0.5.0: + resolution: {integrity: sha512-joAwpkUDwo7ZqJnufXRGzUb+udk20RBgfA8oLPBh5aJH2LeStmV1luBfeJTztPdyCscC2j2SmZ/tVxFRMIxAEw==} + dev: true + + /@prefresh/core@1.5.1(preact@10.17.0): + resolution: {integrity: sha512-e0mB0Oxtog6ZpKPDBYbzFniFJDIktuKMzOHp7sguntU+ot0yi6dbhJRE9Css1qf0u16wdSZjpL2W2ODWuU05Cw==} + peerDependencies: + preact: ^10.0.0 + dependencies: + preact: 10.17.0 + dev: true + + /@prefresh/utils@1.2.0: + resolution: {integrity: sha512-KtC/fZw+oqtwOLUFM9UtiitB0JsVX0zLKNyRTA332sqREqSALIIQQxdUCS1P3xR/jT1e2e8/5rwH6gdcMLEmsQ==} + dev: true + + /@prefresh/vite@2.4.1(preact@10.17.0)(vite@4.4.8): + resolution: {integrity: sha512-vthWmEqu8TZFeyrBNc9YE5SiC3DVSzPgsOCp/WQ7FqdHpOIJi7Z8XvCK06rBPOtG4914S52MjG9Ls22eVAiuqQ==} + peerDependencies: + preact: ^10.4.0 + vite: '>=2.0.0' + dependencies: + '@babel/core': 7.22.10 + '@prefresh/babel-plugin': 0.5.0 + '@prefresh/core': 1.5.1(preact@10.17.0) + '@prefresh/utils': 1.2.0 + '@rollup/pluginutils': 4.2.1 + preact: 10.17.0 + vite: 4.4.8(@types/node@18.15.11) + transitivePeerDependencies: + - supports-color + dev: true + + /@rollup/pluginutils@4.2.1: + resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} + engines: {node: '>= 8.0.0'} + dependencies: + estree-walker: 2.0.2 + picomatch: 2.3.1 + dev: true + + /@types/node@18.15.11: + resolution: {integrity: sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==} + dev: true + + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + + /babel-plugin-transform-hook-names@1.0.2(@babel/core@7.22.10): + resolution: {integrity: sha512-5gafyjyyBTTdX/tQQ0hRgu4AhNHG/hqWi0ZZmg2xvs2FgRkJXzDNKBZCyoYqgFkovfDrgM8OoKg8karoUvWeCw==} + peerDependencies: + '@babel/core': ^7.12.10 + dependencies: + '@babel/core': 7.22.10 + dev: true + + /browserslist@4.21.10: + resolution: {integrity: sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001521 + electron-to-chromium: 1.4.496 + node-releases: 2.0.13 + update-browserslist-db: 1.0.11(browserslist@4.21.10) + dev: true + + /caniuse-lite@1.0.30001521: + resolution: {integrity: sha512-fnx1grfpEOvDGH+V17eccmNjucGUnCbP6KL+l5KqBIerp26WK/+RQ7CIDE37KGJjaPyqWXXlFUyKiWmvdNNKmQ==} + dev: true + + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true + + /convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + dev: true + + /csstype@3.1.0: + resolution: {integrity: sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==} + dev: false + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + + /dompurify@2.3.8: + resolution: {integrity: sha512-eVhaWoVibIzqdGYjwsBWodIQIaXFSB+cKDf4cfxLMsK0xiud6SE+/WCVx/Xw/UwQsa4cS3T2eITcdtmTg2UKcw==} + dev: false + + /dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + dependencies: + no-case: 3.0.4 + tslib: 2.6.2 + dev: false + + /electron-to-chromium@1.4.496: + resolution: {integrity: sha512-qeXC3Zbykq44RCrBa4kr8v/dWzYJA8rAwpyh9Qd+NKWoJfjG5vvJqy9XOJ9H4P/lqulZBCgUWAYi+FeK5AuJ8g==} + dev: true + + /esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + dev: true + + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true + + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + + /estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + dev: true + + /eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + dev: false + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: false + + /fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /function-bind@1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + dev: true + + /gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + dev: true + + /globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + dev: true + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true + + /has@1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + dev: true + + /immer@9.0.19: + resolution: {integrity: sha512-eY+Y0qcsB4TZKwgQzLaE/lqYMlKhv5J9dyd2RhhtGhNo2njPXDqU9XPfcNfa3MIDsdtZt5KlkIsirlo4dHsWdQ==} + dev: false + + /inherits@2.0.3: + resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} + dev: false + + /is-core-module@2.13.0: + resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} + dependencies: + has: 1.0.3 + dev: true + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + /jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + dev: true + + /kolorist@1.8.0: + resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + dev: true + + /lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + dev: false + + /logseq-l10n@0.2.0(@logseq/libs@0.0.15): + resolution: {integrity: sha512-exBSvxVijlAW6Zy6eYRWGroJL3qxAouCZqSQzAeIRuoIU8nA0vN2S2+5slvWTFM159hlqhtdJXqudwgCs4y2Vg==} + peerDependencies: + '@logseq/libs': '>= 0.0.1-alpha.33' + dependencies: + '@logseq/libs': 0.0.15 + dev: false + + /loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + dev: false + + /lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + dependencies: + tslib: 2.6.2 + dev: false + + /lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + dependencies: + yallist: 3.1.1 + dev: true + + /magic-string@0.26.7: + resolution: {integrity: sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==} + engines: {node: '>=12'} + dependencies: + sourcemap-codec: 1.4.8 + dev: true + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + /nanoid@3.3.6: + resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + dependencies: + lower-case: 2.0.2 + tslib: 2.6.2 + dev: false + + /node-releases@2.0.13: + resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} + dev: true + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /path@0.12.7: + resolution: {integrity: sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==} + dependencies: + process: 0.11.10 + util: 0.10.4 + dev: false + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /postcss@8.4.28: + resolution: {integrity: sha512-Z7V5j0cq8oEKyejIKfpD8b4eBy9cwW2JWPk0+fB1HOAMsfHbnAXLLS+PfVWlzMSLQaWttKDt607I0XHmpE67Vw==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.6 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + + /preact@10.17.0: + resolution: {integrity: sha512-SNsI8cbaCcUS5tbv9nlXuCfIXnJ9ysBMWk0WnB6UWwcVA3qZ2O6FxqDFECMAMttvLQcW/HaNZUe2BLidyvrVYw==} + + /process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + dev: false + + /rambdax@10.0.0: + resolution: {integrity: sha512-yNlWz9g4h6C2+49r1L+L9WZLcw3b3mQnxZQfcyqon8hy52Ie6hsWeuxO9/MNXTbqWB2RuSzO5MM3rVSsPKsqfw==} + dev: false + + /react@18.2.0: + resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} + engines: {node: '>=0.10.0'} + dependencies: + loose-envify: 1.4.0 + dev: false + + /reactutils@5.13.0(react@18.2.0): + resolution: {integrity: sha512-vuu2Q1uf9IDxWKa1g4nqLYEWVcL4tFP3DspigEZQtrAdHMWqjgZo1rKQ24Nr+Wn7KaxkdAY6Nu9HiWfkZSCeEg==} + peerDependencies: + react: '>=16.8.0' + dependencies: + react: 18.2.0 + dev: false + + /resolve@1.22.4: + resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==} + hasBin: true + dependencies: + is-core-module: 2.13.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /rollup@3.28.0: + resolution: {integrity: sha512-d7zhvo1OUY2SXSM6pfNjgD5+d0Nz87CUp4mt8l/GgVP3oBsPwzNvSzyu1me6BSG9JIgWNTVcafIXBIyM8yQ3yw==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + dev: true + + /snake-case@3.0.4: + resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + dependencies: + dot-case: 3.0.4 + tslib: 2.6.2 + dev: false + + /source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: true + + /sourcemap-codec@1.4.8: + resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} + deprecated: Please use @jridgewell/sourcemap-codec instead + dev: true + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + dev: true + + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + dev: false + + /typescript@5.1.6: + resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + + /update-browserslist-db@1.0.11(browserslist@4.21.10): + resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.21.10 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + + /util@0.10.4: + resolution: {integrity: sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==} + dependencies: + inherits: 2.0.3 + dev: false + + /vite-plugin-logseq@1.1.2: + resolution: {integrity: sha512-l5YvoH3K25Zx9eqgoJFug7NfVqSPwq7/FcYYhN1TkdG8ZOiD+c+TAwdCS2dJbGgvx8GmSpbgwSZWgslB+wH53g==} + dependencies: + magic-string: 0.26.7 + dev: true + + /vite@4.4.8(@types/node@18.15.11): + resolution: {integrity: sha512-LONawOUUjxQridNWGQlNizfKH89qPigK36XhMI7COMGztz8KNY0JHim7/xDd71CZwGT4HtSRgI7Hy+RlhG0Gvg==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 18.15.11 + esbuild: 0.18.20 + postcss: 8.4.28 + rollup: 3.28.0 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + dev: true diff --git a/src/comps/FavArrow.tsx b/src/comps/FavArrow.tsx new file mode 100644 index 0000000..5a81737 --- /dev/null +++ b/src/comps/FavArrow.tsx @@ -0,0 +1,26 @@ +import { cls } from "reactutils" + +export default function FavArrow({ + expanded, + onToggle, +}: { + expanded: boolean + onToggle: (e: Event) => void +}) { + return ( + + + + + + ) +} diff --git a/src/comps/FavList.tsx b/src/comps/FavList.tsx new file mode 100644 index 0000000..cecfbc1 --- /dev/null +++ b/src/comps/FavList.tsx @@ -0,0 +1,164 @@ +import { produce } from "immer" +import { createPortal } from "preact/compat" +import { useEffect, useState } from "preact/hooks" +import { cls } from "reactutils" +import { queryForSubItems } from "../libs/utils" +import FavArrow from "./FavArrow" + +export default function FavList({ + items, + arrowContainer, +}: { + items: any[] + arrowContainer: HTMLElement +}) { + const [expanded, setExpanded] = useState(false) + + function toggleList(e: Event) { + e.preventDefault() + e.stopPropagation() + setExpanded((v) => !v) + } + + return ( + <> + {createPortal( + , + arrowContainer, + )} + + + ) +} + +function SubList({ items, shown }: { items: any[]; shown: boolean }) { + const [childrenData, setChildrenData] = useState(null) + + useEffect(() => { + setChildrenData(null) + }, [items]) + + useEffect(() => { + if (shown && childrenData == null) { + ;(async () => { + const data: any = {} + for (const item of items) { + if (item.filters) { + if (item.subitems) { + data[item.displayName] = { + expanded: false, + items: Object.values(item.subitems), + } + } + } else { + const subitems = await queryForSubItems(item["original-name"]) + if (subitems?.length > 0) { + data[item.name] = { expanded: false, items: subitems } + } + } + } + setChildrenData(data) + })() + } + }, [shown, childrenData, items]) + + async function openPage(e: MouseEvent, item: any) { + e.preventDefault() + e.stopPropagation() + + if (item.filters) { + let content = (await logseq.Editor.getBlock( + item.blockUUID, + ))!.content.replace(/\n*^filters:: .*\n*/m, "") + content += `\nfilters:: ${`{${item.filters + .map((filter: string) => `"${filter.toLowerCase()}" true`) + .join(", ")}}`}` + await logseq.Editor.updateBlock(item.blockUUID, content) + } + + const url = new URL(`http://localhost${parent.location.hash.substring(6)}`) + const isAlreadyOnPage = + decodeURIComponent(url.pathname.substring(1)) === item.name + if (!isAlreadyOnPage) { + if (e.shiftKey) { + logseq.Editor.openInRightSidebar(item.uuid ?? item.pageUUID) + } else { + ;(logseq.Editor.scrollToBlockInPage as any)(item.name) + } + } else if (item.filters) { + if (e.shiftKey) { + // NOTE: right sidebar refreshing is not possible yet. + logseq.Editor.openInRightSidebar(item.uuid ?? item.pageUUID) + } else { + // HACK: remove this hack later when Logseq's responsive refresh is fixed. + ;(logseq.Editor.scrollToBlockInPage as any)(item.blockUUID) + setTimeout(() => { + ;(logseq.Editor.scrollToBlockInPage as any)(item.name) + }, 50) + } + } + } + + function toggleChild(e: Event, itemName: string) { + e.preventDefault() + e.stopPropagation() + const newChildrenData = produce(childrenData, (draft: any) => { + draft[itemName].expanded = !draft[itemName].expanded + }) + setChildrenData(newChildrenData) + } + + function preventSideEffect(e: Event) { + e.preventDefault() + e.stopPropagation() + } + + return ( +
+ {items.map((item) => { + const displayName = item.displayName ?? item["original-name"] + const data = item.filters + ? childrenData?.[item.displayName] + : childrenData?.[item.name] + + return ( +
+
openPage(e, item)}> + {item.filters ? ( +
+ {logseq.settings?.filterIcon ?? "🔎"} +
+ ) : item.properties?.icon ? ( +
{item.properties?.icon}
+ ) : ( + + )} +
+ {item.filters && + displayName.toLowerCase().startsWith(`${item.name}/`) + ? displayName.substring(item.name.length + 1) + : displayName} +
+ {data && ( + + item.filters + ? toggleChild(e, item.displayName) + : toggleChild(e, item.name) + } + /> + )} +
+ {data?.items?.length > 0 && ( + + )} +
+ ) + })} +
+ ) +} diff --git a/src/global.d.ts b/src/global.d.ts new file mode 100644 index 0000000..38377bf --- /dev/null +++ b/src/global.d.ts @@ -0,0 +1,3 @@ +declare global {} + +export {} diff --git a/src/libs/utils.ts b/src/libs/utils.ts new file mode 100644 index 0000000..ad4655b --- /dev/null +++ b/src/libs/utils.ts @@ -0,0 +1,176 @@ +import { partition } from "rambdax" + +let language: string + +export function setLanguage(val: string) { + language = val +} + +export async function hash(text: string) { + if (!text) return "" + + const bytes = new TextEncoder().encode(text) + const hashedArray = Array.from( + new Uint8Array(await crypto.subtle.digest("SHA-1", bytes)), + ) + const hashed = hashedArray + .map((b) => b.toString(16).padStart(2, "0")) + .join("") + return hashed +} + +export async function queryForSubItems(name: string) { + name = name.toLowerCase() + + const namespaceChildren = ( + await logseq.DB.datascriptQuery( + `[:find (pull ?p [:block/name :block/original-name :block/uuid :block/properties]) + :in $ ?name + :where + [?t :block/name ?name] + [?p :block/namespace ?t]]`, + `"${name}"`, + ) + ).flat() + namespaceChildren.forEach((p: any) => { + const originalName = p["original-name"] + const trimStart = originalName.lastIndexOf("/") + p.displayName = + trimStart > -1 ? originalName.substring(trimStart + 1) : originalName + }) + + const hierarchyProperty = logseq.settings?.hierarchyProperty ?? "tags" + const taggedPages = ( + await logseq.DB.datascriptQuery( + hierarchyProperty === "tags" + ? `[:find (pull ?p [:block/name :block/original-name :block/uuid :block/properties]) + :in $ ?name + :where + [?t :block/name ?name] + [?p :block/tags ?t]]` + : `[:find (pull ?p [:block/name :block/original-name :block/uuid :block/properties]) + :in $ ?name + :where + [?p :block/original-name] + [?p :block/properties ?props] + [(get ?props :${hierarchyProperty}) ?v] + (or [(= ?v ?name)] [(contains? ?v ?name)])]`, + `"${name}"`, + ) + ).flat() + + const quickFilters = await getQuickFilters(name) + + if ( + namespaceChildren.length === 0 && + taggedPages.length === 0 && + quickFilters.length === 0 + ) + return namespaceChildren + + const list = namespaceChildren.concat(taggedPages).concat(quickFilters) + const [fixed, dynamic] = partition( + (p: any) => p.properties?.fixed != null, + list, + ) + fixed.sort((a, b) => a.properties.fixed - b.properties.fixed) + dynamic.sort((a, b) => + (a.displayName ?? a["original-name"]).localeCompare( + b.displayName ?? b["original-name"], + language, + ), + ) + const result = fixed + .concat(dynamic) + .slice(0, logseq.settings?.taggedPageLimit ?? 30) + + return result +} + +async function getQuickFilters(name: string) { + const [{ uuid: blockUUID }, { uuid: pageUUID }] = ( + await logseq.DB.datascriptQuery( + `[:find (pull ?b [:block/uuid]) (pull ?p [:block/uuid]) + :in $ ?name + :where + [?p :block/name ?name] + [?b :block/page ?p] + [?b :block/pre-block? true]]`, + `"${name}"`, + ) + )[0] ?? [{}, {}] + if (blockUUID == null || pageUUID == null) return [] + + let quickFiltersStr + try { + quickFiltersStr = JSON.parse( + (await logseq.Editor.getBlockProperty(blockUUID, "quick-filters")) ?? + '""', + ) + } catch (err) { + console.error(err) + return [] + } + if (!quickFiltersStr) return [] + + const groups = quickFiltersStr.match(/(?:\d+\s+)?(?:\[\[[^\]]+\]\]\s*)+/g) + if (groups == null) return [] + + const quickFilters = groups + .map((filterStr: string) => { + const matches = Array.from( + filterStr.matchAll(/\[\[([^\]]+)\]\]\s*|(\d+)/g), + ) + const fixed = matches[0][2] ? +matches[0][2] : null + const tags = (fixed == null ? matches : matches.slice(1)).map((m) => m[1]) + return [tags, fixed] + }) + .filter(([tags, fixed]: any) => tags.length > 0) + .reduce((filter: any, [tags, fixed]: any) => { + if (filter[tags[0]] == null) { + filter[tags[0]] = {} + if (fixed != null) { + filter[tags[0]].properties = { fixed } + } + } + constructFilter(filter[tags[0]], name, blockUUID, pageUUID, tags, []) + return filter + }, {}) + + return Object.values(quickFilters) +} + +function constructFilter( + obj: Record, + name: string, + blockUUID: string, + pageUUID: string, + tags: string[], + path: string[], +) { + if (obj.displayName == null) { + obj.name = name + obj.blockUUID = blockUUID + obj.pageUUID = pageUUID + obj.displayName = tags[0] + obj.filters = [...path, tags[0]] + } + + tags = tags.slice(1) + if (tags.length === 0) return + + if (obj.subitems == null) { + obj.subitems = {} + } + if (obj.subitems[tags[0]] == null) { + obj.subitems[tags[0]] = {} + } + constructFilter( + obj.subitems[tags[0]], + name, + blockUUID, + pageUUID, + tags, + obj.filters, + ) +} diff --git a/src/plugin.tsx b/src/plugin.tsx new file mode 100644 index 0000000..b0e1de4 --- /dev/null +++ b/src/plugin.tsx @@ -0,0 +1,307 @@ +import "@logseq/libs" +import { setup, t } from "logseq-l10n" +import { render } from "preact" +import { throttle } from "rambdax" +import FavList from "./comps/FavList" +import { hash, queryForSubItems, setLanguage } from "./libs/utils" +import zhCN from "./translations/zh-CN.json" + +let dragHandle: HTMLElement | null = null + +async function main() { + const { preferredLanguage: lang } = await logseq.App.getUserConfigs() + setLanguage(logseq.settings?.sortingLocale || lang) + + await setup({ builtinTranslations: { "zh-CN": zhCN } }) + + provideStyles() + + logseq.useSettingsSchema([ + { + key: "hierarchyProperty", + title: "", + type: "string", + default: "tags", + description: t( + "It controls which property is used to decide a tag's hierarchy.", + ), + }, + { + key: "filterIcon", + title: "", + type: "string", + default: "🔍", + description: t("Define an icon for quick filters."), + }, + { + key: "hoverArrow", + title: "", + type: "boolean", + default: false, + description: t("Show arrows only when hovered."), + }, + { + key: "taggedPageLimit", + title: "", + type: "number", + default: 30, + description: t( + "Maximum number of tagged pages to display on each level for favorites.", + ), + }, + { + key: "sortingLocale", + title: "", + type: "string", + default: "", + description: t( + "Locale used in sorting hierarchical favorites. E.g, zh-CN. Keep it empty to use Logseq's language setting.", + ), + }, + ]) + + const favoritesObserver = new MutationObserver(async (mutationList) => { + const mutation = mutationList[0] + if ( + (mutation?.target as any).classList?.contains("bd") || + (mutation?.target as any).classList?.contains("favorites") + ) { + await processFavorites() + } + }) + const favoritesEl = parent.document.querySelector("#left-sidebar .favorites") + if (favoritesEl != null) { + favoritesObserver.observe(favoritesEl, { childList: true, subtree: true }) + } + + const transactionOff = logseq.DB.onChanged(onTransaction) + + await processFavorites() + + const graph = (await logseq.App.getCurrentGraph())! + const storedWidth = parent.localStorage.getItem(`kef-ae-lsw-${graph.name}`) + if (storedWidth) { + parent.document.documentElement.style.setProperty( + "--ls-left-sidebar-width", + `${+storedWidth}px`, + ) + } + + logseq.provideUI({ + key: "kef-ae-drag-handle", + path: "#left-sidebar", + template: `
`, + }) + setTimeout(() => { + dragHandle = parent.document.querySelector( + "#left-sidebar .kef-ae-drag-handle", + )! + dragHandle.addEventListener("pointerdown", onPointerDown) + }, 0) + + logseq.beforeunload(async () => { + transactionOff() + favoritesObserver.disconnect() + dragHandle?.removeEventListener("pointerdown", onPointerDown) + }) + + console.log("#favorite-tree loaded") +} + +function provideStyles() { + logseq.provideStyle({ + key: "kef-ae-fav", + style: ` + .kef-ae-fav-list { + padding-left: 24px; + display: none; + } + .kef-ae-fav-expanded { + display: block; + } + .kef-ae-fav-arrow { + flex: 0 0 auto; + padding: 4px 20px 4px 10px; + margin-right: -20px; + opacity: ${logseq.settings?.hoverArrow ? 0 : 1}; + transition: opacity 0.3s; + } + :is(.favorite-item, .recent-item):hover > a > .kef-ae-fav-arrow, + .kef-ae-fav-item:hover > .kef-ae-fav-arrow { + opacity: 1; + } + .kef-ae-fav-arrow svg { + transform: rotate(90deg) scale(0.8); + transition: transform 0.04s linear; + } + .kef-ae-fav-arrow-expanded svg { + transform: rotate(0deg) scale(0.8); + } + .kef-ae-fav-item { + display: flex; + align-items: center; + padding: 0 24px; + line-height: 28px; + color: var(--ls-header-button-background); + cursor: pointer; + } + .kef-ae-fav-item:hover { + background-color: var(--ls-quaternary-background-color); + } + .kef-ae-fav-item-icon { + flex: 0 0 auto; + margin-right: 5px; + width: 16px; + text-align: center; + } + .kef-ae-fav-item-name { + flex: 1 1 auto; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + .kef-ae-drag-handle { + content: ""; + position: absolute; + top: 0; + bottom: 0; + right: 0; + width: 4px; + z-index: 10; + } + .kef-ae-drag-handle:hover, + .kef-ae-dragging .kef-ae-drag-handle { + cursor: col-resize; + background: var(--ls-active-primary-color); + } + .kef-ae-dragging { + cursor: col-resize; + } + .kef-ae-dragging :is(#left-sidebar, #main-content-container) { + pointer-events: none; + } + `, + }) +} + +async function processFavorites() { + const favorites = parent.document.querySelectorAll( + `#left-sidebar .favorite-item`, + ) + for (const fav of favorites) { + const items = await queryForSubItems(fav.dataset.ref!) + if (items?.length > 0) { + injectList(fav, items) + } + } +} + +async function injectList(el: HTMLElement, items: any[]) { + const isFav = el.classList.contains("favorite-item") + const key = `kef-ae-${isFav ? "f" : "r"}-${await hash(el.dataset.ref!)}` + + const arrowContainer = el.querySelector("a")! + const arrow = arrowContainer.querySelector(".kef-ae-fav-arrow") + if (arrow != null) { + arrow.remove() + } + + if (parent.document.getElementById(key) == null) { + logseq.provideUI({ + key, + path: `.${isFav ? "favorite" : "recent"}-item[data-ref="${ + el.dataset.ref + }"]`, + template: `
`, + }) + } + + setTimeout(() => { + renderList(key, items, arrowContainer) + }, 0) +} + +function renderList(key: string, items: any[], arrowContainer: HTMLElement) { + const el = parent.document.getElementById(key)! + render(, el) +} + +async function onTransaction({ blocks, txData, txMeta }: any) { + if (needsProcessing(txData)) { + await processFavorites() + } +} + +function needsProcessing(txData: any[]) { + const hierarchyProperty = logseq.settings?.hierarchyProperty ?? "tags" + let oldProperty, newProperty + let oldQuickFilters, newQuickFilters + for (const [_e, attr, val, _tx, added] of txData) { + if (attr === "originalName") return true + if (hierarchyProperty === "tags" && attr === "tags") return true + if (attr === "properties") { + if (val[hierarchyProperty]) { + if (added) { + newProperty = val[hierarchyProperty] + } else { + oldProperty = val[hierarchyProperty] + } + } + if (val.quickFilters) { + if (added) { + newQuickFilters = val.quickFilters + } else { + oldQuickFilters = val.quickFilters + } + } + } + } + if ( + (!oldProperty && !newProperty && !oldQuickFilters && !newQuickFilters) || + (oldProperty?.toString() === newProperty?.toString() && + oldQuickFilters === newQuickFilters) + ) + return false + return true +} + +function onPointerDown(e: Event) { + e.preventDefault() + parent.document.documentElement.classList.add("kef-ae-dragging") + parent.document.addEventListener("pointermove", onPointerMove) + parent.document.addEventListener("pointerup", onPointerUp) + parent.document.addEventListener("pointercancel", onPointerUp) +} + +function onPointerUp(e: MouseEvent) { + e.preventDefault() + parent.document.removeEventListener("pointermove", onPointerMove) + parent.document.removeEventListener("pointerup", onPointerUp) + parent.document.removeEventListener("pointercancel", onPointerUp) + parent.document.documentElement.classList.remove("kef-ae-dragging") + + const pos = e.clientX + parent.document.documentElement.style.setProperty( + "--ls-left-sidebar-width", + `${pos}px`, + ) + ;(async () => { + const graph = (await logseq.App.getCurrentGraph())! + parent.localStorage.setItem(`kef-ae-lsw-${graph.name}`, `${pos}`) + })() +} + +function onPointerMove(e: MouseEvent) { + e.preventDefault() + move(e.clientX) +} + +const move = throttle((pos) => { + parent.document.documentElement.style.setProperty( + "--ls-left-sidebar-width", + `${pos}px`, + ) +}, 12) + +logseq.ready(main).catch(console.error) diff --git a/src/preact.d.ts b/src/preact.d.ts new file mode 100644 index 0000000..ac79d62 --- /dev/null +++ b/src/preact.d.ts @@ -0,0 +1 @@ +import JSX = preact.JSX diff --git a/src/translations/zh-CN.json b/src/translations/zh-CN.json new file mode 100644 index 0000000..11e8f3d --- /dev/null +++ b/src/translations/zh-CN.json @@ -0,0 +1,7 @@ +{ + "It controls which property is used to decide a tag's hierarchy.": "使用哪个属性作为判断标签结构的依据。", + "Define an icon for quick filters.": "给快速过滤器设置一个图标。", + "Show arrows only when hovered.": "仅在鼠标移上去时显示箭头。", + "Maximum number of tagged pages to display on each level for favorites.": "收藏中每级显示的标记页面的最大数量。", + "Locale used in sorting hierarchical favorites. E.g, zh-CN. Keep it empty to use Logseq's language setting.": "在结构化收藏排序中所使用到的locale。例如 zh-CN。留空会使用Logseq的语言设置。" +} diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..cccc91a --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "lib": [ + "DOM", + "DOM.Iterable", + "ESNext" + ], + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve", + "jsxFactory": "h", + "jsxFragmentFactory": "Fragment", + }, + "include": [ + "src" + ], + "references": [ + { + "path": "./tsconfig.node.json" + } + ] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..9d31e2a --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "composite": true, + "module": "ESNext", + "moduleResolution": "Node", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..0e3d02b --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,29 @@ +import preact from "@preact/preset-vite" +import { resolve } from "path" +import { defineConfig } from "vite" +import logseqPlugin from "vite-plugin-logseq" + +const PORT = 3003 + +// https://vitejs.dev/config/ +export default defineConfig(({ mode }) => { + return { + server: { + strictPort: true, + port: PORT, + }, + build: { + cssCodeSplit: false, + rollupOptions: { + input: { + plugin: resolve(__dirname, "index.html"), + }, + output: { + entryFileNames: "[name]-[hash].js", + assetFileNames: "[name][extname]", + }, + }, + }, + plugins: [preact(), logseqPlugin()], + } +})