├── .github
└── workflows
│ └── main.yml
├── LICENSE
├── README.md
├── README_zh_CN.md
├── assets
├── AI_OP.gif
├── addPinyin.gif
├── baiduAI1.png
├── bilink1.gif
├── bilink1bottom.gif
├── bilink2.gif
├── bkAndText2ref.gif
├── bookmark.png
├── bottomBK.png
├── cardSusp.gif
├── cardpri1.gif
├── changeBG.gif
├── cleancards.gif
├── cmd.png
├── copAsImg.gif
├── createCard.gif
├── createXmid.gif
├── delCard.gif
├── fastref.gif
├── fixbrokenlnks.gif
├── flagBookmark.gif
├── gotoBlockID.gif
├── iconmenu.png
├── idea1.jpg
├── idea2.jpg
├── ideaconflict.png
├── ideadesktop.gif
├── knowledgeCfg.png
├── knowledgeCreateApp.png
├── knowledgeCreateAppID.png
├── knowledgeCreateKnowledge.png
├── knowledgeCreateKnowledgeID.png
├── knowledgeShow.gif
├── knowledgeShow2.gif
├── knowledgeShow3.png
├── knowledgeToken.png
├── longCopy.gif
├── longMove.gif
├── makeItBlur.gif
├── makeTab.gif
├── menu.png
├── mergeDocs.gif
├── move2dailynote.gif
├── moveDocs.gif
├── nextdailynote.png
├── openRefByClick.gif
├── overlay.gif
├── randVedio.gif
├── readingpoint.gif
├── removeCardsInDoc.gif
├── scheduleCopyID.gif
├── scheduleSetTime.gif
├── scheduleSetTime.png
├── settings.gif
├── statickLnk.gif
├── statustomato.png
├── taskrm.gif
├── text2ref.gif
├── text2refF3.gif
├── tidyAssets.gif
├── tomatoClockCfg.png
└── uncorrelated.gif
├── dist
├── README.md
├── README_zh_CN.md
├── i18n
│ ├── empty.drawio
│ ├── empty.xmind
│ ├── en_US.json
│ ├── es_ES.json
│ ├── fr_FR.json
│ ├── ja_JP.json
│ ├── zh_CHT.json
│ └── zh_CN.json
├── icon.png
├── index.css
├── index.js
├── plugin.json
└── preview.png
├── icon.png
├── package.json
├── plugin.json
├── preview.png
├── src
├── AIBox.ts
├── AIBoxMenu.svelte
├── BackLinkBottom.svelte
├── BackLinkBottomAutoRefresh.svelte
├── BackLinkBottomBox.ts
├── BackLinkBottomConTree.svelte
├── BackLinkBottomOnceRefresh.svelte
├── BuyTomato.svelte
├── CardBox.ts
├── CardBoxSettings.svelte
├── CardPriorityBar.svelte
├── CardPriorityBox.ts
├── CommentBox.svelte
├── CommentBox.ts
├── CozeSearchBox.ts
├── CozeSearchBoxMenu.svelte
├── CpBox.ts
├── DailyNoteBox.ts
├── DbBkBox.ts
├── FastNoteBox.ts
├── GraphBox.svelte
├── GraphBox.ts
├── GraphControl.svelte
├── GraphMenu.svelte
├── GraphNode.svelte
├── ImgBox.ts
├── ImgOverlayBox.svelte
├── ImgOverlayBox.ts
├── IndexConf.svelte
├── LinkBox.svelte
├── LinkBox.ts
├── LinkBoxBar.svelte
├── ListBox.ts
├── MindWire.ts
├── MixBox.ts
├── NoteBox.svelte
├── NoteBox.ts
├── NotebookSelect.svelte
├── ReadingPoint.svelte
├── ReadingPointBox.ts
├── Schedule.ts
├── Tag2RefBox.ts
├── TomatoClock.ts
├── TomatoClockVedio.svelte
├── TomatoVIP.svelte
├── ToolbarBox.ts
├── constants.ts
├── cssStyle.ts
├── exportFiles.ts
├── fold.ts
├── i18n
│ ├── empty.drawio
│ ├── empty.xmind
│ ├── en_US.json
│ ├── es_ES.json
│ ├── fr_FR.json
│ ├── ja_JP.json
│ ├── zh_CHT.json
│ └── zh_CN.json
├── icons.ts
├── index.scss
├── index.ts
├── libs
│ ├── BaseTomatoPlugin.ts
│ ├── DialogText.ts
│ ├── DialogTextSv.svelte
│ ├── EnumUtils.ts
│ ├── Events.ts
│ ├── SelectionML.ts
│ ├── bkUtils.ts
│ ├── bookmark.ts
│ ├── cache.ts
│ ├── cardUtils.ts
│ ├── cozeAI.ts
│ ├── destroyer.ts
│ ├── docUtils.ts
│ ├── focusUtils.ts
│ ├── functional.ts
│ ├── gconst.ts
│ ├── globalUtils.ts
│ ├── hash.ts
│ ├── ial.ts
│ ├── keyboard.ts
│ ├── listUtils.ts
│ ├── openAI.ts
│ ├── progressive.ts
│ ├── search.ts
│ ├── stores.ts
│ ├── switchDraft.ts
│ ├── sydom.ts
│ ├── taobaocode.ts
│ ├── text1.ts
│ ├── text10.ts
│ ├── text11.ts
│ ├── text2.ts
│ ├── text3.ts
│ ├── text4.ts
│ ├── text5.ts
│ ├── text6.ts
│ ├── text7.ts
│ ├── text8.ts
│ ├── text9.ts
│ ├── timer.ts
│ ├── tools.ts
│ ├── types.ts
│ ├── ui.ts
│ ├── user.ts
│ ├── utils.ts
│ └── winHotkey.ts
├── tomatoI18n.ts
└── types
│ ├── baidu.d.ts
│ ├── siyuan.d.ts
│ └── utils.d.ts
├── svelte.config.js
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Create Release on Tag Push
2 |
3 | on:
4 | push:
5 | tags:
6 | - "v*"
7 | # env:
8 | # PROJ_PATH: sy-tomato-plugin
9 | # YARN_CACHE_DIR_PATH: ""
10 | # jobs:
11 | # build:
12 | # runs-on: ubuntu-latest
13 | # steps:
14 | # - name: Checkout workspace repo # https://github.com/marketplace/actions/checkout#checkout-multiple-repos-side-by-side
15 | # uses: actions/checkout@v4
16 | # with:
17 | # repository: IAliceBobI/sy-plugins
18 |
19 | # - name: Install Node.js
20 | # uses: actions/setup-node@v4
21 | # with:
22 | # node-version: 20
23 | # registry-url: "https://registry.npmjs.org"
24 |
25 | # - name: Install yarn
26 | # run: npm install -g yarn
27 |
28 | # - name: Set yarn cache directory
29 | # shell: bash
30 | # run: echo "YARN_CACHE_DIR_PATH=$(yarn cache dir)" >> $GITHUB_ENV
31 |
32 | # - name: Cache dependencies
33 | # uses: actions/cache@v4
34 | # with:
35 | # path: ${{ env.YARN_CACHE_DIR_PATH }}
36 | # key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
37 | # restore-keys: |
38 | # ${{ runner.os }}-yarn-
39 |
40 | # - name: Install dependencies
41 | # run: |
42 | # cd $PROJ_PATH
43 | # yarn install
44 |
45 | # - name: Build for production
46 | # run: |
47 | # cd $PROJ_PATH
48 | # yarn build
49 | # mv package.zip ..
50 |
51 | # - name: Release
52 | # uses: ncipollo/release-action@v1
53 | # with:
54 | # allowUpdates: true
55 | # artifactErrorsFailBuild: true
56 | # artifacts: "package.zip"
57 | # token: ${{ secrets.GITHUB_TOKEN }}
58 | # prerelease: false
59 | jobs:
60 | build:
61 | runs-on: ubuntu-latest
62 |
63 | steps:
64 | - name: Checkout repository
65 | uses: actions/checkout@v3
66 |
67 | - name: Run Script
68 | run: |
69 | cd dist
70 | zip -r package.zip ./*
71 | mv package.zip ..
72 |
73 | - name: Release
74 | uses: ncipollo/release-action@v1
75 | with:
76 | allowUpdates: true
77 | artifactErrorsFailBuild: true
78 | artifacts: "package.zip"
79 | token: ${{ secrets.GITHUB_TOKEN }}
80 | prerelease: false
81 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 SiYuan sy-tomato-plugin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [OPEN SOURCE 已开源 🔓](https://github.com/IAliceBobI/sy-tomato-plugin)
2 |
3 | # [DOC 帮助文档 📖](https://awx9773btw.feishu.cn/docx/IWPcd438yoL3C6xHC0xcOXDKnmh?from=from_copylink)
4 |
5 | # [ISSUE 反馈 交流 QQ频道 💬](https://pd.qq.com/s/2fh7nh7gz)
6 |
7 | # [CHANGELOG 更新日志 📅](https://awx9773btw.feishu.cn/docx/KekbdZ6Ozo4LLHxAGsncGTKJnff?from=from_copylink)
8 |
9 | # [ACKNOWLEDGMENTS 鸣谢 🙏](https://awx9773btw.feishu.cn/docx/FQ7udC3jeorfDYxI39ict2UNn2g?from=from_copylink)
10 |
11 | # POWERED BY LOVE 为爱发电 💗
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/README_zh_CN.md:
--------------------------------------------------------------------------------
1 | # [OPEN SOURCE 已开源 🔓](https://github.com/IAliceBobI/sy-tomato-plugin)
2 |
3 | # [DOC 帮助文档 📖](https://awx9773btw.feishu.cn/docx/IWPcd438yoL3C6xHC0xcOXDKnmh?from=from_copylink)
4 |
5 | # [ISSUE 反馈 交流 QQ频道 💬](https://pd.qq.com/s/2fh7nh7gz)
6 |
7 | # [CHANGELOG 更新日志 📅](https://awx9773btw.feishu.cn/docx/KekbdZ6Ozo4LLHxAGsncGTKJnff?from=from_copylink)
8 |
9 | # [ACKNOWLEDGMENTS 鸣谢 🙏](https://awx9773btw.feishu.cn/docx/FQ7udC3jeorfDYxI39ict2UNn2g?from=from_copylink)
10 |
11 | # POWERED BY LOVE 为爱发电 💗
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/assets/AI_OP.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/AI_OP.gif
--------------------------------------------------------------------------------
/assets/addPinyin.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/addPinyin.gif
--------------------------------------------------------------------------------
/assets/baiduAI1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/baiduAI1.png
--------------------------------------------------------------------------------
/assets/bilink1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/bilink1.gif
--------------------------------------------------------------------------------
/assets/bilink1bottom.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/bilink1bottom.gif
--------------------------------------------------------------------------------
/assets/bilink2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/bilink2.gif
--------------------------------------------------------------------------------
/assets/bkAndText2ref.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/bkAndText2ref.gif
--------------------------------------------------------------------------------
/assets/bookmark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/bookmark.png
--------------------------------------------------------------------------------
/assets/bottomBK.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/bottomBK.png
--------------------------------------------------------------------------------
/assets/cardSusp.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/cardSusp.gif
--------------------------------------------------------------------------------
/assets/cardpri1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/cardpri1.gif
--------------------------------------------------------------------------------
/assets/changeBG.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/changeBG.gif
--------------------------------------------------------------------------------
/assets/cleancards.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/cleancards.gif
--------------------------------------------------------------------------------
/assets/cmd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/cmd.png
--------------------------------------------------------------------------------
/assets/copAsImg.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/copAsImg.gif
--------------------------------------------------------------------------------
/assets/createCard.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/createCard.gif
--------------------------------------------------------------------------------
/assets/createXmid.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/createXmid.gif
--------------------------------------------------------------------------------
/assets/delCard.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/delCard.gif
--------------------------------------------------------------------------------
/assets/fastref.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/fastref.gif
--------------------------------------------------------------------------------
/assets/fixbrokenlnks.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/fixbrokenlnks.gif
--------------------------------------------------------------------------------
/assets/flagBookmark.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/flagBookmark.gif
--------------------------------------------------------------------------------
/assets/gotoBlockID.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/gotoBlockID.gif
--------------------------------------------------------------------------------
/assets/iconmenu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/iconmenu.png
--------------------------------------------------------------------------------
/assets/idea1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/idea1.jpg
--------------------------------------------------------------------------------
/assets/idea2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/idea2.jpg
--------------------------------------------------------------------------------
/assets/ideaconflict.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/ideaconflict.png
--------------------------------------------------------------------------------
/assets/ideadesktop.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/ideadesktop.gif
--------------------------------------------------------------------------------
/assets/knowledgeCfg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/knowledgeCfg.png
--------------------------------------------------------------------------------
/assets/knowledgeCreateApp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/knowledgeCreateApp.png
--------------------------------------------------------------------------------
/assets/knowledgeCreateAppID.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/knowledgeCreateAppID.png
--------------------------------------------------------------------------------
/assets/knowledgeCreateKnowledge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/knowledgeCreateKnowledge.png
--------------------------------------------------------------------------------
/assets/knowledgeCreateKnowledgeID.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/knowledgeCreateKnowledgeID.png
--------------------------------------------------------------------------------
/assets/knowledgeShow.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/knowledgeShow.gif
--------------------------------------------------------------------------------
/assets/knowledgeShow2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/knowledgeShow2.gif
--------------------------------------------------------------------------------
/assets/knowledgeShow3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/knowledgeShow3.png
--------------------------------------------------------------------------------
/assets/knowledgeToken.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/knowledgeToken.png
--------------------------------------------------------------------------------
/assets/longCopy.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/longCopy.gif
--------------------------------------------------------------------------------
/assets/longMove.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/longMove.gif
--------------------------------------------------------------------------------
/assets/makeItBlur.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/makeItBlur.gif
--------------------------------------------------------------------------------
/assets/makeTab.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/makeTab.gif
--------------------------------------------------------------------------------
/assets/menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/menu.png
--------------------------------------------------------------------------------
/assets/mergeDocs.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/mergeDocs.gif
--------------------------------------------------------------------------------
/assets/move2dailynote.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/move2dailynote.gif
--------------------------------------------------------------------------------
/assets/moveDocs.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/moveDocs.gif
--------------------------------------------------------------------------------
/assets/nextdailynote.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/nextdailynote.png
--------------------------------------------------------------------------------
/assets/openRefByClick.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/openRefByClick.gif
--------------------------------------------------------------------------------
/assets/overlay.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/overlay.gif
--------------------------------------------------------------------------------
/assets/randVedio.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/randVedio.gif
--------------------------------------------------------------------------------
/assets/readingpoint.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/readingpoint.gif
--------------------------------------------------------------------------------
/assets/removeCardsInDoc.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/removeCardsInDoc.gif
--------------------------------------------------------------------------------
/assets/scheduleCopyID.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/scheduleCopyID.gif
--------------------------------------------------------------------------------
/assets/scheduleSetTime.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/scheduleSetTime.gif
--------------------------------------------------------------------------------
/assets/scheduleSetTime.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/scheduleSetTime.png
--------------------------------------------------------------------------------
/assets/settings.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/settings.gif
--------------------------------------------------------------------------------
/assets/statickLnk.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/statickLnk.gif
--------------------------------------------------------------------------------
/assets/statustomato.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/statustomato.png
--------------------------------------------------------------------------------
/assets/taskrm.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/taskrm.gif
--------------------------------------------------------------------------------
/assets/text2ref.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/text2ref.gif
--------------------------------------------------------------------------------
/assets/text2refF3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/text2refF3.gif
--------------------------------------------------------------------------------
/assets/tidyAssets.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/tidyAssets.gif
--------------------------------------------------------------------------------
/assets/tomatoClockCfg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/tomatoClockCfg.png
--------------------------------------------------------------------------------
/assets/uncorrelated.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/assets/uncorrelated.gif
--------------------------------------------------------------------------------
/dist/README.md:
--------------------------------------------------------------------------------
1 | # [OPEN SOURCE 已开源 🔓](https://github.com/IAliceBobI/sy-tomato-plugin)
2 |
3 | # [DOC 帮助文档 📖](https://awx9773btw.feishu.cn/docx/IWPcd438yoL3C6xHC0xcOXDKnmh?from=from_copylink)
4 |
5 | # [ISSUE 反馈 交流 QQ频道 💬](https://pd.qq.com/s/2fh7nh7gz)
6 |
7 | # [CHANGELOG 更新日志 📅](https://awx9773btw.feishu.cn/docx/KekbdZ6Ozo4LLHxAGsncGTKJnff?from=from_copylink)
8 |
9 | # [ACKNOWLEDGMENTS 鸣谢 🙏](https://awx9773btw.feishu.cn/docx/FQ7udC3jeorfDYxI39ict2UNn2g?from=from_copylink)
10 |
11 | # POWERED BY LOVE 为爱发电 💗
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/dist/README_zh_CN.md:
--------------------------------------------------------------------------------
1 | # [OPEN SOURCE 已开源 🔓](https://github.com/IAliceBobI/sy-tomato-plugin)
2 |
3 | # [DOC 帮助文档 📖](https://awx9773btw.feishu.cn/docx/IWPcd438yoL3C6xHC0xcOXDKnmh?from=from_copylink)
4 |
5 | # [ISSUE 反馈 交流 QQ频道 💬](https://pd.qq.com/s/2fh7nh7gz)
6 |
7 | # [CHANGELOG 更新日志 📅](https://awx9773btw.feishu.cn/docx/KekbdZ6Ozo4LLHxAGsncGTKJnff?from=from_copylink)
8 |
9 | # [ACKNOWLEDGMENTS 鸣谢 🙏](https://awx9773btw.feishu.cn/docx/FQ7udC3jeorfDYxI39ict2UNn2g?from=from_copylink)
10 |
11 | # POWERED BY LOVE 为爱发电 💗
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/dist/i18n/empty.drawio:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/dist/i18n/empty.xmind:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/dist/i18n/empty.xmind
--------------------------------------------------------------------------------
/dist/i18n/en_US.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Tomato Timer",
3 | "takeARestPlease": "😊 Take a break!",
4 | "hasWorkedMinutes": "Minutes up",
5 | "takeARestAfterMinutes": "Take a break after",
6 | "startCountdown": "Start countdown",
7 | "cancelCountdown": "Cancel countdown",
8 | "cancelLastCountdown": "Cancel last countdown",
9 | "clickOneBlockFirst": "Please click on a block first",
10 | "setDate": "Set time",
11 | "setDateTitle": "Please set time for the following block",
12 | "scheduledAt": "Reminder time",
13 | "remind": "Remind",
14 | "scheduleSetSuccess": "Reminder set successfully",
15 | "schedule": "Set reminder",
16 | "btnAddADay": "+1 Day",
17 | "addBookmark": "Set reading point",
18 | "addBookmarkWithoutENV": "Set reading point (no environment)",
19 | "removeBrokenCards": "Clean all invalid flashcards cleanCards",
20 | "addFlashCard": "Single list, list card making, cancel card making",
21 | "removedBrokenCards": "Deleted invalid flashcards:",
22 | "thereIsNoInvalidCards": "No invalid flashcards!",
23 | "deleteBlocks": "Batch delete large continuous content blocks deleteBlocks",
24 | "moveBlocks": "Batch move large continuous content blocks moveBlocks",
25 | "copyBlocks": "Batch copy large continuous content blocks copyBlocks",
26 | "deleteBlocksHelp": "Batch delete help: Please wrap the content to be processed with two lines aacc1 and aacc2. aacc1 Today has good weather1! Today has good weather2! ... Today has good weather3! aacc2 ",
27 | "moveBlocksHelp": "Batch move copy help: Please wrap the content to be processed with two lines aacc1 and aacc2. Then insert a line aacc3 at the target location. [Document1] aacc1 Today has good weather1! Today has good weather2! ... Today has good weather3! aacc2 [Document2] ... aacc3 ... (Document1 and Document2 can be the same document) ",
28 | "topBarTitleShowContents": "Open contents/bookmark page",
29 | "thereIsNoBookmark": "No bookmarks found",
30 | "showBookmarks": "View reading points",
31 | "lookingForTheList": "Searching for the list...",
32 | "reindex": "Unable to find the list to make cards. If you have indeed placed the cursor within a list item and tried multiple times, you can try rebuilding the index.",
33 | "wait4finish": "Please wait for the previous operation to finish!",
34 | "bilink": "Bidirectional link",
35 | "bilinkSelectBlock": "Bidirectional link: Select block",
36 | "bilinkCreateLnk": "Bidirectional link: Create reciprocal link",
37 | "backlink": "Backlink",
38 | "bottombacklink": "Insert bottom backlink #bottomBacklink",
39 | "bottomMention": "Insert bottom mention #bottomMention",
40 | "addPicOverlay": "Add picture overlay",
41 | "delCard": "Delete current flashcard during review",
42 | "skipCard": "Skip current flashcard during review",
43 | "moveBlock2today": "Move content to daily note",
44 | "previousNote": "Previous note",
45 | "nextNote": "Next note",
46 | "cardPrioritySet": "Document and sub-document flashcard priority",
47 | "setCardPriority": "Set flashcard priority",
48 | "uncheckAll": "Uncheck all completed todo tasks in the current document",
49 | "delAllchecked": "Delete all completed todo tasks in the current document",
50 | "refreshVirRef": "Refresh virtual reference",
51 | "removeDocCards": "Remove all flashcards in the current document",
52 | "hotMenu": "Quick menu",
53 | "spaceRepeat": "Spaced repetition",
54 | "locateDoc": "Global locate document",
55 | "baiduAI": "Send selected content to ERNIE Bot 4",
56 | "addTODOBookmark": "Add a 🚩 bookmark",
57 | "deleteAllTODOBookmarks": "Delete all 🚩 bookmarks",
58 | "txt2ref": "All content separated by spaces is converted to references (ignore content after ##)",
59 | "noteBox": "Capture fleeting thoughts",
60 | "noteBoxGlobal": "Capture fleeting thoughts (global)",
61 | "gotoBlockID": "Jump to block ID in clipboard (gotoBlockID)",
62 | "deleteBookmark": "Delete reading point in current document",
63 | "gotoBookmark": "Jump to reading point in current document"
64 | }
--------------------------------------------------------------------------------
/dist/i18n/es_ES.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Temporizador de tomate",
3 | "takeARestPlease": "😊 Por favor, descansa un momento!",
4 | "hasWorkedMinutes": "minutos terminados",
5 | "takeARestAfterMinutes": "descansar después de minutos",
6 | "startCountdown": "iniciar cuenta regresiva",
7 | "cancelCountdown": "cancelar cuenta regresiva",
8 | "cancelLastCountdown": "cancelar la cuenta regresiva anterior",
9 | "clickOneBlockFirst": "por favor, haga clic en un bloque de contenido primero",
10 | "setDate": "establecer fecha",
11 | "setDateTitle": "por favor, establezca la hora para el siguiente bloque",
12 | "scheduledAt": "hora programada",
13 | "remind": "recordar",
14 | "scheduleSetSuccess": "recordatorio establecido con éxito",
15 | "schedule": "programar recordatorio",
16 | "btnAddADay": "+1 día",
17 | "addBookmark": "establecer punto de lectura",
18 | "addBookmarkWithoutENV": "establecer punto de lectura (sin entorno)",
19 | "removeBrokenCards": "limpiar todas las tarjetas rotas cleanCards",
20 | "addFlashCard": "lista de elementos individuales, crear tarjeta de lista, cancelar creación de tarjeta",
21 | "removedBrokenCards": "tarjetas rotas eliminadas: ",
22 | "thereIsNoInvalidCards": "¡no hay tarjetas inválidas!",
23 | "deleteBlocks": "eliminar bloques de contenido en masa deleteBlocks",
24 | "moveBlocks": "mover bloques de contenido en masa moveBlocks",
25 | "copyBlocks": "copiar bloques de contenido en masa copyBlocks",
26 | "deleteBlocksHelp": "ayuda para eliminar en masa: por favor, envuelva el contenido que desea procesar con dos líneas aacc1 y aacc2. aacc1 ¡Hoy hace buen tiempo1! ¡Hoy hace buen tiempo2! ... ¡Hoy hace buen tiempo3! aacc2 ",
27 | "moveBlocksHelp": "ayuda para mover y copiar en masa: por favor, envuelva el contenido con dos líneas aacc1 y aacc2. Luego inserte una línea aacc3 en la ubicación objetivo. [Documento1] aacc1 ¡Hoy hace buen tiempo1! ¡Hoy hace buen tiempo2! ... ¡Hoy hace buen tiempo3! aacc2 [Documento2] ... aacc3 ... (Documento1 y Documento2 pueden ser el mismo documento) ",
28 | "topBarTitleShowContents": "abrir página de índice/marcadores",
29 | "thereIsNoBookmark": ":no se encontraron marcadores",
30 | "showBookmarks": "ver puntos de lectura",
31 | "lookingForTheList": "buscando la lista...",
32 | "reindex": "no se puede encontrar la lista para crear tarjetas, si realmente colocó el cursor dentro de un elemento de lista y lo intentó varias veces, puede intentar reconstruir el índice.",
33 | "wait4finish": "¡espere a que finalice la operación anterior!",
34 | "bilink": "enlace bidireccional",
35 | "bilinkSelectBlock": "enlace bidireccional: seleccionar bloque",
36 | "bilinkCreateLnk": "enlace bidireccional: crear enlace de ida y vuelta",
37 | "backlink": "enlace inverso backlink",
38 | "bottombacklink": "insertar enlace inverso en la parte inferior #bottomBacklink",
39 | "bottomMention": "insertar mención en la parte inferior #bottomMention",
40 | "addPicOverlay": "agregar capa de imagen",
41 | "delCard": "eliminar la tarjeta actual al repasar delete current card",
42 | "skipCard": "omitir la tarjeta actual al repasar skip current card",
43 | "moveBlock2today": "mover contenido a la nota diaria",
44 | "previousNote": "nota anterior",
45 | "nextNote": "nota siguiente",
46 | "cardPrioritySet": "prioridad de tarjetas para documentos y subdocumentos",
47 | "setCardPriority": "establecer prioridad de tarjetas",
48 | "uncheckAll": "desmarcar todas las tareas de todo completadas en el documento actual",
49 | "delAllchecked": "eliminar todas las tareas de todo completadas en el documento actual",
50 | "refreshVirRef": "actualizar referencias virtuales",
51 | "removeDocCards": "cancelar todas las tarjetas en el documento actual removeCardsInDoc",
52 | "hotMenu": "menú rápido",
53 | "spaceRepeat": "repetición de espacio",
54 | "locateDoc": "localizar documento globalmente",
55 | "baiduAI": "enviar contenido seleccionado a ERNIE 4",
56 | "addTODOBookmark": "agregar un marcador 🚩",
57 | "deleteAllTODOBookmarks": "eliminar todos los marcadores 🚩",
58 | "txt2ref": "convertir todo el contenido separado por espacios en referencias (ignorar el contenido después de ##)",
59 | "noteBox": "captura de ideas al vuelo",
60 | "noteBoxGlobal": "captura de ideas al vuelo (global)",
61 | "gotoBlockID": "ir al ID de bloque en el portapapeles (gotoBlockID)",
62 | "deleteBookmark": "eliminar el punto de lectura del documento actual rp",
63 | "gotoBookmark": "ir al punto de lectura del documento actual rp"
64 | }
--------------------------------------------------------------------------------
/dist/i18n/fr_FR.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Chronomètre à tomates",
3 | "takeARestPlease": "😊 Prenez une pause !",
4 | "hasWorkedMinutes": "minutes écoulées",
5 | "takeARestAfterMinutes": "pause après minutes",
6 | "startCountdown": "Démarrer le compte à rebours",
7 | "cancelCountdown": "Annuler le compte à rebours",
8 | "cancelLastCountdown": "Annuler le compte à rebours précédent",
9 | "clickOneBlockFirst": "Veuillez cliquer d'abord sur un bloc de contenu",
10 | "setDate": "Définir l'heure",
11 | "setDateTitle": "Veuillez définir l'heure pour les blocs suivants",
12 | "scheduledAt": "Heure de rappel",
13 | "remind": "Rappeler",
14 | "scheduleSetSuccess": "Rappel configuré avec succès",
15 | "schedule": "Configurer un rappel",
16 | "btnAddADay": "+1 jour",
17 | "addBookmark": "Définir un point de lecture",
18 | "addBookmarkWithoutENV": "Définir un point de lecture (sans environnement)",
19 | "removeBrokenCards": "Nettoyer toutes les cartes à étiquettes invalides cleanCards",
20 | "addFlashCard": "Liste unidirectionnelle, création de cartes, annulation de la création de cartes",
21 | "removedBrokenCards": "Cartes à étiquettes invalides supprimées :",
22 | "thereIsNoInvalidCards": "Aucune carte à étiquettes invalide !",
23 | "deleteBlocks": "Supprimer en masse de gros blocs de contenu continu deleteBlocks",
24 | "moveBlocks": "Déplacer en masse de gros blocs de contenu continu moveBlocks",
25 | "copyBlocks": "Copier en masse de gros blocs de contenu continu copyBlocks",
26 | "deleteBlocksHelp": "Aide à la suppression en masse : Veuillez entourer le contenu à traiter avec deux lignes aacc1 et aacc2. aacc1 Il fait beau aujourd'hui 1 ! Il fait beau aujourd'hui 2 ! ... Il fait beau aujourd'hui 3 ! aacc2 ",
27 | "moveBlocksHelp": "Aide au déplacement et à la copie en masse : Entourez le contenu à traiter avec deux lignes aacc1 et aacc2. Insérez ensuite une ligne aacc3 à la destination. [Document 1] aacc1 Il fait beau aujourd'hui 1 ! Il fait beau aujourd'hui 2 ! ... Il fait beau aujourd'hui 3 ! aacc2 [Document 2] ... aacc3 ... (Document 1 et Document 2 peuvent être le même document) ",
28 | "topBarTitleShowContents": "Ouvrir la page de contenu/page de signets",
29 | "thereIsNoBookmark": ":Aucun signet trouvé",
30 | "showBookmarks": "Voir les points de lecture",
31 | "lookingForTheList": "Recherche de la liste…",
32 | "reindex": "Liste pour créer des cartes introuvable, si vous avez vraiment placé le curseur à l'intérieur d'un élément de liste et essayé plusieurs fois, vous pouvez essayer de reconstruire l'index.",
33 | "wait4finish": "Veuillez attendre la fin de l'opération précédente !",
34 | "bilink": "Lien bidirectionnel",
35 | "bilinkSelectBlock": "Lien bidirectionnel : Sélectionner un bloc",
36 | "bilinkCreateLnk": "Lien bidirectionnel : Créer un lien aller-retour",
37 | "backlink": "Retour en arrière backlink",
38 | "bottombacklink": "Insérer un lien arrière en bas #bottomBacklink",
39 | "bottomMention": "Insérer une mention en bas #bottomMention",
40 | "addPicOverlay": "Ajouter un calque de masquage d'image",
41 | "delCard": "Supprimer la carte actuelle lors de la révision delete current card",
42 | "skipCard": "Passer la carte actuelle lors de la révision skip current card",
43 | "moveBlock2today": "Déplacer le contenu vers la note quotidienne",
44 | "previousNote": "Note précédente",
45 | "nextNote": "Note suivante",
46 | "cardPrioritySet": "Priorité des cartes à étiquettes pour le document et les sous-documents",
47 | "setCardPriority": "Définir la priorité des cartes à étiquettes",
48 | "uncheckAll": "Décocher toutes les tâches todo terminées du document actuel",
49 | "delAllchecked": "Supprimer toutes les tâches todo terminées du document actuel",
50 | "refreshVirRef": "Actualiser les références virtuelles",
51 | "removeDocCards": "Annuler toutes les cartes à étiquettes dans le document actuel removeCardsInDoc",
52 | "hotMenu": "Menu rapide",
53 | "spaceRepeat": "Répétition espacée",
54 | "locateDoc": "Localiser un document globalement",
55 | "baiduAI": "Sélectionner le contenu pour le transmettre à Wenxin Yiyan 4",
56 | "addTODOBookmark": "Ajouter un signet 🚩",
57 | "deleteAllTODOBookmarks": "Supprimer tous les signets 🚩",
58 | "txt2ref": "Tout le contenu séparé par des espaces est converti en référence (ignore le contenu après ##)",
59 | "noteBox": "Idée flash photographiée",
60 | "noteBoxGlobal": "Idée flash photographiée (global)",
61 | "gotoBlockID": "Aller au bloc ID dans le presse-papiers (gotoBlockID)",
62 | "deleteBookmark": "Supprimer le point de lecture du document actuel rp",
63 | "gotoBookmark": "Aller au point de lecture du document actuel rp"
64 | }
--------------------------------------------------------------------------------
/dist/i18n/ja_JP.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "トマトクロック",
3 | "takeARestPlease": "😊 しばらく休憩しましょう!",
4 | "hasWorkedMinutes": "分間働いた",
5 | "takeARestAfterMinutes": "分後に休憩",
6 | "startCountdown": "カウントダウン開始",
7 | "cancelCountdown": "カウントダウンキャンセル",
8 | "cancelLastCountdown": "前回のカウントダウンをキャンセル",
9 | "clickOneBlockFirst": "まず内容ブロックをクリックしてください",
10 | "setDate": "時間設定",
11 | "setDateTitle": "以下のブロックに時間を設定してください",
12 | "scheduledAt": "リマインダー時間",
13 | "remind": "リマインド",
14 | "scheduleSetSuccess": "リマインダー設定成功",
15 | "schedule": "リマインダー設定",
16 | "btnAddADay": "+1日",
17 | "addBookmark": "読書ポイントを設定",
18 | "addBookmarkWithoutENV": "読書ポイントを設定(環境なし)",
19 | "removeBrokenCards": "無効なスマートカードをすべてクリーンアップ",
20 | "addFlashCard": "単一リスト、リストからカード作成、カード作成キャンセル",
21 | "removedBrokenCards": "削除された無効なスマートカード:",
22 | "thereIsNoInvalidCards": "無効なスマートカードはありません!",
23 | "deleteBlocks": "大量の連続した内容ブロックを一括削除",
24 | "moveBlocks": "大量の連続した内容ブロックを一括移動",
25 | "copyBlocks": "大量の連続した内容ブロックを一括コピー",
26 | "deleteBlocksHelp": "一括削除のヘルプ: 処理する内容をaacc1とaacc2の2行で囲んでください。 aacc1 今日はいい天気ですね1! 今日はいい天気ですね2! ... 今日はいい天気ですね3! aacc2 ",
27 | "moveBlocksHelp": "一括移動コピーのヘルプ: 処理する内容をaacc1とaacc2の2行で囲み、ターゲット位置にaacc3を挿入してください。 [ドキュメント1] aacc1 今日はいい天気ですね1! 今日はいい天気ですね2! ... 今日はいい天気ですね3! aacc2 [ドキュメント2] ... aacc3 ... (ドキュメント1とドキュメント2は同じドキュメントでも可) ",
28 | "topBarTitleShowContents": "コンテンツページ/ブックマークページを開く",
29 | "thereIsNoBookmark": ":ブックマークはありません",
30 | "showBookmarks": "読書ポイントを表示",
31 | "lookingForTheList": "リストを検索中……",
32 | "reindex": "カード作成のためのリストが見つかりませんでした。カーソルをリストの項目内に置いて何度か試した場合は、インデックスを再構築することを試みてください。",
33 | "wait4finish": "前の操作が完了するまで待ってください!",
34 | "bilink": "双方向リンク",
35 | "bilinkSelectBlock": "双方向リンク:ブロックを選択",
36 | "bilinkCreateLnk": "双方向リンク:往復リンクを作成",
37 | "backlink": "バックリンク",
38 | "bottombacklink": "バックリンクをページ下部に挿入#bottomBacklink",
39 | "bottomMention": "ページ下部にメンションを挿入#bottomMention",
40 | "addPicOverlay": "画像オーバーレイを追加",
41 | "delCard": "復習時に現在のスマートカードを削除",
42 | "skipCard": "復習時に現在のスマートカードをスキップ",
43 | "moveBlock2today": "コンテンツをデイリーノートに移動",
44 | "previousNote": "前のノート",
45 | "nextNote": "次のノート",
46 | "cardPrioritySet": "ドキュメントとサブドキュメントのスマートカードの優先順位",
47 | "setCardPriority": "スマートカードの優先順位を設定",
48 | "uncheckAll": "現在のドキュメントで完了したすべてのTODOタスクのチェックを解除",
49 | "delAllchecked": "現在のドキュメントで完了したすべてのTODOタスクを削除",
50 | "refreshVirRef": "仮想リファレンスを更新",
51 | "removeDocCards": "現在のドキュメント内のすべてのスマートカードを削除",
52 | "hotMenu": "ホットメニュー",
53 | "spaceRepeat": "スペースリピート",
54 | "locateDoc": "ドキュメントのグローバルな位置指定",
55 | "baiduAI": "選択した内容を文心一言4に送信",
56 | "addTODOBookmark": "🚩ブックマークを追加",
57 | "deleteAllTODOBookmarks": "すべての🚩ブックマークを削除",
58 | "txt2ref": "スペースで区切られたすべての内容をリファレンスに変換(##の後の内容は無視)",
59 | "noteBox": "写真での閃き",
60 | "noteBoxGlobal": "写真での閃き(グローバル)",
61 | "gotoBlockID": "クリップボードのブロックIDにジャンプ",
62 | "deleteBookmark": "現在のドキュメントの読書ポイントを削除",
63 | "gotoBookmark": "現在のドキュメントの読書ポイントにジャンプ"
64 | }
--------------------------------------------------------------------------------
/dist/i18n/zh_CHT.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "番茄鐘",
3 | "takeARestPlease": "😊休息一下吧!",
4 | "hasWorkedMinutes": "分鐘已到",
5 | "takeARestAfterMinutes": "分鐘後休息",
6 | "startCountdown": "開始計時",
7 | "cancelCountdown": "取消計時",
8 | "cancelLastCountdown": "取消上次的計時",
9 | "clickOneBlockFirst": "請先點擊一個內容塊",
10 | "setDate": "設置時間",
11 | "setDateTitle": "請為以下塊設置時間",
12 | "scheduledAt": "提醒時間",
13 | "remind": "提醒",
14 | "scheduleSetSuccess": "提醒設置成功",
15 | "schedule": "設置提醒",
16 | "btnAddADay": "+1日",
17 | "addBookmark": "設置閱讀點",
18 | "addBookmarkWithoutENV": "設置閱讀點(無環境)",
19 | "removeBrokenCards": "清理所有失效閃卡cleanCards",
20 | "addFlashCard": "單項列表、列表制卡、取消制卡",
21 | "removedBrokenCards": "已經刪除的失效閃卡:",
22 | "thereIsNoInvalidCards": "無失效閃卡!",
23 | "deleteBlocks": "批量刪除大量連續內容塊deleteBlocks",
24 | "moveBlocks": "批量移動大量連續內容塊moveBlocks",
25 | "copyBlocks": "批量複製大量連續內容塊copyBlocks",
26 | "deleteBlocksHelp": "批量刪除幫助: 請分別用 aacc1 與 aacc2 兩行把要處理的內容包裹起來。 aacc1 今天有個好天氣1! 今天有個好天氣2! ... 今天有個好天氣3! aacc2 ",
27 | "moveBlocksHelp": "批量移動複製幫助: 請分別用 aacc1 與 aacc2 兩行把要處理的內容包裹起來。再到目標位置插入一行 aacc3。 [文檔1] aacc1 今天有個好天氣1! 今天有個好天氣2! ... 今天有個好天氣3! aacc2 [文檔2] ... aacc3 ... (文檔1與文檔2可以是同一個文檔) ",
28 | "topBarTitleShowContents": "打開目錄頁/書籤頁",
29 | "thereIsNoBookmark": ":找不到任何書籤",
30 | "showBookmarks": "查看閱讀點",
31 | "lookingForTheList": "正在查找列表……",
32 | "reindex": "無法找到要制卡的列表,如果你確實是將光標放到列表的某個列表項內嘗試多次,你可以嘗試重建索引。",
33 | "wait4finish": "請等待上個操作完成!",
34 | "bilink": "雙向互鏈",
35 | "bilinkSelectBlock": "雙向互鏈:選擇塊",
36 | "bilinkCreateLnk": "雙向互鏈:創建往返鏈",
37 | "backlink": "反鏈backlink",
38 | "bottombacklink": "插入底部反鏈#bottomBacklink",
39 | "bottomMention": "插入底部提及#bottomMention",
40 | "addPicOverlay": "添加圖片遮擋層",
41 | "delCard": "複習時刪除當前閃卡delete current card",
42 | "skipCard": "複習時跳過當前閃卡skip current card",
43 | "moveBlock2today": "移動內容到 daily note",
44 | "previousNote": "上一個日誌",
45 | "nextNote": "下一個日誌",
46 | "cardPrioritySet": "文檔與子文檔閃卡優先級",
47 | "setCardPriority": "設置閃卡優先級",
48 | "uncheckAll": "取消勾選當前文檔所有已完成的todo任務",
49 | "delAllchecked": "刪除當前文檔所有已完成的todo任務",
50 | "refreshVirRef": "刷新虛擬引用",
51 | "removeDocCards": "取消當前文檔內所有閃卡removeCardsInDoc",
52 | "hotMenu": "快捷菜單",
53 | "spaceRepeat": "間隔重複",
54 | "locateDoc": "全局定位文檔",
55 | "baiduAI": "選中內容發給文心一言4",
56 | "addTODOBookmark": "添加一個🚩書籤",
57 | "deleteAllTODOBookmarks": "刪除所有🚩書籤",
58 | "txt2ref": "空格隔開的所有內容都轉為引用(忽略##後的內容)",
59 | "noteBox": "拍照閃念",
60 | "noteBoxGlobal": "拍照閃念(全局)",
61 | "gotoBlockID": "跳轉到剪貼板中的塊ID(gotoBlockID)",
62 | "deleteBookmark": "刪除當前文檔的閱讀點rp",
63 | "gotoBookmark": "跳到當前文檔的閱讀點rp"
64 | }
--------------------------------------------------------------------------------
/dist/i18n/zh_CN.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "番茄钟",
3 | "takeARestPlease": "😊休息一会儿吧!",
4 | "hasWorkedMinutes": "分钟已到",
5 | "takeARestAfterMinutes": "分钟后休息",
6 | "startCountdown": "开始计时",
7 | "cancelCountdown": "取消计时",
8 | "cancelLastCountdown": "取消上次的计时",
9 | "clickOneBlockFirst": "请先点击一个内容块",
10 | "setDate": "设置时间",
11 | "setDateTitle": "请为以下块设置时间",
12 | "scheduledAt": "提醒时间",
13 | "remind": "提醒",
14 | "scheduleSetSuccess": "提醒设置成功",
15 | "schedule": "设置提醒",
16 | "btnAddADay": "+1日",
17 | "addBookmark": "设置阅读点",
18 | "addBookmarkWithoutENV": "设置阅读点(无环境)",
19 | "removeBrokenCards": "清理所有失效闪卡cleanCards",
20 | "addFlashCard": "单项列表、列表制卡、取消制卡",
21 | "removedBrokenCards": "已经删除的失效闪卡:",
22 | "thereIsNoInvalidCards": "无失效闪卡!",
23 | "deleteBlocks": "批量删除大量连续内容块deleteBlocks",
24 | "moveBlocks": "批量移动大量连续内容块moveBlocks",
25 | "copyBlocks": "批量复制大量连续内容块copyBlocks",
26 | "deleteBlocksHelp": "批量删除帮助: 请分别用 aacc1 与 aacc2 两行把要处理的内容包裹起来。 aacc1 今天有个好天气1! 今天有个好天气2! ... 今天有个好天气3! aacc2 ",
27 | "moveBlocksHelp": "批量移动复制帮助: 请分别用 aacc1 与 aacc2 两行把要处理的内容包裹起来。再到目标位置插入一行 aacc3。 [文档1] aacc1 今天有个好天气1! 今天有个好天气2! ... 今天有个好天气3! aacc2 [文档2] ... aacc3 ... (文档1与文档2可以是同一个文档) ",
28 | "topBarTitleShowContents": "打开目录页/书签页",
29 | "thereIsNoBookmark": ":找不到任何书签",
30 | "showBookmarks": "查看阅读点",
31 | "lookingForTheList": "正在查找列表……",
32 | "reindex": "无法找到要制卡的列表,如果你确实是将光标放到列表的某个列表项内尝试多次,你可以尝试重建索引。",
33 | "wait4finish": "请等待上个操作完成!",
34 | "bilink": "双向互链",
35 | "bilinkSelectBlock": "双向互链:选择块",
36 | "bilinkCreateLnk": "双向互链:创建往返链",
37 | "backlink": "反链backlink",
38 | "bottombacklink": "插入底部反链#bottomBacklink",
39 | "bottomMention": "插入底部提及#bottomMention",
40 | "addPicOverlay": "添加图片遮挡层",
41 | "delCard": "复习时删除当前闪卡delete current card",
42 | "skipCard": "复习时跳过当前闪卡skip current card",
43 | "moveBlock2today": "移动内容到 daily note",
44 | "previousNote": "上一个日志",
45 | "nextNote": "下一个日志",
46 | "cardPrioritySet": "文档与子文档闪卡优先级",
47 | "setCardPriority": "设置闪卡优先级",
48 | "uncheckAll": "取消勾选当前文档所有已完成的todo任务",
49 | "delAllchecked": "删除当前文档所有已完成的todo任务",
50 | "refreshVirRef": "刷新虚拟引用",
51 | "removeDocCards": "取消当前文档内所有闪卡removeCardsInDoc",
52 | "hotMenu": "快捷菜单",
53 | "spaceRepeat": "间隔重复",
54 | "locateDoc": "全局定位文档",
55 | "baiduAI": "选中内容发给文心一言4",
56 | "addTODOBookmark": "添加一个🚩书签",
57 | "deleteAllTODOBookmarks": "删除所有🚩书签",
58 | "txt2ref": "空格隔开的所有内容都转为引用(忽略##后的内容)",
59 | "noteBox": "拍照闪念",
60 | "noteBoxGlobal": "拍照闪念(全局)",
61 | "gotoBlockID": "跳转到剪贴板中的块ID(gotoBlockID)",
62 | "deleteBookmark": "删除当前文档的阅读点rp",
63 | "gotoBookmark": "跳到当前文档的阅读点rp"
64 | }
--------------------------------------------------------------------------------
/dist/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/dist/icon.png
--------------------------------------------------------------------------------
/dist/plugin.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sy-tomato-plugin",
3 | "author": "IAliceBobI",
4 | "url": "https://github.com/IAliceBobI/sy-tomato-plugin",
5 | "version": "4.0.64",
6 | "minAppVersion": "3.0.3",
7 | "backends": [
8 | "all"
9 | ],
10 | "frontends": [
11 | "all"
12 | ],
13 | "displayName": {
14 | "default": "Tomato Toolbox",
15 | "zh_CN": "番茄工具箱"
16 | },
17 | "description": {
18 | "default": "Status Bar Pomodoro, Annotation, Bottom Backlinks, Sync Blocks, Block Relationship Graph, Text to Reference, Flash Idea via Camera, Reading Points, Two-Way Mutual Links, AI Knowledge Base Q&A, Streaming AI, Database Backlinks, Flashcard Priority, Document Merge, Recurring Reminders, Delete Cards During Review, Flashcard Image Cloze, Move to Today's Notes, Clean Up Invalid Flashcards",
19 | "zh_CN": "【已开源】状态栏番茄钟、移动端多选、增强折叠、批注、底部反链、同步块、思维导线、块关系图、文本转引用、拍照闪念、阅读点、双向互链、AI知识库问答、流式AI、数据库反链、闪卡优先级、文档合并、定期提醒、复习时删卡、闪卡图片挖空、移到今日笔记、清理失效闪卡"
20 | },
21 | "readme": {
22 | "default": "README.md",
23 | "zh_CN": "README_zh_CN.md"
24 | },
25 | "funding": {
26 | "openCollective": "",
27 | "patreon": "",
28 | "github": "",
29 | "custom": [
30 | "https://afdian.com/a/playerv5"
31 | ]
32 | },
33 | "keywords": [
34 | "TomatoClock",
35 | "Time management",
36 | "Productivity",
37 | "Pomodoro technique",
38 | "Timer plugin",
39 | "Task scheduling",
40 | "Focus sessions",
41 | "Work intervals",
42 | "Breaks",
43 | "Time tracking",
44 | "Timeboxing",
45 | "Time optimization",
46 | "Efficiency",
47 | "Work-life balance",
48 | "Distraction management",
49 | "Task prioritization",
50 | "Time blocking",
51 | "Time visualization",
52 | "Progress tracking",
53 | "Goal setting"
54 | ]
55 | }
56 |
--------------------------------------------------------------------------------
/dist/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/dist/preview.png
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/icon.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sy-tomato-plugin",
3 | "version": "0.0.1",
4 | "description": "",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite build --watch",
8 | "build": "vite build"
9 | },
10 | "keywords": [
11 | "TomatoClock",
12 | "Time management",
13 | "Productivity",
14 | "Pomodoro technique",
15 | "Timer plugin",
16 | "Task scheduling",
17 | "Focus sessions",
18 | "Work intervals",
19 | "Breaks",
20 | "Time tracking",
21 | "Timeboxing",
22 | "Time optimization",
23 | "Efficiency",
24 | "Work-life balance",
25 | "Distraction management",
26 | "Task prioritization",
27 | "Time blocking",
28 | "Time visualization",
29 | "Progress tracking",
30 | "Goal setting"
31 | ],
32 | "author": "IAliceBobI",
33 | "license": "MIT",
34 | "devDependencies": {
35 | "@sveltejs/vite-plugin-svelte": "^3.0.0",
36 | "@tsconfig/svelte": "^4.0.1",
37 | "@types/elliptic": "^6.4.18",
38 | "@types/fabric": "^5.3.7",
39 | "@types/node": "^20.12.7",
40 | "fast-glob": "^3.2.12",
41 | "glob": "^11.0.0",
42 | "minimist": "^1.2.8",
43 | "rollup-plugin-livereload": "^2.0.5",
44 | "sass": "^1.75.0",
45 | "siyuan": "*",
46 | "svelte": "^4.2.0",
47 | "ts-node": "^10.9.1",
48 | "typescript": "^5.4.5",
49 | "vite": "^5.0.0",
50 | "vite-plugin-static-copy": "^1.0.2",
51 | "vite-plugin-zip-pack": "^1.2.3"
52 | },
53 | "dependencies": {
54 | "@dagrejs/dagre": "^1.1.4",
55 | "@types/uuid": "^9.0.8",
56 | "@xyflow/svelte": "^0.1.23",
57 | "elliptic": "^6.5.7",
58 | "fabric": "^5.4.0",
59 | "html2canvas": "^1.4.1",
60 | "moment-timezone": "^0.5.45",
61 | "openai": "^4.49.1",
62 | "pinyin-pro": "^3.20.3",
63 | "terser": "^5.31.6",
64 | "ts-md5": "^1.3.1",
65 | "uuid": "^9.0.1"
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/plugin.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sy-tomato-plugin",
3 | "author": "IAliceBobI",
4 | "url": "https://github.com/IAliceBobI/sy-tomato-plugin",
5 | "version": "4.0.64",
6 | "minAppVersion": "3.0.3",
7 | "backends": [
8 | "all"
9 | ],
10 | "frontends": [
11 | "all"
12 | ],
13 | "displayName": {
14 | "default": "Tomato Toolbox",
15 | "zh_CN": "番茄工具箱"
16 | },
17 | "description": {
18 | "default": "Status Bar Pomodoro, Annotation, Bottom Backlinks, Sync Blocks, Block Relationship Graph, Text to Reference, Flash Idea via Camera, Reading Points, Two-Way Mutual Links, AI Knowledge Base Q&A, Streaming AI, Database Backlinks, Flashcard Priority, Document Merge, Recurring Reminders, Delete Cards During Review, Flashcard Image Cloze, Move to Today's Notes, Clean Up Invalid Flashcards",
19 | "zh_CN": "【已开源】状态栏番茄钟、移动端多选、增强折叠、批注、底部反链、同步块、思维导线、块关系图、文本转引用、拍照闪念、阅读点、双向互链、AI知识库问答、流式AI、数据库反链、闪卡优先级、文档合并、定期提醒、复习时删卡、闪卡图片挖空、移到今日笔记、清理失效闪卡"
20 | },
21 | "readme": {
22 | "default": "README.md",
23 | "zh_CN": "README_zh_CN.md"
24 | },
25 | "funding": {
26 | "openCollective": "",
27 | "patreon": "",
28 | "github": "",
29 | "custom": [
30 | "https://afdian.com/a/playerv5"
31 | ]
32 | },
33 | "keywords": [
34 | "TomatoClock",
35 | "Time management",
36 | "Productivity",
37 | "Pomodoro technique",
38 | "Timer plugin",
39 | "Task scheduling",
40 | "Focus sessions",
41 | "Work intervals",
42 | "Breaks",
43 | "Time tracking",
44 | "Timeboxing",
45 | "Time optimization",
46 | "Efficiency",
47 | "Work-life balance",
48 | "Distraction management",
49 | "Task prioritization",
50 | "Time blocking",
51 | "Time visualization",
52 | "Progress tracking",
53 | "Goal setting"
54 | ]
55 | }
56 |
--------------------------------------------------------------------------------
/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/preview.png
--------------------------------------------------------------------------------
/src/AIBox.ts:
--------------------------------------------------------------------------------
1 | import { Dialog, IEventBusMap } from "siyuan";
2 | import { getAllText, newID, } from "./libs/utils";
3 | import { events } from "./libs/Events";
4 | import { aiBoxCheckbox, aiBoxMenuShow, } from "./libs/stores";
5 | import AIBoxMenu from "./AIBoxMenu.svelte";
6 | import { DestroyManager } from "./libs/destroyer";
7 | import { tomatoI18n } from "./tomatoI18n";
8 | import { BaseTomatoPlugin } from "./libs/BaseTomatoPlugin";
9 | import { winHotkey } from "./libs/winHotkey";
10 |
11 | type TomatoMenu = IEventBusMap["click-blockicon"] & IEventBusMap["open-menu-content"];
12 | export const AIBoxHotkey = winHotkey("⌥⇧S", "人工智能2024-6-9 01:58:39", "💻", () => tomatoI18n.人工智能)
13 | class AIBox {
14 | private plugin: BaseTomatoPlugin;
15 | private dm: DestroyManager;
16 |
17 | async onload(plugin: BaseTomatoPlugin) {
18 | if (!aiBoxCheckbox.get()) return;
19 |
20 | this.plugin = plugin;
21 | this.plugin.addCommand({
22 | langKey: AIBoxHotkey.langKey,
23 | langText: AIBoxHotkey.langText(),
24 | hotkey: AIBoxHotkey.m,
25 | editorCallback: async (protyle) => {
26 | const { selected, ids } = await events.selectedDivs(protyle);
27 | const id = ids.pop();
28 | await this.ai(id, getAllText(selected));
29 | },
30 | });
31 |
32 | if (aiBoxMenuShow.get()) {
33 | this.plugin.eventBus.on("open-menu-content", ({ detail }) => {
34 | this.aiMenu(detail as any);
35 | });
36 | }
37 | }
38 |
39 | blockIconEvent(detail: IEventBusMap["click-blockicon"]) {
40 | if (!this.plugin) return;
41 | if (aiBoxMenuShow.get()) {
42 | this.aiMenu(detail as any);
43 | }
44 | }
45 |
46 | aiMenu(detail: TomatoMenu) {
47 | const menu = detail.menu;
48 | menu.addItem({
49 | label: AIBoxHotkey.langText(),
50 | iconHTML: AIBoxHotkey.icon,
51 | accelerator: AIBoxHotkey.m,
52 | click: async () => {
53 | const { selected, ids } = await events.selectedDivs(detail.protyle);
54 | const id = ids.pop();
55 | await this.ai(id, getAllText(selected));
56 | },
57 | });
58 | }
59 |
60 | private async ai(anchorID: string, text: string) {
61 | if (!anchorID) return;
62 | if (this.dm) {
63 | this.dm.run();
64 | this.dm.destroyBy();
65 | } else {
66 | this.dm?.destroyBy();
67 | this.dm = new DestroyManager();
68 | const id = newID();
69 | const dialog = new Dialog({
70 | title: AIBoxHotkey.langText(),
71 | content: `
`,
72 | width: events.isMobile ? "90vw" : "700px",
73 | height: events.isMobile ? "180svw" : null,
74 | destroyCallback: () => {
75 | this.dm?.destroyBy("1")
76 | },
77 | });
78 | const d = new AIBoxMenu({
79 | target: dialog.element.querySelector("#" + id),
80 | props: {
81 | dm: this.dm,
82 | text,
83 | anchorID,
84 | }
85 | });
86 | this.dm.add("1", () => { dialog.destroy() })
87 | this.dm.add("2", () => { d.$destroy() })
88 | this.dm.add("dm", () => { this.dm = null; })
89 | }
90 | }
91 | }
92 |
93 | export const aiBox = new AIBox();
94 |
95 |
96 |
--------------------------------------------------------------------------------
/src/AIBoxMenu.svelte:
--------------------------------------------------------------------------------
1 |
96 |
97 |
98 |
99 |
105 |
106 |
111 | {
114 | toggleAddNewPromptDiv();
115 | $aiBoxPrompts.push(newPrompt);
116 | $aiBoxPrompts = $aiBoxPrompts;
117 | aiBoxPrompts.write();
118 | }}>添加新提示词
120 |
121 |
122 | +
127 | {#each $aiBoxPrompts as item}
128 | clickBtn(event, item)}>{item}
133 | {/each}
134 |
135 |
📡直接发送(Alt+Shift+S 或者 Alt+X 或者 F10)
138 |
139 |
140 |
148 |
--------------------------------------------------------------------------------
/src/BackLinkBottomAutoRefresh.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 | ($autoRefreshChecked = !$autoRefreshChecked)}
18 | >
19 | {#if !$autoRefreshChecked}
20 | ⏹️
21 | {:else}
22 | 🔄
23 | {/if}
25 |
--------------------------------------------------------------------------------
/src/BackLinkBottomConTree.svelte:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 | {#if trees?.size > 0}
17 | {#each trees as [k, v]}
18 | {
21 | dm.destroyBy();
22 | OpenSyFile2(backLinkBottomBox.plugin, v.value.id);
23 | }}
24 | >
25 | {SPACE.repeat(depth)} [[ {k.split("|").join(" | ")} ]]
27 |
28 | {/each}
29 | {/if}
30 |
31 |
32 |
37 |
--------------------------------------------------------------------------------
/src/BackLinkBottomOnceRefresh.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 | 👋
13 |
14 |
15 |
25 |
--------------------------------------------------------------------------------
/src/BuyTomato.svelte:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 |
27 | {#if Siyuan?.user?.userName}
28 | {Siyuan?.user?.userName}
29 | {/if}
30 |
31 |
32 | {#if Siyuan?.user?.userAvatarURL}
33 |
38 | {/if}
39 |
40 |
41 |
42 |
43 | {#if isTomato}
44 | {tomatoI18n.番茄工具箱}
45 | {:else}
46 | {tomatoI18n.渐进学习}
47 | {/if}
48 |
49 | {tomatoI18n.功能特性}
50 |
51 |
52 |
53 | ¥{nextPrice}
54 | {"¥" + price + SPACE}
55 | {tomatoI18n.终身}
56 |
57 |
{tomatoI18n.将激活配置中有VIP标志的功能}
58 |
{tomatoI18n.一次付费终身使用}
59 |
{tomatoI18n.离线可用新功能可用}
60 |
{tomatoI18n.vip版本上线前打赏的金额可以双倍抵扣}
61 |
{tomatoI18n.广告语(price)}
62 |
63 |
64 |
65 | {tomatoI18n.点击复制按钮进入店铺将复制的内容发给客服}
66 |
67 |
68 |
69 | {#if $userID}
70 |
71 | ID: {$userID}
72 | {tomatoI18n.复制}
74 |
75 |
76 | {:else}
77 |
78 | {tomatoI18n.如果要激活插件请先登录思源本体的账户}
79 |
80 | {/if}
81 |
{tomatoI18n.点击打开商品}
86 |
87 |
88 |
89 |
90 |
91 |
92 | {tomatoI18n.淘宝店二维码}
93 |
94 |
95 | {tomatoI18n.可联系客服获取7天试用激活码}
96 |
97 |
98 |
99 |
160 |
--------------------------------------------------------------------------------
/src/CozeSearchBox.ts:
--------------------------------------------------------------------------------
1 | import { Dialog, IEventBusMap } from "siyuan";
2 | import { DestroyManager } from "./libs/destroyer";
3 | import { tomatoI18n } from "./tomatoI18n";
4 | import { events } from "./libs/Events";
5 | import { getAllText, newID, siyuan, } from "./libs/utils";
6 | import CozeSearchBoxMenu from "./CozeSearchBoxMenu.svelte"
7 | import { BaseTomatoPlugin } from "./libs/BaseTomatoPlugin";
8 | import { cozeSearchBoxCheckbox, cozeSearchMenuShow } from "./libs/stores";
9 | import { winHotkey } from "./libs/winHotkey";
10 |
11 | type TomatoMenu = IEventBusMap["click-blockicon"] & IEventBusMap["open-menu-content"];
12 |
13 | export const CozeSearchBoxHotkey = winHotkey("⌘⇧o", "coze 2025-1-6 11:44:34",)
14 | class CozeSearchBox {
15 | private plugin: BaseTomatoPlugin;
16 |
17 | async onload(plugin: BaseTomatoPlugin) {
18 | if (!cozeSearchBoxCheckbox.get()) return;
19 | this.plugin = plugin;
20 |
21 | this.plugin.addCommand({
22 | langKey: CozeSearchBoxHotkey.langKey,
23 | langText: "coze" + tomatoI18n.知识库问答,
24 | hotkey: CozeSearchBoxHotkey.m,
25 | callback: async () => {
26 | try {
27 | const { selected, ids } = await events.selectedDivs();
28 | const id = ids.pop();
29 | await this.semantic(id, getAllText(selected));
30 | } catch {
31 | await this.semantic("", "");
32 | }
33 | },
34 | });
35 |
36 | if (cozeSearchMenuShow.get()) {
37 | this.plugin.eventBus.on("open-menu-content", ({ detail }) => {
38 | this.aiMenu(detail as any);
39 | });
40 | }
41 | }
42 |
43 | blockIconEvent(detail: IEventBusMap["click-blockicon"]) {
44 | if (!this.plugin) return;
45 | if (cozeSearchMenuShow.get()) {
46 | this.aiMenu(detail as any);
47 | }
48 | }
49 |
50 | aiMenu(detail: TomatoMenu) {
51 | const menu = detail.menu;
52 | menu.addItem({
53 | label: "coze" + tomatoI18n.知识库问答,
54 | iconHTML: "🔍",
55 | accelerator: CozeSearchBoxHotkey.m,
56 | click: async () => {
57 | const { selected, ids } = await events.selectedDivs(detail.protyle);
58 | const id = ids.pop();
59 | await this.semantic(id, getAllText(selected));
60 | },
61 | });
62 | }
63 |
64 | private async semantic(anchorID: string, text: string) {
65 | if (events.isBrowser) {
66 | siyuan.pushMsg("can not run in browser env.");
67 | return;
68 | }
69 | const dm = new DestroyManager();
70 | const id = newID();
71 | const dialog = new Dialog({
72 | title: "coze" + tomatoI18n.知识库问答,
73 | content: `
`,
74 | width: events.isMobile ? "90vw" : "400px",
75 | height: events.isMobile ? "180svw" : null,
76 | destroyCallback: () => {
77 | dm.destroyBy("1")
78 | },
79 | });
80 | const d = new CozeSearchBoxMenu({
81 | target: dialog.element.querySelector("#" + id),
82 | props: {
83 | dm,
84 | text,
85 | anchorID,
86 | plugin: this.plugin,
87 | }
88 | });
89 | dm.add("1", () => { dialog.destroy() })
90 | dm.add("2", () => { d.$destroy() })
91 | }
92 | }
93 |
94 | export const cozeSearchBox = new CozeSearchBox();
95 |
96 |
97 |
--------------------------------------------------------------------------------
/src/CpBox.ts:
--------------------------------------------------------------------------------
1 | import { siyuan, } from "./libs/utils";
2 | import "./index.scss";
3 | import { BaseTomatoPlugin } from "./libs/BaseTomatoPlugin";
4 | import { cpBoxCheckbox } from "./libs/stores";
5 | import { tomatoI18n } from "./tomatoI18n";
6 | import { OpenSyFile2 } from "./libs/docUtils";
7 | import { events } from "./libs/Events";
8 | import { winHotkey } from "./libs/winHotkey";
9 |
10 | const LongContentOpsLock = "LongContentOpsLock";
11 |
12 | export const CpBox批量删除大量连续内容块 = winHotkey("alt+shift+;", "deleteBlocks 2025-5-10 23:28:29")
13 | export const CpBox批量移动大量连续内容块 = winHotkey("alt+shift+'", "moveBlocks 2025-5-10 23:29:52")
14 | export const CpBox批量复制大量连续内容块 = winHotkey("alt+shift+q", "copyBlocks 2025-5-10 23:31:46")
15 |
16 | class CpBox {
17 | private plugin: BaseTomatoPlugin;
18 |
19 | async onload(plugin: BaseTomatoPlugin) {
20 | if (!cpBoxCheckbox.get()) return;
21 |
22 | this.plugin = plugin;
23 | this.plugin.addCommand({
24 | langKey: CpBox批量删除大量连续内容块.langKey,
25 | langText: tomatoI18n.批量删除大量连续内容块,
26 | hotkey: CpBox批量删除大量连续内容块.m,
27 | callback: async () => {
28 | navigator.locks.request(LongContentOpsLock, { ifAvailable: true }, async (lock) => {
29 | if (lock) {
30 | await this.deleteBlocks();
31 | } else {
32 | siyuan.pushMsg(this.plugin.i18n.wait4finish);
33 | }
34 | });
35 | },
36 | });
37 | this.plugin.addCommand({
38 | langKey: CpBox批量移动大量连续内容块.langKey,
39 | langText: tomatoI18n.批量移动大量连续内容块,
40 | hotkey: CpBox批量移动大量连续内容块.m,
41 | callback: async () => {
42 | navigator.locks.request(LongContentOpsLock, { ifAvailable: true }, async (lock) => {
43 | if (lock) {
44 | await this.moveBlocks(false);
45 | } else {
46 | siyuan.pushMsg(this.plugin.i18n.wait4finish);
47 | }
48 | });
49 | },
50 | });
51 | this.plugin.addCommand({
52 | langKey: CpBox批量复制大量连续内容块.langKey,
53 | langText: tomatoI18n.批量复制大量连续内容块,
54 | hotkey: CpBox批量复制大量连续内容块.m,
55 | callback: async () => {
56 | navigator.locks.request(LongContentOpsLock, { ifAvailable: true }, async (lock) => {
57 | if (lock) {
58 | await this.moveBlocks(true);
59 | } else {
60 | siyuan.pushMsg(this.plugin.i18n.wait4finish);
61 | }
62 | });
63 | },
64 | });
65 | }
66 |
67 | private async deleteBlocks() {
68 | const protyle = events.protyle.protyle;
69 | siyuan.pushMsg(tomatoI18n.批量删除正在检查数据);
70 | await siyuan.deleteBlocksUtil();
71 | protyle.getInstance().reload(false);
72 | await siyuan.pushMsg("batch deleted!");
73 | }
74 |
75 | private async moveBlocks(ops: boolean) {
76 | const protyle = events.protyle.protyle;
77 | siyuan.pushMsg(tomatoI18n.批量复制移动正在检查数据);
78 | const blocks = await siyuan.moveBlocksUtil(ops);
79 | if (blocks?.length > 0) {
80 | await OpenSyFile2(this.plugin, blocks[blocks.length - 1].id)
81 | await OpenSyFile2(this.plugin, blocks[0].id)
82 | protyle.getInstance().reload(false);
83 | }
84 | await siyuan.pushMsg("batch moved!");
85 | }
86 | }
87 |
88 | export const cpBox = new CpBox();
89 |
--------------------------------------------------------------------------------
/src/FastNoteBox.ts:
--------------------------------------------------------------------------------
1 | import { BaseTomatoPlugin } from "./libs/BaseTomatoPlugin";
2 | import { fastNoteBoxCheckbox } from "./libs/stores";
3 | import { siyuan, } from "./libs/utils";
4 | import { tomatoI18n } from "./tomatoI18n";
5 | import { OpenSyFile2 } from "./libs/docUtils";
6 | import { events } from "./libs/Events";
7 | import { createNote, switchDraft } from "./libs/switchDraft";
8 | import { winHotkey } from "./libs/winHotkey";
9 | import { verifyKeyTomato } from "./libs/user";
10 |
11 | export const FastNoteBox创建快速笔记 = winHotkey("shift+alt+n", "创建快速笔记2024-08-06 10:35:21")
12 | export const FastNoteBox打开最后一个笔记 = winHotkey("⌘⌥N", "2024-08-06 10:35:22")
13 | export const FastNoteBox草稿切换 = winHotkey("alt+F4", "草稿切换 2024-9-13 09:32:44", "", () => tomatoI18n.草稿切换 + " · " + tomatoI18n.切换到文档背面, true)
14 |
15 | class FastNoteBox {
16 | plugin: BaseTomatoPlugin;
17 |
18 | async onload(plugin: BaseTomatoPlugin) {
19 | if (!fastNoteBoxCheckbox.get()) return;
20 | await verifyKeyTomato()
21 | this.plugin = plugin;
22 | this.plugin.addCommand({
23 | langKey: FastNoteBox创建快速笔记.langKey,
24 | langText: tomatoI18n.创建快速笔记,
25 | hotkey: FastNoteBox创建快速笔记.m,
26 | callback: () => {
27 | navigator.locks.request("FastNoteBox2024-08-06 12:38:21", { mode: "exclusive" }, async (lock) => {
28 | if (lock) await createNote(this.plugin, events.protyle?.protyle);
29 | })
30 | },
31 | });
32 | this.plugin.addCommand({
33 | langKey: FastNoteBox打开最后一个笔记.langKey,
34 | langText: tomatoI18n.打开最后一个笔记,
35 | hotkey: FastNoteBox打开最后一个笔记.m,
36 | callback: () => {
37 | this.openNote()
38 | },
39 | });
40 | this.plugin.addCommand({
41 | langKey: FastNoteBox草稿切换.langKey,
42 | langText: FastNoteBox草稿切换.langText(),
43 | hotkey: FastNoteBox草稿切换.m,
44 | callback: () => {
45 | if (FastNoteBox草稿切换.cmd()) {
46 | switchDraft(this.plugin, events.protyle?.protyle)
47 | }
48 | },
49 | });
50 | }
51 |
52 | private async openNote() {
53 | const kName: keyof (AttrType) = "custom-fastnote";
54 | const rows = await siyuan.sqlAttr(`select root_id from attributes where name="${kName}" order by value desc limit 1`)
55 | if (!rows || rows.length == 0) {
56 | siyuan.pushMsg("cannot find a fastnote")
57 | return
58 | }
59 | OpenSyFile2(this.plugin, rows[0].root_id)
60 | }
61 | }
62 |
63 | export const fastNoteBox = new FastNoteBox();
64 |
--------------------------------------------------------------------------------
/src/GraphControl.svelte:
--------------------------------------------------------------------------------
1 |
36 |
--------------------------------------------------------------------------------
/src/GraphMenu.svelte:
--------------------------------------------------------------------------------
1 |
32 |
33 |
34 |
35 |
36 |
37 |
55 |
56 |
80 |
--------------------------------------------------------------------------------
/src/GraphNode.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 | {data.text}
12 |
13 |
14 |
15 |
16 |
21 |
--------------------------------------------------------------------------------
/src/ImgBox.ts:
--------------------------------------------------------------------------------
1 | import { IEventBusMap } from "siyuan";
2 | import { events } from "./libs/Events";
3 | import { CUSTOM_RIFF_DECKS, PROTYLE_WYSIWYG_SELECT } from "./libs/gconst";
4 | import { imgBoxCheckbox, imgBoxShowMenu } from "./libs/stores";
5 | import { tomatoI18n } from "./tomatoI18n";
6 | import html2canvas from 'html2canvas';
7 | import { getAllText, siyuan, } from "./libs/utils";
8 | import { BaseTomatoPlugin } from "./libs/BaseTomatoPlugin";
9 | import { winHotkey } from "./libs/winHotkey";
10 |
11 | type TomatoMenu = IEventBusMap["click-blockicon"] & IEventBusMap["open-menu-content"];
12 |
13 | export const ImgBoxHotKey = winHotkey("⌥⇧I", "ImgBox2024-07-03 18:38:32")
14 | class ImgBox {
15 | private plugin: BaseTomatoPlugin;
16 |
17 | async onload(plugin: BaseTomatoPlugin) {
18 | if (!imgBoxCheckbox.get()) return;
19 | this.plugin = plugin;
20 | this.plugin.addCommand({
21 | langKey: ImgBoxHotKey.langKey,
22 | langText: tomatoI18n.复制为图片,
23 | hotkey: ImgBoxHotKey.m,
24 | callback: async () => {
25 | const { selected } = await events.selectedDivs();
26 | await this.copyDiv(selected)
27 | },
28 | });
29 | this.plugin.eventBus.on("open-menu-content", ({ detail }) => {
30 | this.copyDivMenu(detail as any);
31 | });
32 | }
33 |
34 | blockIconEvent(detail: IEventBusMap["click-blockicon"]) {
35 | if (!this.plugin) return;
36 | this.copyDivMenu(detail as any);
37 | }
38 |
39 | private async copyDiv(divs: HTMLElement[]) {
40 | const canvases: HTMLCanvasElement[] = [];
41 |
42 | for (const element of divs) {
43 | element.classList.remove(PROTYLE_WYSIWYG_SELECT);
44 | const txt = getAllText([element])
45 | if (!txt) continue;
46 |
47 | // Save original styles
48 | const originalColor = element.style.color;
49 | const originalBackgroundColor = element.style.backgroundColor;
50 |
51 | // element.style.setProperty('color', '#5D473B', 'important');
52 | // element.style.setProperty('background-color', '#F5F5DC', 'important');
53 | element.style.setProperty('color', '#333333', 'important');
54 | element.style.setProperty('background-color', '#F0F0F0', 'important');
55 | const custom_riff_decks = element.getAttribute(CUSTOM_RIFF_DECKS)
56 | if (custom_riff_decks) element.removeAttribute(CUSTOM_RIFF_DECKS)
57 |
58 | siyuan.pushMsg(tomatoI18n.正在复制为图片请等待)
59 | const canvas = await html2canvas(element);
60 | canvases.push(canvas);
61 |
62 | // Restore original styles
63 | element.style.color = originalColor;
64 | element.style.backgroundColor = originalBackgroundColor;
65 | if (custom_riff_decks) element.setAttribute(CUSTOM_RIFF_DECKS, custom_riff_decks)
66 | }
67 |
68 | if (canvases.length === 0) {
69 | console.error('No valid div elements found');
70 | return;
71 | }
72 |
73 | // Create a new canvas to hold the combined image
74 | const combinedCanvas = document.createElement('canvas');
75 | const ctx = combinedCanvas.getContext('2d');
76 |
77 | if (!ctx) {
78 | console.error('Could not get 2D context for combined canvas');
79 | return;
80 | }
81 |
82 | // Calculate the total width and height for the combined canvas
83 | let maxWidth = 0;
84 | let totalHeight = 0;
85 |
86 | for (const canvas of canvases) {
87 | maxWidth = Math.max(maxWidth, canvas.width);
88 | totalHeight += canvas.height;
89 | }
90 |
91 | combinedCanvas.width = maxWidth;
92 | combinedCanvas.height = totalHeight;
93 |
94 | // Draw each canvas onto the combined canvas
95 | let offsetY = 0;
96 | for (const canvas of canvases) {
97 | ctx.drawImage(canvas, 0, offsetY);
98 | offsetY += canvas.height;
99 | }
100 |
101 | // Convert the combined canvas to a Blob and copy it to the clipboard
102 | combinedCanvas.toBlob(async (blob) => {
103 | if (blob) {
104 | await navigator.clipboard.write([
105 | new ClipboardItem({ 'image/png': blob })
106 | ]);
107 | siyuan.pushMsg(tomatoI18n.复制完成)
108 | }
109 | }, 'image/png');
110 | }
111 |
112 | private copyDivMenu(detail: TomatoMenu) {
113 | if (imgBoxShowMenu.get()) {
114 | const menu = detail.menu;
115 | menu.addItem({
116 | label: tomatoI18n.复制为图片,
117 | iconHTML: "🖼️📋",
118 | accelerator: ImgBoxHotKey.m,
119 | click: async () => {
120 | const { selected } = await events.selectedDivs(detail.protyle);
121 | await this.copyDiv(selected)
122 | },
123 | });
124 | }
125 | }
126 |
127 | }
128 |
129 | export const imgBox = new ImgBox();
130 |
--------------------------------------------------------------------------------
/src/LinkBoxBar.svelte:
--------------------------------------------------------------------------------
1 |
84 |
85 |
86 |
87 |
88 | {#if syncVersion}
89 |
90 | {#if cursorPosID === originID}
91 |
92 | {/if}
93 | v{syncVersion}
94 |
95 | {/if}
96 | {#if !$linkBoxSyncBlockAuto}
97 | {tomatoI18n.保存}
100 | {/if}
101 | {tomatoI18n.全部打开}
104 | {#if syncCount < 0}
105 | {tomatoI18n.同步失败}
107 |
108 | {:else}
109 | {tomatoI18n.已在x个地方同步(syncCount)}
111 |
112 | {/if}
113 |
114 |
115 |
133 |
--------------------------------------------------------------------------------
/src/NotebookSelect.svelte:
--------------------------------------------------------------------------------
1 |
14 |
15 | {#if notebooksPromise}
16 | {#await notebooksPromise}
17 | ...waiting
18 | {:then notebooks}
19 |
20 | {tomatoI18n.笔记本}
21 | {
25 | store.save();
26 | }}
27 | >
28 |
29 | {tomatoI18n.自动}
30 |
31 | {#each notebooks as nb}
32 |
33 | [{nb.name}]
34 |
35 | {/each}
36 |
38 | {/await}
39 | {/if}
40 |
--------------------------------------------------------------------------------
/src/ReadingPoint.svelte:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 |
21 | {#each doms as { dom, row, line }}
22 | {#if row}
23 | {
26 | OpenSyFile2(plugin, row.id);
27 | dm.destroyBy();
28 | }}>{@html line}
30 | {:else}
31 | {@html dom}
32 | {/if}
33 | {/each}
34 |
35 |
--------------------------------------------------------------------------------
/src/Schedule.ts:
--------------------------------------------------------------------------------
1 | import { siyuan, } from "./libs/utils";
2 | import "./index.scss";
3 | import { events } from "./libs/Events";
4 | import { DATA_NODE_ID } from "./libs/gconst";
5 | import { findListTypeByElement } from "./libs/listUtils";
6 | import { BaseTomatoPlugin } from "./libs/BaseTomatoPlugin";
7 | import { tomatoI18n } from "./tomatoI18n";
8 | import { winHotkey } from "./libs/winHotkey";
9 |
10 | export const ScheduleCopyID = winHotkey("shift+alt+3", "copy id 2025-5-12 18:46:16", "", () => tomatoI18n.复制ID)
11 |
12 | class Schedule {
13 | private plugin: BaseTomatoPlugin;
14 |
15 | async onload(plugin: BaseTomatoPlugin) {
16 | this.plugin = plugin;
17 | this.plugin.addCommand({
18 | langKey: ScheduleCopyID.langKey,
19 | langText: ScheduleCopyID.langText(),
20 | hotkey: ScheduleCopyID.m,
21 | callback: () => {
22 | this.showScheduleDialog(events.lastBlockID);
23 | },
24 | });
25 | }
26 |
27 | private async showScheduleDialog(blockID: string) {
28 | if (!blockID) return;
29 | const { id: listID, found } = findListTypeByElement(document.querySelector(`div[data-node-id="${blockID}"]`))
30 | siyuan.getDocIDByBlockID(blockID).then((id) => {
31 | console.info(`DocID: ${id}`)
32 | console.info(`BlockID: ${blockID}`)
33 | if (listID) console.info(`ListID: ${listID}`)
34 | })
35 | await copyID(blockID);
36 | const all = document.querySelectorAll(`div[${DATA_NODE_ID}="${blockID}"]`);
37 | if (all?.length == 1) console.info(all[0])
38 | else console.info(all)
39 | if (found) console.info(found)
40 | }
41 | }
42 |
43 | export async function copyID(idMsg: string) {
44 | await navigator.clipboard.writeText(idMsg);
45 | await siyuan.pushMsg(`copy ID ${idMsg}`);
46 | }
47 |
48 | export const schedule = new Schedule();
49 |
--------------------------------------------------------------------------------
/src/TomatoClockVedio.svelte:
--------------------------------------------------------------------------------
1 |
48 |
49 |
50 |
51 | {#if !vedioID}
52 |
53 |
{tomatoClock.plugin.i18n.takeARestPlease}
54 |
55 | {:else}
56 |
57 | {/if}
58 |
59 |
60 |
72 |
--------------------------------------------------------------------------------
/src/TomatoVIP.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 | {#if codeValid}
9 |
10 | {@html icon("TomatoVIP", ICONS_SIZE)}
12 | {:else}
13 |
14 | {@html icon("VIP", ICONS_SIZE)}
16 | {/if}
17 |
--------------------------------------------------------------------------------
/src/constants.ts:
--------------------------------------------------------------------------------
1 | // export const STORAGE_Prog_SETTINGS = "ProgressiveLearning.json";
2 | export const STORAGE_Prog_SETTINGS = "tomato-settings.json";
3 | export const STORAGE_SETTINGS = "tomato-settings.json"; // plugin switch
4 | export const STORAGE_SCHEDULE = "schedule.json";
5 | export const STORAGE_TOMATO_TIME = "tomato-time.json";
6 | export const STORAGE_AUTO_BK = "auto_bk";
7 | export const STORAGE_INSERT_HEADING = "insert_heading";
8 | export const ATTR_PIC_OVERLAY = "custom-attr-pic-overlay";
9 | export const OVERLAY_DIV = "overlayDiv";
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/i18n/empty.drawio:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/i18n/empty.xmind:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IAliceBobI/sy-tomato-plugin/b7e107023b808fa16e9233e40f40696c2ba11c20/src/i18n/empty.xmind
--------------------------------------------------------------------------------
/src/i18n/en_US.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Tomato Timer",
3 | "takeARestPlease": "😊 Take a break!",
4 | "hasWorkedMinutes": "Minutes up",
5 | "takeARestAfterMinutes": "Take a break after",
6 | "startCountdown": "Start countdown",
7 | "cancelCountdown": "Cancel countdown",
8 | "cancelLastCountdown": "Cancel last countdown",
9 | "clickOneBlockFirst": "Please click on a block first",
10 | "setDate": "Set time",
11 | "setDateTitle": "Please set time for the following block",
12 | "scheduledAt": "Reminder time",
13 | "remind": "Remind",
14 | "scheduleSetSuccess": "Reminder set successfully",
15 | "schedule": "Set reminder",
16 | "btnAddADay": "+1 Day",
17 | "addBookmark": "Set reading point",
18 | "addBookmarkWithoutENV": "Set reading point (no environment)",
19 | "removeBrokenCards": "Clean all invalid flashcards cleanCards",
20 | "addFlashCard": "Single list, list card making, cancel card making",
21 | "removedBrokenCards": "Deleted invalid flashcards:",
22 | "thereIsNoInvalidCards": "No invalid flashcards!",
23 | "deleteBlocks": "Batch delete large continuous content blocks deleteBlocks",
24 | "moveBlocks": "Batch move large continuous content blocks moveBlocks",
25 | "copyBlocks": "Batch copy large continuous content blocks copyBlocks",
26 | "deleteBlocksHelp": "Batch delete help: Please wrap the content to be processed with two lines aacc1 and aacc2. aacc1 Today has good weather1! Today has good weather2! ... Today has good weather3! aacc2 ",
27 | "moveBlocksHelp": "Batch move copy help: Please wrap the content to be processed with two lines aacc1 and aacc2. Then insert a line aacc3 at the target location. [Document1] aacc1 Today has good weather1! Today has good weather2! ... Today has good weather3! aacc2 [Document2] ... aacc3 ... (Document1 and Document2 can be the same document) ",
28 | "topBarTitleShowContents": "Open contents/bookmark page",
29 | "thereIsNoBookmark": "No bookmarks found",
30 | "showBookmarks": "View reading points",
31 | "lookingForTheList": "Searching for the list...",
32 | "reindex": "Unable to find the list to make cards. If you have indeed placed the cursor within a list item and tried multiple times, you can try rebuilding the index.",
33 | "wait4finish": "Please wait for the previous operation to finish!",
34 | "bilink": "Bidirectional link",
35 | "bilinkSelectBlock": "Bidirectional link: Select block",
36 | "bilinkCreateLnk": "Bidirectional link: Create reciprocal link",
37 | "backlink": "Backlink",
38 | "bottombacklink": "Insert bottom backlink #bottomBacklink",
39 | "bottomMention": "Insert bottom mention #bottomMention",
40 | "addPicOverlay": "Add picture overlay",
41 | "delCard": "Delete current flashcard during review",
42 | "skipCard": "Skip current flashcard during review",
43 | "moveBlock2today": "Move content to daily note",
44 | "previousNote": "Previous note",
45 | "nextNote": "Next note",
46 | "cardPrioritySet": "Document and sub-document flashcard priority",
47 | "setCardPriority": "Set flashcard priority",
48 | "uncheckAll": "Uncheck all completed todo tasks in the current document",
49 | "delAllchecked": "Delete all completed todo tasks in the current document",
50 | "refreshVirRef": "Refresh virtual reference",
51 | "removeDocCards": "Remove all flashcards in the current document",
52 | "hotMenu": "Quick menu",
53 | "spaceRepeat": "Spaced repetition",
54 | "locateDoc": "Global locate document",
55 | "baiduAI": "Send selected content to ERNIE Bot 4",
56 | "addTODOBookmark": "Add a 🚩 bookmark",
57 | "deleteAllTODOBookmarks": "Delete all 🚩 bookmarks",
58 | "txt2ref": "All content separated by spaces is converted to references (ignore content after ##)",
59 | "noteBox": "Capture fleeting thoughts",
60 | "noteBoxGlobal": "Capture fleeting thoughts (global)",
61 | "gotoBlockID": "Jump to block ID in clipboard (gotoBlockID)",
62 | "deleteBookmark": "Delete reading point in current document",
63 | "gotoBookmark": "Jump to reading point in current document"
64 | }
--------------------------------------------------------------------------------
/src/i18n/es_ES.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Temporizador de tomate",
3 | "takeARestPlease": "😊 Por favor, descansa un momento!",
4 | "hasWorkedMinutes": "minutos terminados",
5 | "takeARestAfterMinutes": "descansar después de minutos",
6 | "startCountdown": "iniciar cuenta regresiva",
7 | "cancelCountdown": "cancelar cuenta regresiva",
8 | "cancelLastCountdown": "cancelar la cuenta regresiva anterior",
9 | "clickOneBlockFirst": "por favor, haga clic en un bloque de contenido primero",
10 | "setDate": "establecer fecha",
11 | "setDateTitle": "por favor, establezca la hora para el siguiente bloque",
12 | "scheduledAt": "hora programada",
13 | "remind": "recordar",
14 | "scheduleSetSuccess": "recordatorio establecido con éxito",
15 | "schedule": "programar recordatorio",
16 | "btnAddADay": "+1 día",
17 | "addBookmark": "establecer punto de lectura",
18 | "addBookmarkWithoutENV": "establecer punto de lectura (sin entorno)",
19 | "removeBrokenCards": "limpiar todas las tarjetas rotas cleanCards",
20 | "addFlashCard": "lista de elementos individuales, crear tarjeta de lista, cancelar creación de tarjeta",
21 | "removedBrokenCards": "tarjetas rotas eliminadas: ",
22 | "thereIsNoInvalidCards": "¡no hay tarjetas inválidas!",
23 | "deleteBlocks": "eliminar bloques de contenido en masa deleteBlocks",
24 | "moveBlocks": "mover bloques de contenido en masa moveBlocks",
25 | "copyBlocks": "copiar bloques de contenido en masa copyBlocks",
26 | "deleteBlocksHelp": "ayuda para eliminar en masa: por favor, envuelva el contenido que desea procesar con dos líneas aacc1 y aacc2. aacc1 ¡Hoy hace buen tiempo1! ¡Hoy hace buen tiempo2! ... ¡Hoy hace buen tiempo3! aacc2 ",
27 | "moveBlocksHelp": "ayuda para mover y copiar en masa: por favor, envuelva el contenido con dos líneas aacc1 y aacc2. Luego inserte una línea aacc3 en la ubicación objetivo. [Documento1] aacc1 ¡Hoy hace buen tiempo1! ¡Hoy hace buen tiempo2! ... ¡Hoy hace buen tiempo3! aacc2 [Documento2] ... aacc3 ... (Documento1 y Documento2 pueden ser el mismo documento) ",
28 | "topBarTitleShowContents": "abrir página de índice/marcadores",
29 | "thereIsNoBookmark": ":no se encontraron marcadores",
30 | "showBookmarks": "ver puntos de lectura",
31 | "lookingForTheList": "buscando la lista...",
32 | "reindex": "no se puede encontrar la lista para crear tarjetas, si realmente colocó el cursor dentro de un elemento de lista y lo intentó varias veces, puede intentar reconstruir el índice.",
33 | "wait4finish": "¡espere a que finalice la operación anterior!",
34 | "bilink": "enlace bidireccional",
35 | "bilinkSelectBlock": "enlace bidireccional: seleccionar bloque",
36 | "bilinkCreateLnk": "enlace bidireccional: crear enlace de ida y vuelta",
37 | "backlink": "enlace inverso backlink",
38 | "bottombacklink": "insertar enlace inverso en la parte inferior #bottomBacklink",
39 | "bottomMention": "insertar mención en la parte inferior #bottomMention",
40 | "addPicOverlay": "agregar capa de imagen",
41 | "delCard": "eliminar la tarjeta actual al repasar delete current card",
42 | "skipCard": "omitir la tarjeta actual al repasar skip current card",
43 | "moveBlock2today": "mover contenido a la nota diaria",
44 | "previousNote": "nota anterior",
45 | "nextNote": "nota siguiente",
46 | "cardPrioritySet": "prioridad de tarjetas para documentos y subdocumentos",
47 | "setCardPriority": "establecer prioridad de tarjetas",
48 | "uncheckAll": "desmarcar todas las tareas de todo completadas en el documento actual",
49 | "delAllchecked": "eliminar todas las tareas de todo completadas en el documento actual",
50 | "refreshVirRef": "actualizar referencias virtuales",
51 | "removeDocCards": "cancelar todas las tarjetas en el documento actual removeCardsInDoc",
52 | "hotMenu": "menú rápido",
53 | "spaceRepeat": "repetición de espacio",
54 | "locateDoc": "localizar documento globalmente",
55 | "baiduAI": "enviar contenido seleccionado a ERNIE 4",
56 | "addTODOBookmark": "agregar un marcador 🚩",
57 | "deleteAllTODOBookmarks": "eliminar todos los marcadores 🚩",
58 | "txt2ref": "convertir todo el contenido separado por espacios en referencias (ignorar el contenido después de ##)",
59 | "noteBox": "captura de ideas al vuelo",
60 | "noteBoxGlobal": "captura de ideas al vuelo (global)",
61 | "gotoBlockID": "ir al ID de bloque en el portapapeles (gotoBlockID)",
62 | "deleteBookmark": "eliminar el punto de lectura del documento actual rp",
63 | "gotoBookmark": "ir al punto de lectura del documento actual rp"
64 | }
--------------------------------------------------------------------------------
/src/i18n/fr_FR.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Chronomètre à tomates",
3 | "takeARestPlease": "😊 Prenez une pause !",
4 | "hasWorkedMinutes": "minutes écoulées",
5 | "takeARestAfterMinutes": "pause après minutes",
6 | "startCountdown": "Démarrer le compte à rebours",
7 | "cancelCountdown": "Annuler le compte à rebours",
8 | "cancelLastCountdown": "Annuler le compte à rebours précédent",
9 | "clickOneBlockFirst": "Veuillez cliquer d'abord sur un bloc de contenu",
10 | "setDate": "Définir l'heure",
11 | "setDateTitle": "Veuillez définir l'heure pour les blocs suivants",
12 | "scheduledAt": "Heure de rappel",
13 | "remind": "Rappeler",
14 | "scheduleSetSuccess": "Rappel configuré avec succès",
15 | "schedule": "Configurer un rappel",
16 | "btnAddADay": "+1 jour",
17 | "addBookmark": "Définir un point de lecture",
18 | "addBookmarkWithoutENV": "Définir un point de lecture (sans environnement)",
19 | "removeBrokenCards": "Nettoyer toutes les cartes à étiquettes invalides cleanCards",
20 | "addFlashCard": "Liste unidirectionnelle, création de cartes, annulation de la création de cartes",
21 | "removedBrokenCards": "Cartes à étiquettes invalides supprimées :",
22 | "thereIsNoInvalidCards": "Aucune carte à étiquettes invalide !",
23 | "deleteBlocks": "Supprimer en masse de gros blocs de contenu continu deleteBlocks",
24 | "moveBlocks": "Déplacer en masse de gros blocs de contenu continu moveBlocks",
25 | "copyBlocks": "Copier en masse de gros blocs de contenu continu copyBlocks",
26 | "deleteBlocksHelp": "Aide à la suppression en masse : Veuillez entourer le contenu à traiter avec deux lignes aacc1 et aacc2. aacc1 Il fait beau aujourd'hui 1 ! Il fait beau aujourd'hui 2 ! ... Il fait beau aujourd'hui 3 ! aacc2 ",
27 | "moveBlocksHelp": "Aide au déplacement et à la copie en masse : Entourez le contenu à traiter avec deux lignes aacc1 et aacc2. Insérez ensuite une ligne aacc3 à la destination. [Document 1] aacc1 Il fait beau aujourd'hui 1 ! Il fait beau aujourd'hui 2 ! ... Il fait beau aujourd'hui 3 ! aacc2 [Document 2] ... aacc3 ... (Document 1 et Document 2 peuvent être le même document) ",
28 | "topBarTitleShowContents": "Ouvrir la page de contenu/page de signets",
29 | "thereIsNoBookmark": ":Aucun signet trouvé",
30 | "showBookmarks": "Voir les points de lecture",
31 | "lookingForTheList": "Recherche de la liste…",
32 | "reindex": "Liste pour créer des cartes introuvable, si vous avez vraiment placé le curseur à l'intérieur d'un élément de liste et essayé plusieurs fois, vous pouvez essayer de reconstruire l'index.",
33 | "wait4finish": "Veuillez attendre la fin de l'opération précédente !",
34 | "bilink": "Lien bidirectionnel",
35 | "bilinkSelectBlock": "Lien bidirectionnel : Sélectionner un bloc",
36 | "bilinkCreateLnk": "Lien bidirectionnel : Créer un lien aller-retour",
37 | "backlink": "Retour en arrière backlink",
38 | "bottombacklink": "Insérer un lien arrière en bas #bottomBacklink",
39 | "bottomMention": "Insérer une mention en bas #bottomMention",
40 | "addPicOverlay": "Ajouter un calque de masquage d'image",
41 | "delCard": "Supprimer la carte actuelle lors de la révision delete current card",
42 | "skipCard": "Passer la carte actuelle lors de la révision skip current card",
43 | "moveBlock2today": "Déplacer le contenu vers la note quotidienne",
44 | "previousNote": "Note précédente",
45 | "nextNote": "Note suivante",
46 | "cardPrioritySet": "Priorité des cartes à étiquettes pour le document et les sous-documents",
47 | "setCardPriority": "Définir la priorité des cartes à étiquettes",
48 | "uncheckAll": "Décocher toutes les tâches todo terminées du document actuel",
49 | "delAllchecked": "Supprimer toutes les tâches todo terminées du document actuel",
50 | "refreshVirRef": "Actualiser les références virtuelles",
51 | "removeDocCards": "Annuler toutes les cartes à étiquettes dans le document actuel removeCardsInDoc",
52 | "hotMenu": "Menu rapide",
53 | "spaceRepeat": "Répétition espacée",
54 | "locateDoc": "Localiser un document globalement",
55 | "baiduAI": "Sélectionner le contenu pour le transmettre à Wenxin Yiyan 4",
56 | "addTODOBookmark": "Ajouter un signet 🚩",
57 | "deleteAllTODOBookmarks": "Supprimer tous les signets 🚩",
58 | "txt2ref": "Tout le contenu séparé par des espaces est converti en référence (ignore le contenu après ##)",
59 | "noteBox": "Idée flash photographiée",
60 | "noteBoxGlobal": "Idée flash photographiée (global)",
61 | "gotoBlockID": "Aller au bloc ID dans le presse-papiers (gotoBlockID)",
62 | "deleteBookmark": "Supprimer le point de lecture du document actuel rp",
63 | "gotoBookmark": "Aller au point de lecture du document actuel rp"
64 | }
--------------------------------------------------------------------------------
/src/i18n/ja_JP.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "トマトクロック",
3 | "takeARestPlease": "😊 しばらく休憩しましょう!",
4 | "hasWorkedMinutes": "分間働いた",
5 | "takeARestAfterMinutes": "分後に休憩",
6 | "startCountdown": "カウントダウン開始",
7 | "cancelCountdown": "カウントダウンキャンセル",
8 | "cancelLastCountdown": "前回のカウントダウンをキャンセル",
9 | "clickOneBlockFirst": "まず内容ブロックをクリックしてください",
10 | "setDate": "時間設定",
11 | "setDateTitle": "以下のブロックに時間を設定してください",
12 | "scheduledAt": "リマインダー時間",
13 | "remind": "リマインド",
14 | "scheduleSetSuccess": "リマインダー設定成功",
15 | "schedule": "リマインダー設定",
16 | "btnAddADay": "+1日",
17 | "addBookmark": "読書ポイントを設定",
18 | "addBookmarkWithoutENV": "読書ポイントを設定(環境なし)",
19 | "removeBrokenCards": "無効なスマートカードをすべてクリーンアップ",
20 | "addFlashCard": "単一リスト、リストからカード作成、カード作成キャンセル",
21 | "removedBrokenCards": "削除された無効なスマートカード:",
22 | "thereIsNoInvalidCards": "無効なスマートカードはありません!",
23 | "deleteBlocks": "大量の連続した内容ブロックを一括削除",
24 | "moveBlocks": "大量の連続した内容ブロックを一括移動",
25 | "copyBlocks": "大量の連続した内容ブロックを一括コピー",
26 | "deleteBlocksHelp": "一括削除のヘルプ: 処理する内容をaacc1とaacc2の2行で囲んでください。 aacc1 今日はいい天気ですね1! 今日はいい天気ですね2! ... 今日はいい天気ですね3! aacc2 ",
27 | "moveBlocksHelp": "一括移動コピーのヘルプ: 処理する内容をaacc1とaacc2の2行で囲み、ターゲット位置にaacc3を挿入してください。 [ドキュメント1] aacc1 今日はいい天気ですね1! 今日はいい天気ですね2! ... 今日はいい天気ですね3! aacc2 [ドキュメント2] ... aacc3 ... (ドキュメント1とドキュメント2は同じドキュメントでも可) ",
28 | "topBarTitleShowContents": "コンテンツページ/ブックマークページを開く",
29 | "thereIsNoBookmark": ":ブックマークはありません",
30 | "showBookmarks": "読書ポイントを表示",
31 | "lookingForTheList": "リストを検索中……",
32 | "reindex": "カード作成のためのリストが見つかりませんでした。カーソルをリストの項目内に置いて何度か試した場合は、インデックスを再構築することを試みてください。",
33 | "wait4finish": "前の操作が完了するまで待ってください!",
34 | "bilink": "双方向リンク",
35 | "bilinkSelectBlock": "双方向リンク:ブロックを選択",
36 | "bilinkCreateLnk": "双方向リンク:往復リンクを作成",
37 | "backlink": "バックリンク",
38 | "bottombacklink": "バックリンクをページ下部に挿入#bottomBacklink",
39 | "bottomMention": "ページ下部にメンションを挿入#bottomMention",
40 | "addPicOverlay": "画像オーバーレイを追加",
41 | "delCard": "復習時に現在のスマートカードを削除",
42 | "skipCard": "復習時に現在のスマートカードをスキップ",
43 | "moveBlock2today": "コンテンツをデイリーノートに移動",
44 | "previousNote": "前のノート",
45 | "nextNote": "次のノート",
46 | "cardPrioritySet": "ドキュメントとサブドキュメントのスマートカードの優先順位",
47 | "setCardPriority": "スマートカードの優先順位を設定",
48 | "uncheckAll": "現在のドキュメントで完了したすべてのTODOタスクのチェックを解除",
49 | "delAllchecked": "現在のドキュメントで完了したすべてのTODOタスクを削除",
50 | "refreshVirRef": "仮想リファレンスを更新",
51 | "removeDocCards": "現在のドキュメント内のすべてのスマートカードを削除",
52 | "hotMenu": "ホットメニュー",
53 | "spaceRepeat": "スペースリピート",
54 | "locateDoc": "ドキュメントのグローバルな位置指定",
55 | "baiduAI": "選択した内容を文心一言4に送信",
56 | "addTODOBookmark": "🚩ブックマークを追加",
57 | "deleteAllTODOBookmarks": "すべての🚩ブックマークを削除",
58 | "txt2ref": "スペースで区切られたすべての内容をリファレンスに変換(##の後の内容は無視)",
59 | "noteBox": "写真での閃き",
60 | "noteBoxGlobal": "写真での閃き(グローバル)",
61 | "gotoBlockID": "クリップボードのブロックIDにジャンプ",
62 | "deleteBookmark": "現在のドキュメントの読書ポイントを削除",
63 | "gotoBookmark": "現在のドキュメントの読書ポイントにジャンプ"
64 | }
--------------------------------------------------------------------------------
/src/i18n/zh_CHT.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "番茄鐘",
3 | "takeARestPlease": "😊休息一下吧!",
4 | "hasWorkedMinutes": "分鐘已到",
5 | "takeARestAfterMinutes": "分鐘後休息",
6 | "startCountdown": "開始計時",
7 | "cancelCountdown": "取消計時",
8 | "cancelLastCountdown": "取消上次的計時",
9 | "clickOneBlockFirst": "請先點擊一個內容塊",
10 | "setDate": "設置時間",
11 | "setDateTitle": "請為以下塊設置時間",
12 | "scheduledAt": "提醒時間",
13 | "remind": "提醒",
14 | "scheduleSetSuccess": "提醒設置成功",
15 | "schedule": "設置提醒",
16 | "btnAddADay": "+1日",
17 | "addBookmark": "設置閱讀點",
18 | "addBookmarkWithoutENV": "設置閱讀點(無環境)",
19 | "removeBrokenCards": "清理所有失效閃卡cleanCards",
20 | "addFlashCard": "單項列表、列表制卡、取消制卡",
21 | "removedBrokenCards": "已經刪除的失效閃卡:",
22 | "thereIsNoInvalidCards": "無失效閃卡!",
23 | "deleteBlocks": "批量刪除大量連續內容塊deleteBlocks",
24 | "moveBlocks": "批量移動大量連續內容塊moveBlocks",
25 | "copyBlocks": "批量複製大量連續內容塊copyBlocks",
26 | "deleteBlocksHelp": "批量刪除幫助: 請分別用 aacc1 與 aacc2 兩行把要處理的內容包裹起來。 aacc1 今天有個好天氣1! 今天有個好天氣2! ... 今天有個好天氣3! aacc2 ",
27 | "moveBlocksHelp": "批量移動複製幫助: 請分別用 aacc1 與 aacc2 兩行把要處理的內容包裹起來。再到目標位置插入一行 aacc3。 [文檔1] aacc1 今天有個好天氣1! 今天有個好天氣2! ... 今天有個好天氣3! aacc2 [文檔2] ... aacc3 ... (文檔1與文檔2可以是同一個文檔) ",
28 | "topBarTitleShowContents": "打開目錄頁/書籤頁",
29 | "thereIsNoBookmark": ":找不到任何書籤",
30 | "showBookmarks": "查看閱讀點",
31 | "lookingForTheList": "正在查找列表……",
32 | "reindex": "無法找到要制卡的列表,如果你確實是將光標放到列表的某個列表項內嘗試多次,你可以嘗試重建索引。",
33 | "wait4finish": "請等待上個操作完成!",
34 | "bilink": "雙向互鏈",
35 | "bilinkSelectBlock": "雙向互鏈:選擇塊",
36 | "bilinkCreateLnk": "雙向互鏈:創建往返鏈",
37 | "backlink": "反鏈backlink",
38 | "bottombacklink": "插入底部反鏈#bottomBacklink",
39 | "bottomMention": "插入底部提及#bottomMention",
40 | "addPicOverlay": "添加圖片遮擋層",
41 | "delCard": "複習時刪除當前閃卡delete current card",
42 | "skipCard": "複習時跳過當前閃卡skip current card",
43 | "moveBlock2today": "移動內容到 daily note",
44 | "previousNote": "上一個日誌",
45 | "nextNote": "下一個日誌",
46 | "cardPrioritySet": "文檔與子文檔閃卡優先級",
47 | "setCardPriority": "設置閃卡優先級",
48 | "uncheckAll": "取消勾選當前文檔所有已完成的todo任務",
49 | "delAllchecked": "刪除當前文檔所有已完成的todo任務",
50 | "refreshVirRef": "刷新虛擬引用",
51 | "removeDocCards": "取消當前文檔內所有閃卡removeCardsInDoc",
52 | "hotMenu": "快捷菜單",
53 | "spaceRepeat": "間隔重複",
54 | "locateDoc": "全局定位文檔",
55 | "baiduAI": "選中內容發給文心一言4",
56 | "addTODOBookmark": "添加一個🚩書籤",
57 | "deleteAllTODOBookmarks": "刪除所有🚩書籤",
58 | "txt2ref": "空格隔開的所有內容都轉為引用(忽略##後的內容)",
59 | "noteBox": "拍照閃念",
60 | "noteBoxGlobal": "拍照閃念(全局)",
61 | "gotoBlockID": "跳轉到剪貼板中的塊ID(gotoBlockID)",
62 | "deleteBookmark": "刪除當前文檔的閱讀點rp",
63 | "gotoBookmark": "跳到當前文檔的閱讀點rp"
64 | }
--------------------------------------------------------------------------------
/src/i18n/zh_CN.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "番茄钟",
3 | "takeARestPlease": "😊休息一会儿吧!",
4 | "hasWorkedMinutes": "分钟已到",
5 | "takeARestAfterMinutes": "分钟后休息",
6 | "startCountdown": "开始计时",
7 | "cancelCountdown": "取消计时",
8 | "cancelLastCountdown": "取消上次的计时",
9 | "clickOneBlockFirst": "请先点击一个内容块",
10 | "setDate": "设置时间",
11 | "setDateTitle": "请为以下块设置时间",
12 | "scheduledAt": "提醒时间",
13 | "remind": "提醒",
14 | "scheduleSetSuccess": "提醒设置成功",
15 | "schedule": "设置提醒",
16 | "btnAddADay": "+1日",
17 | "addBookmark": "设置阅读点",
18 | "addBookmarkWithoutENV": "设置阅读点(无环境)",
19 | "removeBrokenCards": "清理所有失效闪卡cleanCards",
20 | "addFlashCard": "单项列表、列表制卡、取消制卡",
21 | "removedBrokenCards": "已经删除的失效闪卡:",
22 | "thereIsNoInvalidCards": "无失效闪卡!",
23 | "deleteBlocks": "批量删除大量连续内容块deleteBlocks",
24 | "moveBlocks": "批量移动大量连续内容块moveBlocks",
25 | "copyBlocks": "批量复制大量连续内容块copyBlocks",
26 | "deleteBlocksHelp": "批量删除帮助: 请分别用 aacc1 与 aacc2 两行把要处理的内容包裹起来。 aacc1 今天有个好天气1! 今天有个好天气2! ... 今天有个好天气3! aacc2 ",
27 | "moveBlocksHelp": "批量移动复制帮助: 请分别用 aacc1 与 aacc2 两行把要处理的内容包裹起来。再到目标位置插入一行 aacc3。 [文档1] aacc1 今天有个好天气1! 今天有个好天气2! ... 今天有个好天气3! aacc2 [文档2] ... aacc3 ... (文档1与文档2可以是同一个文档) ",
28 | "topBarTitleShowContents": "打开目录页/书签页",
29 | "thereIsNoBookmark": ":找不到任何书签",
30 | "showBookmarks": "查看阅读点",
31 | "lookingForTheList": "正在查找列表……",
32 | "reindex": "无法找到要制卡的列表,如果你确实是将光标放到列表的某个列表项内尝试多次,你可以尝试重建索引。",
33 | "wait4finish": "请等待上个操作完成!",
34 | "bilink": "双向互链",
35 | "bilinkSelectBlock": "双向互链:选择块",
36 | "bilinkCreateLnk": "双向互链:创建往返链",
37 | "backlink": "反链backlink",
38 | "bottombacklink": "插入底部反链#bottomBacklink",
39 | "bottomMention": "插入底部提及#bottomMention",
40 | "addPicOverlay": "添加图片遮挡层",
41 | "delCard": "复习时删除当前闪卡delete current card",
42 | "skipCard": "复习时跳过当前闪卡skip current card",
43 | "moveBlock2today": "移动内容到 daily note",
44 | "previousNote": "上一个日志",
45 | "nextNote": "下一个日志",
46 | "cardPrioritySet": "文档与子文档闪卡优先级",
47 | "setCardPriority": "设置闪卡优先级",
48 | "uncheckAll": "取消勾选当前文档所有已完成的todo任务",
49 | "delAllchecked": "删除当前文档所有已完成的todo任务",
50 | "refreshVirRef": "刷新虚拟引用",
51 | "removeDocCards": "取消当前文档内所有闪卡removeCardsInDoc",
52 | "hotMenu": "快捷菜单",
53 | "spaceRepeat": "间隔重复",
54 | "locateDoc": "全局定位文档",
55 | "baiduAI": "选中内容发给文心一言4",
56 | "addTODOBookmark": "添加一个🚩书签",
57 | "deleteAllTODOBookmarks": "删除所有🚩书签",
58 | "txt2ref": "空格隔开的所有内容都转为引用(忽略##后的内容)",
59 | "noteBox": "拍照闪念",
60 | "noteBoxGlobal": "拍照闪念(全局)",
61 | "gotoBlockID": "跳转到剪贴板中的块ID(gotoBlockID)",
62 | "deleteBookmark": "删除当前文档的阅读点rp",
63 | "gotoBookmark": "跳到当前文档的阅读点rp"
64 | }
--------------------------------------------------------------------------------
/src/icons.ts:
--------------------------------------------------------------------------------
1 | export const ICONS = `
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | `;
33 |
--------------------------------------------------------------------------------
/src/index.scss:
--------------------------------------------------------------------------------
1 | .protyle-wysiwyg div[custom-staticlink],
2 | .protyle-wysiwyg div[custom-tomato-readingpoint] {
3 | opacity: 0.65;
4 | // transform: scale(0.65);
5 | // transform-origin: 0 0;
6 | color: var(--b3-font-color8);
7 | background-color: var(--b3-font-background5);
8 | }
9 |
10 | .protyle-wysiwyg div[data-subtype="h6"][fold="1"][custom-comment-heading="1"] {
11 | opacity: 0.7 !important;
12 | font-size: 0.7em !important;
13 | }
14 |
15 | .protyle-wysiwyg div[custom-card-priority-stop] {
16 | opacity: 0.3;
17 | }
18 |
19 | .protyle-wysiwyg div[custom-card-priority-stop]::after {
20 | content: attr(custom-card-priority-stop);
21 | color: var(--b3-font-color5);
22 | }
23 |
24 | // ai response
25 | .protyle-wysiwyg div[custom-ai-response] {
26 | color: var(--b3-font-color5);
27 | background-color: var(--b3-font-background5);
28 | }
29 |
30 | // super list
31 | .protyle-wysiwyg div[custom-super-list] {
32 | border-bottom: 1px groove var(--b3-font-color5);
33 | }
34 |
35 | // bottom link
36 | .protyle-wysiwyg div[custom-lnk-bottom] {
37 | border: 1px solid var(--b3-font-color5);
38 | box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
39 | }
40 |
41 | .buttom-bk-borderedDiv {
42 | border: 1px solid var(--b3-theme-surface-lighter);
43 | }
44 |
45 | .protyle-wysiwyg div[custom-sync-block-id] {
46 | border: 1px dashed var(--b3-font-color5);
47 | }
48 |
49 | // readonly
50 | .protyle-wysiwyg div[custom-tomato-readonly]::before {
51 | content: attr(memo);
52 | }
53 |
54 | // settings search
55 | .tomato-highlight {
56 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
57 | font-size: x-large;
58 | color: var(--b3-font-color2);
59 | }
60 |
61 | .protyle-wysiwyg div[custom-tomato-readonly] {
62 | color: var(--b3-font-color4);
63 | background-color: var(--b3-font-background4);
64 |
65 | div:not(.protyle-attr) {
66 | display: none;
67 | }
68 |
69 | div.protyle-attr::after {
70 | content: "🔒";
71 | }
72 | }
73 |
74 | .protyle-wysiwyg div[custom-tomato-line-blur] {
75 | filter: blur(5px);
76 | opacity: 0.7;
77 | }
78 |
79 | .protyle-wysiwyg div[custom-tomato-line-blur]:hover {
80 | filter: none;
81 | opacity: 1;
82 | }
83 |
84 | // bi ref links
85 | .protyle-wysiwyg {
86 | div[custom-tomato-reflink]::after {
87 | content: attr(custom-tomato-reflink);
88 | font-size: small;
89 | }
90 |
91 | div[custom-tomato-reflink] {
92 | display: flex;
93 | }
94 | }
95 |
96 | // super block as card
97 | .protyle-wysiwyg div[custom-super-card-box] {
98 | border: 1px solid var(--b3-font-color5);
99 | }
100 |
101 | // @keyframes tomatoFadeIn {
102 | // from {
103 | // opacity: 0.1;
104 | // color: var(--b3-font-color2);
105 | // background-color: var(--b3-font-background1);
106 | // }
107 | // to {
108 | // opacity: 1;
109 | // }
110 | // }
111 | // .tomato-cmd-box {
112 | // animation: tomatoFadeIn 0.3s forwards 3;
113 | // }
114 |
115 | .tomato-style {
116 | &__bkContainer {
117 | font-size: larger;
118 | }
119 |
120 | &__bkContent {
121 | padding-left: 30px;
122 |
123 | ul {
124 | padding-left: 20px;
125 | }
126 |
127 | ul ul {
128 | padding-left: 40px;
129 | }
130 |
131 | ul ul ul {
132 | padding-left: 60px;
133 | }
134 | }
135 |
136 | &__container {
137 | display: flex;
138 | justify-content: center;
139 | align-items: center;
140 | height: 100vh;
141 | }
142 |
143 | &__centered-text {
144 | text-align: center;
145 | font-size: 30px;
146 | }
147 |
148 | &__code {
149 | background-color: var(--b3-protyle-code-linenumber-hl);
150 | color: var(--b3-font-color5);
151 | font-size: small;
152 | padding: 2px 4px;
153 | border-radius: 4px;
154 | font-family: "Courier New", monospace;
155 | vertical-align: top;
156 | }
157 | }
158 |
159 | .tomato-mind-wire-content {
160 | border: 0.3px dashed var(--b3-font-color5);
161 | }
162 |
163 | #tomato-mind-wire-svg-container {
164 | position: absolute;
165 | top: 0;
166 | left: 0;
167 | width: 100%;
168 | height: 100%;
169 | pointer-events: none;
170 | z-index: 1;
171 | }
172 |
173 | .tomato-mind-wire-connector {
174 | stroke-width: 0.7;
175 | stroke-dasharray: 10;
176 | }
177 |
178 | .tomato-mind-wire-connector-animation {
179 | stroke-width: 0.7;
180 | stroke-dasharray: 10;
181 | animation: tomato-mind-wire-flow 2s linear infinite;
182 | }
183 |
184 | @keyframes tomato-mind-wire-flow {
185 | from {
186 | stroke-dashoffset: 10;
187 | }
188 |
189 | to {
190 | stroke-dashoffset: 0;
191 | }
192 | }
--------------------------------------------------------------------------------
/src/libs/BaseTomatoPlugin.ts:
--------------------------------------------------------------------------------
1 | import { Plugin } from "siyuan";
2 | import { newID } from "./utils";
3 |
4 | export class BaseTomatoPlugin extends Plugin {
5 | constructor(options: any) {
6 | super(options)
7 | }
8 | loadProgStore: (p: BaseTomatoPlugin) => void;
9 | loadStore: (p: BaseTomatoPlugin) => void;
10 | id = newID();
11 | taskCfg: Promise;
12 | settingCfg: TomatoSettings;
13 | pluginSpec: PluginSpec;
14 | readonly global: TomatoGlobal = globalThis as any;
15 | initCfg() {
16 | const cfg = this.global?.tomato_zZmqus5PtYRi?.pluginConfig;
17 | if (cfg != null) {
18 | console.debug("load cfg from global: pluginID: " + this.id);
19 | this.settingCfg = cfg;
20 | if (this.loadStore) this.loadStore(this);
21 | return true;
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/src/libs/DialogText.ts:
--------------------------------------------------------------------------------
1 | import { newID } from "./utils";
2 | import DialogTextSv from "./DialogTextSv.svelte";
3 | import { Dialog } from "siyuan";
4 | import { DestroyManager } from "./destroyer";
5 | import { events } from "./Events";
6 |
7 | export class DialogTextArea {
8 | private title: string;
9 | private defaultValue: string;
10 |
11 | constructor(title: string, defaultValue: string, callback: (s: string) => Promise) {
12 | this.title = title;
13 | this.defaultValue = defaultValue;
14 | this.open(callback);
15 | }
16 |
17 | private open(callback: Func) {
18 | const id = newID();
19 | const dm = new DestroyManager();
20 |
21 | const dialog = new Dialog({
22 | title: this.title,
23 | content: `
`,
24 | width: events.isMobile ? "90vw" : null,
25 | height: events.isMobile ? null : null,
26 | destroyCallback() {
27 | dm.destroyBy("dialog");
28 | },
29 | });
30 | dm.add("dialog", () => dialog.destroy());
31 |
32 | const svelte = new DialogTextSv({
33 | target: dialog.element.querySelector("#" + id),
34 | props: {
35 | dm,
36 | callback,
37 | defaultValue: this.defaultValue,
38 | alwaysConfirm: true,
39 | useTextArea: true,
40 | }
41 | });
42 | dm.add("svelte", () => svelte.$destroy());
43 | }
44 | }
45 |
46 | export class DialogText {
47 | private title: string;
48 | private defaultValue: string;
49 | private description: string;
50 | private alwaysConfirm: boolean;
51 |
52 | constructor(title: string, defaultValue: string, callback: Func, alwaysConfirm = false, description = "") {
53 | this.alwaysConfirm = alwaysConfirm;
54 | this.title = title;
55 | this.defaultValue = defaultValue;
56 | this.description = description;
57 | this.open(callback);
58 | }
59 |
60 | private open(callback: Func) {
61 | const id = newID();
62 | const dm = new DestroyManager();
63 |
64 | const dialog = new Dialog({
65 | title: this.title,
66 | content: `
`,
67 | width: events.isMobile ? "90vw" : null,
68 | height: events.isMobile ? null : null,
69 | destroyCallback() {
70 | dm.destroyBy("dialog");
71 | },
72 | });
73 | dm.add("dialog", () => dialog.destroy());
74 |
75 | const svelte = new DialogTextSv({
76 | target: dialog.element.querySelector("#" + id),
77 | props: {
78 | dm,
79 | callback,
80 | defaultValue: this.defaultValue,
81 | alwaysConfirm: this.alwaysConfirm,
82 | description: this.description,
83 | }
84 | });
85 | dm.add("svelte", () => svelte.$destroy());
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/libs/DialogTextSv.svelte:
--------------------------------------------------------------------------------
1 |
47 |
48 |
49 |
50 |
51 |
52 | {description}
53 |
54 | {#if useTextArea}
55 |
74 | {:else}
75 |
input.select()}
78 | placeholder={defaultValue}
79 | type="text"
80 | class="b3-text-field"
81 | bind:value={inputText}
82 | on:keypress={(event) => {
83 | if (event instanceof KeyboardEvent) {
84 | if (event.key === "Enter") {
85 | btnClick();
86 | }
87 | }
88 | }}
89 | />
90 |
Enter
91 | {/if}
92 |
93 |
94 |
95 |
105 |
--------------------------------------------------------------------------------
/src/libs/EnumUtils.ts:
--------------------------------------------------------------------------------
1 | export type EnumItem = { key: string, value: string, idx: number, text: string }
2 | /**
3 | * enum must be like:
4 | * enum InsertPlace {
5 | here = "0#当前位置",
6 | dailynote = "1#今日笔记",
7 | subdoc = "2#子文档",
8 | }
9 | */
10 | export class EnumUtils {
11 | private enumType: any;
12 | private _map: Map;
13 | public get map(): Map {
14 | return this._map;
15 | }
16 | private set map(value: Map) {
17 | this._map = value;
18 | }
19 | constructor(enumType: any) {
20 | this.enumType = enumType;
21 | this.map = Object.keys(this.enumType).map(key => {
22 | const value: string = enumType[key];
23 | const parts = value.split("#");
24 | return {
25 | key,
26 | value,
27 | idx: Number(parts[0]),
28 | text: parts[1],
29 | };
30 | }).reduce((m, i) => {
31 | m.set(i.idx, i);
32 | return m;
33 | }, new Map());
34 | }
35 |
36 | getItem(enumValue: string) {
37 | const parts = enumValue.split("#");
38 | return this.map.get(Number(parts[0]));
39 | }
40 | }
41 |
42 | export function getAllEnumValues(enumObj: T): T[keyof T][] {
43 | return Object.keys(enumObj)
44 | .map(key => enumObj[key as keyof typeof enumObj]);
45 | }
--------------------------------------------------------------------------------
/src/libs/SelectionML.ts:
--------------------------------------------------------------------------------
1 | import { events } from "./Events";
2 | import { PROTYLE_WYSIWYG_SELECT } from "./gconst";
3 | import { getAttribute } from "./utils";
4 |
5 | export class SelectionML {
6 | private selected: HTMLElement[] = [];
7 | private topElement: HTMLElement;
8 | private bottomElement: HTMLElement;
9 | private firstElement: HTMLElement;
10 | private trace: string[] = [];
11 |
12 | constructor(s: Awaited>) {
13 | this.selected = s.selected;
14 | this.firstElement = this.selected.at(0);
15 | this.topElement = this.selected.at(0);
16 | this.bottomElement = this.selected.at(-1);
17 | }
18 |
19 | private traceElement(e: Element) {
20 | e.classList.add(PROTYLE_WYSIWYG_SELECT);
21 | const id: string = getAttribute(e, "data-node-id");
22 | if (!this.trace.includes(id)) this.trace.push(id);
23 | }
24 |
25 | private untraceElement(e: Element) {
26 | const id: string = getAttribute(e, "data-node-id");
27 | const idx = this.trace.findIndex(i => i == id)
28 | if (idx >= 0) {
29 | this.trace.splice(idx, 1)
30 | }
31 | }
32 |
33 | private unselect(d: Element) {
34 | d.querySelectorAll("." + PROTYLE_WYSIWYG_SELECT).forEach((e) => {
35 | e.classList.remove(PROTYLE_WYSIWYG_SELECT);
36 | this.untraceElement(e);
37 | });
38 | }
39 |
40 | private scrollIntoView(d: Element) {
41 | d.scrollIntoView({
42 | behavior: "smooth",
43 | block: "center",
44 | });
45 | }
46 |
47 | private selectDivUp(d: Element) {
48 | const id = getAttribute(d, "data-node-id");
49 | if (id) {
50 | this.scrollIntoView(d);
51 | this.topElement = d as HTMLElement;
52 | this.traceElement(this.topElement)
53 | return true;
54 | }
55 | }
56 |
57 | selectUp() {
58 | if (this.trace.length == 0) this.traceElement(this.topElement)
59 | if (!this.selectDivUp(this.topElement.previousElementSibling)) {
60 | if (this.selectDivUp(this.topElement.parentElement)) {
61 | this.bottomElement = this.topElement;
62 | this.unselect(this.topElement.parentElement);
63 | this.traceElement(this.topElement)
64 | }
65 | }
66 | }
67 |
68 | private selectDivDown(d: Element) {
69 | const id = getAttribute(d, "data-node-id");
70 | if (id) {
71 | this.scrollIntoView(d);
72 | this.bottomElement = d as HTMLElement;
73 | this.traceElement(this.bottomElement)
74 | return true;
75 | }
76 | }
77 |
78 | selectDown() {
79 | if (this.trace.length == 0) this.traceElement(this.bottomElement)
80 | if (!this.selectDivDown(this.bottomElement.nextElementSibling)) {
81 | if (this.selectDivDown(this.bottomElement.parentElement)) {
82 | this.topElement = this.bottomElement;
83 | this.unselect(this.bottomElement.parentElement);
84 | this.traceElement(this.bottomElement)
85 | }
86 | }
87 | }
88 |
89 | cancelLast() {
90 | const id = this.trace.pop();
91 | if (id) {
92 | const e = document.querySelector(`div[data-node-id="${id}"]`)
93 | if (e) {
94 | this.scrollIntoView(e);
95 | e.classList.remove(PROTYLE_WYSIWYG_SELECT)
96 | }
97 | }
98 |
99 | if (this.trace.length == 0) {
100 | this.topElement = this.firstElement
101 | this.bottomElement = this.firstElement
102 | this.scrollIntoView(this.firstElement);
103 | this.traceElement(this.firstElement);
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/libs/bookmark.ts:
--------------------------------------------------------------------------------
1 | import { Plugin } from "siyuan";
2 | import { siyuan } from "./utils";
3 | import { READINGPOINT } from "./gconst";
4 | import { tomatoI18n } from "../tomatoI18n";
5 | import { OpenSyFile2 } from "./docUtils";
6 |
7 | export async function gotoBookmark(docID: string, plugin: Plugin) {
8 | const rows = await siyuan.sqlAttr(`select * from attributes where name='${READINGPOINT}' and root_id='${docID}'`);
9 | for (const row of rows) {
10 | await OpenSyFile2(plugin, row.block_id);
11 | break;
12 | }
13 | if (rows.length == 0) await siyuan.pushMsg(tomatoI18n.当前文档无书签, 2000);
14 | }
15 |
16 | export async function removeReadingPoint(docID: string) {
17 | const rows = await siyuan.sqlAttr(`select * from attributes where name='${READINGPOINT}' and root_id='${docID}'`);
18 | await siyuan.deleteBlocks(rows.map(row => row.block_id));
19 | await siyuan.removeRiffCards(rows.map(row => row.block_id));
20 | }
21 |
22 | export async function rmTodoBookmark(docID: string) {
23 | const rows = await siyuan.sqlAttr(`select * from attributes where name='bookmark' and value='🚩' and root_id='${docID}'`);
24 | await siyuan.batchSetBlockAttrs(rows.map(row => {
25 | return { id: row.block_id, attrs: { bookmark: "" } as AttrType };
26 | }));
27 | }
28 |
29 | export async function addTodoBookmark(ids: string[]) {
30 | for (const id of ids) {
31 | const attr = await siyuan.getBlockAttrs(id);
32 | if (attr.bookmark == "🚩")
33 | await siyuan.setBlockAttrs(id, { bookmark: "" } as AttrType);
34 | else if (!attr.bookmark)
35 | await siyuan.setBlockAttrs(id, { bookmark: "🚩" } as AttrType);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/libs/cache.ts:
--------------------------------------------------------------------------------
1 | export class MaxCache {
2 | private cache: Map = new Map();
3 | private _maxSize: number;
4 | private deleteCB?: (t: T) => void;
5 | public get maxSize(): number {
6 | return this._maxSize;
7 | }
8 | public set maxSize(value: number) {
9 | this._maxSize = value;
10 | }
11 | constructor(maxSize: number, deleteCB?: (t: T) => void) {
12 | this.maxSize = maxSize;
13 | this.deleteCB = deleteCB;
14 | }
15 | public entries() {
16 | return this.cache.entries();
17 | }
18 | /**
19 | * add `obj` to cache, drop the old one without return it.
20 | * @param key
21 | * @param obj
22 | * @returns return the new value NOT the old one
23 | */
24 | public add(key: string, obj: T): T {
25 | if (obj == null) return obj;
26 | if (this.cache.size > this.maxSize) {
27 | [...this.cache.entries()].sort((e1, e2) => {
28 | return e1[1].timestamp - e2[1].timestamp;
29 | }).slice(0, this.cache.size / 2).forEach(e => {
30 | if (this.deleteCB) this.deleteCB(e[1].obj);
31 | this.cache.delete(e[0]);
32 | });
33 | }
34 |
35 | const e = this.get(key);
36 | if (e != null)
37 | if (this.deleteCB) this.deleteCB(e);
38 | this.cache.set(key, { obj: obj, timestamp: new Date().getTime() });
39 | return obj;
40 | }
41 | /**
42 | * get or save
43 | * @param key
44 | * @param defaultValue
45 | * @returns
46 | */
47 | public get(key: string, defaultValue?: T): T {
48 | const v = this.cache.get(key);
49 | if (v == null) {
50 | return this.add(key, defaultValue);
51 | }
52 | return v.obj;
53 | }
54 | /**
55 | * get or save
56 | * @param key
57 | * @param createValue
58 | * @returns
59 | */
60 | public getOrElse(key: string, createValue: () => T): T {
61 | const v = this.cache.get(key);
62 | if (v == null) {
63 | return this.add(key, createValue());
64 | }
65 | return v.obj;
66 | }
67 | public delete(key: string) {
68 | const e = this.get(key);
69 | if (e == null) return;
70 | if (this.deleteCB) this.deleteCB(e);
71 | return this.cache.delete(key);
72 | }
73 | }
74 |
75 | type DelCB = (k: string, o: any) => void;
76 |
77 | export class WeakCache {
78 | private cache: Map>;
79 | private registry: FinalizationRegistry;
80 | private delCB?: DelCB;
81 | constructor(delCB?: DelCB) {
82 | this.delCB = delCB;
83 | this.cache = new Map();
84 | this.registry = new FinalizationRegistry((key: string) => {
85 | const value = this.cache.get(key);
86 | if (value != null) {
87 | const o = value.deref();
88 | this.delete(key, o);
89 | }
90 | });
91 | }
92 | set(key: string, o: T) {
93 | if (o == null) return;
94 | this.cache.set(key, new WeakRef(o));
95 | this.registry.register(o, key);
96 | }
97 | get(key: string, miss?: T): T {
98 | const value = this.cache.get(key);
99 | if (value != null) {
100 | const o = value.deref();
101 | if (o != null) return o;
102 | this.delete(key, null);
103 | }
104 | this.set(key, miss);
105 | return miss;
106 | }
107 | size() {
108 | return this.cache.size;
109 | }
110 | toKVList() {
111 | return [...this.entries()];
112 | }
113 | entries() {
114 | return this.cache.entries();
115 | }
116 | delete(key: string, v: T) {
117 | if (this.cache.delete(key)) {
118 | if (this.delCB) this.delCB(key, v);
119 | }
120 | }
121 | purge() {
122 | for (const [k, v] of this.entries()) {
123 | if (v?.deref() == null) this.delete(k, null);
124 | }
125 | }
126 | clear() {
127 | for (const [k, v] of this.entries()) {
128 | this.delete(k, v?.deref());
129 | }
130 | }
131 | }
132 |
133 | /// Example usage
134 | /// const myMap = new DefaultMap(0); // Default value is 0
135 | export class DefaultMap extends Map {
136 | private dv: V | (() => V);
137 |
138 | constructor(dv: V | (() => V), entries?: readonly (readonly [K, V])[] | null) {
139 | super(entries);
140 | this.dv = dv;
141 | }
142 |
143 | get(key: K): V {
144 | return this.savingGet(key, this.dv);
145 | }
146 |
147 | protected savingGet(key: K, defaultValue: V | (() => V)): V {
148 | const v = super.get(key);
149 | if (v == null) {
150 | let newValue: V;
151 | if (typeof defaultValue === 'function') {
152 | newValue = (defaultValue as () => V)();
153 | } else {
154 | newValue = defaultValue;
155 | }
156 | this.set(key, newValue);
157 | return newValue;
158 | } else {
159 | return v;
160 | }
161 | }
162 | }
--------------------------------------------------------------------------------
/src/libs/cardUtils.ts:
--------------------------------------------------------------------------------
1 | import { tomatoI18n } from "../tomatoI18n";
2 | import { events } from "./Events";
3 | import { CUSTOM_RIFF_DECKS } from "./gconst";
4 | import { getAttribute, isValidNumber, siyuan, timeUtil } from "./utils";
5 |
6 | export async function removeDocCards(docID: string) {
7 | if (!docID) return;
8 | const ids = (await siyuan.sql(`select block_id as id from attributes
9 | where name="${CUSTOM_RIFF_DECKS}"
10 | and root_id="${docID}"
11 | limit 30000
12 | `)).map(row => row.id);
13 | await siyuan.removeRiffCards(ids);
14 | }
15 |
16 | export async function doStopCards(days: string, blocks: GetCardRetBlock[], spread = false) {
17 | if (!(blocks && blocks.length)) return;
18 | const numDays = Number(days);
19 | if (isValidNumber(numDays)) {
20 | function spreadByDays(idx: number) {
21 | return (idx + 1) * (numDays / blocks.length)
22 | }
23 | await siyuan.pushMsg(tomatoI18n.开始执行)
24 | await siyuan.batchSetBlockAttrs(blocks.map((b, idx) => {
25 | const newAttrs = {} as AttrType;
26 | if (numDays <= 0) {
27 | newAttrs["custom-card-priority-stop"] = "";
28 | newAttrs.bookmark = "";
29 | } else {
30 | let datetimeStr: string;
31 | if (spread) {
32 | datetimeStr = timeUtil.dateFormat(timeUtil.now(spreadByDays(idx) * 24 * 60 * 60));
33 | } else {
34 | datetimeStr = timeUtil.dateFormat(timeUtil.now(numDays * 24 * 60 * 60));
35 | }
36 | newAttrs["custom-card-priority-stop"] = datetimeStr;
37 | newAttrs.bookmark = "🛑 Suspended Cards";
38 | }
39 | newAttrs["custom-card-priority-id"] = b.ial.id;
40 | return { id: b.ial.id, attrs: newAttrs };
41 | }));
42 |
43 | await siyuan.batchSetRiffCardsDueTimeByBlockID(blocks.map((b, idx) => {
44 | let due: string;
45 | if (spread) {
46 | due = timeUtil.getYYYYMMDDHHmmss(timeUtil.nowts(spreadByDays(idx) * 24 * 60 * 60));
47 | } else {
48 | due = timeUtil.getYYYYMMDDHHmmss(timeUtil.nowts(numDays * 24 * 60 * 60));
49 | }
50 | return {
51 | id: b.ial.id,
52 | due,
53 | };
54 | }));
55 |
56 | setTimeout(() => {
57 | events.protyleReload();
58 | }, 500);
59 | await siyuan.pushMsg(tomatoI18n.推迟x个闪卡y天(blocks.length, days));
60 | }
61 | }
62 |
63 | export function showCardAnswer() {
64 | const btnSpace = document.body.querySelector('div.card__action:not(.fn__none) > button[data-type="-1"]') as HTMLButtonElement;
65 | if (btnSpace) {
66 | btnSpace.click();
67 | return true;
68 | }
69 | return false;
70 | }
71 |
72 | export function pressSkip() {
73 | const btnSkip = document.body.querySelector('button[data-type="-3"]') as HTMLButtonElement;
74 | if (btnSkip) {
75 | btnSkip.click();
76 | return true;
77 | }
78 | return false;
79 | }
80 |
81 | export async function getIDFromCard() {
82 | const blockInDocCard = document.querySelector(`div.card__main div[data-doc-type="NodeDocument"][custom-riff-decks] > div[data-node-id]`);
83 | const subBlockID = getAttribute(blockInDocCard as any, "data-node-id");
84 | let cardID = await siyuan.getDocIDByBlockID(subBlockID);
85 |
86 | if (!cardID) {
87 | const card = document.querySelector(`div.card__main div[data-node-id][custom-riff-decks]`);
88 | cardID = getAttribute(card as any, "data-node-id");
89 | }
90 | return cardID;
91 | }
92 |
93 |
--------------------------------------------------------------------------------
/src/libs/cozeAI.ts:
--------------------------------------------------------------------------------
1 | import { tomatoI18n } from "../tomatoI18n";
2 | import { cozeSearchKnowledgeID, cozeSearchOauthTokenID } from "./stores";
3 | import { chunks, getMarkdownsByTrees, siyuan } from "./utils";
4 |
5 | async function postData(url: string, data = {}, headers = {}) {
6 | return fetch('https://api.coze.cn' + url, {
7 | method: 'POST',
8 | headers: {
9 | 'Content-Type': 'application/json',
10 | 'Authorization': 'Bearer ' + cozeSearchOauthTokenID.get(),
11 | 'Agw-Js-Conv': 'str',
12 | ...headers,
13 | },
14 | body: JSON.stringify(data),
15 | });
16 | }
17 |
18 | async function postGetJson(url: string, data = {}, headers = {}) {
19 | return postData(url, data, headers).then(r => r.json())
20 | }
21 |
22 | export async function cozeAddDocTree(docID: string, boxID = "") {
23 | const rows = await getMarkdownsByTrees([docID], boxID)
24 | let name: string;
25 | if (!boxID) {
26 | for (const r of rows) {
27 | if (r.id === docID) {
28 | name = getCozeName(r.content, r.id)
29 | break
30 | }
31 | }
32 | } else {
33 | const nb = await siyuan.getNotebookConf(boxID)
34 | name = getCozeName(nb.name, boxID)
35 | }
36 | await coze_big_file(rows, name);
37 | siyuan.pushMsg(tomatoI18n.更新所有文档成功)
38 | }
39 |
40 | export function getCozeName(name: string, id: string) {
41 | return name?.replaceAll(".", "")?.slice(0, 20) + "." + id
42 | }
43 |
44 | async function coze_big_file(rows: Block[], name: string) {
45 | await cozeDeleteFromKnowledgeByName(name);
46 | const payload: any = {
47 | dataset_id: cozeSearchKnowledgeID.get(),
48 | chunk_strategy: {
49 | remove_extra_spaces: true,
50 | remove_urls_emails: true,
51 | chunk_type: 0
52 | },
53 | format_type: 0,
54 | document_bases: [
55 | {
56 | source_info: {
57 | file_type: "md",
58 | file_base64: encodeToBase64(rows.map(r => r.markdown).join("\n\n")),
59 | },
60 | name,
61 | }
62 | ]
63 | };
64 | await postGetJson("/open_api/knowledge/document/create", payload);
65 | }
66 |
67 | export async function cozeDeleteFromKnowledgeByName(name: string) {
68 | const all = await coze_get_all_files()
69 | for (const f of all) {
70 | if (f.name === name) {
71 | await postGetJson("/open_api/knowledge/document/delete", { document_ids: [f.document_id] });
72 | }
73 | }
74 | }
75 |
76 | export async function cleanCozeDocs() {
77 | const document_ids = await coze_get_all_files().then(fs => fs.map(f => f.document_id))
78 | for (const ids of chunks(document_ids, 10)) {
79 | await postGetJson("/open_api/knowledge/document/delete", { document_ids: ids });
80 | }
81 | }
82 |
83 | // https://app.quicktype.io/?l=ts
84 | export async function coze_get_all_files(size = 100) {
85 | const all = [] as CozeDocumentInfo[]
86 | let page = 1;
87 | while (true) {
88 | const ret = await postGetJson("/open_api/knowledge/document/list", {
89 | "dataset_id": cozeSearchKnowledgeID.get(),
90 | "page": page++,
91 | size,
92 | }) as CozeListDoc;
93 | if (ret.document_infos) all.push(...ret.document_infos)
94 | if (all.length >= ret.total) break;
95 | if (ret.code != 0) break;
96 | }
97 | return all;
98 | }
99 |
100 | function encodeToBase64(str: string): string {
101 | const utf8Bytes = new TextEncoder().encode(str);
102 | const binaryArray = [];
103 | for (const byte of utf8Bytes) {
104 | binaryArray.push(String.fromCharCode(byte));
105 | }
106 | const binaryString = binaryArray.join('');
107 | return btoa(binaryString);
108 | }
109 |
--------------------------------------------------------------------------------
/src/libs/destroyer.ts:
--------------------------------------------------------------------------------
1 | export class DestroyManager {
2 | private destroied = false;
3 | private cbs = new Map();
4 | private actions: Func[] = [];
5 | private data = new Map();
6 | private showMsg: boolean;
7 | private prefix: string;
8 | constructor(showMsg = false, prefix: string = "DestroyManager") {
9 | this.prefix = prefix;
10 | this.showMsg = showMsg;
11 | }
12 | setData(key: string, value: any) {
13 | this.data.set(key, value);
14 | }
15 | getData(key: string) {
16 | return this.data.get(key) as T;
17 | }
18 | action(cb: Func) {
19 | this.actions.push(cb);
20 | }
21 | run() {
22 | this.actions.forEach(i => i());
23 | }
24 | add(name: string, cb: Func) {
25 | this.cbs.set(name.trim(), cb);
26 | }
27 | destroyBy(name: string = null) {
28 | if (!this.destroied) {
29 | this.destroied = true;
30 | const lst = [...this.cbs.entries()];
31 | if (name == null) {
32 | lst.forEach(([k, v]) => {
33 | if (this.showMsg) console.info(`[${this.prefix}] DESTROY [${k}] BY NONE`);
34 | v();
35 | });
36 | } else {
37 | name = name.trim();
38 | lst.filter(([k]) => k !== name).forEach(([k, v]) => {
39 | if (this.showMsg) console.info(`[${this.prefix}] DESTROY [${k}] BY [${name}]`);
40 | v();
41 | });
42 | }
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/src/libs/focusUtils.ts:
--------------------------------------------------------------------------------
1 | import { events } from "./Events";
2 | import { awaysExitFocusStore } from "./stores";
3 | import { isCardByUpLook } from "./utils";
4 |
5 | export function autoExitFocus() {
6 | if (awaysExitFocusStore.get() && !events.isMobile) {
7 | setTimeout(() => {
8 | findExitFocus();
9 | }, 1200);
10 | }
11 | }
12 |
13 | function findExitFocus() {
14 | const elements = document.querySelectorAll(`button[data-type="exit-focus"]`) as unknown as HTMLElement[];
15 | for (const e of elements) {
16 | if (!e.classList.contains("fn__none") && !isCardByUpLook(e)) {
17 | e.click();
18 | setTimeout(() => {
19 | doUnpin(e);
20 | }, 1000);
21 | }
22 | }
23 | }
24 |
25 | function doUnpin(e: HTMLElement) {
26 | const popover = findUpuntilPopover(e);
27 | const pinElement = popover?.querySelector("span[data-type='pin']");
28 | if (pinElement instanceof HTMLElement) {
29 | pinElement.click();
30 | }
31 | }
32 |
33 | function findUpuntilPopover(e: HTMLElement) {
34 | while (e) {
35 | if (e.classList.contains("block__popover--open")) {
36 | return e;
37 | }
38 | e = e.parentElement as HTMLElement;
39 | }
40 | }
--------------------------------------------------------------------------------
/src/libs/functional.ts:
--------------------------------------------------------------------------------
1 | export function zipNways(...arrays: T): Array<{ [K in keyof T]: T[K][number] }> {
2 | // if T[K] is string[], then T[K][number] would be string
3 | // this is equivalent to saying "give me the type of the elements inside each of the arrays in T
4 | const maxLength = Math.max(...arrays.map(arr => {
5 | if (!arr) arr = []
6 | return arr.length
7 | }));
8 | const zipped = Array.from({ length: maxLength }, (_, rowIdx) => (
9 | arrays.map((_, arrIdx) => arrays[arrIdx][rowIdx]) as { [K in keyof T]: T[K][number] }));
10 | return zipped;
11 | }
12 |
13 | export function flatChunkMap(array: any[], num: number, map: (ts: any[]) => M) {
14 | array = array.flat();
15 | const newArr: M[] = [];
16 | for (let i = 0; i < array.length; i += num) {
17 | const part = array.slice(i, i + num);
18 | if (part.length > 0) {
19 | newArr.push(map(part));
20 | }
21 | }
22 | return newArr;
23 | }
24 |
25 | export async function aFlatChunkMap(array: any[], num: number, map: (ts: any[]) => M) {
26 | return flatChunkMap(await Promise.all(array.flat()), num, map);
27 | }
28 |
29 | export function isIterable(obj: any): boolean {
30 | if (obj == null) return obj;
31 |
32 | // Check if the object has the Symbol.iterator property
33 | if (typeof obj[Symbol.iterator] === "function") {
34 | return true;
35 | }
36 |
37 | // Check if the object has the @@iterator method
38 | if (typeof obj["@@iterator"] === "function") {
39 | return true;
40 | }
41 |
42 | // If neither is present, the object is not iterable
43 | return false;
44 | }
--------------------------------------------------------------------------------
/src/libs/globalUtils.ts:
--------------------------------------------------------------------------------
1 |
2 | const prefix = "DaUaeWLNBpAf5CWCSZwZ4eEt3D78"
3 |
4 | export function getGlobal(key: string): T {
5 | return globalThis[prefix + key]
6 | }
7 |
8 | export function setGlobal(key: string, v: T): T {
9 | const old = globalThis[prefix + key];
10 | globalThis[prefix + key] = v
11 | return old;
12 | }
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/libs/hash.ts:
--------------------------------------------------------------------------------
1 | export async function sha1(message: string) {
2 | const msgBuffer = new TextEncoder().encode(message); // 编码为UTF-8
3 | const hashBuffer = await crypto.subtle.digest('SHA-1', msgBuffer); // 哈希
4 | const hashArray = Array.from(new Uint8Array(hashBuffer)); // 转换为字节数组
5 | const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); // 转换为十六进制
6 | return hashHex;
7 | }
8 |
9 | /**
10 | * 左移循环(32位)
11 | */
12 | function rotl32(x: number, r: number): number {
13 | return (x << r) | (x >>> (32 - r));
14 | }
15 |
16 | /**
17 | * 最终混音步骤
18 | */
19 | function mixmur3(h: number): number {
20 | h = h ^ (h >>> 16);
21 | h = (h * 0x85ebca6b) >>> 0;
22 | h = h ^ (h >>> 13);
23 | h = (h * 0xc2b2ae35) >>> 0;
24 | h = h ^ (h >>> 16);
25 | return h;
26 | }
27 |
28 | /**
29 | * MurmurHash3 32位版本简化实现
30 | * @param input 字符串或字节数组
31 | * @param seed 初始种子值,默认为 0x1BD11DBA
32 | * @returns 32位无符号整数哈希值
33 | */
34 | export function murmurHash3(input: string | Uint8Array, seed: number = 0x1BD11DBA): number {
35 | let data: Uint8Array;
36 |
37 | // 将字符串转换为 UTF-8 编码的 Uint8Array
38 | if (typeof input === 'string') {
39 | data = new TextEncoder().encode(input);
40 | } else {
41 | data = input;
42 | }
43 |
44 | let h1 = seed;
45 | const len = data.length;
46 | const nblocks = Math.floor(len / 4); // 每 4 字节为一块
47 |
48 | const c1 = 0xcc9e2d51;
49 | const c2 = 0x1b873593;
50 |
51 | // 处理每一块(4 字节)
52 | for (let i = 0; i < nblocks; i++) {
53 | const i4 = i * 4;
54 | let k1 = data[i4] | (data[i4 + 1] << 8) | (data[i4 + 2] << 16) | (data[i4 + 3] << 24);
55 |
56 | k1 = (k1 * c1) >>> 0;
57 | k1 = rotl32(k1, 15);
58 | k1 = (k1 * c2) >>> 0;
59 |
60 | h1 ^= k1;
61 | h1 = rotl32(h1, 13);
62 | h1 = (h1 * 5 + 0xe6546b64) >>> 0;
63 | }
64 |
65 | // 处理剩余部分(不足 4 字节)
66 | const remaining = len % 4;
67 | let k1 = 0;
68 |
69 | if (remaining > 0) {
70 | switch (remaining) {
71 | case 3:
72 | k1 ^= (data[len - 3] << 16) >>> 0;
73 | k1 ^= (data[len - 2] << 8) >>> 0;
74 | k1 ^= data[len - 1];
75 | break;
76 | case 2:
77 | k1 ^= (data[len - 2] << 8) >>> 0;
78 | k1 ^= data[len - 1];
79 | break;
80 | case 1:
81 | k1 ^= data[len - 1];
82 | break;
83 | }
84 |
85 | k1 = (k1 * c1) >>> 0;
86 | k1 = rotl32(k1, 15);
87 | k1 = (k1 * c2) >>> 0;
88 |
89 | h1 ^= k1;
90 | }
91 |
92 | // 最终混音
93 | h1 ^= len;
94 | h1 = mixmur3(h1);
95 |
96 | return h1 >>> 0; // 返回 32 位无符号整数
97 | }
--------------------------------------------------------------------------------
/src/libs/ial.ts:
--------------------------------------------------------------------------------
1 | enum ParseState {
2 | Start, // 等待开始标记
3 | InTag, // 已进入标签
4 | AttrKey, // 正在读取属性名
5 | PreValue, // 等待等号后的引号
6 | AttrValue, // 正在读取属性值
7 | AfterValue // 值读取完成后的状态
8 | }
9 |
10 | interface ParseResult {
11 | [key: string]: string;
12 | }
13 |
14 | export function parseCustomTag(s: string): ParseResult {
15 | let state: ParseState = ParseState.Start;
16 | const result: ParseResult = {};
17 | let currentKey = '';
18 | let currentValue = '';
19 | let quoteChar: '"' | "'" | null = null;
20 | const stack: string[] = []; // 用于检测标签边界
21 |
22 | for (const c of s) {
23 | switch (state) {
24 | case ParseState.Start:
25 | if (c === '{') {
26 | stack.push(c);
27 | } else if (stack.length > 0 && c === ':' && stack[stack.length - 1] === '{') {
28 | state = ParseState.InTag;
29 | stack.length = 0; // 清空栈
30 | }
31 | break;
32 |
33 | case ParseState.InTag:
34 | if (c === '}') {
35 | if (stack.length > 0 && stack[stack.length - 1] === ':') {
36 | stack.pop();
37 | return result; // 提前结束解析
38 | }
39 | } else if (c === ':') {
40 | stack.push(c);
41 | } else if (!/\s/.test(c)) { // 非空白字符开始新属性
42 | state = ParseState.AttrKey;
43 | currentKey = c;
44 | }
45 | break;
46 |
47 | case ParseState.AttrKey:
48 | if (c === '=' || /\s/.test(c)) {
49 | state = ParseState.PreValue;
50 | } else {
51 | currentKey += c;
52 | }
53 | break;
54 |
55 | case ParseState.PreValue:
56 | if (c === '"' || c === "'") {
57 | quoteChar = c;
58 | state = ParseState.AttrValue;
59 | currentValue = '';
60 | }
61 | break;
62 |
63 | case ParseState.AttrValue:
64 | if (c === quoteChar) {
65 | result[currentKey.trim()] = currentValue;
66 | currentKey = '';
67 | state = ParseState.AfterValue;
68 | } else {
69 | currentValue += c;
70 | }
71 | break;
72 |
73 | case ParseState.AfterValue:
74 | if (c === '}') {
75 | if (stack.length > 0 && stack[stack.length - 1] === ':') {
76 | return result;
77 | }
78 | } else if (/\s/.test(c)) {
79 | state = ParseState.InTag; // 准备读取下一个属性
80 | } else if (c === ':') {
81 | stack.push(c);
82 | }
83 | break;
84 | }
85 | }
86 |
87 | return result;
88 | }
89 |
90 |
91 | // 测试用例
92 | // const testStr = '{: custom-tomatoUpdated="1748308949028" id="20250526082838-qq7xnww" title="2025-05-26-周一" }';
93 | // console.log(parseCustomTag(testStr));
94 | /* 输出:
95 | {
96 | "custom-tomatoUpdated": "1748308949028",
97 | "id": "20250526082838-qq7xnww",
98 | "title": "2025-05-26-周一"
99 | }
100 | */
101 |
--------------------------------------------------------------------------------
/src/libs/keyboard.ts:
--------------------------------------------------------------------------------
1 | export function escOnElement(e: HTMLElement) {
2 | if (e) {
3 | const escEvent = new KeyboardEvent("keydown", {
4 | key: "Escape",
5 | code: "Escape",
6 | charCode: 27,
7 | keyCode: 27,
8 | view: window,
9 | bubbles: true,
10 | });
11 | e.dispatchEvent(escEvent);
12 | }
13 | }
14 |
15 | export function closeAllDialog() {
16 | document.querySelectorAll(`svg.b3-dialog__close`).forEach(e => click(e));
17 | }
18 |
19 | export function click(e: Node) {
20 | if (!e?.dispatchEvent) return;
21 | e.dispatchEvent(new MouseEvent('click', {
22 | bubbles: true,
23 | cancelable: true,
24 | view: window
25 | }));
26 | }
27 |
28 | export function preventKeyboard(event: KeyboardEvent) {
29 | // $autoRefreshChecked = false;
30 | if (event.defaultPrevented) {
31 | return;
32 | }
33 | if (event.ctrlKey && event.key == "c") {
34 | return;
35 | }
36 | event.preventDefault();
37 | }
--------------------------------------------------------------------------------
/src/libs/openAI.ts:
--------------------------------------------------------------------------------
1 | import OpenAI from 'openai';
2 | import { cancelSuperBlock, NewNodeID, Siyuan, siyuan, } from './utils';
3 | import { Stream } from 'openai/streaming';
4 | import { ChatCompletionChunk, ChatCompletionMessageParam } from 'openai/resources/chat/completions';
5 |
6 | export class OpenAIClient {
7 | private openai: OpenAI;
8 | constructor(apiKey: string, baseURL: string) {
9 | this.openai = new OpenAI({
10 | apiKey,
11 | baseURL,
12 | dangerouslyAllowBrowser: true,
13 | });
14 | }
15 |
16 | static async getModel(key: string, baseURL: string, model: string, noSup = false) {
17 | const openAI = new OpenAIClient(key, baseURL);
18 | return (prompt: string, anchorID = "") => {
19 | return openAI.do_completions(model, prompt, anchorID, noSup)
20 | };
21 | }
22 |
23 | static getOfficalModel(noSup = false) {
24 | const aiCfg = Siyuan.config?.ai?.openAI;
25 | if (aiCfg.apiBaseURL && aiCfg.apiModel) {
26 | const openAI = new OpenAIClient(aiCfg.apiKey, aiCfg.apiBaseURL);
27 | return (prompt: string, anchorID = "") => {
28 | return openAI.do_completions(Siyuan.config.ai.openAI.apiModel, prompt, anchorID, noSup)
29 | };
30 | }
31 | }
32 |
33 | private async do_completions(model: string, useInputTxt: string, anchorID: string, noSup: boolean) {
34 | let aiRespTxt: string;
35 | let texts: string[] = [];
36 | let reasoning_texts: string[] = [];
37 | let stream: Stream;
38 | const messages: ChatCompletionMessageParam[] = [{ role: "user", content: useInputTxt }]
39 | try {
40 | stream = await this.openai.chat.completions.create({
41 | model,
42 | messages,
43 | stream: true,
44 | });
45 | } catch (e) {
46 | console.error(e, messages)
47 | return;
48 | }
49 |
50 | let targetID = "";
51 | if (anchorID) {
52 | targetID = NewNodeID();
53 | await siyuan.insertBlockAfter(`{: id="${targetID}"}`, anchorID);
54 | }
55 |
56 | const write = () => {
57 | if (targetID) {
58 | return siyuan.safeUpdateBlock(
59 | targetID,
60 | `{{{row\n\n${aiRespTxt}\n\n}}}\n{: id="${targetID}" custom-ai-response="1"}`,
61 | );
62 | }
63 | }
64 |
65 | let i = 0;
66 | for await (const chunk of stream) {
67 | const delta = chunk.choices?.at(0)?.delta;
68 | texts.push(delta?.content ?? "");
69 | reasoning_texts.push((delta as any)?.reasoning_content ?? "");
70 | aiRespTxt = texts.join("").trim();
71 | if (!aiRespTxt) {
72 | const reasoning = reasoning_texts.join("").trim();
73 | if (reasoning) {
74 | aiRespTxt = reasoning;
75 | } else {
76 | aiRespTxt = `thinking ${i}...`
77 | }
78 | }
79 | if (i++ % 50 === 0) await write();
80 | }
81 |
82 | if (aiRespTxt.startsWith("")) {
83 | const div = document.createElement("div")
84 | div.innerHTML = aiRespTxt;
85 | const t = div.querySelector("think")
86 | div.removeChild(t);
87 | aiRespTxt = div.innerHTML;
88 | }
89 |
90 | await write();
91 | if (noSup && targetID) {
92 | await cancelSuperBlock(targetID);
93 | }
94 | return { targetID, aiRespTxt };
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/libs/progressive.ts:
--------------------------------------------------------------------------------
1 | import { TEMP_CONTENT } from "./gconst";
2 | import { siyuan } from "./utils";
3 |
4 | export async function getBookIDByBlock(blockID: string) {
5 | const docRow = await siyuan.getDocRowByBlockID(blockID);
6 | return getBookID(docRow?.id);
7 | }
8 |
9 | export async function getBookID(docID: string): Promise<{ bookID: string, pieceNum: number }> {
10 | const ret = { bookID: "", pieceNum: NaN } as Awaited>;
11 | if (docID) {
12 | const attrs = await siyuan.getBlockAttrs(docID);
13 | let mark = attrs["custom-progmark"];
14 | const writing = attrs["custom-book-writing"]
15 | if (mark) {
16 | mark = mark.split(TEMP_CONTENT).pop();
17 | const last = mark.split("#").pop();
18 | const parts = last.split(",");
19 | ret.bookID = parts[0];
20 | ret.pieceNum = Number(parts[1]);
21 | } else if (writing) {
22 | const parts = writing.split("#");
23 | ret.bookID = parts[0]
24 | ret.pieceNum = parseInt(parts.pop(), 10);
25 | }
26 | }
27 | return ret;
28 | }
--------------------------------------------------------------------------------
/src/libs/search.ts:
--------------------------------------------------------------------------------
1 | enum SearchEngineConditionTypeOr {
2 | include = "inc",
3 | exclude = "exc",
4 | }
5 | enum SearchEngineConditionType {
6 | include = "inc",
7 | or = "or",
8 | exclude = "exc",
9 | }
10 |
11 | type SearchEngineConditionOr = { type: SearchEngineConditionTypeOr, value: string }
12 | type SearchEngineCondition = { type: SearchEngineConditionType, value: string, values: SearchEngineConditionOr[] }
13 |
14 | export class SearchEngine {
15 | private conditions: SearchEngineCondition[] = [];
16 | private isCaseInsensitive: boolean;
17 |
18 | constructor(isCaseInsensitive: boolean) {
19 | this.isCaseInsensitive = isCaseInsensitive;
20 | }
21 |
22 | setQuery(query: string) {
23 | if (this.isCaseInsensitive)
24 | query = query.toLowerCase();
25 | this.conditions = query.trim().replace(/[!!]+/g, "!").replace(/\s+/, " ")
26 | .replace(/ ?\| ?/g, "|").split(" ").filter(c => c.length > 0)
27 | .filter(c => c != "!").filter(c => c != "|").map(c => {
28 | const con = c.split("|").map(c => c.trim()).filter(c => c.length > 0);
29 | const ret = {} as SearchEngineCondition;
30 | if (con.length == 1) {
31 | const s = con[0];
32 | if (s[0] == "!") {
33 | ret.type = SearchEngineConditionType.exclude;
34 | ret.value = s.slice(1);
35 | } else {
36 | ret.type = SearchEngineConditionType.include;
37 | ret.value = s;
38 | }
39 | } else {
40 | ret.type = SearchEngineConditionType.or;
41 | ret.values = con.map(c => {
42 | const ret = {} as SearchEngineConditionOr;
43 | if (c[0] == "!") {
44 | ret.type = SearchEngineConditionTypeOr.exclude;
45 | ret.value = c.slice(1);
46 | } else {
47 | ret.type = SearchEngineConditionTypeOr.include;
48 | ret.value = c;
49 | }
50 | return ret;
51 | });
52 | }
53 | return ret;
54 | }).filter(c => {
55 | if (c.type == SearchEngineConditionType.or) {
56 | if (c.values.length == 0) return false;
57 | }
58 | return true;
59 | });
60 | }
61 |
62 | jsonCon() {
63 | return JSON.stringify(this.conditions);
64 | }
65 |
66 | match(text: string): boolean {
67 | if (this.isCaseInsensitive)
68 | text = text.toLowerCase();
69 | for (const con of this.conditions) {
70 | if (con.type == SearchEngineConditionType.include) {
71 | if (!text.includes(con.value)) return false;
72 | } else if (con.type == SearchEngineConditionType.exclude) {
73 | if (text.includes(con.value)) return false;
74 | } else if (con.type == SearchEngineConditionType.or) {
75 | let flag = false;
76 | for (const subCon of con.values) {
77 | if (subCon.type == SearchEngineConditionTypeOr.exclude) {
78 | if (!text.includes(subCon.value)) {
79 | flag = true;
80 | break;
81 | }
82 | } else if (subCon.type == SearchEngineConditionTypeOr.include) {
83 | if (text.includes(subCon.value)) {
84 | flag = true;
85 | break;
86 | }
87 | }
88 | }
89 | if (!flag) return false;
90 | }
91 | }
92 | return true;
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/libs/switchDraft.ts:
--------------------------------------------------------------------------------
1 | import { IProtyle, Plugin } from "siyuan";
2 | import { OpenSyFile2, isReadonly } from "./docUtils";
3 | import { events } from "./Events";
4 | import { storeNoteBox_fastnote, fastNoteBoxDelAfterCreating, fastNoteBoxAdd2Flashcard } from "./stores";
5 | import { verifyKeyTomato } from "./user";
6 | import { siyuan, getContextPath, cloneCleanDiv, NewLute, NewNodeID, timeUtil } from "./utils";
7 |
8 | export async function switchDraft(plugin: Plugin, protyle: IProtyle) {
9 | const docID = protyle?.block?.rootID;
10 | if (!docID) return;
11 | const title = protyle.title?.editElement?.textContent
12 | const bt = `backside-${title}`;
13 | const attrs = await siyuan.getBlockAttrs(docID);
14 | let draftID = attrs["custom-fastdraft"];
15 | let isFastNote = !!attrs["custom-fastnote"];
16 | if (await siyuan.checkBlockExist(draftID)) {
17 | await OpenSyFile2(plugin, draftID);
18 | if (isFastNote) {
19 | //close docID
20 | // document.querySelectorAll("span.item__text").forEach((e: HTMLElement) => {
21 | // if (e.textContent === title) {
22 | // const s = e.nextElementSibling as HTMLButtonElement;
23 | // if (s?.click) s.click();
24 | // }
25 | // });
26 | } else {
27 | await siyuan.setBlockAttrs(draftID, { title: bt })
28 | }
29 | } else {
30 | const newID = await createNote(plugin, protyle, false, {
31 | "custom-fastdraft": docID,
32 | "custom-off-tomatobacklink": "1",
33 | }, bt)
34 | await siyuan.setBlockAttrs(docID, { "custom-fastdraft": newID });
35 | }
36 | }
37 |
38 |
39 | export async function createNote(plugin: Plugin, protyle: IProtyle, allowFlashcard = true, attrs: AttrType = {}, title = "") {
40 | let boxID = storeNoteBox_fastnote.getOr();
41 | if (!boxID || !protyle) return;
42 | const { selected, ids, cursorOnly } = await events.selectedDivs(protyle);
43 | if (ids.length <= 0) return;
44 |
45 | const { getPathMd } = await getContextPath(ids[0]);
46 | const path = `${getPathMd()}\n{: id="${NewNodeID()}"}\n`
47 | const lute = NewLute();
48 | const content = selected.map(d => {
49 | d = cloneCleanDiv(d).div
50 | return lute.BlockDOM2Md(d.outerHTML);
51 | });
52 | const taskRo = isReadonly(protyle)
53 | const id = await createAndOpenFastNote(boxID, plugin, attrs, title, path + content.join("\n"));
54 | if (await verifyKeyTomato() && fastNoteBoxDelAfterCreating.get() && await taskRo === "false" && !cursorOnly) await siyuan.transactions(siyuan.transDeleteBlocks(ids));
55 | if (fastNoteBoxAdd2Flashcard.get() && allowFlashcard) {
56 | setTimeout(() => {
57 | siyuan.addRiffCards([id])
58 | }, 800);
59 | }
60 | return id;
61 | }
62 |
63 | export async function createAndOpenFastNote(boxID: string, plugin: Plugin, attrs: AttrType = {}, title: string = "", md = "") {
64 | const { y, M, d, h, m, s } = timeUtil.nowYMDStrPad();
65 | if (!title) title = `f${y}-${M}-${d} ${h}:${m}:${s}`;
66 | const hpath = `/fast note/f${y}/f${y}-${M}/${title}`;
67 | const id = await siyuan.createDocWithMdIfNotExists(boxID, hpath, md, { ...attrs, "custom-fastnote": y + M + d + h + m + s });
68 | await OpenSyFile2(plugin, id);
69 | return id;
70 | }
71 |
--------------------------------------------------------------------------------
/src/libs/taobaocode.ts:
--------------------------------------------------------------------------------
1 | export const taobaoStore = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAB4CAYAAAA5ZDbSAAAAAXNSR0IArs4c6QAADvBJREFUeF7tXWmMHMUV/rp7dmZ2uWwckE2yIQ6xsAyYM2DFsRCxIQQcJSjhMBEmUjgFwWQxAsPigGOOSPgIBMUEnMgYiA1IEK6EwwERGywOoQRbNhaBYDtgRb6AsLtzdUczszPbO1tV71V1z87OUPPL2u5+VfW+9753VHXbCYIggP21rAYcC3DLYltamAW4tfEdDLDjOLEst8L6YXnhSFD5O/dv1KREcqhnRNepaMXVD7X+ytgi/XDHoNZXnUOYomMXHjIYLpgmYJk8YwGmTERxnbJg68Fl5TXUgym6qsVXRsciOxAxhcgoKs9S3k+NoStbZrsinXCNVUXL4fF09MjBYFCSFbdwmaIswGXNqAxGdl1FrEJGkMVgldWHB6HoWOVduiwhs3TRfCiv5kYgLghU/sLVkwltq2STHhzXxEXgWICH9pgswP2WIjM8ldFQxsrNrOMGgYrB1Lwb5sEmdTClZJPFqujYpMSinlEqXNJrMHmmNgkd9hhsAR5oHFE5TVMCrEM9uqUMFb8pT4/bq+vBPJRRNNyDLcADSRRlcNaDa5Ir68ED7hMpyeLWi1STRNXciMvTdTpQ3M6SST1NGZ9usyIKBmQdHEU41ZgwaSJw45JJ84MLDJduubqjDJwrRyuL5gpVJRcmWTTVd1b1fotz4cYy3fuKsrl9Z937ZPOOBYNm2C7kgm4BFsRlC7C6VuUaV5T7hs2Do1CCzrPc3SQqRnPH5LYvo8TguADmrol7X0POZFmAy/BQlQcXRGVWTp2qHK7GuipZo7JxUcZMZbpxKE9HBrVdKErMRPJV95FZtEqgTpygFlMZhwLBhKJNyigdoEzvpXRiAe7XLNcoqPtMgTJ9bkQAzO02cRcZNcbo0pGsVuUyRuU+brJF6YEKb1ETM9X4VYOiyqQoi7UA168Eo4zLAqzQUBSjliWEJhRNde2GxYOjxDpKkXEsUIe2qQSNW0+rqF6mL13Zw+bBFuChNe2IBZiqNymaqU1mZFbJbXRwFUUlK6okiyrFuNcpQ6fkiNbKTXqVyais0cEFQTVxC/CAy1iA+99D17XacANGJ96asIOICai4zR1n2D2YCuK113VqPpWiqDBBKYKbrKiAoZiH640iHXITS5n+uWMLyyRdUE1LAi7AUeYz3M+uX78e3d3dWLNmjXLohgJMNSZ06ZPyhFpNTJ8+HS+++OJwYxP7eNy+MjUwlbjVshm52dBogF977TVMmTKFWveIv24BJl7fGPEIEhNsKoApmuDWxhRoxXGo+ETJaIbrMpZUde24iVV4/WQvWjf9p+idUj4b4CCA/+46BJ/thL/57/D3fEyJjnTdHT0O7sRpcPb7EtzDpxaPYUSS11CAuTPngkndV8sOpAf3fILMgu+gsG0DkM9ypxvPfYkkvM4jkJr/EtBxgFImxXqqUo/yVu0ykDqyI1oJBRy3DNIB2N/4EjILT0XgF+IBzFCK43pIdb8A94hTpBIswP2q4QIc9HyCvp+NaTi4VcN1PaSX74Ij8eSmAFi3F03VyCadrpJCgwCZecej8MHbhj5Xn8e88ccidftbwpismzAVZ8g1Cm16j2uzoV4A+5vXou9X04c/5lJ2kUgifdMauBO/PeTOpgCYWp/qumqBqudECUThjSeQufOsKNOp27OpuY/D++YPWQBHmQTFfiLZsfeiw4PECXBu5TXIPb04in7q9mzbzC60XbDIAsyNMSIPztx1Pgrr/lQ3kKII9qbOQuqqh1sPYIoyqFpORSm1177IAJsyYTk3LX8+wujdpJEIsLPvGMAvIOjZW7UR58BOuIdOhr/lVQSf74nirMJn6+3BFuB+tSeveADesWeUSqr804uR+/PtpSvpOzfA7ZyE4NNdCHZtQ9/1xw0Fyu//k6uPf9MBTNViuhsLMnkyCjeJwc6oQ9C+bHuoHg3Qc44Lt/NIpBe9U0atWEvfPRvuQZ1Ax2h4k06G87Wj4bSlBqEa7N2B/NqHUVi3Cv77b5CI6wDMLSPDOlDhQd1HbjYoU2/Jh75rnxkWgA8Yh/bfbQW8RHn4Qh49s5JI3fAsvGNOH7qMwEfwv92lutrfugn+xr8BHfsjMfVcOKPGAf2gZ++7BPk195eMQ/ZraoBNEiXS5BU3yJroZJLlJpD47s+R/Gm5lMo+MBeFtQ+i/e73gVTHoBHzz92DwltPw3/vdQSf7x4ym8QZVyN54ZKy03++F31zJpR2rUwBDj8Xlz5VcoTXqM8JhydJUXhDAHZcwPWAfA5wAGf/g5FesgnOvgcCuQwKG1+GO+EkOPuMKk0vt6obucdvlU61Y/WAx/bdNA3+lrUW4CjAVp419WDngLFI//pN9F4+HghySF37BLwTflBm61dXI7viGrQv2wo4LvLrViO7dJacdj0gDHD++WXILr+8dQAWrYTyWu7+JEVXMi0qKTq9L9rv+bDkrcHOrcjeewn8f7+NxMyuksfmHrkZ6UUb4Ow3hmWDfTdOQfrW9dV7/R3/Qt+cb2gDzBpM4xMO3ARNpGOyDh7RAANILVgH77DjgUQ5I84sORuF9Y+V/p046yYkz1uAYPd/0Hvl15G6/hl4k2eUY+zOrcg9ObjNWFi/Gu2/31HVU9DXg94L92kdgKNs5FPND8qqTSm6eITGnTAFydlL4U44EcW9496LDwHyPWg77za0nXk1eudORvDfD5Ca9xd4R59amkqxDOqbd+KQaYUpGr6PnlmeMcCUPimdqY5NRS6TuNSrqo0p7+fQNplFV4QkO9C+eBOcg76K3KMLUMyYUzc8AwT5IhkCbgLO2Alw2vcvP1Esl7KZQeBlbj4Z6dtfH/S3nnPlZ7BkZVJFgAU4pEpjDw7JSF6wqBR/cw/NQ/6VlWi/d7uaOGqO//R1T0X6toEYDD9Azyx5i6vpAA5rQ/dEh0iTVKODsvBSTOXuJnkJtC/dAufg8ei9aCyczklwDvxK2Vm3/hP+tg1DKfqGk5CY+hO4x5yOwssrUNjwwqAsGple9MweXEuH10kBbKIT6hluPU1uNjQbwIlps5G8ckWp79x72Tiku9fAnTStpK/sQ9ch/9QiYQxOXfskvBO+X6LsnlkJdKyqNKeBYO9H6L30y8YxmAKLE6KK91COoMRquBodlOVFoejE9EuRvOi3pTibe+rOUjMjPf9luIeXX4PJrpyL/DNLawB+Hdk/diHV9Sic0eNK92XuOLOUaVd+hbf/iswd34sF4Cg7Q5QhtDTA7lEzkOp6rHrCse/GE+G/9wZS1z0F77iZJd34H/4DuVXz0XbOLXDHHzMIsGDPx1WA88/ehcQZV1WvZ/9wFfLP3W0BVmcy5av18ODieajUnFXVDYLSAb1flmnZO+kcpLpWK6dWbGNmF/8I6WLs3m8MsssuRvKy+6rP9M45HMGOLa0DMBUz4qjLwmPUUovudmERGHfchLLIQg69VxyKokdWft7U8+EddVrZQ/08gj07EOzeBuQzyL/yYPnfANzOyfA/3ozEaVcgeWF50yL4dCf6uiYi+GyXNsBRTlWqylPZRNgU3WwAo60D7b95F86osSi88wIyd8wsJUusn9eGjoez8Ldvgv/em0h868dAsr1CN8i/tBzZey9WijLZLpQBKDIK1jokZ6rJLLoiPK5ui8hza7sxuh5cdr8E3IknA54H/53nuTqBN+VspH7xyND7i+8/3X8566BfPcokSt/cBbYOwNwVi+4r7gzuMxruwYchCAoIPtoM5HrZEi3AktMQshhl5MFsOOK/sekA5m5LiRrd3OZ3Uc068aZVDr5Ta6Z0zzFPIb3rfG2Wmx1Sk6UWG15Mq7y6Qq2Z0lnLAvxFfvlMt2QiPZiyEp3tP5UsWT0tXFATvz4a1oEuWLJQxmXRahWk84Z/QwAuNh1G4Avg7ct3ST/lEAfd1gVg7q4FZY3cxjolJ+wBzfgJB532LBWjVYzI7mSNZICLCyx6cvaWU1DYvnH4XwgvfYTlSKTmFw/K8z7CYgFWvDUgtdgm+IySyhtlTjRiPVhJD5JXXOKKUVRS2Kjr3PVROU2U827CVqUJRVuAh5rRiAVYtCFAeQH39KWoZKAMihq7ma5TiafKW0W4cDuH0oPvFH1UBo0b4Hw+j0Si/03BZkKQmKsFuD/hmjFjBhYuXNgSnxXW8byGeTBVq3LjjcjAuc8KW3BEAkeVKCIGouZDeSGneycLg9z5quYtlEF9CM0CPPA6aUsBzO2ccGMwZaH1oihZy6/aqyU+D8xOZgSMQjGPSMfUVqwuE5JvF1ICLcD9nyuyAA/91gVl4Sqv53qW9WAqyApcmAsMVWpRtK2bUITBpOiPS9HcSs2EWqP0HdgY6GwXipQSRzuNWqiO8lRGxTUoLqii5g3FGDLZlDPUGntkgHUHpGI15VHclqeJHNUz3NJIBqaKCSjDZIPE/GyVEAOqTKKsmZtkmQAjYgcTORZgIs6qQLYAl7XDZQJZcsgNdbop06AyiWr+69KobDKqhoFOdhx3okSxAzcHMZFDMWXtdYr+q3ON+n6wikYtwAOwxJ3gWYB1XUJyPwWMSZ4QB/MYARyTTqpiqEycG0+42SYnFtauUUe2LkVT66NCou54okzfqFXJNQQLsPxLtbXGyE1WdRlDmmRxQYwroVBlkeExTHZ0uBkqd80mtE0Bw52jtiFQ7yZxFy2iBxEwMnkWYL1NC5EeheHGAqz/v4m2hAdTCYJuslJPJuAqnG31Bsd9qUYHd2yT+0Rhy6gOVoFEZaMW4KEaiJJFU3kJG2BuJkwBTHmZiQFUnhku2UpPifgfR+uun1pzNaehOlkWYPWZLFGtqguWyf0W4Jg8it0ximk8LtgjAmBZmRRHSdSIPje37x6FykUGRYGurKHrSdEWYPU5NKpJRIXH2hxEqw6mOi8s4RFpi9u1oSxclIypvEzHMFWyRc0fCjQ29XJPcdbTg6nFUMBYgMsa4lYo1oNDL5xbD9Z8+55btFNea9LJoWTqMgnVxTPxKF0qD3sudz5CeqcomlKeqA6kJsSVGZdsC3C/JnUVIQMqrkRBZQiUEVGMEtdauQkc1avmhgyK4YYkv43YTeLufVqAeVynMp6GbPhbgMvAcbtkFMxsgClB9nrzaaCuZ7KaTx2tN+P/A6SnKqeIy5s+AAAAAElFTkSuQmCC";
2 |
3 |
--------------------------------------------------------------------------------
/src/libs/text11.ts:
--------------------------------------------------------------------------------
1 | import { TomatoI18nABCMAX } from "./gconst";
2 |
3 | export abstract class TomatoI18nABC11 extends TomatoI18nABCMAX {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/src/libs/timer.ts:
--------------------------------------------------------------------------------
1 | export function measureTime(func: Func, name = "Function"): number {
2 | const start = performance.now();
3 | func();
4 | const end = performance.now();
5 | const elapsedTime = end - start
6 | console.info(`${name} took ${elapsedTime} milliseconds to run.`);
7 | return elapsedTime;
8 | }
9 |
10 | // console.time('myFunction');
11 | // myFunction();
12 | // console.timeEnd('myFunction');
13 |
14 | /**
15 | const timer = new Timer();
16 | myFunction();
17 | const elapsedTime = timer.stop();
18 | */
19 | export class Timer {
20 | private startTime: number;
21 | private name: string;
22 |
23 | constructor(name = "Function") {
24 | this.startTime = performance.now();
25 | this.name = name;
26 | }
27 |
28 | public stop(): number {
29 | const endTime = performance.now();
30 | const elapsedTime = endTime - this.startTime
31 | console.info(`${this.name} took ${elapsedTime} milliseconds to run.`);
32 | return elapsedTime;
33 | }
34 | }
--------------------------------------------------------------------------------
/src/libs/tools.ts:
--------------------------------------------------------------------------------
1 | import { siyuan } from "./utils";
2 |
3 | export function calcAge(age: string, blockID: string) {
4 | const { years, months, days } = calculateAge(age);
5 | siyuan.insertBlockAfter(`${years}岁${months}月${days}天`, blockID)
6 | }
7 |
8 | function calculateAge(birthday: string): { years: number, months: number, days: number } {
9 | // Parse the birthday string into Date object
10 | const birthDate = new Date(`${birthday.substring(0, 4)}-${birthday.substring(4, 6)}-${birthday.substring(6, 8)}`);
11 |
12 | // Check if the birthDate is valid
13 | if (isNaN(birthDate.getTime())) {
14 | throw new Error('Invalid birthday format');
15 | }
16 |
17 | // Get today's date
18 | const today = new Date();
19 |
20 | // Calculate the difference in years, months, and days
21 | let years = today.getFullYear() - birthDate.getFullYear();
22 | let months = today.getMonth() - birthDate.getMonth();
23 | let days = today.getDate() - birthDate.getDate();
24 |
25 | // Adjust the values if necessary
26 | if (days < 0) {
27 | // Borrow a month
28 | months -= 1;
29 | // Get the last day of the previous month
30 | const lastDayOfPrevMonth = new Date(today.getFullYear(), today.getMonth(), 0).getDate();
31 | days += lastDayOfPrevMonth;
32 | }
33 |
34 | if (months < 0) {
35 | // Borrow a year
36 | years -= 1;
37 | months += 12;
38 | }
39 |
40 | return { years, months, days };
41 | }
42 |
43 | export function getDirectTextContent(element: Element) {
44 | const text = new Str();
45 | for (let child of element.childNodes) {
46 | if (child.nodeType === Node.TEXT_NODE) {
47 | text.add(child.textContent)
48 | }
49 | }
50 | return text.join()
51 | }
52 |
53 | export class Str {
54 | private data: T[];
55 | constructor(data: T[] = []) {
56 | this.data = data;
57 | }
58 | add(...items: T[]): void {
59 | this.data.push(...items);
60 | }
61 | join(separator: string = ''): string {
62 | return this.data.join(separator);
63 | }
64 | static safeJoin(arr: T[] | null | undefined, separator: string = ''): string {
65 | if (arr == null) return '';
66 | return Array.isArray(arr) ? arr.join(separator) : '';
67 | }
68 | }
--------------------------------------------------------------------------------
/src/libs/types.ts:
--------------------------------------------------------------------------------
1 | export function isSortType(value: any): value is SortType {
2 | return Object.values(SortType).includes(value);
3 | }
4 |
5 | export enum SortType {
6 | NameASC = "0", // 0:文件名字母升序
7 | NameDESC = "1", // 1:文件名字母降序
8 | UpdatedASC = "2", // 2:文件更新时间升序
9 | UpdatedDESC = "3", // 3:文件更新时间降序
10 | AlphanumASC = "4", // 4:文件名自然数升序
11 | AlphanumDESC = "5", // 5:文件名自然数降序
12 | Custom = "6", // 6:自定义排序
13 | RefCountASC = "7", // 7:引用数升序
14 | RefCountDESC = "8", // 8:引用数降序
15 | CreatedASC = "9", // 9:文件创建时间升序
16 | CreatedDESC = "10", // 10:文件创建时间降序
17 | SizeASC = "11", // 11:文件大小升序
18 | SizeDESC = "12", // 12:文件大小降序
19 | SubDocCountASC = "13", // 13:子文档数升序
20 | SubDocCountDESC = "14", // 14:子文档数降序
21 | FileTree = "15", // 15:使用文档树排序规则
22 | Unassigned = "256", // 256:未指定排序规则,按照笔记本优先于文档树获取排序规则
23 | }
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/libs/ui.ts:
--------------------------------------------------------------------------------
1 | import { Plugin } from "siyuan"
2 | import { getChildElements } from "./utils";
3 | import { getDirectTextContent } from "./tools";
4 |
5 | export function removeStatusBar(removeTabBar = true) {
6 | const status = document.getElementById("status");
7 | status.style.display = "none";
8 |
9 | document.querySelectorAll('span[data-type="new"]').forEach((e: HTMLElement) => {
10 | if (e.style) e.style.display = "none";
11 | });
12 | document.querySelectorAll('span[data-type="more"]').forEach((e: HTMLElement) => {
13 | if (e.style) e.style.display = "none";
14 | });
15 | document.querySelectorAll("ul.layout-tab-bar.layout-tab-bar--readonly.fn__flex-1").forEach((e: HTMLElement) => {
16 | if (e.style) {
17 | e.style.paddingRight = "70px";
18 | // e.parentElement.style.height = "30px"
19 | }
20 | });
21 | document.getElementById("minWindow").style.display = "none";
22 | document.getElementById("maxWindow").style.display = "none";
23 | // document.getElementById("closeWindow").style.display = "none";
24 | if (removeTabBar) {
25 | document.querySelectorAll('.fn__flex.layout-tab-bar').forEach((e: HTMLElement) => {
26 | if (e.style) e.style.display = "none";
27 | });
28 | }
29 | }
30 |
31 | export function isPinned() {
32 | const div = document.getElementById("pinWindow");
33 | if (!div) return false;
34 | // iconUnpin for pinned status
35 | return div.querySelector('use')?.getAttribute("xlink:href") === "#iconUnpin";
36 | }
37 |
38 | export function addIcon(plugin: Plugin, minute: number | string) {
39 | const id = "iconTomato" + minute;
40 | plugin.addIcons(`
41 | ${minute}
42 | `);
43 | return id;
44 | }
45 |
46 | export function createNumIcon(num: number) {
47 | if (num >= 0 && num <= 9) {
48 | return `
49 | ${num}
50 | `;
51 | }
52 | if (num >= 10) {
53 | return `
54 | 9+
55 | `;
56 | }
57 | if (num < 0 && num >= -9) {
58 | return `
59 | ${num}
60 | `;
61 | }
62 | return `
63 | -n
64 | `;
65 | }
66 |
67 | export function searchSettings(settingsDiv: HTMLElement, searchKey: string) {
68 | const sk = searchKey.toLocaleLowerCase();
69 | getChildElements(settingsDiv).forEach((e) => {
70 | e.style.display = "";
71 | });
72 | settingsDiv.querySelectorAll(".tomato-highlight").forEach(e => {
73 | e.classList.remove("tomato-highlight")
74 | })
75 | if (sk) {
76 | getChildElements(settingsDiv).forEach((e) => {
77 | if (e.hasAttribute("data-search")) return;
78 | if (e.hasAttribute("data-hide")) {
79 | e.style.display = "none";
80 | return;
81 | }
82 | if (!e.textContent.toLocaleLowerCase().includes(sk)) {
83 | e.style.display = "none";
84 | return;
85 | }
86 | });
87 | settingsDiv.querySelectorAll("div,strong").forEach(e => {
88 | if (getDirectTextContent(e).toLocaleLowerCase().includes(sk)) {
89 | e.classList.add("tomato-highlight")
90 | }
91 | })
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/libs/user.ts:
--------------------------------------------------------------------------------
1 | import { ec as EC } from 'elliptic';
2 | import { getMd5, siyuan, Siyuan, timeUtil } from './utils';
3 | import { userID, userToken, writableWithGet } from './stores';
4 |
5 | const MY_PUBKEY = "044ad3bfb46f3b89979dd551a5dada23f8502f8a0c54d247e1f8d31e5d7705a978df1ef30ba5a4b5206f0b0f573c8f76feada715f949430187f62f5640ca144aa7";
6 | const ec = new EC('secp256k1');
7 | const keyPair = ec.keyFromPublic(MY_PUBKEY, 'hex')
8 | let _isValid: boolean = null;
9 |
10 | export function lastVerifyResult(): boolean {
11 | return _isValid;
12 | }
13 |
14 | export const expStore = writableWithGet("")
15 |
16 | export function getUserCodeExp(_exp: string) {
17 | if (!_exp) return ""
18 | return `${_exp.slice(0, 4)} / ${_exp.slice(4, 6)} / ${_exp.slice(6)}`
19 | }
20 |
21 | export function isMe() {
22 | return Siyuan?.user?.userId === "1656951563417";
23 | }
24 |
25 | export function resetKey() {
26 | _isValid = null;
27 | }
28 |
29 | export async function verifyKeyTomato() {
30 | return verifyKey("_siyuanTomatoCode_")
31 | }
32 |
33 | export async function verifyKeyProgressive() {
34 | return verifyKey("_siyuanProgressiveCode_")
35 | }
36 |
37 | async function verifyKey(included: string) {
38 | if (_isValid != null) return _isValid;
39 |
40 | let v = await verifyUserSign(userToken.get(), included);
41 | if (!v.valid) {
42 | const FREE_KEY = "freeze7XSGUQr_20250531_name_siyuanTomatoCode_30450221009c482f5a144f605dca52c5c9991501eb98d281755435d6316e666820c718f7ad02207e175822e94fc88fd397bd14f497d123815b91c7d43935a683e26d6bc99c1f76";
43 | userToken.set(FREE_KEY);
44 | v = await verifyUserSign(FREE_KEY, "_siyuanTomatoCode_");
45 | }
46 | _isValid = v.valid;
47 | return _isValid;
48 | }
49 |
50 | async function verifyUserSign(tokenSign: string, included: string) {
51 | let signValid = false;
52 | let userPart = "";
53 | let userPartShort = "";
54 | let exp = "";
55 | let ldID = "";
56 | let name = "";
57 | {
58 | // 1656951563417_22240101_ldID_siyuanTomatoCode_30qqqqqqqqqqqqqq..
59 | const parts = tokenSign?.split(included);
60 | if (parts?.length === 2) {
61 | userPartShort = parts[0];
62 | userPart = userPartShort + included;
63 | const sign = parts[1];
64 | try {
65 | const msgHash = getMd5(userPart)
66 | signValid = keyPair.verify(msgHash, sign);
67 | } catch {
68 | signValid = false;
69 | }
70 | }
71 | }
72 | {
73 | // freecbly0fNG4_20241206_name
74 | // 1656951563417_22240101_ldID
75 | const ps = userPartShort.split("_")
76 | if (ps.length === 3) {
77 | exp = ps[1];
78 | if (ps[2] === "ldID") {
79 | ldID = ps[0];
80 | } else if (ps[2] === "name") {
81 | name = ps[0];
82 | }
83 | }
84 | }
85 | if (signValid) {
86 | signValid = await checkUserID(ldID, name, exp);
87 | }
88 |
89 | if ([
90 | "",
91 | // "402f82671ae66425e07eaa0a47c5128723383ff0",
92 | // "9d07cd325209e7739fe04da41b2b7887e097b268",
93 | // "5f6df12238cfea797f8c73309dfcf3445fd99d28",
94 | // "1e4b90d2e70cc02b29ad5232094d6ca6a4853366",
95 | ].includes(getMd5(userPartShort))) signValid = false;
96 |
97 | if (included && !tokenSign.includes(included)) signValid = false;
98 | return { exp, valid: signValid };
99 | }
100 |
101 | async function checkUserID(ldID: string, name: string, exp: string) {
102 | const ms = await siyuan.currentTimeMs();
103 | const { y, M, d } = timeUtil.nowYMDStrPad(new Date(ms));
104 | const nowStr = y + M + d;
105 | expStore.set(getUserCodeExp(exp));
106 | if (nowStr <= exp) {
107 | if (ldID) {
108 | return ldID === userID.get();
109 | } else if (name) {
110 | return true;
111 | }
112 | }
113 | return false;
114 | }
115 |
--------------------------------------------------------------------------------
/src/libs/winHotkey.ts:
--------------------------------------------------------------------------------
1 | import { lastVerifyResult } from "./user";
2 | import { Siyuan } from "./utils";
3 |
4 |
5 |
6 | const officalHotkeys = new Map();
7 |
8 | getAllHotkeys
9 | function getAllHotkeys(obj: any) {
10 | const visited = new Set();
11 | const queue = [{ k: "keymap", v: obj }];
12 | while (queue.length) {
13 | const { k, v } = queue.shift();
14 | if (!v || typeof v !== 'object' || visited.has(v)) continue;
15 | visited.add(v);
16 | if (v.custom) {
17 | officalHotkeys.set(v.custom, k);
18 | } else {
19 | for (const [k, va] of Object.entries(v)) {
20 | if (k == "plugin") continue
21 | queue.push({ k, v: va });
22 | }
23 | }
24 | }
25 | }
26 |
27 | function toWin(k: string, mac = true, win = true) {
28 | const w = k
29 | .replaceAll("⌘", "Ctrl+")
30 | .replaceAll("⇧", "Shift+")
31 | .replaceAll("⌥", "Alt+")
32 | .replaceAll("⇥", "Tab")
33 | .replaceAll("⌫", "Backspace")
34 | .replaceAll("⌦", "Delete")
35 | .replaceAll("↩", "Enter");
36 | if (mac && win) {
37 | return `(${k})(${w})`
38 | }
39 | if (mac) {
40 | return `(${k})`
41 | }
42 | return `(${w})`
43 | }
44 |
45 | interface Get {
46 | get(p?: any): boolean,
47 | }
48 |
49 | export function winHotkey(m: string, langKey: string, icon?: string, langText?: () => string, vip?: boolean, store?: Get) {
50 | if (!m) throw Error("null hotkey")
51 | if (!langKey) throw Error("null langKey")
52 | m = m.toLocaleUpperCase()
53 | .replaceAll("CTRL+", "⌘")
54 | .replaceAll("SHIFT+", "⇧")
55 | .replaceAll("ALT+", "⌥")
56 | .replaceAll("TAB", "⇥")
57 | .replaceAll("BACKSPACE", "⌫")
58 | .replaceAll("DELETE", "⌦")
59 | .replaceAll("ENTER", "↩")
60 | .replaceAll("LEFT", "←")
61 | .replaceAll("RIGHT", "→")
62 | .replaceAll("UP", "↑")
63 | .replaceAll("DOWN", "↓")
64 |
65 | const alt = m.includes("⌥")
66 | const shift = m.includes("⇧")
67 | const ctrl = m.includes("⌘")
68 | m = m.replaceAll("⌥", "").replaceAll("⇧", "").replaceAll("⌘", "")
69 | if (ctrl) m = "⌘" + m
70 | if (shift) m = "⇧" + m
71 | if (alt) m = "⌥" + m
72 |
73 | // if (officalHotkeys.size == 0) getAllHotkeys(Siyuan?.config?.keymap);
74 | // if (!globalThis.wieyqstvaPUaBkyoBGpsBztqoIZPplSyMWEETBcF) globalThis.wieyqstvaPUaBkyoBGpsBztqoIZPplSyMWEETBcF = new Map();
75 | // const hotkeySet: Map = globalThis.wieyqstvaPUaBkyoBGpsBztqoIZPplSyMWEETBcF
76 | // if (hotkeySet.has(m)) console.warn("发现重复的hotkey:", m, langKey, "--------", hotkeySet.get(m))
77 | // if (hotkeySet.has(langKey)) console.warn("发现重复的langKey:", m, langKey, "--------", hotkeySet.get(langKey))
78 | // if (officalHotkeys.has(m)) console.warn("发现与官方重复的langKey:", m, langKey, "--------", officalHotkeys.get(m))
79 | // hotkeySet.set(m, langKey);
80 | // hotkeySet.set(langKey, m);
81 |
82 | const w = (mac = true, win = true) => {
83 | const a = Siyuan?.config?.keymap?.plugin?.['sy-tomato-plugin']?.[langKey]?.custom
84 | if (a) return toWin(a, mac, win);
85 |
86 | const b = Siyuan?.config?.keymap?.plugin?.['sy-progressive-plugin']?.[langKey]?.custom
87 | if (b) return toWin(b, mac, win);
88 |
89 | const c = Siyuan?.config?.keymap?.plugin?.['sy-my-plugin']?.[langKey]?.custom
90 | if (c) return toWin(c, mac, win);
91 |
92 | const a1 = Siyuan?.config?.keymap?.plugin?.['sy-tomato-plugin']?.[langKey]?.default
93 | const b1 = Siyuan?.config?.keymap?.plugin?.['sy-progressive-plugin']?.[langKey]?.default
94 | const c1 = Siyuan?.config?.keymap?.plugin?.['sy-my-plugin']?.[langKey]?.default
95 | const invalid = !!a1 || !!b1 || !!c1;
96 |
97 | return toWin(m, mac, win) + (invalid ? "🚫" : "");
98 | }
99 |
100 | const menu = () => {
101 | let ac = true;
102 | if (store) {
103 | ac = store.get()
104 | }
105 | if (vip && !lastVerifyResult()) {
106 | ac = false;
107 | }
108 | return ac;
109 | }
110 | const cmd = () => {
111 | let ac = true;
112 | if (vip && !lastVerifyResult()) {
113 | ac = false;
114 | }
115 | return ac;
116 | }
117 |
118 | return { m, w, langKey, icon, langText, menu, cmd };
119 | }
120 |
121 |
--------------------------------------------------------------------------------
/src/types/baidu.d.ts:
--------------------------------------------------------------------------------
1 | // https://app.quicktype.io/?l=ts
2 |
3 | interface AIResponse {
4 | id: string;
5 | object: string;
6 | created: number;
7 | result: string;
8 | sentence_id: number;
9 | is_end: boolean;
10 | is_truncated: boolean;
11 | need_clear_history: boolean;
12 | finish_reason: string;
13 | usage: Usage;
14 | }
15 |
16 | interface Usage {
17 | prompt_tokens: number;
18 | completion_tokens: number;
19 | total_tokens: number;
20 | }
21 |
22 | interface CalcTokensResponse {
23 | id: string;
24 | object: string;
25 | created: number;
26 | usage: CalcTokenUsage;
27 | }
28 |
29 | interface CalcTokenUsage {
30 | prompt_tokens: number;
31 | total_tokens: number;
32 | }
33 |
34 | type ChatRole = "user" | "assistant"
35 | type Chat = { role: ChatRole, content: string, tokens: number }
36 |
37 | interface AppConversationRunsResp {
38 | request_id: string;
39 | date: Date;
40 | answer: string;
41 | conversation_id: string;
42 | message_id: string;
43 | is_completion: null;
44 | content: AppConversationRunsContent[];
45 | }
46 |
47 | interface AppConversationRunsContent {
48 | result_type: string;
49 | event_code: number;
50 | event_message: string;
51 | event_type: string;
52 | event_id: string;
53 | event_status: string;
54 | content_type: string;
55 | visible_scope: string;
56 | outputs: AppConversationRunsContentOutputs;
57 | usage?: AppConversationRunsContentUsage;
58 | }
59 |
60 | interface AppConversationRunsContentOutputs {
61 | text?: AppConversationRunsContentTextClass | string;
62 | name_cn?: string;
63 | references?: AppConversationRunsContentReference[];
64 | }
65 |
66 | interface AppConversationRunsContentReference {
67 | id: string;
68 | content: string;
69 | type: string;
70 | from: string;
71 | title: string;
72 | segment_id: string;
73 | document_id: string;
74 | document_name: string;
75 | dataset_name: string;
76 | dataset_id: string;
77 | }
78 |
79 | interface AppConversationRunsContentTextClass {
80 | arguments: AppConversationRunsContentArguments;
81 | component_code: string;
82 | component_name: string;
83 | }
84 |
85 | interface AppConversationRunsContentArguments {
86 | origin_query: string;
87 | }
88 |
89 | interface AppConversationRunsContentUsage {
90 | prompt_tokens: number;
91 | completion_tokens: number;
92 | total_tokens: number;
93 | name: string;
94 | type: string;
95 | }
96 |
97 | interface DescribeDocumentsResp {
98 | requestId: string;
99 | marker: string;
100 | isTruncated: boolean;
101 | nextMarker: string;
102 | maxKeys: number;
103 | data: DescribeDocumentsDocument[];
104 | }
105 |
106 | interface DescribeDocumentsDocument {
107 | id: string;
108 | name: string;
109 | sy_doc_id: string;
110 | sy_exists: boolean;
111 | createdAt: number;
112 | wordCount: number;
113 | enabled: boolean;
114 | meta: DescribeDocumentsMeta;
115 | }
116 |
117 | interface DescribeDocumentsMeta {
118 | source: string;
119 | fileId: string;
120 | }
--------------------------------------------------------------------------------
/svelte.config.js:
--------------------------------------------------------------------------------
1 | import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
2 |
3 | const NoWarns = new Set([
4 | "a11y-click-events-have-key-events",
5 | "a11y-no-static-element-interactions",
6 | "a11y-no-noninteractive-element-interactions"
7 | ]);
8 |
9 | export default {
10 | // Consult https://svelte.dev/docs#compile-time-svelte-preprocess
11 | // for more information about preprocessors
12 | preprocess: vitePreprocess(),
13 | onwarn: (warning, handler) => {
14 | // suppress warnings on `vite dev` and `vite build`; but even without this, things still work
15 | if (NoWarns.has(warning.code)) return;
16 | handler(warning);
17 | }
18 | };
19 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "module": "ESNext",
6 | "lib": [
7 | "ES2022",
8 | "DOM",
9 | "DOM.Iterable"
10 | ],
11 | "skipLibCheck": true,
12 | /* Bundler mode */
13 | "moduleResolution": "Node",
14 | // "allowImportingTsExtensions": true,
15 | "allowSyntheticDefaultImports": true,
16 | "resolveJsonModule": true,
17 | "isolatedModules": true,
18 | "noEmit": true,
19 | "jsx": "preserve",
20 | /* Linting */
21 | "strict": false,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "noFallthroughCasesInSwitch": true,
25 | /* Svelte */
26 | /**
27 | * Typecheck JS in `.svelte` and `.js` files by default.
28 | * Disable checkJs if you'd like to use dynamic types in JS.
29 | * Note that setting allowJs false does not prevent the use
30 | * of JS in `.svelte` files.
31 | */
32 | "allowJs": true,
33 | "checkJs": true,
34 | "types": [
35 | "node",
36 | // "vite/client",
37 | // "svelte"
38 | ],
39 | // "baseUrl": "./src",
40 | // "paths": {
41 | // "@/*": ["./src/*"],
42 | // "@/libs/*": ["./src/libs/*"],
43 | // },
44 | "typeRoots": [
45 | "./src/types",
46 | "./node_modules/@types"
47 | ],
48 | },
49 | "include": [
50 | "src/**/*.ts",
51 | "src/**/*.d.ts",
52 | "src/**/*.svelte",
53 | ],
54 | "exclude": [
55 | "node_modules"
56 | ],
57 | "references": [
58 | {
59 | "path": "./tsconfig.node.json"
60 | }
61 | ],
62 | "root": "."
63 | }
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "Node",
7 | "allowSyntheticDefaultImports": true
8 | },
9 | "include": [
10 | "vite.config.ts"
11 | ]
12 | }
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { resolve } from "path";
2 | import { defineConfig } from "vite";
3 | import minimist from "minimist";
4 | import { viteStaticCopy } from "vite-plugin-static-copy";
5 | import livereload from "rollup-plugin-livereload";
6 | import { svelte } from "@sveltejs/vite-plugin-svelte";
7 | import zipPack from "vite-plugin-zip-pack";
8 | import fg from "fast-glob";
9 |
10 | const args = minimist(process.argv.slice(2));
11 | const isWatch = args.watch || args.w || false;
12 | // const devDistDir = "./dev";
13 | // const distDir = isWatch ? devDistDir : "./dist";
14 | // console.info("isWatch=>", isWatch);
15 | // console.info("distDir=>", distDir);
16 | const devDistDir = process.env.SYPLUGINDIR ? process.env.SYPLUGINDIR + "/sy-tomato-plugin" : "build";
17 | const distDir = devDistDir;
18 |
19 | export default defineConfig({
20 | resolve: {
21 | alias: {
22 | "@": resolve(__dirname, "src"),
23 | },
24 | },
25 |
26 | plugins: [
27 | svelte(),
28 | viteStaticCopy({
29 | targets: [
30 | {
31 | src: "./README*.md",
32 | dest: "./",
33 | },
34 | {
35 | src: "./icon.png",
36 | dest: "./",
37 | },
38 | {
39 | src: "./preview.png",
40 | dest: "./",
41 | },
42 | {
43 | src: "./plugin.json",
44 | dest: "./",
45 | },
46 | {
47 | src: "./src/i18n/**",
48 | dest: "./i18n/",
49 | },
50 | ],
51 | }),
52 | ],
53 |
54 | // https://github.com/vitejs/vite/issues/1930
55 | // https://vitejs.dev/guide/env-and-mode.html#env-files
56 | // https://github.com/vitejs/vite/discussions/3058#discussioncomment-2115319
57 | // 在这里自定义变量
58 | define: {
59 | "process.env.DEV_MODE": `"${isWatch}"`,
60 | "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
61 | },
62 |
63 | build: {
64 | // 输出路径
65 | outDir: distDir,
66 | emptyOutDir: false,
67 |
68 | // 构建后是否生成 source map 文件
69 | sourcemap: false,
70 |
71 | // 设置为 false 可以禁用最小化混淆
72 | // 或是用来指定是应用哪种混淆器
73 | // boolean | 'terser' | 'esbuild'
74 | minify: isWatch ? false : "esbuild",
75 |
76 | lib: {
77 | // Could also be a dictionary or array of multiple entry points
78 | entry: resolve(__dirname, "src/index.ts"),
79 | // the proper extensions will be added
80 | fileName: "index",
81 | formats: ["cjs"],
82 | },
83 |
84 | terserOptions: {
85 | compress: true, // 启用压缩
86 | mangle: true, // 启用变量名混淆
87 | output: {
88 | beautify: false, // 禁用美化输出
89 | comments: false // 移除注释
90 | }
91 | },
92 |
93 | rollupOptions: {
94 | plugins: [
95 | ...(isWatch
96 | ? [
97 | livereload(devDistDir),
98 | {
99 | //监听静态资源文件
100 | name: "watch-external",
101 | async buildStart() {
102 | const files = await fg([
103 | "src/i18n/*.json",
104 | // "./README*.md",
105 | "./plugin.json",
106 | ]);
107 | for (const file of files) {
108 | this.addWatchFile(file);
109 | }
110 | },
111 | },
112 | ]
113 | : [
114 | // zipPack({
115 | // inDir: "./dist",
116 | // outDir: "./",
117 | // outFileName: "package.zip",
118 | // }),
119 | ]),
120 | ],
121 |
122 | // make sure to externalize deps that shouldn't be bundled
123 | // into your library
124 | external: ["siyuan", "process"],
125 |
126 | output: {
127 | entryFileNames: "[name].js",
128 | assetFileNames: (assetInfo) => {
129 | if (assetInfo.name === "style.css") {
130 | return "index.css";
131 | }
132 | return assetInfo.name;
133 | },
134 | },
135 | },
136 | },
137 | });
138 |
--------------------------------------------------------------------------------