@riebeckite/plugin-docs 0.0.24 → 0.0.26

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.
@@ -4,5 +4,5 @@ export type DocsPrevNextProps = {
4
4
  readonly next?: DocsNavigationLink;
5
5
  readonly language?: string;
6
6
  };
7
- declare const DocsPrevNext: ({ previous, next, language, }: Readonly<DocsPrevNextProps>) => import("hono/jsx/jsx-dev-runtime").JSX.Element;
7
+ declare const DocsPrevNext: (props: Readonly<DocsPrevNextProps>) => Promise<import("hono/utils/html").HtmlEscapedString>;
8
8
  export default DocsPrevNext;
@@ -1,8 +1,8 @@
1
1
  import type { DocsNavigationItem } from "../src/types.js";
2
2
  export type DocsSidebarProps = {
3
- readonly items: readonly DocsNavigationItem[];
3
+ readonly items?: readonly DocsNavigationItem[];
4
4
  readonly currentPermalink?: string;
5
5
  readonly label?: string;
6
6
  };
7
- declare const DocsSidebar: ({ items, currentPermalink, label, }: Readonly<DocsSidebarProps>) => import("hono/jsx/jsx-dev-runtime").JSX.Element;
7
+ declare const DocsSidebar: (props: Readonly<DocsSidebarProps>) => Promise<import("hono/utils/html").HtmlEscapedString>;
8
8
  export default DocsSidebar;
@@ -1,11 +1,187 @@
1
+ // components/docs-sidebar.tsx
2
+ import { useArticleContext, useSiteContext } from "@riebeckite/ui";
3
+ import { html } from "hono/html";
4
+
5
+ // index.ts
6
+ import {
7
+ createClientEntry,
8
+ createStyleAsset,
9
+ definePlugin
10
+ } from "@riebeckite/core";
11
+
12
+ // src/navigation.ts
13
+ var ROOT_SEGMENT = "";
14
+ function resolveDocsOptions(options) {
15
+ return {
16
+ root: normalizeRoot(options.root),
17
+ sidebar: {
18
+ auto: options.sidebar?.auto ?? true,
19
+ label: options.sidebar?.label ?? "Docs navigation"
20
+ },
21
+ prevNext: options.prevNext ?? true
22
+ };
23
+ }
24
+ function buildDocsNavigation(entries, options) {
25
+ if (!options.sidebar.auto) return [];
26
+ const docsEntries = entries.map((entry) => toNavigationEntry(entry, options.root)).filter((entry) => Boolean(entry)).filter((entry) => !entry.hidden);
27
+ const root = createTreeNode(ROOT_SEGMENT);
28
+ for (const entry of docsEntries) addEntry(root, entry);
29
+ const rootIndex = root.index;
30
+ const items = buildItems(root, []);
31
+ if (!rootIndex) return items;
32
+ return [toItem(rootIndex, []), ...items];
33
+ }
34
+ function normalizeRoot(root) {
35
+ return root.replace(/\\/g, "/").replace(/^\/+|\/+$/g, "");
36
+ }
37
+ function toNavigationEntry(entry, root) {
38
+ const relativePath = resolveRelativeDocsPath(entry, root);
39
+ if (!relativePath) return null;
40
+ const segments = relativePath.split("/").filter(Boolean);
41
+ const isIndex = ["index", "README"].includes(segments.at(-1) ?? "");
42
+ const sidebar = readSidebarFrontmatter(
43
+ "frontmatter" in entry ? entry.frontmatter.sidebar : void 0
44
+ );
45
+ return {
46
+ entry,
47
+ relativePath,
48
+ segments,
49
+ isIndex,
50
+ title: resolveTitle(entry, sidebar),
51
+ order: sidebar.order,
52
+ hidden: sidebar.hidden ?? false,
53
+ collapsed: sidebar.collapsed
54
+ };
55
+ }
56
+ function resolveRelativeDocsPath(entry, root) {
57
+ if (entry.slug === root) return "index";
58
+ if (!entry.slug.startsWith(`${root}/`)) return null;
59
+ return stripFilenameLanguage(
60
+ entry.slug.slice(root.length + 1),
61
+ entry.publicLocation.language
62
+ );
63
+ }
64
+ function stripFilenameLanguage(path, language) {
65
+ if (!language) return path;
66
+ const segments = path.split("/");
67
+ const last = segments.at(-1);
68
+ if (last === void 0) return path;
69
+ segments[segments.length - 1] = last.replace(
70
+ new RegExp(`[._-]${escapeRegExp(language)}$`, "i"),
71
+ ""
72
+ );
73
+ return segments.join("/");
74
+ }
75
+ function escapeRegExp(value) {
76
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
77
+ }
78
+ function readSidebarFrontmatter(value) {
79
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
80
+ const record = value;
81
+ return {
82
+ ...typeof record.label === "string" && record.label.trim() ? { label: record.label.trim() } : {},
83
+ ...typeof record.order === "number" && Number.isFinite(record.order) ? { order: record.order } : {},
84
+ ...typeof record.hidden === "boolean" ? { hidden: record.hidden } : {},
85
+ ...typeof record.collapsed === "boolean" ? { collapsed: record.collapsed } : {}
86
+ };
87
+ }
88
+ function resolveTitle(entry, sidebar) {
89
+ if (sidebar.label) return sidebar.label;
90
+ if (entry.title.trim()) return entry.title.trim();
91
+ return titleFromSegment(entry.slug.split("/").at(-1) ?? entry.slug);
92
+ }
93
+ function addEntry(root, entry) {
94
+ const path = entry.isIndex ? entry.segments.slice(0, -1) : entry.segments;
95
+ let node = root;
96
+ for (const segment of path) {
97
+ const child = node.children.get(segment) ?? createTreeNode(segment);
98
+ node.children.set(segment, child);
99
+ node = child;
100
+ }
101
+ if (entry.isIndex) node.index = entry;
102
+ else node.page = entry;
103
+ }
104
+ function buildItems(node, path) {
105
+ return [...node.children.values()].toSorted(compareNodes).map((child) => nodeToItem(child, [...path, child.segment]));
106
+ }
107
+ function nodeToItem(node, path) {
108
+ const primary = node.index ?? node.page;
109
+ const children = [
110
+ ...node.index && node.page ? [toItem(node.page, [])] : [],
111
+ ...buildItems(node, path)
112
+ ];
113
+ if (primary) return toItem(primary, children);
114
+ return {
115
+ title: titleFromSegment(node.segment),
116
+ children
117
+ };
118
+ }
119
+ function toItem(entry, children) {
120
+ return {
121
+ title: entry.title,
122
+ href: entry.entry.permalink,
123
+ slug: entry.entry.slug,
124
+ ...entry.order === void 0 ? {} : { order: entry.order },
125
+ ...entry.collapsed === void 0 ? {} : { collapsed: entry.collapsed },
126
+ children
127
+ };
128
+ }
129
+ function compareNodes(a, b) {
130
+ const left = a.index ?? a.page;
131
+ const right = b.index ?? b.page;
132
+ return compareNavigationKeys(
133
+ left?.order,
134
+ left?.title ?? titleFromSegment(a.segment),
135
+ a.segment,
136
+ right?.order,
137
+ right?.title ?? titleFromSegment(b.segment),
138
+ b.segment
139
+ );
140
+ }
141
+ function compareNavigationKeys(leftOrder, leftTitle, leftPath, rightOrder, rightTitle, rightPath) {
142
+ if (leftOrder !== void 0 || rightOrder !== void 0) {
143
+ if (leftOrder === void 0) return 1;
144
+ if (rightOrder === void 0) return -1;
145
+ const order = leftOrder - rightOrder;
146
+ if (order !== 0) return order;
147
+ }
148
+ const title = leftTitle.localeCompare(rightTitle, "en", {
149
+ sensitivity: "base"
150
+ });
151
+ if (title !== 0) return title;
152
+ return leftPath.localeCompare(rightPath, "en", { sensitivity: "base" });
153
+ }
154
+ function createTreeNode(segment) {
155
+ return { segment, children: /* @__PURE__ */ new Map() };
156
+ }
157
+ function titleFromSegment(segment) {
158
+ return segment.replace(/[-_]+/g, " ").replace(/\b\p{L}/gu, (letter) => letter.toLocaleUpperCase("en"));
159
+ }
160
+
161
+ // index.ts
162
+ var getDocsNavigation = ({ view, entrySlug, options }) => {
163
+ const entry = view.findRoutableBySlug(entrySlug);
164
+ if (!entry) return [];
165
+ const localized = view.discoverableEntries.filter(
166
+ (candidate) => candidate.publicLocation.language === entry.publicLocation.language
167
+ );
168
+ return buildDocsNavigation(localized, resolveDocsOptions(options));
169
+ };
170
+
1
171
  // components/docs-sidebar.tsx
2
172
  import { jsx, jsxs } from "hono/jsx/jsx-runtime";
3
- var DocsSidebar = (({
4
- items,
5
- currentPermalink,
6
- label = "Docs navigation"
7
- }) => {
8
- if (items.length === 0) return null;
173
+ var DocsSidebar = (async (props) => {
174
+ const site = useSiteContext();
175
+ const article = useArticleContext();
176
+ const options = findDocsOptions(site?.config.plugins);
177
+ const items = props.items ?? (site && article?.entry && options ? await getDocsNavigation({
178
+ view: site.view,
179
+ entrySlug: article.entry.slug,
180
+ options
181
+ }) : []);
182
+ const currentPermalink = props.currentPermalink ?? article?.entry?.permalink;
183
+ const label = props.label ?? options?.sidebar?.label ?? "Docs navigation";
184
+ if (items.length === 0) return html``;
9
185
  const permalink = currentPermalink ?? "";
10
186
  return /* @__PURE__ */ jsxs("details", { class: "rb-docs-sidebar-details", "data-docs-sidebar-details": true, open: true, children: [
11
187
  /* @__PURE__ */ jsx("summary", { class: "rb-docs-sidebar-summary", children: label }),
@@ -84,6 +260,9 @@ function containsPermalink(item, currentPermalink) {
84
260
  return item.href === currentPermalink || item.children.some((child) => containsPermalink(child, currentPermalink));
85
261
  }
86
262
  var docs_sidebar_default = DocsSidebar;
263
+ function findDocsOptions(plugins) {
264
+ return plugins?.find((plugin) => plugin.name.startsWith("docs:"))?.options;
265
+ }
87
266
  export {
88
267
  docs_sidebar_default as default
89
268
  };
package/dist/index.js CHANGED
@@ -161,13 +161,23 @@ function titleFromSegment(segment) {
161
161
  }
162
162
 
163
163
  // components/docs-prev-next.tsx
164
+ import { useArticleContext, useSiteContext } from "@riebeckite/ui";
165
+ import { html } from "hono/html";
164
166
  import { jsx, jsxs } from "hono/jsx/jsx-runtime";
165
- var DocsPrevNext = (({
166
- previous,
167
- next,
168
- language
169
- }) => {
170
- if (!previous && !next) return null;
167
+ var DocsPrevNext = (async (props) => {
168
+ const site = useSiteContext();
169
+ const article = useArticleContext();
170
+ const options = findDocsOptions(site?.config.plugins);
171
+ const navigation = site && article?.entry && options ? await getDocsNavigation({
172
+ view: site.view,
173
+ entrySlug: article.entry.slug,
174
+ options
175
+ }) : [];
176
+ const resolved = article?.entry ? await getDocsPrevNext({ items: navigation, slug: article.entry.slug }) : {};
177
+ const previous = props.previous ?? resolved.previous;
178
+ const next = props.next ?? resolved.next;
179
+ const language = props.language ?? article?.entry?.publicLocation.language;
180
+ if (!previous && !next) return html``;
171
181
  const labels = language === "ja" ? { nav: "前後のドキュメント", previous: "前へ", next: "次へ" } : {
172
182
  nav: "Previous and next docs pages",
173
183
  previous: "Previous",
@@ -209,15 +219,26 @@ var DocsPrevNext = (({
209
219
  ] });
210
220
  });
211
221
  var docs_prev_next_default = DocsPrevNext;
222
+ function findDocsOptions(plugins) {
223
+ return plugins?.find((plugin) => plugin.name.startsWith("docs:"))?.options;
224
+ }
212
225
 
213
226
  // components/docs-sidebar.tsx
227
+ import { useArticleContext as useArticleContext2, useSiteContext as useSiteContext2 } from "@riebeckite/ui";
228
+ import { html as html2 } from "hono/html";
214
229
  import { jsx as jsx2, jsxs as jsxs2 } from "hono/jsx/jsx-runtime";
215
- var DocsSidebar = (({
216
- items,
217
- currentPermalink,
218
- label = "Docs navigation"
219
- }) => {
220
- if (items.length === 0) return null;
230
+ var DocsSidebar = (async (props) => {
231
+ const site = useSiteContext2();
232
+ const article = useArticleContext2();
233
+ const options = findDocsOptions2(site?.config.plugins);
234
+ const items = props.items ?? (site && article?.entry && options ? await getDocsNavigation({
235
+ view: site.view,
236
+ entrySlug: article.entry.slug,
237
+ options
238
+ }) : []);
239
+ const currentPermalink = props.currentPermalink ?? article?.entry?.permalink;
240
+ const label = props.label ?? options?.sidebar?.label ?? "Docs navigation";
241
+ if (items.length === 0) return html2``;
221
242
  const permalink = currentPermalink ?? "";
222
243
  return /* @__PURE__ */ jsxs2("details", { class: "rb-docs-sidebar-details", "data-docs-sidebar-details": true, open: true, children: [
223
244
  /* @__PURE__ */ jsx2("summary", { class: "rb-docs-sidebar-summary", children: label }),
@@ -296,6 +317,9 @@ function containsPermalink(item, currentPermalink) {
296
317
  return item.href === currentPermalink || item.children.some((child) => containsPermalink(child, currentPermalink));
297
318
  }
298
319
  var docs_sidebar_default = DocsSidebar;
320
+ function findDocsOptions2(plugins) {
321
+ return plugins?.find((plugin) => plugin.name.startsWith("docs:"))?.options;
322
+ }
299
323
 
300
324
  // src/docs-sidebar.client.ts
301
325
  var COLLAPSIBLE_PROPERTY = "--rb-docs-sidebar-collapsible";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riebeckite/plugin-docs",
3
- "version": "0.0.24",
3
+ "version": "0.0.26",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -28,11 +28,12 @@
28
28
  },
29
29
  "dependencies": {
30
30
  "hono": "^4.12.25",
31
- "@riebeckite/core": "0.0.24"
31
+ "@riebeckite/core": "0.0.26",
32
+ "@riebeckite/ui": "0.0.26"
32
33
  },
33
34
  "devDependencies": {
34
35
  "tsx": "^4.22.4",
35
- "@riebeckite/test": "0.0.24"
36
+ "@riebeckite/test": "0.0.26"
36
37
  },
37
38
  "publishConfig": {
38
39
  "access": "public"