feat: Support GitHub Gist

This commit is contained in:
4513ECHO 2026-06-21 20:07:36 +09:00
parent f87cb465ef
commit bd560d9ca5
No known key found for this signature in database
GPG key ID: 3A5BDFC6E5C365DD
2 changed files with 96 additions and 0 deletions

View file

@ -17,4 +17,5 @@ Since Obsidian officially supports only [a few kinds of web pages](https://obsid
## Supported web pages
- [Bluesky](https://bsky.app/)
- [GitHub Gist](https://gist.github.com/)
- [Niconico](https://www.nicovideo.jp/)

95
src/source/gist.ts Normal file
View file

@ -0,0 +1,95 @@
import { requestUrl } from "obsidian";
import { EmbedSource } from "../embed_source.ts";
const EMBED_URL = "https://gist.github.com";
const urlPattern = new URLPattern({
pathname: "/:username?/:gistId",
baseURL: EMBED_URL,
});
export default class Gist extends EmbedSource {
static #heightCache: Map<number, number> = new Map();
static id = 0;
#id = Gist.id++;
#srcdoc?: string;
constructor(url: string) {
super(url);
}
static override get meta() {
return {
name: "GitHub Gist",
logo: "https://github.githubassets.com/favicons/favicon.svg",
origin: "https://gist.github.com",
};
}
render(): HTMLElement {
const iframe = createEl("iframe", {
cls: ["external-embed", "node-insert-event"],
attr: {
srcdoc: this.#srcdoc ?? "",
loading: "lazy",
sandbox: "allow-scripts allow-top-navigation-by-user-activation",
},
});
if (this.height) {
iframe.style.height = `${this.height}px`;
}
return iframe;
}
override resolveSrc(): void | Promise<void> {
if (this.#srcdoc) {
return;
}
const matched = urlPattern.exec(this.url)!;
const apiUrl =
`${EMBED_URL}/${matched.pathname.groups.gistId}.json?` +
new URLSearchParams({
file: matched.hash.input,
}).toString();
return requestUrl(apiUrl).json.then(async (result) => {
const stylesheet = await requestUrl(result.stylesheet).text;
const styleDecl = getComputedStyle(document.body);
const interfaceFont = styleDecl.getPropertyValue("--font-interface");
const monospaceFont = styleDecl.getPropertyValue("--font-monospace");
this.#srcdoc = `
<html>
<head>
<base target="_parent" />
<style>
html, body { margin: 0; padding: 0; height: 100%; }
body .gist .gist-meta { font-family: ${interfaceFont}; }
body .gist .highlight { font-family: ${monospaceFont}; }
</style>
<style>${stylesheet}</style>
<script>
window.addEventListener("load", () => {
top.postMessage({ id: ${this.#id}, height: document.body.scrollHeight }, "app://obsidian.md");
});
</script>
</head>
<body>${result.div}</body>
</html>
`;
});
}
override get height(): number | undefined {
return Gist.#heightCache.get(this.#id);
}
override onMessage(event: MessageEvent<{ id: number; height: number }>): boolean {
if (event.origin !== "null") {
return false;
}
const { id, height } = event.data;
if (id !== this.#id || height <= 0) {
return false;
}
Gist.#heightCache.set(this.#id, height);
this.loaded();
return true;
}
}