QuickAdd 脚本 - 插入 Zotero 的条目 PDF 链接
分享一个自己折腾玩的脚本,用于引用 Zotero 中已有 PDF 的条目链接。
通过 Quickadd 和 Modal Forms 这 2 个插件的 API 完成的,其中里面的通过文本识别是通过询问 GPT 采用的一个 Levenshtein距离计算相似度
计算条目名称之间的相似度来选择的。
背景
因为 Zutilo 并没有适配 Zotero 7,无法通过快捷键复制条目的链接,所以写了一个通过 Zotero 引用或者手动选择条目名称来识别转换为条目链接。
Zotero 的 Actions Tags 插件也可以复制条目链接
其实 Zotero 的 Actions Tags 插件 可以自定义脚本:
使用 Zotero 的 Actions Tags 插件更方便,以下脚本的都是瞎折腾的。
演示
模式 1:通过拖拽条目进行识别
模式 2:通过题目名称选择
模式 3:通过选中文本识别 (文献名称需要被《》包裹)
Quickadd 脚本配置
- 前提:需要安装 Quickadd 和 Modal Forms 这 2 个插件。
- 配置:需要配置 Zotero 的 storage 文件夹路径,默认是
D:/Zotero/storage
。
- 配置:需要配置 Zotero 的 storage 文件夹路径,默认是
// 🔴修改Zotero路径即可
const folderPath = 'D:/Zotero/storage';
配置 Capture 动作
脚本代码
```js quickadd
// 🔴修改Zotero路径即可
const folderPath = 'D:/Zotero/storage';
const fs = require('fs');
const modalForm = app.plugins.plugins.modalforms.api;
const files = fs.readdirSync(folderPath);
const zoteroItems = [];
let selection = window.getSelection();
selection = selection.toString();
files.forEach((file) => {
const filePath = `${folderPath}/${file}`;
const stat = fs.statSync(filePath);
if (stat.isDirectory()) {
const subFiles = fs.readdirSync(filePath);
const pdfFiles = subFiles.filter((subFile) => subFile.endsWith('.pdf')).map((pdfFile) => pdfFile.replace('.pdf', ''));
if (pdfFiles.length > 0) {
const zoteroItem = {};
zoteroItem["item"] = file;
zoteroItem["link"] = `zotero://open-pdf/library/items/${file}`;
zoteroItem["file"] = pdfFiles[0];
zoteroItems.push(zoteroItem);
}
}
});
console.log(zoteroItems);
let zoteroItemNames = [];
zoteroItems.forEach((item) => {
zoteroItemNames.push(item["file"]);
});
// console.log(zoteroItemNames)
const values = {
ZoteroCites: selection,
};
const result = await modalForm.openForm({
title: "zoteroModalForms",
name: "zoteroModalForms",
version: "1",
fields: [
{
name: "ZoteroCites",
label: "通过引用匹配对应的Items",
description: "引用格式需要IEEE格式",
input: {
type: "textarea"
}
},
{
name: "ZoteroItems",
label: "",
description: "",
input: {
type: "multiselect",
source: "fixed",
multi_select_options: zoteroItemNames,
}
},
]
}, {values:values});
let getZoteroItems = result.getValue('ZoteroItems').value;
let getZoteroCites = result.getValue('ZoteroCites').value;
if (getZoteroCites) {
getZoteroItems = getZoteroItems.concat(matchZoteroItems(getZoteroCites));
}
console.log(getZoteroItems);
let zoteroItemsLinks = [];
getZoteroItems.forEach((item) => {
// const foundItem = zoteroItems.find((zoteroItem) => zoteroItem["file"].includes(item.substring(0, 10)));
const foundItem = zoteroItems.find((zoteroItem) => {
const similarityThreshold = 0.3; // 相似度阈值
const similarity = 1 - levenshteinDistance(zoteroItem["file"], item) / Math.max(zoteroItem["file"].length, item.length);
return similarity >= similarityThreshold;
});
if (foundItem) {
const zoteroLink = `[《${item}》](${foundItem["link"]})`;
zoteroItemsLinks.push(zoteroLink);
}
});
if (zoteroItemsLinks.length) {
const output = "- " + zoteroItemsLinks.join("\n- ");
console.log(output);
return output;
}
function matchZoteroItems(text) {
const regex = /《(.*?)》/gm;
const matches = text.match(regex);
return matches ? matches.map(match => match.slice(1, -1)) : "";
}
function levenshteinDistance(a, b) {
// Levenshtein距离计算相似度 by ChatGPT @2024-01-04
const m = a.length;
const n = b.length;
const dp = Array.from(Array(m + 1), () => Array(n + 1).fill(0));
for (let i = 0; i <= m; i++) {
dp[i][0] = i;
}
for (let j = 0; j <= n; j++) {
dp[0][j] = j;
}
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
if (a[i - 1] === b[j - 1]) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = Math.min(
dp[i - 1][j] + 1, // 删除操作
dp[i][j - 1] + 1, // 插入操作
dp[i - 1][j - 1] + 1 // 替换操作
);
}
}
}
return dp[m][n];
}
```
References
- zotero的快速复制功能 - 知乎 (zhihu.com)
- Windingwind/zotero-actions-tags:操作、标记、排序。 --- windingwind/zotero-actions-tags: Action it, tag it, sorted. (github.com)
讨论
若阁下有独到的见解或新颖的想法,诚邀您在文章下方留言,与大家共同探讨。
反馈交流
其他渠道
版权声明
版权声明:所有 PKMer 文章如果需要转载,请附上原文出处链接。