From 859e3c6b48a61301163c14ec37a9875599d3e222 Mon Sep 17 00:00:00 2001 From: denislov <2864326614@qq.com> Date: Thu, 16 Jan 2025 22:20:10 +0800 Subject: [PATCH] add url snapshot --- .env.example | 6 +- addon/content/dialog.html | 31 +++++++++ package.json | 14 ++--- src/hooks.ts | 39 ++++++------ src/modules/examples.ts | 6 +- src/modules/urlDialog.ts | 129 ++++++++++++++++++++++++++++++++++++++ src/modules/views.ts | 22 +++++++ 7 files changed, 217 insertions(+), 30 deletions(-) create mode 100644 addon/content/dialog.html create mode 100644 src/modules/urlDialog.ts create mode 100644 src/modules/views.ts diff --git a/.env.example b/.env.example index b81c110..1943fd7 100644 --- a/.env.example +++ b/.env.example @@ -7,17 +7,17 @@ # The path of the Zotero binary file. # The path delimiter should be escaped as `\\` for win32. # The path is `*/Zotero.app/Contents/MacOS/zotero` for MacOS. -ZOTERO_PLUGIN_ZOTERO_BIN_PATH = /path/to/zotero.exe +ZOTERO_PLUGIN_ZOTERO_BIN_PATH = D:/Applications/Scoop/apps/zotero/current/zotero.exe # The path of the profile used for development. # Start the profile manager by `/path/to/zotero.exe -p` to create a profile for development. # @see https://www.zotero.org/support/kb/profile_directory -ZOTERO_PLUGIN_PROFILE_PATH = /path/to/profile +ZOTERO_PLUGIN_PROFILE_PATH = D:/TEMP/zotero_profile # The directory where the database is located. # If this field is kept empty, Zotero will start with the default data. # @see https://www.zotero.org/support/zotero_data -ZOTERO_PLUGIN_DATA_DIR = +ZOTERO_PLUGIN_DATA_DIR =D:/TEMP/zotero # Custom commands to kill Zotero processes. # Commands for different platforms are already built into zotero-plugin, diff --git a/addon/content/dialog.html b/addon/content/dialog.html new file mode 100644 index 0000000..a356b31 --- /dev/null +++ b/addon/content/dialog.html @@ -0,0 +1,31 @@ + + + + + + + + diff --git a/package.json b/package.json index 218fee0..d0cd7f6 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { - "name": "zotero-plugin-template", + "name": "zetter", "version": "3.0.2", - "description": "Zotero Plugin Template", + "description": "Zotero Better Plugin", "config": { - "addonName": "Zotero Plugin Template", - "addonID": "addontemplate@euclpts.com", - "addonRef": "addontemplate", - "addonInstance": "AddonTemplate", - "prefsPrefix": "extensions.zotero.addontemplate" + "addonName": "Zotero Better", + "addonID": "wanghai915@qq.com", + "addonRef": "zetter", + "addonInstance": "Zetter", + "prefsPrefix": "extensions.zotero.zetter" }, "repository": { "type": "git", diff --git a/src/hooks.ts b/src/hooks.ts index f879236..def69bb 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -5,6 +5,8 @@ import { PromptExampleFactory, UIExampleFactory, } from "./modules/examples"; +import { registerViews } from "./modules/views"; + import { getString, initLocale } from "./utils/locale"; import { registerPrefsScripts } from "./modules/preferenceScript"; import { createZToolkit } from "./utils/ztoolkit"; @@ -17,22 +19,21 @@ async function onStartup() { ]); initLocale(); + // BasicExampleFactory.registerPrefs(); - BasicExampleFactory.registerPrefs(); + // BasicExampleFactory.registerNotifier(); - BasicExampleFactory.registerNotifier(); + // KeyExampleFactory.registerShortcuts(); - KeyExampleFactory.registerShortcuts(); + // await UIExampleFactory.registerExtraColumn(); - await UIExampleFactory.registerExtraColumn(); + // await UIExampleFactory.registerExtraColumnWithCustomCell(); - await UIExampleFactory.registerExtraColumnWithCustomCell(); + // UIExampleFactory.registerItemPaneCustomInfoRow(); - UIExampleFactory.registerItemPaneCustomInfoRow(); + // UIExampleFactory.registerItemPaneSection(); - UIExampleFactory.registerItemPaneSection(); - - UIExampleFactory.registerReaderItemPaneSection(); + // UIExampleFactory.registerReaderItemPaneSection(); await Promise.all( Zotero.getMainWindows().map((win) => onMainWindowLoad(win)), @@ -65,29 +66,29 @@ async function onMainWindowLoad(win: Window): Promise { text: `[30%] ${getString("startup-begin")}`, }); - UIExampleFactory.registerStyleSheet(win); - + // UIExampleFactory.registerStyleSheet(win); + registerViews(win); UIExampleFactory.registerRightClickMenuItem(); - UIExampleFactory.registerRightClickMenuPopup(win); + // UIExampleFactory.registerRightClickMenuPopup(win); - UIExampleFactory.registerWindowMenuWithSeparator(); + // UIExampleFactory.registerWindowMenuWithSeparator(); - PromptExampleFactory.registerNormalCommandExample(); + // PromptExampleFactory.registerNormalCommandExample(); - PromptExampleFactory.registerAnonymousCommandExample(win); + // PromptExampleFactory.registerAnonymousCommandExample(win); - PromptExampleFactory.registerConditionalCommandExample(); + // PromptExampleFactory.registerConditionalCommandExample(); - await Zotero.Promise.delay(1000); + // await Zotero.Promise.delay(1000); popupWin.changeLine({ progress: 100, text: `[100%] ${getString("startup-finish")}`, }); - popupWin.startCloseTimer(5000); + popupWin.startCloseTimer(3000); - addon.hooks.onDialogEvents("dialogExample"); + // addon.hooks.onDialogEvents("dialogExample"); } async function onMainWindowUnload(win: Window): Promise { diff --git a/src/modules/examples.ts b/src/modules/examples.ts index a1dab01..7446d09 100644 --- a/src/modules/examples.ts +++ b/src/modules/examples.ts @@ -1,4 +1,5 @@ import { getLocaleID, getString } from "../utils/locale"; +import { URLInputDialog } from "./urlDialog"; function example( target: any, @@ -143,7 +144,10 @@ export class UIExampleFactory { tag: "menuitem", id: "zotero-itemmenu-addontemplate-test", label: getString("menuitem-label"), - commandListener: (ev) => addon.hooks.onDialogEvents("dialogExample"), + commandListener: (ev) => { + const dialog = new URLInputDialog(); + dialog.open(); + }, icon: menuIcon, }); } diff --git a/src/modules/urlDialog.ts b/src/modules/urlDialog.ts new file mode 100644 index 0000000..2825068 --- /dev/null +++ b/src/modules/urlDialog.ts @@ -0,0 +1,129 @@ +import { config } from "../../package.json"; +import { getString } from "../utils/locale"; + +export class URLInputDialog { + private window: Window; + private dialog: Window; + + constructor() { + this.window = Zotero.getMainWindow(); + } + + public open() { + // 创建对话框 HTML 内容 + const dialogContent = ` +
+
+
+ + +
+
+ + +
+
+
+ `; + + // 创建对话框参数 + const params: any = { + dataIn: { + url: "", + accepted: false, + }, + dataOut: null, + }; + + // 打开对话框 + this.dialog = this.window.openDialog( + rootURI + "content/dialog/dialog.html", + "", + "chrome,centerscreen,resizable=false,width=500,height=200", + params, + ); + + // 设置对话框内容和事件监听 + this.dialog.addEventListener("load", () => { + const doc = this.dialog.document; + + // 设置对话框标题 + doc.title = getString("snapshot-dialog.title"); + + // 插入自定义内容 + const container = doc.querySelector(".dialog-content"); + container.innerHTML = dialogContent; + + // 表单提交处理 + const form = doc.getElementById("snapshot-form") as HTMLFormElement; + form.addEventListener("submit", (e) => { + e.preventDefault(); + const urlInput = doc.getElementById("url-input") as HTMLInputElement; + params.dataOut = { + url: urlInput.value, + accepted: true, + }; + this.dialog.close(); + }); + + // 取消按钮处理 + const cancelButton = doc.getElementById("cancel-button"); + cancelButton.addEventListener("click", () => { + params.dataOut = { + accepted: false, + }; + this.dialog.close(); + }); + + // 自动聚焦到 URL 输入框 + const urlInput = doc.getElementById("url-input") as HTMLInputElement; + urlInput.focus(); + }); + + // 等待对话框关闭 + if (params.dataOut?.accepted) { + this.saveSnapshot(params.dataOut.url); + } + } + + async saveSnapshot(url: string) { + try { + // 创建新的 webpage 类型条目 + const item = new Zotero.Item("webpage"); + + // 获取网页内容 + const response = await fetch(url); + const html = await response.text(); + + // 设置基本元数据 + item.setField("title", this.extractTitle(html)); + item.setField("url", url); + item.setField("accessDate", Zotero.Date.dateToSQL(new Date(), true)); + + await item.saveTx(); + + // 保存网页内容作为附件 + const attachment = await Zotero.Attachments.importFromURL({ + url: url, + parentItemID: item.id, + contentType: "text/html", + title: item.getField("title"), + }); + + // 选中新创建的条目 + const zp = Zotero.getActiveZoteroPane(); + zp.selectItem(item.id); + + Zotero.debug(`Web Snapshot: Successfully saved ${url}`); + } catch (error) { + Zotero.debug(`Web Snapshot error: ${error}`); + this.window.alert(getString("snapshot.error")); + } + } + + private extractTitle(html: string): string { + const match = html.match(/(.*?)<\/title>/i); + return match ? match[1] : getString("snapshot.untitled"); + } +} diff --git a/src/modules/views.ts b/src/modules/views.ts new file mode 100644 index 0000000..acb2eb8 --- /dev/null +++ b/src/modules/views.ts @@ -0,0 +1,22 @@ +import { config } from "../../package.json"; +import { getString } from "../utils/locale"; +import { URLInputDialog } from "./urlDialog"; + +export async function registerViews(window: Window) { + // 注册工具栏按钮 + ztoolkit.UI.createElement(window.document, "button", { + id: `${config.addonRef}-toolbar-button`, + listeners: [ + { + type: "click", + listener: (e) => { + const dialog = new URLInputDialog(); + dialog.open(); + dialog.saveSnapshot( + "https://blog.csdn.net/qq_43210428/article/details/120384547", + ); + }, + }, + ], + }); +}