@opentf/web-docs 0.1.0 → 0.3.0
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/build/blog-posts-plugin.js +127 -0
- package/build/docs-nav-plugin.js +17 -10
- package/build/feed.js +136 -0
- package/build/index.js +5 -0
- package/build/last-updated-plugin.js +98 -0
- package/build/last-updated.js +45 -0
- package/build/llms.js +156 -0
- package/build/pagefind.js +58 -0
- package/build/reading-time.js +16 -0
- package/components/BlogLayout.jsx +77 -0
- package/components/Breadcrumbs.jsx +30 -27
- package/components/Callout.jsx +7 -7
- package/components/Card.jsx +21 -0
- package/components/Cards.jsx +11 -0
- package/components/CodeBlock.jsx +39 -0
- package/components/DocsLayout.jsx +66 -6
- package/components/LastUpdated.jsx +22 -0
- package/components/NavIcon.jsx +34 -0
- package/components/Navbar.jsx +34 -22
- package/components/NavbarLink.jsx +33 -0
- package/components/Pagination.jsx +37 -33
- package/components/PostBanner.jsx +25 -0
- package/components/PostCard.jsx +19 -0
- package/components/PostList.jsx +17 -0
- package/components/PostMeta.jsx +28 -0
- package/components/ReadingTime.jsx +6 -0
- package/components/Search.jsx +211 -0
- package/components/SearchTrigger.jsx +14 -3
- package/components/Sidebar.jsx +111 -9
- package/components/SidebarNode.jsx +1 -0
- package/components/SidebarToggle.jsx +47 -0
- package/components/Steps.jsx +14 -0
- package/components/Tabs.jsx +4 -2
- package/components/ThemeToggle.jsx +150 -25
- package/components/Tooltip.jsx +19 -0
- package/components/format.js +11 -0
- package/config.js +28 -2
- package/index.js +29 -2
- package/nav-virtual.js +6 -4
- package/package.json +15 -1
- package/posts-virtual.js +17 -0
- package/theme/index.css +1034 -58
- package/updated-virtual.js +10 -0
- package/components/CodeGroup.jsx +0 -28
|
@@ -1,39 +1,164 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// Theme switcher: a subtle ghost icon button that opens a Light / Dark / System menu.
|
|
2
|
+
//
|
|
3
|
+
// The chosen *mode* is persisted to localStorage("theme") as "light" | "dark" |
|
|
4
|
+
// "system" (default "system"). The applied theme is the resolved value — in System
|
|
5
|
+
// mode it follows the OS and live-updates when the OS preference flips. The trigger
|
|
6
|
+
// shows the mode's icon; in System mode it shows the monitor icon plus a small badge of
|
|
7
|
+
// the currently resolved icon (sun/moon), so you can see both the choice and the result.
|
|
8
|
+
//
|
|
9
|
+
// Pair with the no-flash inline script in index.html, which must resolve "system"/none
|
|
10
|
+
// to a concrete theme on first paint.
|
|
4
11
|
|
|
5
12
|
import { onMount } from "@opentf/web";
|
|
6
13
|
|
|
14
|
+
import Tooltip from "./Tooltip.jsx";
|
|
15
|
+
|
|
16
|
+
const STORAGE_KEY = "theme";
|
|
17
|
+
const prefersDark = () =>
|
|
18
|
+
typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
19
|
+
|
|
7
20
|
export default function ThemeToggle() {
|
|
8
|
-
let
|
|
21
|
+
let mode = $state("system"); // "light" | "dark" | "system"
|
|
22
|
+
let resolved = $state("light"); // "light" | "dark"
|
|
23
|
+
let openMenu = $state(false);
|
|
9
24
|
|
|
10
|
-
|
|
11
|
-
const saved =
|
|
12
|
-
localStorage.getItem("theme") ||
|
|
13
|
-
(window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light");
|
|
14
|
-
theme = saved;
|
|
15
|
-
document.documentElement.setAttribute("data-theme", theme);
|
|
16
|
-
});
|
|
25
|
+
const rootRef = $ref();
|
|
17
26
|
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
27
|
+
const resolve = (m) => (m === "system" ? (prefersDark() ? "dark" : "light") : m);
|
|
28
|
+
|
|
29
|
+
const apply = (m) => {
|
|
30
|
+
resolved = resolve(m);
|
|
31
|
+
if (typeof document !== "undefined") {
|
|
32
|
+
document.documentElement.setAttribute("data-theme", resolved);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const setMode = (m) => {
|
|
37
|
+
mode = m;
|
|
38
|
+
openMenu = false;
|
|
39
|
+
try {
|
|
40
|
+
localStorage.setItem(STORAGE_KEY, m);
|
|
41
|
+
} catch {}
|
|
42
|
+
apply(m);
|
|
22
43
|
};
|
|
23
44
|
|
|
45
|
+
onMount(() => {
|
|
46
|
+
const saved = (() => {
|
|
47
|
+
try {
|
|
48
|
+
return localStorage.getItem(STORAGE_KEY);
|
|
49
|
+
} catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
})();
|
|
53
|
+
mode = saved === "light" || saved === "dark" || saved === "system" ? saved : "system";
|
|
54
|
+
apply(mode);
|
|
55
|
+
|
|
56
|
+
// Follow the OS while in System mode.
|
|
57
|
+
const mql = window.matchMedia("(prefers-color-scheme: dark)");
|
|
58
|
+
const onOS = () => mode === "system" && apply("system");
|
|
59
|
+
mql.addEventListener?.("change", onOS);
|
|
60
|
+
|
|
61
|
+
// Close the menu on outside click / Escape.
|
|
62
|
+
const onDocClick = (e) => {
|
|
63
|
+
if (openMenu && rootRef && !rootRef.contains(e.target)) openMenu = false;
|
|
64
|
+
};
|
|
65
|
+
const onKey = (e) => e.key === "Escape" && (openMenu = false);
|
|
66
|
+
document.addEventListener("click", onDocClick);
|
|
67
|
+
document.addEventListener("keydown", onKey);
|
|
68
|
+
|
|
69
|
+
return () => {
|
|
70
|
+
mql.removeEventListener?.("change", onOS);
|
|
71
|
+
document.removeEventListener("click", onDocClick);
|
|
72
|
+
document.removeEventListener("keydown", onKey);
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
|
|
24
76
|
return (
|
|
25
|
-
<
|
|
77
|
+
<div class="otfw-theme" ref={rootRef}>
|
|
78
|
+
<Tooltip text="Change theme">
|
|
79
|
+
<button
|
|
80
|
+
class="otfw-navbar-icon otfw-theme-trigger"
|
|
81
|
+
onclick={(e) => {
|
|
82
|
+
e.stopPropagation();
|
|
83
|
+
openMenu = !openMenu;
|
|
84
|
+
}}
|
|
85
|
+
aria-label="Change theme"
|
|
86
|
+
aria-haspopup="menu"
|
|
87
|
+
aria-expanded={openMenu ? "true" : "false"}
|
|
88
|
+
>
|
|
89
|
+
{() =>
|
|
90
|
+
mode === "system" ? (
|
|
91
|
+
<span class="otfw-theme-trigger-system">
|
|
92
|
+
<MonitorIcon />
|
|
93
|
+
<span class="otfw-theme-trigger-badge">
|
|
94
|
+
{resolved === "dark" ? <MoonIcon small /> : <SunIcon small />}
|
|
95
|
+
</span>
|
|
96
|
+
</span>
|
|
97
|
+
) : mode === "dark" ? (
|
|
98
|
+
<MoonIcon />
|
|
99
|
+
) : (
|
|
100
|
+
<SunIcon />
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
</button>
|
|
104
|
+
</Tooltip>
|
|
105
|
+
|
|
26
106
|
{() =>
|
|
27
|
-
|
|
28
|
-
<
|
|
29
|
-
<
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
107
|
+
openMenu ? (
|
|
108
|
+
<div class="otfw-theme-menu" role="menu">
|
|
109
|
+
<MenuItem active={mode === "light"} onclick={() => setMode("light")}>
|
|
110
|
+
<SunIcon small /> Light
|
|
111
|
+
</MenuItem>
|
|
112
|
+
<MenuItem active={mode === "dark"} onclick={() => setMode("dark")}>
|
|
113
|
+
<MoonIcon small /> Dark
|
|
114
|
+
</MenuItem>
|
|
115
|
+
<MenuItem active={mode === "system"} onclick={() => setMode("system")}>
|
|
116
|
+
<MonitorIcon small /> System
|
|
117
|
+
</MenuItem>
|
|
118
|
+
</div>
|
|
119
|
+
) : null
|
|
36
120
|
}
|
|
121
|
+
</div>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function MenuItem(props) {
|
|
126
|
+
return (
|
|
127
|
+
<button
|
|
128
|
+
class={props.active ? "otfw-theme-item is-active" : "otfw-theme-item"}
|
|
129
|
+
role="menuitemradio"
|
|
130
|
+
aria-checked={props.active ? "true" : "false"}
|
|
131
|
+
onclick={props.onclick}
|
|
132
|
+
>
|
|
133
|
+
{props.children}
|
|
37
134
|
</button>
|
|
38
135
|
);
|
|
39
136
|
}
|
|
137
|
+
|
|
138
|
+
function SunIcon(props) {
|
|
139
|
+
const s = props.small ? 15 : 17;
|
|
140
|
+
return (
|
|
141
|
+
<svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
142
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364-6.364l-.707.707M6.343 17.657l-.707.707m12.728 0l-.707-.707M6.343 6.343l-.707-.707M12 5a7 7 0 100 14 7 7 0 000-14z" />
|
|
143
|
+
</svg>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function MoonIcon(props) {
|
|
148
|
+
const s = props.small ? 15 : 17;
|
|
149
|
+
return (
|
|
150
|
+
<svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
151
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
|
|
152
|
+
</svg>
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function MonitorIcon(props) {
|
|
157
|
+
const s = props.small ? 15 : 17;
|
|
158
|
+
return (
|
|
159
|
+
<svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
160
|
+
<rect x="2" y="3" width="20" height="14" rx="2" />
|
|
161
|
+
<path d="M8 21h8M12 17v4" />
|
|
162
|
+
</svg>
|
|
163
|
+
);
|
|
164
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// A small hover/focus tooltip. CSS-driven (no JS state) — wrap any element and pass
|
|
2
|
+
// `text`; the bubble shows on hover or keyboard focus of the wrapped content.
|
|
3
|
+
//
|
|
4
|
+
// <Tooltip text="Change theme"><button>…</button></Tooltip>
|
|
5
|
+
//
|
|
6
|
+
// `placement` is "bottom" (default) or "top". The bubble is aria-hidden decorative
|
|
7
|
+
// chrome; give the wrapped control its own `aria-label` for assistive tech.
|
|
8
|
+
|
|
9
|
+
export default function Tooltip(props) {
|
|
10
|
+
const placement = props.placement === "top" ? "otfw-tooltip-top" : "otfw-tooltip-bottom";
|
|
11
|
+
return (
|
|
12
|
+
<span class={`otfw-tooltip ${placement}`}>
|
|
13
|
+
{props.children}
|
|
14
|
+
<span class="otfw-tooltip-bubble" role="tooltip" aria-hidden="true">
|
|
15
|
+
{props.text}
|
|
16
|
+
</span>
|
|
17
|
+
</span>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Small shared formatting helpers for the docs/blog components.
|
|
2
|
+
|
|
3
|
+
// `2026-06-24` (or any Date-parseable value) → `June 24, 2026`. Falls back to the raw
|
|
4
|
+
// string for anything the Date constructor can't parse, so a non-ISO value still
|
|
5
|
+
// renders.
|
|
6
|
+
export function formatDate(value) {
|
|
7
|
+
if (!value) return "";
|
|
8
|
+
const d = new Date(value);
|
|
9
|
+
if (Number.isNaN(d.getTime())) return String(value);
|
|
10
|
+
return d.toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" });
|
|
11
|
+
}
|
package/config.js
CHANGED
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
* @typedef {Object} DocsNavLink
|
|
12
12
|
* @property {string} label
|
|
13
13
|
* @property {string} href
|
|
14
|
+
* @property {string} [icon] NavIcon name shown before the label (e.g. "book").
|
|
15
|
+
* @property {boolean} [external] Render as a plain target=_blank anchor (no SPA nav).
|
|
14
16
|
*
|
|
15
17
|
* @typedef {Object} DocsFooter
|
|
16
18
|
* @property {string} [text]
|
|
@@ -18,17 +20,41 @@
|
|
|
18
20
|
*
|
|
19
21
|
* @typedef {Object} DocsConfig
|
|
20
22
|
* @property {string} [title] Site/product name shown in the navbar.
|
|
23
|
+
* @property {string} [version] Version badge shown next to the brand (e.g. "v0.4.0").
|
|
21
24
|
* @property {string} [logo] URL of the navbar logo image.
|
|
22
25
|
* @property {string} [homeUrl] Where the navbar brand links to (default "/").
|
|
23
26
|
* @property {string} [github] GitHub URL (shown in the navbar).
|
|
24
|
-
* @property {string} [
|
|
27
|
+
* @property {string} [repoUrl] Source repository URL. When set (with `lastUpdated`),
|
|
28
|
+
* each docs page gets an "Edit this page" link to its file on
|
|
29
|
+
* GitHub (`<repoUrl>/edit/main/<path>`).
|
|
30
|
+
* @property {string} [dir] Docs content folder under app/ (default "docs"). Any
|
|
31
|
+
* other top-level folder with a `DocsLayout` is its own section
|
|
32
|
+
* (e.g. `app/api` → `/api`) — no extra config needed.
|
|
25
33
|
* @property {DocsNavLink[]} [nav] Top-level navbar links.
|
|
26
34
|
* @property {DocsFooter} [footer] Footer content.
|
|
27
35
|
* @property {{ provider?: string }} [search] Search provider (Phase 2: "pagefind").
|
|
36
|
+
* @property {boolean} [lastUpdated] Show a "Last updated" line per docs page (from the
|
|
37
|
+
* file's last git commit, or a `lastUpdated` frontmatter
|
|
38
|
+
* override; `lastUpdated: false` in frontmatter hides a page).
|
|
39
|
+
*
|
|
40
|
+
* @typedef {Object} BlogConfig
|
|
41
|
+
* @property {string} [dir] Blog content folder under app/ (default "blog"). Each
|
|
42
|
+
* `<dir>/<slug>/page.mdx` is a post; its frontmatter
|
|
43
|
+
* (title, description, date, author, tags) feeds the
|
|
44
|
+
* generated `@opentf/web-docs/posts` list.
|
|
45
|
+
* @property {string} [title] Feed title (default `"<docs.title> Blog"`).
|
|
46
|
+
* @property {string} [description] Feed description (default the title). RSS and Atom
|
|
47
|
+
* feeds (`<dir>/rss.xml`, `<dir>/atom.xml`) are generated
|
|
48
|
+
* by `otfw build`; production docs/blog builds require
|
|
49
|
+
* `site.url` or `--base-url` so feed URLs are absolute.
|
|
50
|
+
* @property {boolean} [lastUpdated] Show a "Last updated" line on a post when it was
|
|
51
|
+
* edited after its publish date (from git / frontmatter).
|
|
28
52
|
*
|
|
29
53
|
* @typedef {Object} SiteConfig
|
|
30
|
-
* @property {{ url?: string }} [site] Canonical site origin (for
|
|
54
|
+
* @property {{ url?: string }} [site] Canonical site origin (required for production
|
|
55
|
+
* docs/blog builds unless `--base-url` is passed).
|
|
31
56
|
* @property {DocsConfig} [docs] Documentation generator config.
|
|
57
|
+
* @property {BlogConfig} [blog] Blog generator config.
|
|
32
58
|
*/
|
|
33
59
|
|
|
34
60
|
/**
|
package/index.js
CHANGED
|
@@ -16,23 +16,50 @@ import "./components/Toc.jsx";
|
|
|
16
16
|
import "./components/Footer.jsx";
|
|
17
17
|
import "./components/Breadcrumbs.jsx";
|
|
18
18
|
import "./components/Pagination.jsx";
|
|
19
|
+
import "./components/LastUpdated.jsx";
|
|
19
20
|
import "./components/ThemeToggle.jsx";
|
|
20
21
|
import "./components/SearchTrigger.jsx";
|
|
22
|
+
import "./components/Search.jsx";
|
|
21
23
|
import "./components/Callout.jsx";
|
|
24
|
+
import "./components/CodeBlock.jsx";
|
|
22
25
|
import "./components/Tabs.jsx";
|
|
23
|
-
import "./components/
|
|
26
|
+
import "./components/Steps.jsx";
|
|
24
27
|
import "./components/Table.jsx";
|
|
28
|
+
import "./components/Cards.jsx";
|
|
29
|
+
import "./components/Card.jsx";
|
|
30
|
+
import "./components/Tooltip.jsx";
|
|
31
|
+
import "./components/BlogLayout.jsx";
|
|
32
|
+
import "./components/PostList.jsx";
|
|
33
|
+
import "./components/PostCard.jsx";
|
|
34
|
+
import "./components/PostBanner.jsx";
|
|
35
|
+
import "./components/PostMeta.jsx";
|
|
36
|
+
import "./components/ReadingTime.jsx";
|
|
25
37
|
|
|
26
38
|
export { default as DocsLayout } from "./components/DocsLayout.jsx";
|
|
27
39
|
export { default as Navbar } from "./components/Navbar.jsx";
|
|
40
|
+
export { default as NavbarLink } from "./components/NavbarLink.jsx";
|
|
41
|
+
export { default as SidebarToggle } from "./components/SidebarToggle.jsx";
|
|
42
|
+
export { default as NavIcon } from "./components/NavIcon.jsx";
|
|
28
43
|
export { default as Sidebar } from "./components/Sidebar.jsx";
|
|
29
44
|
export { default as Toc } from "./components/Toc.jsx";
|
|
30
45
|
export { default as Footer } from "./components/Footer.jsx";
|
|
31
46
|
export { default as Breadcrumbs } from "./components/Breadcrumbs.jsx";
|
|
32
47
|
export { default as Pagination } from "./components/Pagination.jsx";
|
|
48
|
+
export { default as LastUpdated } from "./components/LastUpdated.jsx";
|
|
33
49
|
export { default as ThemeToggle } from "./components/ThemeToggle.jsx";
|
|
34
50
|
export { default as SearchTrigger } from "./components/SearchTrigger.jsx";
|
|
51
|
+
export { default as Search } from "./components/Search.jsx";
|
|
35
52
|
export { default as Callout } from "./components/Callout.jsx";
|
|
53
|
+
export { default as CodeBlock } from "./components/CodeBlock.jsx";
|
|
36
54
|
export { default as Tabs } from "./components/Tabs.jsx";
|
|
37
|
-
export { default as
|
|
55
|
+
export { default as Steps } from "./components/Steps.jsx";
|
|
38
56
|
export { default as Table } from "./components/Table.jsx";
|
|
57
|
+
export { default as Cards } from "./components/Cards.jsx";
|
|
58
|
+
export { default as Card } from "./components/Card.jsx";
|
|
59
|
+
export { default as Tooltip } from "./components/Tooltip.jsx";
|
|
60
|
+
export { default as BlogLayout } from "./components/BlogLayout.jsx";
|
|
61
|
+
export { default as PostList } from "./components/PostList.jsx";
|
|
62
|
+
export { default as PostCard } from "./components/PostCard.jsx";
|
|
63
|
+
export { default as PostBanner } from "./components/PostBanner.jsx";
|
|
64
|
+
export { default as PostMeta } from "./components/PostMeta.jsx";
|
|
65
|
+
export { default as ReadingTime } from "./components/ReadingTime.jsx";
|
package/nav-virtual.js
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
//
|
|
3
3
|
// When `docsNavPlugin` is active (the toolchain registers it whenever the project
|
|
4
4
|
// has a `docs` config), it intercepts this specifier and replaces it with a
|
|
5
|
-
// build-time-generated module exporting the real navigation
|
|
6
|
-
//
|
|
7
|
-
//
|
|
5
|
+
// build-time-generated module exporting the real navigation. This file is only loaded
|
|
6
|
+
// when the plugin is NOT active (e.g. an editor, or a non-docs build).
|
|
7
|
+
//
|
|
8
|
+
// Shape: a section map `{ [base]: tree }` (e.g. `{ "/docs": [...], "/api": [...] }`);
|
|
9
|
+
// empty here.
|
|
8
10
|
|
|
9
11
|
if (typeof console !== "undefined") {
|
|
10
12
|
console.warn(
|
|
@@ -13,4 +15,4 @@ if (typeof console !== "undefined") {
|
|
|
13
15
|
);
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
export default
|
|
18
|
+
export default {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opentf/web-docs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Documentation-site generator for OTF Web — themed shell components (sidebar, TOC, callouts, tabs), a build-time nav generator, and a default light/dark theme.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -8,12 +8,16 @@
|
|
|
8
8
|
"./theme": "./theme/index.css",
|
|
9
9
|
"./config": "./config.js",
|
|
10
10
|
"./nav": "./nav-virtual.js",
|
|
11
|
+
"./posts": "./posts-virtual.js",
|
|
12
|
+
"./updated": "./updated-virtual.js",
|
|
11
13
|
"./build": "./build/index.js"
|
|
12
14
|
},
|
|
13
15
|
"files": [
|
|
14
16
|
"index.js",
|
|
15
17
|
"config.js",
|
|
16
18
|
"nav-virtual.js",
|
|
19
|
+
"posts-virtual.js",
|
|
20
|
+
"updated-virtual.js",
|
|
17
21
|
"components",
|
|
18
22
|
"theme",
|
|
19
23
|
"build",
|
|
@@ -40,5 +44,15 @@
|
|
|
40
44
|
},
|
|
41
45
|
"publishConfig": {
|
|
42
46
|
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"test": "bun test --env=happy-dom"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"pagefind": "^1.5.2"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@opentf/web": "0.7.0",
|
|
56
|
+
"@opentf/web-test": "1.2.0"
|
|
43
57
|
}
|
|
44
58
|
}
|
package/posts-virtual.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Fallback for `@opentf/web-docs/posts`.
|
|
2
|
+
//
|
|
3
|
+
// When `blogPostsPlugin` is active (the toolchain registers it whenever the project
|
|
4
|
+
// has a `blog` config), it intercepts this specifier and replaces it with a
|
|
5
|
+
// build-time-generated module exporting the post list. This file is only loaded when
|
|
6
|
+
// the plugin is NOT active (e.g. an editor, or a project without a blog), in which
|
|
7
|
+
// case the list is simply empty.
|
|
8
|
+
|
|
9
|
+
if (typeof console !== "undefined") {
|
|
10
|
+
console.warn(
|
|
11
|
+
"[@opentf/web-docs] posts requested but blogPostsPlugin is not active — " +
|
|
12
|
+
"did you add a `blog` block to otfw.config.js? Returning an empty list.",
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const posts = [];
|
|
17
|
+
export default posts;
|