@staticbolt/docs 0.0.1-beta.0 → 0.0.1-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -107,17 +107,17 @@ Copy this as `sources/layouts/document.layout.html` and edit the names:
107
107
 
108
108
  To change the page structure, copy `node_modules/@staticbolt/docs/sources/parts/document/document.part.html` into the project and point the layout at the copy; it references the other parts by package path, so it works as is.
109
109
 
110
- | Part | Role |
111
- | --------------------- | -------------------------------------------------------------------------------- |
112
- | `document` | The whole page. `title`, `minHeading`, `maxHeading`, slot `icons`, default slot. |
113
- | `header` | Takes `title`. Slot `icons` for the social links. |
114
- | `sidebar` | Filled by `contentPlugin`. Slot `icons` for the social links. |
115
- | `table-of-content` | `-desktop` takes `minHeading` and `maxHeading`, `-mobile` none. |
116
- | `pagination-links` | Previous/next links read from the sidebar. |
117
- | `callouts` | Build-time script turning collapsible callouts into accordions. |
118
- | `mermaid` | Renders ` ```mermaid ` fences. Removes itself from pages without. |
119
- | `color-scheme-switch` | Already inside header and sidebar. |
120
- | `badge` | Used by the sidebar for `data.badgeText` / `data.badgeVariant`. |
110
+ | Part | Role |
111
+ | --------------------- | -------------------------------------------------------------------------------------------- |
112
+ | `document` | The whole page. `title`, `minHeading`, `maxHeading`, `mermaid`, slot `icons`, default slot. |
113
+ | `header` | Takes `title`. Slot `icons` for the social links. |
114
+ | `sidebar` | Filled by `contentPlugin`. Slot `icons` for the social links. |
115
+ | `table-of-content` | `-desktop` takes `minHeading` and `maxHeading`, `-mobile` none. |
116
+ | `pagination-links` | Previous/next links read from the sidebar. |
117
+ | `callouts` | Build-time script turning collapsible callouts into accordions. |
118
+ | `mermaid` | Renders ` ```mermaid ` fences. Removes itself from pages without. Needs `mermaid` installed. |
119
+ | `color-scheme-switch` | Already inside header and sidebar. |
120
+ | `badge` | Used by the sidebar for `data.badgeText` / `data.badgeVariant`. |
121
121
 
122
122
  ## Styles
123
123
 
package/lib/ec-config.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as pluginCollapsible } from "./collapsible-plugin-Bwt7dmKE.mjs";
1
+ import { n as pluginCollapsible, t as htmlWithTypeScriptScripts } from "./html-ts-script-lang-DthQZbmB.mjs";
2
2
  import { pluginCollapsibleSections } from "@expressive-code/plugin-collapsible-sections";
3
3
  import { pluginLineNumbers } from "@expressive-code/plugin-line-numbers";
4
4
  import { pluginFileIcons } from "@xt0rted/expressive-code-file-icons";
@@ -17,6 +17,7 @@ const rehypeExpressiveCodeOptions = {
17
17
  collapseStyle: "collapsible-auto"
18
18
  },
19
19
  useDarkModeMediaQuery: false,
20
+ shiki: { langs: [htmlWithTypeScriptScripts] },
20
21
  styleOverrides: {
21
22
  codeBackground: "var(--sb-code-background)",
22
23
  borderColor: "var(--sb-color-gray-5)",
@@ -1,5 +1,7 @@
1
1
  import { definePlugin } from "@expressive-code/core";
2
2
  import { h } from "@expressive-code/core/hast";
3
+ import htmlLangs from "@shikijs/langs/html";
4
+ import tsLangs from "@shikijs/langs/typescript";
3
5
  //#region plugins/collapsible-plugin.ts
4
6
  /**
5
7
  * Collapses opted-in code blocks to a fixed number of preview lines.
@@ -129,4 +131,46 @@ function pluginCollapsible(options = {}) {
129
131
  });
130
132
  }
131
133
  //#endregion
132
- export { pluginCollapsible as t };
134
+ //#region plugins/html-ts-script-lang.ts
135
+ const typeAlternation = ["application/x-typescript"].map((t) => t.replace(/[/+.-]/g, String.raw`\$&`)).join("|");
136
+ const typescriptScriptPattern = {
137
+ begin: String.raw`(?i:(?=type\s*=\s*(["']?)(?:${typeAlternation})["'>\s]))`,
138
+ end: "((<))(?=/(?i:script))",
139
+ endCaptures: {
140
+ "0": { name: "meta.tag.metadata.script.end.html" },
141
+ "1": { name: "punctuation.definition.tag.begin.html" },
142
+ "2": { name: "source.ts" }
143
+ },
144
+ patterns: [{
145
+ begin: String.raw`\G`,
146
+ end: "(>)",
147
+ endCaptures: { "1": { name: "punctuation.definition.tag.end.html" } },
148
+ name: "meta.tag.metadata.script.start.html",
149
+ patterns: [{ include: "#attribute" }]
150
+ }, {
151
+ begin: String.raw`(?!\G)`,
152
+ end: "(?=</(?i:script))",
153
+ name: "source.ts",
154
+ patterns: [{ include: "source.ts" }]
155
+ }]
156
+ };
157
+ /**
158
+ * Shiki's bundled HTML grammar only highlights `<script>` contents when the `type` attribute is a known JavaScript type, leaving
159
+ * TypeScript script blocks unhighlighted. These registrations are a drop-in replacement for the `html` language that also embeds
160
+ * TypeScript.
161
+ */
162
+ const htmlWithTypeScriptScripts = (() => {
163
+ const langs = structuredClone(htmlLangs);
164
+ const html = langs.find((lang) => lang.scopeName === "text.html.basic");
165
+ if (!html) {
166
+ console.warn("[html-ts-script-lang] Shiki's HTML grammar was not found; TypeScript script blocks stay unhighlighted.");
167
+ return [...langs, ...tsLangs];
168
+ }
169
+ const scriptTag = html.repository?.["tags-valid"]?.patterns?.[1]?.patterns?.[0]?.patterns?.[0];
170
+ if (scriptTag?.patterns) scriptTag.patterns.splice(2, 0, typescriptScriptPattern);
171
+ else console.warn("[html-ts-script-lang] Shiki's HTML script-tag patterns moved; TypeScript script blocks stay unhighlighted.");
172
+ html.embeddedLangs = [...html.embeddedLangs ?? [], "typescript"];
173
+ return [...langs, ...tsLangs];
174
+ })();
175
+ //#endregion
176
+ export { pluginCollapsible as n, htmlWithTypeScriptScripts as t };
package/lib/index.d.mts CHANGED
@@ -2,6 +2,7 @@ import { Plugin } from "@staticbolt/core";
2
2
  import { RehypeExpressiveCodeOptions } from "rehype-expressive-code";
3
3
  import { ExpressiveCodePlugin } from "@expressive-code/core";
4
4
  import { Root } from "hast";
5
+ import { LanguageRegistration } from "shiki";
5
6
  import { Plugin as Plugin$1 } from "unified";
6
7
  import { Root as Root$1 } from "mdast";
7
8
  import { Node } from "unist";
@@ -244,8 +245,11 @@ declare function normalizeTitle(string: string): string;
244
245
  //#endregion
245
246
  //#region plugins/ec-cache.d.ts
246
247
  declare function sha1(input: string): string;
247
- /** Invalidates all cached entries when the expressive-code version or its configuration changes. */
248
- declare const ecCacheSalt: string;
248
+ /**
249
+ * Invalidates all cached entries when the expressive-code version or its configuration changes.\
250
+ * Functions in the options, plugin hooks included, count by name only.
251
+ */
252
+ declare function ecCacheSalt(options?: RehypeExpressiveCodeOptions): string;
249
253
  declare class PersistentCache {
250
254
  private readonly filePath;
251
255
  private readonly entries;
@@ -266,6 +270,14 @@ declare function ecCachePlugin(): Plugin;
266
270
  */
267
271
  declare function cachedRehypeExpressiveCode(this: unknown, options: RehypeExpressiveCodeOptions): (tree: Root, ...rest: unknown[]) => Promise<Root | undefined>;
268
272
  //#endregion
273
+ //#region plugins/html-ts-script-lang.d.ts
274
+ /**
275
+ * Shiki's bundled HTML grammar only highlights `<script>` contents when the `type` attribute is a known JavaScript type, leaving
276
+ * TypeScript script blocks unhighlighted. These registrations are a drop-in replacement for the `html` language that also embeds
277
+ * TypeScript.
278
+ */
279
+ declare const htmlWithTypeScriptScripts: LanguageRegistration[];
280
+ //#endregion
269
281
  //#region plugins/katex-fonts-plugin.d.ts
270
282
  /**
271
283
  * Ships the KaTeX fonts `katex.css` loads. In production, references into the theme's `sources/assets` are rewritten to
@@ -334,6 +346,9 @@ declare module "mdast" {
334
346
  interface PhrasingContentMap {
335
347
  obsidianTag: ObsidianTagNode;
336
348
  }
349
+ interface RootContentMap {
350
+ obsidianTag: ObsidianTagNode;
351
+ }
337
352
  }
338
353
  /**
339
354
  * Remark plugin that renders Obsidian tags.
@@ -368,4 +383,4 @@ declare function pagefindPlugin({ glob }?: PagefindPluginOptions): Plugin;
368
383
  //#region plugins/wrap-table.d.ts
369
384
  declare function wrapTables(): (tree: Node) => void;
370
385
  //#endregion
371
- export { CollapsiblePluginOptions, HtmlCodeBlockPlugin, PersistentCache, RemarkObsidianCommentsOptions, cachedRehypeExpressiveCode, contentPlugin, ecCachePlugin, ecCacheSalt, katexFontsPlugin, llmsPlugin, normalizeTitle, pagefindPlugin, pluginCollapsible, rehypeMermaidFence, remarkObsidianComments, remarkObsidianTags, sha1, wrapTables };
386
+ export { CollapsiblePluginOptions, HtmlCodeBlockPlugin, PersistentCache, RemarkObsidianCommentsOptions, cachedRehypeExpressiveCode, contentPlugin, ecCachePlugin, ecCacheSalt, htmlWithTypeScriptScripts, katexFontsPlugin, llmsPlugin, normalizeTitle, pagefindPlugin, pluginCollapsible, rehypeMermaidFence, remarkObsidianComments, remarkObsidianTags, sha1, wrapTables };
package/lib/plugins.mjs CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staticbolt/docs",
3
- "version": "0.0.1-beta.0",
3
+ "version": "0.0.1-beta.2",
4
4
  "description": "Documentation theme for staticbolt: layout parts, styles, and plugins shared by every docs site.",
5
5
  "author": "Ahmed ALABSI",
6
6
  "license": "MIT",
@@ -83,7 +83,7 @@
83
83
  "@eslint/markdown": "^8.0.1",
84
84
  "@html-eslint/eslint-plugin": "^0.60.0",
85
85
  "@html-eslint/parser": "^0.60.0",
86
- "@staticbolt/core": "^1.0.0-beta.29",
86
+ "@staticbolt/core": "beta",
87
87
  "@stylistic/stylelint-config": "^5.0.0",
88
88
  "@trivago/prettier-plugin-sort-imports": "^6.0.2",
89
89
  "@types/hast": "^3.0.5",
@@ -1,5 +1,5 @@
1
1
  <!-- ::: Collapsible callouts (`> [!note]-`) render as `<details>`; swap them for sv-accordion ::: -->
2
- <script build-time>
2
+ <script type="application/x-typescript" build-time>
3
3
  for (const callout of document.querySelectorAll("details.callout")) {
4
4
  const accordion = document.createElement("sv-accordion");
5
5
  accordion.setAttribute("data-callout", callout.getAttribute("data-callout"));
@@ -29,13 +29,12 @@
29
29
  </div>
30
30
 
31
31
  <insert selector="head">
32
- <script>
33
- // @ts-check
32
+ <script type="application/x-typescript">
34
33
  (() => {
35
34
  const STORAGE_KEY = "color-scheme";
36
35
  const PREFERS_DARK = window.matchMedia("(prefers-color-scheme: dark)");
37
36
 
38
- let currentScheme = PREFERS_DARK.matches ? "dark" : "light";
37
+ let currentScheme: "light" | "dark" = PREFERS_DARK.matches ? "dark" : "light";
39
38
 
40
39
  // Override with stored value
41
40
  const stored = window.localStorage.getItem(STORAGE_KEY);
@@ -44,11 +43,7 @@
44
43
  document.documentElement.style.colorScheme = stored;
45
44
  }
46
45
 
47
- /**
48
- * @param {"light" | "dark"} scheme
49
- * @param {NodeListOf<HTMLButtonElement>} buttons
50
- */
51
- const applyScheme = (scheme, buttons) => {
46
+ const applyScheme = (scheme: "light" | "dark", buttons: NodeListOf<HTMLButtonElement>) => {
52
47
  const isDark = scheme === "dark";
53
48
 
54
49
  for (const button of buttons) {
@@ -62,8 +57,7 @@
62
57
  };
63
58
 
64
59
  const onReady = () => {
65
- /** @type {NodeListOf<HTMLButtonElement>} */
66
- const colorSchemeButtons = document.querySelectorAll(".color-scheme-switch");
60
+ const colorSchemeButtons = document.querySelectorAll<HTMLButtonElement>(".color-scheme-switch");
67
61
 
68
62
  const isDark = currentScheme === "dark";
69
63
 
@@ -3,6 +3,7 @@
3
3
  @attr title - Site name shown in the header.
4
4
  @attr minHeading - Lowest heading level in the table of contents. Default 2.
5
5
  @attr maxHeading - Highest heading level in the table of contents. Default 3.
6
+ @attr mermaid - Present to render mermaid fences. Needs the mermaid package.
6
7
  @slot - The page content.
7
8
  @slot logo - Rendered before the title in the header link.
8
9
  @slot icons - Links rendered in the header and the sidebar footer.
@@ -45,10 +46,13 @@
45
46
  </div>
46
47
 
47
48
  <part src="@staticbolt/docs/parts/callouts/callouts.part.html"></part>
49
+
50
+ {{ $data.mermaid === undefined ? "" : `
48
51
  <part src="@staticbolt/docs/parts/mermaid/mermaid.part.html"></part>
52
+ ` }}
49
53
 
50
54
  <!-- ::: Page title and skip link ::: -->
51
- <script build-time>
55
+ <script type="application/x-typescript" build-time>
52
56
  const heading = document.querySelector("h1[id]");
53
57
  const title = document.querySelector("title");
54
58
  const ogTitle = document.querySelector('meta[property="og:title"]');
@@ -36,18 +36,14 @@
36
36
  </header>
37
37
 
38
38
  <!-- ::: Toggle the sidebar ::: -->
39
- <script type="module">
40
- // @ts-check
41
-
39
+ <script type="application/x-typescript" module>
42
40
  (() => {
43
- /** @type {HTMLButtonElement | null} */
44
- const menuTrigger = document.querySelector(".side-menu-trigger-button");
41
+ const menuTrigger = document.querySelector<HTMLButtonElement>(".side-menu-trigger-button");
45
42
  if (!menuTrigger) {
46
43
  return;
47
44
  }
48
45
 
49
- /** @type {HTMLDivElement | null} */
50
- const sidebar = document.querySelector(".sidebar");
46
+ const sidebar = document.querySelector<HTMLDivElement>(".sidebar");
51
47
  if (!sidebar) {
52
48
  menuTrigger.remove();
53
49
  return;
@@ -2,7 +2,7 @@
2
2
  <script type="module" data-mermaid-script src="@staticbolt/docs/scripts/mermaid.ts"></script>
3
3
 
4
4
  <!-- ::: Drop the script from pages without a diagram ::: -->
5
- <script build-time>
5
+ <script type="application/x-typescript" build-time>
6
6
  const mermaidScript = document.querySelector("[data-mermaid-script]");
7
7
 
8
8
  if (mermaidScript) {
@@ -11,7 +11,7 @@
11
11
  <pagefind-modal></pagefind-modal>
12
12
 
13
13
  <!-- ::: Path from this page to the Pagefind bundle, so the site works on any subpath ::: -->
14
- <script type="module" build-time>
14
+ <script type="application/x-typescript" build-time module>
15
15
  import { dirname, relative } from "node:path";
16
16
 
17
17
  const modal = document.querySelector("pagefind-modal");
@@ -21,14 +21,11 @@
21
21
  <insert selector="body">
22
22
  <link class="pagefind-stylesheet" as="style" bundle href="./pagefind.style.css" rel="preload" />
23
23
 
24
- <script type="module">
25
- // @ts-check
26
-
24
+ <script type="application/x-typescript" module>
27
25
  (async () => {
28
26
  // ::: Lazy-load the Pagefind UI. :::
29
27
 
30
- /** @type {HTMLLinkElement | null} */
31
- const pagefindStyleSheet = document.querySelector(".pagefind-stylesheet");
28
+ const pagefindStyleSheet = document.querySelector<HTMLLinkElement>(".pagefind-stylesheet");
32
29
  if (!pagefindStyleSheet) {
33
30
  console.error("Could not find pagefind stylesheet element");
34
31
  return;
@@ -53,15 +50,13 @@
53
50
 
54
51
  // ::: Open the Pagefind modal when the search button is clicked. :::
55
52
 
56
- /** @type {(HTMLElement & { openModal(): void }) | null} */
57
- const pagefindModalTrigger = document.querySelector("pagefind-modal-trigger");
53
+ const pagefindModalTrigger = document.querySelector<HTMLElement & { openModal(): void }>("pagefind-modal-trigger");
58
54
  if (!pagefindModalTrigger) {
59
55
  console.error("Could not find pagefind modal trigger element");
60
56
  return;
61
57
  }
62
58
 
63
- /** @type {HTMLButtonElement | null} */
64
- const triggerModalIconButton = document.querySelector(".pagefind-modal-trigger-button");
59
+ const triggerModalIconButton = document.querySelector<HTMLButtonElement>(".pagefind-modal-trigger-button");
65
60
  if (!triggerModalIconButton) {
66
61
  console.error("Could not find pagefind modal trigger button element");
67
62
  return;
@@ -25,7 +25,7 @@
25
25
  </div>
26
26
 
27
27
  <!-- This script will run once at build time for each page -->
28
- <script type="module" build-time>
28
+ <script type="application/x-typescript" build-time module>
29
29
  // ::: Get current link and populate previous/next links :::
30
30
  import { join, dirname, normalize } from "node:path";
31
31
 
@@ -14,7 +14,7 @@
14
14
  </div>
15
15
  </div>
16
16
 
17
- <script type="module" build-time>
17
+ <script type="application/x-typescript" build-time module>
18
18
  import { join, dirname, normalize } from "node:path";
19
19
 
20
20
  // ::: Mark current page links as active and expand matching accordions. :::
@@ -42,17 +42,16 @@
42
42
  </script>
43
43
 
44
44
  <!-- ::: Persist sidebar navigation state across page loads. ::: -->
45
- <script>
45
+ <script type="application/x-typescript">
46
46
  (() => {
47
- /** @type {NodeListOf<HTMLDivElement>} */
48
- const accordions = document.querySelectorAll(".sidebar-accordion");
47
+ const accordions = document.querySelectorAll<HTMLDivElement>(".sidebar-accordion");
49
48
 
50
49
  // ::: Restore the accordion's previous open state. :::
51
50
  try {
52
- const state = JSON.parse(sessionStorage.getItem("sidebar-state") ?? {});
51
+ const state = JSON.parse(sessionStorage.getItem("sidebar-state") ?? "{}");
53
52
 
54
53
  for (const [accordionIndex, opened] of Object.entries(state)) {
55
- const accordion = /** @type {HTMLDivElement} */ (accordions[accordionIndex]);
54
+ const accordion = accordions[Number(accordionIndex)];
56
55
  if (!accordion) continue;
57
56
 
58
57
  // Keep accordions containing the active link expanded by default
@@ -6,7 +6,7 @@
6
6
  </div>
7
7
  </aside>
8
8
 
9
- <script build-time>
9
+ <script type="application/x-typescript" build-time>
10
10
  const tocContents = document.querySelectorAll(".toc-content");
11
11
  const headings = document
12
12
  .querySelectorAll("h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]")
@@ -65,7 +65,7 @@
65
65
  }
66
66
  </script>
67
67
 
68
- <script>
68
+ <script type="application/x-typescript">
69
69
  (() => {
70
70
  // All links in the mobile and desktop TOC
71
71
  const links = document.querySelectorAll(".toc-list a");
@@ -12,7 +12,7 @@
12
12
  </div>
13
13
 
14
14
  <!-- ::: Close the accordion when a link is clicked ::: -->
15
- <script type="module">
15
+ <script type="application/x-typescript" module>
16
16
  const tocAccordion = document.querySelector(".toc-accordion");
17
17
  const tocLinks = document.querySelectorAll(".toc-accordion a");
18
18