Ob_Zotero联动流程,Better notes + pdf translate +zotero style +green frog 插件大串联

Obsidian是markdown笔记管理工具、Zotero作为强大的文献管理工具,它们都有着大量活跃开发者提供各式插件,高度灵活,免费,是各自领域的集大成者。而且他们都将数据保存在本地,意谓着数据都自己掌控,还能自由同步。
Better Notes有着真正的双向同步机制,因此我觉得以此插件为基础,可以实现大众需求的笔记同步。于是我探索了相关流程,分享给大家,抛砖引玉。
文章来源:https://github.com/windingwind/zotero-better-notes/discussions/611

前言

过去,根据相关教程,网友们探索了多种联动方案,比如我先前的流程:

  1. Zotero better bibtex导出bib json文件
  2. Obsidian bibnotes formatter导入数据

然后后续就在Ob里做笔记,然而这个方案无法同步笔记里的图片,也不能双向同步。
但是基于Zotero Better Notes,可以解决这两个问题。同步笔记里的图片并且双向同步

Zotero Better Notes差异合并(Diff-Merge)

后来发现Zotero有个强大的笔记插件,叫 Zotero Better Notes, 其中笔记导入为markdown并可以实现双向同步,其同步策略官方说明如下:
同步检查有多种可能触发:

  1. Zotero笔记产生编辑时,添加后台任务自动对编辑的笔记进行同步检查;
  2. 用户点击同步窗口中的“同步”按钮,对当前笔记或全部设置同步的笔记进行同步检查;
  3. Zotero主窗口存在于前台且激活时(即,当前选中的窗口是Zotero),定期对全部设置同步的笔记进行同步检查。

请注意同步检查并不意味着触发一次同步。仅当检测到外部Markdown或笔记产生修改时,才会开始同步。
在上述同步管理窗口中可设置自动同步周期,默认为30s。如果设置数值小于0,则不会自动同步。
绝大多数情况下,笔记与Markdown的编辑能够自动同步。如果自从上一次同步以来,Zotero笔记与外部Markdown文件都进行了编辑,则可能会进入差异合并阶段。
在此时,会弹出差异合并窗口,由用户手动选择需要保留的编辑。

image.png

上方信息栏显示当前正在比较的笔记及对应Markdown文件信息;
左侧是修改选择栏,可以多选数个要接受的编辑。中间为笔记的HTML(raw)格式的差异比较,红色为相较上次同步后笔记内删除的内容,绿色为相较上次同步后笔记内新增的内容。右侧为实时预览栏,根据左侧修改选择的情况实时预览编辑合并后的笔记。
点击Finish来保存合并,点击Unsync将会取消合并并且不再同步该笔记,点击Skip跳过本次比较。

由以上说明可以知道,Better Notes有着真正的双向同步机制,因此我觉得以此插件为基础,可以实现大众需求的笔记同步。
于是我探索了相关流程,分享给大家,抛砖引玉。

展示

Zotero里

image.png

Obsidian里

其中File是链接到zotero pdf,可以跳转打开Zotero


image.png

Ob Dataview展示

image.png

相关插件及配置

Zotero 6.0.26

  1. Better Notes 1.0.4, 基本同步功能
  2. PDF translate 1.0.22,标题、摘要翻译
  3. Zotero style 2.6.7,影响因子颜色
  4. Green frog 0.13.0,影响因子单独提取

Obsidain 1.3.4

  1. DataView,展示卡片,其他不需要插件了

流程配置

Better Notes设置模板

Better Notes有几个重要的模板需要设置,相关模板设置参考了多个模板项目,在此致谢:
[Item] A template including Year_First creator_Title and outline for EE students · windingwind/zotero-better-notes · Discussion #581 (github.com)
[item] 显示期刊标签的笔记模板示例 · windingwind/zotero-better-notes · Discussion #521 (github.com)
[Item] SCI论文阅读模版-new,自用生物医学类SCI笔记模板 · windingwind/zotero-better-notes · Discussion #501 (github.com)
[Item] SCI论文阅读模版 · windingwind/zotero-better-notes · Discussion #339 (github.com)

修改ExportMDFileNameV2

一个是ExportMDFileNameV2模板,按官方的模板导出的md文件名是这个样子的:
“ 标题: MRI-Based Metastatic Nodal Number and Associated Nomogram Improve Stratification of Nasopharyngeal Carcinoma Patients: Potential Indications for Individual Induction Chemotherapy“
但是我希望尽量简短一些,比如Author+Year+title[前4个词]
于是修改这里的模板

${noteItem.parentItem.getField('firstCreator').split(" ",1).slice(0)}_${noteItem.parentItem.getField('date')?noteItem.parentItem.getField('date').substring(0, 4):""}_${noteItem.parentItem.getField("title").split(" ").slice(0,4).join("_")}.md
image.png

添加 [Item] SCI论文阅读模版-noHtml模板

然后由于我们要插入各种字段,于是需要自定义显示:

  1. 获取条目下的pdf链接,用于自动跳转
  2. 需要Zotero style获取带颜色的IF style
  3. 对于单独提取分区,影响因子之类的,我用green frog,它把数据保存到extra字段
  4. 同理,标题和摘要的翻译,也是用的PDF translate插件,数据保存在extra字段里,经过大量努力,把extra里的数据全部单独获取到了。
  5. 我把自己对条目的备注放在了archive 存档 字段中,当作ShortNote备注

这是我的模板
复制,然后剪切板导入

name: "[Item] SCI论文阅读模版-md"
content: |-
 // @新奥尔良烤乳猪
 // @use-markdown
 # ${topItem.getField('firstCreator').split(" ",1).slice(0)}_${topItem.getField('date')?topItem.getField('date').substring(0, 4):""}_${topItem.getField("title").split(" ").slice(0,4).join("_")}  
 > File:: [${topItem.firstCreator}, ${topItem.getField("year")}](${await new Promise(async (resolve) => {
      async function getPDFLink(item) {
        const att = await item.getBestAttachment();
        if (!att || !att.isPDFAttachment()) {
          return "";
        }
        key = att.key;
        if (att.libraryID === 1) {
          return `zotero://open-pdf/library/items/${key}`;
        } else {
          groupID = Zotero.Libraries.get(att.libraryID).id;
          return `zotero://open-pdf/groups/${groupID}/items/${key}`;
        }
      }
      resolve(await getPDFLink(topItem));
    })})  
 > Style:: ${
  <!-- 获取zotero style数据 -->
    (() => {
    let space = " "
    return Array.prototype.map.call(Zotero.ZoteroStyle.data.ztoolkit.ItemTree.globalCache.renderCellHooks.PublicationTags(
    0, Zotero.ZoteroStyle.data.ztoolkit.ItemTree.fieldHooks.globalCache.getFieldHooks.PublicationTags(
    "", true, true,
    topItem, undefined)
    ).childNodes,
    e => {
    e.innerText = e.innerText;
    return e.outerHTML
    }).join(space)
    })()
    }  
 > Author:: ${topItem.getCreators().map((v)=>v.firstName+" "+v.lastName).join("; ")}  
 > Year:: ${topItem.getField('year')}  
 > IF:: ==${topItem.getField('libraryCatalog')}==  
 > DOI:: ${topItem.getField("DOI")}  
 > Journal:: ${topItem.getField('publicationTitle')}  
 > journalAbbreviation:: ${topItem.getField('journalAbbreviation')}  
 > ShortNote:: ==${topItem.getField('archive')}==  
 > Keywords:: ${topItem.getField('keywordsAll')} 
 > 中科院分区升级版:: ${   
   <!-- 获取extra数据并正则提取 -->
   (topItem.getField("extra").match(/中科院分区升级版: \s*(.*?)($|\n)/) ? topItem.getField("extra").match(/中科院分区升级版: \s*(.*?)($|\n)/)[0]:"").replace(/(\r\n)|(\n)/g,"").split(":").slice(1)}  
 > 5年影响因子:: ${(topItem.getField("extra").match(/5年影响因子:\s*(.*?)($|\n)/) ? topItem.getField("extra").match(/5年影响因子:\s*(.*?)($|\n)/)[0]:"").replace(/(\r\n)|(\n)/g,"").split(":").slice(1)}  
 > JCR分区:: ${(topItem.getField("extra").match(/JCR分区:\s*(.*?)($|\n)/) ? topItem.getField("extra").match(/JCR分区:\s*(.*?)($|\n)/)[0]:"").replace(/(\r\n)|(\n)/g,"").split(":").slice(1)}  
 > 影响因子:: ${(topItem.getField("extra").match(/影响因子:\s*(.*?)($|\n)/) ? topItem.getField("extra").match(/影响因子:\s*(.*?)($|\n)/)[0]:"").replace(/(\r\n)|(\n)/g,"").split(":").slice(1)}  
 > titleTranslation:: ${ (topItem.getField("extra").match(/titleTranslation:\s*(.*?)($|\n)/) ? topItem.getField("extra").match(/titleTranslation:\s*(.*?)($|\n)/)[0]:"").replace(/(\r\n)|(\n)/g,"").split(":").slice(1)} 
 > abstractTranslation:: ${ (topItem.getField("extra").match(/abstractTranslation:\s*(.*?)($|\n)/) ? topItem.getField("extra").match(/abstractTranslation:\s*(.*?)($|\n)/)[0]:"").replace(/(\r\n)|(\n)/g,"").split(":").slice(1)}  
 <!-- 自定义其他内容 -->
  

 ### 创新:

 ### 疑问:

  
 ## 我的评价


 ## 前言


 ## 方法


 ## 结果


 ## Discussin & Conclusion & Limitation


image.png

然后就可以创建笔记了,相关教程看Better notes官方文档: 4.2 笔记模板/Note Template (yuque.com)

Better Notes设置同步

Better Notes设置同步,保存到ob的库里面


image.png
image.png

Obsidian dataview展示

打开ob,现在已经可以看到笔记了,但是没有dataview卡片展示,这个其实可有可无,但是还是分享给大家。教程来源: How to use Minimal Theme's Cards View on Any Other Theme in Obsidian - YouTube
我用的Topaz主题,没有把dataview table显示成卡片的功能,需要css,配置css
创建文件Minimal Theme cards.css,粘贴进css代码

/* MIT License | Copyright (c) Stephan Ango (@kepano) 
Cards snippet for Obsidian
author: @kepano
version: 2.0.0
Support my work:
https://github.com/sponsors/kepano
*/
:root {
  --cards-min-width: 180px;
  --cards-max-width: 1fr;
  --cards-mobile-width: 120px;
  --cards-image-height: 400px;
  --cards-padding: 1.2em;
  --cards-image-fit: contain;
  --cards-background: transparent;
  --cards-border-width: 1px;
  --cards-aspect-ratio: auto;
  --cards-columns: repeat(auto-fit, minmax(var(--cards-min-width), var(--cards-max-width))); }

@media (max-width: 400pt) {
  :root {
    --cards-min-width:var(--cards-mobile-width); } }
.cards.table-100 table.dataview tbody,
.table-100 .cards table.dataview tbody {
  padding: 0.25rem 0.75rem; }

.cards table.dataview tbody {
  clear: both;
  padding: 0.5rem 0;
  display: grid;
  grid-template-columns: var(--cards-columns);
  grid-column-gap: 0.75rem;
  grid-row-gap: 0.75rem; }
.cards table.dataview > tbody > tr {
  background-color: var(--cards-background);
  border: var(--cards-border-width) solid var(--background-modifier-border);
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0 0 calc(var(--cards-padding)/3) 0;
  border-radius: 6px;
  overflow: hidden;
  transition: box-shadow 0.15s linear;
  max-width: var(--cards-max-width); }
.cards table.dataview > tbody > tr:hover {
  border: var(--cards-border-width) solid var(--background-modifier-border-hover);
  box-shadow: 0 4px 6px 0px rgba(0, 0, 0, 0.05), 0 1px 3px 1px rgba(0, 0, 0, 0.025);
  transition: box-shadow 0.15s linear; }
.cards table.dataview tbody > tr > td {
  /* Paragraphs */
  /* Links */
  /* Buttons */
  /* Lists */
  /* Images */ }
  .cards table.dataview tbody > tr > td:first-child {
    font-weight: var(--bold-weight); }
  .cards table.dataview tbody > tr > td:first-child a {
    padding: 0 0 calc(var(--cards-padding)/3);
    display: block; }
  .cards table.dataview tbody > tr > td:not(:first-child) {
    font-size: 90%;
    color: var(--text-muted); }
  .cards table.dataview tbody > tr > td .el-p {
    display: block;
    width: 100%; }
  .cards table.dataview tbody > tr > td > *:not(.el-embed-image) {
    padding: calc(var(--cards-padding)/3) 0; }
  .cards table.dataview tbody > tr > td:not(:last-child):not(:first-child) > .el-p:not(.el-embed-image) {
    border-bottom: 1px solid var(--background-modifier-border);
    width: 100%; }
  .cards table.dataview tbody > tr > td a {
    text-decoration: none; }
  .cards table.dataview tbody > tr > td > button {
    width: 100%;
    margin: calc(var(--cards-padding)/2) 0; }
  .cards table.dataview tbody > tr > td:last-child > button {
    margin-bottom: calc(var(--cards-padding)/6); }
  .cards table.dataview tbody > tr > td > ul {
    width: 100%;
    padding: 0.25em 0 !important;
    margin: 0 auto !important; }
  .cards table.dataview tbody > tr > td:not(:last-child) > ul {
    border-bottom: 1px solid var(--background-modifier-border); }
  .cards table.dataview tbody > tr > td .el-embed-image {
    background-color: var(--background-secondary);
    display: block;
    margin: 0 calc(var(--cards-padding)/-2) 0 calc(var(--cards-padding)/-2);
    width: calc(100% + var(--cards-padding)); }
  .cards table.dataview tbody > tr > td img {
    aspect-ratio: var(--cards-aspect-ratio);
    width: 100%;
    object-fit: var(--cards-image-fit);
    max-height: var(--cards-image-height);
    background-color: var(--background-secondary);
    vertical-align: bottom; }

.markdown-source-view.mod-cm6.cards .dataview.table-view-table > tbody > tr > td,
.trim-cols .cards table.dataview tbody > tr > td {
  white-space: normal; }

.cards .dataview.table-view-table > tbody > tr > td,
.cards table.dataview tbody > tr > td,
.markdown-source-view.mod-cm6.cards .dataview.table-view-table > tbody > tr > td,
.markdown-source-view.mod-cm6.cards table.dataview tbody > tr > td {
  border-bottom: none;
  padding: 0 !important;
  line-height: 1.2;
  width: calc(100% - var(--cards-padding));
  margin: 0 auto;
  overflow: visible !important;
  max-width: 100%;
  display: flex; }

.links-int-on .cards table.dataview tbody > tr > td a {
  text-decoration: none; }

/* Block button */
.markdown-source-view.mod-cm6.cards .edit-block-button {
  top: 0px; }

/* ------------------- */
/* Sorting menu */
.cards.table-100 table.dataview thead > tr,
.table-100 .cards table.dataview thead > tr {
  right: 0.75rem; }

.table-100 .cards table.dataview thead:before,
.cards.table-100 table.dataview thead:before {
  margin-right: 0.75rem; }

.theme-light .cards table.dataview thead:before {
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 100 100"><path fill="black" d="M49.792 33.125l-5.892 5.892L33.333 28.45V83.333H25V28.45L14.438 39.017L8.542 33.125L29.167 12.5l20.625 20.625zm41.667 33.75L70.833 87.5l-20.625 -20.625l5.892 -5.892l10.571 10.567L66.667 16.667h8.333v54.883l10.567 -10.567l5.892 5.892z"></path></svg>'); }

.cards .el-pre + .el-lang-dataview .table-view-thead {
  padding-top: 8px; }
.cards table.dataview thead {
  user-select: none;
  width: 180px;
  display: block;
  float: right;
  position: relative;
  text-align: right;
  height: 24px;
  padding-bottom: 4px; }
.cards table.dataview thead:hover:before {
  opacity: 0.5;
  background-color: var(--background-modifier-hover); }
.cards table.dataview thead:before {
  content: '';
  position: absolute;
  right: 0;
  top: 0;
  width: 10px;
  height: 16px;
  background-repeat: no-repeat;
  cursor: var(--cursor);
  text-align: right;
  padding: var(--size-4-1) var(--size-4-2);
  margin-bottom: 2px;
  border-radius: var(--radius-s);
  font-weight: 500;
  font-size: var(--font-adaptive-small);
  opacity: 0.25;
  background-position: center center;
  background-size: 16px;
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 100 100"><path fill="white" d="M49.792 33.125l-5.892 5.892L33.333 28.45V83.333H25V28.45L14.438 39.017L8.542 33.125L29.167 12.5l20.625 20.625zm41.667 33.75L70.833 87.5l-20.625 -20.625l5.892 -5.892l10.571 10.567L66.667 16.667h8.333v54.883l10.567 -10.567l5.892 5.892z"></path></svg>'); }
.cards table.dataview thead > tr {
  top: -1px;
  position: absolute;
  display: none;
  z-index: 9;
  border: 1px solid var(--background-modifier-border-hover);
  background-color: var(--background-secondary);
  box-shadow: var(--shadow-s);
  padding: 6px;
  border-radius: var(--radius-m);
  flex-direction: column;
  margin: 26px 0 0 0;
  width: 100%; }
.cards table.dataview thead:hover > tr {
  display: flex; }
.cards table.dataview thead > tr > th {
  display: block;
  padding: 3px 30px 3px 6px !important;
  border-radius: var(--radius-s);
  width: 100%;
  font-weight: 400;
  color: var(--text-normal);
  cursor: var(--cursor);
  border: none;
  font-size: var(--font-ui-small); }
.cards table.dataview thead > tr > th[sortable-style="sortable-asc"],
.cards table.dataview thead > tr > th[sortable-style="sortable-desc"] {
  color: var(--text-normal); }
.cards table.dataview thead > tr > th:hover {
  color: var(--text-normal);
  background-color: var(--background-modifier-hover); }

/* ------------------- */
/* Helper classes */
.cards.cards-16-9 {
  --cards-aspect-ratio: 16/9; }
.cards.cards-1-1 {
  --cards-aspect-ratio: 1/1; }
.cards.cards-2-1 {
  --cards-aspect-ratio: 2/1; }
.cards.cards-2-3 {
  --cards-aspect-ratio: 2/3; }
.cards.cards-cols-1 {
  --cards-columns: repeat(1, minmax(0, 1fr)); }
.cards.cards-cols-2 {
  --cards-columns: repeat(2, minmax(0, 1fr)); }
.cards.cards-cover table.dataview tbody > tr > td img {
  object-fit: cover; }
.cards.cards-align-bottom table.dataview tbody > tr > td:last-child {
  align-items: flex-end;
  flex-grow: 1; }

@media (max-width: 400pt) {
  .cards table.dataview tbody > tr > td:not(:first-child) {
    font-size: 80%; } }
@media (min-width: 400pt) {
  .cards-cols-3 {
    --cards-columns: repeat(3, minmax(0, 1fr)); }

  .cards-cols-4 {
    --cards-columns: repeat(4, minmax(0, 1fr)); }

  .cards-cols-5 {
    --cards-columns: repeat(5, minmax(0, 1fr)); }

  .cards-cols-6 {
    --cards-columns: repeat(6, minmax(0, 1fr)); }

  .cards-cols-7 {
    --cards-columns: repeat(7, minmax(0, 1fr)); }

  .cards-cols-8 {
    --cards-columns: repeat(8, minmax(0, 1fr)); } }

而后,创建dataview,在yaml中键入相关代码:

---

cssClasses: cards
---

正文键入相关代码,其中test文件夹是你数据库中的文献文件夹

Table truncate(titleTranslation, 50) ,Style,ShortNote, "期刊:"+journalAbbreviation , truncate(Author, 50), truncate(abstractTranslation,100)
from "test"
image.png

然后就可以像开头展示的那样了,并且可以双向同步

存在的问题

当然,这个工作中仍然存在一些问题

  1. 其中最大的问题是,在Zotero里导出的markdown笔记,可能带有较多的反斜杠 \
  2. Zotero链接,如果存在多个pdf,可能会出现读取失败

我没有找到好办法处理,把流程分享给大家,也是想集思广益,解决流程中的问题。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,864评论 6 494
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,175评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,401评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,170评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,276评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,364评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,401评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,179评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,604评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,902评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,070评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,751评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,380评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,077评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,312评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,924评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,957评论 2 351

推荐阅读更多精彩内容