diff --git a/auto-i18n.project.yaml b/auto-i18n.project.yaml index 4c49a71..3f17189 100644 --- a/auto-i18n.project.yaml +++ b/auto-i18n.project.yaml @@ -18,12 +18,12 @@ code_files: - src/**/*.ts - src/**/*.svelte dict: - - 思源翻译为 SiYuan - - QueryView, DataView 这些专有名词, 不用翻译, 保持原名 - - "视图组件" 翻译成 "View Component" - - "块" 是思源笔记中的基本单位,翻译成 Block - - 嵌入块 翻译成 Embed Block - - "反向链接" 或者 "反链" 翻译成 "backlink" +# - 思源翻译为 SiYuan +# - QueryView, DataView 这些专有名词, 不用翻译, 保持原名 +# - "视图组件" 翻译成 "View Component" +# - "块" 是思源笔记中的基本单位,翻译成 Block +# - 嵌入块 翻译成 Embed Block +# - "反向链接" 或者 "反链" 翻译成 "backlink" export_dir: src/types i18n_dir: public/i18n i18n_pattern: \(\(`(.+?)`\)\) @@ -31,6 +31,4 @@ i18n_var_mid: pathname i18n_var_prefix: i18n main_file: zh_CN.yaml strategy: diff -global_config: - GPT: - model: doubao-pro-128k + diff --git a/example/exp1.js b/example/exp1.js deleted file mode 100644 index 4a7842a..0000000 --- a/example/exp1.js +++ /dev/null @@ -1,61 +0,0 @@ -//!js -const query = async () => { - let dv = Query.DataView(protyle, item, top); - const createdToday = await Query.sql(` - select * from blocks where type='d' and created like '${Query.utils.today().slice(0, 8)}%' -`); - dv.addmd(`#### 今天创建了 ${createdToday.length} 篇文档:`); - - - const l = [] - createdToday.groupby('box', (box, blocks) => { - l.push(`笔记本 ${Query.utils.notebook(box).name} 共创建: ${blocks.length} 篇文档`) - }); - dv.addcols([ - dv.blocktable(createdToday, ['type', 'hpath', 'box'], { fullwidth: true }), - dv.list(l) - ]) - - - // 今日更新 - const updatedToday = await Query.sql(` - select * from blocks where type='d' and updated like '${Query.utils.today().slice(0, 8)}%' - `); - /** - * @type {Record} - */ - const updatedBins = updatedToday.groupby((block) => { - // 按照小时切分时间段 - const time = Query.utils.asdate(block.updated); - return time.getHours(); - }); - const wordsCounts = Object.fromEntries(Object.entries(updatedBins).map(([hour, blocks]) => { - const words = blocks.reduce((acc, block) => acc + block.content.length, 0); - return [hour, words]; - })); - //0, 23 - const x = Array.from({ length: 24 }, (_, i) => i); - const y = new Array(24).fill(0); - Object.entries(wordsCounts).forEach(([hour, words]) => { - y[hour] = words; - }); - dv.addechartsLine(x, y, { - xlabel: '时间段', - ylabel: '字数', - title: '今日更新字数变化', - height: '300px', - width: '100%', - echartsOption: { - grid: { - left: '5%', - right: '5%', - containLabel: true - } - } - }); - - dv.render(); - -} - -return query(); \ No newline at end of file diff --git a/package.json b/package.json index 761bf0e..faae5b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sy-query-view", - "version": "1.0.0-beta9", + "version": "1.0.0-beta10", "type": "module", "description": "This is a sample plugin based on vite and svelte for Siyuan (https://b3log.org/siyuan). Created with siyuan-plugin-cli v2.4.5.", "repository": "https://github.com/frostime/sy-query-view", diff --git a/plugin.json b/plugin.json index 4301571..79a2784 100644 --- a/plugin.json +++ b/plugin.json @@ -2,7 +2,7 @@ "name": "sy-query-view", "author": "frostime", "url": "https://github.com/frostime/sy-query-view", - "version": "1.0.0-beta9", + "version": "1.0.0-beta10", "minAppVersion": "3.1.14", "backends": [ "windows", diff --git a/example/exp-avs-under-root-doc.js b/public/example/exp-avs-under-root-doc.js similarity index 100% rename from example/exp-avs-under-root-doc.js rename to public/example/exp-avs-under-root-doc.js diff --git a/example/exp-created-docs.js b/public/example/exp-created-docs.js similarity index 100% rename from example/exp-created-docs.js rename to public/example/exp-created-docs.js diff --git a/example/exp-daily-sentence.js b/public/example/exp-daily-sentence.js similarity index 100% rename from example/exp-daily-sentence.js rename to public/example/exp-daily-sentence.js diff --git a/example/exp-doc-backlinks-table.js b/public/example/exp-doc-backlinks-table.js similarity index 100% rename from example/exp-doc-backlinks-table.js rename to public/example/exp-doc-backlinks-table.js diff --git a/example/exp-gpt-chat.js b/public/example/exp-gpt-chat.js similarity index 100% rename from example/exp-gpt-chat.js rename to public/example/exp-gpt-chat.js diff --git a/example/exp-gpt-translate.js b/public/example/exp-gpt-translate.js similarity index 100% rename from example/exp-gpt-translate.js rename to public/example/exp-gpt-translate.js diff --git a/example/exp-outline.js b/public/example/exp-outline.js similarity index 100% rename from example/exp-outline.js rename to public/example/exp-outline.js diff --git a/example/exp-sql-executor.js b/public/example/exp-sql-executor.js similarity index 100% rename from example/exp-sql-executor.js rename to public/example/exp-sql-executor.js diff --git a/example/exp-today-updated.js b/public/example/exp-today-updated.js similarity index 100% rename from example/exp-today-updated.js rename to public/example/exp-today-updated.js diff --git a/public/i18n/en_US.yaml b/public/i18n/en_US.yaml index a6b715f..5bf4eaa 100644 --- a/public/i18n/en_US.yaml +++ b/public/i18n/en_US.yaml @@ -12,6 +12,7 @@ src_core_editorts: src_core_indexts: custom_queryview_error: 'Note: There is a problem with the format of the custom QueryView script, and it cannot be imported normally!' + reload_custom_comp: Reload Custom src_dataquery_componentsts: mermaid_render_failed: Mermaid rendering failed, please check the code src_dataquery_editorts: @@ -38,6 +39,8 @@ src_userhelp_indexts: download: Download help_doc: Help Document help_doc_2: Help Document +src_userhelp_sydocts: + plugin_update_doc: Detected plugin version update, updating documentation, please wait... user_help: ahead_hint: | > 💡 Note: This document is automatically generated by the SiYuan Query&View plugin **{{version}}** for the plugin's README documentation. diff --git a/public/i18n/zh_CN.yaml b/public/i18n/zh_CN.yaml index 0da62a2..1eab369 100644 --- a/public/i18n/zh_CN.yaml +++ b/public/i18n/zh_CN.yaml @@ -7,6 +7,7 @@ src_core_editorts: unusableexteditorcmd: 无法使用外部编辑器命令 {0}, 请检查命令的正确性 src_core_indexts: custom_queryview_error: '注意: 自定义的 QueryView 脚本格式存在问题,无法正常导入!' + reload_custom_comp: 重载自定义组件 src_dataquery_componentsts: mermaid_render_failed: Mermaid 渲染失败, 请检查代码 src_dataquery_editorts: @@ -19,8 +20,7 @@ src_setting_indexts: echarts_renderer_option: Echarts 渲染器,可以为 "canvas" 或 "svg", 默认 "svg" import_failed: 导入失败, 详细情况请检查控制台报错 import_success: 导入成功, 共 {cnt} 个自定义视图 - local_command_desc: | - 本地用于打开代码编辑器的命令, 默认为 "code -w {{filepath}}" 代表使用 VSCode 打开, 其中 {{filepath}} 在运行时会被替换为真实的代码文件 + local_command_desc: '本地用于打开代码编辑器的命令, 默认为 "code -w {{filepath}}" 代表使用 VSCode 打开, 其中 {{filepath}} 在运行时会被替换为真实的代码文件' open_local_editor: ✒️ 打开本地编辑器 reload: 重新加载 table_default_columns: 🔑 表格视图默认显示列 @@ -32,6 +32,8 @@ src_userhelp_indexts: download: 下载 help_doc: 帮助文档 help_doc_2: 帮助文档 +src_userhelp_sydocts: + plugin_update_doc: 检查到插件版本已更新,正在更新文档,请稍等... user_help: ahead_hint: | > 💡 注: 本文档由 SiYuan Query&View 插件 **{{version}}** 版自动生成,为插件的 README 文档。 diff --git a/public/types.d.ts b/public/types.d.ts index 31d129c..8352f2d 100644 --- a/public/types.d.ts +++ b/public/types.d.ts @@ -2,7 +2,7 @@ * @name sy-query-view * @author frostime * @version 1.0.0-beta9 - * @updated 2024-12-11T08:30:13.446Z + * @updated 2024-12-11T10:23:43.467Z */ declare module 'siyuan' { diff --git a/src/api.ts b/src/api.ts index 3722365..6e670e6 100644 --- a/src/api.ts +++ b/src/api.ts @@ -385,7 +385,7 @@ export async function removeFile(path: string) { -export async function readDir(path: string): Promise { +export async function readDir(path: string): Promise { let data = { path: path } diff --git a/src/core/data-view.ts b/src/core/data-view.ts index 8535f8b..7ff725e 100644 --- a/src/core/data-view.ts +++ b/src/core/data-view.ts @@ -3,7 +3,7 @@ * @Author : frostime * @Date : 2024-12-02 10:15:04 * @FilePath : /src/core/data-view.ts - * @LastEditTime : 2024-12-10 16:11:54 + * @LastEditTime : 2024-12-11 18:10:46 * @Description : */ import { @@ -219,7 +219,23 @@ export class DataView extends UseStateMixin implements IDataView { this._element = document.createElement("div"); this.lute = getLute(); - this._element.classList.add(styles["data-query-embed"], 'protyle-wysiwyg__embed', 'protyle-custom'); + this._element.classList.add(styles["data-query-embed"], 'protyle-custom'); + /** + * 加了这一行有时候会出现 getBlockInfo 的问题, 样例: + //!js + let dv = Query.DataView(protyle, item, top); + const state = dv.useState('counter', 1); + const button = document.createElement('button'); + button.textContent = '+1'; + button.onclick = (e) => { + state.value += 1; + dv.repaint(); + } + dv.addcols([button, dv.md(`State = ${state()}`)]); + + dv.render(); + */ + // this._element.classList.add('protyle-wysiwyg__embed'); this._element.dataset.id = window.Lute.NewNodeID(); this.thisEmbedNode.lastElementChild.insertAdjacentElement("beforebegin", this._element); @@ -478,6 +494,11 @@ export class DataView extends UseStateMixin implements IDataView { markdown(md: string) { let elem = newViewWrapper(); elem.innerHTML = this.lute.Md2BlockDOM(md); + // elem.querySelectorAll('[contenteditable]').forEach + let editableNodeList = elem.querySelectorAll('[contenteditable="true"]'); + editableNodeList.forEach(node => { + node.setAttribute('contenteditable', 'false'); + }); return elem; } diff --git a/src/core/index.ts b/src/core/index.ts index 925b016..7c614c6 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -3,14 +3,13 @@ * @Author : frostime * @Date : 2024-05-08 15:00:37 * @FilePath : /src/core/index.ts - * @LastEditTime : 2024-12-08 21:34:19 + * @LastEditTime : 2024-12-11 18:17:26 * @Description : * - Fork from project https://github.com/zxhd863943427/siyuan-plugin-data-query * - 基于该项目的 v0.0.7 版本进行修改 */ // import type FMiscPlugin from "@/index"; import { - Plugin, showMessage, } from "siyuan"; @@ -18,9 +17,10 @@ import { embedBlockEvent } from "./editor"; import Query from "./query"; import { finalizeAllDataviews, onProtyleDestroyed } from "./finalize"; import { loadUserCustomView } from "./custom-view"; +import type QueryViewPlugin from ".."; +import { i18n } from ".."; - -const load = (plugin: Plugin) => { +const load = (plugin: QueryViewPlugin) => { globalThis.Query = Query; @@ -38,9 +38,24 @@ const load = (plugin: Plugin) => { console.debug(`Load custom query-view: ${Object.keys(status.custom)}`); } }); + + plugin.registerMenuItem({ + //@ts-ignore + label: i18n.src_core_indexts.reload_custom_comp, + icon: 'iconAccount', + click: async () => { + const result = await loadUserCustomView(); + if (result.ok) { + let cnt = Object.keys(result.custom).length; + showMessage(i18n.src_setting_indexts.import_success.replace('{cnt}', `${cnt}`), 3000, 'info'); + } else { + showMessage(i18n.src_setting_indexts.import_failed, 3000, 'error'); + } + } + }); } -const unload = (plugin: Plugin) => { +const unload = (plugin: QueryViewPlugin) => { finalizeAllDataviews(); diff --git a/src/setting/index.ts b/src/setting/index.ts index d137705..b432829 100644 --- a/src/setting/index.ts +++ b/src/setting/index.ts @@ -106,6 +106,7 @@ export const load = async (plugin: Plugin) => { svg: 'svg' } }); + /* For Debug Only settingUtils.addItem({ type: 'hint', title: 'Session Storage Size', @@ -152,6 +153,7 @@ export const load = async (plugin: Plugin) => { return div; } }); + */ const configs = await settingUtils.load(); delete configs.codeEditor; defaultSetting = { ...defaultSetting, ...configs }; diff --git a/src/types/i18n.d.ts b/src/types/i18n.d.ts index d47dfdb..2b1533b 100644 --- a/src/types/i18n.d.ts +++ b/src/types/i18n.d.ts @@ -9,6 +9,7 @@ interface I18n { }; src_core_indexts: { custom_queryview_error: string; + reload_custom_comp: string; }; src_dataquery_componentsts: { mermaid_render_failed: string; @@ -37,6 +38,9 @@ interface I18n { help_doc: string; help_doc_2: string; }; + src_userhelp_sydocts: { + plugin_update_doc: string; + }; user_help: { ahead_hint: string; }; diff --git a/src/user-help/examples.ts b/src/user-help/examples.ts new file mode 100644 index 0000000..f9dc25f --- /dev/null +++ b/src/user-help/examples.ts @@ -0,0 +1,60 @@ +import { getFileBlob, readDir } from "@/api"; +import type QueryViewPlugin from ".." +import { getLute } from "@/core/lute"; +import { openTab } from "siyuan"; + +let exampleHTML = ''; + +const addSection = (title: string, content: string) => { +let newpart = ` +

${title}

+ + +`; + exampleHTML += newpart; +} + +const loadJsContent = async (path: string) => { + const blob = await getFileBlob(path); + const content = await blob.text(); + return content; +} + +export const useExamples = async (plugin: QueryViewPlugin) => { + const examplePath = `/data/plugins/${plugin.name}/example`; + const files = await readDir(examplePath); + + const routePrefix = `/plugins/${plugin.name}/example`; + for (const file of files) { + let name = file.name; + const filePath = `${examplePath}/${name}`; + const content = await loadJsContent(filePath); + addSection(name, content); + } + + plugin.addTab({ + type: 'js-example', + init() { + const readme = document.createElement('div'); + readme.classList.add('item__readme'); + readme.classList.add('b3-typography'); + readme.classList.add('b3-typography--default'); + readme.innerHTML = exampleHTML; + readme.style.padding = '10px 15px'; + this.element.appendChild(readme); + } + }); + + return { + open() { + openTab({ + app: plugin.app, + custom: { + id: `${plugin.name}js-example`, + title: 'Query&View Examples', + icon: 'iconCode' + } + }) + } + } +} diff --git a/src/user-help/index.ts b/src/user-help/index.ts index 6cfa1d1..58f0ff5 100644 --- a/src/user-help/index.ts +++ b/src/user-help/index.ts @@ -1,117 +1,15 @@ -import { createDocWithMd, removeDoc, renameDoc, request, setBlockAttrs, sql, updateBlock } from "@/api"; -import type QueryViewPlugin from "@/index"; -import { openBlock } from "@/utils"; -import { showMessage } from "siyuan"; +/* + * Copyright (c) 2024 by frostime. All Rights Reserved. + * @Author : frostime + * @Date : 2024-12-10 18:46:12 + * @FilePath : /src/user-help/index.ts + * @LastEditTime : 2024-12-11 18:35:52 + * @Description : + */ import { i18n } from "@/index"; - - -const CUSTOM_USER_README_ATTR = 'custom-query-view-user-readme'; - -const compareVersion = (v1: string, v2: string) => { - const onlyNum = (str: string) => str.replace(/\D/g, ''); - const toNum = (str: string) => { - str = onlyNum(str); - return str.length > 0 ? Number(str) : 0; - } - - const v1Arr = v1.split('.').map(toNum); - const v2Arr = v2.split('.').map(toNum); - for (let i = 0; i < v1Arr.length; i++) { - if (v1Arr[i] > v2Arr[i]) { - return 1; - } else if (v1Arr[i] < v2Arr[i]) { - return -1; - } - } - return 0; -} - -const OutlineCode = ` -> {{//!js_esc_newline_const query = async () => {_esc_newline_ let dv = Query.DataView(protyle, item, top);_esc_newline_ let ans = await Query.request('/api/outline/getDocOutline', {_esc_newline_ id: Query.root_id(protyle)_esc_newline_ });_esc_newline_ const iterate = (data) => {_esc_newline_ for (let item of data) {_esc_newline_ if (item.count > 0) {_esc_newline_ let subtocs = iterate(item.blocks ?? item.children);_esc_newline_ item.children = Query.wrapBlocks(subtocs);_esc_newline_ }_esc_newline_ }_esc_newline_ return data;_esc_newline_ }_esc_newline_ let tocs = iterate(ans);_esc_newline_ dv.addmd('### Outline');_esc_newline_ dv.addlist(tocs, {_esc_newline_ renderer: b => \`[\${b.name || b.content}](\${b.asurl})\`,_esc_newline_ columns: 2_esc_newline_ });_esc_newline_ dv.render();_esc_newline_}_esc_newline__esc_newline_return query();}} -`.trim(); - - -const createReadmeText = async (plugin: QueryViewPlugin) => { - const lang = window.siyuan.config.lang; - const fname = lang.startsWith('zh') ? 'README_zh_CN.md' : 'README.md'; - - const response = await fetch(`/plugins/sy-query-view/${fname}`); - let readme = await response.text(); - - const AheadHint = i18n.user_help.ahead_hint.trim(); - let ahead = AheadHint.replace('{{version}}', plugin.version); - readme = ahead + '\n' + OutlineCode + '\n\n' + readme; - return readme; -} - -const createReadme = async (plugin: QueryViewPlugin) => { - const title = `${plugin.displayName}@${plugin.version} ` + i18n.src_userhelp_indexts.help_doc; - const notebooks = window.siyuan.notebooks.filter(n => n.closed === false); - if (notebooks.length === 0) { - showMessage(i18n.src_userhelp_indexts.create_notebook); - return null; - } - const notebook = notebooks[0]; - let readme = await createReadmeText(plugin); - let docId = await createDocWithMd(notebook.id, `/${title}`, readme); - const attr = { - [CUSTOM_USER_README_ATTR]: plugin.version, - 'custom-sy-readonly': 'true' - }; - await setBlockAttrs(docId, attr); - return docId; -} - -const useUserReadme = async (plugin: QueryViewPlugin) => { - const docs: (Block & { version: string })[] = await sql(` - SELECT B.*, A.value as version - FROM blocks AS B - JOIN attributes AS A ON A.block_id = B.id - WHERE A.name = '${CUSTOM_USER_README_ATTR}' - ORDER BY B.UPDATED DESC; - `); - - let targetDocId: DocumentId = null; - - if (!docs || docs.length === 0) { - targetDocId = await createReadme(plugin); - } else if (docs.length === 1) { - targetDocId = docs[0].id; - } else { - //找到版本号最大的文档 - docs.sort((a, b) => compareVersion(b.version, a.version)); - targetDocId = docs[0].id; - const others = docs.slice(1); - for (let doc of others) { - await removeDoc(doc.box, doc.path); - } - } - - // validate version - if (!targetDocId) return; - const attrVer = await sql(` - SELECT A.value - FROM attributes AS A - WHERE A.name = '${CUSTOM_USER_README_ATTR}' - AND A.block_id = '${targetDocId}' - `); - if (attrVer.length === 0) return; - const attrVerStr = attrVer[0].value; - if (compareVersion(attrVerStr, plugin.version) < 0) { - showMessage(((`检查到插件版本已更新,正在更新文档,请稍等...`)), 5000) - let newText = await createReadmeText(plugin); - const title = `${plugin.displayName}@${plugin.version} ` + i18n.src_userhelp_indexts.help_doc; - const doc = docs[0]; - await renameDoc(doc.box, doc.path, title) - await updateBlock('markdown', newText, targetDocId); - await setBlockAttrs(targetDocId, { - [CUSTOM_USER_README_ATTR]: plugin.version, - 'custom-sy-readonly': 'true' - }); - } - - openBlock(targetDocId); -} +import type QueryViewPlugin from "@/index"; +import { useUserReadme } from "./sy-doc"; +import { useExamples } from "./examples"; export const load = async (plugin: QueryViewPlugin) => { const pluignUrl = '/plugins/sy-query-view/plugin.json'; @@ -140,4 +38,13 @@ export const load = async (plugin: QueryViewPlugin) => { useUserReadme(plugin); } }); -} \ No newline at end of file + + plugin.registerMenuItem({ + label: i18n.src_userhelp_indexts.help_doc, + icon: 'iconHelp', + click: async () => { + const { open } = await useExamples(plugin); + open(); + } + }); +} diff --git a/src/user-help/sy-doc.ts b/src/user-help/sy-doc.ts new file mode 100644 index 0000000..31fd1cb --- /dev/null +++ b/src/user-help/sy-doc.ts @@ -0,0 +1,118 @@ +import { createDocWithMd, removeDoc, renameDoc, request, setBlockAttrs, sql, updateBlock } from "@/api"; +import type QueryViewPlugin from "@/index"; +import { openBlock } from "@/utils"; +import { showMessage } from "siyuan"; +import { i18n } from "@/index"; + + +const CUSTOM_USER_README_ATTR = 'custom-query-view-user-readme'; + +const compareVersion = (v1: string, v2: string) => { + const onlyNum = (str: string) => str.replace(/\D/g, ''); + const toNum = (str: string) => { + str = onlyNum(str); + return str.length > 0 ? Number(str) : 0; + } + + const v1Arr = v1.split('.').map(toNum); + const v2Arr = v2.split('.').map(toNum); + for (let i = 0; i < v1Arr.length; i++) { + if (v1Arr[i] > v2Arr[i]) { + return 1; + } else if (v1Arr[i] < v2Arr[i]) { + return -1; + } + } + return 0; +} + +const OutlineCode = ` +> {{//!js_esc_newline_const query = async () => {_esc_newline_ let dv = Query.DataView(protyle, item, top);_esc_newline_ let ans = await Query.request('/api/outline/getDocOutline', {_esc_newline_ id: Query.root_id(protyle)_esc_newline_ });_esc_newline_ const iterate = (data) => {_esc_newline_ for (let item of data) {_esc_newline_ if (item.count > 0) {_esc_newline_ let subtocs = iterate(item.blocks ?? item.children);_esc_newline_ item.children = Query.wrapBlocks(subtocs);_esc_newline_ }_esc_newline_ }_esc_newline_ return data;_esc_newline_ }_esc_newline_ let tocs = iterate(ans);_esc_newline_ dv.addmd('### Outline');_esc_newline_ dv.addlist(tocs, {_esc_newline_ renderer: b => \`[\${b.name || b.content}](\${b.asurl})\`,_esc_newline_ columns: 2_esc_newline_ });_esc_newline_ dv.render();_esc_newline_}_esc_newline__esc_newline_return query();}} +`.trim(); + + +const createReadmeText = async (plugin: QueryViewPlugin) => { + const lang = window.siyuan.config.lang; + const fname = lang.startsWith('zh') ? 'README_zh_CN.md' : 'README.md'; + + const response = await fetch(`/plugins/sy-query-view/${fname}`); + let readme = await response.text(); + + const AheadHint = i18n.user_help.ahead_hint.trim(); + let ahead = AheadHint.replace('{{version}}', plugin.version); + readme = ahead + '\n' + OutlineCode + '\n\n' + readme; + return readme; +} + +const createReadme = async (plugin: QueryViewPlugin) => { + const title = `${plugin.displayName}@${plugin.version} ` + i18n.src_userhelp_indexts.help_doc; + const notebooks = window.siyuan.notebooks.filter(n => n.closed === false); + if (notebooks.length === 0) { + showMessage(i18n.src_userhelp_indexts.create_notebook); + return null; + } + const notebook = notebooks[0]; + let readme = await createReadmeText(plugin); + let docId = await createDocWithMd(notebook.id, `/${title}`, readme); + const attr = { + [CUSTOM_USER_README_ATTR]: plugin.version, + 'custom-sy-readonly': 'true' + }; + await setBlockAttrs(docId, attr); + return docId; +} + +const useUserReadme = async (plugin: QueryViewPlugin) => { + const docs: (Block & { version: string })[] = await sql(` + SELECT B.*, A.value as version + FROM blocks AS B + JOIN attributes AS A ON A.block_id = B.id + WHERE A.name = '${CUSTOM_USER_README_ATTR}' + ORDER BY B.UPDATED DESC; + `); + + let targetDocId: DocumentId = null; + + if (!docs || docs.length === 0) { + targetDocId = await createReadme(plugin); + } else if (docs.length === 1) { + targetDocId = docs[0].id; + } else { + //找到版本号最大的文档 + docs.sort((a, b) => compareVersion(b.version, a.version)); + targetDocId = docs[0].id; + const others = docs.slice(1); + for (let doc of others) { + await removeDoc(doc.box, doc.path); + } + } + + // validate version + if (!targetDocId) return; + const attrVer = await sql(` + SELECT A.value + FROM attributes AS A + WHERE A.name = '${CUSTOM_USER_README_ATTR}' + AND A.block_id = '${targetDocId}' + `); + if (attrVer.length === 0) return; + const attrVerStr = attrVer[0].value; + if (compareVersion(attrVerStr, plugin.version) < 0) { + showMessage(i18n.src_userhelp_sydocts.plugin_update_doc, 5000) + let newText = await createReadmeText(plugin); + const title = `${plugin.displayName}@${plugin.version} ` + i18n.src_userhelp_indexts.help_doc; + const doc = docs[0]; + await renameDoc(doc.box, doc.path, title) + await updateBlock('markdown', newText, targetDocId); + await setBlockAttrs(targetDocId, { + [CUSTOM_USER_README_ATTR]: plugin.version, + 'custom-sy-readonly': 'true' + }); + } + + openBlock(targetDocId); +} + +export { + useUserReadme +}