diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index f06235c..0000000 --- a/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules -dist diff --git a/.eslintrc.cjs b/.eslintrc.cjs deleted file mode 100644 index 0781605..0000000 --- a/.eslintrc.cjs +++ /dev/null @@ -1,38 +0,0 @@ -module.exports = { - extends: [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:svelte/recommended", - "turbo", - "prettier", - ], - - parser: "@typescript-eslint/parser", - - overrides: [ - { - files: ["*.svelte"], - parser: "svelte-eslint-parser", - // Parse the script in `.svelte` as TypeScript by adding the following configuration. - parserOptions: { - parser: "@typescript-eslint/parser", - }, - }, - ], - - plugins: ["@typescript-eslint", "prettier"], - - rules: { - // Note: you must disable the base rule as it can report incorrect errors - semi: "off", - quotes: "off", - "no-undef": "off", - "@typescript-eslint/no-var-requires": "off", - "@typescript-eslint/no-this-alias": "off", - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/no-unused-vars": "off", - "@typescript-eslint/no-explicit-any": "off", - "turbo/no-undeclared-env-vars": "off", - "prettier/prettier": "error", - }, -} diff --git a/.gitignore b/.gitignore index e18ba80..71ca752 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .vscode .DS_Store pnpm-lock.yaml +package-lock.json package.zip node_modules dev diff --git a/CHANGELOG.md b/CHANGELOG.md index ce36ec8..af04807 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # Changelog -## 0.3.5 2024-03 +## v0.3.5 2024-04-30 + +* [Add `direction` to plugin method `Setting.addItem`](https://github.com/siyuan-note/siyuan/issues/11183) + ## 0.3.4 2024-02-20 diff --git a/README.md b/README.md index 981ca43..a675463 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [中文版](./README_zh_CN.md) -> Consistent with [siyuan/plugin-sample](https://github.com/siyuan-note/plugin-sample) [v0.3.4](https://github.com/siyuan-note/plugin-sample/tree/v0.3.4) +> Consistent with [siyuan/plugin-sample](https://github.com/siyuan-note/plugin-sample) [v0.3.5](https://github.com/siyuan-note/plugin-sample/tree/v0.3.5) @@ -11,7 +11,9 @@ 2. Use symbolic linking instead of putting the project into the plugins directory program development 3. Built-in support for the svelte framework - > If don't want svelte, turn to this template: [frostime/plugin-sample-vite](https://github.com/frostime/plugin-sample-vite) + > **If don't want svelte, turn to this template**: [frostime/plugin-sample-vite](https://github.com/frostime/plugin-sample-vite) + > + > **We also provide with a vite+solidjs template**: [frostime/plugin-sample-vite-solidjs](https://github.com/frostime/plugin-sample-vite-solidjs) 4. Provides a github action template to automatically generate package.zip and upload to new release @@ -65,8 +67,11 @@ complete the following tasks: * Meta information about the plugin itself, such as plugin description and readme * `description` and `readme` fields in plugin.json, and the corresponding README*.md file * Text used in the plugin, such as button text and tooltips - * src/i18n/*.json language configuration files + * public/i18n/*.json language configuration files * Use `this.i18.key` to get the text in the code +* YAML Support + * This template specifically supports I18n based on YAML syntax, see `public/i18n/zh_CN.yaml` + * During compilation, the defined YAML files will be automatically translated into JSON files and placed in the dist or dev directory. It is recommended that the plugin supports at least English and Simplified Chinese, so that more people can use it more conveniently. @@ -246,43 +251,12 @@ Related APIs can be found at: `/api/file/*` (e.g., `/api/file/getFile`). ### 2. Daily Note Attribute Specifications -When creating a diary in SiYuan, a custom-dailynote-yyyymmdd attribute will be automatically added to the document to distinguish it from regular documents. +When creating a daily note in SiYuan, a custom-dailynote-yyyymmdd attribute will be automatically added to the document to distinguish it from regular documents. > For more details, please refer to [Github Issue #9807](https://github.com/siyuan-note/siyuan/issues/9807). Developers should pay attention to the following when developing the functionality to manually create Daily Notes: -- If `/api/filetree/createDailyNote` is called to create a diary, the attribute will be automatically added to the document, and developers do not need to handle it separately. -- If a document is created manually by developer's code (e.g., using the `createDocWithMd` API to create a diary), please manually add this attribute to the document. +* If `/api/filetree/createDailyNote` is called to create a daily note, the attribute will be automatically added to the document, and developers do not need to handle it separately +* If a document is created manually by developer's code (e.g., using the `createDocWithMd` API to create a daily note), please manually add this attribute to the document -Here is a reference code: - -```ts -/* - * Copyright (c) 2023 by frostime. All Rights Reserved. - * @Author : frostime - * @Url : https://github.com/frostime/siyuan-dailynote-today/blob/v1.3.0/src/func/dailynote/dn-attr.ts - */ - -export function formatDate(date?: Date, sep=''): string { - date = date === undefined ? new Date() : date; - let year = date.getFullYear(); - let month = date.getMonth() + 1; - let day = date.getDate(); - return `${year}${sep}${month < 10 ? '0' + month : month}${sep}${day < 10 ? '0' + day : day}`; -} - -/** - * Set custom attribute: `custom-dailynote-yyyyMMdd`: yyyyMMdd - * https://github.com/siyuan-note/siyuan/issues/9807 - * @param doc_id Id of daily note - */ -export function setCustomDNAttr(doc_id: string, date?: Date) { - let td = formatDate(date); - let attr = `custom-dailynote-${td}`; - // 构建 attr: td - let attrs: { [key: string]: string } = {}; - attrs[attr] = td; - serverApi.setBlockAttrs(doc_id, attrs); -} -``` diff --git a/README_zh_CN.md b/README_zh_CN.md index b2d3ad3..d0f153c 100644 --- a/README_zh_CN.md +++ b/README_zh_CN.md @@ -4,13 +4,15 @@ [English](./README.md) -> 本例同 [siyuan/plugin-sample](https://github.com/siyuan-note/plugin-sample) [v0.3.4](https://github.com/siyuan-note/plugin-sample/tree/v0.3.4) +> 本例同 [siyuan/plugin-sample](https://github.com/siyuan-note/plugin-sample) [v0.3.5](https://github.com/siyuan-note/plugin-sample/tree/v0.3.5) 1. 使用 vite 打包 2. 使用符号链接、而不是把项目放到插件目录下的模式进行开发 3. 内置对 svelte 框架的支持 - > 如果不想要 svelte,请移步 [frostime/plugin-sample-vite](https://github.com/frostime/plugin-sample-vite) + > **如果不想要 svelte,请移步这个模板:** [frostime/plugin-sample-vite](https://github.com/frostime/plugin-sample-vite) + > + > **这里还提供了一个 vite+solidjs 的模板**: [frostime/plugin-sample-vite-solidjs](https://github.com/frostime/plugin-sample-vite-solidjs) 4. 提供一个github action 模板,能自动生成package.zip并上传到新版本中 @@ -62,9 +64,12 @@ * 插件自身的元信息,比如插件描述和自述文件 * plugin.json 中的 `description` 和 `readme` 字段,以及对应的 README*.md 文件 * 插件中使用的文本,比如按钮文字和提示信息 - * src/i18n/*.json 语言配置文件 + * public/i18n/*.json 语言配置文件 * 代码中使用 `this.i18.key` 获取文本 * 最后在 plugin.json 中的 `i18n` 字段中声明该插件支持的语言 +* yaml 支持 + * 本模板特别支持基于 Yaml 语法的 I18n,见 `public/i18n/zh_CN.yaml` + * 编译时,会自动把定义的 yaml 文件翻译成 json 文件放到 dist 或 dev 目录下 建议插件至少支持英文和简体中文,这样可以方便更多人使用。 @@ -234,47 +239,15 @@ PR 社区集市仓库。 插件或者外部扩展如果有直接读取或者写入 data 下文件的需求,请通过调用内核 API 来实现,**不要自行调用 `fs` 或者其他 electron、nodejs API**,否则可能会导致数据同步时分块丢失,造成云端数据损坏。 -相关 API 见: `/api/file/*`(例如 `/api/file/getFile` 等)。 +相关 API 见 `/api/file/*`(例如 `/api/file/getFile` 等)。 ### 2. Daily Note 属性规范 -思源在创建日记的时候会自动为文档添加 custom-dailynote-yyyymmdd 属性, 以方便将日记文档同普通文档区分。 +思源在创建日记的时候会自动为文档添加 custom-dailynote-yyyymmdd 属性,以方便将日记文档同普通文档区分。 > 详情请见 [Github Issue #9807](https://github.com/siyuan-note/siyuan/issues/9807)。 开发者在开发手动创建 Daily Note 的功能时请注意: -- 如果调用了 `/api/filetree/createDailyNote` 创建日记,那么文档会自动添加这个属性,无需开发者特别处理。 -- 如果是开发者代码手动创建文档(例如使用 `createDocWithMd` API 创建日记),请手动为文档添加该属性。 - -参考代码: - -```ts -/* - * Copyright (c) 2023 by frostime. All Rights Reserved. - * @Author : frostime - * @Url : https://github.com/frostime/siyuan-dailynote-today/blob/v1.3.0/src/func/dailynote/dn-attr.ts - */ - -export function formatDate(date?: Date, sep=''): string { - date = date === undefined ? new Date() : date; - let year = date.getFullYear(); - let month = date.getMonth() + 1; - let day = date.getDate(); - return `${year}${sep}${month < 10 ? '0' + month : month}${sep}${day < 10 ? '0' + day : day}`; -} - -/** - * Set custom attribute: `custom-dailynote-yyyyMMdd`: yyyyMMdd - * https://github.com/siyuan-note/siyuan/issues/9807 - * @param doc_id Id of daily note - */ -export function setCustomDNAttr(doc_id: string, date?: Date) { - let td = formatDate(date); - let attr = `custom-dailynote-${td}`; - // 构建 attr: td - let attrs: { [key: string]: string } = {}; - attrs[attr] = td; - serverApi.setBlockAttrs(doc_id, attrs); -} -``` +* 如果调用了 `/api/filetree/createDailyNote` 创建日记,那么文档会自动添加这个属性,无需开发者特别处理 +* 如果是开发者代码手动创建文档(例如使用 `createDocWithMd` API 创建日记),请手动为文档添加该属性 diff --git a/package.json b/package.json index 24108a9..fd49b9c 100644 --- a/package.json +++ b/package.json @@ -1,33 +1,34 @@ { "name": "plugin-sample-vite-svelte", - "version": "0.3.2", + "version": "0.3.5", "type": "module", "description": "This is a sample plugin based on vite and svelte for Siyuan (https://b3log.org/siyuan)", "repository": "", "homepage": "", - "author": "", + "author": "frostime", "license": "MIT", "scripts": { "make-link": "node --no-warnings ./scripts/make_dev_link.js", "dev": "vite build --watch", - "build": "vite build" + "build": "vite build", + "make-install": "vite build && node --no-warnings ./scripts/make_install.js" }, "devDependencies": { - "@sveltejs/vite-plugin-svelte": "^2.4.1", + "@sveltejs/vite-plugin-svelte": "^3.0.0", "@tsconfig/svelte": "^4.0.1", "@types/node": "^20.3.0", - "eslint": "^8.42.0", "fast-glob": "^3.2.12", "glob": "^7.2.3", + "js-yaml": "^4.1.0", "minimist": "^1.2.8", "rollup-plugin-livereload": "^2.0.5", "sass": "^1.63.3", - "siyuan": "0.9.4", - "svelte": "^3.59.1", + "siyuan": "0.9.9", + "svelte": "^4.2.0", "ts-node": "^10.9.1", "typescript": "^5.1.3", - "vite": "^4.5.2", - "vite-plugin-static-copy": "^0.15.0", + "vite": "^5.0.0", + "vite-plugin-static-copy": "^1.0.2", "vite-plugin-zip-pack": "^1.0.5" } } diff --git a/plugin.json b/plugin.json index b810268..865a240 100644 --- a/plugin.json +++ b/plugin.json @@ -2,8 +2,8 @@ "name": "plugin-sample-vite-svelte", "author": "frostime", "url": "https://github.com/siyuan-note/plugin-sample-vite-svelte", - "version": "0.3.4", - "minAppVersion": "3.0.0", + "version": "0.3.5", + "minAppVersion": "3.0.12", "backends": [ "windows", "linux", @@ -32,7 +32,7 @@ }, "funding": { "custom": [ - "https://afdian.net/a/frostime" + "" ] }, "keywords": [ diff --git a/public/i18n/zh_CN.yaml b/public/i18n/zh_CN.yaml new file mode 100644 index 0000000..16099a3 --- /dev/null +++ b/public/i18n/zh_CN.yaml @@ -0,0 +1,21 @@ +--- +addTopBarIcon: 使用插件添加一个顶栏按钮 +cancel: 取消 +save: 保存 +byeMenu: 再见,菜单! +helloPlugin: 你好,插件! +byePlugin: 再见,插件! +showDialog: 弹出一个对话框 +removedData: 数据已删除 +confirmRemove: 确认删除 ${name} 中的数据? +insertEmoji: 插入表情 +removeSpace: 移除空格 +getTab: 在日志中打印出已打开的所有自定义页签 +name: 思源 +hello: + makesure: 使用这个模板之前,请阅读官方教程, + 确保自己已经理解了插件的基本开发流程。 +hintTitle: 关于 +hintDesc: "\U0001F517 + plugin-sample-vite-svelte
\U0001F4BB @frostime
\U0001F4BB @88250
\U0001F4BB + @zxkmm" diff --git a/scripts/make_install.js b/scripts/make_install.js new file mode 100644 index 0000000..9c2cd9b --- /dev/null +++ b/scripts/make_install.js @@ -0,0 +1,191 @@ +import fs from 'fs'; +import path from 'path'; +import http from 'node:http'; +import readline from 'node:readline'; + + +//************************************ Write you dir here ************************************ + +let targetDir = ''; // the target directory of the plugin, '*/data/plugin' +//******************************************************************************************** + +const log = (info) => console.log(`\x1B[36m%s\x1B[0m`, info); +const error = (info) => console.log(`\x1B[31m%s\x1B[0m`, info); + +let POST_HEADER = { + // "Authorization": `Token ${token}`, + "Content-Type": "application/json", +} + +async function myfetch(url, options) { + //使用 http 模块,从而兼容那些不支持 fetch 的 nodejs 版本 + return new Promise((resolve, reject) => { + let req = http.request(url, options, (res) => { + let data = ''; + res.on('data', (chunk) => { + data += chunk; + }); + res.on('end', () => { + resolve({ + ok: true, + status: res.statusCode, + json: () => JSON.parse(data) + }); + }); + }); + req.on('error', (e) => { + reject(e); + }); + req.end(); + }); +} + +async function getSiYuanDir() { + let url = 'http://127.0.0.1:6806/api/system/getWorkspaces'; + let conf = {}; + try { + let response = await myfetch(url, { + method: 'POST', + headers: POST_HEADER + }); + if (response.ok) { + conf = await response.json(); + } else { + error(`\tHTTP-Error: ${response.status}`); + return null; + } + } catch (e) { + error(`\tError: ${e}`); + error("\tPlease make sure SiYuan is running!!!"); + return null; + } + return conf.data; +} + +async function chooseTarget(workspaces) { + let count = workspaces.length; + log(`>>> Got ${count} SiYuan ${count > 1 ? 'workspaces' : 'workspace'}`) + for (let i = 0; i < workspaces.length; i++) { + log(`\t[${i}] ${workspaces[i].path}`); + } + + if (count == 1) { + return `${workspaces[0].path}/data/plugins`; + } else { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); + let index = await new Promise((resolve, reject) => { + rl.question(`\tPlease select a workspace[0-${count-1}]: `, (answer) => { + resolve(answer); + }); + }); + rl.close(); + return `${workspaces[index].path}/data/plugins`; + } +} + +log('>>> Try to visit constant "targetDir" in make_install.js...') + +if (targetDir === '') { + log('>>> Constant "targetDir" is empty, try to get SiYuan directory automatically....') + let res = await getSiYuanDir(); + + if (res === null || res === undefined || res.length === 0) { + error('>>> Can not get SiYuan directory automatically'); + + process.exit(1); + } else { + targetDir = await chooseTarget(res); + } + + log(`>>> Successfully got target directory: ${targetDir}`); +} + +//Check +if (!fs.existsSync(targetDir)) { + error(`Failed! plugin directory not exists: "${targetDir}"`); + error(`Please set the plugin directory in scripts/make_install.js`); + process.exit(1); +} + + +//check if plugin.json exists +if (!fs.existsSync('./plugin.json')) { + //change dir to parent + process.chdir('../'); + if (!fs.existsSync('./plugin.json')) { + error('Failed! plugin.json not found'); + process.exit(1); + } +} + +//load plugin.json +const plugin = JSON.parse(fs.readFileSync('./plugin.json', 'utf8')); +const name = plugin?.name; +if (!name || name === '') { + error('Failed! Please set plugin name in plugin.json'); + process.exit(1); +} + +const distDir = `${process.cwd()}/dist`; +//mkdir if not exists +if (!fs.existsSync(distDir)) { + fs.mkdirSync(distDir); +} + +function cmpPath(path1, path2) { + path1 = path1.replace(/\\/g, '/'); + path2 = path2.replace(/\\/g, '/'); + // sepertor at tail + if (path1[path1.length - 1] !== '/') { + path1 += '/'; + } + if (path2[path2.length - 1] !== '/') { + path2 += '/'; + } + return path1 === path2; +} + +const targetPath = `${targetDir}/${name}`; + + +function copyDirectory(srcDir, dstDir) { + if (!fs.existsSync(dstDir)) { + fs.mkdirSync(dstDir); + log(`Created directory ${dstDir}`); + } + //将 distDir 下的所有文件复制到 targetPath + fs.readdir(srcDir, { withFileTypes: true }, (err, files) => { + if (err) { + error('Error reading source directory:', err); + return; + } + + // 遍历源目录中的所有文件和子目录 + files.forEach((file) => { + const src = path.join(srcDir, file.name); + const dst = path.join(dstDir, file.name); + + // 判断当前项是文件还是目录 + if (file.isDirectory()) { + // 如果是目录,则递归调用复制函数复制子目录 + copyDirectory(src, dst); + } else { + // 如果是文件,则复制文件到目标目录 + fs.copyFile(src, dst, (err) => { + if (err) { + error('Error copying file:' + err); + } else { + log(`Copied file: ${src} --> ${dst}`); + } + }); + } + }); + log(`Copied ${distDir} to ${targetPath}`); + }); +} +copyDirectory(distDir, targetPath); + + diff --git a/src/api.ts b/src/api.ts index c227518..0a421ce 100644 --- a/src/api.ts +++ b/src/api.ts @@ -6,7 +6,7 @@ * API 文档见 [API_zh_CN.md](https://github.com/siyuan-note/siyuan/blob/master/API_zh_CN.md) */ -import { fetchSyncPost, IWebSocketData } from "siyuan"; +import { fetchPost, fetchSyncPost, IWebSocketData } from "siyuan"; export async function request(url: string, data: any) { @@ -328,12 +328,11 @@ export async function getFile(path: string): Promise { path: path } let url = '/api/file/getFile'; - try { - let file = await fetchSyncPost(url, data); - return file; - } catch (error_msg) { - return null; - } + return new Promise((resolve, _) => { + fetchPost(url, data, (content: any) => { + resolve(content) + }); + }); } export async function putFile(path: string, isDir: boolean, file: any) { diff --git a/src/index.ts b/src/index.ts index b950a7b..0a885c1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -31,7 +31,7 @@ const DOCK_TYPE = "dock_tab"; export default class PluginSample extends Plugin { - private customTab: () => IModel; + customTab: () => IModel; private isMobile: boolean; private blockIconEventBindThis = this.blockIconEvent.bind(this); private settingUtils: SettingUtils; @@ -163,20 +163,23 @@ export default class PluginSample extends Plugin { } }); - this.settingUtils = new SettingUtils(this, STORAGE_NAME); - - try { - this.settingUtils.load(); - } catch (error) { - console.error("Error loading settings storage, probably empty config json:", error); - } - + this.settingUtils = new SettingUtils({ + plugin: this, name: STORAGE_NAME + }); this.settingUtils.addItem({ key: "Input", value: "", type: "textinput", title: "Readonly text", description: "Input description", + action: { + // Called when focus is lost and content changes + callback: () => { + // Return data and save it in real time + let value = this.settingUtils.takeAndSave("Input"); + console.log(value); + } + } }); this.settingUtils.addItem({ key: "InputArea", @@ -184,6 +187,14 @@ export default class PluginSample extends Plugin { type: "textarea", title: "Readonly text", description: "Input description", + // Called when focus is lost and content changes + action: { + callback: () => { + // Read data in real time + let value = this.settingUtils.take("InputArea"); + console.log(value); + } + } }); this.settingUtils.addItem({ key: "Check", @@ -191,16 +202,31 @@ export default class PluginSample extends Plugin { type: "checkbox", title: "Checkbox text", description: "Check description", + action: { + callback: () => { + // Return data and save it in real time + let value = !this.settingUtils.get("Check"); + this.settingUtils.set("Check", value); + console.log(value); + } + } }); this.settingUtils.addItem({ key: "Select", value: 1, type: "select", - title: "Readonly text", + title: "Select", description: "Select description", options: { 1: "Option 1", 2: "Option 2" + }, + action: { + callback: () => { + // Read data in real time + let value = this.settingUtils.take("Select"); + console.log(value); + } } }); this.settingUtils.addItem({ @@ -209,10 +235,18 @@ export default class PluginSample extends Plugin { type: "slider", title: "Slider text", description: "Slider description", + direction: "column", slider: { min: 0, max: 100, step: 1, + }, + action:{ + callback: () => { + // Read data in real time + let value = this.settingUtils.take("Slider"); + console.log(value); + } } }); this.settingUtils.addItem({ @@ -228,6 +262,28 @@ export default class PluginSample extends Plugin { } } }); + this.settingUtils.addItem({ + key: "Custom Element", + value: "", + type: "custom", + direction: "row", + title: "Custom Element", + description: "Custom Element description", + //Any custom element must offer the following methods + createElement: (currentVal: any) => { + let div = document.createElement('div'); + div.style.border = "1px solid var(--b3-theme-primary)"; + div.contentEditable = "true"; + div.textContent = currentVal; + return div; + }, + getEleVal: (ele: HTMLElement) => { + return ele.textContent; + }, + setEleVal: (ele: HTMLElement, val: any) => { + ele.textContent = val; + } + }); this.settingUtils.addItem({ key: "onlyEnableListedDevices", value: false, @@ -276,6 +332,13 @@ export default class PluginSample extends Plugin { description: this.i18n.hintDesc, }); + try { + this.settingUtils.load(); + } catch (error) { + console.error("Error loading settings storage, probably empty config json:", error); + } + + this.protyleSlash = [{ filter: ["insert emoji 😊", "插入表情 😊", "crbqwx"], html: `
${this.i18n.insertEmoji}😊
`, @@ -379,7 +442,6 @@ export default class PluginSample extends Plugin { async onunload() { console.log(this.i18n.byePlugin); - await this.settingUtils.save(); showMessage("Goodbye SiYuan Plugin"); console.log("onunload"); } @@ -408,7 +470,7 @@ export default class PluginSample extends Plugin { let dialog = new Dialog({ title: "SettingPannel", content: `
`, - width: "600px", + width: "800px", destroyCallback: (options) => { console.log("destroyCallback", options); //You'd better destroy the component when the dialog is closed @@ -460,7 +522,7 @@ export default class PluginSample extends Plugin { title: `SiYuan ${Constants.SIYUAN_VERSION}`, content: `
`, width: this.isMobile ? "92vw" : "720px", - destroyCallback(options) { + destroyCallback() { // hello.$destroy(); }, }); diff --git a/src/libs/const.ts b/src/libs/const.ts new file mode 100644 index 0000000..2dd3e06 --- /dev/null +++ b/src/libs/const.ts @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2024 by frostime. All Rights Reserved. + * @Author : frostime + * @Date : 2024-06-08 20:36:30 + * @FilePath : /src/libs/const.ts + * @LastEditTime : 2024-06-08 20:48:06 + * @Description : + */ + + +export const BlockType2NodeType: {[key in BlockType]: string} = { + d: 'NodeDocument', + p: 'NodeParagraph', + query_embed: 'NodeBlockQueryEmbed', + l: 'NodeList', + i: 'NodeListItem', + h: 'NodeHeading', + iframe: 'NodeIFrame', + tb: 'NodeThematicBreak', + b: 'NodeBlockquote', + s: 'NodeSuperBlock', + c: 'NodeCodeBlock', + widget: 'NodeWidget', + t: 'NodeTable', + html: 'NodeHTMLBlock', + m: 'NodeMathBlock', + av: 'NodeAttributeView', + audio: 'NodeAudio' +} + + +export const NodeIcons = { + NodeAttributeView: { + icon: "iconDatabase" + }, + NodeAudio: { + icon: "iconRecord" + }, + NodeBlockQueryEmbed: { + icon: "iconSQL" + }, + NodeBlockquote: { + icon: "iconQuote" + }, + NodeCodeBlock: { + icon: "iconCode" + }, + NodeDocument: { + icon: "iconFile" + }, + NodeHTMLBlock: { + icon: "iconHTML5" + }, + NodeHeading: { + icon: "iconHeadings", + subtypes: { + h1: { icon: "iconH1" }, + h2: { icon: "iconH2" }, + h3: { icon: "iconH3" }, + h4: { icon: "iconH4" }, + h5: { icon: "iconH5" }, + h6: { icon: "iconH6" } + } + }, + NodeIFrame: { + icon: "iconLanguage" + }, + NodeList: { + subtypes: { + o: { icon: "iconOrderedList" }, + t: { icon: "iconCheck" }, + u: { icon: "iconList" } + } + }, + NodeListItem: { + icon: "iconListItem" + }, + NodeMathBlock: { + icon: "iconMath" + }, + NodeParagraph: { + icon: "iconParagraph" + }, + NodeSuperBlock: { + icon: "iconSuper" + }, + NodeTable: { + icon: "iconTable" + }, + NodeThematicBreak: { + icon: "iconLine" + }, + NodeVideo: { + icon: "iconVideo" + }, + NodeWidget: { + icon: "iconBoth" + } +}; diff --git a/src/libs/dialog.ts b/src/libs/dialog.ts new file mode 100644 index 0000000..ea2b187 --- /dev/null +++ b/src/libs/dialog.ts @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2024 by frostime. All Rights Reserved. + * @Author : frostime + * @Date : 2024-03-23 21:37:33 + * @FilePath : /src/libs/dialog.ts + * @LastEditTime : 2024-06-01 16:28:30 + * @Description : Kits about dialogs + */ +import { Dialog } from "siyuan"; + +export const inputDialog = (args: { + title: string, placeholder?: string, defaultText?: string, + confirm?: (text: string) => void, cancel?: () => void, + width?: string, height?: string +}) => { + const dialog = new Dialog({ + title: args.title, + content: `
+
+
+
+
+ +
`, + width: args.width ?? "520px", + height: args.height + }); + const target: HTMLTextAreaElement = dialog.element.querySelector(".b3-dialog__content>div.ft__breakword>textarea"); + const btnsElement = dialog.element.querySelectorAll(".b3-button"); + btnsElement[0].addEventListener("click", () => { + if (args?.cancel) { + args.cancel(); + } + dialog.destroy(); + }); + btnsElement[1].addEventListener("click", () => { + if (args?.confirm) { + args.confirm(target.value); + } + dialog.destroy(); + }); +}; + +export const inputDialogSync = async (args: { + title: string, placeholder?: string, defaultText?: string, + width?: string, height?: string +}) => { + return new Promise((resolve) => { + let newargs = { + ...args, confirm: (text) => { + resolve(text); + }, cancel: () => { + resolve(null); + } + }; + inputDialog(newargs); + }); +} + + +interface IConfirmDialogArgs { + title: string; + content: string | HTMLElement; + confirm?: (ele?: HTMLElement) => void; + cancel?: (ele?: HTMLElement) => void; + width?: string; + height?: string; +} + +export const confirmDialog = (args: IConfirmDialogArgs) => { + const { title, content, confirm, cancel, width, height } = args; + + const dialog = new Dialog({ + title, + content: `
+
+
+
+
+
+ +
`, + width: width, + height: height + }); + + const target: HTMLElement = dialog.element.querySelector(".b3-dialog__content>div.ft__breakword"); + if (typeof content === "string") { + target.innerHTML = content; + } else { + target.appendChild(content); + } + + const btnsElement = dialog.element.querySelectorAll(".b3-button"); + btnsElement[0].addEventListener("click", () => { + if (cancel) { + cancel(target); + } + dialog.destroy(); + }); + btnsElement[1].addEventListener("click", () => { + if (confirm) { + confirm(target); + } + dialog.destroy(); + }); +}; + + +export const confirmDialogSync = async (args: IConfirmDialogArgs) => { + return new Promise((resolve) => { + let newargs = { + ...args, confirm: (ele: HTMLElement) => { + resolve(ele); + }, cancel: (ele: HTMLElement) => { + resolve(ele); + } + }; + confirmDialog(newargs); + }); +}; diff --git a/src/libs/index.d.ts b/src/libs/index.d.ts index 04b53f0..27a27ed 100644 --- a/src/libs/index.d.ts +++ b/src/libs/index.d.ts @@ -1,10 +1,17 @@ -type TSettingItemType = "checkbox" | "select" | "textinput" | "textarea" | "number" | "slider" | "button" | "hint"; -interface ISettingItem { +/* + * Copyright (c) 2024 by frostime. All Rights Reserved. + * @Author : frostime + * @Date : 2024-04-19 18:30:12 + * @FilePath : /src/libs/index.d.ts + * @LastEditTime : 2024-04-30 16:39:54 + * @Description : + */ +type TSettingItemType = "checkbox" | "select" | "textinput" | "textarea" | "number" | "slider" | "button" | "hint" | "custom"; + +interface ISettingItemCore { + type: TSettingItemType; key: string; value: any; - type: TSettingItemType; - title: string; - description?: string; placeholder?: string; slider?: { min: number; @@ -17,3 +24,20 @@ interface ISettingItem { callback: () => void; } } + +interface ISettingItem extends ISettingItemCore { + title: string; + description: string; + direction?: "row" | "column"; +} + + +//Interface for setting-utils +interface ISettingUtilsItem extends ISettingItem { + action?: { + callback: () => void; + } + createElement?: (currentVal: any) => HTMLElement; + getEleVal?: (ele: HTMLElement) => any; + setEleVal?: (ele: HTMLElement, val: any) => void; +} diff --git a/src/libs/item-input.svelte b/src/libs/item-input.svelte new file mode 100644 index 0000000..3a42b45 --- /dev/null +++ b/src/libs/item-input.svelte @@ -0,0 +1,108 @@ + + + +{#if type === "checkbox"} + + +{:else if type === "textinput"} + + +{:else if type === "textarea"} +