From 06096fd3640a79c6d80feac8df540aadc1dacbef Mon Sep 17 00:00:00 2001
From: deliciously-default
<181034365+deliciously-default@users.noreply.github.com>
Date: Wed, 4 Dec 2024 22:11:49 -0300
Subject: [PATCH] Render HTML from the plugin if possible
---
lexicons-src/publicFile.yml | 3 +
lexicons/publicFile.json | 4 +
package.json | 8 +-
pnpm-lock.yaml | 148 ++++-----
spa/index.html | 2 +-
spa/package.json | 2 +
spa/pnpm-lock.yaml | 11 +
spa/src/api-client.ts | 3 +-
spa/src/routes/Page.svelte | 100 +++++-
spa/src/styles/obsidian.scss | 225 +++++++++++++
spa/src/styles/style.scss | 14 +
spa/src/styles/vanilla.css | 212 -------------
spa/src/styles/vanilla.scss | 222 +++++++++++++
src/index.tsx | 8 +-
src/lexicon/index.ts | 2 +
src/lexicon/lexicons.ts | 2 +
src/markdown-renderer/renderer.ts | 332 ++++++++++++++++++++
src/markdown-renderer/styles/app-css.ts | 207 ++++++++++++
src/markdown-renderer/styles/mathjax-css.ts | 111 +++++++
src/sync/share.ts | 41 ++-
tsconfig.json | 2 +-
utils/glob-run.ts | 39 +++
22 files changed, 1381 insertions(+), 317 deletions(-)
create mode 100644 spa/src/styles/obsidian.scss
create mode 100644 spa/src/styles/style.scss
delete mode 100644 spa/src/styles/vanilla.css
create mode 100644 spa/src/styles/vanilla.scss
create mode 100644 src/markdown-renderer/renderer.ts
create mode 100644 src/markdown-renderer/styles/app-css.ts
create mode 100644 src/markdown-renderer/styles/mathjax-css.ts
create mode 100644 utils/glob-run.ts
diff --git a/lexicons-src/publicFile.yml b/lexicons-src/publicFile.yml
index 84a80f0..dc231f5 100644
--- a/lexicons-src/publicFile.yml
+++ b/lexicons-src/publicFile.yml
@@ -19,6 +19,9 @@ defs:
body:
type: blob
description: The contents of the file.
+ html:
+ type: string
+ description: Rendered HTML contents of the file, for Markdown files.
vaultName:
type: string
description: The name of the vault the file is stored in.
diff --git a/lexicons/publicFile.json b/lexicons/publicFile.json
index 8771b54..cf78668 100644
--- a/lexicons/publicFile.json
+++ b/lexicons/publicFile.json
@@ -20,6 +20,10 @@
"type": "blob",
"description": "The contents of the file."
},
+ "html": {
+ "type": "string",
+ "description": "Rendered HTML contents of the file, for Markdown files."
+ },
"vaultName": {
"type": "string",
"description": "The name of the vault the file is stored in."
diff --git a/package.json b/package.json
index 7ef00ef..4725842 100644
--- a/package.json
+++ b/package.json
@@ -7,21 +7,23 @@
"dev": "vite build --watch --mode development",
"build": "tsc -noEmit -skipLibCheck && vite build --mode production",
"version": "node version-bump.mjs && git add manifest.json versions.json",
- "lex": "tsx utils/yaml2json -i \"./lexicons-src/*.yml\" -o \"./lexicons\"; lex-cli generate -o ./src/lexicon/lexicons.ts ./lexicons/*; lex-cli generate-main -o ./src/lexicon/index.ts ./lexicons/*"
+ "lex": "tsx utils/yaml2json -i \"./lexicons-src/*.yml\" -o \"./lexicons\" && tsx utils/glob-run lex-cli generate -o \"./src/lexicon/lexicons.ts\" \"./lexicons/*\" && tsx utils/glob-run lex-cli generate-main -o \"./src/lexicon/index.ts\" \"./lexicons/*\""
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
- "@atproto/lex-cli": "^0.5.2",
+ "@atcute/lex-cli": "^1.0.3",
"@craftamap/esbuild-plugin-html": "^0.8.0",
"@sveltejs/vite-plugin-svelte": "^3.1.2",
"@tsconfig/svelte": "^5.0.4",
+ "@types/child-process-promise": "^2.2.6",
"@types/express": "^5.0.0",
"@types/mime-db": "^1.43.5",
"@types/node": "^22.10.1",
"@typescript-eslint/eslint-plugin": "^8.16.0",
"@typescript-eslint/parser": "^8.16.0",
+ "child-process-promise": "^2.2.1",
"esbuild": "^0.24.0",
"esbuild-multicontext": "^0.13.0",
"esbuild-plugin-copy": "^2.1.1",
@@ -40,7 +42,6 @@
"dependencies": {
"@atcute/cid": "^1.0.2",
"@atcute/client": "^2.0.6",
- "@atcute/lex-cli": "^1.0.3",
"@atcute/oauth-browser-client": "^1.0.7",
"@atproto/api": "^0.13.18",
"@atproto/common": "^0.4.4",
@@ -65,6 +66,7 @@
"crockford-base32": "^2.0.0",
"esbuild-plugins-node-modules-polyfill": "^1.6.8",
"express": "^4.21.1",
+ "js-base64": "^3.7.7",
"mime-db": "^1.53.0",
"mime-type": "^5.0.0",
"multiformats": "^13.3.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 705ef2c..f920333 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -14,9 +14,6 @@ importers:
'@atcute/client':
specifier: ^2.0.6
version: 2.0.6
- '@atcute/lex-cli':
- specifier: ^1.0.3
- version: 1.0.3
'@atcute/oauth-browser-client':
specifier: ^1.0.7
version: 1.0.7
@@ -89,6 +86,9 @@ importers:
express:
specifier: ^4.21.1
version: 4.21.1
+ js-base64:
+ specifier: ^3.7.7
+ version: 3.7.7
mime-db:
specifier: ^1.53.0
version: 1.53.0
@@ -111,9 +111,9 @@ importers:
specifier: ^5.1.0
version: 5.1.0
devDependencies:
- '@atproto/lex-cli':
- specifier: ^0.5.2
- version: 0.5.2
+ '@atcute/lex-cli':
+ specifier: ^1.0.3
+ version: 1.0.3
'@craftamap/esbuild-plugin-html':
specifier: ^0.8.0
version: 0.8.0(esbuild@0.24.0)
@@ -123,6 +123,9 @@ importers:
'@tsconfig/svelte':
specifier: ^5.0.4
version: 5.0.4
+ '@types/child-process-promise':
+ specifier: ^2.2.6
+ version: 2.2.6
'@types/express':
specifier: ^5.0.0
version: 5.0.0
@@ -138,6 +141,9 @@ importers:
'@typescript-eslint/parser':
specifier: ^8.16.0
version: 8.16.0(eslint@9.16.0)(typescript@5.8.0-dev.20241201)
+ child-process-promise:
+ specifier: ^2.2.1
+ version: 2.2.1
esbuild:
specifier: ^0.24.0
version: 0.24.0
@@ -260,10 +266,6 @@ packages:
'@atproto/jwk@0.1.1':
resolution: {integrity: sha512-6h/bj1APUk7QcV9t/oA6+9DB5NZx9SZru9x+/pV5oHFI9Xz4ZuM5+dq1PfsJV54pZyqdnZ6W6M717cxoC7q7og==}
- '@atproto/lex-cli@0.5.2':
- resolution: {integrity: sha512-fM/FR/FpOMUOpwir7odZNiJhY3at0gMDGZpkLJeWFDaYyChtwqCGd6x7J4S6w/mqtCDcx1bzGTrnsLkJjtuNfg==}
- hasBin: true
-
'@atproto/lexicon@0.4.3':
resolution: {integrity: sha512-lFVZXe1S1pJP0dcxvJuHP3r/a+EAIBwwU7jUK+r8iLhIja+ml6NmYv8KeFHmIJATh03spEQ9s02duDmFVdCoXg==}
@@ -1096,15 +1098,15 @@ packages:
resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==}
engines: {node: '>= 6'}
- '@ts-morph/common@0.17.0':
- resolution: {integrity: sha512-RMSSvSfs9kb0VzkvQ2NWobwnj7TxCA9vI/IjR9bDHqgAyVbu2T0DN4wiKVqomyDWqO7dPr/tErSfq7urQ1Q37g==}
-
'@tsconfig/svelte@5.0.4':
resolution: {integrity: sha512-BV9NplVgLmSi4mwKzD8BD/NQ8erOY/nUE/GpgWe2ckx+wIQF5RyRirn/QsSSCPeulVpc3RA/iJt6DpfTIZps0Q==}
'@types/body-parser@1.19.5':
resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==}
+ '@types/child-process-promise@2.2.6':
+ resolution: {integrity: sha512-g0pOHijr6Trug43D2bV0PLSIsSHa/xHEES2HeX5BAlduq1vW0nZcq27Zeud5lgmNB+kPYYVqiMap32EHGTco/w==}
+
'@types/codemirror@5.60.8':
resolution: {integrity: sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==}
@@ -1437,6 +1439,9 @@ packages:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
+ child-process-promise@2.2.1:
+ resolution: {integrity: sha512-Fi4aNdqBsr0mv+jgWxcZ/7rAIC2mgihrptyVI4foh/rrjY/3BNjfP9+oaiFx/fzim+1ZyCNBae0DlyfQhSugog==}
+
chokidar@3.6.0:
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
engines: {node: '>= 8.10.0'}
@@ -1449,9 +1454,6 @@ packages:
resolution: {integrity: sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==}
engines: {node: '>= 0.10'}
- code-block-writer@11.0.3:
- resolution: {integrity: sha512-NiujjUFB4SwScJq2bwbYUtXbZhBSlY6vYzm++3Q6oC+U+injTqfPYFK8wS9COOmb2lueqp0ZRB4nK1VYeHgNyw==}
-
code-red@1.0.4:
resolution: {integrity: sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==}
@@ -1466,10 +1468,6 @@ packages:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
- commander@9.5.0:
- resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==}
- engines: {node: ^12.20.0 || >=14}
-
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
@@ -1515,6 +1513,9 @@ packages:
crockford-base32@2.0.0:
resolution: {integrity: sha512-qRLBMo1MfpajCVi4Ks+MKPk3aRtl+41BMBt2HC3roG81Ip948EIKmfbZO4R3L5Y12/8sDjfCdTmrJ4Cw9IpNfA==}
+ cross-spawn@4.0.2:
+ resolution: {integrity: sha512-yAXz/pA1tD8Gtg2S98Ekf/sewp3Lcp3YoFKJ4Hkp5h5yLWnKVTDU0kwjKJ8NDCYcfTLfyGkzTikst+jWypT1iA==}
+
cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
@@ -2063,6 +2064,9 @@ packages:
jose@5.9.6:
resolution: {integrity: sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==}
+ js-base64@3.7.7:
+ resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==}
+
js-yaml@4.1.0:
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
hasBin: true
@@ -2132,6 +2136,9 @@ packages:
resolution: {integrity: sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==}
engines: {node: 20 || >=22}
+ lru-cache@4.1.5:
+ resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==}
+
magic-string@0.30.14:
resolution: {integrity: sha512-5c99P1WKTed11ZC0HMJOj6CDIue6F8ySu+bJL+85q1zBEIY8IklrJ1eiKC2NDRh3Ct3FcvmJPyQHb9erXMTJNw==}
@@ -2198,10 +2205,6 @@ packages:
minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
- minimatch@5.1.6:
- resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
- engines: {node: '>=10'}
-
minimatch@9.0.5:
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -2210,11 +2213,6 @@ packages:
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
engines: {node: '>=16 || 14 >=14.17'}
- mkdirp@1.0.4:
- resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
- engines: {node: '>=10'}
- hasBin: true
-
mlly@1.7.3:
resolution: {integrity: sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==}
@@ -2261,6 +2259,10 @@ packages:
resolution: {integrity: sha512-g/koYzOr9Fb1Jc+tHUHlFd5gODjGn48tHexUK8q6iqOVriEgSnd3/1T7myBYc+0KBVze/7F7n65ec9rW6OD7xw==}
engines: {node: '>=10'}
+ node-version@1.2.0:
+ resolution: {integrity: sha512-ma6oU4Sk0qOoKEAymVoTvk8EdXEobdS7m/mAGhDJ8Rouugho48crHBORAmy5BoOcv8wraPM6xumapQp5hl4iIQ==}
+ engines: {node: '>=6.0.0'}
+
normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
@@ -2439,10 +2441,16 @@ packages:
resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
engines: {node: '>= 0.6.0'}
+ promise-polyfill@6.1.0:
+ resolution: {integrity: sha512-g0LWaH0gFsxovsU7R5LrrhHhWAWiHRnh1GPrhXnPgYsDkIqjRYUYSZEsej/wtleDrz5xVSIDbeKfidztp2XHFQ==}
+
proxy-addr@2.0.7:
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
engines: {node: '>= 0.10'}
+ pseudomap@1.0.2:
+ resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==}
+
psl@1.15.0:
resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==}
@@ -2774,9 +2782,6 @@ packages:
peerDependencies:
typescript: '>=4.2.0'
- ts-morph@16.0.0:
- resolution: {integrity: sha512-jGNF0GVpFj0orFw55LTsQxVYEUOCWBAbR5Ls7fTYE5pQsbW18ssTb/6UXx/GYAEjS+DQTp8VoTw0vqYMiaaQuw==}
-
tsconfck@3.1.4:
resolution: {integrity: sha512-kdqWFGVJqe+KGYvlSO9NIaWn9jT1Ny4oKVzAJsKii5eoE9snzTJzL4+MMVOMn+fikWGFmKEylcXL710V/kIPJQ==}
engines: {node: ^18 || >=20}
@@ -2973,6 +2978,10 @@ packages:
resolution: {integrity: sha512-g+N+GAWiRj66DngFwHvISJd+ITsyphZvD1vChfVg6cEdnzy53GzB3oy0fUNlvhz7H7+MiqhYr26qxQShCpKTTQ==}
engines: {node: '>= 0.4'}
+ which@1.3.1:
+ resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
+ hasBin: true
+
which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}
@@ -3012,14 +3021,14 @@ packages:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'}
+ yallist@2.1.2:
+ resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==}
+
yaml@2.6.1:
resolution: {integrity: sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==}
engines: {node: '>= 14'}
hasBin: true
- yesno@0.4.0:
- resolution: {integrity: sha512-tdBxmHvbXPBKYIg81bMCB7bVeDmHkRzk5rVJyYYXurwKkHq/MCd8rz4HSJUP7hW0H2NlXiq8IFiWvYKEHhlotA==}
-
yocto-queue@0.1.0:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
@@ -3168,17 +3177,6 @@ snapshots:
multiformats: 9.9.0
zod: 3.23.8
- '@atproto/lex-cli@0.5.2':
- dependencies:
- '@atproto/lexicon': 0.4.3
- '@atproto/syntax': 0.3.1
- chalk: 4.1.2
- commander: 9.5.0
- prettier: 3.4.1
- ts-morph: 16.0.0
- yesno: 0.4.0
- zod: 3.23.8
-
'@atproto/lexicon@0.4.3':
dependencies:
'@atproto/common-web': 0.3.1
@@ -3811,13 +3809,6 @@ snapshots:
'@tootallnate/once@1.1.2': {}
- '@ts-morph/common@0.17.0':
- dependencies:
- fast-glob: 3.3.2
- minimatch: 5.1.6
- mkdirp: 1.0.4
- path-browserify: 1.0.1
-
'@tsconfig/svelte@5.0.4': {}
'@types/body-parser@1.19.5':
@@ -3825,6 +3816,10 @@ snapshots:
'@types/connect': 3.4.38
'@types/node': 22.10.1
+ '@types/child-process-promise@2.2.6':
+ dependencies:
+ '@types/node': 22.10.1
+
'@types/codemirror@5.60.8':
dependencies:
'@types/tern': 0.23.9
@@ -4222,6 +4217,12 @@ snapshots:
ansi-styles: 4.3.0
supports-color: 7.2.0
+ child-process-promise@2.2.1:
+ dependencies:
+ cross-spawn: 4.0.2
+ node-version: 1.2.0
+ promise-polyfill: 6.1.0
+
chokidar@3.6.0:
dependencies:
anymatch: 3.1.3
@@ -4243,8 +4244,6 @@ snapshots:
inherits: 2.0.4
safe-buffer: 5.2.1
- code-block-writer@11.0.3: {}
-
code-red@1.0.4:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0
@@ -4263,8 +4262,6 @@ snapshots:
dependencies:
delayed-stream: 1.0.0
- commander@9.5.0: {}
-
concat-map@0.0.1: {}
confbox@0.1.8: {}
@@ -4313,6 +4310,11 @@ snapshots:
dependencies:
'@types/node': 14.18.63
+ cross-spawn@4.0.2:
+ dependencies:
+ lru-cache: 4.1.5
+ which: 1.3.1
+
cross-spawn@7.0.6:
dependencies:
path-key: 3.1.1
@@ -4975,6 +4977,8 @@ snapshots:
jose@5.9.6: {}
+ js-base64@3.7.7: {}
+
js-yaml@4.1.0:
dependencies:
argparse: 2.0.1
@@ -5063,6 +5067,11 @@ snapshots:
lru-cache@11.0.2: {}
+ lru-cache@4.1.5:
+ dependencies:
+ pseudomap: 1.0.2
+ yallist: 2.1.2
+
magic-string@0.30.14:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0
@@ -5121,18 +5130,12 @@ snapshots:
dependencies:
brace-expansion: 1.1.11
- minimatch@5.1.6:
- dependencies:
- brace-expansion: 2.0.1
-
minimatch@9.0.5:
dependencies:
brace-expansion: 2.0.1
minipass@7.1.2: {}
- mkdirp@1.0.4: {}
-
mlly@1.7.3:
dependencies:
acorn: 8.14.0
@@ -5196,6 +5199,8 @@ snapshots:
util: 0.12.5
vm-browserify: 1.1.2
+ node-version@1.2.0: {}
+
normalize-path@3.0.0: {}
nwsapi@2.2.16: {}
@@ -5373,11 +5378,15 @@ snapshots:
process@0.11.10: {}
+ promise-polyfill@6.1.0: {}
+
proxy-addr@2.0.7:
dependencies:
forwarded: 0.2.0
ipaddr.js: 1.9.1
+ pseudomap@1.0.2: {}
+
psl@1.15.0:
dependencies:
punycode: 2.3.1
@@ -5730,11 +5739,6 @@ snapshots:
dependencies:
typescript: 5.8.0-dev.20241201
- ts-morph@16.0.0:
- dependencies:
- '@ts-morph/common': 0.17.0
- code-block-writer: 11.0.3
-
tsconfck@3.1.4(typescript@5.8.0-dev.20241201):
optionalDependencies:
typescript: 5.8.0-dev.20241201
@@ -5894,6 +5898,10 @@ snapshots:
gopd: 1.1.0
has-tostringtag: 1.0.2
+ which@1.3.1:
+ dependencies:
+ isexe: 2.0.0
+
which@2.0.2:
dependencies:
isexe: 2.0.0
@@ -5920,9 +5928,9 @@ snapshots:
xtend@4.0.2: {}
- yaml@2.6.1: {}
+ yallist@2.1.2: {}
- yesno@0.4.0: {}
+ yaml@2.6.1: {}
yocto-queue@0.1.0: {}
diff --git a/spa/index.html b/spa/index.html
index 6a8d093..21a5f5e 100644
--- a/spa/index.html
+++ b/spa/index.html
@@ -5,7 +5,7 @@
Svelte 4 + svelte-spa-router + TypeScript
-
+
diff --git a/spa/package.json b/spa/package.json
index 8a11fe3..0a6593d 100644
--- a/spa/package.json
+++ b/spa/package.json
@@ -11,12 +11,14 @@
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^3.1.2",
+ "@types/path-browserify": "^1.0.3",
"svelte-check": "^3.6.0",
"vite": "^5.4.11",
"vite-plugin-node-polyfills": "^0.22.0",
"vite-tsconfig-paths": "^5.1.3"
},
"dependencies": {
+ "path-browserify": "^1.0.1",
"svelte": "^4.2.19",
"svelte-spa-router": "^4.0.0"
}
diff --git a/spa/pnpm-lock.yaml b/spa/pnpm-lock.yaml
index 2ce337d..b681499 100644
--- a/spa/pnpm-lock.yaml
+++ b/spa/pnpm-lock.yaml
@@ -8,6 +8,9 @@ importers:
.:
dependencies:
+ path-browserify:
+ specifier: ^1.0.1
+ version: 1.0.1
svelte:
specifier: ^4.2.19
version: 4.2.19
@@ -18,6 +21,9 @@ importers:
'@sveltejs/vite-plugin-svelte':
specifier: ^3.1.2
version: 3.1.2(svelte@4.2.19)(vite@5.4.11(sass@1.82.0))
+ '@types/path-browserify':
+ specifier: ^1.0.3
+ version: 1.0.3
svelte-check:
specifier: ^3.6.0
version: 3.8.6(postcss@8.4.49)(sass@1.82.0)(svelte@4.2.19)
@@ -401,6 +407,9 @@ packages:
'@types/estree@1.0.6':
resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
+ '@types/path-browserify@1.0.3':
+ resolution: {integrity: sha512-ZmHivEbNCBtAfcrFeBCiTjdIc2dey0l7oCGNGpSuRTy8jP6UVND7oUowlvDujBy8r2Hoa8bfFUOCiPWfmtkfxw==}
+
'@types/pug@2.0.10':
resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==}
@@ -1442,6 +1451,8 @@ snapshots:
'@types/estree@1.0.6': {}
+ '@types/path-browserify@1.0.3': {}
+
'@types/pug@2.0.10': {}
acorn@8.14.0: {}
diff --git a/spa/src/api-client.ts b/spa/src/api-client.ts
index e1cc9b2..19a14e8 100644
--- a/spa/src/api-client.ts
+++ b/spa/src/api-client.ts
@@ -57,8 +57,7 @@ export class ApiClient {
const contents = await downloadFileContents(this.did, this.pdsAgent, file);
return {
- vaultName: file.vaultName,
- filePath: file.filePath,
+ ...file,
contents,
uri,
mimeType: file.body.mimeType,
diff --git a/spa/src/routes/Page.svelte b/spa/src/routes/Page.svelte
index 0ebba73..56e7d94 100644
--- a/spa/src/routes/Page.svelte
+++ b/spa/src/routes/Page.svelte
@@ -1,5 +1,7 @@
{#await filePromise}
+
Loading file from repo @{handle}
{:then file}
-
{file.filePath}
- {#if file.textContent}
-
- {file.textContent}
+ {#if 'html' in file && file.html}
+
+
+
{path.basename(file.filePath, '.md')}
+
+
+ {:else if textContent}
+
+
{path.basename(file.filePath, '.md')}
+
+ {textContent}
+
{/if}
{:catch err}
@@ -37,11 +105,11 @@
diff --git a/spa/src/styles/obsidian.scss b/spa/src/styles/obsidian.scss
new file mode 100644
index 0000000..502fe5f
--- /dev/null
+++ b/spa/src/styles/obsidian.scss
@@ -0,0 +1,225 @@
+.notion-root {
+ & {
+ text-rendering: optimizeLegibility;
+ font-family: var(--default-font);
+ line-height: 1.5em;
+ font-size: 16px;
+ background-color: var(--background-primary);
+ color: var(--text-normal);
+ }
+
+ pre,
+ code {
+ font-family: var(--font-monospace);
+ }
+
+ h1,
+ h2,
+ h3,
+ h4,
+ h5,
+ h6 {
+ font-weight: 800;
+ }
+
+ a {
+ color: var(--text-accent);
+ outline: none;
+ }
+
+ a:hover {
+ color: var(--text-accent-hover);
+ }
+
+ audio {
+ outline: none;
+ }
+
+ hr {
+ border: none;
+ border-top: 1px solid;
+ border-color: var(--background-modifier-border);
+ margin: 26px 0;
+ }
+
+ * {
+ box-sizing: border-box;
+ }
+
+ ul ul,
+ ol ul,
+ ol ul,
+ ul ol {
+ list-style-type: disc;
+ }
+
+ /* PrismJS 1.20.0
+ https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+abap+abnf+actionscript+ada+al+antlr4+apacheconf+apl+applescript+aql+arduino+arff+asciidoc+asm6502+aspnet+autohotkey+autoit+bash+basic+batch+bbcode+bison+bnf+brainfuck+brightscript+bro+c+concurnas+csharp+cpp+cil+coffeescript+cmake+clojure+crystal+csp+css-extras+d+dart+dax+diff+django+dns-zone-file+docker+ebnf+eiffel+ejs+elixir+elm+etlua+erb+erlang+excel-formula+fsharp+factor+firestore-security-rules+flow+fortran+ftl+gcode+gdscript+gedcom+gherkin+git+glsl+gml+go+graphql+groovy+haml+handlebars+haskell+haxe+hcl+hlsl+http+hpkp+hsts+ichigojam+icon+iecst+inform7+ini+io+j+java+javadoc+javadoclike+javastacktrace+jolie+jq+jsdoc+js-extras+js-templates+json+jsonp+json5+julia+keyman+kotlin+latex+latte+less+lilypond+liquid+lisp+livescript+llvm+lolcode+lua+makefile+markdown+markup-templating+matlab+mel+mizar+monkey+moonscript+n1ql+n4js+nand2tetris-hdl+nasm+neon+nginx+nim+nix+nsis+objectivec+ocaml+opencl+oz+parigp+parser+pascal+pascaligo+pcaxis+peoplecode+perl+php+phpdoc+php-extras+plsql+powerquery+powershell+processing+prolog+properties+protobuf+pug+puppet+pure+purebasic+python+q+qml+qore+r+racket+jsx+tsx+renpy+reason+regex+rest+rip+roboconf+robotframework+ruby+rust+sas+sass+scss+scala+scheme+shell-session+smalltalk+smarty+solidity+solution-file+soy+sparql+splunk-spl+sqf+sql+stylus+swift+tap+tcl+textile+toml+tt2+turtle+twig+typescript+t4-cs+t4-vb+t4-templating+unrealscript+vala+vbnet+velocity+verilog+vhdl+vim+visual-basic+warpscript+wasm+wiki+xeora+xml-doc+xojo+xquery+yaml+zig */
+ /**
+ * prism.js default theme for JavaScript, CSS and HTML
+ * Based on dabblet (http://dabblet.com)
+ * @author Lea Verou
+ */
+ /* Code blocks */
+ /* Inline code */
+ code[class*="language-"],
+ pre[class*="language-"] {
+ color: black;
+ background: none;
+ text-shadow: 0 1px white;
+ font-family: var(--font-monospace);
+ text-align: left;
+ white-space: pre;
+ word-spacing: normal;
+ word-break: normal;
+ word-wrap: normal;
+ line-height: 1.5;
+ -moz-tab-size: 4;
+ -o-tab-size: 4;
+ tab-size: 4;
+ -webkit-hyphens: none;
+ -moz-hyphens: none;
+ -ms-hyphens: none;
+ hyphens: none;
+ }
+
+ pre[class*="language-"]::-moz-selection,
+ pre[class*="language-"] ::-moz-selection,
+ code[class*="language-"]::-moz-selection,
+ code[class*="language-"] ::-moz-selection {
+ text-shadow: none;
+ background: #b3d4fc;
+ }
+
+ pre[class*="language-"]::selection,
+ pre[class*="language-"] ::selection,
+ code[class*="language-"]::selection,
+ code[class*="language-"] ::selection {
+ text-shadow: none;
+ background: #b3d4fc;
+ }
+
+ @media print {
+
+ code[class*="language-"],
+ pre[class*="language-"] {
+ text-shadow: none;
+ }
+ }
+
+ pre[class*="language-"] {
+ padding: 1em;
+ margin: 0.5em 0;
+ overflow: auto;
+ }
+
+ :not(pre)>code[class*="language-"],
+ pre[class*="language-"] {
+ background: #f5f2f0;
+ }
+
+ :not(pre)>code[class*="language-"] {
+ padding: 0.1em;
+ border-radius: 0.3em;
+ white-space: normal;
+ }
+
+ .token.comment,
+ .token.prolog,
+ .token.doctype,
+ .token.cdata {
+ color: slategray;
+ }
+
+ .token.punctuation {
+ color: #999;
+ }
+
+ .token.namespace {
+ opacity: 0.7;
+ }
+
+ .token.property,
+ .token.tag,
+ .token.boolean,
+ .token.number,
+ .token.constant,
+ .token.symbol,
+ .token.deleted {
+ color: #905;
+ }
+
+ .token.selector,
+ .token.attr-name,
+ .token.string,
+ .token.char,
+ .token.builtin,
+ .token.inserted {
+ color: #690;
+ }
+
+ .token.operator,
+ .token.entity,
+ .token.url,
+ .language-css .token.string,
+ .style .token.string {
+ color: #9a6e3a;
+ background: hsla(0, 0%, 100%, 0.5);
+ }
+
+ .token.atrule,
+ .token.attr-value,
+ .token.keyword {
+ color: #07a;
+ }
+
+ .token.function,
+ .token.class-name {
+ color: #DD4A68;
+ }
+
+ .token.regex,
+ .token.important,
+ .token.variable {
+ color: #e90;
+ }
+
+ .token.important,
+ .token.bold {
+ font-weight: bold;
+ }
+
+ .token.italic {
+ font-style: italic;
+ }
+
+ .token.entity {
+ cursor: help;
+ }
+}
+
+:root {
+ --default-font: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Microsoft YaHei Light", sans-serif;
+ --font-monospace: 'Source Code Pro', monospace;
+ --background-primary: #ffffff;
+ --background-modifier-border: #ddd;
+ --text-accent: #705dcf;
+ --text-accent-hover: #7a6ae6;
+ --text-normal: #2e3338;
+ --background-secondary: #f2f3f5;
+ --background-secondary-alt: #e3e5e8;
+ --text-muted: #888888;
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --background-primary: #202020;
+ --background-modifier-border: #333;
+ --text-accent: #7f6df2;
+ --text-accent-hover: #8875ff;
+ --text-normal: #dcddde;
+ --background-secondary: #161616;
+ --background-secondary-alt: #000000;
+ --text-muted: #999;
+ }
+}
diff --git a/spa/src/styles/style.scss b/spa/src/styles/style.scss
new file mode 100644
index 0000000..8820ee1
--- /dev/null
+++ b/spa/src/styles/style.scss
@@ -0,0 +1,14 @@
+@use 'obsidian.scss';
+@use 'vanilla.scss';
+
+/* Reset */
+html, body {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ vertical-align: baseline;
+}
+
+* {
+ box-sizing: border-box;
+}
diff --git a/spa/src/styles/vanilla.css b/spa/src/styles/vanilla.css
deleted file mode 100644
index b1bcc86..0000000
--- a/spa/src/styles/vanilla.css
+++ /dev/null
@@ -1,212 +0,0 @@
-/*!
-https://git.sr.ht/~bt/vanilla-css
-
-MIT License
-
-Copyright (c) 2018 Bradley Taunt
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-/* Reset */
-html, body, div, span, applet, object, iframe,
-h1, h2, h3, h4, h5, h6, p, blockquote, pre,
-a, abbr, acronym, address, big, cite, code,
-del, dfn, em, img, ins, kbd, q, s, samp,
-small, strike, strong, sub, sup, tt, var,
-b, u, i, center,
-dl, dt, dd, ol, ul, li,
-fieldset, form, label, legend,
-table, caption, tbody, tfoot, thead, tr, th, td,
-article, aside, canvas, details, embed,
-figure, figcaption, footer, header, hgroup,
-menu, nav, output, ruby, section, summary,
-time, mark, audio, video {
- margin: 0;
- padding: 0;
- border: 0;
- font-size: 100%;
- font: inherit;
- vertical-align: baseline;
-}
-
-* {
- box-sizing: border-box;
-}
-
-/* Variables */
-:root {
- --desktop-font-size: 1.2rem/1.55;
- --mobile-font-size: 1rem/1.45;
- --text-color: #2d2d2d;
- --link-color: blue;
- --link-color-alt: darkblue;
- --primary-color: lightsteelblue;
- --secondary-color: aliceblue;
- --tertiary-color: whitesmoke;
-}
-
-/* Typography */
-body {
- color: var(--text-color);
- margin: 0 auto;
- max-width: 75ch;
- padding: 0 0.5rem;
-}
-body, input {
- font: var(--desktop-font-size) -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto, Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji", "Segoe UI Symbol";
-}
-
-h1,h2,h3,h4,h5,h6,p,blockquote,dl,img,figure {
- margin: 2rem 0;
-}
-
-h1,h2,h3,h4,h5,h6 { font-weight: bold; line-height: 1.2; }
-h1 { font-size: 200%; }
-h2 { font-size: 150%; }
-h3 { font-size: 120%; }
-h4,h5,h6 { font-size: 100%; }
-h5, h6 { text-transform: uppercase; }
-
-header h1 { border-bottom: 1px solid; }
-
-a,a:visited { color: var(--link-color); }
-a:hover,a:focus { color: var(--link-color-alt); }
-
-strong, time, b { font-weight: bold; }
-em, dfn, i { font-style: italic; }
-sub { font-size: 60%; vertical-align: bottom; }
-small { font-size: 80%; }
-
-blockquote, q {
- background: var(--secondary-color);
- border-left: 10px solid var(--primary-color);
- display: block;
- font-family: "Georgia", serif;
- padding: 1rem;
-}
-blockquote p:first-child { margin-top: 0; }
-blockquote p:last-child { margin-bottom: 0; }
-cite {
- font-family: "Georgia", serif;
- font-style: italic;
- font-weight: bold;
- margin-top: 1rem;
-}
-
-kbd,code,samp,pre,var { font: var(--mobile-font-size) monospace; }
-code, pre {
- background: var(--tertiary-color);
- border: 1px solid;
- overflow: auto;
- padding: 0.25rem 0.5rem;
-}
-code pre , pre code { border: 0; padding: 0; }
-
-/* Elements */
-hr {
- background: var(--text-color);
- border: 0;
- height: 1px;
- margin: 4rem 0;
-}
-
-img {
- display: block;
- height: auto;
- max-width: 100%;
-}
-
-figure {
- border: 1px solid var(--primary-color);
- display: inline-block;
- padding: 1rem;
- width: 100%;
-}
-figure img { margin: 0 auto; }
-figure figcaption { font-size: 80%; margin-top: 0.5rem; text-align: center; }
-
-ul, ol { margin: 2rem 0; padding: 0 0 0 2rem; }
-ul li, ol li { margin-bottom: 1rem; }
-li > ul, li > ol { margin: 0.25rem 0 0.5rem; padding: 0 0 0 2rem; }
-li > ul li, li > ol li { margin-bottom: 0.5rem; }
-
-dl dd { padding-left: 2rem; }
-
-table {
- border: 1px solid var(--primary-color);
- border-collapse: collapse;
- table-layout: fixed;
- text-align: left;
- width: 100%;
-}
-table caption { margin: 2rem 0; }
-table tr { border-bottom: 1px solid var(--primary-color); }
-table thead { position: sticky; top: 0; }
-table tbody tr:nth-child(even) { background: var(--tertiary-color); }
-table th { background: var(--secondary-color); font-weight: bold; }
-table th, table td { padding: 0.5rem; }
-
-input {
- appearance: none;
- border: 1px solid var(--text-color);
- display: block;
- margin: 0.5rem 0;
- padding: 0.8rem;
-}
-input:focus, input:active { background-color: var(--secondary-color); border-color: var(--link-color); }
-
-sup { font-size: 80%; vertical-align: top; }
-
-/* Mobile Styling */
-@media screen and (max-width: 75ch) {
- body, input {
- font: var(--mobile-font-size) -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto, Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji", "Segoe UI Symbol";
- }
- table { table-layout: auto; }
-}
-
-/* Dark mode support */
-@media (prefers-color-scheme: dark) {
- body {
- background: #191919;
- }
- input:focus, input:active {
- background-color: var(--text-color);
- color: var(--secondary-color);
- }
- table,
- table tr,
- table th:not(:last-of-type),
- table td:not(:last-of-type) {
- border-color: var(--text-color);
- }
- table thead th,
- table tfoot th {
- background-color: var(--primary-color-light);
- }
- :root {
- --text-color: #fff;
- --link-color: orange;
- --link-color-alt: yellow;
- --primary-color: orange;
- --primary-color-light: dimgrey;
- --secondary-color: black;
- --tertiary-color: #2d2d2d;
- }
-}
\ No newline at end of file
diff --git a/spa/src/styles/vanilla.scss b/spa/src/styles/vanilla.scss
new file mode 100644
index 0000000..c992210
--- /dev/null
+++ b/spa/src/styles/vanilla.scss
@@ -0,0 +1,222 @@
+/*!
+https://git.sr.ht/~bt/vanilla-css
+
+MIT License
+
+Copyright (c) 2018 Bradley Taunt
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+/* Typography */
+.vanilla-root {
+ & {
+ color: var(--text-color);
+ margin: 0 auto;
+ max-width: 75ch;
+ padding: 0 0.5rem;
+ font: var(--desktop-font-size) -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto, Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji", "Segoe UI Symbol";
+ }
+
+ /* Reset */
+ html, body, div, span, applet, object, iframe,
+ h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+ a, abbr, acronym, address, big, cite, code,
+ del, dfn, em, img, ins, kbd, q, s, samp,
+ small, strike, strong, sub, sup, tt, var,
+ b, u, i, center,
+ dl, dt, dd, ol, ul, li,
+ fieldset, form, label, legend,
+ table, caption, tbody, tfoot, thead, tr, th, td,
+ article, aside, canvas, details, embed,
+ figure, figcaption, footer, header, hgroup,
+ menu, nav, output, ruby, section, summary,
+ time, mark, audio, video {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-size: 100%;
+ font: inherit;
+ vertical-align: baseline;
+ }
+
+ * {
+ box-sizing: border-box;
+ }
+
+ input {
+ font: var(--desktop-font-size) -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto, Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji", "Segoe UI Symbol";
+ }
+
+ h1,h2,h3,h4,h5,h6,p,blockquote,dl,img,figure {
+ margin: 2rem 0;
+ }
+
+ h1,h2,h3,h4,h5,h6 { font-weight: bold; line-height: 1.2; }
+ h1 { font-size: 200%; }
+ h2 { font-size: 150%; }
+ h3 { font-size: 120%; }
+ h4,h5,h6 { font-size: 100%; }
+ h5, h6 { text-transform: uppercase; }
+
+ header h1 { border-bottom: 1px solid; }
+
+ a,a:visited { color: var(--link-color); }
+ a:hover,a:focus { color: var(--link-color-alt); }
+
+ strong, time, b { font-weight: bold; }
+ em, dfn, i { font-style: italic; }
+ sub { font-size: 60%; vertical-align: bottom; }
+ small { font-size: 80%; }
+
+ blockquote, q {
+ background: var(--secondary-color);
+ border-left: 10px solid var(--primary-color);
+ display: block;
+ font-family: "Georgia", serif;
+ padding: 1rem;
+ }
+ blockquote p:first-child { margin-top: 0; }
+ blockquote p:last-child { margin-bottom: 0; }
+ cite {
+ font-family: "Georgia", serif;
+ font-style: italic;
+ font-weight: bold;
+ margin-top: 1rem;
+ }
+
+ kbd,code,samp,pre,var { font: var(--mobile-font-size) monospace; }
+ code, pre {
+ background: var(--tertiary-color);
+ border: 1px solid;
+ overflow: auto;
+ padding: 0.25rem 0.5rem;
+ }
+ code pre , pre code { border: 0; padding: 0; }
+
+ /* Elements */
+ hr {
+ background: var(--text-color);
+ border: 0;
+ height: 1px;
+ margin: 4rem 0;
+ }
+
+ img {
+ display: block;
+ height: auto;
+ max-width: 100%;
+ }
+
+ figure {
+ border: 1px solid var(--primary-color);
+ display: inline-block;
+ padding: 1rem;
+ width: 100%;
+ }
+ figure img { margin: 0 auto; }
+ figure figcaption { font-size: 80%; margin-top: 0.5rem; text-align: center; }
+
+ ul, ol { margin: 2rem 0; padding: 0 0 0 2rem; }
+ ul li, ol li { margin-bottom: 1rem; }
+ li > ul, li > ol { margin: 0.25rem 0 0.5rem; padding: 0 0 0 2rem; }
+ li > ul li, li > ol li { margin-bottom: 0.5rem; }
+
+ dl dd { padding-left: 2rem; }
+
+ table {
+ border: 1px solid var(--primary-color);
+ border-collapse: collapse;
+ table-layout: fixed;
+ text-align: left;
+ width: 100%;
+ }
+ table caption { margin: 2rem 0; }
+ table tr { border-bottom: 1px solid var(--primary-color); }
+ table thead { position: sticky; top: 0; }
+ table tbody tr:nth-child(even) { background: var(--tertiary-color); }
+ table th { background: var(--secondary-color); font-weight: bold; }
+ table th, table td { padding: 0.5rem; }
+
+ input {
+ appearance: none;
+ border: 1px solid var(--text-color);
+ display: block;
+ margin: 0.5rem 0;
+ padding: 0.8rem;
+ }
+ input:focus, input:active { background-color: var(--secondary-color); border-color: var(--link-color); }
+
+ sup { font-size: 80%; vertical-align: top; }
+
+ /* Mobile Styling */
+ @media screen and (max-width: 75ch) {
+ body, input {
+ font: var(--mobile-font-size) -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto, Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji", "Segoe UI Symbol";
+ }
+ table { table-layout: auto; }
+ }
+
+ /* Dark mode support */
+ @media (prefers-color-scheme: dark) {
+ body {
+ background: #191919;
+ }
+ input:focus, input:active {
+ background-color: var(--text-color);
+ color: var(--secondary-color);
+ }
+ table,
+ table tr,
+ table th:not(:last-of-type),
+ table td:not(:last-of-type) {
+ border-color: var(--text-color);
+ }
+ table thead th,
+ table tfoot th {
+ background-color: var(--primary-color-light);
+ }
+ }
+}
+
+:root {
+ --desktop-font-size: 1.2rem/1.55;
+ --mobile-font-size: 1rem/1.45;
+ --text-color: #2d2d2d;
+ --link-color: blue;
+ --link-color-alt: darkblue;
+ --primary-color: lightsteelblue;
+ --secondary-color: aliceblue;
+ --tertiary-color: whitesmoke;
+ --bg-color: white;
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --text-color: #fff;
+ --link-color: orange;
+ --link-color-alt: yellow;
+ --primary-color: orange;
+ --primary-color-light: dimgrey;
+ --secondary-color: black;
+ --tertiary-color: #2d2d2d;
+
+ --bg-color: #191919;
+ }
+}
\ No newline at end of file
diff --git a/src/index.tsx b/src/index.tsx
index b0461b7..e9f8ed4 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -161,7 +161,10 @@ export default class MyPlugin extends Plugin {
return allResolvedLinks;
};
- const allResolvedLinks = recurseLinks(this.app.metadataCache.resolvedLinks[activeFile.path], {});
+ const allResolvedLinks = recurseLinks(
+ this.app.metadataCache.resolvedLinks[activeFile.path],
+ {[activeFile.path]: 1}
+ );
const settings = this.settings;
class VerifyPromptModal extends Modal {
@@ -176,7 +179,6 @@ export default class MyPlugin extends Plugin {
This will make the following files PUBLICALLY VISIBLE ON THE AT PROTOCOL:
- - {activeFile!.path}: Main file
{Object.entries(allResolvedLinks).map(([k, v]) =>
- {k}: Linked to {v} times
)}
@@ -215,7 +217,7 @@ export default class MyPlugin extends Plugin {
reject();
}).open();
}).then(async () => {
- await doShare(await this.agent, this.app, this.settings, Object.keys(allResolvedLinks));
+ await doShare(await this.agent, this.app, this, this.settings, Object.keys(allResolvedLinks));
});
}
});
diff --git a/src/lexicon/index.ts b/src/lexicon/index.ts
index 025070e..5ac4ab5 100644
--- a/src/lexicon/index.ts
+++ b/src/lexicon/index.ts
@@ -172,6 +172,8 @@ export declare namespace IoGithubObsidatPublicFile {
description?: string;
/** The Markdown frontmatter, serialized as an array of keyValuePair objects. Will be `undefined` for non-Markdown or non-textual files. */
frontmatter?: IoGithubObsidatGeneric.KeyValuePair[];
+ /** Rendered HTML contents of the file, for Markdown files. */
+ html?: string;
/** A list of pages this page links to, alongside the link count. */
resolvedLinks?: IoGithubObsidatPublicFile.PageAndLinkCount[];
/** Tags for the file, if any. */
diff --git a/src/lexicon/lexicons.ts b/src/lexicon/lexicons.ts
index 54ef69b..189d5c8 100644
--- a/src/lexicon/lexicons.ts
+++ b/src/lexicon/lexicons.ts
@@ -122,6 +122,8 @@ declare module "@atcute/client/lexicons" {
description?: string;
/** The Markdown frontmatter, serialized as an array of keyValuePair objects. Will be `undefined` for non-Markdown or non-textual files. */
frontmatter?: IoGithubObsidatGeneric.KeyValuePair[];
+ /** Rendered HTML contents of the file, for Markdown files. */
+ html?: string;
/** A list of pages this page links to, alongside the link count. */
resolvedLinks?: IoGithubObsidatPublicFile.PageAndLinkCount[];
/** Tags for the file, if any. */
diff --git a/src/markdown-renderer/renderer.ts b/src/markdown-renderer/renderer.ts
new file mode 100644
index 0000000..81dd01d
--- /dev/null
+++ b/src/markdown-renderer/renderer.ts
@@ -0,0 +1,332 @@
+
+/*
+ * renderer.ts
+ *
+ * This module exposes a function that turns an Obsidian markdown string into
+ * an HTML string with as many inconsistencies ironed out as possible
+ *
+ */
+
+import * as path from 'path';
+import * as fs from 'fs';
+import * as YAML from 'yaml';
+import { Base64 } from 'js-base64';
+
+import { App, FileSystemAdapter, MarkdownRenderer, MarkdownView, Notice } from 'obsidian';
+
+import mathJaxFontCSS from './styles/mathjax-css';
+import appCSS, { variables as appCSSVariables } from './styles/app-css';
+import MyPlugin from '..';
+
+export interface PandocPluginSettings {
+ // Show a command like `pandoc -o Output.html -t html -f commonmark Input.md`
+ // in the UI as an example of how to do something similar in the terminal
+ // showCLICommands: boolean;
+ // When rendering internal [[wikilinks]], should we add a file extension?
+ // eg turns to
+ addExtensionsToInternalLinks: string,
+ // Which default theme's CSS do we inject? (we only use a tiny subset - variables, basic styling)
+ injectAppCSS: 'current' | 'none' | 'light' | 'dark',
+ // Do we inject 3rd party theme CSS as well as the app's default CSS?
+ // injectThemeCSS: boolean,
+ // Use a custom local .css file?
+ customCSSFile: string | null,
+ // Do we want to display the YAML frontmatter in the output?
+ displayYAMLFrontmatter: boolean,
+ // Do we strip [[wikilinks]] entirely, turn them into normal text, or leave them as links, or leave them as raw [[wikilinks]]?
+ linkStrippingBehaviour: 'strip' | 'text' | 'link' | 'unchanged',
+ // Do we render SVGs at 2x the size?
+ highDPIDiagrams: boolean,
+ // Custom Pandoc & LaTeX binary paths (useful for PATH variable issues)
+ // pandoc: string | null,
+ // pdflatex: string | null,
+ // Output folder - if unspecified exports are saved next to where they were exported from
+ // The path is absolute
+ // outputFolder: string | null,
+ // Extra CLI arguments for Pandoc to support features we don't have a UI for yet
+ // extraArguments: string,
+ // Export from HTML or from markdown?
+ // exportFrom: 'html' | 'md',
+}
+
+export class MyMarkdownRenderer {
+ constructor(
+ private app: App,
+ private plugin: MyPlugin,
+ private settings: PandocPluginSettings,
+ ) {}
+
+ vaultBasePath(): string {
+ return (this.app.vault.adapter as FileSystemAdapter).getBasePath();
+ }
+
+ // Note: parentFiles is for internal use (to prevent recursively embedded notes)
+ // inputFile must be an absolute file path
+ async render(
+ view: MarkdownView, markdown: string, inputFile: string, outputFormat: string = 'html', parentFiles: string[] = []
+ ): Promise<{ html: string, metadata: { [index: string]: string } }>
+ {
+ // Use Obsidian's markdown renderer to render to a hidden
+ const wrapper = document.createElement('div');
+ wrapper.style.display = 'hidden';
+ document.body.appendChild(wrapper);
+ await MarkdownRenderer.render(this.app, markdown, wrapper, path.dirname(inputFile), view);
+
+ // Post-process the HTML in-place
+ await this.postProcessRenderedHTML(inputFile, wrapper, outputFormat,
+ parentFiles, await this.mermaidCSS(this.vaultBasePath()));
+
+ let html = wrapper.innerHTML;
+ document.body.removeChild(wrapper);
+
+ // If it's a top level note, make the HTML a standalone document - inject CSS, a
, etc.
+ const metadata = this.getYAMLMetadata(markdown);
+ metadata.title ??= this.fileBaseName(inputFile);
+ if (parentFiles.length === 0) {
+ html = await this.standaloneHTML(html, metadata.title, this.vaultBasePath());
+ }
+
+ return { html, metadata };
+ }
+
+ // Takes any file path like '/home/oliver/zettelkasten/Obsidian.md' and
+ // takes the base name, in this case 'Obsidian'
+ fileBaseName(file: string): string {
+ return path.basename(file, path.extname(file));
+ }
+
+ getYAMLMetadata(markdown: string) {
+ markdown = markdown.trim();
+ if (markdown.startsWith('---')) {
+ const trailing = markdown.substring(3);
+ const frontmatter = trailing.substring(0, trailing.indexOf('---')).trim();
+ return YAML.parse(frontmatter);
+ }
+ return {};
+ }
+
+ async getCustomCSS(vaultBasePath: string): Promise {
+ if (!this.settings.customCSSFile) return '';
+ let file = this.settings.customCSSFile;
+
+ let buffer: Buffer | null = null;
+
+ // Try absolute path
+ try {
+ let test = await fs.promises.readFile(file);
+ buffer = test;
+ } catch(e) { }
+
+ // Try relative path
+ try {
+ let test = await fs.promises.readFile(path.join(vaultBasePath, file));
+ buffer = test;
+ } catch(e) { }
+
+ if(!buffer) {
+ new Notice('Failed to load custom Pandoc CSS file: ' + this.settings.customCSSFile);
+ return '';
+ } else {
+ return buffer.toString();
+ }
+ }
+
+ async getAppConfig(vaultBasePath: string): Promise {
+ return JSON.parse((await fs.promises.readFile(path.join(vaultBasePath, '.obsidian', 'config'))).toString());
+ }
+
+ async currentThemeIsLight(vaultBasePath: string, config: any = null): Promise {
+ try {
+ if (!config) config = await this.getAppConfig(vaultBasePath);
+ return config.theme !== 'obsidian';
+ } catch (e) {
+ return true;
+ }
+ }
+
+ async mermaidCSS(vaultBasePath: string): Promise {
+ // We always inject CSS into Mermaid diagrams, using light theme if the user has requested no CSS
+ // otherwise the diagrams look terrible. The output is a PNG either way
+ let light = true;
+ if (this.settings.injectAppCSS === 'dark') light = false;
+ if (this.settings.injectAppCSS === 'current') {
+ light = await this.currentThemeIsLight(vaultBasePath);
+ }
+ return appCSSVariables(light);
+ }
+
+ // Gets a small subset of app CSS and 3rd party theme CSS if desired
+ async getThemeCSS(vaultBasePath: string): Promise {
+ if (this.settings.injectAppCSS === 'none') return '';
+ try {
+ const config = await this.getAppConfig(vaultBasePath);
+ let light = await this.currentThemeIsLight(vaultBasePath, config);
+ if (this.settings.injectAppCSS === 'light') light = true;
+ if (this.settings.injectAppCSS === 'dark') light = false;
+ return appCSS(light);
+ } catch (e) {
+ return '';
+ }
+ }
+
+ async getDesiredCSS(html: string, vaultBasePath: string): Promise {
+ let css = await this.getThemeCSS(vaultBasePath);
+ if (this.settings.injectAppCSS !== 'none') {
+ css += ' ' + Array.from(document.querySelectorAll('style'))
+ .map(s => s.innerHTML).join(' ');
+ }
+ // Inject MathJax font CSS if needed (at this stage embedded notes are
+ // already embedded so doesn't duplicate CSS)
+ if (html.indexOf('jax="CHTML"') !== -1)
+ css += ' ' + mathJaxFontCSS;
+ // Inject custom local CSS file if it exists
+ css += await this.getCustomCSS(vaultBasePath);
+ return css;
+ }
+
+ async standaloneHTML(html: string, title: string, vaultBasePath: string): Promise {
+ // Wraps an HTML fragment in a proper document structure
+ // and injects the page's CSS
+ const css = await this.getDesiredCSS(html, vaultBasePath);
+
+ return `\n` +
+ `\n` +
+ ` \n` +
+ ` ${title}\n` +
+ ` \n` +
+ ` \n` +
+ ` \n` +
+ ` \n` +
+ `${html}\n` +
+ ` \n` +
+ ``;
+ }
+
+ async postProcessRenderedHTML(
+ inputFile: string, wrapper: HTMLElement, outputFormat: string, parentFiles: string[] = [], css: string = ''
+ )
+ {
+ const dirname = path.dirname(inputFile);
+ const adapter = this.plugin.app.vault.adapter as FileSystemAdapter;
+
+ // Fix
+ for (let span of Array.from(wrapper.querySelectorAll('span[src$=".png"], span[src$=".jpg"], span[src$=".gif"], span[src$=".jpeg"]'))) {
+ span.innerHTML = '';
+ span.outerHTML = span.outerHTML.replace(/span/g, 'img');
+ }
+ // Fix
+ for (let span of Array.from(wrapper.querySelectorAll('span.internal-embed'))) {
+ let src = span.getAttribute('src');
+ if (src) {
+ const subfolder = inputFile.substring(adapter.getBasePath().length); // TODO: this is messy
+ const file = this.plugin.app.metadataCache.getFirstLinkpathDest(src, subfolder)!;
+ try {
+ if (parentFiles.indexOf(file.path) !== -1) {
+ // We've got an infinite recursion on our hands
+ // We should replace the embed with a wikilink
+ // Then our link processing happens afterwards
+ span.outerHTML = `${span.innerHTML}`;
+ } else {
+ const markdown = await adapter.read(file.path);
+ const newParentFiles = [...parentFiles];
+ newParentFiles.push(inputFile);
+ // TODO: because of this cast, embedded notes won't be able to handle complex plugins (eg DataView)
+ const html = await this.render({ data: markdown } as MarkdownView, markdown, file.path, outputFormat, newParentFiles);
+ span.outerHTML = html.html;
+ }
+ } catch (e) {
+ // Continue if it can't be loaded
+ console.error(`Pandoc plugin encountered an error trying to load an embedded note: ${e}`);
+ }
+ }
+ }
+
+ // Fix
+ const prefix = 'app://obsidian.md/';
+ for (let a of Array.from(wrapper.querySelectorAll('a'))) {
+ if (!a.href.startsWith(prefix)) continue;
+ // This is now an internal link (wikilink)
+ if (this.settings.linkStrippingBehaviour === 'link' || outputFormat === 'html') {
+ let href = path.join(dirname, a.href.substring(prefix.length));
+ if (this.settings.addExtensionsToInternalLinks.length && a.href.startsWith(prefix)) {
+ if (path.extname(href) === '') {
+ const dir = path.dirname(href);
+ const base = path.basename(href);
+ // Be careful to turn [[note#heading]] into note.extension#heading not note#heading.extension
+ const hashIndex = base.indexOf('#');
+ if (hashIndex !== -1) {
+ href = path.join(dir, base.substring(0, hashIndex) + '.' + this.settings.addExtensionsToInternalLinks + base.substring(hashIndex));
+ } else {
+ href = path.join(dir, base + '.' + this.settings.addExtensionsToInternalLinks);
+ }
+ }
+ }
+ a.href = href;
+ } else if (this.settings.linkStrippingBehaviour === 'strip') {
+ a.outerHTML = '';
+ } else if (this.settings.linkStrippingBehaviour === 'text') {
+ a.outerHTML = a.innerText;
+ } else if (this.settings.linkStrippingBehaviour === 'unchanged') {
+ a.outerHTML = `[[${a.outerHTML}]]`;
+ }
+ }
+
+ // Fix
+ // Note: this will throw errors when Obsidian tries to load images with a (now invalid) src
+ // These errors can be safely ignored
+ if (outputFormat !== 'html') {
+ for (let img of Array.from(wrapper.querySelectorAll('img'))) {
+ if (img.src.startsWith(prefix) && img.getAttribute('data-touched') !== 'true') {
+ img.src = adapter.getFullPath(img.src.substring(prefix.length));
+ img.setAttribute('data-touched', 'true');
+ }
+ }
+ }
+
+ // Remove YAML frontmatter from the output if desired
+ if (!this.settings.displayYAMLFrontmatter) {
+ Array.from(wrapper.querySelectorAll('.frontmatter, .frontmatter-container'))
+ .forEach(el => wrapper.removeChild(el));
+ }
+
+ // Fix Mermaid.js diagrams
+ for (let svg of Array.from(wrapper.querySelectorAll('svg'))) {
+ // Insert the CSS variables as a CSS string (even if the user doesn't want CSS injected; Mermaid diagrams look terrible otherwise)
+ // TODO: it injects light theme CSS, do we want this?
+ let style: HTMLStyleElement = svg.querySelector('style') || svg.appendChild(document.createElement('style'));
+ style.innerHTML += css;
+ // Inject a marker (arrowhead) for Mermaid.js diagrams and use it at the end of paths
+ svg.innerHTML += `""`;
+ svg.innerHTML = svg.innerHTML.replace(/app:\/\/obsidian\.md\/index\.html#arrowhead\d*/g, "#mermaid_arrowhead");
+ // If the output isn't HTML, replace the SVG with a PNG for compatibility
+ if (outputFormat !== 'html') {
+ const scale = this.settings.highDPIDiagrams ? 2 : 1;
+ const png = await this.convertSVGToPNG(svg, scale);
+ svg.parentNode!.replaceChild(png, svg);
+ }
+ }
+ }
+
+ // This creates an unmounted element with a transparent background PNG data URL as the src
+ // The scale parameter is used for high DPI renders (the element size is the same,
+ // but the underlying PNG is higher resolution)
+ convertSVGToPNG(svg: SVGSVGElement, scale: number = 1): Promise {
+ const canvas = document.createElement('canvas');
+ canvas.width = Math.ceil(svg.width.baseVal.value * scale);
+ canvas.height = Math.ceil(svg.height.baseVal.value * scale);
+ const ctx = canvas.getContext('2d')!;
+ var svgImg = new Image();
+ svgImg.src = `data:image/svg+xml;base64,${Base64.encode(svg.outerHTML)}`;
+ return new Promise((resolve, reject) => {
+ svgImg.onload = () => {
+ ctx.drawImage(svgImg, 0, 0, canvas.width, canvas.height);
+ const pngData = canvas.toDataURL('png');
+ const img = document.createElement('img');
+ img.src = pngData;
+ img.width = Math.ceil(svg.width.baseVal.value);
+ img.height = Math.ceil(svg.height.baseVal.value);
+ resolve(img);
+ };
+ });
+ }
+}
diff --git a/src/markdown-renderer/styles/app-css.ts b/src/markdown-renderer/styles/app-css.ts
new file mode 100644
index 0000000..4718622
--- /dev/null
+++ b/src/markdown-renderer/styles/app-css.ts
@@ -0,0 +1,207 @@
+
+// This CSS is composed of Prism.css and a small amount of Obsidian CSS,
+// which is copyrighted by the Obsidian developers.
+// I've received permission from @Licat on Discord to include this snippet in the plugin
+// and HTML exports from it.
+// See https://discord.com/channels/686053708261228577/707816848615407697/830630553883377690
+
+export function variables (light: boolean = true) {
+ if (light) return `
+:root {
+ --default-font: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Microsoft YaHei Light", sans-serif;
+ --font-monospace: 'Source Code Pro', monospace;
+ --background-primary: #ffffff;
+ --background-modifier-border: #ddd;
+ --text-accent: #705dcf;
+ --text-accent-hover: #7a6ae6;
+ --text-normal: #2e3338;
+ --background-secondary: #f2f3f5;
+ --background-secondary-alt: #e3e5e8;
+ --text-muted: #888888;
+}`;
+ else return `
+:root {
+ --background-primary: #202020;
+ --background-modifier-border: #333;
+ --text-accent: #7f6df2;
+ --text-accent-hover: #8875ff;
+ --text-normal: #dcddde;
+ --background-secondary: #161616;
+ --background-secondary-alt: #000000;
+ --text-muted: #999;
+}
+`;
+}
+
+export default function (light: boolean = true) {
+ return variables(light) + body();
+}
+
+function body () {
+ return `
+pre, code {
+ font-family: var(--font-monospace);
+}
+h1, h2, h3, h4, h5, h6 {
+ font-weight: 800;
+}
+a {
+ color: var(--text-accent);
+ outline: none;
+}
+a:hover {
+ color: var(--text-accent-hover);
+}
+audio {
+ outline: none;
+}
+hr {
+ border: none;
+ border-top: 1px solid;
+ border-color: var(--background-modifier-border);
+ margin: 26px 0;
+}
+* {
+ box-sizing: border-box;
+}
+body {
+ text-rendering: optimizeLegibility;
+ font-family: var(--default-font);
+ line-height: 1.5em;
+ font-size: 16px;
+ background-color: var(--background-primary);
+ color: var(--text-normal);
+}
+ul ul, ol ul, ol ul, ul ol {
+ list-style-type: disc;
+}
+
+
+
+ /* PrismJS 1.20.0
+https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+abap+abnf+actionscript+ada+al+antlr4+apacheconf+apl+applescript+aql+arduino+arff+asciidoc+asm6502+aspnet+autohotkey+autoit+bash+basic+batch+bbcode+bison+bnf+brainfuck+brightscript+bro+c+concurnas+csharp+cpp+cil+coffeescript+cmake+clojure+crystal+csp+css-extras+d+dart+dax+diff+django+dns-zone-file+docker+ebnf+eiffel+ejs+elixir+elm+etlua+erb+erlang+excel-formula+fsharp+factor+firestore-security-rules+flow+fortran+ftl+gcode+gdscript+gedcom+gherkin+git+glsl+gml+go+graphql+groovy+haml+handlebars+haskell+haxe+hcl+hlsl+http+hpkp+hsts+ichigojam+icon+iecst+inform7+ini+io+j+java+javadoc+javadoclike+javastacktrace+jolie+jq+jsdoc+js-extras+js-templates+json+jsonp+json5+julia+keyman+kotlin+latex+latte+less+lilypond+liquid+lisp+livescript+llvm+lolcode+lua+makefile+markdown+markup-templating+matlab+mel+mizar+monkey+moonscript+n1ql+n4js+nand2tetris-hdl+nasm+neon+nginx+nim+nix+nsis+objectivec+ocaml+opencl+oz+parigp+parser+pascal+pascaligo+pcaxis+peoplecode+perl+php+phpdoc+php-extras+plsql+powerquery+powershell+processing+prolog+properties+protobuf+pug+puppet+pure+purebasic+python+q+qml+qore+r+racket+jsx+tsx+renpy+reason+regex+rest+rip+roboconf+robotframework+ruby+rust+sas+sass+scss+scala+scheme+shell-session+smalltalk+smarty+solidity+solution-file+soy+sparql+splunk-spl+sqf+sql+stylus+swift+tap+tcl+textile+toml+tt2+turtle+twig+typescript+t4-cs+t4-vb+t4-templating+unrealscript+vala+vbnet+velocity+verilog+vhdl+vim+visual-basic+warpscript+wasm+wiki+xeora+xml-doc+xojo+xquery+yaml+zig */
+ /**
+ * prism.js default theme for JavaScript, CSS and HTML
+ * Based on dabblet (http://dabblet.com)
+ * @author Lea Verou
+ */
+ /* Code blocks */
+ /* Inline code */
+code[class*="language-"],
+pre[class*="language-"] {
+ color: black;
+ background: none;
+ text-shadow: 0 1px white;
+ font-family: var(--font-monospace);
+ text-align: left;
+ white-space: pre;
+ word-spacing: normal;
+ word-break: normal;
+ word-wrap: normal;
+ line-height: 1.5;
+ -moz-tab-size: 4;
+ -o-tab-size: 4;
+ tab-size: 4;
+ -webkit-hyphens: none;
+ -moz-hyphens: none;
+ -ms-hyphens: none;
+ hyphens: none;
+}
+pre[class*="language-"]::-moz-selection,
+pre[class*="language-"] ::-moz-selection,
+code[class*="language-"]::-moz-selection,
+code[class*="language-"] ::-moz-selection {
+ text-shadow: none;
+ background: #b3d4fc;
+}
+pre[class*="language-"]::selection,
+pre[class*="language-"] ::selection,
+code[class*="language-"]::selection,
+code[class*="language-"] ::selection {
+ text-shadow: none;
+ background: #b3d4fc;
+}
+@media print {
+ code[class*="language-"],
+ pre[class*="language-"] {
+ text-shadow: none;
+ }
+}
+pre[class*="language-"] {
+ padding: 1em;
+ margin: 0.5em 0;
+ overflow: auto;
+}
+:not(pre) > code[class*="language-"],
+pre[class*="language-"] {
+ background: #f5f2f0;
+}
+:not(pre) > code[class*="language-"] {
+ padding: 0.1em;
+ border-radius: 0.3em;
+ white-space: normal;
+}
+.token.comment,
+.token.prolog,
+.token.doctype,
+.token.cdata {
+ color: slategray;
+}
+.token.punctuation {
+ color: #999;
+}
+.token.namespace {
+ opacity: 0.7;
+}
+.token.property,
+.token.tag,
+.token.boolean,
+.token.number,
+.token.constant,
+.token.symbol,
+.token.deleted {
+ color: #905;
+}
+.token.selector,
+.token.attr-name,
+.token.string,
+.token.char,
+.token.builtin,
+.token.inserted {
+ color: #690;
+}
+.token.operator,
+.token.entity,
+.token.url,
+.language-css .token.string,
+.style .token.string {
+ color: #9a6e3a;
+ background: hsla(0, 0%, 100%, 0.5);
+}
+.token.atrule,
+.token.attr-value,
+.token.keyword {
+ color: #07a;
+}
+.token.function,
+.token.class-name {
+ color: #DD4A68;
+}
+.token.regex,
+.token.important,
+.token.variable {
+ color: #e90;
+}
+.token.important,
+.token.bold {
+ font-weight: bold;
+}
+.token.italic {
+ font-style: italic;
+}
+.token.entity {
+ cursor: help;
+}
+
+`;
+}
diff --git a/src/markdown-renderer/styles/mathjax-css.ts b/src/markdown-renderer/styles/mathjax-css.ts
new file mode 100644
index 0000000..690dfe2
--- /dev/null
+++ b/src/markdown-renderer/styles/mathjax-css.ts
@@ -0,0 +1,111 @@
+
+export default `
+@font-face /* 0 */ {
+ font-family: MJXZERO;
+ src: url("http://cdn.mathjax.org/mathjax/latest/fonts/HTML-CSS/TeX/woff/MathJax_Zero.woff") format("woff");
+}
+
+@font-face /* 1 */ {
+ font-family: MJXTEX;
+ src: url("http://cdn.mathjax.org/mathjax/latest/fonts/HTML-CSS/TeX/woff/MathJax_Main-Regular.woff") format("woff");
+}
+
+@font-face /* 2 */ {
+ font-family: MJXTEX-B;
+ src: url("http://cdn.mathjax.org/mathjax/latest/fonts/HTML-CSS/TeX/woff/MathJax_Main-Bold.woff") format("woff");
+}
+
+@font-face /* 3 */ {
+ font-family: MJXTEX-I;
+ src: url("http://cdn.mathjax.org/mathjax/latest/fonts/HTML-CSS/TeX/woff/MathJax_Math-Italic.woff") format("woff");
+}
+
+@font-face /* 4 */ {
+ font-family: MJXTEX-MI;
+ src: url("http://cdn.mathjax.org/mathjax/latest/fonts/HTML-CSS/TeX/woff/MathJax_Main-Italic.woff") format("woff");
+}
+
+@font-face /* 5 */ {
+ font-family: MJXTEX-BI;
+ src: url("http://cdn.mathjax.org/mathjax/latest/fonts/HTML-CSS/TeX/woff/MathJax_Math-BoldItalic.woff") format("woff");
+}
+
+@font-face /* 6 */ {
+ font-family: MJXTEX-S1;
+ src: url("http://cdn.mathjax.org/mathjax/latest/fonts/HTML-CSS/TeX/woff/MathJax_Size1-Regular.woff") format("woff");
+}
+
+@font-face /* 7 */ {
+ font-family: MJXTEX-S2;
+ src: url("http://cdn.mathjax.org/mathjax/latest/fonts/HTML-CSS/TeX/woff/MathJax_Size2-Regular.woff") format("woff");
+}
+
+@font-face /* 8 */ {
+ font-family: MJXTEX-S3;
+ src: url("http://cdn.mathjax.org/mathjax/latest/fonts/HTML-CSS/TeX/woff/MathJax_Size3-Regular.woff") format("woff");
+}
+
+@font-face /* 9 */ {
+ font-family: MJXTEX-S4;
+ src: url("http://cdn.mathjax.org/mathjax/latest/fonts/HTML-CSS/TeX/woff/MathJax_Size4-Regular.woff") format("woff");
+}
+
+@font-face /* 10 */ {
+ font-family: MJXTEX-A;
+ src: url("http://cdn.mathjax.org/mathjax/latest/fonts/HTML-CSS/TeX/woff/MathJax_AMS-Regular.woff") format("woff");
+}
+
+@font-face /* 11 */ {
+ font-family: MJXTEX-C;
+ src: url("http://cdn.mathjax.org/mathjax/latest/fonts/HTML-CSS/TeX/woff/MathJax_Calligraphic-Regular.woff") format("woff");
+}
+
+@font-face /* 12 */ {
+ font-family: MJXTEX-CB;
+ src: url("http://cdn.mathjax.org/mathjax/latest/fonts/HTML-CSS/TeX/woff/MathJax_Calligraphic-Bold.woff") format("woff");
+}
+
+@font-face /* 13 */ {
+ font-family: MJXTEX-FR;
+ src: url("http://cdn.mathjax.org/mathjax/latest/fonts/HTML-CSS/TeX/woff/MathJax_Fraktur-Regular.woff") format("woff");
+}
+
+@font-face /* 14 */ {
+ font-family: MJXTEX-FRB;
+ src: url("http://cdn.mathjax.org/mathjax/latest/fonts/HTML-CSS/TeX/woff/MathJax_Fraktur-Bold.woff") format("woff");
+}
+
+@font-face /* 15 */ {
+ font-family: MJXTEX-SS;
+ src: url("http://cdn.mathjax.org/mathjax/latest/fonts/HTML-CSS/TeX/woff/MathJax_SansSerif-Regular.woff") format("woff");
+}
+
+@font-face /* 16 */ {
+ font-family: MJXTEX-SSB;
+ src: url("http://cdn.mathjax.org/mathjax/latest/fonts/HTML-CSS/TeX/woff/MathJax_SansSerif-Bold.woff") format("woff");
+}
+
+@font-face /* 17 */ {
+ font-family: MJXTEX-SSI;
+ src: url("http://cdn.mathjax.org/mathjax/latest/fonts/HTML-CSS/TeX/woff/MathJax_SansSerif-Italic.woff") format("woff");
+}
+
+@font-face /* 18 */ {
+ font-family: MJXTEX-SC;
+ src: url("http://cdn.mathjax.org/mathjax/latest/fonts/HTML-CSS/TeX/woff/MathJax_Script-Regular.woff") format("woff");
+}
+
+@font-face /* 19 */ {
+ font-family: MJXTEX-T;
+ src: url("http://cdn.mathjax.org/mathjax/latest/fonts/HTML-CSS/TeX/woff/MathJax_Typewriter-Regular.woff") format("woff");
+}
+
+@font-face /* 20 */ {
+ font-family: MJXTEX-V;
+ src: url("http://cdn.mathjax.org/mathjax/latest/fonts/HTML-CSS/TeX/woff/MathJax_Vector-Regular.woff") format("woff");
+}
+
+@font-face /* 21 */ {
+ font-family: MJXTEX-VB;
+ src: url("http://cdn.mathjax.org/mathjax/latest/fonts/HTML-CSS/TeX/woff/MathJax_Vector-Bold.woff") format("woff");
+}`;
diff --git a/src/sync/share.ts b/src/sync/share.ts
index dfc66c7..ae9e1f5 100644
--- a/src/sync/share.ts
+++ b/src/sync/share.ts
@@ -1,12 +1,13 @@
-import { App, Notice, TFile, arrayBufferToBase64 } from "obsidian";
+import { App, MarkdownView, Notice, TFile, arrayBufferToBase64 } from "obsidian";
import { encryptData } from "../encryption";
import { paginatedListRecords, hashFileName, isCidMatching, detectMimeType, chunks, toKeyValuePairs, ATMOSPHERE_CLIENT, toPageAndLinkCounts } from "../utils";
import { XRPC } from "@atcute/client";
import { Brand, ComAtprotoRepoApplyWrites, IoGithubObsidatFile, IoGithubObsidatPublicFile } from "@atcute/client/lexicons";
-import { MyPluginSettings } from "..";
+import MyPlugin, { MyPluginSettings } from "..";
import { CaseInsensitiveMap } from "../utils/cim";
+import { MyMarkdownRenderer } from "../markdown-renderer/renderer";
-export async function doShare(agent: XRPC, app: App, settings: MyPluginSettings, files: string[]) {
+export async function doShare(agent: XRPC, app: App, plugin: MyPlugin, settings: MyPluginSettings, files: string[]) {
const currentDate = new Date();
const collection = 'io.github.obsidat.publicFile';
@@ -20,7 +21,9 @@ export async function doShare(agent: XRPC, app: App, settings: MyPluginSettings,
new Notice(`Sharing ${files.length} files to @${settings.bskyHandle}...`);
- const localFileList = files.map(file => app.vault.getAbstractFileByPath(file));
+ const allFiles = new Map(app.vault.getAllLoadedFiles().map(e => [e.path, e]));
+
+ const localFileList = files.map(file => allFiles.get(file)!);
const localFilesByRkey = CaseInsensitiveMap.toMap(
localFileList.filter(e => e instanceof TFile),
@@ -32,15 +35,34 @@ export async function doShare(agent: XRPC, app: App, settings: MyPluginSettings,
);
for (const [rkey, file] of localFilesByRkey.entries()) {
+ let renderedHtml: string | undefined;
+
+ const fileData = await file.vault.readBinary(file);
+
if (file.extension === 'md' || file.extension === '.md') {
console.log(`processing frontmatter for ${file.path}`);
- app.fileManager.processFrontMatter(file, (frontmatter) => {
- frontmatter["share-url"] = `${ATMOSPHERE_CLIENT}/page/${settings.bskyHandle}/${rkey}`;
- });
+ try {
+ await app.fileManager.processFrontMatter(file, (frontmatter) => {
+ frontmatter["share-url"] = `${ATMOSPHERE_CLIENT}/page/${settings.bskyHandle}/${rkey}`;
+ });
+ } catch (err) {
+ console.error('error processing frontmatter', err);
+ }
console.log(`processed frontmatter for ${file.path}`);
- }
- const fileData = await file.vault.readBinary(file);
+ const markdown = new TextDecoder().decode(fileData);
+
+ console.log(`decoded ${file.path}`);
+
+ ({ html: renderedHtml } = await new MyMarkdownRenderer(app, plugin, {
+ addExtensionsToInternalLinks: '',
+ displayYAMLFrontmatter: true,
+ customCSSFile: null,
+ highDPIDiagrams: true,
+ injectAppCSS: 'none',
+ linkStrippingBehaviour: 'link',
+ }).render({ data: markdown } as MarkdownView, markdown, file.path));
+ }
const remoteFile = remoteFilesByRkey.get(rkey);
@@ -72,6 +94,7 @@ export async function doShare(agent: XRPC, app: App, settings: MyPluginSettings,
frontmatter: toKeyValuePairs(fileCache?.frontmatter),
recordCreatedAt: currentDate.toISOString(),
fileLastCreatedOrModified: new Date(file.fileLastCreatedOrModified).toISOString(),
+ html: renderedHtml,
} satisfies IoGithubObsidatPublicFile.Record;
writes.push({
diff --git a/tsconfig.json b/tsconfig.json
index abf20a6..97970b4 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -30,6 +30,6 @@
"jsx": "react",
"jsxFactory": "h"
},
- "include": [ "./src/**/*.ts", "./src/**/*.tsx", "./utils/**/*.ts" ],
+ "include": [ "./src/**/*.ts", "./src/**/*.tsx", "./utils/**/*.ts", "./utils/**/*.mts" ],
"exclude": ["node_modules", "spa", "/main.js"],
}
diff --git a/utils/glob-run.ts b/utils/glob-run.ts
new file mode 100644
index 0000000..1e17f39
--- /dev/null
+++ b/utils/glob-run.ts
@@ -0,0 +1,39 @@
+import { glob } from 'glob';
+import { spawn } from 'child-process-promise';
+
+(async () => {
+ const command = process.argv.slice(2);
+ if (command.length === 0) {
+ console.log('No command given to run.');
+ process.exit(1);
+ return;
+ }
+
+ const commands = await lib(command);
+
+ console.log('>', ...commands)
+
+ const cp = spawn(commands[0], commands.slice(1), {
+ stdio: 'inherit'
+ });
+
+ process.on('exit', function() {
+ cp.childProcess.kill('SIGHUP');
+ });
+
+ await cp;
+
+ async function lib(args: string[]) {
+ const outArgs: string[] = [];
+
+ for (const arg of args) {
+ if (arg.indexOf('*') > -1) {
+ outArgs.push(...await glob(arg));
+ } else {
+ outArgs.push(arg);
+ }
+ }
+
+ return outArgs;
+ };
+})();
\ No newline at end of file