├── manifest.json
├── styles.css
├── data.json
├── README.md
└── main.js
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "simpread",
3 | "name": "SimpRead Sync",
4 | "version": "3.0.0",
5 | "minAppVersion": "0.12.10",
6 | "description": "Auto sync SimpRead unreader",
7 | "author": "Kenshin",
8 | "authorUrl": "http://simpread.pro/",
9 | "isDesktopOnly": true
10 | }
11 |
--------------------------------------------------------------------------------
/styles.css:
--------------------------------------------------------------------------------
1 |
2 | .theme-light {
3 | --hyper-highlight: var(--text-highlight-bg, #FFF2AC);
4 | --error-color: #DB5461;
5 | --success-color: #8AAA79;
6 | --info-color: var(--text-muted, #000);
7 | }
8 |
9 | .theme-dark {
10 | --hyper-highlight: var(--text-highlight-bg, #1C5998);
11 | --error-color: #DB5461;
12 | --success-color: #8AAA79;
13 | --info-color: var(--text-muted, #fff);
14 | }
15 |
16 | .rw-info-container {
17 | margin-right: 10px;
18 | }
19 |
20 | .rw-info {
21 | color: var(--info-color);
22 | font-size: smaller;
23 | }
24 |
25 | .rw-error {
26 | color: var(--error-color);
27 | }
28 | .rw-success {
29 | color: var(--success-color);
30 | }
31 |
32 |
33 | img[alt=rw-book-cover] {
34 | max-height: 200px;
35 | }
36 |
37 | .rw-hyper-highlight {
38 | background-color: var(--hyper-highlight);
39 | }
40 |
41 |
--------------------------------------------------------------------------------
/data.json:
--------------------------------------------------------------------------------
1 | {
2 | "config": "simpread_config.json",
3 | "host": "localhost",
4 | "port": 7027,
5 | "count": "10",
6 | "override": true,
7 | "folder": "SimpRead",
8 | "sub_folder": true,
9 | "path": "",
10 | "assets_root": "root",
11 | "assets": "",
12 | "img_relative": true,
13 | "annote": true,
14 | "frequency": "-1",
15 | "tag_prefix": "#",
16 | "tag_suffix": " ",
17 | "format": "{\"headingStyle\":\"atx\",\"hr\":\"---\",\"bulletListMarker\":\"-\"}",
18 | "ext_uri": "",
19 | "title": "{{id}}-{{title}}{{mode}}",
20 | "template": "---\ntitle: \"srAnnote@{{title}}\"\naliases: [<% if ( unread.note && unread.title != unread.note ) { %>\"srAnnote@{{note}}\",<% } %>\"srAnnote@{{title}}\"]\ntype: Simpread\ntags:\n{{ - |tag|\n| }}\n---\n\n# {{title}}\n<% if ( unread.desc ) { %>\n> [!summary] 描述 \n> <%- unread.desc %>\n<% } %>\n> [!md] Metadata \n> **标题**:: \"{{title}}\" \n> **日期**:: [[{{create|yyyy-mm-dd}}]] \n<% if ( unread.refs ) { -%>\n> **外部引用**:: {{refs}} \n<% } %>\n## Annotations\n<% if ( unread.annotations.length > 0 ) { %>\n{{annotations}}\n<% } %>\n",
21 | "annotation": "<%\nlet colors = [ '#B4D9FB', '#ffeb3b', '#a2e9f2', '#a1e0ff', '#a8ea68', '#ffb7da' ],\n color = colors[ annote.color ];\n-%>\n> [📌](<{{an_int_uri}}>) \">Highlight {{an_tags}}\n{{{html_format|>|{{an_html}}}}}\n<% if (annote.note) { -%>\n> - {{an_note}}\n<% } %>\n^sran-{{an_id}}\n\n\n"
22 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |

2 |
3 | # 描述
4 |
5 | 全新 3.0 版,大幅优化了配置相关的内容,目前分为两种使用方案:
6 |
7 | 1️⃣ 轻量级使用,**安装此插件后无须任何设置**,配合 [导入到 Obsidian](https://github.com/Kenshin/simpread/discussions/2902) 就能将实现剪藏功能(包括图片本地化)
8 |
9 | 2️⃣ 重度级使用,当设置了 `simpread_config.json` 就能实现:**集文章标注实时同步到 Obsidain、本地快照实时保存到本地、管理你全部的稍后读于一身的 Obsidian + 简悦全流程化一站式解决方案**。
10 |
11 | 不仅如此:还支持标 Command Support 等众多 Obsidian 功能。
12 |
13 | # 视频
14 |
15 | https://user-images.githubusercontent.com/81074/135744012-ea403cb7-3145-401a-b9d5-ac588ff82975.mp4
16 |
17 | # 官网
18 |
19 | https://github.com/Kenshin/simpread-obsidian-plugin
20 |
21 | # 版本
22 |
23 | [](https://github.com/kenshin/simpread-obsidian-plugin/releases/latest)
24 |
25 | # 优势
26 |
27 | - 内置与 [Markdown 模板辅助增强](https://github.com/Kenshin/simpread/discussions/3725) 具有一样的模板解析引擎。
28 |
29 | - 在 Web 标注 → 标注的 Markdown 自动出现在你的 Obsdian。(全自动化方案)
30 |
31 | - 标注的页面也可以同步生成本地永久快照。
32 |
33 | - 每条标注均可链接到本地永久快照的原文对应位置。
34 |
35 | - 得益于简悦精准的阅读模式匹配功能以及 HTML → Markdown 方案,通过此方式生成的 Markdown 的精准度最高,甚至可以匹配数学公式(LaTeX)
36 |
37 | - 使用 Command Support 可以将任意稍后读转换为 Markdown,可以弥补 [Markdown 模板辅助增强](https://github.com/Kenshin/simpread/discussions/3725) 仅能用于阅读模式的缺陷。
38 |
39 | # 集成标注功能
40 |
41 | 得益于 [同步助手 1.1.0](https://github.com/Kenshin/simpread/discussions/4049#discussioncomment-2982511) 的 [标注嵌入模式](https://github.com/Kenshin/simpread/discussions/4070),终于打通了 Obsidian 标注的最后一道门槛:**在 Obsidian 中标注本地快照并实时同步到你的 Obsidian,不仅如此还可以管理你的稍后读**,详细说明 [请看这里](https://github.com/Kenshin/simpread/discussions/2889#discussioncomment-2983691)。
42 |
43 | https://user-images.githubusercontent.com/81074/174549318-892ed045-49f2-4c3f-9674-92c735c6674a.mp4
44 |
45 | # 受众
46 |
47 | 如果你只希望实现 Obsidian 剪藏功能、无须配置的轻量级使用者 [详细说明](https://github.com/Kenshin/simpread/discussions/2889#discussioncomment-14330230)
48 |
49 | 如果你是简悦 + Obsidian 的深度使用者,尤其是使用同步助手的用户,非常建议使用此方式,会节省你大量的时间,可使用下面的 [**配置库方案**](https://github.com/Kenshin/simpread/discussions/4531#discussioncomment-3844881) 或 [**一站式教程**](https://www.yuque.com/kenshin/simpread/fr8zo5)
50 |
51 | # 配置库
52 |
53 | [简悦 · 配置库](https://github.com/Kenshin/simpread/discussions/4531) 是简悦官方推出的一套针对新用户的极简配置方案,方便新用户用最快的方式使用简悦的各种高级服务,配置库内置了常用的双链笔记用法,如:Notion、Obsidian、Logseq、Roam Research,同时包含了简悦在阅读模式上的一些常规插件:Live Editor、题图、Safari 阅读模式等。
54 |
55 | 目前配置库包含了 Obsidian 的一站式配置方案,细节 [请看这里](https://github.com/Kenshin/simpread/discussions/4531#discussioncomment-3844881)。
56 |
57 | # 一站式教程
58 |
59 | https://www.yuque.com/kenshin/simpread/fr8zo5
60 |
61 | # 安装与升级
62 |
63 | 此插件没有上架到 Obsdian 第三方社区,可通过 [此方式](https://github.com/Kenshin/simpread/discussions/2889#discussioncomment-2831907) 自动安装。
64 |
65 | # 功能
66 |
67 | - [Server Settings](https://github.com/Kenshin/simpread/discussions/2889#discussioncomment-1388527)
68 |
69 | - [Saved Settings](https://github.com/Kenshin/simpread/discussions/2889#discussioncomment-1389535)
70 |
71 | - [Image Hosting](https://github.com/Kenshin/simpread/discussions/2889#discussioncomment-10446316)
72 |
73 | - [Sync Settings](https://github.com/Kenshin/simpread/discussions/2889#discussioncomment-1393730)
74 |
75 | - [Markdown Template Settings](https://github.com/Kenshin/simpread/discussions/2889#discussioncomment-1420516)
76 |
77 | - [Commands Support](https://github.com/Kenshin/simpread/discussions/2889#discussioncomment-1420517)
78 |
79 | # 工作流
80 |
81 | 这是来自 [简悦社区](https://t.me/simpread) 用户 [1wingedangel](https://github.com/1wingedangel) 的基于 Obsidan + SimpRead Unreader Sync 的 [工作流分享](https://github.com/Kenshin/simpread/discussions/3999)。
82 |
83 | # 更新日志
84 |
85 | https://github.com/Kenshin/simpread/discussions/2889#discussioncomment-2831918
86 |
87 | # 注意
88 |
89 | - [如何为导出文件排序](https://github.com/Kenshin/simpread/discussions/2889#discussioncomment-2831914)
90 |
91 | - 简悦稍后读 → Obsidian 属于单向更新。
92 |
93 | - 通过此插件生成的 Markdown 的任何改动都可能被覆盖。
94 |
95 | - 在简悦删除稍后读仅能删除扩展端的稍后读,无法同步删除 Obsidian 对应的文件。
96 |
97 | # 关联
98 |
99 | 简悦与 Obsidian 都是基于 Local first 概念,即使只是轻量级使用简悦,也可以实现很多联动:
100 |
101 | - [利用简悦插件 Live Editor 来助力你的双向链接笔记剪藏流程](https://zhuanlan.zhihu.com/p/412710060)
102 |
103 | - [自动导入标注到 Obsidian(不使用同步助手方案)](https://github.com/Kenshin/simpread/discussions/3932)
104 |
105 | - [利用 Dataview + Blue Topaz + Markdown 辅助增强 + 导入到 Obsidian 插件,实现对标注的汇总与回顾](https://github.com/Kenshin/simpread/discussions/3807)
106 |
107 | 关于简悦与 Obsidian 的更多联动 [请看这里](https://github.com/Kenshin/simpread/discussions?discussions_q=label%3Aobsidian)。
108 |
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | var obsidian = require( 'obsidian' );
2 | const http = require( 'http' ),
3 | fs = require( 'fs' ),
4 | path = require( 'path' ),
5 | { shell } = require( 'electron' );
6 |
7 | function __awaiter(thisArg, _arguments, P, generator) {
8 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
9 | return new (P || (P = Promise))(function (resolve, reject) {
10 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
11 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
12 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
13 | step((generator = generator.apply(thisArg, _arguments || [])).next());
14 | });
15 | }
16 |
17 | function TurndownService() {
18 | return function(){"use strict";function e(e,n){return Array(n+1).join(e)}var n=["ADDRESS","ARTICLE","ASIDE","AUDIO","BLOCKQUOTE","BODY","CANVAS","CENTER","DD","DIR","DIV","DL","DT","FIELDSET","FIGCAPTION","FIGURE","FOOTER","FORM","FRAMESET","H1","H2","H3","H4","H5","H6","HEADER","HGROUP","HR","HTML","ISINDEX","LI","MAIN","MENU","NAV","NOFRAMES","NOSCRIPT","OL","OUTPUT","P","PRE","SECTION","TABLE","TBODY","TD","TFOOT","TH","THEAD","TR","UL"];function t(e){return a(e,n)}var r=["AREA","BASE","BR","COL","COMMAND","EMBED","HR","IMG","INPUT","KEYGEN","LINK","META","PARAM","SOURCE","TRACK","WBR"];function i(e){return a(e,r)}var o=["A","TABLE","THEAD","TBODY","TFOOT","TH","TD","IFRAME","SCRIPT","AUDIO","VIDEO"];function a(e,n){return n.indexOf(e.nodeName)>=0}function l(e,n){return e.getElementsByTagName&&n.some((function(n){return e.getElementsByTagName(n).length}))}var u={};function c(e){return e?e.replace(/(\n+\s*)+/g,"\n"):""}function s(e){for(var n in this.options=e,this._keep=[],this._remove=[],this.blankRule={replacement:e.blankReplacement},this.keepReplacement=e.keepReplacement,this.defaultRule={replacement:e.defaultReplacement},this.array=[],e.rules)this.array.push(e.rules[n])}function f(e,n,t){for(var r=0;r-1)return!0}else{if("function"!=typeof r)throw new TypeError("`filter` needs to be a string, array, or function");if(r.call(e,n,t))return!0}}function p(e){var n=e.nextSibling||e.parentNode;return e.parentNode.removeChild(e),n}function h(e,n,t){return e&&e.parentNode===n||t(n)?n.nextSibling||n.parentNode:n.firstChild||n.nextSibling||n.parentNode}u.paragraph={filter:"p",replacement:function(e){return"\n\n"+e+"\n\n"}},u.lineBreak={filter:"br",replacement:function(e,n,t){return t.br+"\n"}},u.heading={filter:["h1","h2","h3","h4","h5","h6"],replacement:function(n,t,r){var i=Number(t.nodeName.charAt(1));return"setext"===r.headingStyle&&i<3?"\n\n"+n+"\n"+e(1===i?"=":"-",n.length)+"\n\n":"\n\n"+e("#",i)+" "+n+"\n\n"}},u.blockquote={filter:"blockquote",replacement:function(e){return"\n\n"+(e=(e=e.replace(/^\n+|\n+$/g,"")).replace(/^/gm,"> "))+"\n\n"}},u.list={filter:["ul","ol"],replacement:function(e,n){var t=n.parentNode;return"LI"===t.nodeName&&t.lastElementChild===n?"\n"+e:"\n\n"+e+"\n\n"}},u.listItem={filter:"li",replacement:function(e,n,t){e=e.replace(/^\n+/,"").replace(/\n+$/,"\n").replace(/\n/gm,"\n ");var r=t.bulletListMarker+" ",i=n.parentNode;if("OL"===i.nodeName){var o=i.getAttribute("start"),a=Array.prototype.indexOf.call(i.children,n);r=(o?Number(o)+a:a+1)+". "}return r+e+(n.nextSibling&&!/\n$/.test(e)?"\n":"")}},u.indentedCodeBlock={filter:function(e,n){return"indented"===n.codeBlockStyle&&"PRE"===e.nodeName&&e.firstChild&&"CODE"===e.firstChild.nodeName},replacement:function(e,n,t){return"\n\n "+n.firstChild.textContent.replace(/\n/g,"\n ")+"\n\n"}},u.fencedCodeBlock={filter:function(e,n){return"fenced"===n.codeBlockStyle&&"PRE"===e.nodeName&&e.firstChild&&"CODE"===e.firstChild.nodeName},replacement:function(n,t,r){for(var i,o=((t.firstChild.getAttribute("class")||"").match(/language-(\S+)/)||[null,""])[1],a=t.firstChild.textContent,l=r.fence.charAt(0),u=3,c=new RegExp("^"+l+"{3,}","gm");i=c.exec(a);)i[0].length>=u&&(u=i[0].length+1);var s=e(l,u);return"\n\n"+s+o+"\n"+a.replace(/\n$/,"")+"\n"+s+"\n\n"}},u.horizontalRule={filter:"hr",replacement:function(e,n,t){return"\n\n"+t.hr+"\n\n"}},u.inlineLink={filter:function(e,n){return"inlined"===n.linkStyle&&"A"===e.nodeName&&e.getAttribute("href")},replacement:function(e,n){var t=n.getAttribute("href"),r=c(n.getAttribute("title"));return r&&(r=' "'+r+'"'),"["+e+"]("+t+r+")"}},u.referenceLink={filter:function(e,n){return"referenced"===n.linkStyle&&"A"===e.nodeName&&e.getAttribute("href")},replacement:function(e,n,t){var r,i,o=n.getAttribute("href"),a=c(n.getAttribute("title"));switch(a&&(a=' "'+a+'"'),t.linkReferenceStyle){case"collapsed":r="["+e+"][]",i="["+e+"]: "+o+a;break;case"shortcut":r="["+e+"]",i="["+e+"]: "+o+a;break;default:var l=this.references.length+1;r="["+e+"]["+l+"]",i="["+l+"]: "+o+a}return this.references.push(i),r},references:[],append:function(e){var n="";return this.references.length&&(n="\n\n"+this.references.join("\n")+"\n\n",this.references=[]),n}},u.emphasis={filter:["em","i"],replacement:function(e,n,t){return e.trim()?t.emDelimiter+e+t.emDelimiter:""}},u.strong={filter:["strong","b"],replacement:function(e,n,t){return e.trim()?t.strongDelimiter+e+t.strongDelimiter:""}},u.code={filter:function(e){var n=e.previousSibling||e.nextSibling,t="PRE"===e.parentNode.nodeName&&!n;return"CODE"===e.nodeName&&!t},replacement:function(e){if(!e)return"";e=e.replace(/\r?\n|\r/g," ");for(var n=/^`|^ .*?[^ ].* $|`$/.test(e)?" ":"",t="`",r=e.match(/`+/gm)||[];-1!==r.indexOf(t);)t+="`";return t+n+e+n+t}},u.image={filter:"img",replacement:function(e,n){var t=c(n.getAttribute("alt")),r=n.getAttribute("src")||"",i=c(n.getAttribute("title"));return r?"+")":""}},s.prototype={add:function(e,n){this.array.unshift(n)},keep:function(e){this._keep.unshift({filter:e,replacement:this.keepReplacement})},remove:function(e){this._remove.unshift({filter:e,replacement:function(){return""}})},forNode:function(e){return e.isBlank?this.blankRule:(n=f(this.array,e,this.options))||(n=f(this._keep,e,this.options))||(n=f(this._remove,e,this.options))?n:this.defaultRule;var n},forEach:function(e){for(var n=0;n'+e+"","text/html").getElementById("turndown-root"):r=e.cloneNode(!0);return function(e){var n=e.element,t=e.isBlock,r=e.isVoid,i=e.isPre||function(e){return"PRE"===e.nodeName};if(n.firstChild&&!i(n)){for(var o=null,a=!1,l=null,u=h(l,n,i);u!==n;){if(3===u.nodeType||4===u.nodeType){var c=u.data.replace(/[ \r\n\t]+/g," ");if(o&&!/ $/.test(o.data)||a||" "!==c[0]||(c=c.substr(1)),!c){u=p(u);continue}u.data=c,o=u}else{if(1!==u.nodeType){u=p(u);continue}t(u)||"BR"===u.nodeName?(o&&(o.data=o.data.replace(/ $/,"")),o=null,a=!1):r(u)||i(u)?(o=null,a=!0):o&&(a=!1)}var s=h(l,u,i);l=u,u=s}o&&(o.data=o.data.replace(/ $/,""),o.data||p(o))}}({element:r,isBlock:t,isVoid:i,isPre:n.preformattedCode?N:null}),r}function N(e){return"PRE"===e.nodeName||"CODE"===e.nodeName}function E(e,n){return e.isBlock=t(e),e.isCode="CODE"===e.nodeName||e.parentNode.isCode,e.isBlank=function(e){return!i(e)&&!function(e){return a(e,o)}(e)&&/^\s*$/i.test(e.textContent)&&!function(e){return l(e,r)}(e)&&!function(e){return l(e,o)}(e)}(e),e.flankingWhitespace=function(e,n){if(e.isBlock||n.preformattedCode&&e.isCode)return{leading:"",trailing:""};var t=(r=e.textContent,i=r.match(/^(([ \t\r\n]*)(\s*))[\s\S]*?((\s*?)([ \t\r\n]*))$/),{leading:i[1],leadingAscii:i[2],leadingNonAscii:i[3],trailing:i[4],trailingNonAscii:i[5],trailingAscii:i[6]});var r,i;t.leadingAscii&&T("left",e,n)&&(t.leading=t.leadingNonAscii);t.trailingAscii&&T("right",e,n)&&(t.trailing=t.trailingNonAscii);return{leading:t.leading,trailing:t.trailing}}(e,n),e}function T(e,n,r){var i,o,a;return"left"===e?(i=n.previousSibling,o=/ $/):(i=n.nextSibling,o=/^ /),i&&(3===i.nodeType?a=o.test(i.nodeValue):r.preformattedCode&&"CODE"===i.nodeName?a=!1:1!==i.nodeType||t(i)||(a=o.test(i.textContent))),a}var R=Array.prototype.reduce,C=[[/\\/g,"\\\\"],[/\*/g,"\\*"],[/^-/g,"\\-"],[/^\+ /g,"\\+ "],[/^(=+)/g,"\\$1"],[/^(#{1,6}) /g,"\\$1 "],[/`/g,"\\`"],[/^~~~/g,"\\~~~"],[/\[/g,"\\["],[/\]/g,"\\]"],[/^>/g,"\\>"],[/_/g,"\\_"],[/^(\d+)\. /g,"$1\\. "]];function k(e){if(!(this instanceof k))return new k(e);var n={rules:u,headingStyle:"setext",hr:"* * *",bulletListMarker:"*",codeBlockStyle:"indented",fence:"```",emDelimiter:"_",strongDelimiter:"**",linkStyle:"inlined",linkReferenceStyle:"full",br:" ",preformattedCode:!1,blankReplacement:function(e,n){return n.isBlock?"\n\n":""},keepReplacement:function(e,n){return n.isBlock?"\n\n"+n.outerHTML+"\n\n":n.outerHTML},defaultReplacement:function(e,n){return n.isBlock?"\n\n"+e+"\n\n":e}};this.options=function(e){for(var n=1;n0&&"\n"===e[n-1];)n--;return e.substring(0,n)}(e),r=n.replace(/^\n*/,""),i=Math.max(e.length-t.length,n.length-r.length);return t+"\n\n".substring(0,i)+r}return k.prototype={turndown:function(e){if(!function(e){return null!=e&&("string"==typeof e||e.nodeType&&(1===e.nodeType||9===e.nodeType||11===e.nodeType))}(e))throw new TypeError(e+" is not a string, or an element/document/fragment node.");if(""===e)return"";var n=b.call(this,new y(e,this.options));return O.call(this,n)},use:function(e){if(Array.isArray(e))for(var n=0;n1;if(options.cache){if(!filename){throw new Error("cache option requires a filename")}func=exports.cache.get(filename);if(func){return func}if(!hasTemplate){template=fileLoader(filename).toString().replace(_BOM,"")}}else if(!hasTemplate){if(!filename){throw new Error("Internal EJS error: no file name or template "+"provided")}template=fileLoader(filename).toString().replace(_BOM,"")}func=exports.compile(template,options);if(options.cache){exports.cache.set(filename,func)}return func}function tryHandleCache(options,data,cb){var result;if(!cb){if(typeof exports.promiseImpl=="function"){return new exports.promiseImpl(function(resolve,reject){try{result=handleCache(options)(data);resolve(result)}catch(err){reject(err)}})}else{throw new Error("Please provide a callback function")}}else{try{result=handleCache(options)(data)}catch(err){return cb(err)}cb(null,result)}}function fileLoader(filePath){return exports.fileLoader(filePath)}function includeFile(path,options){var opts=utils.shallowCopy({},options);opts.filename=getIncludePath(path,opts);if(typeof options.includer==="function"){var includerResult=options.includer(path,opts.filename);if(includerResult){if(includerResult.filename){opts.filename=includerResult.filename}if(includerResult.template){return handleCache(opts,includerResult.template)}}}return handleCache(opts)}function rethrow(err,str,flnm,lineno,esc){var lines=str.split("\n");var start=Math.max(lineno-3,0);var end=Math.min(lines.length,lineno+3);var filename=esc(flnm);var context=lines.slice(start,end).map(function(line,i){var curr=i+start+1;return(curr==lineno?" >> ":" ")+curr+"| "+line}).join("\n");err.path=filename;err.message=(filename||"ejs")+":"+lineno+"\n"+context+"\n\n"+err.message;throw err}function stripSemi(str){return str.replace(/;(\s*$)/,"$1")}exports.compile=function compile(template,opts){var templ;if(opts&&opts.scope){if(!scopeOptionWarned){console.warn("`scope` option is deprecated and will be removed in EJS 3");scopeOptionWarned=true}if(!opts.context){opts.context=opts.scope}delete opts.scope}templ=new Template(template,opts);return templ.compile()};exports.render=function(template,d,o){var data=d||{};var opts=o||{};if(arguments.length==2){utils.shallowCopyFromList(opts,data,_OPTS_PASSABLE_WITH_DATA)}return handleCache(opts,template)(data)};exports.renderFile=function(){var args=Array.prototype.slice.call(arguments);var filename=args.shift();var cb;var opts={filename:filename};var data;var viewOpts;if(typeof arguments[arguments.length-1]=="function"){cb=args.pop()}if(args.length){data=args.shift();if(args.length){utils.shallowCopy(opts,args.pop())}else{if(data.settings){if(data.settings.views){opts.views=data.settings.views}if(data.settings["view cache"]){opts.cache=true}viewOpts=data.settings["view options"];if(viewOpts){utils.shallowCopy(opts,viewOpts)}}utils.shallowCopyFromList(opts,data,_OPTS_PASSABLE_WITH_DATA_EXPRESS)}opts.filename=filename}else{data={}}return tryHandleCache(opts,data,cb)};exports.Template=Template;exports.clearCache=function(){exports.cache.reset()};function Template(text,opts){opts=opts||{};var options={};this.templateText=text;this.mode=null;this.truncate=false;this.currentLine=1;this.source="";options.client=opts.client||false;options.escapeFunction=opts.escape||opts.escapeFunction||utils.escapeXML;options.compileDebug=opts.compileDebug!==false;options.debug=!!opts.debug;options.filename=opts.filename;options.openDelimiter=opts.openDelimiter||exports.openDelimiter||_DEFAULT_OPEN_DELIMITER;options.closeDelimiter=opts.closeDelimiter||exports.closeDelimiter||_DEFAULT_CLOSE_DELIMITER;options.delimiter=opts.delimiter||exports.delimiter||_DEFAULT_DELIMITER;options.strict=opts.strict||false;options.context=opts.context;options.cache=opts.cache||false;options.rmWhitespace=opts.rmWhitespace;options.root=opts.root;options.includer=opts.includer;options.outputFunctionName=opts.outputFunctionName;options.localsName=opts.localsName||exports.localsName||_DEFAULT_LOCALS_NAME;options.views=opts.views;options.async=opts.async;options.destructuredLocals=opts.destructuredLocals;options.legacyInclude=typeof opts.legacyInclude!="undefined"?!!opts.legacyInclude:true;if(options.strict){options._with=false}else{options._with=typeof opts._with!="undefined"?opts._with:true}this.opts=options;this.regex=this.createRegex()}Template.modes={EVAL:"eval",ESCAPED:"escaped",RAW:"raw",COMMENT:"comment",LITERAL:"literal"};Template.prototype={createRegex:function(){var str=_REGEX_STRING;var delim=utils.escapeRegExpChars(this.opts.delimiter);var open=utils.escapeRegExpChars(this.opts.openDelimiter);var close=utils.escapeRegExpChars(this.opts.closeDelimiter);str=str.replace(/%/g,delim).replace(//g,close);return new RegExp(str)},compile:function(){var src;var fn;var opts=this.opts;var prepended="";var appended="";var escapeFn=opts.escapeFunction;var ctor;var sanitizedFilename=opts.filename?JSON.stringify(opts.filename):"undefined";if(!this.source){this.generateSource();prepended+=' var __output = "";\n'+" function __append(s) { if (s !== undefined && s !== null) __output += s }\n";if(opts.outputFunctionName){prepended+=" var "+opts.outputFunctionName+" = __append;"+"\n"}if(opts.destructuredLocals&&opts.destructuredLocals.length){var destructuring=" var __locals = ("+opts.localsName+" || {}),\n";for(var i=0;i0){destructuring+=",\n "}destructuring+=name+" = __locals."+name}prepended+=destructuring+";\n"}if(opts._with!==false){prepended+=" with ("+opts.localsName+" || {}) {"+"\n";appended+=" }"+"\n"}appended+=" return __output;"+"\n";this.source=prepended+this.source+appended}if(opts.compileDebug){src="var __line = 1"+"\n"+" , __lines = "+JSON.stringify(this.templateText)+"\n"+" , __filename = "+sanitizedFilename+";"+"\n"+"try {"+"\n"+this.source+"} catch (e) {"+"\n"+" rethrow(e, __lines, __filename, __line, escapeFn);"+"\n"+"}"+"\n"}else{src=this.source}if(opts.client){src="escapeFn = escapeFn || "+escapeFn.toString()+";"+"\n"+src;if(opts.compileDebug){src="rethrow = rethrow || "+rethrow.toString()+";"+"\n"+src}}if(opts.strict){src='"use strict";\n'+src}if(opts.debug){console.log(src)}if(opts.compileDebug&&opts.filename){src=src+"\n"+"//# sourceURL="+sanitizedFilename+"\n"}try{if(opts.async){try{ctor=new Function("return (async function(){}).constructor;")()}catch(e){if(e instanceof SyntaxError){throw new Error("This environment does not support async/await")}else{throw e}}}else{ctor=Function}fn=new ctor(opts.localsName+", escapeFn, include, rethrow",src)}catch(e){if(e instanceof SyntaxError){if(opts.filename){e.message+=" in "+opts.filename}e.message+=" while compiling ejs\n\n";e.message+="If the above error is not helpful, you may want to try EJS-Lint:\n";e.message+="https://github.com/RyanZim/EJS-Lint";if(!opts.async){e.message+="\n";e.message+="Or, if you meant to create an async function, pass `async: true` as an option."}}throw e}var returnedFn=opts.client?fn:function anonymous(data){var include=function(path,includeData){var d=utils.shallowCopy({},data);if(includeData){d=utils.shallowCopy(d,includeData)}return includeFile(path,opts)(d)};return fn.apply(opts.context,[data||{},escapeFn,include,rethrow])};if(opts.filename&&typeof Object.defineProperty==="function"){var filename=opts.filename;var basename=path.basename(filename,path.extname(filename));try{Object.defineProperty(returnedFn,"name",{value:basename,writable:false,enumerable:false,configurable:true})}catch(e){}}return returnedFn},generateSource:function(){var opts=this.opts;if(opts.rmWhitespace){this.templateText=this.templateText.replace(/[\r\n]+/g,"\n").replace(/^\s+|\s+$/gm,"")}this.templateText=this.templateText.replace(/[ \t]*<%_/gm,"<%_").replace(/_%>[ \t]*/gm,"_%>");var self=this;var matches=this.parseTemplateText();var d=this.opts.delimiter;var o=this.opts.openDelimiter;var c=this.opts.closeDelimiter;if(matches&&matches.length){matches.forEach(function(line,index){var closing;if(line.indexOf(o+d)===0&&line.indexOf(o+d+d)!==0){closing=matches[index+2];if(!(closing==d+c||closing=="-"+d+c||closing=="_"+d+c)){throw new Error('Could not find matching close tag for "'+line+'".')}}self.scanLine(line)})}},parseTemplateText:function(){var str=this.templateText;var pat=this.regex;var result=pat.exec(str);var arr=[];var firstPos;while(result){firstPos=result.index;if(firstPos!==0){arr.push(str.substring(0,firstPos));str=str.slice(firstPos)}arr.push(result[0]);str=str.slice(result[0].length);result=pat.exec(str)}if(str){arr.push(str)}return arr},_addOutput:function(line){if(this.truncate){line=line.replace(/^(?:\r\n|\r|\n)/,"");this.truncate=false}if(!line){return line}line=line.replace(/\\/g,"\\\\");line=line.replace(/\n/g,"\\n");line=line.replace(/\r/g,"\\r");line=line.replace(/"/g,'\\"');this.source+=' ; __append("'+line+'")'+"\n"},scanLine:function(line){var self=this;var d=this.opts.delimiter;var o=this.opts.openDelimiter;var c=this.opts.closeDelimiter;var newLineCount=0;newLineCount=line.split("\n").length-1;switch(line){case o+d:case o+d+"_":this.mode=Template.modes.EVAL;break;case o+d+"=":this.mode=Template.modes.ESCAPED;break;case o+d+"-":this.mode=Template.modes.RAW;break;case o+d+"#":this.mode=Template.modes.COMMENT;break;case o+d+d:this.mode=Template.modes.LITERAL;this.source+=' ; __append("'+line.replace(o+d+d,o+d)+'")'+"\n";break;case d+d+c:this.mode=Template.modes.LITERAL;this.source+=' ; __append("'+line.replace(d+d+c,d+c)+'")'+"\n";break;case d+c:case"-"+d+c:case"_"+d+c:if(this.mode==Template.modes.LITERAL){this._addOutput(line)}this.mode=null;this.truncate=line.indexOf("-")===0||line.indexOf("_")===0;break;default:if(this.mode){switch(this.mode){case Template.modes.EVAL:case Template.modes.ESCAPED:case Template.modes.RAW:if(line.lastIndexOf("//")>line.lastIndexOf("\n")){line+="\n"}}switch(this.mode){case Template.modes.EVAL:this.source+=" ; "+line+"\n";break;case Template.modes.ESCAPED:this.source+=" ; __append(escapeFn("+stripSemi(line)+"))"+"\n";break;case Template.modes.RAW:this.source+=" ; __append("+stripSemi(line)+")"+"\n";break;case Template.modes.COMMENT:break;case Template.modes.LITERAL:this._addOutput(line);break}}else{this._addOutput(line)}}if(self.opts.compileDebug&&newLineCount){this.currentLine+=newLineCount;this.source+=" ; __line = "+this.currentLine+"\n"}}};exports.escapeXML=utils.escapeXML;exports.__express=exports.renderFile;exports.VERSION=_VERSION_STRING;exports.name=_NAME;if(typeof window!="undefined"){window.ejs=exports}},{"../package.json":6,"./utils":2,fs:3,path:4}],2:[function(require,module,exports){"use strict";var regExpChars=/[|\\{}()[\]^$+*?.]/g;exports.escapeRegExpChars=function(string){if(!string){return""}return String(string).replace(regExpChars,"\\$&")};var _ENCODE_HTML_RULES={"&":"&","<":"<",">":">",'"':""","'":"'"};var _MATCH_HTML=/[&<>'"]/g;function encode_char(c){return _ENCODE_HTML_RULES[c]||c}var escapeFuncStr="var _ENCODE_HTML_RULES = {\n"+' "&": "&"\n'+' , "<": "<"\n'+' , ">": ">"\n'+' , \'"\': """\n'+' , "\'": "'"\n'+" }\n"+" , _MATCH_HTML = /[&<>'\"]/g;\n"+"function encode_char(c) {\n"+" return _ENCODE_HTML_RULES[c] || c;\n"+"};\n";exports.escapeXML=function(markup){return markup==undefined?"":String(markup).replace(_MATCH_HTML,encode_char)};exports.escapeXML.toString=function(){return Function.prototype.toString.call(this)+";\n"+escapeFuncStr};exports.shallowCopy=function(to,from){from=from||{};for(var p in from){to[p]=from[p]}return to};exports.shallowCopyFromList=function(to,from,list){for(var i=0;i=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up--;up){parts.unshift("..")}}return parts}exports.resolve=function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:process.cwd();if(typeof path!=="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){continue}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=path.charAt(0)==="/"}resolvedPath=normalizeArray(filter(resolvedPath.split("/"),function(p){return!!p}),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."};exports.normalize=function(path){var isAbsolute=exports.isAbsolute(path),trailingSlash=substr(path,-1)==="/";path=normalizeArray(filter(path.split("/"),function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path};exports.isAbsolute=function(path){return path.charAt(0)==="/"};exports.join=function(){var paths=Array.prototype.slice.call(arguments,0);return exports.normalize(filter(paths,function(p,index){if(typeof p!=="string"){throw new TypeError("Arguments to path.join must be strings")}return p}).join("/"))};exports.relative=function(from,to){from=exports.resolve(from).substr(1);to=exports.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i=1;--i){code=path.charCodeAt(i);if(code===47){if(!matchedSlash){end=i;break}}else{matchedSlash=false}}if(end===-1)return hasRoot?"/":".";if(hasRoot&&end===1){return"/"}return path.slice(0,end)};function basename(path){if(typeof path!=="string")path=path+"";var start=0;var end=-1;var matchedSlash=true;var i;for(i=path.length-1;i>=0;--i){if(path.charCodeAt(i)===47){if(!matchedSlash){start=i+1;break}}else if(end===-1){matchedSlash=false;end=i+1}}if(end===-1)return"";return path.slice(start,end)}exports.basename=function(path,ext){var f=basename(path);if(ext&&f.substr(-1*ext.length)===ext){f=f.substr(0,f.length-ext.length)}return f};exports.extname=function(path){if(typeof path!=="string")path=path+"";var startDot=-1;var startPart=0;var end=-1;var matchedSlash=true;var preDotState=0;for(var i=path.length-1;i>=0;--i){var code=path.charCodeAt(i);if(code===47){if(!matchedSlash){startPart=i+1;break}continue}if(end===-1){matchedSlash=false;end=i+1}if(code===46){if(startDot===-1)startDot=i;else if(preDotState!==1)preDotState=1}else if(startDot!==-1){preDotState=-1}}if(startDot===-1||end===-1||preDotState===0||preDotState===1&&startDot===end-1&&startDot===startPart+1){return""}return path.slice(startDot,end)};function filter(xs,f){if(xs.filter)return xs.filter(f);var res=[];for(var i=0;i1){for(var i=1;i (http://fleegix.org)",license:"Apache-2.0",bin:{ejs:"./bin/cli.js"},main:"./lib/ejs.js",jsdelivr:"ejs.min.js",unpkg:"ejs.min.js",repository:{type:"git",url:"git://github.com/mde/ejs.git"},bugs:"https://github.com/mde/ejs/issues",homepage:"https://github.com/mde/ejs",dependencies:{jake:"^10.6.1"},devDependencies:{browserify:"^16.5.1",eslint:"^6.8.0","git-directory-deploy":"^1.5.1",jsdoc:"^3.6.4","lru-cache":"^4.0.1",mocha:"^7.1.1","uglify-js":"^3.3.16"},engines:{node:">=0.10.0"},scripts:{test:"mocha"}}},{}]},{},[1])(1)});
23 | }
24 |
25 | function getContentType( ext ) {
26 | switch ( ext ) {
27 | case '.png':
28 | return 'image/png';
29 | case '.jpg':
30 | case '.jpeg':
31 | return 'image/jpeg';
32 | case '.gif':
33 | return 'image/gif';
34 | default:
35 | return 'image/png';
36 | }
37 | }
38 |
39 | /**
40 | * @param {string} root image root path e.g. /Users/xxx/Documents/simpread@images
41 | * @param {string} folder image path e.g. aaa/bbb/ccc
42 | * @param {string} data image base64
43 | * @param {boolean} override
44 | * @param {string} name image name
45 | * @param {func} callback
46 | */
47 | function base64toImage( root, folder, data, override, name, callback ) {
48 | const folderPath = path.join( root, folder );
49 | !fs.existsSync( folderPath ) && fs.mkdirSync( folderPath, { recursive: true });
50 | const suffix = () => {
51 | const arr = data.match( /^data:image\/([a-zA-Z0-9]+);base64,/ );
52 | return arr && arr[1] ? arr[1] : 'png';
53 | },
54 | ext = '.' + suffix(),
55 | base64Data = data.replace( /^data:image\/\w+;base64,/, '' ),
56 | binaryData = new Buffer( base64Data, 'base64' ).toString( 'binary' ),
57 | file = path.join( folderPath, name + ext );
58 | let is_write = false;
59 | if ( override ) is_write = true;
60 | else if ( !override && !fs.existsSync( file )) is_write = true;
61 | if ( is_write ) {
62 | fs.writeFile( file, binaryData, 'binary', error => {
63 | error && console.log( '/assets/upload error: ', err );
64 | !error && callback( path.join( folder, name + ext ) );
65 | });
66 | } else callback( path.join( folder, name + ext ));
67 | }
68 |
69 | const DEFAULT_SETTINGS = {
70 | config : 'simpread_config.json',
71 | host : 'localhost',
72 | port : 7027,
73 | count : '10',
74 | override : true,
75 | folder : 'SimpRead',
76 | sub_folder: true,
77 | path : '',
78 | assets_root : 'root',
79 | assets : '',
80 | img_relative : true,
81 | annote : true,
82 | frequency : '-1',
83 | tag_prefix: '#',
84 | tag_suffix: ' ',
85 | format : '{"headingStyle":"atx","hr":"---","bulletListMarker":"-"}',
86 | ext_uri : '',
87 | title : '{{id}}-{{title}}{{mode}}',
88 | template : `---
89 | title: "srAnnote@{{title}}"
90 | aliases: [<% if ( unread.note && unread.title != unread.note ) { %>"srAnnote@{{note}}",<% } %>"srAnnote@{{title}}"]
91 | type: Simpread
92 | tags:
93 | {{ - |tag|\n| }}
94 | ---
95 |
96 | # {{title}}
97 | <% if ( unread.desc ) { %>
98 | > [!summary] 描述
99 | > <%- unread.desc %>
100 | <% } %>
101 | > [!md] Metadata
102 | > **标题**:: "{{title}}"
103 | > **日期**:: [[{{create|yyyy-mm-dd}}]]
104 | <% if ( unread.refs ) { -%>
105 | > **外部引用**:: {{refs}}
106 | <% } %>
107 | ## Annotations
108 | <% if ( unread.annotations.length > 0 ) { %>
109 | {{annotations}}
110 | <% } %>
111 | `,
112 | annotation: `<%
113 | let colors = [ '#B4D9FB', '#ffeb3b', '#a2e9f2', '#a1e0ff', '#a8ea68', '#ffb7da' ],
114 | color = colors[ annote.color ];
115 | -%>
116 | > [📌](<{{an_int_uri}}>) Highlight {{an_tags}}
117 | {{{html_format|>|{{an_html}}}}}
118 | <% if (annote.note) { -%>
119 | > - {{an_note}}
120 | <% } %>
121 | ^sran-{{an_id}}
122 |
123 |
124 | `
125 | }
126 |
127 | const DEFAULT_COMMANDS = [{ title: '↩️ 返回上级菜单查看全部命令', desc: '', idx: 'all' }, { title: '🗃 打开高级查询对话框', desc: '', idx: 'search' }, { title: '┅', desc: '', idx: 'divider' }];
128 |
129 | class CommandsSuggest extends obsidian.SuggestModal {
130 |
131 | constructor() {
132 | super( ...arguments );
133 | this.settings = arguments[1];
134 | this.template2 = arguments[2];
135 | this.safe = arguments[3];
136 | this.parseTitle = arguments[4];
137 | this.commands = [
138 | { title: '📕 查询24小时内的稍后读', desc: '', type: 'daily' },
139 | { title: '📗 查询今日的稍后读', desc: '', type: 'today' },
140 | { title: '📘 查询昨日的稍后读', desc: '', type: 'yestoday' },
141 | { title: '📙 查询本周的稍后读', desc: '', type: 'sunday' },
142 | { title: '📔 查询活跃列表', desc: '', type: 'unarchive' },
143 | { title: '📓 查询归档列表', desc: '', type: 'archive' },
144 | ];
145 | this.filter = arguments[6];
146 | this.unrdist = arguments[7];
147 | }
148 |
149 | getSuggestions( query ) {
150 | return this.commands.filter( command =>
151 | command.title.toLowerCase().includes( query.toLowerCase() )
152 | );
153 | }
154 |
155 | renderSuggestion( command, el ) {
156 | el.createEl( 'div', { text: command.title });
157 | el.createEl( 'small', { text: command.desc });
158 | }
159 |
160 | onChooseSuggestion( command, evt ) {
161 | let arr = this.filter( command.type , this.unrdist );
162 | arr = DEFAULT_COMMANDS.concat( arr );
163 | new UnreadSuggest( this.app, this.settings, this.template2, this.safe, this.parseTitle, arr, this.filter, this.unrdist ).open();
164 | }
165 | }
166 |
167 | class UnreadSuggest extends obsidian.SuggestModal {
168 |
169 | constructor() {
170 | super( ...arguments );
171 | this.settings = arguments[1];
172 | this.template2 = arguments[2];
173 | this.safe = arguments[3];
174 | this.parseTitle = arguments[4];
175 | this.unreads = arguments[5];
176 | this.filter = arguments[6];
177 | this.unrdist = arguments[7];
178 | }
179 |
180 | getSuggestions( query ) {
181 | return this.unreads.filter( unread =>
182 | unread.title.toLowerCase().includes( query.toLowerCase() )
183 | );
184 | }
185 |
186 | renderSuggestion( unread, el ) {
187 | el.createEl( 'div', { text: unread.title });
188 | el.createEl( 'small', { text: unread.desc });
189 | }
190 |
191 | onChooseSuggestion( unread, evt ) {
192 | if ( unread.idx == 'divider' ) return;
193 | if ( unread.idx == 'all' || unread.idx == 'search' ) {
194 | unread.idx == 'all' && new CommandsSuggest( this.app, this.settings, this.template2, this.safe, this.parseTitle, [], this.filter, this.unrdist ).open();
195 | unread.idx == 'search' && new SearchModal( this.app, this.settings, this.template2, this.safe, this.parseTitle, this.unrdist, this.filter ).open();
196 | return;
197 | }
198 | if ( !unread.annotations ) unread.annotations = [];
199 | try {
200 | this.template2( unread, this.unrdist, md => {
201 | const //path = this.app.vault.adapter.basePath + '/' + this.settings.folder,
202 | path = this.app.vault.adapter.path.join( this.app.vault.adapter.basePath, this.settings.folder ),
203 | title = this.safe( this.parseTitle( this.settings.title, unread ));
204 | //this.app.vault.adapter.fs.writeFileSync( path + '/' + title + '.md', md );
205 | this.app.vault.adapter.fs.writeFileSync( this.app.vault.adapter.path.join( path, title + '.md' ), md );
206 | setTimeout( () => {
207 | const file = this.app.vault.getFiles().find( f => f.name === title + '.md' );
208 | file && this.app.workspace.getMostRecentLeaf().openFile( file );
209 | }, 100 );
210 | });
211 | } catch ( error ) {
212 | console.error( 'current unread write error: ', error, unread )
213 | }
214 | }
215 | }
216 |
217 | class SearchModal extends obsidian.Modal {
218 |
219 | constructor() {
220 | super( ...arguments );
221 | this.arguments = arguments;
222 | this.settings = arguments[1];
223 | this.template2 = arguments[2];
224 | this.safe = arguments[3];
225 | this.parseTitle = arguments[4];
226 | this.unrdist = arguments[5];
227 | this.filter = arguments[6];
228 | }
229 |
230 | SearchKeywordsDesc() {
231 | const descEl = document.createDocumentFragment();
232 | descEl.appendText( 'Support placeholders:' );
233 | descEl.appendChild( document.createElement( 'br' ));
234 | descEl.appendText( '{{title}} {{desc}} {{note}} {{tags}} {{annote}} ...' );
235 | descEl.appendChild( document.createElement( 'br' ));
236 | descEl.appendText( 'For more syntax, refer to ' );
237 | const a = document.createElement( 'a' );
238 | a.href = 'https://github.com/Kenshin/simpread/discussions/2889#discussioncomment-1420517';
239 | a.text = 'format reference';
240 | a.target = '_blank';
241 | descEl.appendChild( a );
242 | descEl.appendText( '.' );
243 | return descEl;
244 | }
245 |
246 | onOpen() {
247 | const { contentEl } = this;
248 |
249 | contentEl.createEl( 'h2', { text: 'Advanced search' });
250 |
251 | new obsidian.Setting( contentEl )
252 | .setName( 'Search Keywords' )
253 | .setDesc( this.SearchKeywordsDesc() )
254 | .addText( text =>
255 | text.onChange( value => {
256 | this.result = value;
257 | }));
258 |
259 | new obsidian.Setting( contentEl )
260 | .addButton( btn =>
261 | btn
262 | .setButtonText( 'Search' )
263 | .setCta()
264 | .onClick(() => {
265 | let arr = this.filter( this.result.includes( ':' ) ? this.result : 'any', this.unrdist, this.result );
266 | arr = DEFAULT_COMMANDS.concat( arr );
267 | new UnreadSuggest( this.app, this.settings, this.template2, this.safe, this.parseTitle, arr, this.filter, this.unrdist ).open();
268 | this.close();
269 | }));
270 | }
271 |
272 | onClose() {
273 | const { contentEl } = this;
274 | contentEl.empty();
275 | }
276 | }
277 |
278 | class SimpReadPlugin extends obsidian.Plugin {
279 |
280 | constructor() {
281 | super( ...arguments );
282 | this.scheduleInterval = null;
283 | this.srv = null;
284 | this.registerCommands();
285 | engine();
286 | setTimeout( () => this.server(), 400 )
287 | }
288 |
289 | registerCommands() {
290 | this.addCommand({
291 | id: "sr-command-daily",
292 | name: "查询24小时内的稍后读",
293 | callback: () => {
294 | this.search( 'daily' );
295 | }
296 | });
297 | this.addCommand({
298 | id: "sr-command-today",
299 | name: "查询今日的稍后读",
300 | callback: () => {
301 | this.search( 'today' );
302 | }
303 | });
304 | this.addCommand({
305 | id: "sr-command-yestoday",
306 | name: "查询昨日的稍后读",
307 | callback: () => {
308 | this.search( 'yestoday' );
309 | }
310 | });
311 | this.addCommand({
312 | id: "sr-command-week",
313 | name: "查询本周的稍后读",
314 | callback: () => {
315 | this.search( 'week' );
316 | }
317 | });
318 | this.addCommand({
319 | id: "sr-command-unarchive",
320 | name: "查询活跃列表",
321 | callback: () => {
322 | this.search( 'unarchive' );
323 | }
324 | });
325 | this.addCommand({
326 | id: "sr-command-archive",
327 | name: "查询归档列表",
328 | callback: () => {
329 | this.search( 'archive' );
330 | }
331 | });
332 | this.addCommand({
333 | id: "sr-command-search",
334 | name: "高级查询",
335 | callback: () => {
336 | this.search( 'search' );
337 | }
338 | });
339 | this.addCommand({
340 | id: "sr-command-panel",
341 | name: "打开当前文档的本地快照",
342 | callback: () => {
343 | this.preview();
344 | }
345 | });
346 | }
347 |
348 | search( type ) {
349 | this.config( () => {
350 | if ( type == 'search' ) {
351 | new SearchModal( this.app, this.settings, this.template2, this.safe, this.parseTitle, this.unrdist, this.filter ).open();
352 | } else {
353 | let arr = this.filter( type, this.unrdist );
354 | arr = DEFAULT_COMMANDS.concat( arr );
355 | new UnreadSuggest( this.app, this.settings, this.template2, this.safe, this.parseTitle, arr, this.filter, this.unrdist ).open();
356 | }
357 | });
358 | }
359 |
360 | // from filter.js
361 | filter( type, arr, str ) {
362 | if("today"==type)arr=arr.filter(e=>{const t=new Date,a=new Date(e.create.replace(/年|月/gi,"-").replace("日",""));return t.getFullYear()==a.getFullYear()&&t.getMonth()==a.getMonth()&&t.getDate()==a.getDate()});else if("yestoday"==type)arr=arr.filter(e=>{const t=new Date,a=t.getFullYear(),r=t.getMonth()+1,n=t.getDate(),s=+new Date(a+`-${r}-${n} 00:00:00`),l=+new Date(a+`-${r}-${n} 23:59:59`)-864e5,o=+new Date(e.create.replace(/年|月/gi,"-").replace("日",""));return l<864e5+o&&o{const t=new Date,a=t.getFullYear(),r=t.getMonth()+1,n=t.getDate(),s=+new Date(a+`-${r}-${n} 23:59:59`),l=+new Date(e.create.replace(/年|月/gi,"-").replace("日",""));return s<6048e5+l});else if("sunday"==type){const u=new Date,v=u.getTime(),w=u.getDay()||7,x=864e5,y=(e,t)=>{var a=(e=new Date(e)).getFullYear(),r=e.getMonth()+1,e=e.getDate();return+new Date(a+`-${r}-${e} `+t)},z=y(v-(w-1)*x,"00:00:00"),A=y(v+(7-w)*x,"23:59:59");arr=arr.filter(e=>{e=+new Date(e.create.replace(/年|月/gi,"-").replace("日",""));return e>=z&&e<=A})}else if("daily"==type)arr=arr.filter(e=>{return+new Date-+new Date(e.create.replace(/年|月/gi,"-").replace("日",""))<=864e5});else if("nohighlight"==type)arr=arr.filter(e=>!e.annotations||0==e.annotations.length);else if("notags"==type)arr=arr.filter(e=>!e.tags||0==e.tags.length);else if("unarchive"==type)arr=arr.filter(e=>!e.archive);else if("archive"==type)arr=arr.filter(e=>e.archive);else if("share"==type)arr=arr.filter(e=>e.share);else if("annoate"==type){let r=[];arr.forEach(e=>{{var a=e.annotations;let t=!1;return a&&a.forEach(e=>{null==e.note&&(e.note=""),""==e.note&&(t=!0)}),void(t&&r.push(e))}}),arr=r}else if("img"==type||"code"==type){let r=[];arr.forEach(e=>{{var a=e.annotations;let t=!1;return a&&a.forEach(e=>e.type==type&&(t=!0)),void(t&&r.push(e))}}),arr=r}else if("note"==type){let r=[];arr.forEach(e=>{e.note&&0e.note&&0{if(e.desc||(e.desc=""),e.note||(e.note=""),e.tags||(e.tags=[]),e.annotations||(e.annotations=[]),e.title.includes(str)||e.desc.includes(str)||e.note.includes(str)||e.tags.includes(str))r.push(e);else{var a=e.annotations;let t=!1;a&&a.forEach(e=>{e.note||(e.note=""),e.tags||(e.tags=[]),(e.note.includes(str)||e.text.includes(str)||e.tags.includes(str))&&(t=!0)}),t&&r.push(e)}}),arr=r}else if(type.includes(":")){let r=[];str=type.split(":")[1],type=type.split(":")[0],arr.forEach(e=>{if(e.desc||(e.desc=""),e.note||(e.note=""),e.tags||(e.tags=[]),e.annotations||(e.annotations=[]),["title","desc","note","tags","tag"].includes(type)&&("tag"==type&&(type="tags"),e[type].includes(str)&&r.push(e)),["note","tags","tag","annote","text"].includes(type)){var a=e.annotations;let t=!1;a&&a.forEach(e=>{"tag"==(type="annote"==type?"text":type)&&(type="tags"),e.note||(e.note=""),e.tags||(e.tags=[]),e[type].includes(str)&&(t=!0)}),t&&r.push(e)}}),arr=r}
363 | return arr;
364 | }
365 |
366 | server() {
367 | const requestListener = ( req, res ) => {
368 | res.setHeader( 'Access-Control-Allow-Origin', '*' );
369 | res.setHeader( 'Access-Control-Allow-Methods', '*' );
370 | if ( req.method === 'GET' && req.url == '/' ) {
371 | res.writeHead( 200 );
372 | res.end( 'SimpRead Sync server run' );
373 | } else if ( req.method === 'GET' && req.url.startsWith( '/assets' )) {
374 | const uri = req.url.replace( '/assets', '' ),
375 | root = this.settings.assets_root == 'root'
376 | ? path.join( this.app.vault.adapter.basePath, this.settings.folder )
377 | : this.settings.assets,
378 | filePath = path.join( root, decodeURI( uri ));
379 | fs.access( filePath, fs.constants.F_OK, err => {
380 | if ( err ) {
381 | res.statusCode = 404;
382 | res.end( 'File not found' );
383 | } else fs.readFile( filePath, ( err, data ) => {
384 | if ( err ) {
385 | res.statusCode = 500;
386 | res.end( 'Internal Server Error' );
387 | } else {
388 | const ext = path.extname( filePath ),
389 | contentType = getContentType( ext );
390 | res.setHeader( 'Content-Type', contentType );
391 | res.statusCode = 200;
392 | res.end( data );
393 | }
394 | });
395 | });
396 | } else if ( req.method === 'POST' && req.url == '/assets/upload' ) {
397 | let body = '';
398 | req.on( 'data', chunk => {
399 | body += chunk;
400 | });
401 | req.on( 'end', () => {
402 | try {
403 | body = JSON.parse( body );
404 | const { title, base64, override, name } = body;
405 | let root = this.settings.assets_root == 'root'
406 | ? path.join( this.app.vault.adapter.basePath, this.settings.folder )
407 | : this.settings.assets;
408 | let folder = this.settings.assets_root == 'root' ? path.join( title, 'assets' ) : body.folder || '';
409 | base64toImage( root, folder, base64 || '', override, name || + new Date(), file => {
410 | res.setHeader( 'Content-Type', 'application/json' );
411 | res.writeHead( 200 );
412 | res.end( JSON.stringify( file ));
413 | });
414 | } catch ( error ) {
415 | console.error( error )
416 | res.setHeader( 'Content-Type', 'application/json' );
417 | res.writeHead( 403 );
418 | res.end( `{ "code": 403, "message": "${ error.message }" }` );
419 | }
420 | });
421 | } else if ( req.method === 'POST' && req.url == '/assets/upload/indexes' ) {
422 | let body = '';
423 | req.on( 'data', chunk => {
424 | body += chunk;
425 | });
426 | req.on( 'end', () => {
427 | try {
428 | body = JSON.parse( body );
429 | const { title, folder, indexes } = body;
430 | this.indexes( 'write', title, folder, indexes, error => {
431 | if ( !error ) {
432 | res.setHeader( 'Content-Type', 'application/json' );
433 | res.writeHead( 200 );
434 | res.end(JSON.stringify({ code: 200 }));
435 | } else console.log( '/assets/upload/indexes error: ', error );
436 | });
437 | } catch ( error ) {
438 | console.error( error )
439 | res.setHeader( 'Content-Type', 'application/json' );
440 | res.writeHead( 403 );
441 | res.end( `{ "code": 403, "message": "${ error.message }" }` );
442 | }
443 | });
444 | } else if ( req.method === 'POST' ) {
445 | let body = '';
446 | req.on( 'data', chunk => {
447 | body += chunk;
448 | });
449 | req.on( 'end', () => {
450 | try {
451 | body = JSON.parse( body );
452 | console.log( 'simpread post data is', body )
453 | if ( req.url == '/unread' ) {
454 | const unread = body.unread;
455 | if ( !unread.annotations ) unread.annotations = [];
456 | // fixed image url
457 | this.fixedImgURL( unread );
458 | this.template2( unread, this.unrdist, md => {
459 | /*
460 | const path = this.app.vault.adapter.basePath + '/' + this.settings.folder,
461 | title = this.safe( this.parseTitle( this.settings.title, unread ));
462 | */
463 | const title = this.safe( this.parseTitle( this.settings.title, unread )),
464 | folder= this.settings.sub_folder ? title.replace( /@annote|@all/i, '' ) : '',
465 | path = this.app.vault.adapter.path.join( this.app.vault.adapter.basePath, this.settings.folder, folder ),
466 | file = this.app.vault.adapter.path.resolve( path, this.safe( title ) + '.md' );
467 | //md = this.relative( title, md );
468 | //this.app.vault.adapter.fs.writeFileSync( this.app.vault.adapter.path.join( path, this.safe( title ) + '.md' ), md );
469 | this.replaceImage( title, md, str => {
470 | md = str;
471 | md = this.relative( title, md, 'unread' );
472 | this.read( body.type, title, ( file, md ) => this.update( file, md, { md } ) );
473 | this.app.vault.adapter.fs.writeFileSync( this.app.vault.adapter.path.join( path, this.safe( title ) + '.md' ), md );
474 | });
475 | });
476 | } else {
477 | /*
478 | if ( this.settings.img_relative ) {
479 | if ( this.settings.assets_root == 'root' ) {
480 | let title = body.title.replace( /@annote|@all/i, '' );
481 | title = body.type == 'unread' ? title : encodeURI( title );
482 | body.md = body.md.replace( new RegExp( 'http://localhost:7027/assets/' + title + '/', 'ig' ), '' );
483 | } else {
484 | body.md = body.md.replace( new RegExp( 'http://localhost:7027/assets', 'ig' ), 'file:///' + this.settings.assets );
485 | }
486 | console.log( 'adadadf', body.md )
487 | }
488 | */
489 | //body.md = this.relative( body.title, body.md, body.type );
490 | //this.read( body.title, ( file, md ) => this.update( file, md, body ) );
491 | this.replaceImage( body.title, body.md, md => {
492 | body.md = md;
493 | body.md = this.relative( body.title, body.md, 'unread' );
494 | this.read( body.type, body.title, ( file, md ) => this.update( file, md, body ) );
495 | });
496 | }
497 | res.setHeader( 'Content-Type', 'application/json' );
498 | res.writeHead( 200 );
499 | res.end( `{ "code": 200, "message": "simpread data post success" }` );
500 | } catch ( error ) {
501 | console.error( error )
502 | res.setHeader( 'Content-Type', 'application/json' );
503 | res.writeHead( 403 );
504 | res.end( `{ "code": 403, "message": "${ error.message }" }` );
505 | }
506 | });
507 | }
508 | };
509 | this.srv = http.createServer( requestListener );
510 | this.srv.listen( this.settings.port, this.settings.host, () => {
511 | console.log( `Simpread Sync server is running on http://${this.settings.host}:${this.settings.port}` );
512 | });
513 | }
514 |
515 | onload() {
516 | return __awaiter( this, void 0, void 0, function* () {
517 | yield this.loadSettings();
518 | this.addSettingTab( new SimpReadSettingTab( this.app, this ));
519 | yield this.schedule();
520 | yield this.config();
521 | yield this.getManifest();
522 | yield this.openUnradbylink();
523 | });
524 | }
525 |
526 | onunload() {
527 | this.srv.close();
528 | return;
529 | }
530 |
531 | loadSettings() {
532 | return __awaiter( this, void 0, void 0, function* () {
533 | this.settings = Object.assign( {}, DEFAULT_SETTINGS, yield this.loadData() );
534 | });
535 | }
536 |
537 | saveSettings() {
538 | return __awaiter( this, void 0, void 0, function* () {
539 | //yield this.saveData( this.settings );
540 | this.saveData2( this.settings );
541 | });
542 | }
543 |
544 | saveData2( data ) {
545 | let path = `${ this.app.vault.configDir }/plugins/simpread/data.json`;
546 | path = this.app.vault.adapter.path.resolve( this.app.vault.adapter.getBasePath(), path );
547 | this.app.vault.adapter.fs.writeFileSync( path, JSON.stringify( data ));
548 | }
549 |
550 | getManifest() {
551 | let path = `${ this.app.vault.configDir }/plugins/simpread/manifest.json`;
552 | path = this.app.vault.adapter.path.resolve( this.app.vault.adapter.getBasePath(), path );
553 | this.app.vault.adapter.fs.readFile( path, 'utf8', ( err, result ) => {
554 | if ( !err ) {
555 | this.manifest = JSON.parse( result );
556 | }
557 | })
558 | }
559 |
560 | config( callback ) {
561 | this.unrdist = [];
562 | const path = this.app.vault.adapter.path.resolve( this.settings.path, DEFAULT_SETTINGS.config );
563 | this.app.vault.adapter.fs.readFile( path, 'utf8', ( err, result ) => {
564 | if ( !err ) {
565 | const config = JSON.parse( result ),
566 | unrdist = config.unrdist;
567 | if ( unrdist && unrdist.length > 0 ) {
568 | this.unrdist = [ ...unrdist ];
569 | callback && callback();
570 | }
571 | }
572 | });
573 | }
574 |
575 | sync() {
576 | if ( this.settings.path.trim() == '/' ) {
577 | this.notice( 'SimpRead config path not empty', true, 4, true );
578 | return;
579 | }
580 | const path = this.app.vault.adapter.path.resolve( this.settings.path, DEFAULT_SETTINGS.config );
581 | this.app.vault.adapter.fs.readFile( path, 'utf8', ( err, result ) => {
582 | if ( !err ) {
583 | const config = JSON.parse( result ),
584 | unrdist = config.unrdist;
585 | if ( unrdist && unrdist.length > 0 ) this.write( unrdist );
586 | else this.notice( 'Not found unread list', true, 4, true );
587 | } else this.notice( 'Not found simpead_config.json', true, 4, true );
588 | });
589 | }
590 |
591 | safe( title ) {
592 | return title.replace( /:/ig, ':' )
593 | .replace( /\?/ig, '?' )
594 | .replace( /\/|\*|\||<|>|\.|,|=/ig, '_' )
595 | .replace( /\\/ig, '-' )
596 | .replace( /%/ig, '%' )
597 | .replace( /;/ig, ';' )
598 | .replace( /"/ig, '' )
599 | .replace( /\[/ig, '【' )
600 | .replace( /\]/ig, '】' )
601 | .replace( /#/ig, '' )
602 | .trim();
603 | }
604 |
605 | parseTitle( title, unread , mode = '@annote' ) {
606 | return title = title && unread
607 | ? title
608 | .replace( /{{id}}/ig, unread.idx )
609 | .replace( /{{title}}/ig, unread.title )
610 | .replace( /{{un_title}}/ig, unread.title )
611 | .replace( /{{timestamp}}/ig, unread.create.replace( /年|月|日|:| /ig, '' ) )
612 | .replace( /{{note}}/ig, unread.note || unread.title )
613 | .replace( /{{mode}}/ig, mode )
614 | : unread ? unread.title : title;
615 | }
616 |
617 | update( file, md, body ) {
618 | /*
619 | const parseTags = tags => {
620 | let html = '';
621 | this.settings.tag_suffix == '\\n' && ( this.settings.tag_suffix = '\n' );
622 | tags && tags.forEach( tag => html += this.settings.tag_prefix + `${ tag.replace( / /ig, '_' ) }` + this.settings.tag_suffix );
623 | return html.trim();
624 | }
625 | let diff = body.diff[0],
626 | id = diff.id,
627 | props = diff.props,
628 | start = `%sran_id=${ id }_start%`,
629 | end = `%sran_id=${ id }_end%`,
630 | startId = md.indexOf( start ),
631 | endId = md.indexOf( end ),
632 | str = md.substring( startId, endId ),
633 | new_str = str;
634 | if ( props == 'note' ) {
635 | new_str = str.replace( diff.old, diff.value );
636 | } else if ( props == 'tags' ) {
637 | let old = parseTags( diff.old ),
638 | value = parseTags( diff.value );
639 | md = md.replace( old, value );
640 | } else if ( props == 'color' ) {
641 | let colors = [ '#B4D9FB', '#ffeb3b', '#a2e9f2', '#a1e0ff', '#a8ea68', '#ffb7da' ],
642 | old = colors[ diff.old ],
643 | value = colors[ diff.value ];
644 | console.log( 'asffasdf', old, value )
645 | md = md.replace( old, value );
646 | }
647 | md = md.replace( str, new_str );
648 | console.log( md )
649 | */
650 | this.app.vault.adapter.fs.writeFileSync( file, body.md );
651 | }
652 |
653 | read( type, title, callback ) {
654 | const folder= type && type.startsWith( 'collection' ) ? type.replace( 'collection:', '' ) : this.settings.sub_folder ? '/' + title.replace( /@annote|@all/i, '' ) : '',
655 | path = this.app.vault.adapter.path.join( this.app.vault.adapter.basePath, this.settings.folder, folder ),
656 | file = this.app.vault.adapter.path.resolve( path, this.safe( title ) + '.md' );
657 | if ( this.settings.sub_folder ) {
658 | const exists = this.app.vault.adapter.fs.existsSync( path );
659 | if ( !exists ) {
660 | this.app.vault.adapter.fs.mkdirSync( path, { recursive: true });
661 | }
662 | }
663 | this.app.vault.adapter.fs.readFile( file, 'utf8', ( err, result ) => {
664 | if ( !err ) {
665 | callback( file, result );
666 | } else {
667 | callback( file, undefined );
668 | }
669 | });
670 | }
671 |
672 | write( unrdist ) {
673 | const //path = this.app.vault.adapter.basePath + '/' + this.settings.folder,
674 | path = this.app.vault.adapter.path.join( this.app.vault.adapter.basePath, this.settings.folder ),
675 | exists = this.app.vault.adapter.fs.existsSync( path );
676 | if ( !exists ) {
677 | this.app.vault.adapter.fs.mkdirSync( path );
678 | }
679 | const files = /{{id}}|{{timestamp}}/.test( this.settings.title ) ? this.app.vault.adapter.fs.readdirSync( path , { encoding:'utf8' }) : [];
680 | this.unrdist = [ ...unrdist ];
681 | if ( this.settings.count == 0 ) {
682 | unrdist = unrdist.reverse();
683 | } else {
684 | unrdist = unrdist.splice( 0, parseInt( this.settings.count ));
685 | unrdist = unrdist.reverse();
686 | }
687 | for ( let i = 0; i < unrdist.length; i++ ) {
688 | if ( i >= unrdist.length - 1 ) {
689 | this.notify && this.notice( 'Write all files complete.', true, 4, true);
690 | this.notify = false;
691 | }
692 | const unread = unrdist[i];
693 | if ( unread ) {
694 | if ( this.settings.annote && (( unread.annotations && unread.annotations.length == 0 ) || !unread.annotations )) {
695 | continue;
696 | }
697 | const file = this.app.vault.adapter.path.resolve( path, this.safe( unread.title ) + '.md' );
698 | const existfiles = () => {
699 | const idx = unread.idx,
700 | timestamp = unread.create.replace( /年|月|日|:| /ig, '' );
701 | let existfile = [];
702 | /{{id}}/.test( this.settings.title ) && ( existfile = files.filter( item => {
703 | const arr = item.match( /^\d+/ );
704 | if ( arr && arr.length > 0 ) {
705 | return arr[0] == idx;
706 | }
707 | }));
708 | /{{timestamp}}/.test( this.settings.title ) && ( existfile = files.filter( item => {
709 | const arr = item.match( /^\d+/ );
710 | if ( arr && arr.length > 0 ) {
711 | return arr[0] == timestamp;
712 | }
713 | }));
714 | return existfile;
715 | },
716 | somefiles = existfiles();
717 | if ( !this.settings.override ) {
718 | if ( files.length > 0 && somefiles.length > 0 ) {
719 | continue;
720 | } else if ( this.app.vault.adapter.fs.existsSync( file )) {
721 | continue;
722 | }
723 | }
724 | if ( !unread.annotations ) unread.annotations = [];
725 | try {
726 | this.template2( unread, this.unrdist, md => {
727 | const title = this.safe( this.parseTitle( this.settings.title, unread )),
728 | folder= this.settings.sub_folder ? title.replace( /@annote|@all/i, '' ) : '',
729 | path = this.app.vault.adapter.path.join( this.app.vault.adapter.basePath, this.settings.folder, folder ),
730 | file = this.app.vault.adapter.path.resolve( path, this.safe( title ) + '.md' );
731 | // when override remove exist file
732 | somefiles.forEach( remove => {
733 | this.app.vault.adapter.fs.existsSync( file ) &&
734 | this.app.vault.adapter.fs.unlinkSync( file );
735 | });
736 | !this.app.vault.adapter.fs.existsSync( path ) && fs.mkdirSync( path, { recursive: true });
737 | //this.app.vault.adapter.fs.writeFileSync( file, this.relative( title, md ));
738 | this.replaceImage( title, md, str => {
739 | md = str;
740 | this.app.vault.adapter.fs.writeFileSync( file, this.relative( title, md ));
741 | });
742 | });
743 | } catch ( error ) {
744 | console.error( 'current unread write error: ', error, unread )
745 | }
746 | }
747 | }
748 | }
749 |
750 | // from template2.js
751 | template2( unread, unrdist, global_callback ) {
752 | const plugin_storage=this.settings;function markdown(e,t,a,r=0,n=""){try{n=JSON.parse(n||"{}")}catch(e){n={}}const l=(new TurndownService)(n);l.escape=e=>e,l.addRule("pre",{filter:["pre"],replacement:e=>"\n\n```\n"+e+"\n```\n\n"}),l.addRule("table",{filter:["table"],replacement:(e,t,a)=>{try{if("md"!=a.table)return t.outerHTML;{const l=e.trim().split("\n"),i=[];for(let e=0;e``+"\n\n"}),l.addRule("math",{filter:["math"],replacement:(e,t,a)=>""});n=l.turndown(e);a&&a(n)}function AnnoteMDTemplate2(o,p,n,t,a,m,e){const c=()=>{d=/d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,h=/\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,y=/[^-+\dA-Z]/g;var d,h,y,f=function(e,t,a){var r=f;if(1!=arguments.length||"[object String]"!=Object.prototype.toString.call(e)||/\d/.test(e)||(t=e,e=void 0),e=e?new Date(e):new Date,isNaN(e))throw SyntaxError("invalid date");"UTC:"==(t=String(r.masks[t]||t||r.masks.default)).slice(0,4)&&(t=t.slice(4),a=!0);var n=a?"getUTC":"get",l=e[n+"Date"](),i=e[n+"Day"](),o=e[n+"Month"](),c=e[n+"FullYear"](),s=e[n+"Hours"](),g=e[n+"Minutes"](),p=e[n+"Seconds"](),n=e[n+"Milliseconds"](),m=a?0:e.getTimezoneOffset(),u={d:l,dd:_(l),ddd:r.i18n.dayNames[i],dddd:r.lang.dayNames.long[i],m:o+1,mm:_(o+1),mmm:r.i18n.monthNames[o],mmmm:r.lang.monthNames.long[o],yy:String(c).slice(2),yyyy:c,h:s%12||12,hh:_(s%12||12),H:s,HH:_(s),M:g,MM:_(g),s:p,ss:_(p),l:_(n,3),L:_(99{var a={M:t.getMonth()+1,d:t.getDate(),h:t.getHours(),m:t.getMinutes(),s:t.getSeconds()};return(e=e.replace(/(M+|d+|h+|m+|s+)/g,function(e){return((1{const t=o.match(/{{date_format\|[\S ]+\|[\S ]+}} /);let a="";try{if(t&&0title=plugin_storage.title&&e?plugin_storage.title.replace(/{{id}}/gi,e.idx).replace(/{{title}}/gi,e.title).replace(/{{un_title}}/gi,e.title).replace(/{{timestamp}}/gi,e.create.replace(/年|月|日|:| /gi,"")).replace(/{{now\|[\w-\/ :]+}}/gi,_).replace(/{{note}}/gi,e.note||e.title):e?e.title:"<解析失败>",l=e=>new URL(e),d=e=>{let t="";return"\\n"==plugin_storage.tag_suffix&&(plugin_storage.tag_suffix="\n"),e&&e.forEach(e=>t+=plugin_storage.tag_prefix+(""+e.replace(/ /gi,"_"))+plugin_storage.tag_suffix),t.trim()},i=(e,t)=>{let a="";const r=(e=e.replace(/\{|\}/gi,"")).split("|"),n=r[0],l=r[2];return t&&t.forEach(e=>a+=n+e+("\\n"==l?"\n":l)),a=4==r.length?a.replace(new RegExp(r[3]+"$"),""):a},h=e=>{let t="";return e&&e.split("\n").forEach(e=>{e.startsWith("<")?(e=e.replace(/^<|>$/g,""),t+=`[${e}](<${e}>)`+"\n"):t+=e+"\n"}),t.trim()},y=(e,a)=>{const r=new RegExp(`{{[ \\S]+\\|${e}}}`);if(r.test(o)){let t="";const n=o.match(r)[0].replace(/{|{|}|}|}|\|/gi,"").replace(e,"");return a&&a.split("\n").forEach(e=>t+=n+" "+e+"\n"),t.trim()}return a},f=(t,a,r,e)=>{if("org"==t)return n.url+"#:~:text="+encodeURIComponent(r);if("int"==t)return"http://localhost:7026/reading/"+n.idx+(a?"#id="+a:"");if("uri"==t)return"http://localhost:7026/unread/"+n.idx+(a?"#id="+a:"");if("ext"==t){let e=plugin_storage.ext_uri;return e.startsWith("https://simpread.pro/@")?e?e+n.idx+(a?"#id="+a:""):"{{ext_uri}}":(e=(r=plugin_storage.ext_uri,t=n,r&&t?r.replace(/{{id}}/gi,t.idx).replace(/{{title}}/gi,t.title).replace(/{{un_title}}/gi,t.title).replace(/{{timestamp}}/gi,t.create.replace(/年|月|日|:| /gi,"")).replace(/{{now\|[\w-\/ :]+}}/gi,_).replace(/{{note}}/gi,t.note||t.title):r))+(a?"#id="+a:"")}},g=(e,t)=>{const a=c(),[r,n,l]=e.replace(/{|}/gi,"").split("|");a.setLocal(l||"short");let i="";return i="create"==r?t.replace(/年|月/gi,"-").replace(/日/gi,""):new Date(t),a(i,n)},_=e=>{e=e.replace(/(\{\{now\|)|(\}\})/gi,"");return c()(+new Date,e)},S=e=>{return c()(e,"yyyyddmmHHMMss")},k=e=>{let a="";return e&&e.forEach(e=>{var t;"unread"==e.type?(t=((t,a)=>{for(let e=0;e{for(let e=0;ee.id==t);if(-1{const e=o.match(/{{#each}}[\S\n ]+{{\/each}}/gi);if(e&&0{const{type:t,text:a,html:r,note:n,tags:l,id:i,refs:o}=e;let c="";"img"==t?c=``:"code"==t?c="```\n"+a.trim()+"\n```":"paragraph"==t&&(c=m(r,void 0,void 0,!0,p.format)),s+=g.replace(/{{an_create}}/gi,(e=>{const t=new Date(e),a=e=>e=e<10?"0"+e:e;return t.getFullYear()+"年"+a(t.getMonth()+1)+"月"+a(t.getDate())+"日 "+a(t.getHours())+":"+a(t.getMinutes())+":"+a(t.getSeconds())})(i)).replace(/{{an_html}}/gi,c).replace(/{{an_timestamp}}/gi,S(i)).replace(/{{an_id}}/gi,i).replace(/{{an_text}}/gi,a).replace(/{{an_short_text}}/gi,a.substr(0,20)+(10g(e,v)).replace(/{{cover}}/gi,n.img||"").replace(/{{idx}}/gi,n.idx).replace(/{{url}}/gi,n.url).replace(/{{title}}/gi,n.title).replace(/{{un_title}}/gi,n.idx+"-"+n.title).replace(/{{create}}/gi,n.create).replace(/{{timestamp}}/gi,n.create.replace(/年|月|日|:| /gi,"")).replace(/{{host}}/gi,l(n.url).host).replace(/{{desc}}/gi,n.desc).replace(/{{note}}/gi,n.note).replace(/{{local}}/gi,"http://localhost:7026/reading/"+n.idx).replace(/{{unread}}/gi,"http://localhost:7026/unread/"+n.idx).replace(/{{now\|[ \S]+}}/gi,e=>g(e,+new Date)).replace(/{{an_type}}/gi,M).replace(/{{an_timestamp}}/gi,S(v)).replace(/{{an_html}}/gi,e).replace(/{{an_id}}/gi,v).replace(/{{an_text}}/gi,x).replace(/{{an_short_text}}/gi,x.substr(0,20)+(10i(e,N)).replace(/{{3}html\_format\|[^|]+\|!\[\S?\]\([a-zA-z]+:\/\/[^\s]*\}\}\}/gi,e=>{const t=e.split("|"),a=t[1],r=t[t.length-1].replace(/}{3}$/,"");return/<\w+>/.test(a)||a.startsWith(">")&&1g(e,n.create)).replace(/{{now\|[ \S]+}}/gi,e=>g(e,+new Date)).replace(/{{date_format\|[\S ]+\|[\S ]+}}/,u()).replace(/{{cover}}/gi,n.img||"").replace(/{{idx}}/gi,n.idx).replace(/{{url}}/gi,n.url).replace(/{{title}}/gi,n.title).replace(/{{un_title}}/gi,n.idx+"-"+n.title).replace(/{{create}}/gi,n.create).replace(/{{timestamp}}/gi,n.create.replace(/年|月|日|:| /gi,"")).replace(/{{desc}}/gi,n.desc).replace(/{{note}}/gi,n.note).replace(/{{local}}/gi,"http://localhost:7026/reading/"+n.idx).replace(/{{unread}}/gi,"http://localhost:7026/unread/"+n.idx).replace(/{{backlinks}}/gi,k(n.backlinks)).replace(/{{host}}/gi,l(n.url).host).replace(/{{tags}}/gi,d(n.tags)).replace(/{{int_uri}}/gi,f("int")).replace(/{{ext_uri}}/gi,f("ext")).replace(/{{org_uri}}/gi,f("org")).replace(/{{refs}}/gi,y("refs",h(n.refs))).replace(/{{[ \S]+\|refs}}/gi,y("refs",h(n.refs))).replace(/{{[ \S]+\|desc}}/gi,y("desc",n.desc)).replace(/{{[ \S]+\|note}}/gi,y("note",n.note)).replace(/{{[ \S]+\|backlinks}}/gi,y("backlinks",k(n.backlinks))).replace(/{{color1}}/gi,getComputedStyle(document.documentElement).getPropertyValue("--sr-annote-color-1").trim()).replace(/{{color2}}/gi,getComputedStyle(document.documentElement).getPropertyValue("--sr-annote-color-2").trim()).replace(/{{color3}}/gi,getComputedStyle(document.documentElement).getPropertyValue("--sr-annote-color-3").trim()).replace(/{{color4}}/gi,getComputedStyle(document.documentElement).getPropertyValue("--sr-annote-color-4").trim()).replace(/{{color5}}/gi,getComputedStyle(document.documentElement).getPropertyValue("--sr-annote-color-5").trim()).replace(/{{[^{]+\|tag\|[^}]+}}/gi,e=>i(e,n.tags))).replace(/{{#each}}[\S\n ]+{{\/each}}/gi,b(n.annotations));return o}e({parseURLScheme:l,fmtDate:c,parseBakinks:k})}function mdtemplate(l){let e=plugin_storage.template,i=plugin_storage.annotation;const o=(e,t,a)=>{let r=plugin_storage.format;try{r=r?{bulletListMarker:"-",...r=JSON.parse(r)}:JSON.stringify({bulletListMarker:"-"})}catch(e){r=JSON.stringify({bulletListMarker:"-"}),console.error("format_html option error",e)}if(/^```/i.test(e)&&/```$/i.test(e)&&(e=e.replace(//gi,">")),t)if(">"==t)e=`${e}
`;else if(t.startsWith(">")&&1".repeat(t.length)+e+"".repeat(t.length);else if(/{\d+}/i.test(t)){const n=t.replace(/{\d+}/gi,""),l=t.replace(/{|}\S+/gi,"");e=n.repeat(l)+e}else"-"==t||"*"==t?e=`${e}`:/<\w+>/.test(t)&&(e=`<${t=t.replace(/<|>/gi,"")}>${e}<${t}>`);else e=`${e}
`;markdown(e=e.replace(/\n/gi,"
"),void 0,e=>{t.startsWith("!")&&(t=t.replace("!",""),e=e.split("\n").map(e=>t+e).join("\n")),e=e.replace(/!\\\[\\\]/i,"![]"),a(e)},!1,JSON.stringify(r))};let c=ejs.render(e,{unread:l});const a=(c=AnnoteMDTemplate2(c,plugin_storage,l,void 0,l.annotations,markdown)).match(/{{3}html\_format\|[^|]+\|[^{{{]+}{3}|{{3}html\_format\|[^|]+\|[^]+}{3}/gi)||[],r=a.length,n=[];let s=0;const g=e=>{var t=e.split("|")[1],e=e.replace("{{{html_format|"+t+"|","").replace(/}}}$/,"");o(e,t,e=>{n.push(e),++s{c=c.replace(e,n[t])}),(/{{annotations}}/.test(c)?p:m)())})},p=()=>{let t="",a=0;const r=l.annotations.length,n=e=>{t+=e,++a{console.log("unread template is ",c),global_callback?global_callback(c):console.log("unread template is ",c)};try{0{var t=e.split("|")[1],e=e.replace("{{{html_format|"+t+"|","").replace(/}}}$/,"");r(e,t,e=>{c.push(e),++s{l=l.replace(e,c[t])}),n(l))})};0/gi,">")),r)if(">"==r)a=`${a}
`;else if(r.startsWith(">")&&1".repeat(r.length)+a+"".repeat(r.length);else if(/{\d+}/i.test(r)){const e=r.replace(/{\d+}/gi,""),t=r.replace(/{|}\S+/gi,"");a=e.repeat(t)+a}else"-"==r||"*"==r?a=`${a}`:/<\w+>/.test(r)&&(r=r.replace(/<|>/gi,""),a=`<${r}>${a}<${r}>`);else a=`${a}
`;return a=a.replace(/\n/gi,"
"),new Promise((t,e)=>{markdown(a,void 0,e=>{r.startsWith("!")&&(r=r.replace("!",""),e=e.split("\n").map(e=>r+e).join("\n")),t(e)},!1,JSON.stringify(n))})}function htmlformat(e,t,a){let r=plugin_storage.format;try{r=r?{bulletListMarker:"-",...r=JSON.parse(r)}:JSON.stringify({bulletListMarker:"-"})}catch(e){r=JSON.stringify({bulletListMarker:"-"}),console.error("format_html option error",e)}if(/^```/i.test(e)&&/```$/i.test(e)&&(e=e.replace(//gi,">")),t)if(">"==t)e=`${e}
`;else if(t.startsWith(">")&&1".repeat(t.length)+e+"".repeat(t.length);else if(/{\d+}/i.test(t)){const n=t.replace(/{\d+}/gi,""),l=t.replace(/{|}\S+/gi,"");e=n.repeat(l)+e}else"-"==t||"*"==t?e=`${e}`:/<\w+>/.test(t)&&(e=`<${t=t.replace(/<|>/gi,"")}>${e}<${t}>`);else e=`${e}
`;markdown(e=e.replace(/\n/gi,"
"),void 0,e=>{t.startsWith("!")&&(t=t.replace("!",""),e=e.split("\n").map(e=>t+e).join("\n")),a(e)},!1,JSON.stringify(r))}window.enhanTemplateFormatSync||(window.enhanTemplateFormatSync=htmlformatSync),window.enhanTemplateFormat||(window.enhanTemplateFormat=htmlformat),mdtemplate(unread);
753 | }
754 |
755 | schedule() {
756 | window.clearInterval( this.interval );
757 | this.interval = null;
758 | this.updater = 0;
759 | if ( this.settings.frequency == -1 ) {
760 | return;
761 | }
762 | if ( this.settings.frequency != -1 ) {
763 | this.notice( '监控 simpread_config.json 变化功能已下线,请使用 Sync Server 方案。', true, 4, true );
764 | this.settings.frequency = -1;
765 | this.saveSettings();
766 | return;
767 | }
768 | const minutes = parseInt( this.settings.frequency ),
769 | loop = minutes * 60 * 1000;
770 | if ( this.settings.frequency == 0 ) {
771 | const path = this.app.vault.adapter.path.resolve( this.settings.path, DEFAULT_SETTINGS.config );
772 | this.app.vault.adapter.fs.watch( path, ( event, filename ) => {
773 | if ( this.settings.frequency != 0 ) {
774 | return;
775 | }
776 | const now = +new Date();
777 | if ( now - this.updater > 2000 ) {
778 | setTimeout( () => this.sync(), 2000 );
779 | this.updater = now;
780 | }
781 | });
782 | } else {
783 | this.interval = window.setInterval(() => {
784 | this.sync();
785 | }, loop );
786 | }
787 | }
788 |
789 | replaceImage( title, md, callback ) {
790 | this.settings.img_relative
791 | ? this.indexes( 'read', title, undefined, undefined, ( err, data ) => {
792 | if ( err ) {
793 | console.log( 'indexes.json not found: ', title );
794 | } else {
795 | const urls = JSON.parse( data );
796 | Object.keys( urls ).forEach( url => {
797 | let uri = this.settings.assets_root == 'root' ? urls[ url ] : `|||${ urls[ url ] }|||`;
798 | uri = uri.replace( /\\/ig, '/' );
799 | md = md.replaceAll( url, uri );
800 | });
801 | console.log( '==== replace img relative from indexes.json ====' );
802 | console.log( md );
803 | }
804 | callback( md );
805 | })
806 | : callback( md );
807 | }
808 |
809 | indexes( state = 'write', title, folder, indexes, callback ) {
810 | title = title.replace( /@annote|@all/i, '' );
811 | const root = this.settings.assets_root == 'root'
812 | ? path.join( this.app.vault.adapter.basePath, this.settings.folder )
813 | : this.settings.assets;
814 | folder = this.settings.assets_root == 'root' ? path.join( title, 'assets' ) : folder || title || '';
815 | const filePath = path.join( root, folder, 'indexes.json' );
816 | if ( state == 'write' ) {
817 | fs.writeFile( filePath, JSON.stringify( indexes ), callback );
818 | } else {
819 | fs.readFile( filePath, 'utf8', callback );
820 | }
821 | }
822 |
823 | relative( title, md, type = 'annote' ) {
824 | if ( this.settings.img_relative ) {
825 | if ( this.settings.assets_root == 'root' ) {
826 | title = title.replace( /@annote|@all/i, '' );
827 | //title = type == 'unread' ? title : encodeURI( title );
828 | md = md.replace( new RegExp( 'http://localhost:7027/assets/' + title + '/', 'ig' ), '' );
829 | md = md.replace( new RegExp( 'http://localhost:7027/assets/' + encodeURI( title ) + '/', 'ig' ), '' );
830 | md = md.replace( new RegExp( title + '/', 'ig' ), '' );
831 | } else {
832 | //md = md.replace( new RegExp( 'http://localhost:7027/assets', 'ig' ), 'file:///' + this.settings.assets );
833 | md = md.replace( /(!\[\]\(http:\/\/localhost:7027\/assets\/)([\S ]+)(\.[\w]+\))/ig, ( match, p1, p2, p3 ) => '![]()' ) );
834 | md = md.replaceAll( '' );
836 | }
837 | }
838 | return md;
839 | }
840 |
841 | fixedImgURL( unread ) {
842 | unread.annotations.forEach( annote => {
843 | if ( annote.type == 'img' && annote.text.startsWith( 'http://localhost:7027/assets' )) {
844 | if ( this.settings.assets_root == 'root' ) {
845 | annote.text = `assets/${ new URL( annote.text ).pathname.split( '/' ).pop() }`;
846 | } else {
847 | const str1 = decodeURI( annote.text ).replace( 'http://localhost:7027/assets/', '' ).replace( / |\+/ig, '' ),
848 | str2 = unread.title.replace( / |\+/ig, '' );
849 | if ( str1.search( str2 ) > -1 ) {
850 | annote.text = '`;
851 | } else {
852 | annote.text = '';
853 | }
854 | console.log( str1, str2 )
855 | }
856 | }
857 | });
858 | }
859 |
860 | notice( msg, show = false, timeout = 0, forcing = false ) {
861 | show && new obsidian.Notice( msg );
862 | //this.statusBar.displayMessage(msg.toLowerCase(), timeout, forcing);
863 | }
864 |
865 | getObsidianClientID() {
866 | let obsidianClientId = window.localStorage.getItem( 'rw-ObsidianClientId' );
867 | if ( obsidianClientId ) {
868 | return obsidianClientId;
869 | }
870 | else {
871 | obsidianClientId = Math.random().toString( 36 ).substring( 2, 15 );
872 | window.localStorage.setItem( 'rw-ObsidianClientId', obsidianClientId );
873 | return obsidianClientId;
874 | }
875 | }
876 |
877 | preview( idx, hash ) {
878 | const MM_VIEW_TYPE = 'simpread-unreader',
879 | title = idx ? '' : this.app.workspace.activeLeaf.getDisplayText();
880 | if ( idx || /^\d+-/.test( title )) {
881 | let preview;
882 | if ( this.app.workspace.getLeavesOfType( MM_VIEW_TYPE ).length > 0 ) {
883 | preview = this.app.workspace.getLeavesOfType( MM_VIEW_TYPE )[0];
884 | } else {
885 | preview = this.app.workspace.splitActiveLeaf( 'vertical' );
886 | }
887 | !idx && ( idx = title.match( /^\d+/ )[0] );
888 | const srPreview = new UnreadView( this.settings, preview, title, idx, hash );
889 | preview.open( srPreview );
890 | } else this.notice( '只有当前文件的标题以 id- 开头才能使用此功能', true, 4, true );
891 | }
892 |
893 | openUnradbylink() {
894 | const me = this;
895 | this.registerMarkdownPostProcessor(( element, context ) => {
896 | const links = element.querySelectorAll( 'a' );
897 | for ( let i = 0; i < links.length; i++ ) {
898 | const link = links.item( i );
899 | if ( link.href.startsWith( 'http://localhost:7026/unread' )) {
900 | link.removeAttribute( 'href' );
901 | link.addEventListener( 'mousedown', event => this.srLinkHandler( event, me ) );
902 | }
903 | }
904 | });
905 | }
906 |
907 | srLinkHandler( event, me ) {
908 | const url = new URL( event.currentTarget.ariaLabel ),
909 | idx = url.pathname.replace( '/unread/', '' ),
910 | hash = url.hash.replace( '#id=', '#anchor=' );
911 | me.preview( idx, hash );
912 | }
913 | }
914 |
915 | class UnreadView extends obsidian.ItemView {
916 |
917 | constructor( settings, leaf, title, idx, hash ) {
918 | super( leaf );
919 | this.settings = settings;
920 | this.title = title;
921 | this.idx = idx;
922 | this.hash = hash;
923 | }
924 |
925 | getViewType() {
926 | return 'simpread-unreader';
927 | }
928 |
929 | getDisplayText() {
930 | return 'SimpRead UnReader ' + this.title;
931 | }
932 |
933 | onMoreOptionsMenu( menu ) {
934 | menu
935 | .addItem((item) =>
936 | item
937 | .setIcon( 'popup-open' )
938 | .setTitle( 'Open other unread by idx' )
939 | .onClick(() => this.openUnread() )
940 | )
941 | .addItem((item) =>
942 | item
943 | .setIcon( 'popup-open' )
944 | .setTitle( 'Open current unread by new tab' )
945 | .onClick(() => this.openUnreadBy( 'browser' ) )
946 | )
947 | .addItem((item) =>
948 | item
949 | .setIcon( 'popup-open' )
950 | .setTitle( 'Open current unread by window' )
951 | .onClick(() => this.openUnreadBy( 'urlscheme' ) )
952 | )
953 | .addItem((item) =>
954 | item
955 | .setIcon( 'popup-open' )
956 | .setTitle( 'Refresh' )
957 | .onClick(() => this.onRefresh() )
958 | );
959 | menu.showAtPosition({ x: 0, y: 0 });
960 | }
961 |
962 | onload() {
963 | let { containerEl } = this;
964 | const help = containerEl.createEl( 'p' );
965 | help.innerHTML = `