Implement a plugin to improve inter-page links
This commit is contained in:
parent
9d28b89824
commit
d4ed6492ad
4
.gitignore
vendored
4
.gitignore
vendored
@ -19,6 +19,8 @@ Cargo.lock
|
||||
/content/.obsidian/themes
|
||||
/content/.obsidian/appearance.json
|
||||
/content/.obsidian/workspace.json
|
||||
/content/@
|
||||
|
||||
# Python
|
||||
.venv
|
||||
.venv
|
||||
|
||||
|
@ -7,6 +7,7 @@ build_search_index = true
|
||||
generate_feed = true
|
||||
feed_filename = "rss.xml"
|
||||
minify_html = false # This breaks mermaid diagrams :(
|
||||
ignored_content = ["content/@/blog/*.md"]
|
||||
|
||||
[markdown]
|
||||
highlight_code = true
|
||||
|
4
content/.obsidian/community-plugins.json
vendored
4
content/.obsidian/community-plugins.json
vendored
@ -1,3 +1,5 @@
|
||||
[
|
||||
"obsidian-front-matter-title-plugin"
|
||||
"obsidian-front-matter-title-plugin",
|
||||
"link-interceptor",
|
||||
"hot-reload"
|
||||
]
|
2
content/.obsidian/graph.json
vendored
2
content/.obsidian/graph.json
vendored
@ -32,6 +32,6 @@
|
||||
"repelStrength": 14.1666666666667,
|
||||
"linkStrength": 1,
|
||||
"linkDistance": 250,
|
||||
"scale": 0.13187606777463332,
|
||||
"scale": 0.07178423896278742,
|
||||
"close": false
|
||||
}
|
11
content/.obsidian/hotkeys.json
vendored
11
content/.obsidian/hotkeys.json
vendored
@ -1 +1,10 @@
|
||||
{}
|
||||
{
|
||||
"app:reload": [
|
||||
{
|
||||
"modifiers": [
|
||||
"Mod"
|
||||
],
|
||||
"key": "R"
|
||||
}
|
||||
]
|
||||
}
|
110
content/.obsidian/plugins/hot-reload/main.js
vendored
Normal file
110
content/.obsidian/plugins/hot-reload/main.js
vendored
Normal file
@ -0,0 +1,110 @@
|
||||
const {Plugin, Notice, debounce} = require("obsidian");
|
||||
const fs = require("fs");
|
||||
|
||||
const watchNeeded = window.process.platform !== "darwin" && window.process.platform !== "win32";
|
||||
|
||||
module.exports = class HotReload extends Plugin {
|
||||
|
||||
statCache = new Map(); // path -> Stat
|
||||
queue = Promise.resolve();
|
||||
|
||||
run(val, err) {
|
||||
return this.queue = this.queue.then(val, err);
|
||||
}
|
||||
|
||||
reindexPlugins = debounce(() => this.run(() => this.getPluginNames()), 500, true);
|
||||
requestScan = debounce(() => this.run(() => this.checkVersions()), 250, true);
|
||||
|
||||
onload() {
|
||||
app.workspace.onLayoutReady(async ()=> {
|
||||
this.pluginReloaders = {};
|
||||
this.inProgress = null;
|
||||
await this.getPluginNames();
|
||||
this.registerEvent( this.app.vault.on("raw", this.requestScan));
|
||||
this.watch(".obsidian/plugins");
|
||||
this.requestScan();
|
||||
this.addCommand({
|
||||
id: "scan-for-changes",
|
||||
name: "Check plugins for changes and reload them",
|
||||
callback: () => this.requestScan()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
watch(path) {
|
||||
if (this.app.vault.adapter.watchers.hasOwnProperty(path)) return;
|
||||
const realPath = [this.app.vault.adapter.basePath, path].join("/");
|
||||
const lstat = fs.lstatSync(realPath);
|
||||
if (lstat && (watchNeeded || lstat.isSymbolicLink()) && fs.statSync(realPath).isDirectory()) {
|
||||
this.app.vault.adapter.startWatchPath(path, false);
|
||||
}
|
||||
}
|
||||
|
||||
async checkVersions() {
|
||||
const base = this.app.plugins.getPluginFolder();
|
||||
for (const dir of Object.keys(this.pluginNames)) {
|
||||
for (const file of ["manifest.json", "main.js", "styles.css", ".hotreload"]) {
|
||||
const path = `${base}/${dir}/${file}`;
|
||||
const stat = await app.vault.adapter.stat(path);
|
||||
if (stat) {
|
||||
if (this.statCache.has(path) && stat.mtime !== this.statCache.get(path).mtime) {
|
||||
this.onFileChange(path);
|
||||
}
|
||||
this.statCache.set(path, stat);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getPluginNames() {
|
||||
const plugins = {}, enabled = new Set();
|
||||
for (const {id, dir} of Object.values(app.plugins.manifests)) {
|
||||
this.watch(dir);
|
||||
plugins[dir.split("/").pop()] = id;
|
||||
if (
|
||||
await this.app.vault.exists(dir+"/.git") ||
|
||||
await this.app.vault.exists(dir+"/.hotreload")
|
||||
) enabled.add(id);
|
||||
}
|
||||
this.pluginNames = plugins;
|
||||
this.enabledPlugins = enabled;
|
||||
}
|
||||
|
||||
onFileChange(filename) {
|
||||
if (!filename.startsWith(this.app.plugins.getPluginFolder()+"/")) return;
|
||||
const path = filename.split("/");
|
||||
const base = path.pop(), dir = path.pop();
|
||||
if (path.length === 1 && dir === "plugins") return this.watch(filename);
|
||||
if (path.length != 2) return;
|
||||
const plugin = dir && this.pluginNames[dir];
|
||||
if (base === "manifest.json" || base === ".hotreload" || base === ".git" || !plugin) return this.reindexPlugins();
|
||||
if (base !== "main.js" && base !== "styles.css") return;
|
||||
if (!this.enabledPlugins.has(plugin)) return;
|
||||
const reloader = this.pluginReloaders[plugin] || (
|
||||
this.pluginReloaders[plugin] = debounce(() => this.run(() => this.reload(plugin), console.error), 750, true)
|
||||
);
|
||||
reloader();
|
||||
}
|
||||
|
||||
async reload(plugin) {
|
||||
const plugins = app.plugins;
|
||||
|
||||
// Don't reload disabled plugins
|
||||
if (!plugins.enabledPlugins.has(plugin)) return;
|
||||
|
||||
await plugins.disablePlugin(plugin);
|
||||
console.debug("disabled", plugin);
|
||||
|
||||
// Ensure sourcemaps are loaded (Obsidian 14+)
|
||||
const oldDebug = localStorage.getItem("debug-plugin");
|
||||
localStorage.setItem("debug-plugin", "1");
|
||||
try {
|
||||
await plugins.enablePlugin(plugin);
|
||||
} finally {
|
||||
// Restore previous setting
|
||||
if (oldDebug === null) localStorage.removeItem("debug-plugin"); else localStorage.setItem("debug-plugin", oldDebug);
|
||||
}
|
||||
console.debug("enabled", plugin);
|
||||
new Notice(`Plugin "${plugin}" has been reloaded`);
|
||||
}
|
||||
}
|
10
content/.obsidian/plugins/hot-reload/manifest.json
vendored
Normal file
10
content/.obsidian/plugins/hot-reload/manifest.json
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"id": "hot-reload",
|
||||
"name": "Hot Reload",
|
||||
"author": "PJ Eby",
|
||||
"authorUrl": "https://github.com/pjeby",
|
||||
"version": "0.1.10",
|
||||
"minAppVersion": "0.15.9",
|
||||
"description": "Automatically reload in-development plugins when their files are changed",
|
||||
"isDesktopOnly": true
|
||||
}
|
0
content/.obsidian/plugins/link-interceptor/.hotreload
vendored
Normal file
0
content/.obsidian/plugins/link-interceptor/.hotreload
vendored
Normal file
87
content/.obsidian/plugins/link-interceptor/main.js
vendored
Normal file
87
content/.obsidian/plugins/link-interceptor/main.js
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
const { MarkdownPostProcessorContext, Plugin } = require('obsidian');
|
||||
|
||||
module.exports = class LinkInterceptorPlugin extends Plugin {
|
||||
async onload() {
|
||||
console.log('Intercept Links plugin loaded');
|
||||
|
||||
// // Register an event to handle link clicks
|
||||
// this.registerEvent(
|
||||
// this.app.workspace.on('link-clicked',(args)=>{console.log(args);})
|
||||
// );
|
||||
|
||||
// Register an event to handle opening another file through a markdown link
|
||||
this.registerEvent(
|
||||
this.app.workspace.on('file-open', (file) => {
|
||||
if (file) {
|
||||
console.log(`File open request for: ${file.path}`);
|
||||
|
||||
// If the path starts with an `@` then it's a link to another file
|
||||
if (file.path.startsWith('@/')) {
|
||||
// remove the @/
|
||||
const newLink = file.path.substring(2);
|
||||
// open the new link
|
||||
this.app.workspace.openLinkText(newLink, '', false);
|
||||
|
||||
// Delete the auto-created file
|
||||
this.app.vault.delete(file);
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// Rewrite all resolved links in the metadata cache
|
||||
this.resolveAtLinks();
|
||||
this.registerEvent(
|
||||
this.app.metadataCache.on('resolved-links', () => {
|
||||
this.resolveAtLinks();
|
||||
})
|
||||
);
|
||||
this.registerEvent(
|
||||
this.app.metadataCache.on('resolve', () => {
|
||||
this.resolveAtLinks();
|
||||
})
|
||||
);
|
||||
this.registerEvent(
|
||||
this.app.metadataCache.on('changed', () => {
|
||||
this.resolveAtLinks();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
resolveAtLinks() {
|
||||
let unresolved_links = this.app.metadataCache.unresolvedLinks;
|
||||
let unsreolved_at_links = {};
|
||||
for (let source in unresolved_links) {
|
||||
for (let destination in unresolved_links[source]) {
|
||||
if (destination.startsWith('@')) {
|
||||
if (!unsreolved_at_links.hasOwnProperty(source)) {
|
||||
unsreolved_at_links[source] = {};
|
||||
}
|
||||
unsreolved_at_links[source][destination] = unresolved_links[source][destination];
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(`Found ${Object.keys(unsreolved_at_links).length} unresolved @ links`);
|
||||
|
||||
// Resolve the links
|
||||
for (let source in unsreolved_at_links) {
|
||||
for (let destination in unsreolved_at_links[source]) {
|
||||
// remove the @/
|
||||
const newLink = destination.substring(2);
|
||||
|
||||
// Add to resolvedLinks
|
||||
if (!app.metadataCache.resolvedLinks[source].hasOwnProperty(`${newLink}.md`)) {
|
||||
app.metadataCache.resolvedLinks[source][`${newLink}.md`] = 0;
|
||||
}
|
||||
app.metadataCache.resolvedLinks[source][`${newLink}.md`] += unsreolved_at_links[source][destination];
|
||||
console.log(`Resolved @ link: ${source} -> ${newLink}`);
|
||||
console.log(app.metadataCache.resolvedLinks[source]);
|
||||
|
||||
// Remove the old link
|
||||
delete unsreolved_at_links[source][destination];
|
||||
delete this.app.metadataCache.unresolvedLinks[source][destination];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
11
content/.obsidian/plugins/link-interceptor/manifest.json
vendored
Normal file
11
content/.obsidian/plugins/link-interceptor/manifest.json
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"id": "link-interceptor",
|
||||
"name": "Zola Link Interceptor",
|
||||
"version": "0.0.1",
|
||||
"minAppVersion": "0.16.3",
|
||||
"description": "Handles Zola's @ links",
|
||||
"author": "Evan Pratten <evan@ewpratten.com>",
|
||||
"authorUrl": "https://ewpratten.com",
|
||||
"isDesktopOnly": false,
|
||||
"fundingUrl": "https://ewp.fyi/sponsor"
|
||||
}
|
@ -4,21 +4,19 @@ title: Building a VPN with an AMPRNet BGP allocation
|
||||
description: A quick guide on using your shiny new AMPRNet allocation
|
||||
date: 2023-07-07
|
||||
tags:
|
||||
- networking
|
||||
- amprnet
|
||||
- bgp
|
||||
- networking
|
||||
- amprnet
|
||||
- bgp
|
||||
draft: false
|
||||
extra:
|
||||
auto_center_images: true
|
||||
excerpt: A guide on setting up a VPS, announcing a prefix over BGP, and using it
|
||||
as a VPN server
|
||||
excerpt: A guide on setting up a VPS, announcing a prefix over BGP, and using it as a VPN server
|
||||
discuss:
|
||||
reddit: https://www.reddit.com/r/ewpratten/comments/14tdltu/building_a_vpn_with_an_amprnet_bgp_allocation/
|
||||
hacker_news: https://news.ycombinator.com/item?id=36635146
|
||||
uses:
|
||||
- mermaid
|
||||
aliases:
|
||||
- /blog/ampr-vpn
|
||||
- mermaid
|
||||
aliases: []
|
||||
---
|
||||
|
||||
One of the most common emails I receive from readers of this website generally starts with:
|
||||
|
@ -15,10 +15,10 @@ extra:
|
||||
aliases:
|
||||
- /blog/obsidian-blogging
|
||||
---
|
||||
|
||||
It recently occurred to me that [Obsidian](https://obsidian.md) is capable of editing *any* type of markdown document store, not just its own note "Vaults". So, as a test I've been using it to interface with the source files that make up this website.
|
||||
|
||||
This post largely exists for the sake of figuring out how Obsidian behaves when forced into an environment that doesn't entirely agree with the "obsidian way of doing things".
|
||||
This post largely exists for the sake of figuring out how Obsidian behaves when forced into an environment that doesn't entirely agree with the "obsidian way of doing things".
|
||||
[test link](@/blog/2023-07-07-ampr-vpn.md)
|
||||
|
||||
---
|
||||
- Editing extra field is hard
|
||||
|
Loading…
x
Reference in New Issue
Block a user