Extension
GemBook renders Markdown on the server and extends the Markdown syntax, custom elements can be used in Markdown to extend documents.
Markdown enhancement
Code blocks info string
Syntax:
<language>? <filename>? <status>? <highlight>?language: The language of syntax highlighting, highlighted when buildingfilename: Displayed above the code, it is also the tab name in and the file name in<gbp-code-group><gbp-sandpack>status: In code groups,activeis displayed by default,hiddenis not displayed in the tabshighlight: The lines to highlight, e.g.1,3-5, negative numbers count from the end
Use highlight example:
# Code Block Info
```md 1
# Code Block Info
```Fixed heading anchor hash
By default, hash is generated based on the title text field, but sometimes you need to fix the hash, such as internationalization.
### Fixed heading anchor hash {#fixed-hash}Highlight blockquote
> [!TIP]
> This is [highlight blockquote](https://github.com/orgs/community/discussions/16925)NOTE
This is
[!NOTE]
TIP
This is
[!TIP]
IMPORTANT
This is
[!IMPORTANT]
WARNING
This is
[!WARNING]
CAUTION
This is
[!CAUTION]
Page regions
Use <gbp-content>
nav-inside: In the navigation barsidebar-before: At the top of the sidebarmain-before: Before the documentmain-after: After the document
<gbp-content slot="main-before">
<p>This document is deprecated.</p>
</gbp-content>Plugins
GemBook uses custom elements as a plugin system, the
<gbp-raw src="package.json"></gbp-raw>Some plugins are rendered on the server when building (e.g. <gbp-raw>, <gbp-include>, <gbp-api>), they don't load any code in the browser,
others are rendered on the server and work in the browser (e.g. <gbp-code-group>, <gbp-sandpack>).
TIP
Write the start tag of a plugin in one line, otherwise Markdown may treat it as text
VSCode cannot use Emmet in Markdown files by default. You can enable it through settings:
"emmet.excludeLanguages": [], "emmet.includeLanguages": {"markdown": "html"},
Custom elements
Write your own plugins as custom elements, for example with Gem:
import { adoptedStyle, css, customElement, GemElement, html, mounted, template } from '@mantou/gem';
// 从 esm.sh 加载,版本由 `<gbp-import>` 的 `dependencies` 指定
// @ts-expect-error
import confetti from 'canvas-confetti';
const style = css`
:scope {
display: block;
padding: 1rem;
border-radius: 0.5rem;
background: rgb(from currentColor r g b / 0.05);
}
`;
@customElement('my-plugin-hello')
@adoptedStyle(style)
export class MyPluginHelloElement extends GemElement {
@mounted()
#init = () => {
const observer = new IntersectionObserver(([entry]) => {
if (!entry.isIntersecting) return;
confetti({ particleCount: 50, origin: { x: 0.5, y: 0.5 } });
observer.disconnect();
});
observer.observe(this);
return () => observer.disconnect();
};
@template()
#content = () => html`Hello, <code>${'<gbp-import>'}</code>`;
}Use <gbp-import>
<gbp-import name="my-plugin-hello" dependencies="canvas-confetti" src="docs/hello.ts"></gbp-import>Or add it to plugins
<my-plugin-hello></my-plugin-hello>Plugin API
Bundled plugins (site-wide plugins and local <gbp-import>gem-book/plugin, it is available even if gem-book is not installed in the project (e.g. using npx):
import { adoptedStyle, connectStore, css, customElement, GemElement, html, template } from '@mantou/gem';
import { bookStore, getGlobal, theme } from 'gem-book/plugin';
const style = css`
:scope {
color: ${theme.primaryColor};
}
`;
@customElement('my-page-info')
@adoptedStyle(style)
@connectStore(bookStore)
export class MyPageInfoElement extends GemElement {
@template()
#content = () => html`${bookStore.doc?.title} (${bookStore.lang}) v${getGlobal('version')}`;
}bookStore: the data of the website and the current page, also available when rendering on the server, updated when navigatingconfig: the config of the websitelang: the current languagepath: the path of the current page without the language prefix, e.g./v1/guideversion: the old version of the current page, empty for the latest versionsite: the navigation and sidebar of the website, including old versionsdoc: the current document (title, headings, etc.), empty on other pages (e.g. 404)
theme: the of the theme, e.g.CSS variables theme.primaryColorgetGlobal(name): the value of ,globalnamecan be a path, e.g.pkg.versionhtmlTemplate(html): renders an HTML string as a template, custom elements in it are also rendered on the server
gem-book/plugin/server is for code only executed on the server, such as server elements marked with @server(), the current document is the document of the current page:
import fs from 'node:fs';
import { createState, customElement, GemElement, html, mounted, template } from '@mantou/gem';
import { server } from '@mantou/gemkit';
import { htmlTemplate } from 'gem-book/plugin';
import { renderMarkdown, resolveSrc } from 'gem-book/plugin/server';
// Only outputs HTML, the code is not loaded in the browser
@customElement('my-changelog')
@server()
export class MyChangelogElement extends GemElement {
#state = createState({ html: '' });
@server()
@mounted()
#init = async () => {
const resolved = resolveSrc(this.getAttribute('src') || '');
if ('file' in resolved) this.#state({ html: await renderMarkdown(fs.readFileSync(resolved.file, 'utf8')) });
};
@template()
#content = () => htmlTemplate(this.#state.html);
}resolveSrc(src): resolvessrclike built-in plugins: a URL, or a file relative to the current document (starting with.) or the current working directory, supportsimportMaprenderMarkdown(md): renders Markdown like documents (syntax highlighting, relative links, server plugins), returns HTMLgetGlobal(name): same as above
