Skip to content

Commit caf6a3b

Browse files
committed
v0.0.14
2 parents 519ebce + e2cd7a8 commit caf6a3b

10 files changed

+140
-42
lines changed

manifest.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"id": "obsidian-raindrop-highlights",
33
"name": "Raindrop Highlights",
4-
"version": "0.0.13",
4+
"version": "0.0.14",
55
"minAppVersion": "0.14.0",
66
"description": "Sync your Raindrop.io highlights.",
77
"author": "kaiiiz",

package.json

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "obsidian-raindrop-highlights",
3-
"version": "0.0.13",
3+
"version": "0.0.14",
44
"description": "Sync your Raindrop.io highlights.",
55
"main": "main.js",
66
"scripts": {
@@ -12,23 +12,23 @@
1212
"author": "kaiiiz",
1313
"license": "MIT",
1414
"devDependencies": {
15-
"@tsconfig/svelte": "^3.0.0",
16-
"@types/node": "^16.11.6",
17-
"@types/nunjucks": "^3.2.1",
15+
"@tsconfig/svelte": "3.0.0",
16+
"@types/node": "16.11.6",
17+
"@types/nunjucks": "3.2.1",
1818
"@typescript-eslint/eslint-plugin": "5.29.0",
1919
"@typescript-eslint/parser": "5.29.0",
2020
"builtin-modules": "3.3.0",
2121
"esbuild": "0.14.47",
22-
"esbuild-svelte": "^0.7.1",
22+
"esbuild-svelte": "0.7.1",
2323
"obsidian": "latest",
24-
"svelte": "^3.49.0",
25-
"svelte-preprocess": "^4.10.7",
24+
"svelte": "3.49.0",
25+
"svelte-preprocess": "4.10.7",
2626
"tslib": "2.4.0",
2727
"typescript": "4.7.4"
2828
},
2929
"dependencies": {
30-
"axios": "^0.27.2",
31-
"nunjucks": "^3.2.3",
32-
"sanitize-filename": "^1.6.3"
30+
"axios": "0.27.2",
31+
"nunjucks": "3.2.3",
32+
"sanitize-filename": "1.6.3"
3333
}
3434
}
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{% if link %}link: {{link}}{% endif %}
2+
{% if tags|length %}tags: {%for tag in tags %}
3+
- {{tag}}{% endfor %}{% endif %}

src/constants.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import DEFAULT_TEMPLATE from './assets/defaultTemplate.njk';
22
import type { RaindropPluginSettings } from "./types";
33

4-
export const VERSION = '0.0.13';
4+
export const VERSION = '0.0.14';
55

66
export const DEFAULT_SETTINGS: RaindropPluginSettings = {
77
version: VERSION,
@@ -13,6 +13,7 @@ export const DEFAULT_SETTINGS: RaindropPluginSettings = {
1313
highlightsFolder: '/',
1414
syncCollections: {},
1515
template: DEFAULT_TEMPLATE,
16+
metadataTemplate: "",
1617
dateTimeFormat: 'YYYY/MM/DD HH:mm:ss',
1718
autoSyncInterval: 0,
1819
};

src/renderer.ts

+66-18
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import nunjucks from "nunjucks";
22
import Moment from "moment";
33
import type RaindropPlugin from "./main";
44
import type { BookmarkFileFrontMatter, RaindropBookmark } from "./types";
5-
import { stringifyYaml } from "obsidian";
5+
import { Notice, parseYaml, stringifyYaml } from "obsidian";
66

77
type RenderHighlight = {
88
id: string;
@@ -32,6 +32,32 @@ type RenderTemplate = {
3232
important: boolean;
3333
};
3434

35+
const FAKE_RENDER_CONTEXT: RenderTemplate = {
36+
is_new_article: true,
37+
id: 1000,
38+
title: "fake_title",
39+
excerpt: "fake_excerpt",
40+
link: "https://example.com",
41+
highlights: [
42+
{
43+
id: "123456789abcdefghijklmno",
44+
color: "red",
45+
created: "2022-08-11T01:58:27.457Z",
46+
lastUpdate: "2022-08-13T01:58:27.457Z",
47+
note: "fake_note",
48+
text: "fake_text",
49+
}
50+
],
51+
collection: {
52+
title: "fake_collection",
53+
},
54+
tags: ["fake_tag1", "fake_tag2"],
55+
cover: "https://example.com",
56+
created: "2022-08-10T01:58:27.457Z",
57+
type: "link",
58+
important: false,
59+
};
60+
3561
export default class Renderer {
3662
plugin: RaindropPlugin;
3763

@@ -40,16 +66,50 @@ export default class Renderer {
4066
nunjucks.configure({ autoescape: false });
4167
}
4268

43-
validate(template: string): boolean {
69+
validate(template: string, isYaml=false): boolean {
4470
try {
45-
nunjucks.renderString(template, {});
71+
const fakeContent = nunjucks.renderString(template, FAKE_RENDER_CONTEXT);
72+
if (isYaml) {
73+
const {id, created} = FAKE_RENDER_CONTEXT;
74+
const fakeMetadata = `raindrop_id: ${id}\nraindrop_last_update: ${created}\n${fakeContent}`
75+
parseYaml(fakeMetadata);
76+
}
4677
return true;
4778
} catch (error) {
4879
return false;
4980
}
5081
}
5182

52-
renderContent(bookmark: RaindropBookmark, newArticle = true) {
83+
renderContent(bookmark: RaindropBookmark, newArticle: boolean) {
84+
return this.renderTemplate(this.plugin.settings.template, bookmark, newArticle);
85+
}
86+
87+
renderFrontmatter(bookmark: RaindropBookmark, newArticle: boolean) {
88+
const newMdFrontmatter = this.renderTemplate(this.plugin.settings.metadataTemplate, bookmark, newArticle);
89+
let frontmatter: BookmarkFileFrontMatter = {
90+
raindrop_id: bookmark.id,
91+
raindrop_last_update: (new Date()).toISOString(),
92+
};
93+
try {
94+
frontmatter = {
95+
...frontmatter,
96+
...parseYaml(newMdFrontmatter),
97+
};
98+
} catch (e) {
99+
console.error(e);
100+
new Notice(`Failed to parse YAML for ${bookmark.title}: ${e.message}`)
101+
}
102+
return stringifyYaml(frontmatter);
103+
}
104+
105+
renderFullArticle(bookmark: RaindropBookmark) {
106+
const newMdContent = this.renderContent(bookmark, true);
107+
const newMdFrontmatter = this.renderFrontmatter(bookmark, true);
108+
const mdContent = `---\n${newMdFrontmatter}---\n${newMdContent}`;
109+
return mdContent;
110+
}
111+
112+
private renderTemplate(template:string, bookmark: RaindropBookmark, newArticle: boolean) {
53113
const dateTimeFormat = this.plugin.settings.dateTimeFormat;
54114

55115
const renderHighlights: RenderHighlight[] = bookmark.highlights.map((hl) => {
@@ -64,7 +124,7 @@ export default class Renderer {
64124
return renderHighlight;
65125
});
66126

67-
// sync() should keep the latest collection data in local in the beginning
127+
// the latest collection data is sync from Raindrop at the beginning of `sync` function
68128
const renderCollection: RenderCollection = {
69129
title: this.plugin.settings.syncCollections[bookmark.collectionId].title,
70130
}
@@ -83,20 +143,8 @@ export default class Renderer {
83143
type: bookmark.type,
84144
important: bookmark.important,
85145
};
86-
87-
const template = this.plugin.settings.template;
146+
88147
const content = nunjucks.renderString(template, context);
89148
return content;
90149
}
91-
92-
renderFullPost(bookmark: RaindropBookmark) {
93-
const newMdContent = this.renderContent(bookmark, true);
94-
const frontmatter: BookmarkFileFrontMatter = {
95-
raindrop_id: bookmark.id,
96-
raindrop_last_update: (new Date()).toISOString(),
97-
};
98-
const frontmatterStr = stringifyYaml(frontmatter);
99-
const mdContent = `---\n${frontmatterStr}---\n${newMdContent}`;
100-
return mdContent;
101-
}
102150
}

src/settings.ts

+33-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import {App, Notice, PluginSettingTab, Setting} from 'obsidian';
2+
import DEFAULT_METADATA_TEMPLATE from './assets/defaultMetadataTemplate.njk';
23
import templateInstructions from './templates/templateInstructions.html';
4+
import metadataTemplateInstructions from './templates/metadataTemplateInstructions.html';
35
import datetimeInstructions from './templates/datetimeInstructions.html';
46
import appendModeInstructions from './templates/appendModeInstructions.html';
57
import type { RaindropAPI } from './api';
@@ -37,6 +39,7 @@ export class RaindropSettingTab extends PluginSettingTab {
3739
this.autoSyncInterval();
3840
this.dateFormat();
3941
this.template();
42+
this.metadataTemplate();
4043
this.resetSyncHistory();
4144
}
4245

@@ -76,7 +79,7 @@ export class RaindropSettingTab extends PluginSettingTab {
7679
.setName('Only sync bookmarks with highlights')
7780
.addToggle((toggle) => {
7881
return toggle
79-
.setValue(this.plugin.settings.ribbonIcon)
82+
.setValue(this.plugin.settings.onlyBookmarksWithHl)
8083
.onChange(async (value) => {
8184
this.plugin.settings.onlyBookmarksWithHl = value;
8285
await this.plugin.saveSettings();
@@ -204,7 +207,7 @@ export class RaindropSettingTab extends PluginSettingTab {
204207
.createContextualFragment(templateInstructions);
205208

206209
new Setting(this.containerEl)
207-
.setName('Highlights template')
210+
.setName('Content template')
208211
.setDesc(templateDescFragment)
209212
.addTextArea((text) => {
210213
text.inputEl.style.width = '100%';
@@ -224,6 +227,34 @@ export class RaindropSettingTab extends PluginSettingTab {
224227
return text;
225228
});
226229
}
230+
private async metadataTemplate(): Promise<void> {
231+
const templateDescFragment = document
232+
.createRange()
233+
.createContextualFragment(metadataTemplateInstructions);
234+
235+
new Setting(this.containerEl)
236+
.setName('Metadata template')
237+
.setDesc(templateDescFragment)
238+
.addTextArea((text) => {
239+
text.inputEl.style.width = '100%';
240+
text.inputEl.style.height = '250px';
241+
text.inputEl.style.fontSize = '0.8em';
242+
text.setPlaceholder(DEFAULT_METADATA_TEMPLATE);
243+
text.setValue(this.plugin.settings.metadataTemplate)
244+
.onChange(async (value) => {
245+
const isValid = this.renderer.validate(value, true);
246+
247+
if (isValid) {
248+
this.plugin.settings.metadataTemplate = value;
249+
await this.plugin.saveSettings();
250+
}
251+
252+
text.inputEl.style.border = isValid ? '' : '1px solid red';
253+
});
254+
return text;
255+
});
256+
}
257+
227258

228259
private resetSyncHistory(): void {
229260
new Setting(this.containerEl)

src/sync.ts

+10-9
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ export default class RaindropSync {
9797
async updateFileAppendMode(file: TFile, bookmark: RaindropBookmark) {
9898
console.debug("update file append mode", file.path);
9999
const metadata = this.app.metadataCache.getFileCache(file);
100+
100101
if (metadata?.frontmatter && 'raindrop_last_update' in metadata.frontmatter) {
101102
const localLastUpdate = new Date(metadata.frontmatter.raindrop_last_update);
102103
if (localLastUpdate >= bookmark.lastUpdate) {
@@ -109,36 +110,36 @@ export default class RaindropSync {
109110
});
110111
}
111112

112-
const newMdContent = this.renderer.renderContent(bookmark, false);
113-
await this.app.vault.append(file, newMdContent);
113+
const appendedContent = this.renderer.renderContent(bookmark, false);
114114

115-
// update frontmatter
115+
await this.app.vault.append(file, appendedContent);
116+
117+
// update raindrop_last_update
116118
if (metadata?.frontmatter) {
117119
// separate content and front matter
118120
const fileContent = await this.app.vault.cachedRead(file);
119121
const {position: {start, end}} = metadata.frontmatter;
120-
const fileContentObj = this.splitFrontmatterAndContent(fileContent, end.line);
122+
const article = this.splitFrontmatterAndContent(fileContent, end.line);
121123

122-
// update frontmatter
123-
const frontmatterObj: BookmarkFileFrontMatter = parseYaml(fileContentObj.frontmatter);
124+
const frontmatterObj: BookmarkFileFrontMatter = parseYaml(article.frontmatter);
124125
frontmatterObj.raindrop_last_update = (new Date()).toISOString();
125126

126127
// stringify and concat
127128
const newFrontmatter = stringifyYaml(frontmatterObj);
128-
const newFullFileContent = `---\n${newFrontmatter}---\n${fileContentObj.content}`;
129+
const newFullFileContent = `---\n${newFrontmatter}---\n${article.content}`;
129130
await this.app.vault.modify(file, newFullFileContent);
130131
}
131132
}
132133

133134
async updateFileOverwriteMode(file: TFile, bookmark: RaindropBookmark) {
134135
console.debug("update file overwrite mode", file.path);
135-
const mdContent = this.renderer.renderFullPost(bookmark);
136+
const mdContent = this.renderer.renderFullArticle(bookmark);
136137
return this.app.vault.modify(file, mdContent);
137138
}
138139

139140
async createFile(filePath: string, bookmark: RaindropBookmark): Promise<TFile> {
140141
console.debug("create file", filePath);
141-
const mdContent = this.renderer.renderFullPost(bookmark);
142+
const mdContent = this.renderer.renderFullArticle(bookmark);
142143
return this.app.vault.create(filePath, mdContent);
143144
}
144145

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Metadata template (<a href="https://mozilla.github.io/nunjucks/">Nunjucks</a>) for
2+
rendering every synced Raindrop.io highlights & annotations.
3+
4+
<p>
5+
Some notes:
6+
</p>
7+
<ul>
8+
<li>The rendered result is placed in the front matter of the generated post.</li>
9+
<li>If the rendered result does not follow the YAML syntax, the plugin will reject the update.</li>
10+
<li><code>raindrop_id</code> and <code>raindrop_last_update</code> properties are transparently added by the plugin.</li>
11+
<li>Available variables to use are the same as the previous template.</li>
12+
</ul>

src/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export interface RaindropPluginSettings {
6767
highlightsFolder: string;
6868
syncCollections: SyncCollectionSettings;
6969
template: string;
70+
metadataTemplate: string;
7071
dateTimeFormat: string;
7172
autoSyncInterval: number;
7273
}

versions.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@
1111
"0.0.10": "0.14.0",
1212
"0.0.11": "0.14.0",
1313
"0.0.12": "0.14.0",
14-
"0.0.13": "0.14.0"
14+
"0.0.13": "0.14.0",
15+
"0.0.14": "0.14.0"
1516
}

0 commit comments

Comments
 (0)