From 84af3290410831b37a638be4bb5e10e3708a83e3 Mon Sep 17 00:00:00 2001 From: frostime Date: Tue, 3 Dec 2024 17:39:03 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Misc=20=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/i18n/en_US.yaml | 19 +++++++++++++ public/i18n/zh_CN.yaml | 15 +++++++++++ src/core/data-view.ts | 5 ++-- src/core/editor.ts | 61 +++++++++++++++++++++++++++++++++--------- src/core/gc.ts | 46 ++++++++++++++++++++++++------- src/core/index.ts | 2 +- src/core/query.ts | 1 + src/core/utils.ts | 4 +-- src/index.ts | 1 + src/libs/dialog.ts | 4 +-- src/setting/index.ts | 12 ++++----- src/types/i18n.d.ts | 16 +++++++++++ 12 files changed, 150 insertions(+), 36 deletions(-) diff --git a/public/i18n/en_US.yaml b/public/i18n/en_US.yaml index 40667f7..1650678 100644 --- a/public/i18n/en_US.yaml +++ b/public/i18n/en_US.yaml @@ -1,3 +1,17 @@ +src_core_editorts: + ext_code_editor_closed: The external code editor process {0} has been closed + show_as_template_format: Display as template format + show_embedded_block_code_in_siyuan_template_format: Display embedded block code + in SiYuan template format + unableto_use_ext_cmd: Unable to use the external editor command {0}, please check + the correctness of the command + unableto_use_external_editor_cmd: Unable to use the external editor command {0}, + please check the correctness of the command + unusableexteditorcmd: Unable to use the external editor command {0}, please check + the correctness of the command +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!' src_dataquery_componentsts: mermaid_render_failed: Mermaid rendering failed, please check the code src_dataquery_editorts: @@ -7,8 +21,13 @@ src_setting_indexts: apitypedefinition: 'The type definition of the API interface, please refer to:' defaultcolumnsofdataviewtable: When calling DataView.table, the default columns to be displayed, separated by commas; leaving it blank means showing all columns + import_failed: Import failed. Please check the console error for details. + import_success: Import succeeded. There are a total of {cnt} custom views. local_command_desc: The local command used to open the code editor, by default it is "code {{filepath}}" which represents opening with VSCode, where {{filepath}} will be replaced with the actual code file at runtime open_local_editor: ✒️ Open Local Editor + reload: Reload table_default_columns: 🔑 Default columns displayed in table view + user_custom_view: User custom view + user_self_written_view: User self-written View component, stored under '/data/public/custom-query-view.js' diff --git a/public/i18n/zh_CN.yaml b/public/i18n/zh_CN.yaml index e2425cb..c9c013d 100644 --- a/public/i18n/zh_CN.yaml +++ b/public/i18n/zh_CN.yaml @@ -1,3 +1,12 @@ +src_core_editorts: + ext_code_editor_closed: 外部代码编辑器进程 {0} 已关闭 + show_as_template_format: 显示为模板格式 + show_embedded_block_code_in_siyuan_template_format: 以思源模板的格式显示嵌入块代码 + unableto_use_ext_cmd: 无法使用外部编辑器命令 {0}, 请检查命令的正确性 + unableto_use_external_editor_cmd: 无法使用外部编辑器命令 {0}, 请检查命令的正确性 + unusableexteditorcmd: 无法使用外部编辑器命令 {0}, 请检查命令的正确性 +src_core_indexts: + custom_queryview_error: '注意: 自定义的 QueryView 脚本格式存在问题,无法正常导入!' src_dataquery_componentsts: mermaid_render_failed: Mermaid 渲染失败, 请检查代码 src_dataquery_editorts: @@ -6,7 +15,13 @@ src_setting_indexts: api_interface: 📃 API 接口 apitypedefinition: 'API 接口的类型定义,请参考:' defaultcolumnsofdataviewtable: 当调用 DataView.table 时, 默认显示的列, 以逗号分隔; 留空则表示显示所有列 + import_failed: 导入失败, 详细情况请检查控制台报错 + import_success: 导入成功, 共 {cnt} 个自定义视图 local_command_desc: 本地用于打开代码编辑器的命令, 默认为 "code {{filepath}}" 代表使用 VSCode 打开, 其中 {{filepath}} 在运行时会被替换为真实的代码文件 open_local_editor: ✒️ 打开本地编辑器 + reload: 重新加载 table_default_columns: 🔑 表格视图默认显示列 + user_custom_view: 用户自定义视图 + user_self_written_view: 用户自行编写的 View 组件, 存放在 '/data/public/custom-query-view.js' + 下 diff --git a/src/core/data-view.ts b/src/core/data-view.ts index 821a0bc..8c7724d 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-03 15:02:36 + * @LastEditTime : 2024-12-03 15:19:13 * @Description : */ import { @@ -1002,7 +1002,8 @@ export class DataView implements IDataView { this.observer.disconnect(); this.observer = null; } - }); // Triggered on rerendered + }); + // Triggered on rerendered this.observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { mutation.removedNodes.forEach((node) => { diff --git a/src/core/editor.ts b/src/core/editor.ts index 7e682fd..dbf9206 100644 --- a/src/core/editor.ts +++ b/src/core/editor.ts @@ -7,6 +7,7 @@ import { debounce } from "@/utils"; import { setting } from "@/setting"; import { i18n } from "@/index"; +import { inputDialog, simpleDialog } from "@/libs/dialog"; const child_process = require("child_process"); @@ -33,16 +34,6 @@ const editJsCode = async (blockId: BlockId, code: string) => { updateBlock('markdown', embedBlock, blockId); } const updateBlockDataDebounced = debounce(updateBlockData, 1500); - // const update = (updatedContent: string) => { - // const el = elementRef.deref(); - // if (el) { - // // el.dataset.content = window.Lute.EscapeHTMLStr(updatedContent); - // // const btn: HTMLElement | null = el.querySelector('span.protyle-action__reload'); - // // btn?.click(); - // //更新内核后会自动刷新, 所以不需要手动刷新 - // updateBlockDataDebounced(updatedContent); - // } - // }; //桌面环境, 可以访问 node 环境 if (child_process) { @@ -67,18 +58,36 @@ const editJsCode = async (blockId: BlockId, code: string) => { const codeEditor = setting.codeEditor; //codeEditor 为一个命令行, 其中 {{filepath}} 会被替换为真实的文件路径 - const command = codeEditor.replace('{{filepath}}', filePath); - const commandArr = command.split(' ').map(item => item.trim()).filter(item => item !== ''); + const input = codeEditor.replace('{{filepath}}', filePath); + const commandArr = input.split(' ').map(item => item.trim()).filter(item => item !== ''); + + // 添加调试信息 + console.debug('About to execute command:', { + command: input, + commandArr, + codeEditor: setting.codeEditor + }); + let command = commandArr[0]; try { - editor = child_process.spawn(commandArr[0], commandArr.slice(1)); + editor = child_process.spawn(command, commandArr.slice(1), { + shell: true + }); } catch (e) { console.error('启动代码编辑器失败:', e); + showMessage(i18n.src_core_editorts.unusableexteditorcmd.replace('{0}', input), 5000, 'error'); cleanUp(); return; } + editor.on('error', (err: any) => { + console.error('代码编辑器错误:', err); + showMessage(i18n.src_core_editorts.unusableexteditorcmd.replace('{0}', input)); + cleanUp(); + }); + editor.on('exit', () => { + showMessage(i18n.src_core_editorts.ext_code_editor_closed.replace('{0}', commandArr[0])); console.log('代码编辑器已关闭'); try { const updatedContent = fs.readFileSync(filePath, 'utf-8'); @@ -125,4 +134,30 @@ export async function embedBlockEvent({ detail }: any) { editJsCode(id, ele.dataset.content); } }); + menu.addItem({ + icon: "iconMarkdown", + label: i18n.src_core_editorts.show_as_template_format, + click: () => { + let code = ele.dataset.content; + code = window.Lute.UnEscapeHTMLStr(code); + //换行符全部替换为 `_esc_newline_` + code = code.replace(/\n/g, '_esc_newline_'); + const textarea = document.createElement('textarea'); + textarea.value = `{{${code}}}`; + textarea.style.flex = "1"; + textarea.style.fontSize = "16px"; + textarea.style.margin = "15px 10px"; + textarea.style.borderRadius = "5px"; + textarea.style.resize = "none"; + // 不可编辑 + textarea.setAttribute("readonly", "true"); + + simpleDialog({ + title: i18n.src_core_editorts.show_embedded_block_code_in_siyuan_template_format, + ele: textarea, + width: "700px", + height: "300px" + }); + } + }); } diff --git a/src/core/gc.ts b/src/core/gc.ts index 1bb448d..db069de 100644 --- a/src/core/gc.ts +++ b/src/core/gc.ts @@ -1,6 +1,28 @@ +/* + * Copyright (c) 2024 by frostime. All Rights Reserved. + * @Author : frostime + * @Date : 2024-12-01 11:21:44 + * @FilePath : /src/core/gc.ts + * @LastEditTime : 2024-12-03 15:18:18 + * @Description : + */ import { DataView } from "./data-view"; -const dataviews = new WeakMap[]>(); +const dataviews = new Map[]>(); + +const registry = new FinalizationRegistry((docId: string) => { + const views = dataviews.get(docId); + if (views) { + views.forEach(view => { + const dataView = view.deref(); + if (dataView) { + console.debug('FinalizationRegistry dispose dataView', dataView); + dataView.dispose(); + } + }); + dataviews.delete(docId); + } +}); /** * Register DataView for Garbage Collection on Document Closed @@ -8,24 +30,28 @@ const dataviews = new WeakMap[]>(); * @param dataView */ export const registerProtyleGC = (docId: DocumentId, dataView: DataView) => { - const key = { id: docId }; - if (!dataviews.has(key)) { - dataviews.set(key, []); + if (!dataviews.has(docId)) { + dataviews.set(docId, []); } - const views = dataviews.get(key); + const views = dataviews.get(docId); views.push(new WeakRef(dataView)); + + // 注册 dataView 到 FinalizationRegistry + registry.register(dataView, docId); } export const onProtyleDestroyed = ({ detail }) => { - console.log('closed protyle'); - console.log(detail); const rootID = detail.protyle.block.rootID; - if (!(rootID in dataviews)) return; - dataviews[rootID].forEach(view => { + + if (!dataviews.has(rootID)) return; + + const views = dataviews.get(rootID); + views.forEach(view => { const dataView = view.deref(); if (dataView) { + console.debug('onProtyleDestroyed dispose dataView', dataView); dataView.dispose(); } }); - delete dataviews[rootID]; + dataviews.delete(rootID); } diff --git a/src/core/index.ts b/src/core/index.ts index a5c60a6..660ca48 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -33,7 +33,7 @@ export const load = (plugin: Plugin) => { loadUserCustomView().then(status => { if (status.exists && !status.valid) { - showMessage(((`注意: 自定义的 QueryView 脚本格式存在问题,无法正常导入!`)), 5000, 'error'); + showMessage(i18n.src_core_indexts.custom_queryview_error, 5000, 'error'); return; } if (status.ok) { diff --git a/src/core/query.ts b/src/core/query.ts index 82b0ea4..46561f7 100644 --- a/src/core/query.ts +++ b/src/core/query.ts @@ -459,6 +459,7 @@ const addAlias = (obj: any, attr: string, alias: string[]) => { }); } +addAlias(Query, 'DataView', ['Dataview']); addAlias(Query, 'Utils', ['utils']); addAlias(Query, 'getBlocksByIds', ['getBlocksByIDs']); addAlias(Query, 'docid', ['docId']); diff --git a/src/core/utils.ts b/src/core/utils.ts index e93a9dc..8b7dbac 100644 --- a/src/core/utils.ts +++ b/src/core/utils.ts @@ -2,8 +2,8 @@ * Copyright (c) 2024 by frostime. All Rights Reserved. * @Author : frostime * @Date : 2024-11-28 21:29:40 - * @FilePath : /src/func/data-query/utils.ts - * @LastEditTime : 2024-11-30 16:18:55 + * @FilePath : /src/core/utils.ts + * @LastEditTime : 2024-12-03 15:52:14 * @Description : */ //https://github.com/siyuan-note/siyuan/blob/master/app/src/protyle/util/addScript.ts diff --git a/src/index.ts b/src/index.ts index bee476e..8caada6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,6 +9,7 @@ import * as Setting from "./setting"; let i18n: I18n; let app: App; + export default class QueryView extends Plugin { async onload() { diff --git a/src/libs/dialog.ts b/src/libs/dialog.ts index d0fe582..d9f6bca 100644 --- a/src/libs/dialog.ts +++ b/src/libs/dialog.ts @@ -3,7 +3,7 @@ * @Author : frostime * @Date : 2024-03-23 21:37:33 * @FilePath : /src/libs/dialog.ts - * @LastEditTime : 2024-10-16 14:31:04 + * @LastEditTime : 2024-12-03 15:35:47 * @Description : Kits about dialogs */ import { Dialog } from "siyuan"; @@ -17,7 +17,7 @@ export const inputDialog = (args: { const dialog = new Dialog({ title: args.title, content: `
-
+
diff --git a/src/setting/index.ts b/src/setting/index.ts index e29a6d3..caba6d0 100644 --- a/src/setting/index.ts +++ b/src/setting/index.ts @@ -3,7 +3,7 @@ * @Author : frostime * @Date : 2024-12-01 16:25:57 * @FilePath : /src/setting/index.ts - * @LastEditTime : 2024-12-03 12:45:28 + * @LastEditTime : 2024-12-03 15:43:15 * @Description : */ @@ -76,19 +76,19 @@ export const load = async (plugin: Plugin) => { }); settingUtils.addItem({ type: 'button', - title: ((`用户自定义视图`)), - description: ((`用户自行编写的 View 组件, 存放在 '/data/public/custom-query-view.js' 下`)), + title: i18n.src_setting_indexts.user_custom_view, + description: i18n.src_setting_indexts.user_self_written_view, key: 'userCustomView', value: '', button: { - label: '重新导入', + label: i18n.src_setting_indexts.reload, callback: async () => { const result = await loadUserCustomView(); if (result.ok) { let cnt = Object.keys(result.custom).length; - showMessage(((`导入成功, 共 {cnt} 个自定义视图`)).replace('{cnt}', `${cnt}`), 3000, 'info'); + showMessage(i18n.src_setting_indexts.import_success.replace('{cnt}', `${cnt}`), 3000, 'info'); } else { - showMessage(((`导入失败, 详细情况请检查控制台报错`)), 3000, 'error'); + showMessage(i18n.src_setting_indexts.import_failed, 3000, 'error'); } } } diff --git a/src/types/i18n.d.ts b/src/types/i18n.d.ts index b7963d0..fa05ba7 100644 --- a/src/types/i18n.d.ts +++ b/src/types/i18n.d.ts @@ -1,4 +1,15 @@ interface I18n { + src_core_editorts: { + ext_code_editor_closed: string; + show_as_template_format: string; + show_embedded_block_code_in_siyuan_template_format: string; + unableto_use_ext_cmd: string; + unableto_use_external_editor_cmd: string; + unusableexteditorcmd: string; + }; + src_core_indexts: { + custom_queryview_error: string; + }; src_dataquery_componentsts: { mermaid_render_failed: string; }; @@ -9,8 +20,13 @@ interface I18n { api_interface: string; apitypedefinition: string; defaultcolumnsofdataviewtable: string; + import_failed: string; + import_success: string; local_command_desc: string; open_local_editor: string; + reload: string; table_default_columns: string; + user_custom_view: string; + user_self_written_view: string; }; }