docula 0.40.0 → 0.41.1
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 +89 -0
- package/dist/docula.d.ts +6 -0
- package/dist/docula.js +172 -25
- package/package.json +32 -31
- package/template/api.hbs +28 -0
- package/template/changelog-entry.hbs +79 -0
- package/template/changelog.hbs +81 -0
- package/template/css/base.css +140 -0
- package/template/css/multipage.css +0 -16
- package/template/includes/multipage/doc.hbs +11 -5
- /package/bin/{docula.mjs → docula.js} +0 -0
package/README.md
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
- [Building Multiple Pages](#building-multiple-pages)
|
|
18
18
|
- [Public Folder](#public-folder)
|
|
19
19
|
- [Announcements](#announcements)
|
|
20
|
+
- [Changelog](#changelog)
|
|
20
21
|
- [Alert, Info, Warn Styling](#alert-info-warn-styling)
|
|
21
22
|
- [Using a Github Token](#using-a-github-token)
|
|
22
23
|
- [Helpers](#helpers)
|
|
@@ -272,6 +273,94 @@ You can customize the appearance by overriding the `.announcement` class in your
|
|
|
272
273
|
|
|
273
274
|
Simply delete the `announcement.md` file when you no longer need the announcement. The home page will automatically return to its normal layout.
|
|
274
275
|
|
|
276
|
+
# Changelog
|
|
277
|
+
|
|
278
|
+
Docula can generate a changelog section for your site from markdown files. This is useful for documenting release notes, updates, and changes to your project in a structured, browsable format.
|
|
279
|
+
|
|
280
|
+
## Setup
|
|
281
|
+
|
|
282
|
+
Create a `changelog` folder inside your site directory and add markdown (`.md` or `.mdx`) files for each entry:
|
|
283
|
+
|
|
284
|
+
```
|
|
285
|
+
site
|
|
286
|
+
├───changelog
|
|
287
|
+
│ ├───2025-01-15-initial-release.md
|
|
288
|
+
│ ├───2025-02-01-new-features.md
|
|
289
|
+
│ └───2025-03-10-bug-fixes.md
|
|
290
|
+
├───logo.svg
|
|
291
|
+
├───favicon.ico
|
|
292
|
+
└───docula.config.mjs
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Entry Format
|
|
296
|
+
|
|
297
|
+
Each changelog entry is a markdown file with front matter:
|
|
298
|
+
|
|
299
|
+
```md
|
|
300
|
+
---
|
|
301
|
+
title: "Initial Release"
|
|
302
|
+
date: 2025-01-15
|
|
303
|
+
tag: "Release"
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
We're excited to announce the initial release! Here's what's included:
|
|
307
|
+
|
|
308
|
+
- Feature A
|
|
309
|
+
- Feature B
|
|
310
|
+
- Bug fix C
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Front Matter Fields
|
|
314
|
+
|
|
315
|
+
| Field | Required | Description |
|
|
316
|
+
|-------|----------|-------------|
|
|
317
|
+
| `title` | No | Display title for the entry. Defaults to the filename if not provided. |
|
|
318
|
+
| `date` | Yes | Date of the entry (`YYYY-MM-DD`). Used for sorting (newest first). |
|
|
319
|
+
| `tag` | No | A label displayed as a badge (e.g., `Release`, `Bug Fix`, `Feature`). Gets a CSS class based on its value for styling. |
|
|
320
|
+
|
|
321
|
+
## File Naming
|
|
322
|
+
|
|
323
|
+
Files can optionally be prefixed with a date in `YYYY-MM-DD-` format. The date prefix is stripped to create the URL slug:
|
|
324
|
+
|
|
325
|
+
- `2025-01-15-initial-release.md` → `/changelog/initial-release/`
|
|
326
|
+
- `new-features.md` → `/changelog/new-features/`
|
|
327
|
+
|
|
328
|
+
## Generated Pages
|
|
329
|
+
|
|
330
|
+
When changelog entries are found, Docula generates:
|
|
331
|
+
|
|
332
|
+
- **Changelog listing page** at `/changelog/` — shows all entries sorted by date (newest first) with titles, dates, tags, and content
|
|
333
|
+
- **Individual entry pages** at `/changelog/{slug}/` — a dedicated page for each entry with a back link to the listing
|
|
334
|
+
|
|
335
|
+
Changelog URLs are also automatically added to the generated `sitemap.xml`.
|
|
336
|
+
|
|
337
|
+
## Styling
|
|
338
|
+
|
|
339
|
+
Tags receive a CSS class based on their value (e.g., a tag of `"Bug Fix"` gets the class `changelog-tag-bug-fix`). You can style tags and other changelog elements by overriding these classes in your `variables.css`:
|
|
340
|
+
|
|
341
|
+
```css
|
|
342
|
+
.changelog-entry {
|
|
343
|
+
border-bottom: 1px solid var(--border);
|
|
344
|
+
padding: 1.5rem 0;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.changelog-tag {
|
|
348
|
+
font-size: 0.75rem;
|
|
349
|
+
padding: 0.2rem 0.5rem;
|
|
350
|
+
border-radius: 4px;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.changelog-tag-release {
|
|
354
|
+
background-color: #d4edda;
|
|
355
|
+
color: #155724;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.changelog-tag-bug-fix {
|
|
359
|
+
background-color: #f8d7da;
|
|
360
|
+
color: #721c24;
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
275
364
|
# Alert, Info, Warn Styling
|
|
276
365
|
|
|
277
366
|
Docula uses Writr's GitHub-flavored Markdown plugins, including GitHub-style blockquote alerts. Use the alert syntax directly in Markdown:
|
package/dist/docula.d.ts
CHANGED
|
@@ -49,6 +49,12 @@ declare class DoculaOptions {
|
|
|
49
49
|
* Sections
|
|
50
50
|
*/
|
|
51
51
|
sections?: DoculaSection[];
|
|
52
|
+
/**
|
|
53
|
+
* OpenAPI specification URL for API documentation.
|
|
54
|
+
* When provided, creates a dedicated /api page
|
|
55
|
+
* Supports both external URLs (https://...) and relative paths (/openapi.json)
|
|
56
|
+
*/
|
|
57
|
+
openApiUrl?: string;
|
|
52
58
|
constructor(options?: Record<string, unknown>);
|
|
53
59
|
parseOptions(options: Record<string, any>): void;
|
|
54
60
|
}
|
package/dist/docula.js
CHANGED
|
@@ -11,9 +11,7 @@ import updateNotifier from "update-notifier";
|
|
|
11
11
|
// src/builder.ts
|
|
12
12
|
import fs from "fs";
|
|
13
13
|
import path3 from "path";
|
|
14
|
-
import * as cheerio from "cheerio";
|
|
15
14
|
import { Ecto } from "ecto";
|
|
16
|
-
import he from "he";
|
|
17
15
|
import { Writr } from "writr";
|
|
18
16
|
|
|
19
17
|
// src/console.ts
|
|
@@ -291,6 +289,12 @@ var DoculaOptions = class {
|
|
|
291
289
|
* Sections
|
|
292
290
|
*/
|
|
293
291
|
sections;
|
|
292
|
+
/**
|
|
293
|
+
* OpenAPI specification URL for API documentation.
|
|
294
|
+
* When provided, creates a dedicated /api page
|
|
295
|
+
* Supports both external URLs (https://...) and relative paths (/openapi.json)
|
|
296
|
+
*/
|
|
297
|
+
openApiUrl;
|
|
294
298
|
constructor(options) {
|
|
295
299
|
if (options) {
|
|
296
300
|
this.parseOptions(options);
|
|
@@ -331,6 +335,9 @@ var DoculaOptions = class {
|
|
|
331
335
|
if (options.singlePage !== void 0 && typeof options.singlePage === "boolean") {
|
|
332
336
|
this.singlePage = options.singlePage;
|
|
333
337
|
}
|
|
338
|
+
if (options.openApiUrl) {
|
|
339
|
+
this.openApiUrl = options.openApiUrl;
|
|
340
|
+
}
|
|
334
341
|
}
|
|
335
342
|
};
|
|
336
343
|
|
|
@@ -360,7 +367,8 @@ var DoculaBuilder = class {
|
|
|
360
367
|
templatePath: this.options.templatePath,
|
|
361
368
|
outputPath: this.options.outputPath,
|
|
362
369
|
githubPath: this.options.githubPath,
|
|
363
|
-
sections: this.options.sections
|
|
370
|
+
sections: this.options.sections,
|
|
371
|
+
openApiUrl: this.options.openApiUrl
|
|
364
372
|
};
|
|
365
373
|
doculaData.github = await this.getGithubData(this.options.githubPath);
|
|
366
374
|
doculaData.documents = this.getDocuments(
|
|
@@ -372,9 +380,13 @@ var DoculaBuilder = class {
|
|
|
372
380
|
this.options
|
|
373
381
|
);
|
|
374
382
|
doculaData.hasDocuments = doculaData.documents?.length > 0;
|
|
383
|
+
const changelogPath = `${doculaData.sitePath}/changelog`;
|
|
384
|
+
doculaData.changelogEntries = this.getChangelogEntries(changelogPath);
|
|
385
|
+
doculaData.hasChangelog = doculaData.changelogEntries.length > 0;
|
|
375
386
|
doculaData.templates = await this.getTemplates(
|
|
376
387
|
this.options,
|
|
377
|
-
doculaData.hasDocuments
|
|
388
|
+
doculaData.hasDocuments,
|
|
389
|
+
doculaData.hasChangelog
|
|
378
390
|
);
|
|
379
391
|
await this.buildIndexPage(doculaData);
|
|
380
392
|
await this.buildReleasePage(doculaData);
|
|
@@ -383,6 +395,13 @@ var DoculaBuilder = class {
|
|
|
383
395
|
if (doculaData.hasDocuments) {
|
|
384
396
|
await this.buildDocsPages(doculaData);
|
|
385
397
|
}
|
|
398
|
+
if (doculaData.openApiUrl) {
|
|
399
|
+
await this.buildApiPage(doculaData);
|
|
400
|
+
}
|
|
401
|
+
if (doculaData.hasChangelog) {
|
|
402
|
+
await this.buildChangelogPage(doculaData);
|
|
403
|
+
await this.buildChangelogEntryPages(doculaData);
|
|
404
|
+
}
|
|
386
405
|
const siteRelativePath = this.options.sitePath;
|
|
387
406
|
if (fs.existsSync(`${siteRelativePath}/favicon.ico`)) {
|
|
388
407
|
await fs.promises.copyFile(
|
|
@@ -442,7 +461,7 @@ var DoculaBuilder = class {
|
|
|
442
461
|
const github = new Github(options);
|
|
443
462
|
return github.getData();
|
|
444
463
|
}
|
|
445
|
-
async getTemplates(options, hasDocuments) {
|
|
464
|
+
async getTemplates(options, hasDocuments, hasChangelog = false) {
|
|
446
465
|
const templates = {
|
|
447
466
|
index: "",
|
|
448
467
|
releases: ""
|
|
@@ -463,6 +482,18 @@ var DoculaBuilder = class {
|
|
|
463
482
|
if (documentPage) {
|
|
464
483
|
templates.docPage = documentPage;
|
|
465
484
|
}
|
|
485
|
+
const apiPage = options.openApiUrl ? await this.getTemplateFile(options.templatePath, "api") : void 0;
|
|
486
|
+
if (apiPage) {
|
|
487
|
+
templates.api = apiPage;
|
|
488
|
+
}
|
|
489
|
+
const changelogPage = hasChangelog ? await this.getTemplateFile(options.templatePath, "changelog") : void 0;
|
|
490
|
+
if (changelogPage) {
|
|
491
|
+
templates.changelog = changelogPage;
|
|
492
|
+
}
|
|
493
|
+
const changelogEntryPage = hasChangelog ? await this.getTemplateFile(options.templatePath, "changelog-entry") : void 0;
|
|
494
|
+
if (changelogEntryPage) {
|
|
495
|
+
templates.changelogEntry = changelogEntryPage;
|
|
496
|
+
}
|
|
466
497
|
} else {
|
|
467
498
|
throw new Error(`No template path found at ${options.templatePath}`);
|
|
468
499
|
}
|
|
@@ -490,6 +521,17 @@ var DoculaBuilder = class {
|
|
|
490
521
|
async buildSiteMapPage(data) {
|
|
491
522
|
const sitemapPath = `${data.outputPath}/sitemap.xml`;
|
|
492
523
|
const urls = [{ url: data.siteUrl }, { url: `${data.siteUrl}/releases` }];
|
|
524
|
+
if (data.openApiUrl && data.templates?.api) {
|
|
525
|
+
urls.push({ url: `${data.siteUrl}/api` });
|
|
526
|
+
}
|
|
527
|
+
if (data.hasChangelog && data.templates?.changelog) {
|
|
528
|
+
urls.push({ url: `${data.siteUrl}/changelog` });
|
|
529
|
+
for (const entry of data.changelogEntries ?? []) {
|
|
530
|
+
urls.push({
|
|
531
|
+
url: `${data.siteUrl}/changelog/${entry.slug}`
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
}
|
|
493
535
|
for (const document of data.documents ?? []) {
|
|
494
536
|
let { urlPath } = document;
|
|
495
537
|
if (urlPath.endsWith("index.html")) {
|
|
@@ -574,12 +616,11 @@ var DoculaBuilder = class {
|
|
|
574
616
|
recursive: true
|
|
575
617
|
});
|
|
576
618
|
const slug = `${data.outputPath}${document.urlPath}`;
|
|
577
|
-
|
|
619
|
+
const documentContent = await this._ecto.renderFromFile(
|
|
578
620
|
documentsTemplate,
|
|
579
621
|
{ ...data, ...document },
|
|
580
622
|
data.templatePath
|
|
581
623
|
);
|
|
582
|
-
documentContent = he.decode(documentContent);
|
|
583
624
|
return fs.promises.writeFile(slug, documentContent, "utf8");
|
|
584
625
|
});
|
|
585
626
|
await Promise.all(promises);
|
|
@@ -587,6 +628,121 @@ var DoculaBuilder = class {
|
|
|
587
628
|
throw new Error("No templates found");
|
|
588
629
|
}
|
|
589
630
|
}
|
|
631
|
+
async buildApiPage(data) {
|
|
632
|
+
if (!data.openApiUrl || !data.templates?.api) {
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
const apiPath = `${data.outputPath}/api/index.html`;
|
|
636
|
+
const apiOutputPath = `${data.outputPath}/api`;
|
|
637
|
+
await fs.promises.mkdir(apiOutputPath, { recursive: true });
|
|
638
|
+
const apiTemplate = `${data.templatePath}/${data.templates.api}`;
|
|
639
|
+
const apiContent = await this._ecto.renderFromFile(
|
|
640
|
+
apiTemplate,
|
|
641
|
+
{ ...data, specUrl: data.openApiUrl },
|
|
642
|
+
data.templatePath
|
|
643
|
+
);
|
|
644
|
+
await fs.promises.writeFile(apiPath, apiContent, "utf8");
|
|
645
|
+
}
|
|
646
|
+
getChangelogEntries(changelogPath) {
|
|
647
|
+
const entries = [];
|
|
648
|
+
if (!fs.existsSync(changelogPath)) {
|
|
649
|
+
return entries;
|
|
650
|
+
}
|
|
651
|
+
const files = fs.readdirSync(changelogPath);
|
|
652
|
+
for (const file of files) {
|
|
653
|
+
const filePath = `${changelogPath}/${file}`;
|
|
654
|
+
const stats = fs.statSync(filePath);
|
|
655
|
+
if (stats.isFile() && (file.endsWith(".md") || file.endsWith(".mdx"))) {
|
|
656
|
+
const entry = this.parseChangelogEntry(filePath);
|
|
657
|
+
entries.push(entry);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
entries.sort((a, b) => {
|
|
661
|
+
const dateA = new Date(a.date).getTime();
|
|
662
|
+
const dateB = new Date(b.date).getTime();
|
|
663
|
+
if (Number.isNaN(dateA) && Number.isNaN(dateB)) {
|
|
664
|
+
return 0;
|
|
665
|
+
}
|
|
666
|
+
if (Number.isNaN(dateA)) {
|
|
667
|
+
return 1;
|
|
668
|
+
}
|
|
669
|
+
if (Number.isNaN(dateB)) {
|
|
670
|
+
return -1;
|
|
671
|
+
}
|
|
672
|
+
return dateB - dateA;
|
|
673
|
+
});
|
|
674
|
+
return entries;
|
|
675
|
+
}
|
|
676
|
+
parseChangelogEntry(filePath) {
|
|
677
|
+
const fileContent = fs.readFileSync(filePath, "utf8");
|
|
678
|
+
const writr = new Writr(fileContent);
|
|
679
|
+
const matterData = writr.frontMatter;
|
|
680
|
+
const markdownContent = writr.body;
|
|
681
|
+
const fileName = path3.basename(filePath, path3.extname(filePath));
|
|
682
|
+
const slug = fileName.replace(/^\d{4}-\d{2}-\d{2}-/, "");
|
|
683
|
+
const isMdx = filePath.endsWith(".mdx");
|
|
684
|
+
const tag = matterData.tag;
|
|
685
|
+
const tagClass = tag ? tag.toLowerCase().replace(/\s+/g, "-") : void 0;
|
|
686
|
+
let dateString = "";
|
|
687
|
+
if (matterData.date instanceof Date) {
|
|
688
|
+
dateString = matterData.date.toISOString().split("T")[0];
|
|
689
|
+
} else if (matterData.date) {
|
|
690
|
+
dateString = String(matterData.date);
|
|
691
|
+
}
|
|
692
|
+
let formattedDate = dateString;
|
|
693
|
+
const parsedDate = new Date(dateString);
|
|
694
|
+
if (!Number.isNaN(parsedDate.getTime())) {
|
|
695
|
+
formattedDate = parsedDate.toLocaleDateString("en-US", {
|
|
696
|
+
year: "numeric",
|
|
697
|
+
month: "long",
|
|
698
|
+
day: "numeric"
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
return {
|
|
702
|
+
title: matterData.title ?? fileName,
|
|
703
|
+
date: dateString,
|
|
704
|
+
formattedDate,
|
|
705
|
+
tag,
|
|
706
|
+
tagClass,
|
|
707
|
+
slug,
|
|
708
|
+
content: markdownContent,
|
|
709
|
+
generatedHtml: new Writr(markdownContent).renderSync({ mdx: isMdx }),
|
|
710
|
+
urlPath: `/changelog/${slug}/index.html`
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
async buildChangelogPage(data) {
|
|
714
|
+
if (!data.hasChangelog || !data.templates?.changelog) {
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
const changelogOutputPath = `${data.outputPath}/changelog`;
|
|
718
|
+
const changelogIndexPath = `${changelogOutputPath}/index.html`;
|
|
719
|
+
await fs.promises.mkdir(changelogOutputPath, { recursive: true });
|
|
720
|
+
const changelogTemplate = `${data.templatePath}/${data.templates.changelog}`;
|
|
721
|
+
const changelogContent = await this._ecto.renderFromFile(
|
|
722
|
+
changelogTemplate,
|
|
723
|
+
{ ...data, entries: data.changelogEntries },
|
|
724
|
+
data.templatePath
|
|
725
|
+
);
|
|
726
|
+
await fs.promises.writeFile(changelogIndexPath, changelogContent, "utf8");
|
|
727
|
+
}
|
|
728
|
+
async buildChangelogEntryPages(data) {
|
|
729
|
+
if (!data.hasChangelog || !data.templates?.changelogEntry || !data.changelogEntries?.length) {
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
const entryTemplate = `${data.templatePath}/${data.templates.changelogEntry}`;
|
|
733
|
+
const promises = data.changelogEntries.map(async (entry) => {
|
|
734
|
+
const entryOutputPath = `${data.outputPath}/changelog/${entry.slug}`;
|
|
735
|
+
await fs.promises.mkdir(entryOutputPath, { recursive: true });
|
|
736
|
+
const entryContent = await this._ecto.renderFromFile(
|
|
737
|
+
entryTemplate,
|
|
738
|
+
{ ...data, ...entry, entries: data.changelogEntries },
|
|
739
|
+
data.templatePath
|
|
740
|
+
);
|
|
741
|
+
const entryFilePath = `${entryOutputPath}/index.html`;
|
|
742
|
+
return fs.promises.writeFile(entryFilePath, entryContent, "utf8");
|
|
743
|
+
});
|
|
744
|
+
await Promise.all(promises);
|
|
745
|
+
}
|
|
590
746
|
generateSidebarItems(data) {
|
|
591
747
|
let sidebarItems = [...data.sections ?? []];
|
|
592
748
|
for (const document of data.documents ?? []) {
|
|
@@ -723,6 +879,11 @@ var DoculaBuilder = class {
|
|
|
723
879
|
urlPath = documentPath.slice(documentsFolderIndex).replace(fileExtension, ".html");
|
|
724
880
|
}
|
|
725
881
|
}
|
|
882
|
+
if (!this.hasTableOfContents(markdownContent)) {
|
|
883
|
+
markdownContent = `## Table of Contents
|
|
884
|
+
|
|
885
|
+
${markdownContent}`;
|
|
886
|
+
}
|
|
726
887
|
return {
|
|
727
888
|
title: matterData.title,
|
|
728
889
|
navTitle: matterData.navTitle ?? matterData.title,
|
|
@@ -732,29 +893,15 @@ var DoculaBuilder = class {
|
|
|
732
893
|
keywords: matterData.keywords ?? [],
|
|
733
894
|
content: documentContent,
|
|
734
895
|
markdown: markdownContent,
|
|
735
|
-
generatedHtml: new Writr(markdownContent).renderSync({
|
|
736
|
-
|
|
896
|
+
generatedHtml: new Writr(markdownContent).renderSync({
|
|
897
|
+
toc: true,
|
|
898
|
+
mdx: isMdx
|
|
899
|
+
}),
|
|
737
900
|
documentPath,
|
|
738
901
|
urlPath,
|
|
739
902
|
isRoot
|
|
740
903
|
};
|
|
741
904
|
}
|
|
742
|
-
getTableOfContents(markdown, isMdx = false) {
|
|
743
|
-
if (this.hasTableOfContents(markdown)) {
|
|
744
|
-
return void 0;
|
|
745
|
-
}
|
|
746
|
-
markdown = `## Table of Contents
|
|
747
|
-
|
|
748
|
-
${markdown}`;
|
|
749
|
-
const html = new Writr(markdown).renderSync({ mdx: isMdx });
|
|
750
|
-
const $ = cheerio.load(html);
|
|
751
|
-
const tocTitle = $("h2").first();
|
|
752
|
-
const tocContent = tocTitle.next("ul").toString();
|
|
753
|
-
if (tocContent) {
|
|
754
|
-
return tocTitle.toString() + tocContent;
|
|
755
|
-
}
|
|
756
|
-
return void 0;
|
|
757
|
-
}
|
|
758
905
|
hasTableOfContents(markdown) {
|
|
759
906
|
const normalized = markdown.replace(/\r\n/g, "\n");
|
|
760
907
|
const atxHeading = /^#{1,6}\s*(table of contents|toc)\s*$/im;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "docula",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.1",
|
|
4
4
|
"description": "Beautiful Website for Your Projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/docula.js",
|
|
@@ -11,26 +11,15 @@
|
|
|
11
11
|
"import": "./dist/docula.js"
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
|
-
"repository":
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/jaredwray/docula.git"
|
|
17
|
+
},
|
|
15
18
|
"author": "Jared Wray <me@jaredwray.com>",
|
|
16
19
|
"engines": {
|
|
17
20
|
"node": ">=20"
|
|
18
21
|
},
|
|
19
22
|
"license": "MIT",
|
|
20
|
-
"scripts": {
|
|
21
|
-
"clean": "rimraf ./dist ./coverage ./node_modules ./package-lock.json ./yarn.lock ./pnpm-lock.yaml ./site/README.md ./site/dist",
|
|
22
|
-
"build": "pnpm generate-init-file && rimraf ./dist && tsup src/docula.ts --format esm --dts --clean",
|
|
23
|
-
"lint": "biome check --write --error-on-warnings",
|
|
24
|
-
"lint:ci": "biome check --error-on-warnings",
|
|
25
|
-
"test": "pnpm lint && vitest run --coverage",
|
|
26
|
-
"test:ci": "pnpm lint:ci && vitest run --coverage",
|
|
27
|
-
"generate-init-file": "tsx scripts/generate-init-file.ts",
|
|
28
|
-
"website:build": "rimraf ./site/README.md && node bin/docula.mjs build -s ./site -o ./site/dist",
|
|
29
|
-
"website:serve": "rimraf ./site/README.md && node bin/docula.mjs serve -s ./site -o ./site/dist",
|
|
30
|
-
"website:build:mega": "rimraf ./test/fixtures/mega-page-site/dist && node bin/docula.mjs build -s ./test/fixtures/mega-page-site",
|
|
31
|
-
"website:serve:mega": "rimraf ./test/fixtures/mega-page-site/dist && node bin/docula.mjs serve -s ./test/fixtures/mega-page-site",
|
|
32
|
-
"prepare": "pnpm build"
|
|
33
|
-
},
|
|
34
23
|
"keywords": [
|
|
35
24
|
"static-site-generator",
|
|
36
25
|
"static-site",
|
|
@@ -48,38 +37,50 @@
|
|
|
48
37
|
"handlebars"
|
|
49
38
|
],
|
|
50
39
|
"bin": {
|
|
51
|
-
"docula": "./bin/docula.
|
|
40
|
+
"docula": "./bin/docula.js"
|
|
52
41
|
},
|
|
53
42
|
"dependencies": {
|
|
54
|
-
"@cacheable/net": "^2.0.
|
|
55
|
-
"
|
|
56
|
-
"ecto": "^4.7.1",
|
|
43
|
+
"@cacheable/net": "^2.0.5",
|
|
44
|
+
"ecto": "^4.8.2",
|
|
57
45
|
"feed": "^5.2.0",
|
|
58
|
-
"he": "^1.2.0",
|
|
59
46
|
"jiti": "^2.6.1",
|
|
60
47
|
"serve-handler": "^6.1.6",
|
|
61
48
|
"update-notifier": "^7.3.1",
|
|
62
|
-
"writr": "^5.0.
|
|
49
|
+
"writr": "^5.0.3"
|
|
63
50
|
},
|
|
64
51
|
"devDependencies": {
|
|
65
|
-
"@biomejs/biome": "^2.
|
|
52
|
+
"@biomejs/biome": "^2.4.2",
|
|
66
53
|
"@types/express": "^5.0.6",
|
|
67
|
-
"@types/he": "^1.2.3",
|
|
68
54
|
"@types/js-yaml": "^4.0.9",
|
|
69
|
-
"@types/node": "^25.
|
|
55
|
+
"@types/node": "^25.2.3",
|
|
70
56
|
"@types/serve-handler": "^6.1.4",
|
|
71
57
|
"@types/update-notifier": "^6.0.8",
|
|
72
|
-
"@vitest/coverage-v8": "^4.0.
|
|
73
|
-
"dotenv": "^17.
|
|
74
|
-
"rimraf": "^6.1.
|
|
58
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
59
|
+
"dotenv": "^17.3.1",
|
|
60
|
+
"rimraf": "^6.1.3",
|
|
75
61
|
"tsup": "^8.5.1",
|
|
76
62
|
"tsx": "^4.21.0",
|
|
77
63
|
"typescript": "^5.9.3",
|
|
78
|
-
"vitest": "^4.0.
|
|
64
|
+
"vitest": "^4.0.18"
|
|
79
65
|
},
|
|
80
66
|
"files": [
|
|
81
67
|
"dist",
|
|
82
68
|
"template",
|
|
83
69
|
"bin"
|
|
84
|
-
]
|
|
85
|
-
|
|
70
|
+
],
|
|
71
|
+
"scripts": {
|
|
72
|
+
"clean": "rimraf ./dist ./coverage ./node_modules ./package-lock.json ./yarn.lock ./pnpm-lock.yaml ./site/README.md ./site/dist",
|
|
73
|
+
"build": "pnpm generate-init-file && rimraf ./dist && tsup src/docula.ts --format esm --dts --clean",
|
|
74
|
+
"lint": "biome check --write --error-on-warnings",
|
|
75
|
+
"lint:ci": "biome check --error-on-warnings",
|
|
76
|
+
"test": "pnpm lint && vitest run --coverage",
|
|
77
|
+
"test:ci": "pnpm lint:ci && vitest run --coverage",
|
|
78
|
+
"generate-init-file": "tsx scripts/generate-init-file.ts",
|
|
79
|
+
"website:build": "rimraf ./site/README.md && node bin/docula.js build -s ./site -o ./site/dist",
|
|
80
|
+
"website:serve": "rimraf ./site/README.md && node bin/docula.js serve -s ./site -o ./site/dist",
|
|
81
|
+
"website:build:mega": "rimraf ./test/fixtures/mega-page-site/dist && node bin/docula.js build -s ./test/fixtures/mega-page-site",
|
|
82
|
+
"website:serve:mega": "rimraf ./test/fixtures/mega-page-site/dist && node bin/docula.js serve -s ./test/fixtures/mega-page-site",
|
|
83
|
+
"website:build:changelog": "rimraf ./test/fixtures/changelog-site/dist && node bin/docula.js build -s ./test/fixtures/changelog-site",
|
|
84
|
+
"website:serve:changelog": "rimraf ./test/fixtures/changelog-site/dist && node bin/docula.js serve -s ./test/fixtures/changelog-site"
|
|
85
|
+
}
|
|
86
|
+
}
|
package/template/api.hbs
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
{{> header }}
|
|
6
|
+
<title>API Documentation - {{ siteTitle }}</title>
|
|
7
|
+
<meta name="description" content="API Documentation for {{ siteTitle }}" />
|
|
8
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@docutopia/react/dist/browser/docutopia.css" />
|
|
9
|
+
<style>
|
|
10
|
+
body {
|
|
11
|
+
margin: 0;
|
|
12
|
+
padding: 0;
|
|
13
|
+
}
|
|
14
|
+
</style>
|
|
15
|
+
</head>
|
|
16
|
+
|
|
17
|
+
<body>
|
|
18
|
+
<div id="docs" style="height: 100vh;"></div>
|
|
19
|
+
|
|
20
|
+
<script src="https://cdn.jsdelivr.net/npm/@docutopia/react/dist/browser/docutopia.js"></script>
|
|
21
|
+
<script>
|
|
22
|
+
Docutopia.render('docs', {
|
|
23
|
+
specUrl: '{{ specUrl }}',
|
|
24
|
+
});
|
|
25
|
+
</script>
|
|
26
|
+
</body>
|
|
27
|
+
|
|
28
|
+
</html>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
{{> header }}
|
|
6
|
+
<title>{{siteTitle}} - {{title}}</title>
|
|
7
|
+
</head>
|
|
8
|
+
|
|
9
|
+
<body>
|
|
10
|
+
{{> singlepage/hero }}
|
|
11
|
+
<a href="https://github.com/{{ githubPath }}" class="github-corner" aria-label="View source on GitHub"><svg width="80"
|
|
12
|
+
height="80" viewBox="0 0 250 250" style="color:#fff; position: absolute; top: 0; border: 0; right: 0;"
|
|
13
|
+
aria-hidden="true">
|
|
14
|
+
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
|
|
15
|
+
<path
|
|
16
|
+
d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
|
|
17
|
+
fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
|
|
18
|
+
<path
|
|
19
|
+
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
|
|
20
|
+
fill="currentColor" class="octo-body"></path>
|
|
21
|
+
</svg></a>
|
|
22
|
+
<style>
|
|
23
|
+
.github-corner:hover .octo-arm {
|
|
24
|
+
animation: octocat-wave 560ms ease-in-out
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@keyframes octocat-wave {
|
|
28
|
+
|
|
29
|
+
0%,
|
|
30
|
+
100% {
|
|
31
|
+
transform: rotate(0)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
20%,
|
|
35
|
+
60% {
|
|
36
|
+
transform: rotate(-25deg)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
40%,
|
|
40
|
+
80% {
|
|
41
|
+
transform: rotate(10deg)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@media (max-width:500px) {
|
|
46
|
+
.github-corner:hover .octo-arm {
|
|
47
|
+
animation: none
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.github-corner .octo-arm {
|
|
51
|
+
animation: octocat-wave 560ms ease-in-out
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
</style>
|
|
55
|
+
<main class="versions-container">
|
|
56
|
+
<div class="versions-content">
|
|
57
|
+
<div class="changelog-entry-nav">
|
|
58
|
+
<a href="/changelog/">← Back to Changelog</a>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="changelog-entry changelog-entry-single">
|
|
61
|
+
<div class="changelog-entry-header">
|
|
62
|
+
<h1 class="changelog-entry-title">{{title}}</h1>
|
|
63
|
+
{{#if tag}}
|
|
64
|
+
<span class="changelog-tag changelog-tag-{{tagClass}}">{{tag}}</span>
|
|
65
|
+
{{/if}}
|
|
66
|
+
</div>
|
|
67
|
+
<span class="changelog-entry-date">{{formattedDate}}</span>
|
|
68
|
+
<div class="changelog-entry-body">
|
|
69
|
+
{{{generatedHtml}}}
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
</main>
|
|
74
|
+
{{> footer}}
|
|
75
|
+
|
|
76
|
+
{{> scripts }}
|
|
77
|
+
</body>
|
|
78
|
+
|
|
79
|
+
</html>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
{{> header }}
|
|
6
|
+
<title>{{siteTitle}} Changelog</title>
|
|
7
|
+
</head>
|
|
8
|
+
|
|
9
|
+
<body>
|
|
10
|
+
{{> singlepage/hero }}
|
|
11
|
+
<a href="https://github.com/{{ githubPath }}" class="github-corner" aria-label="View source on GitHub"><svg width="80"
|
|
12
|
+
height="80" viewBox="0 0 250 250" style="color:#fff; position: absolute; top: 0; border: 0; right: 0;"
|
|
13
|
+
aria-hidden="true">
|
|
14
|
+
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
|
|
15
|
+
<path
|
|
16
|
+
d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
|
|
17
|
+
fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
|
|
18
|
+
<path
|
|
19
|
+
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
|
|
20
|
+
fill="currentColor" class="octo-body"></path>
|
|
21
|
+
</svg></a>
|
|
22
|
+
<style>
|
|
23
|
+
.github-corner:hover .octo-arm {
|
|
24
|
+
animation: octocat-wave 560ms ease-in-out
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@keyframes octocat-wave {
|
|
28
|
+
|
|
29
|
+
0%,
|
|
30
|
+
100% {
|
|
31
|
+
transform: rotate(0)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
20%,
|
|
35
|
+
60% {
|
|
36
|
+
transform: rotate(-25deg)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
40%,
|
|
40
|
+
80% {
|
|
41
|
+
transform: rotate(10deg)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@media (max-width:500px) {
|
|
46
|
+
.github-corner:hover .octo-arm {
|
|
47
|
+
animation: none
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.github-corner .octo-arm {
|
|
51
|
+
animation: octocat-wave 560ms ease-in-out
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
</style>
|
|
55
|
+
<main class="versions-container">
|
|
56
|
+
<div class="versions-content">
|
|
57
|
+
<h2 class="home-title">Changelog</h2>
|
|
58
|
+
{{#if entries}}
|
|
59
|
+
{{#each entries as |entry|}}
|
|
60
|
+
<div class="changelog-entry">
|
|
61
|
+
<div class="changelog-entry-header">
|
|
62
|
+
<a class="changelog-entry-title" href="/changelog/{{entry.slug}}/">{{entry.title}}</a>
|
|
63
|
+
{{#if entry.tag}}
|
|
64
|
+
<span class="changelog-tag changelog-tag-{{entry.tagClass}}">{{entry.tag}}</span>
|
|
65
|
+
{{/if}}
|
|
66
|
+
</div>
|
|
67
|
+
<span class="changelog-entry-date">{{entry.formattedDate}}</span>
|
|
68
|
+
<div class="changelog-entry-body">
|
|
69
|
+
{{{entry.generatedHtml}}}
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
{{/each}}
|
|
73
|
+
{{/if}}
|
|
74
|
+
</div>
|
|
75
|
+
</main>
|
|
76
|
+
{{> footer}}
|
|
77
|
+
|
|
78
|
+
{{> scripts }}
|
|
79
|
+
</body>
|
|
80
|
+
|
|
81
|
+
</html>
|
package/template/css/base.css
CHANGED
|
@@ -193,6 +193,140 @@ hr {
|
|
|
193
193
|
text-decoration: underline;
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
+
/* Changelog */
|
|
197
|
+
.changelog-entry {
|
|
198
|
+
overflow: hidden;
|
|
199
|
+
width: 100%;
|
|
200
|
+
line-break: anywhere;
|
|
201
|
+
margin-top: 2rem;
|
|
202
|
+
padding-bottom: 2rem;
|
|
203
|
+
border-bottom: 1px solid var(--border);
|
|
204
|
+
color: var(--color-text);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.changelog-entry:last-child {
|
|
208
|
+
border-bottom: none;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.changelog-entry-header {
|
|
212
|
+
display: flex;
|
|
213
|
+
align-items: center;
|
|
214
|
+
gap: 0.75rem;
|
|
215
|
+
margin-bottom: 0.5rem;
|
|
216
|
+
flex-wrap: wrap;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.changelog-entry-title {
|
|
220
|
+
font-size: 1.375rem;
|
|
221
|
+
color: var(--color-primary);
|
|
222
|
+
font-weight: 700;
|
|
223
|
+
transition: color .3s;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
a.changelog-entry-title:hover {
|
|
227
|
+
color: var(--color-secondary-dark);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
h1.changelog-entry-title {
|
|
231
|
+
font-size: 1.875rem;
|
|
232
|
+
margin: 0;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.changelog-entry-date {
|
|
236
|
+
font-size: 0.75rem;
|
|
237
|
+
display: block;
|
|
238
|
+
margin-bottom: 1rem;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.changelog-tag {
|
|
242
|
+
display: inline-block;
|
|
243
|
+
padding: 0.2rem 0.6rem;
|
|
244
|
+
border-radius: 1rem;
|
|
245
|
+
font-size: 0.7rem;
|
|
246
|
+
font-weight: 600;
|
|
247
|
+
text-transform: uppercase;
|
|
248
|
+
letter-spacing: 0.025em;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.changelog-tag-added {
|
|
252
|
+
background-color: rgba(140, 220, 0, 0.15);
|
|
253
|
+
color: #4a7a00;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.changelog-tag-improved {
|
|
257
|
+
background-color: rgba(59, 130, 246, 0.15);
|
|
258
|
+
color: #1d4ed8;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.changelog-tag-fixed {
|
|
262
|
+
background-color: rgba(245, 158, 11, 0.15);
|
|
263
|
+
color: #b45309;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.changelog-tag-removed {
|
|
267
|
+
background-color: rgba(239, 68, 68, 0.15);
|
|
268
|
+
color: #b91c1c;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.changelog-tag-deprecated {
|
|
272
|
+
background-color: rgba(156, 163, 175, 0.15);
|
|
273
|
+
color: #4b5563;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.changelog-tag-security {
|
|
277
|
+
background-color: rgba(168, 85, 247, 0.15);
|
|
278
|
+
color: #6d28d9;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.changelog-entry-body p {
|
|
282
|
+
margin: 0.5rem 0;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.changelog-entry-body pre {
|
|
286
|
+
margin-bottom: 2rem;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.changelog-entry-body h1 {
|
|
290
|
+
font-size: 1.375rem;
|
|
291
|
+
margin-bottom: 1rem;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.changelog-entry-body h2 {
|
|
295
|
+
font-size: 1.125rem;
|
|
296
|
+
margin-top: 1.75rem;
|
|
297
|
+
margin-bottom: 1rem;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.changelog-entry-body ul {
|
|
301
|
+
padding-left: 1rem;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.changelog-entry-body ul > li {
|
|
305
|
+
margin-bottom: 0.75rem;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.changelog-entry-body ul > li a {
|
|
309
|
+
text-decoration: underline;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.changelog-entry-nav {
|
|
313
|
+
margin-bottom: 1.5rem;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.changelog-entry-nav a {
|
|
317
|
+
color: var(--color-primary);
|
|
318
|
+
font-size: 0.875rem;
|
|
319
|
+
transition: color .3s;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.changelog-entry-nav a:hover {
|
|
323
|
+
color: var(--color-secondary-dark);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.changelog-entry-single {
|
|
327
|
+
border-bottom: none;
|
|
328
|
+
}
|
|
329
|
+
|
|
196
330
|
.github-corner {
|
|
197
331
|
fill: var(--color-primary) !important;
|
|
198
332
|
}
|
|
@@ -378,6 +512,9 @@ footer img {
|
|
|
378
512
|
.release {
|
|
379
513
|
margin-bottom: 4rem;
|
|
380
514
|
}
|
|
515
|
+
.changelog-entry {
|
|
516
|
+
margin-bottom: 2rem;
|
|
517
|
+
}
|
|
381
518
|
.versions-container {
|
|
382
519
|
margin-bottom: 4.5rem;
|
|
383
520
|
}
|
|
@@ -417,6 +554,9 @@ footer img {
|
|
|
417
554
|
.content h1 {
|
|
418
555
|
font-size: 1.9rem;
|
|
419
556
|
}
|
|
557
|
+
h1.changelog-entry-title {
|
|
558
|
+
font-size: 2.25rem;
|
|
559
|
+
}
|
|
420
560
|
.content > ul, .content > ol {
|
|
421
561
|
padding-left: 2rem;
|
|
422
562
|
}
|
|
@@ -241,10 +241,6 @@ details > summary {
|
|
|
241
241
|
margin-right: 2rem
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
-
.main-toc {
|
|
245
|
-
padding-bottom: 1.5rem;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
244
|
.toc {
|
|
249
245
|
font-size: 0.875rem;
|
|
250
246
|
}
|
|
@@ -287,18 +283,6 @@ details > summary {
|
|
|
287
283
|
text-decoration: none;
|
|
288
284
|
}
|
|
289
285
|
|
|
290
|
-
.main-toc ul > li > a::after {
|
|
291
|
-
position: absolute;
|
|
292
|
-
right: 0;
|
|
293
|
-
content: '';
|
|
294
|
-
display: inline-block;
|
|
295
|
-
width: 0.25rem;
|
|
296
|
-
height: 0.25rem;
|
|
297
|
-
border-right: 1px solid var(--color-primary);
|
|
298
|
-
border-bottom: 1px solid var(--color-primary);
|
|
299
|
-
transform: rotate(-45deg);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
286
|
.fixed-toc {
|
|
303
287
|
display: none;
|
|
304
288
|
position: fixed;
|
|
@@ -2,16 +2,22 @@
|
|
|
2
2
|
<div class="main-content">
|
|
3
3
|
<article class="content">
|
|
4
4
|
<h1>{{title}}</h1>
|
|
5
|
-
<div class="toc main-toc">
|
|
6
|
-
{{tableOfContents}}
|
|
7
|
-
</div>
|
|
8
5
|
{{generatedHtml}}
|
|
9
6
|
</article>
|
|
10
7
|
<div class="fixed-toc-container">
|
|
11
8
|
<div class="toc fixed-toc" id="fixed-toc">
|
|
12
|
-
{{tableOfContents}}
|
|
13
9
|
</div>
|
|
14
10
|
</div>
|
|
15
11
|
</div>
|
|
16
12
|
|
|
17
|
-
</div>
|
|
13
|
+
</div>
|
|
14
|
+
<script>
|
|
15
|
+
const tocHeading = document.getElementById('table-of-contents');
|
|
16
|
+
if (tocHeading) {
|
|
17
|
+
const tocList = tocHeading.nextElementSibling;
|
|
18
|
+
const fixedToc = document.getElementById('fixed-toc');
|
|
19
|
+
if (tocList && tocList.tagName === 'UL' && fixedToc) {
|
|
20
|
+
fixedToc.innerHTML = tocList.outerHTML;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
</script>
|
|
File without changes
|