lildocs 0.1.14 → 0.1.16
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 +4 -4
- package/dist/cli.mjs +304 -434
- package/dist/render/Layout.tsrx +362 -0
- package/dist/render/Search.tsrx +409 -0
- package/dist/render/client.tsrx +50 -0
- package/dist/render/copy-code.ts +56 -0
- package/dist/render/heading-links.ts +50 -0
- package/dist/render/navigation.ts +16 -0
- package/dist/render/paths.ts +47 -0
- package/dist/render/renderPage.tsrx +14 -0
- package/dist/render/section-highlight.ts +63 -0
- package/dist/render/sidebar.ts +31 -0
- package/dist/render/styles.css +390 -137
- package/dist/render/table-viewer.ts +98 -0
- package/dist/render/tabler-icons.css +16 -0
- package/dist/render/types.ts +9 -0
- package/package.json +16 -12
- package/dist/render/copy-code.js +0 -54
- package/dist/render/navigation.js +0 -14
- package/dist/render/search.js +0 -3254
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import type { Heading, Page } from "../core/content.js";
|
|
2
|
+
import type { ResolvedLogo } from "../core/logo.js";
|
|
3
|
+
import type { NavItem } from "../core/nav.js";
|
|
4
|
+
import type { NavigationOptions } from "../core/theme.js";
|
|
5
|
+
import { relativeUrl, rootRelativeUrl } from "./paths.js";
|
|
6
|
+
import type { PageNavigation } from "./types.js";
|
|
7
|
+
|
|
8
|
+
export type LayoutProps = {
|
|
9
|
+
page: Page;
|
|
10
|
+
nav: NavItem[];
|
|
11
|
+
pageNavigation?: PageNavigation;
|
|
12
|
+
css: string;
|
|
13
|
+
searchIndexJson: string;
|
|
14
|
+
logo: ResolvedLogo;
|
|
15
|
+
favicon?: string;
|
|
16
|
+
repositoryUrl?: string;
|
|
17
|
+
projectName?: string;
|
|
18
|
+
navigation?: NavigationOptions;
|
|
19
|
+
clientScriptPath: string;
|
|
20
|
+
clientStylePaths: string[];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export function Layout({
|
|
24
|
+
page,
|
|
25
|
+
nav,
|
|
26
|
+
pageNavigation,
|
|
27
|
+
searchIndexJson,
|
|
28
|
+
logo,
|
|
29
|
+
favicon,
|
|
30
|
+
repositoryUrl,
|
|
31
|
+
projectName,
|
|
32
|
+
navigation,
|
|
33
|
+
clientScriptPath,
|
|
34
|
+
clientStylePaths,
|
|
35
|
+
}: LayoutProps) @{
|
|
36
|
+
const transition = navigation?.transition ?? "fade";
|
|
37
|
+
const repoIconUrl = rootRelativeUrl(page.route, "assets/github-icon.svg");
|
|
38
|
+
const issueUrl = issueUrlForRepository(repositoryUrl);
|
|
39
|
+
const repositoryLabel = repositoryLabelForUrl(repositoryUrl);
|
|
40
|
+
|
|
41
|
+
<>
|
|
42
|
+
<meta charSet="utf-8" />
|
|
43
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
44
|
+
<title>
|
|
45
|
+
{(projectName ? `${page.title} • ${projectName}` : page.title) as string}
|
|
46
|
+
</title>
|
|
47
|
+
@if (favicon) {
|
|
48
|
+
<link rel="icon" href={assetSrc(page.route, favicon)} />
|
|
49
|
+
}
|
|
50
|
+
<link
|
|
51
|
+
rel="stylesheet"
|
|
52
|
+
href={rootRelativeUrl(page.route, "assets/tabler-icons.css")}
|
|
53
|
+
/>
|
|
54
|
+
<link
|
|
55
|
+
rel="stylesheet"
|
|
56
|
+
href={rootRelativeUrl(page.route, "assets/lildocs.css")}
|
|
57
|
+
/>
|
|
58
|
+
@for (const stylePath of clientStylePaths; key stylePath) {
|
|
59
|
+
<link rel="stylesheet" href={assetSrc(page.route, stylePath)} />
|
|
60
|
+
}
|
|
61
|
+
<div class="pageShell">
|
|
62
|
+
<aside id="lildocs-sidebar" class="sidebar">
|
|
63
|
+
<div class="sidebarContents">
|
|
64
|
+
<div class="sidebarHeader">
|
|
65
|
+
<a class="brand" href={relativeUrl(page.route, "index.html")}>
|
|
66
|
+
@if (logo.image) {
|
|
67
|
+
<img
|
|
68
|
+
class="brandLogo"
|
|
69
|
+
src={assetSrc(page.route, logo.image)}
|
|
70
|
+
alt=""
|
|
71
|
+
/>
|
|
72
|
+
}
|
|
73
|
+
@if (logo.text) {
|
|
74
|
+
<span>
|
|
75
|
+
{logo.text as string}
|
|
76
|
+
</span>
|
|
77
|
+
}
|
|
78
|
+
</a>
|
|
79
|
+
<button
|
|
80
|
+
id="lildocs-sidebar-toggle"
|
|
81
|
+
class="sidebarToggle"
|
|
82
|
+
type="button"
|
|
83
|
+
aria-label="Collapse sidebar"
|
|
84
|
+
aria-controls="lildocs-sidebar"
|
|
85
|
+
>
|
|
86
|
+
<span
|
|
87
|
+
class="ti ti-layout-sidebar-left-collapse"
|
|
88
|
+
aria-hidden="true"
|
|
89
|
+
/>
|
|
90
|
+
</button>
|
|
91
|
+
</div>
|
|
92
|
+
<div id="lildocs-search-root" class="searchBox">
|
|
93
|
+
<span class="searchIcon ti ti-search" aria-hidden="true" />
|
|
94
|
+
<input
|
|
95
|
+
id="lildocs-search-input"
|
|
96
|
+
type="search"
|
|
97
|
+
placeholder="Search docs"
|
|
98
|
+
aria-label="Search docs"
|
|
99
|
+
/>
|
|
100
|
+
</div>
|
|
101
|
+
<nav
|
|
102
|
+
id="lildocs-sidebar-navigation"
|
|
103
|
+
aria-label="Documentation navigation"
|
|
104
|
+
>
|
|
105
|
+
<NavList
|
|
106
|
+
items={nav}
|
|
107
|
+
currentRoute={page.route}
|
|
108
|
+
pageRoute={page.route}
|
|
109
|
+
/>
|
|
110
|
+
</nav>
|
|
111
|
+
</div>
|
|
112
|
+
</aside>
|
|
113
|
+
<div class="sidebarFloatingControls">
|
|
114
|
+
<button
|
|
115
|
+
id="lildocs-sidebar-expand"
|
|
116
|
+
class="sidebarToggle"
|
|
117
|
+
type="button"
|
|
118
|
+
aria-label="Expand sidebar"
|
|
119
|
+
aria-controls="lildocs-sidebar"
|
|
120
|
+
>
|
|
121
|
+
<span class="ti ti-layout-sidebar-left-expand" aria-hidden="true" />
|
|
122
|
+
</button>
|
|
123
|
+
<button
|
|
124
|
+
id="lildocs-floating-search"
|
|
125
|
+
class="sidebarToggle"
|
|
126
|
+
type="button"
|
|
127
|
+
aria-label="Search docs"
|
|
128
|
+
>
|
|
129
|
+
<span class="ti ti-search" aria-hidden="true" />
|
|
130
|
+
</button>
|
|
131
|
+
</div>
|
|
132
|
+
<div id="swup" class="contentGrid">
|
|
133
|
+
<main class={`content transition-${transition}`}>
|
|
134
|
+
<article>
|
|
135
|
+
<GroupBreadcrumbs route={page.route} />
|
|
136
|
+
<div dangerouslySetInnerHTML={{ __html: page.html ?? "" }} />
|
|
137
|
+
</article>
|
|
138
|
+
<PageNav pageNavigation={pageNavigation} />
|
|
139
|
+
</main>
|
|
140
|
+
<aside class={`toc transition-${transition}`}>
|
|
141
|
+
<Toc
|
|
142
|
+
headings={page.headings}
|
|
143
|
+
repositoryUrl={repositoryUrl}
|
|
144
|
+
repositoryLabel={repositoryLabel}
|
|
145
|
+
repoIconUrl={repoIconUrl}
|
|
146
|
+
/>
|
|
147
|
+
</aside>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
<script
|
|
151
|
+
dangerouslySetInnerHTML={{
|
|
152
|
+
__html: `window.lildocsSearchUrl = ${JSON.stringify(
|
|
153
|
+
rootRelativeUrl(page.route, "search-index.json"),
|
|
154
|
+
)};${
|
|
155
|
+
issueUrl
|
|
156
|
+
? `window.lildocsIssueUrl = ${JSON.stringify(issueUrl)};`
|
|
157
|
+
: ""
|
|
158
|
+
}`,
|
|
159
|
+
}}
|
|
160
|
+
/>
|
|
161
|
+
<script
|
|
162
|
+
type="application/json"
|
|
163
|
+
id="lildocs-search-index"
|
|
164
|
+
dangerouslySetInnerHTML={{ __html: searchIndexJson }}
|
|
165
|
+
/>
|
|
166
|
+
<div id="lildocs-overlay-root" />
|
|
167
|
+
<script type="module" src={assetSrc(page.route, clientScriptPath)} />
|
|
168
|
+
</>
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function issueUrlForRepository(repositoryUrl: string | undefined) {
|
|
172
|
+
if (!repositoryUrl) {
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
let url: URL;
|
|
177
|
+
try {
|
|
178
|
+
url = new URL(repositoryUrl);
|
|
179
|
+
} catch {
|
|
180
|
+
return undefined;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (url.hostname !== "github.com") {
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const [owner, repo] = url.pathname.split("/").filter(Boolean);
|
|
188
|
+
if (!owner || !repo) {
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return `https://github.com/${owner}/${repo}/issues/new`;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function repositoryLabelForUrl(repositoryUrl: string | undefined) {
|
|
196
|
+
if (!repositoryUrl) {
|
|
197
|
+
return undefined;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
const url = new URL(repositoryUrl);
|
|
202
|
+
if (url.hostname !== "github.com") {
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const [owner, repo] = url.pathname.split("/").filter(Boolean);
|
|
207
|
+
return owner && repo ? `${owner}/${repo}` : undefined;
|
|
208
|
+
} catch {
|
|
209
|
+
return undefined;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function assetSrc(pageRoute: string, image: string) {
|
|
214
|
+
if (
|
|
215
|
+
/^(?:[a-z]+:)?\/\//i.test(image) || image.startsWith("data:") ||
|
|
216
|
+
image.startsWith("/")
|
|
217
|
+
) {
|
|
218
|
+
return image;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return rootRelativeUrl(pageRoute, image);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function GroupBreadcrumbs({ route }: { route: string }) @{
|
|
225
|
+
const dir = route.split("/").slice(0, -1);
|
|
226
|
+
if (dir.length === 0) {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
<p class="groupBreadcrumbs">
|
|
231
|
+
{dir.map(titleFromDir).join(" / ") as string}
|
|
232
|
+
</p>
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function titleFromDir(dir: string) {
|
|
236
|
+
return dir.replace(/[-_]+/g, " ").replace(
|
|
237
|
+
/\S+/g,
|
|
238
|
+
(word) => `${word[0]?.toLocaleUpperCase() ?? ""}${word.slice(1)}`,
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function PageNav({ pageNavigation }: { pageNavigation?: PageNavigation }) @{
|
|
243
|
+
if (!pageNavigation?.next) {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
<nav class="pageNav" aria-label="Page navigation">
|
|
248
|
+
<p>
|
|
249
|
+
{"Next: "}
|
|
250
|
+
<a rel="next" href={pageNavigation.next.href}>
|
|
251
|
+
{pageNavigation.next.title as string}
|
|
252
|
+
</a>
|
|
253
|
+
</p>
|
|
254
|
+
</nav>
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function NavList({
|
|
258
|
+
items,
|
|
259
|
+
currentRoute,
|
|
260
|
+
pageRoute,
|
|
261
|
+
}: {
|
|
262
|
+
items: NavItem[];
|
|
263
|
+
currentRoute: string;
|
|
264
|
+
pageRoute: string;
|
|
265
|
+
}) @{
|
|
266
|
+
<ul class="navList">
|
|
267
|
+
@for (const item of items; key item.route) {
|
|
268
|
+
<li class={item.children.length > 0 ? "navGroup" : undefined}>
|
|
269
|
+
@if (item.children.length > 0 && item.hasPage) {
|
|
270
|
+
<details
|
|
271
|
+
class="navDisclosure"
|
|
272
|
+
open={isActiveBranch(item, currentRoute)}
|
|
273
|
+
>
|
|
274
|
+
<summary class={item.route === currentRoute ? "active" : undefined}>
|
|
275
|
+
{item.title as string}
|
|
276
|
+
</summary>
|
|
277
|
+
<NavList
|
|
278
|
+
items={item.children}
|
|
279
|
+
currentRoute={currentRoute}
|
|
280
|
+
pageRoute={pageRoute}
|
|
281
|
+
/>
|
|
282
|
+
</details>
|
|
283
|
+
} @else if (item.children.length > 0) {
|
|
284
|
+
<span class="navFolder">
|
|
285
|
+
{item.title as string}
|
|
286
|
+
</span>
|
|
287
|
+
} @else {
|
|
288
|
+
<a
|
|
289
|
+
class={item.route === currentRoute ? "active" : undefined}
|
|
290
|
+
href={relativeUrl(pageRoute, item.route)}
|
|
291
|
+
>
|
|
292
|
+
{item.title as string}
|
|
293
|
+
</a>
|
|
294
|
+
}
|
|
295
|
+
@if (item.children.length > 0 && !item.hasPage) {
|
|
296
|
+
<NavList
|
|
297
|
+
items={item.children}
|
|
298
|
+
currentRoute={currentRoute}
|
|
299
|
+
pageRoute={pageRoute}
|
|
300
|
+
/>
|
|
301
|
+
}
|
|
302
|
+
</li>
|
|
303
|
+
}
|
|
304
|
+
</ul>
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function isActiveBranch(item: NavItem, currentRoute: string): boolean {
|
|
308
|
+
return item.route === currentRoute ||
|
|
309
|
+
item.children.some((child) => isActiveBranch(child, currentRoute));
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function Toc({
|
|
313
|
+
headings,
|
|
314
|
+
repositoryUrl,
|
|
315
|
+
repositoryLabel,
|
|
316
|
+
repoIconUrl,
|
|
317
|
+
}: {
|
|
318
|
+
headings: Heading[];
|
|
319
|
+
repositoryUrl?: string;
|
|
320
|
+
repositoryLabel?: string;
|
|
321
|
+
repoIconUrl: string;
|
|
322
|
+
}) @{
|
|
323
|
+
const tocHeadings = headings.filter(
|
|
324
|
+
(heading) => heading.depth > 1 && heading.depth < 4,
|
|
325
|
+
);
|
|
326
|
+
if (tocHeadings.length === 0 && !repositoryLabel) {
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
<nav aria-label="Table of contents">
|
|
331
|
+
@if (tocHeadings.length > 0) {
|
|
332
|
+
<>
|
|
333
|
+
<p>On this page</p>
|
|
334
|
+
<ul>
|
|
335
|
+
@for (const heading of tocHeadings; key heading.id) {
|
|
336
|
+
<li class={`tocDepth${heading.depth}`}>
|
|
337
|
+
<a href={`#${heading.id}`}>
|
|
338
|
+
{heading.text as string}
|
|
339
|
+
</a>
|
|
340
|
+
</li>
|
|
341
|
+
}
|
|
342
|
+
</ul>
|
|
343
|
+
</>
|
|
344
|
+
}
|
|
345
|
+
@if (repositoryUrl && repositoryLabel) {
|
|
346
|
+
<a
|
|
347
|
+
class="tocRepoLink"
|
|
348
|
+
href={repositoryUrl}
|
|
349
|
+
aria-label="View repository on GitHub"
|
|
350
|
+
>
|
|
351
|
+
<span
|
|
352
|
+
class="repoIcon"
|
|
353
|
+
aria-hidden="true"
|
|
354
|
+
style={`--ld-repo-icon: url(${repoIconUrl})`}
|
|
355
|
+
/>
|
|
356
|
+
<span>
|
|
357
|
+
{repositoryLabel as string}
|
|
358
|
+
</span>
|
|
359
|
+
</a>
|
|
360
|
+
}
|
|
361
|
+
</nav>
|
|
362
|
+
}
|