From 39eda568e9c8dd9ac6ee17f656fd4f9b5b365a6b Mon Sep 17 00:00:00 2001 From: Seth Date: Sat, 19 Aug 2023 10:25:15 +0800 Subject: [PATCH] chore: initial commit --- .github/workflows/main.yml | 67 +++ .gitignore | 26 + .prettierrc | 7 + LICENSE | 21 + README.md | 15 + README.zh.md | 15 + index.html | 15 + package.json | 29 + pnpm-lock.yaml | 1043 +++++++++++++++++++++++++++++++++++ src/comps/FavArrow.tsx | 26 + src/comps/FavList.tsx | 164 ++++++ src/global.d.ts | 3 + src/libs/utils.ts | 176 ++++++ src/plugin.tsx | 307 +++++++++++ src/preact.d.ts | 1 + src/translations/zh-CN.json | 7 + src/vite-env.d.ts | 1 + tsconfig.json | 33 ++ tsconfig.node.json | 9 + vite.config.ts | 29 + 20 files changed, 1994 insertions(+) create mode 100644 .github/workflows/main.yml create mode 100644 .gitignore create mode 100644 .prettierrc create mode 100644 LICENSE create mode 100644 README.md create mode 100644 README.zh.md create mode 100644 index.html create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 src/comps/FavArrow.tsx create mode 100644 src/comps/FavList.tsx create mode 100644 src/global.d.ts create mode 100644 src/libs/utils.ts create mode 100644 src/plugin.tsx create mode 100644 src/preact.d.ts create mode 100644 src/translations/zh-CN.json create mode 100644 src/vite-env.d.ts create mode 100644 tsconfig.json create mode 100644 tsconfig.node.json create mode 100644 vite.config.ts 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()], + } +})