+ {props.options.header.left}
+
+ {props.options.header.right}
{props.options.config.github && (
diff --git a/src/lib/render.ts b/src/lib/render.ts
index 8bf6aff..472b1b7 100644
--- a/src/lib/render.ts
+++ b/src/lib/render.ts
@@ -3,7 +3,7 @@ import { extract } from "std/encoding/front_matter/any.ts";
import { renderToString } from "preact-render-to-string";
import { page } from "./page.tsx";
import { get_route_map, resolve_file } from "./route_map.ts";
-import type { Config, Magic } from "./types.ts";
+import type { Config, Magic, PluginResult } from "./types.ts";
import {
consume,
@@ -13,6 +13,7 @@ import {
tw,
} from "https://esm.sh/@twind/core@1.1.3";
import presetTailwind from "https://esm.sh/@twind/preset-tailwind@1.1.4";
+import { getHeaderElements } from "../utils.ts";
install(defineConfig({
presets: [presetTailwind()],
@@ -22,6 +23,7 @@ export async function render(
config: Config,
magic: Magic,
path: string,
+ plugins: PluginResult[],
dev = false,
) {
const [file_type, markdown] = resolve_file(resolve("pages", path));
@@ -43,6 +45,7 @@ export async function render(
magic,
file_type,
dev,
+ header: getHeaderElements(plugins),
},
}),
);
diff --git a/src/lib/types.ts b/src/lib/types.ts
index 1d1184b..8ffc5b3 100644
--- a/src/lib/types.ts
+++ b/src/lib/types.ts
@@ -1,10 +1,37 @@
+import type { JSX } from "preact";
+export type { JSX } from "preact";
+
+type MaybePromise = T | Promise;
+
export interface Config {
title: string;
github?: string;
copyright?: string;
footer?: Record;
+ plugins?: string[];
}
+export type PluginResult = {
+ /**
+ * Header bar elements
+ */
+ header?: {
+ left?: JSX.Element;
+ right?: JSX.Element;
+ };
+ /**
+ * A method that returns a list of routes to handle.
+ * This has to be a finite list for static site building.
+ */
+ routes?: string[];
+ /**
+ * The method for actually handling whatever route
+ */
+ handle?: (req: Request) => MaybePromise;
+};
+
+export type Plugin = () => MaybePromise;
+
export interface Magic {
background: string;
}
diff --git a/src/utils.ts b/src/utils.ts
index c524ca6..1bdddf3 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -2,7 +2,7 @@ import remarkGfm from "https://esm.sh/remark-gfm@3.0.1";
import rehypeHighlight from "https://esm.sh/@mapbox/rehype-prism@0.8.0";
import { compile } from "https://esm.sh/@mdx-js/mdx@2.2.1";
import { render } from "gfm";
-import type { FileTypes } from "./lib/types.ts";
+import type { FileTypes, JSX, Plugin, PluginResult } from "./lib/types.ts";
import "https://esm.sh/prismjs@1.29.0/components/prism-json?no-check";
import "https://esm.sh/prismjs@1.29.0/components/prism-bash?no-check";
@@ -49,3 +49,28 @@ export async function renderMD(data: string) {
disableHtmlSanitization: true,
});
}
+
+export function loadPlugins(plugins: string[]) {
+ return Promise.all(
+ plugins.map(async (plugin) => ((await import(plugin)).default as Plugin)()),
+ );
+}
+
+export function getHeaderElements(plugins: PluginResult[]) {
+ const header = {
+ left: [] as JSX.Element[],
+ right: [] as JSX.Element[],
+ };
+
+ for (const plugin of plugins) {
+ if (!plugin.header) continue;
+ if (plugin.header.left) {
+ header.left = [...header.left, plugin.header.left];
+ }
+ if (plugin.header.right) {
+ header.right = [...header.right, plugin.header.right];
+ }
+ }
+
+ return header;
+}
diff --git a/www/pages/core-concepts/plugins.md b/www/pages/core-concepts/plugins.md
new file mode 100644
index 0000000..bce19d7
--- /dev/null
+++ b/www/pages/core-concepts/plugins.md
@@ -0,0 +1,36 @@
+---
+title: Plugins
+description: Pyro was designed from the ground up to be no-config and incredibly fast.
+index: 3
+---
+
+While Pyro is designed to have all of the basic features you will need built-in,
+there are some cases where one would want to extend the feature set. This can be
+achieved with plugins.
+
+A simple plugin will look like so:
+
+```tsx
+import { Plugin } from "https://deno.land/x/pyro/src/lib/types.ts";
+
+const plugin: Plugin = () => {
+ return {
+ header: {
+ left: Demo,
+ right: Home,
+ },
+ routes: ["/demo.png"],
+ handle: async () => {
+ const req = await fetch(
+ "https://github.com/lino-levan/pyro/raw/main/www/static/icon.png",
+ );
+ return req;
+ },
+ };
+};
+
+export default plugin;
+```
+
+More examples can be found in
+[the official plugins](https://github.com/lino-levan/pyro/tree/main/plugins).
diff --git a/www/pages/getting-started/configuration.md b/www/pages/getting-started/configuration.md
index 0cceb6d..267d933 100644
--- a/www/pages/getting-started/configuration.md
+++ b/www/pages/getting-started/configuration.md
@@ -33,6 +33,10 @@ footer:
Community:
- Discord https://discord.gg/XJMMSSC4Fj
- Support https://github.com/lino-levan/pyro/issues/new
+
+# Any plugins you want to be used (optional)
+plugin:
+ - https://deno.land/x/pyro/plugins/demo.tsx
```
## How do I configure individual pages?