docula 1.14.0 → 2.0.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/README.md +3 -3
- package/dist/docula.d.ts +1 -0
- package/dist/docula.js +36 -28
- package/package.json +17 -18
- package/templates/classic/includes/header.hbs +1 -1
- package/templates/modern/css/styles.css +1 -1
- package/templates/modern/includes/header.hbs +1 -1
- package/templates/modern/includes/scripts.hbs +2 -5
package/README.md
CHANGED
|
@@ -56,7 +56,7 @@ You can build Docula as a standalone binary that runs without Node.js installed.
|
|
|
56
56
|
|
|
57
57
|
## Building the Binary
|
|
58
58
|
|
|
59
|
-
Requires Node.js >=
|
|
59
|
+
Requires Node.js >= 25.7.0 to build (the resulting binary does not need Node.js to run). The build uses [tsdown's `exe` option](https://tsdown.dev/options/exe), which wraps Node.js's [Single Executable Applications](https://nodejs.org/api/single-executable-applications.html) feature added in Node 25.7.
|
|
60
60
|
|
|
61
61
|
```bash
|
|
62
62
|
pnpm install
|
|
@@ -69,7 +69,7 @@ This produces a platform-specific binary at `dist/docula` (or `dist/docula.exe`
|
|
|
69
69
|
|
|
70
70
|
1. Embeds all built-in templates (modern, classic) into the bundle as base64
|
|
71
71
|
2. Bundles all source code and dependencies into a single CJS file via [tsdown](https://tsdown.dev/)
|
|
72
|
-
3. Uses tsdown's built-in `exe` option to create a Node.js SEA binary
|
|
72
|
+
3. Uses tsdown's built-in `exe` option to create a Node.js SEA binary — blob generation, injection, and (on macOS hosts) ad-hoc code signing are handled automatically
|
|
73
73
|
|
|
74
74
|
## Testing the Binary
|
|
75
75
|
|
|
@@ -91,7 +91,7 @@ After building, test it locally:
|
|
|
91
91
|
|
|
92
92
|
## Cross-Platform Binaries
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
The CI workflow (`.github/workflows/build-binaries.yaml`) builds each platform natively on its own runner. Native builds avoid the cross-compile signing pitfall on Apple Silicon, where unsigned Mach-O binaries are killed on launch.
|
|
95
95
|
|
|
96
96
|
| Platform | Runner | Artifact |
|
|
97
97
|
|---|---|---|
|
package/dist/docula.d.ts
CHANGED
package/dist/docula.js
CHANGED
|
@@ -11,8 +11,9 @@ import { Writr, Writr as Writr$1 } from "writr";
|
|
|
11
11
|
import { blue, bold, cyan, dim, gray, green, magenta, red, white, yellow } from "colorette";
|
|
12
12
|
import { CacheableNet } from "@cacheable/net";
|
|
13
13
|
import os from "node:os";
|
|
14
|
+
import sea from "node:sea";
|
|
14
15
|
//#region package.json
|
|
15
|
-
var version = "
|
|
16
|
+
var version = "2.0.0";
|
|
16
17
|
var package_default = {
|
|
17
18
|
name: "docula",
|
|
18
19
|
version,
|
|
@@ -27,12 +28,12 @@ var package_default = {
|
|
|
27
28
|
"url": "git+https://github.com/jaredwray/docula.git"
|
|
28
29
|
},
|
|
29
30
|
author: "Jared Wray <me@jaredwray.com>",
|
|
30
|
-
engines: { "node": ">=
|
|
31
|
+
engines: { "node": "^22.18.0 || >=24.0.0" },
|
|
31
32
|
license: "MIT",
|
|
32
33
|
scripts: {
|
|
33
34
|
"clean": "rimraf ./dist ./coverage ./node_modules ./package-lock.json ./yarn.lock ./pnpm-lock.yaml ./site/dist",
|
|
34
35
|
"build": "pnpm generate-init-file && tsdown",
|
|
35
|
-
"build:binary": "pnpm generate-init-file && pnpm generate-embedded-templates && tsdown --config tsdown.config.binary.ts
|
|
36
|
+
"build:binary": "pnpm generate-init-file && pnpm generate-embedded-templates && tsdown --config tsdown.config.binary.ts",
|
|
36
37
|
"generate-embedded-templates": "tsx scripts/generate-embedded-templates.ts",
|
|
37
38
|
"lint": "biome check --write --error-on-warnings",
|
|
38
39
|
"lint:ci": "biome check --error-on-warnings",
|
|
@@ -69,39 +70,39 @@ var package_default = {
|
|
|
69
70
|
],
|
|
70
71
|
bin: { "docula": "./bin/docula.js" },
|
|
71
72
|
dependencies: {
|
|
72
|
-
"@ai-sdk/anthropic": "^3.0.
|
|
73
|
-
"@ai-sdk/google": "^3.0.
|
|
74
|
-
"@ai-sdk/openai": "^3.0.
|
|
73
|
+
"@ai-sdk/anthropic": "^3.0.77",
|
|
74
|
+
"@ai-sdk/google": "^3.0.72",
|
|
75
|
+
"@ai-sdk/openai": "^3.0.63",
|
|
75
76
|
"@cacheable/net": "^2.0.7",
|
|
76
|
-
"ai": "^6.0.
|
|
77
|
+
"ai": "^6.0.178",
|
|
77
78
|
"colorette": "^2.0.20",
|
|
78
|
-
"ecto": "^4.8.
|
|
79
|
+
"ecto": "^4.8.5",
|
|
79
80
|
"hashery": "^2.0.0",
|
|
80
|
-
"jiti": "^2.
|
|
81
|
+
"jiti": "^2.7.0",
|
|
81
82
|
"serve-handler": "^6.1.7",
|
|
82
83
|
"update-notifier": "^7.3.1",
|
|
83
|
-
"writr": "^6.1.
|
|
84
|
+
"writr": "^6.1.2"
|
|
84
85
|
},
|
|
85
86
|
devDependencies: {
|
|
86
|
-
"@biomejs/biome": "^2.4.
|
|
87
|
-
"@playwright/test": "^1.
|
|
88
|
-
"@types/node": "^
|
|
87
|
+
"@biomejs/biome": "^2.4.15",
|
|
88
|
+
"@playwright/test": "^1.60.0",
|
|
89
|
+
"@types/node": "^24.12.4",
|
|
89
90
|
"@types/serve-handler": "^6.1.4",
|
|
90
91
|
"@types/update-notifier": "^6.0.8",
|
|
91
|
-
"@vitest/coverage-v8": "^4.1.
|
|
92
|
+
"@vitest/coverage-v8": "^4.1.6",
|
|
92
93
|
"dotenv": "^17.4.2",
|
|
93
|
-
"postject": "1.0.0-alpha.6",
|
|
94
94
|
"rimraf": "^6.1.3",
|
|
95
|
-
"tsdown": "^0.
|
|
95
|
+
"tsdown": "^0.22.0",
|
|
96
96
|
"tsx": "^4.21.0",
|
|
97
|
-
"typescript": "^6.0.
|
|
98
|
-
"vitest": "^4.1.
|
|
97
|
+
"typescript": "^6.0.3",
|
|
98
|
+
"vitest": "^4.1.6"
|
|
99
99
|
},
|
|
100
100
|
files: [
|
|
101
101
|
"dist",
|
|
102
102
|
"templates",
|
|
103
103
|
"bin"
|
|
104
|
-
]
|
|
104
|
+
],
|
|
105
|
+
packageManager: "pnpm@11.1.3+sha512.c85357fe17ca12dd23dd7071822666dfd7e3cb76fe214e3370b5ea2fb34f2a231185509b63e717f3cd0acb38dd3f8d82bcd5e8172400ae678b70ea4fbed0896d"
|
|
105
106
|
};
|
|
106
107
|
//#endregion
|
|
107
108
|
//#region src/builder-ai.ts
|
|
@@ -118,15 +119,15 @@ async function createAIModel(ai) {
|
|
|
118
119
|
switch (ai.provider) {
|
|
119
120
|
case "anthropic": {
|
|
120
121
|
const { createAnthropic } = await import("@ai-sdk/anthropic");
|
|
121
|
-
return createAnthropic({ apiKey: ai.apiKey })(ai.model
|
|
122
|
+
return createAnthropic(ai.apiKey ? { apiKey: ai.apiKey } : {})(ai.model || "claude-haiku-4-5");
|
|
122
123
|
}
|
|
123
124
|
case "openai": {
|
|
124
125
|
const { createOpenAI } = await import("@ai-sdk/openai");
|
|
125
|
-
return createOpenAI({ apiKey: ai.apiKey })(ai.model
|
|
126
|
+
return createOpenAI(ai.apiKey ? { apiKey: ai.apiKey } : {})(ai.model || "gpt-4o-mini");
|
|
126
127
|
}
|
|
127
128
|
case "google": {
|
|
128
129
|
const { createGoogleGenerativeAI } = await import("@ai-sdk/google");
|
|
129
|
-
return createGoogleGenerativeAI({ apiKey: ai.apiKey })(ai.model
|
|
130
|
+
return createGoogleGenerativeAI(ai.apiKey ? { apiKey: ai.apiKey } : {})(ai.model || "gemini-2.5-flash-lite");
|
|
130
131
|
}
|
|
131
132
|
default: return;
|
|
132
133
|
}
|
|
@@ -597,6 +598,7 @@ function extractResponses(operation, spec) {
|
|
|
597
598
|
responses.push({
|
|
598
599
|
statusCode,
|
|
599
600
|
statusClass: getStatusClass(statusCode),
|
|
601
|
+
/* v8 ignore next */
|
|
600
602
|
description: response.description ?? "",
|
|
601
603
|
contentType,
|
|
602
604
|
schemaProperties,
|
|
@@ -1171,6 +1173,7 @@ function hasAssetsChanged(hash, sitePath, previousAssets, autoReadme) {
|
|
|
1171
1173
|
for (const file of [
|
|
1172
1174
|
"favicon.ico",
|
|
1173
1175
|
"logo.svg",
|
|
1176
|
+
"logo.png",
|
|
1174
1177
|
"logo_horizontal.png",
|
|
1175
1178
|
"variables.css",
|
|
1176
1179
|
"api/swagger.json",
|
|
@@ -2695,12 +2698,7 @@ var DoculaOptions = class {
|
|
|
2695
2698
|
* Returns true when running as a single-executable application (SEA).
|
|
2696
2699
|
*/
|
|
2697
2700
|
function isSEA() {
|
|
2698
|
-
|
|
2699
|
-
return Boolean(process.sea);
|
|
2700
|
-
} catch {
|
|
2701
|
-
/* v8 ignore next -- @preserve */
|
|
2702
|
-
return false;
|
|
2703
|
-
}
|
|
2701
|
+
return sea.isSea();
|
|
2704
2702
|
}
|
|
2705
2703
|
/**
|
|
2706
2704
|
* Returns the deterministic temp directory path for extracted templates.
|
|
@@ -2831,6 +2829,12 @@ var DoculaBuilder = class {
|
|
|
2831
2829
|
editPageUrl: this.options.editPageUrl,
|
|
2832
2830
|
openGraph: this.options.openGraph
|
|
2833
2831
|
};
|
|
2832
|
+
const resolvedFavicon = [
|
|
2833
|
+
"favicon.ico",
|
|
2834
|
+
"logo.svg",
|
|
2835
|
+
"logo.png"
|
|
2836
|
+
].find((file) => fs.existsSync(path.join(this.options.sitePath, file)));
|
|
2837
|
+
if (resolvedFavicon) doculaData.faviconUrl = buildUrlPath(this.options.baseUrl, resolvedFavicon);
|
|
2834
2838
|
if (siteReadmeExists) currentAssetHashes["README.md"] = hashFile(this._hash, path.join(this.options.sitePath, "README.md"));
|
|
2835
2839
|
else if (autoReadmeResult) currentAssetHashes.__autoReadme = hashFile(this._hash, autoReadmeResult.sourcePath);
|
|
2836
2840
|
if (Array.isArray(this.options.openApiUrl)) doculaData.openApiSpecs = this.options.openApiUrl.map((spec) => ({
|
|
@@ -2966,6 +2970,10 @@ var DoculaBuilder = class {
|
|
|
2966
2970
|
await fs.promises.copyFile(`${siteRelativePath}/logo.svg`, `${this.options.output}/logo.svg`);
|
|
2967
2971
|
this._console.fileCopied("logo.svg");
|
|
2968
2972
|
}
|
|
2973
|
+
if (!hashAssetAndCheckSkip(this._hash, path.join(siteRelativePath, "logo.png"), path.join(this.options.output, "logo.png"), "logo.png", previousAssets, currentAssetHashes)) {
|
|
2974
|
+
await fs.promises.copyFile(path.join(siteRelativePath, "logo.png"), path.join(this.options.output, "logo.png"));
|
|
2975
|
+
this._console.fileCopied("logo.png");
|
|
2976
|
+
}
|
|
2969
2977
|
if (!hashAssetAndCheckSkip(this._hash, `${siteRelativePath}/logo_horizontal.png`, `${this.options.output}/logo_horizontal.png`, "logo_horizontal.png", previousAssets, currentAssetHashes)) {
|
|
2970
2978
|
await fs.promises.copyFile(`${siteRelativePath}/logo_horizontal.png`, `${this.options.output}/logo_horizontal.png`);
|
|
2971
2979
|
this._console.fileCopied("logo_horizontal.png");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "docula",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Beautiful Website for Your Projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/docula.js",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
},
|
|
18
18
|
"author": "Jared Wray <me@jaredwray.com>",
|
|
19
19
|
"engines": {
|
|
20
|
-
"node": ">=
|
|
20
|
+
"node": "^22.18.0 || >=24.0.0"
|
|
21
21
|
},
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"keywords": [
|
|
@@ -40,33 +40,32 @@
|
|
|
40
40
|
"docula": "./bin/docula.js"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@ai-sdk/anthropic": "^3.0.
|
|
44
|
-
"@ai-sdk/google": "^3.0.
|
|
45
|
-
"@ai-sdk/openai": "^3.0.
|
|
43
|
+
"@ai-sdk/anthropic": "^3.0.77",
|
|
44
|
+
"@ai-sdk/google": "^3.0.72",
|
|
45
|
+
"@ai-sdk/openai": "^3.0.63",
|
|
46
46
|
"@cacheable/net": "^2.0.7",
|
|
47
|
-
"ai": "^6.0.
|
|
47
|
+
"ai": "^6.0.178",
|
|
48
48
|
"colorette": "^2.0.20",
|
|
49
|
-
"ecto": "^4.8.
|
|
49
|
+
"ecto": "^4.8.5",
|
|
50
50
|
"hashery": "^2.0.0",
|
|
51
|
-
"jiti": "^2.
|
|
51
|
+
"jiti": "^2.7.0",
|
|
52
52
|
"serve-handler": "^6.1.7",
|
|
53
53
|
"update-notifier": "^7.3.1",
|
|
54
|
-
"writr": "^6.1.
|
|
54
|
+
"writr": "^6.1.2"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@biomejs/biome": "^2.4.
|
|
58
|
-
"@playwright/test": "^1.
|
|
59
|
-
"@types/node": "^
|
|
57
|
+
"@biomejs/biome": "^2.4.15",
|
|
58
|
+
"@playwright/test": "^1.60.0",
|
|
59
|
+
"@types/node": "^24.12.4",
|
|
60
60
|
"@types/serve-handler": "^6.1.4",
|
|
61
61
|
"@types/update-notifier": "^6.0.8",
|
|
62
|
-
"@vitest/coverage-v8": "^4.1.
|
|
62
|
+
"@vitest/coverage-v8": "^4.1.6",
|
|
63
63
|
"dotenv": "^17.4.2",
|
|
64
|
-
"postject": "1.0.0-alpha.6",
|
|
65
64
|
"rimraf": "^6.1.3",
|
|
66
|
-
"tsdown": "^0.
|
|
65
|
+
"tsdown": "^0.22.0",
|
|
67
66
|
"tsx": "^4.21.0",
|
|
68
|
-
"typescript": "^6.0.
|
|
69
|
-
"vitest": "^4.1.
|
|
67
|
+
"typescript": "^6.0.3",
|
|
68
|
+
"vitest": "^4.1.6"
|
|
70
69
|
},
|
|
71
70
|
"files": [
|
|
72
71
|
"dist",
|
|
@@ -76,7 +75,7 @@
|
|
|
76
75
|
"scripts": {
|
|
77
76
|
"clean": "rimraf ./dist ./coverage ./node_modules ./package-lock.json ./yarn.lock ./pnpm-lock.yaml ./site/dist",
|
|
78
77
|
"build": "pnpm generate-init-file && tsdown",
|
|
79
|
-
"build:binary": "pnpm generate-init-file && pnpm generate-embedded-templates && tsdown --config tsdown.config.binary.ts
|
|
78
|
+
"build:binary": "pnpm generate-init-file && pnpm generate-embedded-templates && tsdown --config tsdown.config.binary.ts",
|
|
80
79
|
"generate-embedded-templates": "tsx scripts/generate-embedded-templates.ts",
|
|
81
80
|
"lint": "biome check --write --error-on-warnings",
|
|
82
81
|
"lint:ci": "biome check --error-on-warnings",
|
|
@@ -31,4 +31,4 @@ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
|
|
31
31
|
rel="stylesheet"
|
|
32
32
|
/>
|
|
33
33
|
<link rel="stylesheet" href="/css/highlight/styles/base16/dracula.min.css" />
|
|
34
|
-
<link rel="icon" href="
|
|
34
|
+
{{#if faviconUrl}}<link rel="icon" href="{{faviconUrl}}" />{{/if}}
|
|
@@ -398,7 +398,7 @@ body {
|
|
|
398
398
|
.copy-code-btn { position: absolute; top: 8px; right: 8px; padding: 4px; line-height: 0; border-radius: 4px; background: transparent; color: var(--muted); border: none; cursor: pointer; opacity: 0; transition: opacity 0.15s; }
|
|
399
399
|
pre:hover .copy-code-btn { opacity: 1; }
|
|
400
400
|
.copy-code-btn:hover { color: var(--fg); }
|
|
401
|
-
.article__main img, .changelog-entry-body img { cursor: zoom-in; }
|
|
401
|
+
.article__main img:not(a img), .changelog-entry-body img:not(a img) { cursor: zoom-in; }
|
|
402
402
|
.lightbox-overlay { display: none; position: fixed; inset: 0; z-index: 200; background: rgba(0, 0, 0, 0.8); justify-content: center; align-items: center; cursor: pointer; }
|
|
403
403
|
.lightbox-overlay--visible { display: flex !important; }
|
|
404
404
|
.lightbox-overlay img { max-width: 90vw; max-height: 90vh; border-radius: 8px; box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4); cursor: default; }
|
|
@@ -22,7 +22,7 @@ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
|
|
22
22
|
<link rel="stylesheet" href="{{baseUrl}}/css/variables.css">
|
|
23
23
|
<link rel="stylesheet" href="{{baseUrl}}/css/styles.css">
|
|
24
24
|
<link rel="stylesheet" href="{{baseUrl}}/css/highlight/styles/base16/docula.css">
|
|
25
|
-
<link rel="icon" href="{{
|
|
25
|
+
{{#if faviconUrl}}<link rel="icon" href="{{faviconUrl}}">{{/if}}
|
|
26
26
|
<script>
|
|
27
27
|
(function(){
|
|
28
28
|
window.__doculaThemeKey = 'docula:theme:' + ({{#if siteUrl}}'{{siteUrl}}'{{else}}location.origin{{/if}}).replace(/^https?:\/\//, '');
|
|
@@ -236,10 +236,6 @@
|
|
|
236
236
|
storedSectionState = JSON.parse(localStorage.getItem(SIDEBAR_STORAGE_KEY) || '{}');
|
|
237
237
|
} catch (e) { storedSectionState = {}; }
|
|
238
238
|
|
|
239
|
-
const activeSidebarLink = document.querySelector('.nav-sidebar__item--active');
|
|
240
|
-
const activeSection = activeSidebarLink ? activeSidebarLink.closest('.nav-sidebar__section--collapsible') : null;
|
|
241
|
-
const defaultOpenSection = activeSection || collapsibleSections[0];
|
|
242
|
-
|
|
243
239
|
const setSectionOpen = (section, toggle, open) => {
|
|
244
240
|
toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
|
|
245
241
|
section.classList.toggle('nav-sidebar__section--collapsed', !open);
|
|
@@ -255,7 +251,7 @@
|
|
|
255
251
|
|
|
256
252
|
const key = 'section-' + idx;
|
|
257
253
|
const hasStored = Object.prototype.hasOwnProperty.call(storedSectionState, key);
|
|
258
|
-
const isOpen = hasStored ? !!storedSectionState[key] :
|
|
254
|
+
const isOpen = hasStored ? !!storedSectionState[key] : true;
|
|
259
255
|
setSectionOpen(section, toggle, isOpen);
|
|
260
256
|
|
|
261
257
|
toggle.addEventListener('click', () => {
|
|
@@ -324,6 +320,7 @@
|
|
|
324
320
|
document.body.appendChild(lightboxOverlay);
|
|
325
321
|
|
|
326
322
|
document.querySelectorAll('.article__main img, .changelog-entry-body img').forEach(function(img) {
|
|
323
|
+
if (img.closest('a')) return;
|
|
327
324
|
img.addEventListener('click', function() {
|
|
328
325
|
lightboxImg.src = img.src;
|
|
329
326
|
lightboxOverlay.classList.add('lightbox-overlay--visible');
|