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文件都进行了编辑,则可能会进入差异合并阶段。
在此时,会弹出差异合并窗口,由用户手动选择需要保留的编辑。
在这里插入图片描述

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

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

展示

Zotero里

Obsidian里

其中File是链接到zotero pdf,可以跳转打开Zotero
在这里插入图片描述

Ob Dataview展示

在这里插入图片描述

相关插件及配置

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

在这里插入图片描述

添加 [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


在这里插入图片描述

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

Better Notes设置同步

Better Notes设置同步,保存到ob的库里面
在这里插入图片描述

在这里插入图片描述

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"

在这里插入图片描述

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

存在的问题

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值