docpensieve 0.1.0 → 0.1.2

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.
Files changed (42) hide show
  1. package/README.md +5 -2
  2. package/bin/docpensieve.js +1 -0
  3. package/package.json +8 -6
  4. package/src/commands/dev.js +1 -1
  5. package/src/commands/init.js +214 -39
  6. package/starter/01-guide/01-installation.md +79 -0
  7. package/starter/01-guide/02-first-site.md +97 -0
  8. package/starter/01-guide/03-writing-pages.md +138 -0
  9. package/starter/01-guide/04-versions.md +179 -0
  10. package/starter/01-guide/05-themes.md +116 -0
  11. package/starter/01-guide/06-deployment.md +124 -0
  12. package/starter/01-guide/index.md +38 -0
  13. package/starter/02-components/01-card.mdx +195 -0
  14. package/starter/02-components/02-columns.mdx +193 -0
  15. package/starter/02-components/03-time-timer.mdx +120 -0
  16. package/starter/02-components/04-tooltip.mdx +100 -0
  17. package/starter/02-components/05-tree.mdx +163 -0
  18. package/starter/02-components/06-scroll-to-top.mdx +103 -0
  19. package/starter/02-components/07-skill.mdx +214 -0
  20. package/starter/02-components/08-logo-icon.mdx +122 -0
  21. package/starter/02-components/icons/banner.svg +15 -0
  22. package/starter/02-components/icons/book.svg +4 -0
  23. package/starter/02-components/icons/lightning.svg +3 -0
  24. package/starter/02-components/icons/shield.svg +4 -0
  25. package/starter/02-components/icons/star.svg +3 -0
  26. package/starter/02-components/index.md +41 -0
  27. package/starter/03-reference/01-cli.md +134 -0
  28. package/starter/03-reference/02-configuration.md +132 -0
  29. package/starter/03-reference/03-frontmatter.md +110 -0
  30. package/starter/03-reference/04-theme.md +149 -0
  31. package/starter/03-reference/index.md +32 -0
  32. package/starter/04-architecture.md +106 -0
  33. package/starter/icons/blocks.svg +6 -0
  34. package/starter/icons/book.svg +4 -0
  35. package/starter/icons/branch.svg +6 -0
  36. package/starter/icons/compass.svg +4 -0
  37. package/starter/icons/lightning.svg +3 -0
  38. package/starter/icons/list.svg +4 -0
  39. package/starter/icons/shield.svg +4 -0
  40. package/starter/icons/star.svg +3 -0
  41. package/starter/index.mdx +244 -0
  42. package/types/commands/init.d.ts +7 -4
@@ -0,0 +1,138 @@
1
+ ---
2
+ title: Writing pages
3
+ description: Frontmatter, URLs, menu order, links and images.
4
+ tags: [guide, content]
5
+
6
+ jsonld:
7
+ type: TechArticle
8
+ breadcrumbs: true
9
+ ---
10
+
11
+ # Writing pages
12
+
13
+ A page is a `.md` or `.mdx` file in the version folder. Both go through the
14
+ same chain: the extension does not change what is possible, it only states the
15
+ intent.
16
+
17
+ ## The frontmatter
18
+
19
+ ```yaml
20
+ ---
21
+ title: Installation
22
+ description: What you need, and how to set up a project.
23
+ date: 2026-09-09
24
+ tags: [guide, installation]
25
+ ---
26
+ ```
27
+
28
+ `title` becomes the `<title>` tag, the menu entry and the breadcrumb.
29
+ `description` feeds the metadata and the JSON-LD. Everything is optional:
30
+ without `title`, the project name stands in.
31
+
32
+ The fields are detailed in the [reference](../reference/frontmatter/).
33
+
34
+ ## What the URL depends on
35
+
36
+ The file path gives the URL path, stripped of its extension and of its sorting
37
+ prefix:
38
+
39
+ | File | URL |
40
+ | --------------------------- | ---------------------- |
41
+ | `index.md` | `/` |
42
+ | `guide/01-installation.md` | `/guide/installation/` |
43
+ | `guide/index.md` | `/guide/` |
44
+ | `components/02-columns.mdx` | `/components/columns/` |
45
+
46
+ The `01-` prefix **orders the menu without appearing in the URL**. It is the
47
+ only way to sort pages other than alphabetically, and it saves keeping a
48
+ separate list.
49
+
50
+ ## The menu order
51
+
52
+ The menu is derived from the file tree. Folders become sections, numeric
53
+ prefixes give the order, and an `index.md` in a folder provides the title of
54
+ the section.
55
+
56
+ ```
57
+ docs/v1.0/
58
+ ├── index.mdx → /
59
+ ├── guide/
60
+ │ ├── index.md → /guide/ (title of the section)
61
+ │ ├── 01-installation.md
62
+ │ └── 02-first-site.md
63
+ └── components/
64
+ ├── index.md
65
+ └── 01-card.mdx
66
+ ```
67
+
68
+ ## Internal links
69
+
70
+ Two spellings, two meanings:
71
+
72
+ - **relative** — `./sibling/`, `../guide/` — resolves against the folder of the
73
+ page's file, as between any two files;
74
+ - **absolute** — `/guide/installation/` — starts from the **version root**, not
75
+ from the domain root.
76
+
77
+ The second rule deserves a pause. A documentation does not know it may be
78
+ served under `/my-project/versions/v1.0/`: if `/guide/installation/` were taken
79
+ literally, every internal link would break as soon as a prefix comes into play.
80
+ They are therefore rewritten at build time.
81
+
82
+ To target a real domain URL, the full address remains.
83
+
84
+ ## Images
85
+
86
+ An image sits next to the page and is written relatively:
87
+
88
+ ```md
89
+ ![Pipeline diagram](./diagram.png)
90
+ ```
91
+
92
+ Files that are not pages are copied as is into the output, at the same
93
+ relative place. The path is rewritten like a link.
94
+
95
+ ## Components
96
+
97
+ In an `.mdx` page, the shipped components are used **without an import**:
98
+
99
+ ```mdx
100
+ <Columns>
101
+ <Column span={8}>The bulk of the point</Column>
102
+ <Column span={4}>A side remark</Column>
103
+ </Columns>
104
+ ```
105
+
106
+ They are rendered at build time: the delivered HTML only holds their result.
107
+ The list is in [Components](../components/).
108
+
109
+ ## A trap to know
110
+
111
+ The content of a JSX tag **left alone on its own line** becomes a paragraph:
112
+
113
+ ```mdx
114
+ <p className="flex gap-3">Some text</p>
115
+ ```
116
+
117
+ produces `<p class="flex gap-3"><p>Some text</p></p>` — two nested paragraphs,
118
+ which is invalid. The browser closes the first one by itself: the wrapper
119
+ disappears, and the intended layout with it.
120
+
121
+ The formatter makes the trap sneaky. A long string of classes ends up broken
122
+ over several lines, which leaves the text alone on its line **after the fact**,
123
+ without anyone having written it that way.
124
+
125
+ Three ways to guard against it:
126
+
127
+ - prefer `<div>` to `<p>` as a wrapper — a paragraph is valid inside it;
128
+ - write short content on the same line as its tags;
129
+ - for repeated cases, put a class in `theme.css` rather than a long string of
130
+ utilities, so that the line stays short.
131
+
132
+ `docpensieve check` reports these nestings on the produced site.
133
+
134
+ ## What is not published
135
+
136
+ A page whose frontmatter carries `draft: true` is loaded but kept out of the
137
+ output. That is what lets a work-in-progress page stay in the repository
138
+ without being published.
@@ -0,0 +1,179 @@
1
+ ---
2
+ title: Versions
3
+ description: Keep the current version and the one being prepared side by side.
4
+ tags: [guide, versions]
5
+
6
+ jsonld:
7
+ type: TechArticle
8
+ breadcrumbs: true
9
+ ---
10
+
11
+ # Versions
12
+
13
+ A version is a **source folder** and a **configuration entry**. Nothing else.
14
+ Everything else — URL, menu, switcher, branch — follows from them.
15
+
16
+ ## The two-track model
17
+
18
+ It is the most common arrangement, and the one DocPensieve's own documentation uses:
19
+
20
+ | Version | Role | Who sees it |
21
+ | ----------- | ------------------------------------------- | -------------------- |
22
+ | **current** | the one that is online and being fixed | everyone, by default |
23
+ | **beta** | the one being prepared for the next release | those who pick it |
24
+
25
+ A visitor arriving at the root is sent to the **current** one. The beta exists,
26
+ it is reachable, but nobody lands there by chance.
27
+
28
+ ## 1. Declaring the versions
29
+
30
+ In `docpensieve.config.mjs`:
31
+
32
+ ```js
33
+ versions: [
34
+ { slug: 'v1.1-beta', name: '1.1 (beta)', folder: 'docs/v1.1-beta', prerelease: true },
35
+ { slug: 'v1.0', name: '1.0', folder: 'docs/v1.0', current: true },
36
+ ],
37
+ ```
38
+
39
+ | Field | Role |
40
+ | ------------ | ------------------------------------------------------------- |
41
+ | `slug` | URL and branch identifier. It shows up in `/versions/<slug>/` |
42
+ | `name` | What the visitor reads in the switcher |
43
+ | `folder` | Source folder, relative to the project root |
44
+ | `current` | The version served by default. **At most one** |
45
+ | `archived` | Version kept but no longer receiving fixes |
46
+ | `prerelease` | Version in preparation, not yet the current one |
47
+
48
+ Two rules to remember:
49
+
50
+ - **only one version can carry `current`** — two stop the build;
51
+ - if **none** carries it, the first in the list becomes current. A
52
+ single-version configuration therefore has nothing to specify.
53
+
54
+ The switcher only appears in the header from **two** versions on: a single
55
+ choice is not a choice.
56
+
57
+ ## 2. Opening the beta
58
+
59
+ Start from the current version, and give it its own folder:
60
+
61
+ ```bash
62
+ cp -r docs/v1.0 docs/v1.1-beta
63
+ ```
64
+
65
+ Then declare the entry with `prerelease` — and **without** `current`, which the
66
+ current version keeps:
67
+
68
+ ```js
69
+ versions: [
70
+ { slug: 'v1.1-beta', name: '1.1 (beta)', folder: 'docs/v1.1-beta', prerelease: true },
71
+ { slug: 'v1.0', name: '1.0', folder: 'docs/v1.0', current: true },
72
+ ],
73
+ ```
74
+
75
+ `prerelease` is not just a label. It puts on **every page** of the version a
76
+ banner pointing to the current one, and a
77
+ `<meta name="robots" content="noindex, follow">` in the head.
78
+
79
+ This second point matters more than it seems. A beta is a near-identical copy
80
+ of the current version: without it, both compete for the same place in search
81
+ engines, and it is often the wrong one that comes up. Someone would then read a
82
+ documentation in progress while believing they read the one that counts.
83
+ `follow` still lets the page's links be followed.
84
+
85
+ A version cannot be both `current` and `prerelease`: the build stops. The
86
+ notice of one would contradict the role of the other.
87
+
88
+ ```bash
89
+ npx docpensieve build
90
+ npx docpensieve serve
91
+ ```
92
+
93
+ The switcher now offers both. The root still leads to `1.0`.
94
+
95
+ From there, the two folders live their own lives: what you write in
96
+ `docs/v1.1-beta` does not touch the online documentation.
97
+
98
+ ### A fix that applies to both
99
+
100
+ Fixing a typo in the current version does not fix it in the beta: they are two
101
+ separate folders. It is the price of frozen versions, and it is paid at every
102
+ fix — carrying it over into both folders is part of the work.
103
+
104
+ ## 3. Promoting the beta
105
+
106
+ On release day, the beta becomes the current version. Only one thing changes:
107
+ **where `current` sits**.
108
+
109
+ ```js
110
+ versions: [
111
+ { slug: 'v1.1', name: '1.1', folder: 'docs/v1.1', current: true },
112
+ { slug: 'v1.0', name: '1.0', folder: 'docs/v1.0', archived: true },
113
+ ],
114
+ ```
115
+
116
+ In practice, three steps:
117
+
118
+ 1. rename the `docs/v1.1-beta` folder to `docs/v1.1`;
119
+ 2. in the configuration, change the `slug`, the `name`, the `folder`, and move
120
+ `current`;
121
+ 3. remove `prerelease` from the new one, and mark the old one `archived`.
122
+
123
+ ```bash
124
+ npx docpensieve build
125
+ ```
126
+
127
+ `archived` says the version will no longer receive fixes. It **stays online**:
128
+ it is not a shutdown, it is information. Its pages also carry a banner pointing
129
+ to the current version, but **stay indexed** — a past version still matters to
130
+ those who use it.
131
+
132
+ > **The `slug` changes along with the folder, and so does the URL.**
133
+ > `/versions/v1.1-beta/` disappears in favour of `/versions/v1.1/`, and the
134
+ > external links that pointed to the beta lead nowhere any more. To avoid this,
135
+ > keep the same slug from start to finish — `v1.1` from the start, with only
136
+ > the label mentioning the beta.
137
+
138
+ ## What the build produces
139
+
140
+ ```
141
+ dist/
142
+ ├── index.html redirect to the current version
143
+ ├── versions.json the list, for whoever wants to read it
144
+ └── versions/
145
+ ├── v1.1-beta/
146
+ └── v1.0/
147
+ ```
148
+
149
+ The root is an **HTML redirect**, not a server rule: the output stays
150
+ publishable on any static host, without configuration.
151
+
152
+ `versions.json` describes each version — its slug, its label, its URL, whether
153
+ it is current, whether it is archived.
154
+
155
+ ## Building a single version
156
+
157
+ ```bash
158
+ npx docpensieve build v1.1-beta
159
+ ```
160
+
161
+ Only this version is written, in `versions/v1.1-beta/`. The others are not
162
+ touched — handy when working on the beta without wanting to rebuild the rest.
163
+
164
+ One caveat: this form writes **neither the manifest nor the root redirect**,
165
+ which concern no version in particular. After adding, renaming or removing a
166
+ version, run a full build.
167
+
168
+ ## One branch per version
169
+
170
+ The model goes further than the folder: each compiled version can live on its
171
+ **own orphan branch**, named after its slug, with a history separate from that
172
+ of the sources.
173
+
174
+ The point is not storage, it is time. A version published two years ago stays
175
+ what it was, in the state in which it was produced — without depending on
176
+ today's generator or today's sources. Rebuilding it is never necessary, and
177
+ nothing would guarantee it gives the same result.
178
+
179
+ The sources stay on the working branch. The two never mix.
@@ -0,0 +1,116 @@
1
+ ---
2
+ title: Themes
3
+ description: Style the site, and change the classes without touching the HTML.
4
+ tags: [guide, theme]
5
+
6
+ jsonld:
7
+ type: TechArticle
8
+ breadcrumbs: true
9
+ ---
10
+
11
+ # Themes
12
+
13
+ ## Two stylings
14
+
15
+ ```js
16
+ theme: {
17
+ framework: 'tailwind',
18
+ darkMode: 'class',
19
+ },
20
+ ```
21
+
22
+ `tailwind` compiles on demand only the classes actually used in the produced
23
+ pages. `custom` does without it entirely: a stylesheet written in the package
24
+ styles the site, with no styling dependency.
25
+
26
+ The templates are the same in both cases. Switching values requires touching
27
+ no page.
28
+
29
+ ## The principle: slots, not classes
30
+
31
+ The templates **hard-code no class**. They ask the theme for the class of each
32
+ slot — the header, the menu, a navigation link — and the theme answers.
33
+
34
+ The twenty-four slots are listed in the [reference](../reference/theme/).
35
+
36
+ That is what lets a utility styling and a classic styling share the same HTML:
37
+ one answers `dp-nav-link`, the other a handful of utilities. The template
38
+ itself does not change.
39
+
40
+ Components follow the same rule. When the theme does not answer, they fall
41
+ back on a `dp-*` class that their stylesheet styles from the `--dp-*` tokens:
42
+ they therefore follow the active palette without knowing anything about it.
43
+
44
+ ## Changing the colours
45
+
46
+ The fifteen tokens are listed in the [reference](../reference/theme/). They are
47
+ redefined from the configuration:
48
+
49
+ ```js
50
+ theme: {
51
+ framework: 'tailwind',
52
+ tokens: {
53
+ '--dp-accent': 'oklch(55% 0.2 250)',
54
+ '--dp-radius': '0.75rem',
55
+ },
56
+ },
57
+ ```
58
+
59
+ | Token | What it sets |
60
+ | -------------------------------------- | -------------------------------------------------------------- |
61
+ | `--dp-bg`, `--dp-bg-soft` | Backgrounds |
62
+ | `--dp-text`, `--dp-text-soft` | Text |
63
+ | `--dp-border`, `--dp-rule` | Borders and rules |
64
+ | `--dp-accent`, `--dp-accent-soft` | Accent colour |
65
+ | `--dp-radius` | Corner rounding |
66
+ | `--dp-font`, `--dp-font-mono` | Font families |
67
+ | `--dp-content-width` | Reading width. `none` by default: the content fills its column |
68
+ | `--dp-sidebar-width`, `--dp-toc-width` | Side columns |
69
+
70
+ A redefined token propagates everywhere: components included, without any of
71
+ them having to know.
72
+
73
+ ## Adding CSS
74
+
75
+ ```js
76
+ theme: {
77
+ framework: 'tailwind',
78
+ css: '.dp-article h2 { letter-spacing: -0.01em; }',
79
+ },
80
+ ```
81
+
82
+ The content of `css` is appended to the produced stylesheet.
83
+
84
+ ## Layer order
85
+
86
+ Under the `tailwind` theme, the stylesheet declares its layers in this order:
87
+
88
+ ```css
89
+ @layer theme, base, components, utilities;
90
+ ```
91
+
92
+ Component rules live in `components`, **below** the utilities. A `className`
93
+ set at use therefore always wins, whatever the place of the rule in the file:
94
+
95
+ ```mdx
96
+ <Card className="border-0 shadow-none">…</Card>
97
+ ```
98
+
99
+ Without this layer, a component rule written after a utility would beat it at
100
+ equal specificity, and the author's `className` would be ignored without a
101
+ word.
102
+
103
+ The `custom` theme has no utilities: a `className` there designates your own
104
+ classes. Declare them in `theme.css` — outside any layer, they come before the
105
+ component rules.
106
+
107
+ ## The stylesheet is compiled last
108
+
109
+ A utility theme only emits the rules of the classes actually used: it
110
+ therefore needs the rendered pages before it can compile. The build first
111
+ writes every page, collecting the classes along the way, then compiles the
112
+ stylesheet.
113
+
114
+ That is also why the slot classes are available **without** waiting for that
115
+ compilation: the templates need them to be rendered. The two things are kept
116
+ apart for this reason alone.
@@ -0,0 +1,124 @@
1
+ ---
2
+ title: Deployment
3
+ description: Publish the site, and keep past versions online.
4
+ tags: [guide, deployment]
5
+
6
+ jsonld:
7
+ type: TechArticle
8
+ breadcrumbs: true
9
+ ---
10
+
11
+ # Deployment
12
+
13
+ ## The deployment prefix
14
+
15
+ It is the setting that breaks the most sites, and the only one you really have
16
+ to understand.
17
+
18
+ A site served at the root of a domain has nothing to set. A site served under a
19
+ sub-path — `https://example.com/my-project/` — must know it, otherwise every
20
+ internal link will point one notch too high.
21
+
22
+ ```js
23
+ siteUrl: 'https://example.com/my-project',
24
+ ```
25
+
26
+ The sub-path of `siteUrl` **is enough**: `baseUrl` is derived from it when it
27
+ is not set. Setting it only serves to depart from it.
28
+
29
+ ```js
30
+ siteUrl: 'https://example.com/my-project',
31
+ baseUrl: '/other-path/',
32
+ ```
33
+
34
+ ## Building for going live
35
+
36
+ ```bash
37
+ npx docpensieve build
38
+ ```
39
+
40
+ The output folder is self-contained: HTML files, one stylesheet per version,
41
+ the copied resources. No server rule is needed — the root is an HTML redirect,
42
+ and each page is a folder with its `index.html`.
43
+
44
+ ## Continuous integration
45
+
46
+ ```yaml
47
+ name: Deploy
48
+
49
+ on:
50
+ push:
51
+ branches: [main]
52
+
53
+ permissions:
54
+ contents: read
55
+ pages: write
56
+ id-token: write
57
+
58
+ jobs:
59
+ publish:
60
+ runs-on: ubuntu-latest
61
+ environment:
62
+ name: github-pages
63
+ url: ${{ steps.deployment.outputs.page_url }}
64
+ steps:
65
+ - uses: actions/checkout@v7
66
+ - uses: actions/setup-node@v7
67
+ with:
68
+ node-version: '22'
69
+
70
+ - run: npx docpensieve build
71
+
72
+ # A successful build says nothing of a dead link.
73
+ - run: npx docpensieve check
74
+
75
+ - uses: actions/configure-pages@v6
76
+ - uses: actions/upload-pages-artifact@v5
77
+ with:
78
+ path: dist
79
+ - id: deployment
80
+ uses: actions/deploy-pages@v5
81
+ ```
82
+
83
+ The pages source must be set to "GitHub Actions" in the repository settings:
84
+ otherwise the artifact is produced but never served.
85
+
86
+ A project created by `init` has no `package.json`: the recipe therefore calls
87
+ the tool through `npx`, which takes the latest published version. To pin it
88
+ from one build to the next, declare `docpensieve` in a `package.json` and add
89
+ `npm ci` before the build.
90
+
91
+ ## Reading back before publishing
92
+
93
+ A generated site can compile without error and contain dead links.
94
+
95
+ ```bash
96
+ npx docpensieve check
97
+ ```
98
+
99
+ The command exits with code 1 if any remain: placed after the build, it stops
100
+ the publication rather than put online a site whose links lead nowhere. It is
101
+ the only safeguard that looks at the result rather than at the sources.
102
+
103
+ ## A scheduled build
104
+
105
+ What depends on the moment is frozen at build time. A page that shows "the
106
+ offer ends tomorrow" will still say so in six months if the site has not been
107
+ rebuilt.
108
+
109
+ For those pages, a periodic build is enough:
110
+
111
+ ```yaml
112
+ on:
113
+ schedule:
114
+ - cron: '0 4 * * *'
115
+ ```
116
+
117
+ ## Keeping past versions
118
+
119
+ Already published versions do not need to be rebuilt: their output is the one
120
+ produced at the time, and nothing would guarantee that a rebuild gives the same
121
+ result years later.
122
+
123
+ The branch model answers this — each compiled version on its own orphan branch,
124
+ with its history. See [Versions](./versions/).
@@ -0,0 +1,38 @@
1
+ ---
2
+ title: Guide
3
+ description: From installation to deployment, in order.
4
+ tags: [guide]
5
+
6
+ jsonld:
7
+ type: TechArticle
8
+ breadcrumbs: true
9
+ ---
10
+
11
+ # Guide
12
+
13
+ These pages are read in order. Each one starts from what the previous one set
14
+ up.
15
+
16
+ 1. **[Installation](./installation/)** — what you need, and how to set up
17
+ a project.
18
+ 2. **[First site](./first-site/)** — build, serve, look at what was
19
+ produced.
20
+ 3. **[Writing pages](./writing-pages/)** — frontmatter, URLs, menu, links,
21
+ images.
22
+ 4. **[Versions](./versions/)** — several documentation versions, one
23
+ branch each.
24
+ 5. **[Themes](./themes/)** — style the site, change the classes without
25
+ touching the HTML.
26
+ 6. **[Deployment](./deployment/)** — publish, and keep past versions
27
+ online.
28
+
29
+ ## What to know first
30
+
31
+ The produced site is **entirely static**. Each page is a complete HTML file,
32
+ served as is, along with a single stylesheet. Nothing is computed on the
33
+ reader's side.
34
+
35
+ This has a consequence to keep in mind throughout: what depends on the moment —
36
+ a date, a countdown — is frozen at build time, not at reading time.
37
+ [TimeTimer](../components/time-timer/) says so explicitly, and a scheduled build
38
+ is enough to keep it right.