Compare commits

...

5 commits

Author SHA1 Message Date
Ell 0717165809 started on JSP view 2023-08-16 14:21:40 +02:00
Ell ad6b1a223f cleanup 2023-08-16 13:34:33 +02:00
Ell fa85bd8d93 embed attachments as base64 when uploading 2023-08-16 13:32:47 +02:00
Ell c9a1dae9a2 added frontmatter stripping 2023-08-16 12:47:47 +02:00
Ell 36f68d47bc switched to markdown-it 2023-08-16 12:13:00 +02:00
12 changed files with 175 additions and 981 deletions

955
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -14,7 +14,6 @@
"devDependencies": {
"@types/node": "^16.11.6",
"builtin-modules": "^3.2.0",
"electron": "^13.6.2",
"esbuild": "0.14.0",
"esbuild-plugin-copy": "^1.3.0",
"obsidian": "latest",

View file

@ -10,9 +10,9 @@
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.0/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.0.5/dist/purify.js"></script>
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.8.0/build/highlight.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/marked@7.0.2/lib/marked.umd.js"></script>
<script src="https://cdn.jsdelivr.net/npm/marked-katex-extension@3.0.3/lib/index.umd.js"></script>
<script src="https://cdn.jsdelivr.net/npm/marked-highlight@2.0.4/lib/index.umd.js"></script>
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/markdown-it@13.0.1/dist/markdown-it.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/markdown-it-texmath/texmath.min.js"></script>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter&display=swap">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=JetBrains+Mono&display=swap">
@ -27,14 +27,19 @@
<div class="content">
<div id="main">
<script>
marked.use(markedKatex());
marked.use(markedHighlight.markedHighlight({
let md = markdownit({
html: true,
linkify: true,
langPrefix: "hljs language-",
highlight: (c, l) => {
const language = hljs.getLanguage(l) ? l : "plaintext";
return hljs.highlight(c, {language}).value;
}
}));
});
md.use(texmath, {
engine: katex,
delimiters: ["dollars", "beg_end"]
});
let main = $("#main");
$(window).on("hashchange", display);
@ -47,7 +52,7 @@
$.ajax({
method: "get",
url: hash ? `share.php?id=${hash}` : "index.md",
success: t => main.html(DOMPurify.sanitize(marked.parse(t))),
success: t => main.html(DOMPurify.sanitize(md.render(t))),
error: (r, s, e) => main.html(`<div class="error"><p>Error loading shared note with id <code>${hash}</code>: ${e}</p><p><a href="#">Home</a></p></div>`)
});
}

View file

@ -1,21 +1,30 @@
import {Notice, Plugin, requestUrl, TFile} from "obsidian";
import {arrayBufferToBase64, Notice, Plugin, requestUrl, TFile} from "obsidian";
import {defaultSettings, JSPSettings, SharedItem} from "./settings";
import {JSPSettingsTab} from "./settings-tab";
import {basename, extname} from "path";
import {JSPView} from "./view";
export default class JustSharePleasePlugin extends Plugin {
// TODO panel that displays all shares, including ones for removed files, and allows unsharing or updating them
// TODO add a setting for auto-refreshing uploads when saving
// TODO strip frontmatter before uploading? maybe optionally
settings: JSPSettings;
public settings: JSPSettings;
async onload(): Promise<void> {
await this.loadSettings();
this.addSettingTab(new JSPSettingsTab(this.app, this));
this.registerView(JSPView.type, l => new JSPView(this, l));
this.addCommand({
id: `open-${JSPView.type}`,
name: `Open Just Share Please view`,
callback: async () => {
if (!this.app.workspace.getLeavesOfType(JSPView.type).length)
await this.app.workspace.getRightLeaf(false).setViewState({type: JSPView.type, active: true});
this.app.workspace.revealLeaf(this.app.workspace.getLeavesOfType(JSPView.type)[0]);
}
});
this.registerEvent(this.app.workspace.on("file-menu", async (m, f) => {
if (f instanceof TFile) {
if (f instanceof TFile && f.extension == "md") {
let shared = this.getSharedItem(f);
if (!shared) {
m.addItem(i => {
@ -94,6 +103,7 @@ export default class JustSharePleasePlugin extends Plugin {
return false;
}
});
}
async loadSettings(): Promise<void> {
@ -113,7 +123,7 @@ export default class JustSharePleasePlugin extends Plugin {
let response = await requestUrl({
url: `${this.settings.url}/share.php`,
method: "POST",
body: JSON.stringify({content: await this.app.vault.cachedRead(file)})
body: JSON.stringify({content: await this.preProcessMarkdown(file)})
});
let shared = response.json as SharedItem;
shared.path = file.path;
@ -123,6 +133,7 @@ export default class JustSharePleasePlugin extends Plugin {
this.settings.shared.push(shared);
await this.saveSettings();
this.refreshAllViews();
return shared;
} catch (e) {
@ -140,7 +151,7 @@ export default class JustSharePleasePlugin extends Plugin {
url: `${this.settings.url}/share.php?id=${item.id}`,
method: "PATCH",
headers: {"Password": item.password},
body: JSON.stringify({content: await this.app.vault.cachedRead(file)})
body: JSON.stringify({content: await this.preProcessMarkdown(file)})
});
new Notice(`Successfully updated ${file.basename} on JSP`);
return true;
@ -168,6 +179,7 @@ export default class JustSharePleasePlugin extends Plugin {
this.settings.shared.remove(item);
await this.saveSettings();
this.refreshAllViews();
return true;
} catch (e) {
@ -184,4 +196,41 @@ export default class JustSharePleasePlugin extends Plugin {
if (notice)
new Notice(`Copied link to ${basename(item.path, extname(item.path))} to clipboard`);
}
async preProcessMarkdown(file: TFile): Promise<string> {
let text = await this.app.vault.cachedRead(file);
// strip frontmatter
if (this.settings.stripFrontmatter)
text = text.replace(/^---\s*\n.*?\n---\s*\n(.*)$/s, "$1");
// embed attachments directly
let attachments = /!\[(.*)]\((.+)\)|!\[\[(.+)]]/g;
let match: RegExpExecArray;
while ((match = attachments.exec(text)) != null) {
let alt = match[1] ?? "";
let url = match[2] ?? match[3];
if (url.startsWith("http"))
continue;
try {
let resolved = this.app.metadataCache.getFirstLinkpathDest(url, file.path).path;
let attachment = this.app.vault.getAbstractFileByPath(resolved);
let data = arrayBufferToBase64(await this.app.vault.readBinary(attachment as TFile));
let img = `<img src="data:image/${extname(resolved).substring(1)};base64, ${data}" alt="${alt}">`;
text = text.substring(0, match.index) + img + text.substring(match.index + match[0].length);
} catch (e) {
console.log(`Error embedding attachment ${url}: ${e}`);
}
}
return text;
}
// TODO refresh when a file is moved or deleted in Obsidian
refreshAllViews(): void {
for (let leaf of this.app.workspace.getLeavesOfType(JSPView.type)) {
if (leaf.view instanceof JSPView)
leaf.view.refresh();
}
}
}

View file

@ -1,12 +1,16 @@
export const defaultSettings: JSPSettings = {
url: "http://localhost:8080",
shared: []
shared: [],
stripFrontmatter: true
};
// TODO add a setting for auto-refreshing uploads when saving
// TODO add a setting for auto-removing JSP shares when the original file is deleted
export interface JSPSettings {
url: string;
shared: SharedItem[];
stripFrontmatter: boolean;
}
@ -14,6 +18,7 @@ export interface SharedItem {
id: string;
password: string;
// TODO auto-update path when file is moved
path: string;
}

66
src/view.ts Normal file
View file

@ -0,0 +1,66 @@
import {ButtonComponent, ItemView, TFile, WorkspaceLeaf} from "obsidian";
import {basename, extname} from "path";
import JustSharePleasePlugin from "./main";
export class JSPView extends ItemView {
public static readonly type: string = "jsp-view";
private readonly plugin: JustSharePleasePlugin;
constructor(plugin: JustSharePleasePlugin, leaf: WorkspaceLeaf) {
super(leaf);
this.plugin = plugin;
}
public refresh(): void {
this.contentEl.empty();
let content = this.contentEl.createDiv({cls: "just-share-please-view"});
for (let shared of this.plugin.settings.shared) {
let file = this.plugin.app.vault.getAbstractFileByPath(shared.path) as TFile;
let div = content.createDiv({cls: "just-share-please-shared-item"});
div.createSpan({cls: "just-share-please-shared-name", text: basename(shared.path, extname(shared.path))});
new ButtonComponent(div)
.setClass("clickable-icon")
.setTooltip("Copy JSP link")
.setIcon("link")
.onClick(async () => this.plugin.copyShareLink(shared));
if (file) {
new ButtonComponent(div)
.setClass("clickable-icon")
.setTooltip("Open in Obsidian")
.setIcon("edit")
.onClick(async () => {
// TODO open in obsidian
});
new ButtonComponent(div)
.setClass("clickable-icon")
.setTooltip("Update in JSP")
.setIcon("share")
.onClick(async () => this.plugin.updateFile(shared, file));
}
new ButtonComponent(div)
.setClass("clickable-icon")
.setTooltip("Delete from JSP")
.setIcon("trash")
.onClick(async () => this.plugin.deleteFile(shared));
}
}
public onload(): void {
this.refresh();
}
public getDisplayText(): string {
return "Just Share Please";
}
public getViewType(): string {
return JSPView.type;
}
public getIcon(): string {
return "share";
}
}

View file

@ -3,3 +3,17 @@
width: 400px;
height: auto;
}
.just-share-please-view .clickable-icon {
display: inline;
}
.just-share-please-shared-name {
display: block;
font-weight: bold;
padding-bottom: 5px;
}
.just-share-please-shared-item {
margin-bottom: 20px;
}

View file

@ -2,14 +2,10 @@
"url": "http://localhost:8080",
"shared": [
{
"id": "7dd626d1",
"password": "6253e1ab4b813dc08d52ee7c82b0990b",
"path": "Second note.md"
},
{
"id": "281d96f9",
"password": "2eb4979a9e853df98f5fcdc2cdc4b770",
"id": "de56fe78",
"password": "2d276f4d3c0715be08ad1812faa97deb",
"path": "Cool Test Note.md"
}
]
],
"stripFrontmatter": true
}

View file

@ -1,3 +1,8 @@
---
test: yes
---
yo
# Cool Test Note
This is a cool test note, my friends!
@ -30,4 +35,14 @@ cool!!
The following is $x^2 = 7$, but more complicated!
$$
x^2 + \sum_{i = 1}^{10000} x^2 \cdot 0 = 7
$$
$$
image!
![this is an image my friends, and this is my alt text](Obsidian_TtC7w4GA86.png)
wikilink image!
![[Pasted image 20230816130420.png]]
nice

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB