├── .eslintignore
├── .github
├── ISSUE_TEMPLATE
│ ├── config.yml
│ └── chinese.yml
└── workflows
│ └── release.yml
├── scripts
├── .gitignore
└── make_dev_link.js
├── icon.png
├── preview.png
├── asset
└── action.png
├── src
├── libs
│ ├── b3-typography.svelte
│ ├── index.d.ts
│ ├── setting-panel.svelte
│ ├── setting-item.svelte
│ └── setting-utils.ts
├── index.scss
├── types
│ ├── api.d.ts
│ └── index.d.ts
├── i18n
│ ├── zh_CN.json
│ └── en_US.json
├── hello.svelte
├── setting-example.svelte
├── index.ts
└── api.ts
├── tools
└── IMG_1665.jpeg
├── CHANGELOG.md
├── .gitignore
├── svelte.config.js
├── tsconfig.node.json
├── plugin.json
├── package.json
├── LICENSE
├── .eslintrc.cjs
├── README_zh_CN.md
├── tsconfig.json
├── README.md
└── vite.config.ts
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 |
--------------------------------------------------------------------------------
/scripts/.gitignore:
--------------------------------------------------------------------------------
1 | .venv
2 | build
3 | dist
4 | *.exe
5 | *.spec
6 |
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxkmm/siyuan_leave_to_lock/HEAD/icon.png
--------------------------------------------------------------------------------
/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxkmm/siyuan_leave_to_lock/HEAD/preview.png
--------------------------------------------------------------------------------
/asset/action.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxkmm/siyuan_leave_to_lock/HEAD/asset/action.png
--------------------------------------------------------------------------------
/src/libs/b3-typography.svelte:
--------------------------------------------------------------------------------
1 |
39 |
appId:
40 |
41 |
${app?.appId}
42 |
43 |
44 |
API demo:
45 |
46 |
47 | System current time: {time}
48 |
49 |
50 |
51 |
Protyle demo: id = {blockID}
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Create Release on Tag Push
2 |
3 | on:
4 | push:
5 | tags:
6 | - "v*"
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 | steps:
12 | # Checkout
13 | - name: Checkout
14 | uses: actions/checkout@v3
15 |
16 | # Install Node.js
17 | - name: Install Node.js
18 | uses: actions/setup-node@v3
19 | with:
20 | node-version: 18
21 | registry-url: "https://registry.npmjs.org"
22 |
23 | # Install pnpm
24 | - name: Install pnpm
25 | uses: pnpm/action-setup@v4
26 | id: pnpm-install
27 | with:
28 | version: 8
29 | run_install: false
30 |
31 | # Get pnpm store directory
32 | - name: Get pnpm store directory
33 | id: pnpm-cache
34 | shell: bash
35 | run: |
36 | echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
37 |
38 | # Setup pnpm cache
39 | - name: Setup pnpm cache
40 | uses: actions/cache@v3
41 | with:
42 | path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
43 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
44 | restore-keys: |
45 | ${{ runner.os }}-pnpm-store-
46 |
47 | # Install dependencies
48 | - name: Install dependencies
49 | run: pnpm install
50 |
51 | # Build for production, 这一步会生成一个 package.zip
52 | - name: Build for production
53 | run: pnpm build
54 |
55 | - name: Release
56 | uses: ncipollo/release-action@v1
57 | with:
58 | allowUpdates: true
59 | artifactErrorsFailBuild: true
60 | artifacts: "package.zip"
61 | token: ${{ secrets.GITHUB_TOKEN }}
62 | prerelease: true
63 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Siyuan Locks When You Leave
2 | Automatically lock SiYuan when you leave the Siyuan Note. Multiple monitoring modes and custom delays can be configured.
3 |
4 | # changelog
5 | ## 0.2.1
6 | - seperate the two monitor methods' delay
7 | - decrease minimum delay to a better value
8 |
9 | # Precautions
10 | To avoid the possibility of being unable to unlock the loop screen due to system incompatibility or Electron bugs, please try in a new workspace first. Ensure that your system supports this plugin.
11 | If you unfortunately remain locked, go to the workspace and delete the entire `siyuan_leave_to_lock` folder. Also, refrain from using it in operating systems or environments like iOS and Docker, where editing workspace files is difficult or impossible.
12 |
13 | # Note
14 | Please star⭐ my GitHub repository if you like this plugin. [https://github.com/zxkmm/siyuan_leave_to_lock](https://github.com/zxkmm/siyuan_leave_to_lock)
15 |
16 | # Credits
17 | [Frostime](https://github.com/frostime) ([b3log](https://ld246.com/member/Frostime))
18 | [Zuoqiu-Yingyi](https://github.com/Zuoqiu-Yingyi) ([b3log](https://ld246.com/member/shuoying))
19 |
20 |
21 | # Additional Attachment to MIT License
22 |
23 | You are free to use the code in this repository, regardless of whether it's closed source or not, or whether it's part of paid software or not. However, I have incorporated these additional requests into the license of this repository. If you use the code, design, text, algorithms, or anything else from this repository, you must include my username "zxkmm" and the link to this repository in three places:
24 |
25 | 1. In the code comments.
26 | 2. In the settings interface related to my code.
27 | 3. On the 'About' page of your software/website/and or any other format of computer production.
28 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/chinese.yml:
--------------------------------------------------------------------------------
1 | name: Issue
2 | description: Add a new issue
3 | body:
4 | - type: markdown
5 | attributes:
6 | value: |
7 | > [!IMPORTANT]
8 | > A Message from the Developer:
9 | > Hello! Thank you for using my plugin. Unlike many other Obsidian plugins, my plugin is 100% free and 100% open source, which should give you more confidence in using it.
10 | > Also, because this plugin is 100% open source and free, the only thing I can gain from maintaining and writing this plugin is your encouragement.
11 | > Therefore, before you continue to submit issues, I hope you could give this repository a free ⭐ star. ([How to star?](https://docs.github.com/zh/get-started/exploring-projects-on-github/saving-repositories-with-stars#starring-a-repository))
12 | >
13 | >
14 | > 来自开发者的话:
15 | > 您好!感谢使用我的插件。不像很多其他思源插件,我的插件是100%免费且100%开源的,这会让您更放心的使用我的插件。
16 | > 同时,由于该插件的100%开源免费,我从维护和编写这个插件中,唯一能得到的只有您的鼓励。
17 | > 所以,在您继续提交issue之前,希望您能给这个仓库点击一个免费的⭐星星(star)。([怎么点?](https://docs.github.com/zh/get-started/exploring-projects-on-github/saving-repositories-with-stars#starring-a-repository))
18 | - type: textarea
19 | id: description
20 | attributes:
21 | label: Feature request | 功能请求
22 | description: "Fill the blank below if you are submitting a featire request | 如果是(添加)功能请求,请在下面填写"
23 | placeholder: |
24 | Please Start mt repo before submitting an issue ticket.
25 | 在提交之前请给我的GitHub仓库点一个免费的星星
26 | validations:
27 | required: false
28 |
29 | - type: textarea
30 | id: repro
31 | attributes:
32 | label: Report bug | 汇报Bug
33 | description: "Please carefully describe how to reproduce the bug. attach log and the document you are having bug would be better | 请仔细描述bug的复现步骤,最好同时上传你发现问题的文档和思源的日志"
34 | placeholder: |
35 | Please Start mt repo before submitting an issue ticket.
36 | 在提交之前请给我的GitHub仓库点一个免费的星星
37 | validations:
38 | required: false
39 |
--------------------------------------------------------------------------------
/src/i18n/en_US.json:
--------------------------------------------------------------------------------
1 | {
2 | "mainSwitch": "Main Switch",
3 | "monitorVisibility": "Monitor SiYuan Window Visibility (Minimized)",
4 | "monitorMouse": "Monitor Mouse Inside SiYuan Window",
5 | "timeout": "Locking delay of mouse over monitor",
6 | "visibilityDelay": "Locking delay of Visibility monitor",
7 | "timeUnit": "Minutes",
8 | "onlyEnableListedDevices": "Enable Only on Listed Devices",
9 | "onlyEnableListedDevicesDesc": "When the switch is on, this plugin enabled only on the devices listed in the list. When the switch is off, this plugin enabled on all devices.",
10 | "enableDeviceList": "List of Enabled Devices",
11 | "enableDeviceListDesc": "Devices listed in the table will enable this plugin function when the switch is on. Please avoid manual editing of this table as much as possible.",
12 | "addCurrentDeviceIntoList": "Add Current Device to List",
13 | "addCurrentDeviceIntoListDesc": "Add the current device to the table.",
14 | "addCurrentDeviceIntoListLabel": "Add",
15 | "removeCurrentDeviceFromList": "Remove Current Device from List",
16 | "removeCurrentDeviceFromListDesc": "Remove the current device from the table.",
17 | "removeCurrentDeviceFromListLabel": "Remove",
18 | "lockImplementation": "Lock Screen Implementation",
19 | "lockImplementationDesc": "If app version supports, API is recommended. If not, use simulate click.",
20 | "simulateClick": "Simulate Click",
21 | "simulateClickText": "Simulate Click Button Text",
22 | "simulateClickTextDesc": "Only valid when you use simulate click option above.",
23 | "dangerousModeDesc" : "Allow shorter delay.
This mode is dangerous. Please use it with caution.",
24 | "dangerousMode": "Dangerous Mode",
25 | "hintTitle": "About",
26 | "hintDesc": "
"
27 | }
--------------------------------------------------------------------------------
/src/types/index.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2023 frostime. All rights reserved.
3 | */
4 |
5 | /**
6 | * Frequently used data structures in SiYuan
7 | */
8 | type DocumentId = string;
9 | type BlockId = string;
10 | type NotebookId = string;
11 | type PreviousID = BlockId;
12 | type ParentID = BlockId | DocumentId;
13 |
14 | type Notebook = {
15 | id: NotebookId;
16 | name: string;
17 | icon: string;
18 | sort: number;
19 | closed: boolean;
20 | }
21 |
22 | type NotebookConf = {
23 | name: string;
24 | closed: boolean;
25 | refCreateSavePath: string;
26 | createDocNameTemplate: string;
27 | dailyNoteSavePath: string;
28 | dailyNoteTemplatePath: string;
29 | }
30 |
31 | type BlockType = "d" | "s" | "h" | "t" | "i" | "p" | "f" | "audio" | "video" | "other";
32 |
33 | type BlockSubType = "d1" | "d2" | "s1" | "s2" | "s3" | "t1" | "t2" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "table" | "task" | "toggle" | "latex" | "quote" | "html" | "code" | "footnote" | "cite" | "collection" | "bookmark" | "attachment" | "comment" | "mindmap" | "spreadsheet" | "calendar" | "image" | "audio" | "video" | "other";
34 |
35 | type Block = {
36 | id: BlockId;
37 | parent_id?: BlockId;
38 | root_id: DocumentId;
39 | hash: string;
40 | box: string;
41 | path: string;
42 | hpath: string;
43 | name: string;
44 | alias: string;
45 | memo: string;
46 | tag: string;
47 | content: string;
48 | fcontent?: string;
49 | markdown: string;
50 | length: number;
51 | type: BlockType;
52 | subtype: BlockSubType;
53 | /** string of { [key: string]: string }
54 | * For instance: "{: custom-type=\"query-code\" id=\"20230613234017-zkw3pr0\" updated=\"20230613234509\"}"
55 | */
56 | ial?: string;
57 | sort: number;
58 | created: string;
59 | updated: string;
60 | }
61 |
62 | type doOperation = {
63 | action: string;
64 | data: string;
65 | id: BlockId;
66 | parentID: BlockId | DocumentId;
67 | previousID: BlockId;
68 | retData: null;
69 | }
70 |
71 | interface Window {
72 | siyuan: {
73 | notebooks: any;
74 | menus: any;
75 | dialogs: any;
76 | blockPanels: any;
77 | storage: any;
78 | user: any;
79 | ws: any;
80 | languages: any;
81 | };
82 | }
83 |
--------------------------------------------------------------------------------
/src/setting-example.svelte:
--------------------------------------------------------------------------------
1 |
62 |
63 |
64 |
65 | {#each groups as group}
66 | {
71 | focusGroup = group;
72 | }}
73 | on:keydown={() => {}}
74 | >
75 | {group}
76 |
77 | {/each}
78 |
79 |
80 |
86 |
87 | 💡 This is our default settings.
88 |
89 |
90 |
91 |
92 |
93 |
101 |
102 |
--------------------------------------------------------------------------------
/src/libs/setting-item.svelte:
--------------------------------------------------------------------------------
1 |
28 |
29 |
30 |
31 | {title}
32 |
33 | {@html description}
34 |
35 |
36 |
37 |
38 | {#if type === "checkbox"}
39 |
40 |
47 | {:else if type === "input"}
48 |
49 |
56 | {:else if type === "number"}
57 |
64 | {:else if type === "button"}
65 |
66 |
71 | {settingValue}
72 |
73 | {:else if type === "select"}
74 |
75 |
81 | {#each Object.entries(options) as [value, text]}
82 | {text}
83 | {/each}
84 |
85 | {:else if type == "slider"}
86 |
87 |
88 |
98 |
99 | {/if}
100 |
101 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { resolve } from "path"
2 | import { defineConfig, loadEnv } 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 |
15 | console.log("isWatch=>", isWatch)
16 | console.log("distDir=>", distDir)
17 |
18 | export default defineConfig({
19 | resolve: {
20 | alias: {
21 | "@": resolve(__dirname, "src"),
22 | }
23 | },
24 |
25 | plugins: [
26 | svelte(),
27 |
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 | // 不压缩,用于调试
75 | minify: !isWatch,
76 |
77 | lib: {
78 | // Could also be a dictionary or array of multiple entry points
79 | entry: resolve(__dirname, "src/index.ts"),
80 | // the proper extensions will be added
81 | fileName: "index",
82 | formats: ["cjs"],
83 | },
84 | rollupOptions: {
85 | plugins: [
86 | ...(
87 | isWatch ? [
88 | livereload(devDistDir),
89 | {
90 | //监听静态资源文件
91 | name: 'watch-external',
92 | async buildStart() {
93 | const files = await fg([
94 | 'src/i18n/*.json',
95 | './README*.md',
96 | './plugin.json'
97 | ]);
98 | for (let file of files) {
99 | this.addWatchFile(file);
100 | }
101 | }
102 | }
103 | ] : [
104 | zipPack({
105 | inDir: './dist',
106 | outDir: './',
107 | outFileName: 'package.zip'
108 | })
109 | ]
110 | )
111 | ],
112 |
113 | // make sure to externalize deps that shouldn't be bundled
114 | // into your library
115 | external: ["siyuan", "process"],
116 |
117 | output: {
118 | entryFileNames: "[name].js",
119 | assetFileNames: (assetInfo) => {
120 | if (assetInfo.name === "style.css") {
121 | return "index.css"
122 | }
123 | return assetInfo.name
124 | },
125 | },
126 | },
127 | }
128 | })
129 |
--------------------------------------------------------------------------------
/scripts/make_dev_link.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import http from 'node:http';
3 | import readline from 'node:readline';
4 |
5 |
6 | //************************************ Write you dir here ************************************
7 |
8 | //Please write the "workspace/data/plugins" directory here
9 | //请在这里填写你的 "workspace/data/plugins" 目录
10 | let targetDir = '/home/zxkmm/Documents/siyuan_dev/data/plugins';
11 | //Like this
12 | // let targetDir = `H:\\SiYuanDevSpace\\data\\plugins`;
13 | //********************************************************************************************
14 |
15 | const log = (info) => console.log(`\x1B[36m%s\x1B[0m`, info);
16 | const error = (info) => console.log(`\x1B[31m%s\x1B[0m`, info);
17 |
18 | let POST_HEADER = {
19 | // "Authorization": `Token ${token}`,
20 | "Content-Type": "application/json",
21 | }
22 |
23 | async function myfetch(url, options) {
24 | //使用 http 模块,从而兼容那些不支持 fetch 的 nodejs 版本
25 | return new Promise((resolve, reject) => {
26 | let req = http.request(url, options, (res) => {
27 | let data = '';
28 | res.on('data', (chunk) => {
29 | data += chunk;
30 | });
31 | res.on('end', () => {
32 | resolve({
33 | ok: true,
34 | status: res.statusCode,
35 | json: () => JSON.parse(data)
36 | });
37 | });
38 | });
39 | req.on('error', (e) => {
40 | reject(e);
41 | });
42 | req.end();
43 | });
44 | }
45 |
46 | async function getSiYuanDir() {
47 | let url = 'http://127.0.0.1:6806/api/system/getWorkspaces';
48 | let conf = {};
49 | try {
50 | let response = await myfetch(url, {
51 | method: 'POST',
52 | headers: POST_HEADER
53 | });
54 | if (response.ok) {
55 | conf = await response.json();
56 | } else {
57 | error(`\tHTTP-Error: ${response.status}`);
58 | return null;
59 | }
60 | } catch (e) {
61 | error(`\tError: ${e}`);
62 | error("\tPlease make sure SiYuan is running!!!");
63 | return null;
64 | }
65 | return conf.data;
66 | }
67 |
68 | async function chooseTarget(workspaces) {
69 | let count = workspaces.length;
70 | log(`>>> Got ${count} SiYuan ${count > 1 ? 'workspaces' : 'workspace'}`)
71 | for (let i = 0; i < workspaces.length; i++) {
72 | log(`\t[${i}] ${workspaces[i].path}`);
73 | }
74 |
75 | if (count == 1) {
76 | return `${workspaces[0].path}/data/plugins`;
77 | } else {
78 | const rl = readline.createInterface({
79 | input: process.stdin,
80 | output: process.stdout
81 | });
82 | let index = await new Promise((resolve, reject) => {
83 | rl.question(`\tPlease select a workspace[0-${count - 1}]: `, (answer) => {
84 | resolve(answer);
85 | });
86 | });
87 | rl.close();
88 | return `${workspaces[index].path}/data/plugins`;
89 | }
90 | }
91 |
92 | log('>>> Try to visit constant "targetDir" in make_dev_link.js...')
93 |
94 | if (targetDir === '') {
95 | log('>>> Constant "targetDir" is empty, try to get SiYuan directory automatically....')
96 | let res = await getSiYuanDir();
97 |
98 | if (res === null || res === undefined || res.length === 0) {
99 | log('>>> Can not get SiYuan directory automatically, try to visit environment variable "SIYUAN_PLUGIN_DIR"....');
100 |
101 | // console.log(process.env)
102 | let env = process.env?.SIYUAN_PLUGIN_DIR;
103 | if (env !== undefined && env !== null && env !== '') {
104 | targetDir = env;
105 | log(`\tGot target directory from environment variable "SIYUAN_PLUGIN_DIR": ${targetDir}`);
106 | } else {
107 | error('\tCan not get SiYuan directory from environment variable "SIYUAN_PLUGIN_DIR", failed!');
108 | process.exit(1);
109 | }
110 | } else {
111 | targetDir = await chooseTarget(res);
112 | }
113 |
114 |
115 | log(`>>> Successfully got target directory: ${targetDir}`);
116 | }
117 |
118 | //Check
119 | if (!fs.existsSync(targetDir)) {
120 | error(`Failed! plugin directory not exists: "${targetDir}"`);
121 | error(`Please set the plugin directory in scripts/make_dev_link.js`);
122 | process.exit(1);
123 | }
124 |
125 |
126 | //check if plugin.json exists
127 | if (!fs.existsSync('./plugin.json')) {
128 | //change dir to parent
129 | process.chdir('../');
130 | if (!fs.existsSync('./plugin.json')) {
131 | error('Failed! plugin.json not found');
132 | process.exit(1);
133 | }
134 | }
135 |
136 | //load plugin.json
137 | const plugin = JSON.parse(fs.readFileSync('./plugin.json', 'utf8'));
138 | const name = plugin?.name;
139 | if (!name || name === '') {
140 | error('Failed! Please set plugin name in plugin.json');
141 | process.exit(1);
142 | }
143 |
144 | //dev directory
145 | const devDir = `${process.cwd()}/dev`;
146 | //mkdir if not exists
147 | if (!fs.existsSync(devDir)) {
148 | fs.mkdirSync(devDir);
149 | }
150 |
151 | function cmpPath(path1, path2) {
152 | path1 = path1.replace(/\\/g, '/');
153 | path2 = path2.replace(/\\/g, '/');
154 | // sepertor at tail
155 | if (path1[path1.length - 1] !== '/') {
156 | path1 += '/';
157 | }
158 | if (path2[path2.length - 1] !== '/') {
159 | path2 += '/';
160 | }
161 | return path1 === path2;
162 | }
163 |
164 | const targetPath = `${targetDir}/${name}`;
165 | //如果已经存在,就退出
166 | if (fs.existsSync(targetPath)) {
167 | let isSymbol = fs.lstatSync(targetPath).isSymbolicLink();
168 |
169 | if (isSymbol) {
170 | let srcPath = fs.readlinkSync(targetPath);
171 |
172 | if (cmpPath(srcPath, devDir)) {
173 | log(`Good! ${targetPath} is already linked to ${devDir}`);
174 | } else {
175 | error(`Error! Already exists symbolic link ${targetPath}\nBut it links to ${srcPath}`);
176 | }
177 | } else {
178 | error(`Failed! ${targetPath} already exists and is not a symbolic link`);
179 | }
180 |
181 | } else {
182 | //创建软链接
183 | fs.symlinkSync(devDir, targetPath, 'junction');
184 | log(`Done! Created symlink ${targetPath}`);
185 | }
186 |
187 |
--------------------------------------------------------------------------------
/src/libs/setting-utils.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2023 by frostime. All Rights Reserved.
3 | * @Author : frostime
4 | * @Date : 2023-09-16 18:05:00
5 | * @FilePath : /src/libs/setting-utils.ts
6 | * @LastEditTime : 2023-11-28 21:46:29
7 | * @Description : A utility for siyuan plugin settings
8 | */
9 |
10 | import { it } from 'node:test';
11 | import { Plugin, Setting } from 'siyuan';
12 |
13 | export class SettingUtils {
14 | plugin: Plugin;
15 | name: string;
16 | file: string;
17 |
18 | settings: Map
= new Map();
19 | elements: Map = new Map();
20 |
21 | constructor(plugin: Plugin, name?: string, callback?: (data: any) => void, width?: string, height?: string) {
22 | this.name = name ?? 'settings';
23 | this.plugin = plugin;
24 | this.file = this.name.endsWith('.json') ? this.name : `${this.name}.json`;
25 | this.plugin.setting = new Setting({
26 | width: width,
27 | height: height,
28 | confirmCallback: () => {
29 | for (let key of this.settings.keys()) {
30 | this.updateValue(key);
31 | }
32 | let data = this.dump();
33 | if (callback !== undefined) {
34 | callback(data);
35 | } else {
36 | this.plugin.data[this.name] = data;
37 | this.save();
38 | window.location.reload();
39 | }
40 | }
41 | });
42 | }
43 |
44 | async load() {
45 | let data = await this.plugin.loadData(this.file);
46 | console.debug('Load config:', data);
47 | if (data) {
48 | for (let [key, item] of this.settings) {
49 | item.value = data?.[key] ?? item.value;
50 | }
51 | }
52 | this.plugin.data[this.name] = this.dump();
53 | return data;
54 | }
55 |
56 | async save() {
57 | let data = this.dump();
58 | await this.plugin.saveData(this.file, this.dump());
59 | return data;
60 | }
61 |
62 | /**
63 | * Get setting item value
64 | * @param key key name
65 | * @returns setting item value
66 | */
67 | get(key: string) {
68 | return this.settings.get(key)?.value;
69 | }
70 |
71 | async assignValue(_key_: string, _value_: any) {
72 | let item = this.settings.get(_key_);
73 | item.value = _value_;
74 | this.plugin.data[this.name] = item.value;
75 | await this.save();
76 | window.location.reload();
77 | }
78 |
79 | /**
80 | * 将设置项目导出为 JSON 对象
81 | * @returns object
82 | */
83 | dump(): Object {
84 | let data: any = {};
85 | for (let [key, item] of this.settings) {
86 | if (item.type === 'button') continue;
87 | data[key] = item.value;
88 | }
89 | return data;
90 | }
91 |
92 |
93 | addItem(item: ISettingItem) {
94 | this.settings.set(item.key, item);
95 | let itemElement: HTMLElement;
96 | switch (item.type) {
97 | case 'checkbox':
98 | let element: HTMLInputElement = document.createElement('input');
99 | element.type = 'checkbox';
100 | element.checked = item.value;
101 | element.className = "b3-switch fn__flex-center";
102 | itemElement = element;
103 | break;
104 | case 'select':
105 | let selectElement: HTMLSelectElement = document.createElement('select');
106 | selectElement.className = "b3-select fn__flex-center fn__size200";
107 | let options = item?.options ?? {};
108 | for (let val in options) {
109 | let optionElement = document.createElement('option');
110 | let text = options[val];
111 | optionElement.value = val;
112 | optionElement.text = text;
113 | selectElement.appendChild(optionElement);
114 | }
115 | selectElement.value = item.value;
116 | itemElement = selectElement;
117 | break;
118 | case 'slider':
119 | let sliderElement: HTMLInputElement = document.createElement('input');
120 | sliderElement.type = 'range';
121 | sliderElement.className = 'b3-slider fn__size200 b3-tooltips b3-tooltips__n';
122 | sliderElement.ariaLabel = item.value;
123 | sliderElement.min = item.slider?.min.toString() ?? '0';
124 | sliderElement.max = item.slider?.max.toString() ?? '100';
125 | sliderElement.step = item.slider?.step.toString() ?? '1';
126 | sliderElement.value = item.value;
127 | sliderElement.onchange = () => {
128 | sliderElement.ariaLabel = sliderElement.value;
129 | }
130 | itemElement = sliderElement;
131 | break;
132 | case 'textinput':
133 | let textInputElement: HTMLInputElement = document.createElement('input');
134 | textInputElement.className = 'b3-text-field fn__flex-center fn__size200';
135 | textInputElement.value = item.value;
136 | itemElement = textInputElement;
137 | break;
138 | case 'textarea':
139 | let textareaElement: HTMLTextAreaElement = document.createElement('textarea');
140 | textareaElement.className = "b3-text-field fn__block";
141 | textareaElement.value = item.value;
142 | itemElement = textareaElement;
143 | break;
144 | case 'button':
145 | let buttonElement: HTMLButtonElement = document.createElement('button');
146 | buttonElement.className = "b3-button b3-button--outline fn__flex-center fn__size200";
147 | buttonElement.innerText = item.button?.label ?? 'Button';
148 | buttonElement.onclick = item.button?.callback ?? (() => { });
149 | itemElement = buttonElement;
150 | break;
151 | case 'hint':
152 | let hintElement: HTMLElement = document.createElement('div');
153 | hintElement.className = 'b3-label fn__flex-center';
154 | itemElement = hintElement;
155 | break;
156 | }
157 | this.elements.set(item.key, itemElement);
158 | this.plugin.setting.addItem({
159 | title: item.title,
160 | description: item?.description,
161 | createActionElement: () => {
162 | let element = this.getElement(item.key);
163 | return element;
164 | }
165 | })
166 | }
167 |
168 | private getElement(key: string) {
169 | let item = this.settings.get(key);
170 | let element = this.elements.get(key) as any;
171 | switch (item.type) {
172 | case 'checkbox':
173 | element.checked = item.value;
174 | break;
175 | case 'select':
176 | element.value = item.value;
177 | break;
178 | case 'slider':
179 | element.value = item.value;
180 | element.ariaLabel = item.value;
181 | break;
182 | case 'textinput':
183 | element.value = item.value;
184 | break;
185 | case 'textarea':
186 | element.value = item.value;
187 | break;
188 | }
189 | return element;
190 | }
191 |
192 | private updateValue(key: string) {
193 | let item = this.settings.get(key);
194 | let element = this.elements.get(key) as any;
195 | // console.debug(element, element?.value);
196 | switch (item.type) {
197 | case 'checkbox':
198 | item.value = element.checked;
199 | break;
200 | case 'select':
201 | item.value = element.value;
202 | break;
203 | case 'slider':
204 | item.value = element.value;
205 | break;
206 | case 'textinput':
207 | item.value = element.value;
208 | break;
209 | case 'textarea':
210 | item.value = element.value;
211 | break;
212 | }
213 | }
214 |
215 | }
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Plugin,
3 | lockScreen
4 | } from "siyuan";
5 | import "@/index.scss";
6 |
7 |
8 |
9 | import { SettingUtils } from "./libs/setting-utils";
10 |
11 | const STORAGE_NAME = "menu-config";
12 | var minLockDelay = 0.5; // minutes
13 |
14 | export default class siyuan_leave_to_lock extends Plugin {
15 |
16 | private settingUtils: SettingUtils;
17 |
18 | async onload() {
19 | this.data[STORAGE_NAME] = { readonlyText: "Readonly" };
20 |
21 | this.settingUtils = new SettingUtils(this, STORAGE_NAME);
22 |
23 | this.settingUtils.load();
24 |
25 | this.settingUtils.addItem({
26 | key: "mainSwitch",
27 | value: false,
28 | type: "checkbox",
29 | title: this.i18n.mainSwitch,
30 | description: "",
31 | });
32 |
33 | this.settingUtils.addItem({
34 | key: "monitorVisibility",
35 | value: true,
36 | type: "checkbox",
37 | title: this.i18n.monitorVisibility,
38 | description: "",
39 | });
40 |
41 | this.settingUtils.addItem({
42 | key: "monitorMouse",
43 | value: true,
44 | type: "checkbox",
45 | title: this.i18n.monitorMouse,
46 | description: "",
47 | });
48 |
49 | this.settingUtils.addItem({
50 | key: "Slider",
51 | value: 50,
52 | type: "slider",
53 | title: this.i18n.timeout,
54 | description: this.i18n.timeUnit,
55 | slider: {
56 | min: 0.5,
57 | max: 120,
58 | step: 0.5,
59 | }
60 | });
61 |
62 | this.settingUtils.addItem({
63 | key: "visibilityDelay",
64 | value: 50,
65 | type: "slider",
66 | title: this.i18n.visibilityDelay,
67 | description: this.i18n.timeUnit,
68 | slider: {
69 | min: 0,
70 | max: 120,
71 | step: 0.1,
72 | }
73 | });
74 |
75 | this.settingUtils.addItem({
76 | key: "lockImplementation",
77 | value: 1,
78 | type: "select",
79 | title: this.i18n.lockImplementation,
80 | description: this.i18n.lockImplementationDesc,
81 | options: {
82 | 1: "API",
83 | 2: this.i18n.simulateClick,
84 | }
85 | });
86 |
87 | this.settingUtils.addItem({
88 | key: "simulateClickText",
89 | value: "锁屏",
90 | type: "textinput",
91 | title: this.i18n.simulateClickText,
92 | description: this.i18n.simulateClickTextDesc,
93 | });
94 |
95 | this.settingUtils.addItem({
96 | key: "onlyEnableListedDevices",
97 | value: false,
98 | type: "checkbox",
99 | title: this.i18n.onlyEnableListedDevices,
100 | description: this.i18n.onlyEnableListedDevicesDesc,
101 | });
102 |
103 | this.settingUtils.addItem({
104 | key: "enableDeviceList",
105 | value: "",
106 | type: "textarea",
107 | title: this.i18n.enableDeviceList,
108 | description: this.i18n.enableDeviceListDesc,
109 | });
110 |
111 | this.settingUtils.addItem({
112 | key: "addCurrentDeviceIntoList",
113 | value: "",
114 | type: "button",
115 | title: this.i18n.addCurrentDeviceIntoList,
116 | description: this.i18n.addCurrentDeviceIntoListDesc,
117 | button: {
118 | label: this.i18n.addCurrentDeviceIntoListLabel,
119 | callback: () => {
120 | this.appendCurrentDeviceIntoList();
121 | }
122 | }
123 | });
124 |
125 | this.settingUtils.addItem({
126 | key: "removeCurrentDeviceFromList",
127 | value: "",
128 | type: "button",
129 | title: this.i18n.removeCurrentDeviceFromList,
130 | description: this.i18n.removeCurrentDeviceFromListDesc,
131 | button: {
132 | label: this.i18n.removeCurrentDeviceFromListLabel,
133 | callback: () => {
134 | this.removeCurrentDeviceFromList();
135 | }
136 | }
137 | });
138 |
139 | this.settingUtils.addItem({
140 | key: "hint",
141 | value: "",
142 | type: "hint",
143 | title: this.i18n.hintTitle,
144 | description: this.i18n.hintDesc,
145 | });
146 |
147 |
148 | }
149 |
150 |
151 |
152 | onLayoutReady() {
153 | this.loadData(STORAGE_NAME);
154 | this.settingUtils.load();
155 |
156 | const layoutReadyAsyncHandler = async () => {
157 |
158 |
159 | /*条件列表:
160 | 当前设备真, 仅允许开关开,后半段为假 :真||假: 执行
161 | 当前设备真, 仅允许开关关,后半段为真 :真||真: 执行
162 | 当前设备假, 仅允许开关开,后半段为假 :假||假: 不执行
163 | 当前设备假, 仅允许开关关,后半段为真 :假||真: 执行
164 | */
165 |
166 |
167 | try {
168 |
169 | const _visibilityDelay_ = this.settingUtils.get("visibilityDelay") * 1000 * 60;
170 | const _mouseOverDelay_ = this.settingUtils.get("Slider") * 1000 * 60;
171 | if ((await this.currentDeviceInList() || !this.settingUtils.get("onlyEnableListedDevices")) && this.settingUtils.get("mainSwitch")) {
172 | // console.log("siyuan_leave_to_lock: device ifEnable condition entered"); //DBG
173 |
174 | let timer;
175 |
176 | document.addEventListener("visibilitychange", () => {
177 | if (document.hidden) {
178 | timer = setTimeout(() => {
179 | if (this.settingUtils.get("mainSwitch") && this.settingUtils.get("monitorVisibility")) {
180 |
181 |
182 | if (this.settingUtils.get("lockImplementation") == 1) {
183 | console.log("condition,1,1"); //DBG
184 | this.lock_screen_with_api();
185 | } else if (this.settingUtils.get("lockImplementation") == 2) {
186 | console.log("condition,1,2"); //DBG
187 | this.lock_screen_with_simulate_click();
188 | } else {
189 | console.log("condition,1,3"); //DBG
190 | this.lock_screen_with_api();
191 | }
192 |
193 | this.sleep(1000);
194 | }
195 | }, _visibilityDelay_);
196 | } else {
197 | clearTimeout(timer);
198 | }
199 | });
200 |
201 | document.addEventListener("mouseout", () => {
202 | timer = setTimeout(() => {
203 | if (this.settingUtils.get("mainSwitch") && this.settingUtils.get("monitorMouse")) {
204 | if (this.settingUtils.get("lockImplementation") == 1) {
205 | this.lock_screen_with_api();
206 | // console.log("condition,2,1"); //DBG
207 | } else if (this.settingUtils.get("lockImplementation") == 2) {
208 | // console.log("condition,2,2"); //DBG
209 | this.lock_screen_with_simulate_click();
210 | } else {
211 | this.lock_screen_with_api();
212 | // console.log("condition,2,3"); //DBG
213 | }
214 | this.sleep(1000);
215 | }
216 | }, _mouseOverDelay_);
217 | });
218 |
219 | document.addEventListener("mouseover", () => {
220 | clearTimeout(timer);
221 | });
222 | }
223 | } catch (error) {
224 | console.error("sy_leave_to_lock: failed loading device ifEnable condition", error);
225 | }
226 | };
227 |
228 | layoutReadyAsyncHandler();
229 | }
230 |
231 |
232 | lock_screen_with_api() {
233 | lockScreen(this.app);
234 | }
235 |
236 |
237 |
238 | async lock_screen_with_simulate_click() {
239 | var user_defined_simulate_click_text = this.settingUtils.get("simulateClickText");
240 | // console.log("try to lock"); //DBG
241 | var mainMenuButton = document.getElementById("barWorkspace");
242 |
243 | // main menu
244 | if (mainMenuButton) {
245 | mainMenuButton.click();
246 | await this.sleep(300);
247 | } else {
248 | console.log("siyuan_leave_to_lock: cant find the main menu button");
249 | return;
250 | }
251 |
252 | await this.sleep(100);
253 |
254 |
255 | function findTargetButton(elements) {
256 | var targetButton = null;
257 | elements.forEach(function (button) {
258 | var labelElement = button.querySelector('.b3-menu__label');
259 | if (labelElement && labelElement.textContent.trim() == user_defined_simulate_click_text) {
260 | targetButton = button;
261 | } else {
262 | var submenu = button.querySelector('.b3-menu__submenu');
263 | if (submenu) {
264 | // submenu exists 递归
265 | targetButton = findTargetButton(submenu.querySelectorAll('.b3-menu__item'));
266 | }
267 | }
268 | });
269 | return targetButton;
270 | }
271 |
272 | var targetButton = findTargetButton(document.querySelectorAll('.b3-menu__item'));
273 |
274 | if (targetButton) {
275 | targetButton.click();
276 | } else {
277 | console.error('siyuan_leave_to_lock: cant find the text you defined');
278 | }
279 | }
280 |
281 |
282 |
283 |
284 | sleep(ms) {
285 | return new Promise(resolve => setTimeout(resolve, ms));
286 | }
287 |
288 |
289 |
290 | async onunload() {
291 | await this.settingUtils.save();
292 | // window.location.reload();
293 | }
294 |
295 |
296 | async currentDeviceInList() {
297 | try {
298 | var current_device_info = await this.fetchCurrentDeviceInfo();
299 |
300 | var enableDeviceList = await this.settingUtils.get("enableDeviceList");
301 | var enableDeviceListArray = enableDeviceList.split("\n");
302 |
303 | return enableDeviceListArray.includes(current_device_info);
304 | } catch (error) {
305 | console.error("Error checking if current device is enabled:", error);
306 | }
307 | }
308 |
309 |
310 | fetchCurrentDeviceInfo(): Promise {
311 | var current_device_uuid = window.siyuan.config.system.id;
312 | var current_device_name = window.siyuan.config.system.name;
313 | var current_device_info = current_device_uuid + " " + current_device_name;
314 |
315 | return Promise.resolve(current_device_info.toString());
316 | }
317 |
318 |
319 | async appendCurrentDeviceIntoList() {
320 | try {
321 | // await!!!!!
322 | var current_device_info = await this.fetchCurrentDeviceInfo();
323 |
324 | var enableDeviceList = this.settingUtils.get("enableDeviceList");
325 | var enableDeviceListArray = enableDeviceList.split("\n");
326 | var enableDeviceListArrayLength = enableDeviceListArray.length;
327 | var enableDeviceListArrayLast = enableDeviceListArray[enableDeviceListArrayLength - 1];
328 |
329 | // remove empty line
330 | if (enableDeviceListArrayLast === "") {
331 | enableDeviceListArray.pop();
332 | }
333 |
334 | enableDeviceListArray.push(current_device_info);
335 |
336 | var enableDeviceListArrayString = enableDeviceListArray.join("\n");
337 |
338 | this.settingUtils.assignValue("enableDeviceList", enableDeviceListArrayString);
339 | this.settingUtils.save();
340 | } catch (error) {
341 | console.error("Error appending current device into list:", error);
342 | }
343 | }
344 |
345 |
346 |
347 | async removeCurrentDeviceFromList() {
348 |
349 | try {
350 |
351 | var current_device_info = await this.fetchCurrentDeviceInfo();
352 |
353 | var enableDeviceList = this.settingUtils.get("enableDeviceList");
354 | var enableDeviceListArray = enableDeviceList.split("\n");
355 |
356 | // make sure visited the entire list
357 | for (var i = enableDeviceListArray.length - 1; i >= 0; i--) {
358 | var deviceInfo = enableDeviceListArray[i];
359 |
360 | if (deviceInfo === current_device_info) {
361 | enableDeviceListArray.splice(i, 1);
362 | }
363 | }
364 |
365 | // reassemble list
366 | var enableDeviceListArrayString = enableDeviceListArray.join("\n");
367 |
368 | this.settingUtils.assignValue("enableDeviceList", enableDeviceListArrayString);
369 | this.settingUtils.save();
370 | } catch (error) {
371 | console.error("Error removing current device from list:", error);
372 | }
373 |
374 | }
375 |
376 |
377 |
378 |
379 |
380 | }
381 |
--------------------------------------------------------------------------------
/src/api.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2023 frostime. All rights reserved.
3 | * https://github.com/frostime/sy-plugin-template-vite
4 | *
5 | * See API Document in [API.md](https://github.com/siyuan-note/siyuan/blob/master/API.md)
6 | * API 文档见 [API_zh_CN.md](https://github.com/siyuan-note/siyuan/blob/master/API_zh_CN.md)
7 | */
8 |
9 | import { fetchSyncPost, IWebSocketData } from "siyuan";
10 |
11 |
12 | async function request(url: string, data: any) {
13 | let response: IWebSocketData = await fetchSyncPost(url, data);
14 | let res = response.code === 0 ? response.data : null;
15 | return res;
16 | }
17 |
18 |
19 | // **************************************** Noteboook ****************************************
20 |
21 |
22 | export async function lsNotebooks(): Promise {
23 | let url = '/api/notebook/lsNotebooks';
24 | return request(url, '');
25 | }
26 |
27 |
28 | export async function openNotebook(notebook: NotebookId) {
29 | let url = '/api/notebook/openNotebook';
30 | return request(url, { notebook: notebook });
31 | }
32 |
33 |
34 | export async function closeNotebook(notebook: NotebookId) {
35 | let url = '/api/notebook/closeNotebook';
36 | return request(url, { notebook: notebook });
37 | }
38 |
39 |
40 | export async function renameNotebook(notebook: NotebookId, name: string) {
41 | let url = '/api/notebook/renameNotebook';
42 | return request(url, { notebook: notebook, name: name });
43 | }
44 |
45 |
46 | export async function createNotebook(name: string): Promise {
47 | let url = '/api/notebook/createNotebook';
48 | return request(url, { name: name });
49 | }
50 |
51 |
52 | export async function removeNotebook(notebook: NotebookId) {
53 | let url = '/api/notebook/removeNotebook';
54 | return request(url, { notebook: notebook });
55 | }
56 |
57 |
58 | export async function getNotebookConf(notebook: NotebookId): Promise {
59 | let data = { notebook: notebook };
60 | let url = '/api/notebook/getNotebookConf';
61 | return request(url, data);
62 | }
63 |
64 |
65 | export async function setNotebookConf(notebook: NotebookId, conf: NotebookConf): Promise {
66 | let data = { notebook: notebook, conf: conf };
67 | let url = '/api/notebook/setNotebookConf';
68 | return request(url, data);
69 | }
70 |
71 |
72 | // **************************************** File Tree ****************************************
73 | export async function createDocWithMd(notebook: NotebookId, path: string, markdown: string): Promise {
74 | let data = {
75 | notebook: notebook,
76 | path: path,
77 | markdown: markdown,
78 | };
79 | let url = '/api/filetree/createDocWithMd';
80 | return request(url, data);
81 | }
82 |
83 |
84 | export async function renameDoc(notebook: NotebookId, path: string, title: string): Promise {
85 | let data = {
86 | doc: notebook,
87 | path: path,
88 | title: title
89 | };
90 | let url = '/api/filetree/renameDoc';
91 | return request(url, data);
92 | }
93 |
94 |
95 | export async function removeDoc(notebook: NotebookId, path: string) {
96 | let data = {
97 | notebook: notebook,
98 | path: path,
99 | };
100 | let url = '/api/filetree/removeDoc';
101 | return request(url, data);
102 | }
103 |
104 |
105 | export async function moveDocs(fromPaths: string[], toNotebook: NotebookId, toPath: string) {
106 | let data = {
107 | fromPaths: fromPaths,
108 | toNotebook: toNotebook,
109 | toPath: toPath
110 | };
111 | let url = '/api/filetree/moveDocs';
112 | return request(url, data);
113 | }
114 |
115 |
116 | export async function getHPathByPath(notebook: NotebookId, path: string): Promise {
117 | let data = {
118 | notebook: notebook,
119 | path: path
120 | };
121 | let url = '/api/filetree/getHPathByPath';
122 | return request(url, data);
123 | }
124 |
125 |
126 | export async function getHPathByID(id: BlockId): Promise {
127 | let data = {
128 | id: id
129 | };
130 | let url = '/api/filetree/getHPathByID';
131 | return request(url, data);
132 | }
133 |
134 |
135 | export async function getIDsByHPath(notebook: NotebookId, path: string): Promise {
136 | let data = {
137 | notebook: notebook,
138 | path: path
139 | };
140 | let url = '/api/filetree/getIDsByHPath';
141 | return request(url, data);
142 | }
143 |
144 | // **************************************** Asset Files ****************************************
145 |
146 | export async function upload(assetsDirPath: string, files: any[]): Promise {
147 | let form = new FormData();
148 | form.append('assetsDirPath', assetsDirPath);
149 | for (let file of files) {
150 | form.append('file[]', file);
151 | }
152 | let url = '/api/asset/upload';
153 | return request(url, form);
154 | }
155 |
156 | // **************************************** Block ****************************************
157 | type DataType = "markdown" | "dom";
158 | export async function insertBlock(
159 | dataType: DataType, data: string,
160 | nextID?: BlockId, previousID?: BlockId, parentID?: BlockId
161 | ): Promise {
162 | let payload = {
163 | dataType: dataType,
164 | data: data,
165 | nextID: nextID,
166 | previousID: previousID,
167 | parentID: parentID
168 | }
169 | let url = '/api/block/insertBlock';
170 | return request(url, payload);
171 | }
172 |
173 |
174 | export async function prependBlock(dataType: DataType, data: string, parentID: BlockId | DocumentId): Promise {
175 | let payload = {
176 | dataType: dataType,
177 | data: data,
178 | parentID: parentID
179 | }
180 | let url = '/api/block/prependBlock';
181 | return request(url, payload);
182 | }
183 |
184 |
185 | export async function appendBlock(dataType: DataType, data: string, parentID: BlockId | DocumentId): Promise {
186 | let payload = {
187 | dataType: dataType,
188 | data: data,
189 | parentID: parentID
190 | }
191 | let url = '/api/block/appendBlock';
192 | return request(url, payload);
193 | }
194 |
195 |
196 | export async function updateBlock(dataType: DataType, data: string, id: BlockId): Promise {
197 | let payload = {
198 | dataType: dataType,
199 | data: data,
200 | id: id
201 | }
202 | let url = '/api/block/updateBlock';
203 | return request(url, payload);
204 | }
205 |
206 |
207 | export async function deleteBlock(id: BlockId): Promise {
208 | let data = {
209 | id: id
210 | }
211 | let url = '/api/block/deleteBlock';
212 | return request(url, data);
213 | }
214 |
215 |
216 | export async function moveBlock(id: BlockId, previousID?: PreviousID, parentID?: ParentID): Promise {
217 | let data = {
218 | id: id,
219 | previousID: previousID,
220 | parentID: parentID
221 | }
222 | let url = '/api/block/moveBlock';
223 | return request(url, data);
224 | }
225 |
226 |
227 | export async function getBlockKramdown(id: BlockId): Promise {
228 | let data = {
229 | id: id
230 | }
231 | let url = '/api/block/getBlockKramdown';
232 | return request(url, data);
233 | }
234 |
235 |
236 | export async function getChildBlocks(id: BlockId): Promise {
237 | let data = {
238 | id: id
239 | }
240 | let url = '/api/block/getChildBlocks';
241 | return request(url, data);
242 | }
243 |
244 | export async function transferBlockRef(fromID: BlockId, toID: BlockId, refIDs: BlockId[]) {
245 | let data = {
246 | fromID: fromID,
247 | toID: toID,
248 | refIDs: refIDs
249 | }
250 | let url = '/api/block/transferBlockRef';
251 | return request(url, data);
252 | }
253 |
254 | // **************************************** Attributes ****************************************
255 | export async function setBlockAttrs(id: BlockId, attrs: { [key: string]: string }) {
256 | let data = {
257 | id: id,
258 | attrs: attrs
259 | }
260 | let url = '/api/attr/setBlockAttrs';
261 | return request(url, data);
262 | }
263 |
264 |
265 | export async function getBlockAttrs(id: BlockId): Promise<{ [key: string]: string }> {
266 | let data = {
267 | id: id
268 | }
269 | let url = '/api/attr/getBlockAttrs';
270 | return request(url, data);
271 | }
272 |
273 | // **************************************** SQL ****************************************
274 |
275 | export async function sql(sql: string): Promise {
276 | let sqldata = {
277 | stmt: sql,
278 | };
279 | let url = '/api/query/sql';
280 | return request(url, sqldata);
281 | }
282 |
283 | export async function getBlockByID(blockId: string): Promise {
284 | let sqlScript = `select * from blocks where id ='${blockId}'`;
285 | let data = await sql(sqlScript);
286 | return data[0];
287 | }
288 |
289 | // **************************************** Template ****************************************
290 |
291 | export async function render(id: DocumentId, path: string): Promise {
292 | let data = {
293 | id: id,
294 | path: path
295 | }
296 | let url = '/api/template/render';
297 | return request(url, data);
298 | }
299 |
300 |
301 | export async function renderSprig(template: string): Promise {
302 | let url = '/api/template/renderSprig';
303 | return request(url, { template: template });
304 | }
305 |
306 | // **************************************** File ****************************************
307 |
308 | export async function getFile(path: string): Promise {
309 | let data = {
310 | path: path
311 | }
312 | let url = '/api/file/getFile';
313 | try {
314 | let file = await fetchSyncPost(url, data);
315 | return file;
316 | } catch (error_msg) {
317 | return null;
318 | }
319 | }
320 |
321 | export async function putFile(path: string, isDir: boolean, file: any) {
322 | let form = new FormData();
323 | form.append('path', path);
324 | form.append('isDir', isDir.toString());
325 | // Copyright (c) 2023, terwer.
326 | // https://github.com/terwer/siyuan-plugin-importer/blob/v1.4.1/src/api/kernel-api.ts
327 | form.append('modTime', Math.floor(Date.now() / 1000).toString());
328 | form.append('file', file);
329 | let url = '/api/file/putFile';
330 | return request(url, form);
331 | }
332 |
333 | export async function removeFile(path: string) {
334 | let data = {
335 | path: path
336 | }
337 | let url = '/api/file/removeFile';
338 | return request(url, data);
339 | }
340 |
341 |
342 |
343 | export async function readDir(path: string): Promise {
344 | let data = {
345 | path: path
346 | }
347 | let url = '/api/file/readDir';
348 | return request(url, data);
349 | }
350 |
351 |
352 | // **************************************** Export ****************************************
353 |
354 | export async function exportMdContent(id: DocumentId): Promise {
355 | let data = {
356 | id: id
357 | }
358 | let url = '/api/export/exportMdContent';
359 | return request(url, data);
360 | }
361 |
362 | export async function exportResources(paths: string[], name: string): Promise {
363 | let data = {
364 | paths: paths,
365 | name: name
366 | }
367 | let url = '/api/export/exportResources';
368 | return request(url, data);
369 | }
370 |
371 | // **************************************** Convert ****************************************
372 |
373 | export type PandocArgs = string;
374 | export async function pandoc(args: PandocArgs[]) {
375 | let data = {
376 | args: args
377 | }
378 | let url = '/api/convert/pandoc';
379 | return request(url, data);
380 | }
381 |
382 | // **************************************** Notification ****************************************
383 |
384 | // /api/notification/pushMsg
385 | // {
386 | // "msg": "test",
387 | // "timeout": 7000
388 | // }
389 | export async function pushMsg(msg: string, timeout: number = 7000) {
390 | let payload = {
391 | msg: msg,
392 | timeout: timeout
393 | };
394 | let url = "/api/notification/pushMsg";
395 | return request(url, payload);
396 | }
397 |
398 | export async function pushErrMsg(msg: string, timeout: number = 7000) {
399 | let payload = {
400 | msg: msg,
401 | timeout: timeout
402 | };
403 | let url = "/api/notification/pushErrMsg";
404 | return request(url, payload);
405 | }
406 |
407 | // **************************************** Network ****************************************
408 | export async function forwardProxy(
409 | url: string, method: string = 'GET', payload: any = {},
410 | headers: any[] = [], timeout: number = 7000, contentType: string = "text/html"
411 | ): Promise {
412 | let data = {
413 | url: url,
414 | method: method,
415 | timeout: timeout,
416 | contentType: contentType,
417 | headers: headers,
418 | payload: payload
419 | }
420 | let url1 = '/api/network/forwardProxy';
421 | return request(url1, data);
422 | }
423 |
424 |
425 | // **************************************** System ****************************************
426 |
427 | export async function bootProgress(): Promise {
428 | return request('/api/system/bootProgress', {});
429 | }
430 |
431 |
432 | export async function version(): Promise {
433 | return request('/api/system/version', {});
434 | }
435 |
436 |
437 | export async function currentTime(): Promise {
438 | return request('/api/system/currentTime', {});
439 | }
440 |
--------------------------------------------------------------------------------