diff --git a/addon/chrome/locale/en-US/addon.properties b/addon/chrome/locale/en-US/addon.properties index 822113d..ccc4a90 100644 --- a/addon/chrome/locale/en-US/addon.properties +++ b/addon/chrome/locale/en-US/addon.properties @@ -6,6 +6,7 @@ menuItem.exportNote=Export Note menuEdit.insertTemplate=Insert Template to Workspace Note menuEdit.exportTemplate=Export Template to File... menuEdit.templateEditor=Template Editor +menuEdit.importTemplate=New Template from Clipboard menuTools.syncManager=Sync Manager @@ -108,6 +109,11 @@ editor.toolbar.settings.refreshSyncing=Sync Now editor.toolbar.link.title=Link current note to workspace editor.toolbar.link.popup.nodata=Workspace note is invalid +templatePicker.itemData.info=are selected in library. Please choose the data source: +templatePicker.itemData.useLibrary=Use selected items in library +templatePicker.itemData.useCustom=Choose another... +templatePicker.itemData.title=Choose Item Template Data Source + alert.notValidCollectionError=Please select a valid collection. alert.notValidParentItemError=No valid parent item. alert.notValidWorkspaceNote=Workspace note is not set. Create one? diff --git a/addon/chrome/locale/ru-RU/addon.properties b/addon/chrome/locale/ru-RU/addon.properties index 0124078..b8eb266 100644 --- a/addon/chrome/locale/ru-RU/addon.properties +++ b/addon/chrome/locale/ru-RU/addon.properties @@ -6,6 +6,7 @@ menuItem.exportNote=Экспорт заметки menuEdit.insertTemplate=Вставить шаблон в заметку раб. пространства menuEdit.exportTemplate=Экспорт шаблона в файл... menuEdit.templateEditor=Редактор шаблонов +menuEdit.importTemplate=New Template from Clipboard menuTools.syncManager=Синк менеджер @@ -108,6 +109,11 @@ editor.toolbar.settings.refreshSyncing=Синхронизировать сейч editor.toolbar.link.title=Ссылка текущей заметки в рабочее пространство editor.toolbar.link.popup.nodata=Workspace note is invalid +templatePicker.itemData.info=are selected in library. Please choose the data source: +templatePicker.itemData.useLibrary=Use selected items in library +templatePicker.itemData.useCustom=Choose another... +templatePicker.itemData.title=Choose Item Template Data Source + alert.notValidCollectionError=Выберите валидную коллекцию. alert.notValidParentItemError=Нет валидного родительского элемента. alert.notValidWorkspaceNote=Заметка рабочего пространства не установлена. Создать? diff --git a/addon/chrome/locale/zh-CN/addon.properties b/addon/chrome/locale/zh-CN/addon.properties index 99ec168..988bda9 100644 --- a/addon/chrome/locale/zh-CN/addon.properties +++ b/addon/chrome/locale/zh-CN/addon.properties @@ -6,6 +6,7 @@ menuItem.exportNote=导出笔记 menuEdit.insertTemplate=插入模板到工作区笔记 menuEdit.exportTemplate=导出模板到文件... menuEdit.templateEditor=模板编辑器 +menuEdit.importTemplate=从剪贴板导入新模板 menuTools.syncManager=同步管理器 @@ -108,6 +109,11 @@ editor.toolbar.settings.refreshSyncing=立即同步 editor.toolbar.link.title=链接当前笔记到工作区 editor.toolbar.link.popup.nodata=工作区笔记不可用 +templatePicker.itemData.info=在文库中被选中。请选择模板数据源: +templatePicker.itemData.useLibrary=使用文库中选中的条目 +templatePicker.itemData.useCustom=另作选择... +templatePicker.itemData.title=选择条目模板数据源 + alert.notValidCollectionError=请选择一个有效的分类。 alert.notValidParentItemError=无效的父条目。 alert.notValidWorkspaceNote=工作区笔记未设置。创建一个吗? diff --git a/src/hooks.ts b/src/hooks.ts index 6da82ee..30e799f 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -3,7 +3,10 @@ import { initLocale } from "./utils/locale"; import { registerPrefsWindow } from "./modules/preferenceWindow"; import { registerNoteLinkProxyHandler } from "./modules/noteLink"; import { registerEditorInstanceHook } from "./modules/editor/initalize"; -import { initTemplates } from "./modules/template/controller"; +import { + importTemplateFromClipboard, + initTemplates, +} from "./modules/template/controller"; import { registerMenus } from "./modules/menu"; import { activateWorkspaceTab, @@ -281,6 +284,8 @@ const onShowTemplatePicker = showTemplatePicker; const onUpdateTemplatePicker = updateTemplatePicker; +const onImportTemplateFromClipboard = importTemplateFromClipboard; + const onShowImageViewer = showImageViewer; const onShowExportNoteOptions = showExportNoteOptions; @@ -314,6 +319,7 @@ export default { onSyncing, onShowTemplatePicker, onUpdateTemplatePicker, + onImportTemplateFromClipboard, onShowImageViewer, onShowExportNoteOptions, onShowSyncDiff, diff --git a/src/modules/menu.ts b/src/modules/menu.ts index ade5b23..6f450f6 100644 --- a/src/modules/menu.ts +++ b/src/modules/menu.ts @@ -74,6 +74,19 @@ export function registerMenus() { "before", menuEditAnchor ); + ztoolkit.Menu.register( + "menuEdit", + { + tag: "menuitem", + label: getString("menuEdit.importTemplate"), + icon: `chrome://${config.addonRef}/content/icons/favicon.png`, + commandListener: (ev) => { + addon.hooks.onImportTemplateFromClipboard(); + }, + }, + "before", + menuEditAnchor + ); ztoolkit.Menu.register( "menuEdit", { tag: "menuseparator" }, diff --git a/src/modules/template/api.ts b/src/modules/template/api.ts index a178dc6..0cac4cf 100644 --- a/src/modules/template/api.ts +++ b/src/modules/template/api.ts @@ -1,5 +1,7 @@ import { itemPicker } from "../../utils/itemPicker"; +import { getString } from "../../utils/locale"; import { copyEmbeddedImagesInHTML, renderNoteHTML } from "../../utils/note"; +import { fill, slice } from "../../utils/str"; export { runTemplate, runItemTemplate }; @@ -59,7 +61,7 @@ async function runTemplate( if (endIndex < 0) { endIndex = templateLines.length; } - // Skip the flag lines + // Skip the markdown flag lines templateLines = templateLines.slice(startIndex + 1, endIndex); let useMarkdown = false; let mdIndex = templateLines.indexOf("// @use-markdown"); @@ -67,6 +69,8 @@ async function runTemplate( useMarkdown = true; templateLines.splice(mdIndex, 1); } + // Skip other flag lines + templateLines = templateLines.filter((line) => !line.startsWith("// @")); templateText = templateLines.join("\n"); try { @@ -100,7 +104,10 @@ async function runItemTemplate( */ let { itemIds, targetNoteId, dryRun } = options; if (!itemIds) { - itemIds = await itemPicker(); + itemIds = await getItemTemplateData(); + } + if (itemIds?.length === 0) { + return ""; } const targetNoteItem = Zotero.Items.get(targetNoteId || -1); @@ -170,3 +177,43 @@ async function runItemTemplate( } return html; } + +async function getItemTemplateData() { + const librarySelectedIds = addon.data.templatePicker.data.librarySelectedIds; + if (librarySelectedIds && librarySelectedIds.length !== 0) { + const firstSelectedItem = Zotero.Items.get(librarySelectedIds[0]); + const data = {} as Record; + data; + new ztoolkit.Dialog(1, 1) + .setDialogData(data) + .addCell(0, 0, { + tag: "div", + properties: { + innerHTML: `${fill( + slice( + (firstSelectedItem.getField("title") as string) || + firstSelectedItem.key, + 40 + ), + 40 + )} ${ + librarySelectedIds.length > 1 + ? `and ${librarySelectedIds.length - 1} more` + : "" + } ${getString("templatePicker.itemData.info")}`, + }, + }) + .addButton(getString("templatePicker.itemData.useLibrary"), "useLibrary") + .addButton(getString("templatePicker.itemData.useCustom"), "useCustom") + .open(getString("templatePicker.itemData.title")); + await data.unloadLock.promise; + if (data._lastButtonId === "useLibrary") { + return librarySelectedIds; + } else if (data._lastButtonId == "useCustom") { + return await itemPicker(); + } else { + return []; + } + } + return await itemPicker(); +} diff --git a/src/modules/template/controller.ts b/src/modules/template/controller.ts index 5c6a75c..09e0562 100644 --- a/src/modules/template/controller.ts +++ b/src/modules/template/controller.ts @@ -1,11 +1,14 @@ +import YAML = require("yamljs"); import { clearPref, getPref, setPref } from "../../utils/prefs"; +import { showHint } from "../../utils/hint"; export { getTemplateKeys, getTemplateText, setTemplate, - initTemplates, removeTemplate, + initTemplates, + importTemplateFromClipboard, }; // Controller @@ -83,3 +86,29 @@ function initTemplates() { } addon.hooks.onUpdateTemplatePicker(); } + +function importTemplateFromClipboard() { + const templateText = Zotero.Utilities.Internal.getClipboard("text/unicode"); + if (!templateText) { + return; + } + let templateData: NoteTemplate; + try { + templateData = YAML.parse(templateText); + } catch (e) { + try { + templateData = JSON.parse(templateText); + } catch (e) { + showHint("Invalid template data"); + return; + } + } + if (!templateData.name || !templateData.text) { + showHint("Invalid template data"); + return; + } + if (!window.confirm(`Import template "${templateData.name}"?`)) { + return; + } + setTemplate(templateData); +} diff --git a/src/modules/template/picker.ts b/src/modules/template/picker.ts index aa9ce9f..6778b5f 100644 --- a/src/modules/template/picker.ts +++ b/src/modules/template/picker.ts @@ -95,6 +95,8 @@ async function insertTemplateCallback(name: string) { } async function createTemplateNoteCallback(name: string) { + addon.data.templatePicker.data.librarySelectedIds = + ZoteroPane.getSelectedItems(true); switch (addon.data.templatePicker.data.noteType) { case "standalone": { const currentCollection = ZoteroPane.getSelectedCollection(); @@ -124,6 +126,8 @@ async function createTemplateNoteCallback(name: string) { } async function exportTemplateCallback(name: string) { + addon.data.templatePicker.data.librarySelectedIds = + ZoteroPane.getSelectedItems(true); // Create temp note const noteItem = new Zotero.Item("note"); noteItem.libraryID = Zotero.Libraries.userLibraryID;