aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorXe Iaso <me@xeiaso.net>2023-04-01 17:54:07 -0400
committerXe Iaso <me@xeiaso.net>2023-04-01 17:54:07 -0400
commit06070a820f5a92c2671242b044cec5f6b3dacff5 (patch)
tree257be786bee080c0eb76941df827680eec9912f0 /src
parentc78d291679a0b054e20ad35784dd5a8f9238f0a9 (diff)
downloadxesite-06070a820f5a92c2671242b044cec5f6b3dacff5.tar.xz
xesite-06070a820f5a92c2671242b044cec5f6b3dacff5.zip
build javascript files with esbuild
Signed-off-by: Xe Iaso <me@xeiaso.net>
Diffstat (limited to 'src')
-rw-r--r--src/frontend/.gitignore1
-rwxr-xr-xsrc/frontend/build.sh1
-rw-r--r--src/frontend/build.ts25
-rw-r--r--src/frontend/components/ConvSnippet.tsx56
-rw-r--r--src/frontend/components/MastodonShareButton.tsx76
-rw-r--r--src/frontend/components/WASITerm.tsx83
-rw-r--r--src/frontend/deno.lock32
-rw-r--r--src/frontend/deps.ts10
-rw-r--r--src/frontend/import_map.json9
-rw-r--r--src/frontend/mastodon_share_button.tsx64
-rw-r--r--src/frontend/state.js5
-rw-r--r--src/frontend/wasiterm.tsx53
-rw-r--r--src/tmpl/blog.rs11
13 files changed, 273 insertions, 153 deletions
diff --git a/src/frontend/.gitignore b/src/frontend/.gitignore
new file mode 100644
index 0000000..5f98501
--- /dev/null
+++ b/src/frontend/.gitignore
@@ -0,0 +1 @@
+dist/*.js
diff --git a/src/frontend/build.sh b/src/frontend/build.sh
index 33577ae..ef3fed1 100755
--- a/src/frontend/build.sh
+++ b/src/frontend/build.sh
@@ -9,4 +9,3 @@ set -e
export RUST_LOG=info
denobuild ./mastodon_share_button.tsx ../../static/js/mastodon_share_button.js
-denobuild ./wasiterm.tsx ../../static/js/wasiterm.js
diff --git a/src/frontend/build.ts b/src/frontend/build.ts
new file mode 100644
index 0000000..70c8b2b
--- /dev/null
+++ b/src/frontend/build.ts
@@ -0,0 +1,25 @@
+import * as esbuild from "@esbuild";
+import { denoPlugin } from "@esbuild/deno";
+
+const result = await esbuild.build({
+ plugins: [denoPlugin({
+ importMapURL: new URL("./import_map.json", import.meta.url),
+ })],
+ entryPoints: [
+ "./components/ConvSnippet.tsx",
+ "./components/MastodonShareButton.tsx",
+ "./components/WASITerm.tsx",
+ ],
+ outdir: Deno.env.get("WRITE_TO")
+ ? Deno.env.get("WRITE_TO")
+ : "../../static/xeact",
+ bundle: true,
+ splitting: true,
+ format: "esm",
+ //minifyWhitespace: true,
+ inject: ["xeact"],
+ jsxFactory: "h",
+});
+console.log(result.outputFiles);
+
+esbuild.stop();
diff --git a/src/frontend/components/ConvSnippet.tsx b/src/frontend/components/ConvSnippet.tsx
index 02b2121..375ed9a 100644
--- a/src/frontend/components/ConvSnippet.tsx
+++ b/src/frontend/components/ConvSnippet.tsx
@@ -1,27 +1,43 @@
+// @jsxImportSource xeact
+// @jsxRuntime automatic
+
export interface ConvSnippetProps {
- name: string;
- mood: string;
- children: HTMLElement[];
+ name: string;
+ mood: string;
+ children: HTMLElement[];
}
-const ConvSnippet = ({name, mood, children}: ConvSnippetProps) => {
- const nameLower = name.toLowerCase();
- name = name.replace(" ", "_");
+const ConvSnippet = ({ name, mood, children }: ConvSnippetProps) => {
+ const nameLower = name.toLowerCase();
+ name = name.replace(" ", "_");
- return (
- <div className="conversation">
- <div className="conversation-standalone">
- <picture>
- <source type="image/avif" srcset={`https://cdn.xeiaso.net/file/christine-static/stickers/${nameLower}/${mood}.avif`} />
- <source type="image/webp" srcset={`https://cdn.xeiaso.net/file/christine-static/stickers/${nameLower}/${mood}.webp`} />
- <img style="max-height:4.5rem" alt={`${name} is ${mood}`} loading="lazy" src={`https://cdn.xeiaso.net/file/christine-static/stickers/${nameLower}/${mood}.png`} />
- </picture>
- </div>
- <div className="conversation-chat">
- &lt;<a href={`/characters#${nameLower}`}><b>{name}</b></a>&gt; {children}
- </div>
- </div>
- );
+ return (
+ <div className="conversation">
+ <div className="conversation-standalone">
+ <picture>
+ <source
+ type="image/avif"
+ srcset={`https://cdn.xeiaso.net/file/christine-static/stickers/${nameLower}/${mood}.avif`}
+ />
+ <source
+ type="image/webp"
+ srcset={`https://cdn.xeiaso.net/file/christine-static/stickers/${nameLower}/${mood}.webp`}
+ />
+ <img
+ style="max-height:4.5rem"
+ alt={`${name} is ${mood}`}
+ loading="lazy"
+ src={`https://cdn.xeiaso.net/file/christine-static/stickers/${nameLower}/${mood}.png`}
+ />
+ </picture>
+ </div>
+ <div className="conversation-chat">
+ &lt;<a href={`/characters#${nameLower}`}>
+ <b>{name}</b>
+ </a>&gt; {children}
+ </div>
+ </div>
+ );
};
export default ConvSnippet;
diff --git a/src/frontend/components/MastodonShareButton.tsx b/src/frontend/components/MastodonShareButton.tsx
new file mode 100644
index 0000000..03fe596
--- /dev/null
+++ b/src/frontend/components/MastodonShareButton.tsx
@@ -0,0 +1,76 @@
+// @jsxImportSource xeact
+// @jsxRuntime automatic
+
+import { u } from "xeact";
+
+export interface MastodonShareButtonProps {
+ title: string;
+ url: string;
+ series?: string;
+ tags: string;
+}
+
+export default function MastodonShareButton(
+ { title, url = u(), series, tags }: MastodonShareButtonProps,
+) {
+ let defaultURL = localStorage["mastodon_instance"];
+
+ if (defaultURL == undefined) {
+ defaultURL = "";
+ }
+
+ const tootTemplate = `${title}
+
+${url}
+
+${series ? "#" + series + " " : ""}${
+ tags ? tags.map((x) => "#" + x).join(" ") : ""
+ } @cadey@pony.social`;
+
+ const instanceBox = (
+ <input
+ type="text"
+ placeholder="https://pony.social"
+ value={defaultURL}
+ />
+ );
+ const tootBox = (
+ <textarea rows={6} cols={40}>
+ {tootTemplate}
+ </textarea>
+ );
+
+ return (
+ <div>
+ <details>
+ <summary>Share on Mastodon</summary>
+ <span>{"Instance URL (https://mastodon.example)"}</span>
+ <br />
+ {instanceBox}
+ <br />
+ {tootBox}
+ <br />
+ <button
+ onClick={() => {
+ let instanceURL = instanceBox.value;
+
+ if (!instanceURL.startsWith("https://")) {
+ instanceURL = `https://${instanceURL}`;
+ }
+
+ localStorage["mastodon_instance"] = instanceURL;
+ const text = tootBox.value;
+ const mastodonURL = u(instanceURL + "/share", {
+ text,
+ visibility: "public",
+ });
+ console.log({ text, mastodonURL });
+ window.open(mastodonURL, "_blank");
+ }}
+ >
+ Share
+ </button>
+ </details>
+ </div>
+ );
+}
diff --git a/src/frontend/components/WASITerm.tsx b/src/frontend/components/WASITerm.tsx
new file mode 100644
index 0000000..5f7e7f9
--- /dev/null
+++ b/src/frontend/components/WASITerm.tsx
@@ -0,0 +1,83 @@
+// @jsxImportSource xeact
+// @jsxRuntime automatic
+
+import { t, x } from "xeact";
+import Terminal from "@xterm";
+import * as fitAdd from "@xterm/addon-fit";
+import { Fd, File, PreopenDirectory, WASI } from "@bjorn3/browser_wasi_shim";
+
+class XtermStdio extends Fd {
+ term: Terminal;
+
+ constructor(term: Terminal) {
+ super();
+ this.term = term;
+ }
+
+ fd_write(view8: Uint8Array, iovs: any) {
+ let nwritten = 0;
+ for (let iovec of iovs) {
+ console.log(
+ iovec.buf_len,
+ iovec.buf_len,
+ view8.slice(iovec.buf, iovec.buf + iovec.buf_len),
+ );
+ let buffer = view8.slice(iovec.buf, iovec.buf + iovec.buf_len);
+ this.term.write(buffer);
+ nwritten += iovec.buf_len;
+ }
+ return { ret: 0, nwritten };
+ }
+}
+
+const loadExternalFile = async (path: string) => {
+ return new File(await (await (await fetch(path)).blob()).arrayBuffer());
+};
+
+export interface WASITermProps {
+ href: string;
+ env: string[];
+ args: string[];
+}
+
+export default function WASITerm({ href, env, args }: WASITermProps) {
+ const root = <div style="max-width:80ch;max-height:20ch"></div>;
+
+ const term = new Terminal({
+ convertEol: true,
+ fontFamily: "Iosevka Curly Iaso",
+ });
+
+ const fit = new fitAdd.default();
+ term.loadAddon(fit);
+ fit.fit();
+
+ return (
+ <div>
+ {root}
+ <button
+ onClick={async () => {
+ term.writeln(`\x1B[93mfetching ${href}...\x1B[0m`);
+ const wasm = await WebAssembly.compileStreaming(fetch(href));
+ term.writeln("\x1B[93mdone, instantiating\x1B[0m");
+
+ const fds = [
+ new XtermStdio(term),
+ new XtermStdio(term),
+ new XtermStdio(term),
+ new PreopenDirectory("/tmp", {}),
+ ];
+
+ let wasi = new WASI(args, env, fds);
+ term.clear();
+ // let inst = await WebAssembly.instantiate(wasm, {
+ // "wasi_snapshot_preview1": wasi.wasiImport,
+ // });
+ // wasi.start(inst);
+ }}
+ >
+ Run
+ </button>
+ </div>
+ );
+}
diff --git a/src/frontend/deno.lock b/src/frontend/deno.lock
index 01f35a5..8c5f04b 100644
--- a/src/frontend/deno.lock
+++ b/src/frontend/deno.lock
@@ -1,9 +1,35 @@
{
"version": "2",
"remote": {
- "https://deno.land/x/wasm@v1.2.2/pkg/wasmer_wasi_js.d.ts": "0e202696f693f922ed63e1f372dd7b4641ce587d357fcf32de3d09cb48785867",
- "https://deno.land/x/wasm@v1.2.2/pkg/wasmer_wasi_js.js": "33c58765ded371211b754157955a7ab1d2de921a9c8fee8748112d25605726dd",
- "https://deno.land/x/wasm@v1.2.2/wasi.ts": "4fde34f8fa8c52af06adcc241f1b18fabf2df0d8b1f1b62d05e70377688a8fcb",
+ "https://deno.land/std@0.156.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74",
+ "https://deno.land/std@0.156.0/_util/os.ts": "3b4c6e27febd119d36a416d7a97bd3b0251b77c88942c8f16ee5953ea13e2e49",
+ "https://deno.land/std@0.156.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3",
+ "https://deno.land/std@0.156.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09",
+ "https://deno.land/std@0.156.0/path/_util.ts": "d16be2a16e1204b65f9d0dfc54a9bc472cafe5f4a190b3c8471ec2016ccd1677",
+ "https://deno.land/std@0.156.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633",
+ "https://deno.land/std@0.156.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee",
+ "https://deno.land/std@0.156.0/path/mod.ts": "56fec03ad0ebd61b6ab39ddb9b0ddb4c4a5c9f2f4f632e09dd37ec9ebfd722ac",
+ "https://deno.land/std@0.156.0/path/posix.ts": "c1f7afe274290ea0b51da07ee205653b2964bd74909a82deb07b69a6cc383aaa",
+ "https://deno.land/std@0.156.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9",
+ "https://deno.land/std@0.156.0/path/win32.ts": "bd7549042e37879c68ff2f8576a25950abbfca1d696d41d82c7bca0b7e6f452c",
+ "https://deno.land/x/denoflate@1.2.1/mod.ts": "f5628e44b80b3d80ed525afa2ba0f12408e3849db817d47a883b801f9ce69dd6",
+ "https://deno.land/x/denoflate@1.2.1/pkg/denoflate.js": "b9f9ad9457d3f12f28b1fb35c555f57443427f74decb403113d67364e4f2caf4",
+ "https://deno.land/x/denoflate@1.2.1/pkg/denoflate_bg.wasm.js": "d581956245407a2115a3d7e8d85a9641c032940a8e810acbd59ca86afd34d44d",
+ "https://deno.land/x/esbuild@v0.15.10/mod.d.ts": "12e96a8d05c8e2c5638aa9cb09f24222134d437abb2f3f52de0156adb2270719",
+ "https://deno.land/x/esbuild@v0.15.10/mod.js": "b6076f05f58057025ce0eddb404a4da3b7a0e3768c53f6c87959c8c32240e9cd",
+ "https://deno.land/x/esbuild@v0.17.13/mod.d.ts": "dc279a3a46f084484453e617c0cabcd5b8bd1920c0e562e4ea02dfc828c8f968",
+ "https://deno.land/x/esbuild@v0.17.13/mod.js": "99302be2ce2a11eecd7a0764d970e2a0aa1c2d38d5ffcbb041d360a124c9aabc",
+ "https://deno.land/x/esbuild_deno_loader@0.6.0/deps.ts": "fe86f62cb954edc2580868cdc3292d4446f22cd2c4eedbf8ee26513fdf74e343",
+ "https://deno.land/x/esbuild_deno_loader@0.6.0/mod.ts": "5d8a429b26c70f9fd522a8629e751f3bdf63680da473e537bc420d552ee3bf0b",
+ "https://deno.land/x/esbuild_deno_loader@0.6.0/src/deno.ts": "0e83ccabbe2b004389288e38df2031b79eb347df2d139fce9394d8e88a11f259",
+ "https://deno.land/x/esbuild_deno_loader@0.6.0/src/native_loader.ts": "343854a566cf510cf25144f7c09fc0c1097780a31830305142a075d12bb697ba",
+ "https://deno.land/x/esbuild_deno_loader@0.6.0/src/portable_loader.ts": "35b6c526eed8c2c781a3256b23c30aa7cce69c0ef1d583c15528663287ba18a3",
+ "https://deno.land/x/esbuild_deno_loader@0.6.0/src/shared.ts": "b64749cd8c0f6252a11498bd8758ef1220003e46b2c9b68e16da63fd7e92b13a",
+ "https://deno.land/x/importmap@0.2.1/_util.ts": "ada9a9618b537e6c0316c048a898352396c882b9f2de38aba18fd3f2950ede89",
+ "https://deno.land/x/importmap@0.2.1/mod.ts": "ae3d1cd7eabd18c01a4960d57db471126b020f23b37ef14e1359bbb949227ade",
+ "https://esm.sh/v113/@bjorn3/browser_wasi_shim@0.2.7/es2022/browser_wasi_shim.mjs": "0d06fb9add2a2e53d8c52e3e5c3b5aa2115fe3ee651021d3ae1fff150a662e4e",
+ "https://esm.sh/v113/xterm-addon-fit@0.7.0/es2022/xterm-addon-fit.mjs": "131a41086e2e41272da58fac19e128029fb0d363103ae5f68bb71c8b7beadcf9",
+ "https://esm.sh/v113/xterm@5.1.0/es2022/xterm.mjs": "e0697e20cbe92d2177abe9fb744457df5c0f89f77a8e8ce7e9515b7d60f5cb57",
"https://xena.greedo.xeserv.us/pkg/xeact/v0.69.71/jsx-runtime.js": "a8e0e04c44bbc80bb0a8c59e1d3ee338dd799e25e89792cadb67f00bf4f6ceca",
"https://xena.greedo.xeserv.us/pkg/xeact/v0.69.71/xeact.js": "4b943b147cddd20b514b0cf85c68438e585712e809d8da4283ee5f0b8ac1ab8d",
"https://xena.greedo.xeserv.us/pkg/xeact/v0.69.71/xeact.ts": "b6def4bebff19548539f4708cff028cd1fd7abdf6bfd7b3cd8dd5025b97d676e"
diff --git a/src/frontend/deps.ts b/src/frontend/deps.ts
deleted file mode 100644
index f19e9ae..0000000
--- a/src/frontend/deps.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import * as wasi from "https://deno.land/x/wasm@v1.2.2/wasi.ts";
-//import * as xterm from "https://esm.sh/xterm@5.0.0?pin=v102";
-
-import * as xeact from "xeact";
-
-export {
- wasi,
- xeact,
- //xterm
-};
diff --git a/src/frontend/import_map.json b/src/frontend/import_map.json
index 9e87d14..5794405 100644
--- a/src/frontend/import_map.json
+++ b/src/frontend/import_map.json
@@ -1,8 +1,15 @@
{
"imports": {
+ "@bjorn3/browser_wasi_shim": "https://esm.sh/v113/@bjorn3/browser_wasi_shim@0.2.7/es2022/browser_wasi_shim.mjs",
+ "@xterm": "https://esm.sh/v113/xterm@5.1.0/es2022/xterm.mjs",
+ "@xterm/addon-fit": "https://esm.sh/v113/xterm-addon-fit@0.7.0/es2022/xterm-addon-fit.mjs",
+
+ "@esbuild": "https://deno.land/x/esbuild@v0.17.13/mod.js",
+ "@esbuild/deno": "https://deno.land/x/esbuild_deno_loader@0.6.0/mod.ts",
+
"xeact": "https://xena.greedo.xeserv.us/pkg/xeact/v0.69.71/xeact.ts",
"xeact/jsx-runtime": "https://xena.greedo.xeserv.us/pkg/xeact/v0.69.71/jsx-runtime.js",
- "/": "./",
+ "@/": "./",
"./": "./"
}
}
diff --git a/src/frontend/mastodon_share_button.tsx b/src/frontend/mastodon_share_button.tsx
deleted file mode 100644
index 744570f..0000000
--- a/src/frontend/mastodon_share_button.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-import { g, r, u, x } from "xeact";
-
-r(() => {
- const root = g("mastodon_share_button");
-
- let defaultURL = localStorage["mastodon_instance"];
-
- if (defaultURL == undefined) {
- defaultURL = "";
- }
-
- const title = document.querySelectorAll('meta[property="og:title"]')[0]
- .getAttribute("content");
- let series = g("mastodon_share_series").innerText;
- if (series != "") {
- series = `#${series} `;
- }
- const tags = g("mastodon_share_tags");
- const articleURL = u();
-
- const tootTemplate = `${title}
-
-${articleURL}
-
-${series}${tags.innerText} @cadey@pony.social`;
-
- const instanceBox = (
- <input type="text" placeholder="https://pony.social" value={defaultURL} />
- );
- const tootBox = <textarea rows={6} cols={40}>{tootTemplate}</textarea>;
-
- const doShare = () => {
- let instanceURL = instanceBox.value;
-
- if (!instanceURL.startsWith("https://")) {
- instanceURL = `https://${instanceURL}`;
- }
-
- localStorage["mastodon_instance"] = instanceURL;
- const text = tootBox.value;
- const mastodonURL = u(instanceURL + "/share", { text, visibility: "public" });
- console.log({ text, mastodonURL });
- window.open(mastodonURL, "_blank");
- };
-
- const shareButton = <button onclick={doShare}>Share</button>;
-
- x(root);
-
- root.appendChild(
- <div>
- <details>
- <summary>Share on Mastodon</summary>
- <span>Instance URL (https://mastodon.example)</span>
- <br />
- {instanceBox}
- <br />
- {tootBox}
- <br />
- {shareButton}
- </details>
- </div>,
- );
-});
diff --git a/src/frontend/state.js b/src/frontend/state.js
new file mode 100644
index 0000000..195fd24
--- /dev/null
+++ b/src/frontend/state.js
@@ -0,0 +1,5 @@
+export const useState = (value = undefined) => {
+ return [() => value, (x) => {
+ value = x;
+ }];
+};
diff --git a/src/frontend/wasiterm.tsx b/src/frontend/wasiterm.tsx
deleted file mode 100644
index a9b0ea5..0000000
--- a/src/frontend/wasiterm.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import { wasi as wasiMod, xeact, /*xterm*/ } from "./deps.ts";
-
-const { x, t } = xeact;
-//const { Terminal } = xterm;
-const { WASI } = wasiMod;
-
-const init = async (rootElem: Element, wasmURL: string) => {
- //const termElem = <div></div>;
- const termElem = <code></code>;
-
- //const term = new Terminal();
- //term.open(termElem);
-
- const runProgram = async () => {
- await wasiMod.init(new URL("https://cdn.xeiaso.net/file/christine-static/wasm/5410143de81b20061e9750d1cf80aceef56d2938ab949e30dd7b13fa699307ad.wasm"));
-
- //term.writeln(`loading ${wasmURL}`);
- termElem.appendChild(t(`loading ${wasmURL}`));
-
- const wasi = new WASI({
- env: {
- "HOSTNAME": "pyra",
- },
- })
-
- const moduleBytes = fetch(wasmURL);
- const module = await WebAssembly.compileStreaming(moduleBytes);
- await wasi.instantiate(module, {});
- //term.writeln("executing");
- termElem.appendChild(t("executing"));
- let exitCode = wasi.start();
- let stdout = wasi.getStdoutString();
- console.log(`${stdout}\n\n(exit code: ${exitCode})`);
- //term.writeln(`${stdout}\n\n(exit code: ${exitCode})`);
- termElem.appendChild(t(`${stdout}\n\n(exit code: ${exitCode})`));
- };
-
- const runButton = <button onclick={runProgram}>Run</button>;
-
- const root = <div>
- <link rel="stylesheet" href="https://cdn.xeiaso.net/file/christine-static/wasm/xterm/xterm-a36f07105014cc9220cae423d97c30d1a59fdb0230da8e53bb74bb0faade4310.css" type="text/css" />
- {runButton}
- <pre>{termElem}</pre>
- </div>;
-
- x(rootElem);
- rootElem.appendChild(root);
-
- //term.writeln(`loading ${wasmURL}`);
- //term.writeln(`${stdout}\n\n(exit code: ${exitCode})`);
-}
-
-export { init };
diff --git a/src/tmpl/blog.rs b/src/tmpl/blog.rs
index f940db4..18d66e7 100644
--- a/src/tmpl/blog.rs
+++ b/src/tmpl/blog.rs
@@ -1,6 +1,7 @@
use super::{base, nag};
use crate::post::{schemaorg::Article, Post};
use maud::{html, Markup, PreEscaped};
+use xesite_templates::xeact_component;
fn post_metadata(post: &Post) -> Markup {
let art: Article = post.into();
@@ -28,11 +29,19 @@ fn post_metadata(post: &Post) -> Markup {
}
fn share_button(post: &Post) -> Markup {
+ return xeact_component("MastodonShareButton", serde_json::json!({
+ "title": post.front_matter.title,
+ "series": post.front_matter.series,
+ "tags": post.front_matter.tags.as_ref().unwrap_or(&Vec::new())
+ }));
+}
+
+fn share_button_old(post: &Post) -> Markup {
html! {
div # mastodon_share_button {}
div # mastodon_share_series style="display:none" {(post.front_matter.series.as_ref().unwrap_or(&"".to_string()))}
div # mastodon_share_tags style="display:none" {@for tag in post.front_matter.tags.as_ref().unwrap_or(&Vec::new()) {"#" (tag) " "}}
- script r#type="module" src="/static/js/mastodon_share_button.js" {}
+ script type="module" src="/static/js/mastodon_share_button.js" {}
}
}