opdoc 0.1.0__tar.gz

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.
@@ -0,0 +1,7 @@
1
+ Copyright 2026 Meehai
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,4 @@
1
+ include README.md
2
+ include LICENSE.TXT
3
+ include opdoc/*.css
4
+ include opdoc/*.html.jinja2
opdoc-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,133 @@
1
+ Metadata-Version: 2.4
2
+ Name: opdoc
3
+ Version: 0.1.0
4
+ Summary: Opinionated, self-contained pdoc documentation sites
5
+ Home-page: https://gitlab.com/meehai/opdoc
6
+ License: MIT
7
+ Requires-Python: >=3.12
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE.TXT
10
+ Requires-Dist: pdoc==16.0.0
11
+ Provides-Extra: dev
12
+ Requires-Dist: pytest>=8.4; extra == "dev"
13
+ Dynamic: description
14
+ Dynamic: description-content-type
15
+ Dynamic: home-page
16
+ Dynamic: license
17
+ Dynamic: license-file
18
+ Dynamic: provides-extra
19
+ Dynamic: requires-dist
20
+ Dynamic: requires-python
21
+ Dynamic: summary
22
+
23
+ # opdoc
24
+
25
+ Opinionated, self-contained [pdoc](https://pdoc.dev/) documentation sites. One command turns a
26
+ project's `docs/` directory plus its `README.md` into a themed static site with prose pages, API
27
+ reference, client-side search, and no config file.
28
+
29
+ ```bash
30
+ opdoc docs/ public
31
+ ```
32
+
33
+ `DOCS` is required; `OUT` defaults to `<project-root>/public`. The project root is the parent of
34
+ `DOCS`, so `opdoc docs/` works from a normal checkout.
35
+
36
+ ## What a project supplies
37
+
38
+ ```
39
+ project/
40
+ ├── README.md home page (rendered as guide.html)
41
+ ├── docs/
42
+ │ ├── custom.css optional project CSS, appended after opdoc's base style
43
+ │ ├── source/**/*.md prose pages; the directory tree is the "Pages" sidebar
44
+ │ └── scripts/*.py optional build-time page generators
45
+ └── <packages>/ auto-detected API roots
46
+ ```
47
+
48
+ That is the whole contract. Theme, template, search, link rewriting, and the pdoc pin live in
49
+ opdoc.
50
+
51
+ ## API pages
52
+
53
+ Without flags, opdoc documents every top-level package directory (a directory with `__init__.py`),
54
+ skipping `docs/`, `test/`, `examples/`, `pkg/`, `build/`, `dist/`, and `public/`. Each package's
55
+ re-exports are blanked in memory by default, so a class is documented once on its own module page.
56
+
57
+ For a `src/` layout, namespace packages, or a subset of packages, pass `--api-package` and the
58
+ matching import roots:
59
+
60
+ ```bash
61
+ opdoc docs/ public \
62
+ --api-package src/robolib \
63
+ --api-package src/robosim \
64
+ --import-path src \
65
+ --import-path .
66
+ ```
67
+
68
+ `--keep-reexports` leaves `__all__` alone when a package landing page should show its re-exports.
69
+
70
+ ## Scripts
71
+
72
+ Every non-underscore `docs/scripts/*.py` is imported before `docs/source` is scanned. It must
73
+ define:
74
+
75
+ ```python
76
+ def generate(site):
77
+ text, _missing = build_the_page()
78
+ site.write_page("3. Reference/generated.md", text)
79
+ ```
80
+
81
+ `site.write_page(relative_path, text)` writes a markdown page below `docs/source`; the normal scan
82
+ then renders it to HTML. `site.root`, `site.docs`, and `site.out` are absolute paths. Scripts run
83
+ in filename order. `_`-prefixed files are helpers and are not run.
84
+
85
+ Scripts can import project code: `--import-path` entries are already on `sys.path` and in
86
+ `PYTHONPATH`. The scripts directory is on `sys.path` too, so a script may `import _helper` from a
87
+ sibling `_`-prefixed file.
88
+
89
+ ## Pages menu
90
+
91
+ `docs/source/` is the sidebar:
92
+
93
+ - every `*.md` is a page; the title is its first `# ` heading (fallback: file name);
94
+ - a directory is a collapsible group; `index.md` makes the group itself a page;
95
+ - a leading number sorts first and numerically (`1. Examples`, `2. Architecture`);
96
+ - `_`-prefixed files/dirs are drafts; a top-level `README.md` is ignored (the repo README is home);
97
+ - names that slugify to the same module are an error.
98
+
99
+ Links in `README.md` and pages stay repo-relative. opdoc rewrites `*.md` links to the generated
100
+ pages, copies referenced images, and sends other repo paths to GitLab blob/tree URLs. Set
101
+ `--repo-slug owner/project` and `--repo-branch branch` when the git remote does not describe the
102
+ published repo (for example, a vendored checkout).
103
+
104
+ ## Vendored sites
105
+
106
+ `--vendored NAME` adds `vendored/NAME/index.html` to the sidebar. It does **not** build that site;
107
+ an orchestrating project script builds each `pkg/NAME/docs` into `OUT/vendored/NAME/` with the same
108
+ tool. This keeps every site self-contained and avoids module collisions between projects.
109
+
110
+ ## Install
111
+
112
+ ```bash
113
+ pip install opdoc
114
+ ```
115
+
116
+ opdoc pins the pdoc version it is tested against (`pdoc==16.0.0`) because it drives pdoc's render
117
+ API directly. Python 3.12+.
118
+
119
+ For development:
120
+
121
+ ```bash
122
+ python -m venv .venv && source .venv/bin/activate
123
+ pip install -e ".[dev]"
124
+ python -m pytest test/
125
+ python -m opdoc docs/ public
126
+ ```
127
+
128
+ ## Tests
129
+
130
+ - `test/unit/` exercises the pure helpers (slugging, ordering, source scanning, script loading,
131
+ path guards).
132
+ - `test/integration/` builds a real fixture site with pdoc and checks the home page, API pages,
133
+ generated script page, sidebar, rewritten links, assets, and CSS.
opdoc-0.1.0/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # opdoc
2
+
3
+ Opinionated, self-contained [pdoc](https://pdoc.dev/) documentation sites. One command turns a
4
+ project's `docs/` directory plus its `README.md` into a themed static site with prose pages, API
5
+ reference, client-side search, and no config file.
6
+
7
+ ```bash
8
+ opdoc docs/ public
9
+ ```
10
+
11
+ `DOCS` is required; `OUT` defaults to `<project-root>/public`. The project root is the parent of
12
+ `DOCS`, so `opdoc docs/` works from a normal checkout.
13
+
14
+ ## What a project supplies
15
+
16
+ ```
17
+ project/
18
+ ├── README.md home page (rendered as guide.html)
19
+ ├── docs/
20
+ │ ├── custom.css optional project CSS, appended after opdoc's base style
21
+ │ ├── source/**/*.md prose pages; the directory tree is the "Pages" sidebar
22
+ │ └── scripts/*.py optional build-time page generators
23
+ └── <packages>/ auto-detected API roots
24
+ ```
25
+
26
+ That is the whole contract. Theme, template, search, link rewriting, and the pdoc pin live in
27
+ opdoc.
28
+
29
+ ## API pages
30
+
31
+ Without flags, opdoc documents every top-level package directory (a directory with `__init__.py`),
32
+ skipping `docs/`, `test/`, `examples/`, `pkg/`, `build/`, `dist/`, and `public/`. Each package's
33
+ re-exports are blanked in memory by default, so a class is documented once on its own module page.
34
+
35
+ For a `src/` layout, namespace packages, or a subset of packages, pass `--api-package` and the
36
+ matching import roots:
37
+
38
+ ```bash
39
+ opdoc docs/ public \
40
+ --api-package src/robolib \
41
+ --api-package src/robosim \
42
+ --import-path src \
43
+ --import-path .
44
+ ```
45
+
46
+ `--keep-reexports` leaves `__all__` alone when a package landing page should show its re-exports.
47
+
48
+ ## Scripts
49
+
50
+ Every non-underscore `docs/scripts/*.py` is imported before `docs/source` is scanned. It must
51
+ define:
52
+
53
+ ```python
54
+ def generate(site):
55
+ text, _missing = build_the_page()
56
+ site.write_page("3. Reference/generated.md", text)
57
+ ```
58
+
59
+ `site.write_page(relative_path, text)` writes a markdown page below `docs/source`; the normal scan
60
+ then renders it to HTML. `site.root`, `site.docs`, and `site.out` are absolute paths. Scripts run
61
+ in filename order. `_`-prefixed files are helpers and are not run.
62
+
63
+ Scripts can import project code: `--import-path` entries are already on `sys.path` and in
64
+ `PYTHONPATH`. The scripts directory is on `sys.path` too, so a script may `import _helper` from a
65
+ sibling `_`-prefixed file.
66
+
67
+ ## Pages menu
68
+
69
+ `docs/source/` is the sidebar:
70
+
71
+ - every `*.md` is a page; the title is its first `# ` heading (fallback: file name);
72
+ - a directory is a collapsible group; `index.md` makes the group itself a page;
73
+ - a leading number sorts first and numerically (`1. Examples`, `2. Architecture`);
74
+ - `_`-prefixed files/dirs are drafts; a top-level `README.md` is ignored (the repo README is home);
75
+ - names that slugify to the same module are an error.
76
+
77
+ Links in `README.md` and pages stay repo-relative. opdoc rewrites `*.md` links to the generated
78
+ pages, copies referenced images, and sends other repo paths to GitLab blob/tree URLs. Set
79
+ `--repo-slug owner/project` and `--repo-branch branch` when the git remote does not describe the
80
+ published repo (for example, a vendored checkout).
81
+
82
+ ## Vendored sites
83
+
84
+ `--vendored NAME` adds `vendored/NAME/index.html` to the sidebar. It does **not** build that site;
85
+ an orchestrating project script builds each `pkg/NAME/docs` into `OUT/vendored/NAME/` with the same
86
+ tool. This keeps every site self-contained and avoids module collisions between projects.
87
+
88
+ ## Install
89
+
90
+ ```bash
91
+ pip install opdoc
92
+ ```
93
+
94
+ opdoc pins the pdoc version it is tested against (`pdoc==16.0.0`) because it drives pdoc's render
95
+ API directly. Python 3.12+.
96
+
97
+ For development:
98
+
99
+ ```bash
100
+ python -m venv .venv && source .venv/bin/activate
101
+ pip install -e ".[dev]"
102
+ python -m pytest test/
103
+ python -m opdoc docs/ public
104
+ ```
105
+
106
+ ## Tests
107
+
108
+ - `test/unit/` exercises the pure helpers (slugging, ordering, source scanning, script loading,
109
+ path guards).
110
+ - `test/integration/` builds a real fixture site with pdoc and checks the home page, API pages,
111
+ generated script page, sidebar, rewritten links, assets, and CSS.
@@ -0,0 +1,4 @@
1
+ """opdoc -- opinionated pdoc docs generator."""
2
+ from opdoc.opdoc import Site, build_site, main
3
+
4
+ __all__ = ["Site", "build_site", "main"]
@@ -0,0 +1,4 @@
1
+ from opdoc.opdoc import main
2
+
3
+ if __name__ == "__main__":
4
+ raise SystemExit(main())
@@ -0,0 +1,391 @@
1
+ body {
2
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
3
+ }
4
+
5
+ .pdoc .il {
6
+ color: #131416;
7
+ }
8
+
9
+ nav.pdoc a.pdoc-button.module-list-button {
10
+ display: none;
11
+ }
12
+
13
+ .pdoc .modulename a:hover {
14
+ color: whitesmoke;
15
+ }
16
+
17
+ .pdoc .classattr {
18
+ color: #fff;
19
+ }
20
+
21
+ .pdoc .docstring .pdoc-code {
22
+ background-color: #232627;
23
+ }
24
+
25
+ .pdoc pre {
26
+ width: fit-content;
27
+ }
28
+
29
+ .pdoc pre a {
30
+ color: whitesmoke;
31
+ }
32
+
33
+ .pdoc pre a:hover {
34
+ color: #676767;
35
+ }
36
+
37
+
38
+ /* Submodules and API Doc. */
39
+
40
+ .pdoc h2 {
41
+ color: #fff;
42
+ font-weight: 0;
43
+ margin: 0.3em 0;
44
+ padding: 0.2em 0;
45
+ content: "Module Content";
46
+ }
47
+
48
+ /* header */
49
+ .pdoc h5 {
50
+ color: white;
51
+ font-style: italic;
52
+ }
53
+
54
+ /* Module name */
55
+
56
+ .pdoc .modulename {
57
+ color: #fff;
58
+ font-weight: 0;
59
+ }
60
+
61
+ .pdoc-code .ow {
62
+ color: var(--red);
63
+ }
64
+
65
+ nav.pdoc a.function {
66
+ color: var(--purple);
67
+ }
68
+
69
+ nav.pdoc a.function:hover {
70
+ background-color: var(--dark);
71
+ }
72
+
73
+ nav.pdoc .function::after {
74
+ content: "";
75
+ }
76
+
77
+ nav.pdoc .function::before {
78
+ content: "fn ";
79
+ color: var(--red);
80
+ }
81
+
82
+ nav.pdoc a.class {
83
+ color: whitesmoke;
84
+ }
85
+
86
+ /* Before the class name. This adds "class 'className'". */
87
+ nav.pdoc .class::before {
88
+ content: "class ";
89
+ color: var(--red);
90
+ }
91
+
92
+ /* After the class name. This will remove `()` That comes after the class name. */
93
+ nav.pdoc .class::after {
94
+ content: "";
95
+ }
96
+
97
+ nav.pdoc a.class:hover {
98
+ color: #fff;
99
+ background-color: var(--dark);
100
+ }
101
+
102
+ nav.pdoc a.variable {
103
+ color: whitesmoke;
104
+ }
105
+
106
+ /* Adds "var varname" to class variables. */
107
+ nav.pdoc a.variable::before {
108
+ content: "var ";
109
+ color: #9d9d9d;
110
+ color: var(--red);
111
+ }
112
+
113
+ nav.pdoc a.variable:hover {
114
+ color: #fff;
115
+ background-color: var(--dark);
116
+ }
117
+
118
+ /* nav modules */
119
+ nav.pdoc li a {
120
+ color: var(--purple);
121
+ }
122
+
123
+ nav.pdoc li a:hover {
124
+ background-color: var(--dark);
125
+ }
126
+
127
+ /* Assigned values */
128
+ .pdoc span.default_value {
129
+ color: var(--lightblue);
130
+ }
131
+
132
+ .pdoc span.def {
133
+ font-weight: normal;
134
+ color: var(--red);
135
+ }
136
+
137
+ /*
138
+ Attributes
139
+ ----------
140
+ */
141
+ .pdoc h6#attributes {
142
+ color: #fff;
143
+ font-size: 25px;
144
+ }
145
+
146
+ /*
147
+ Example
148
+ -------
149
+ */
150
+
151
+ .pdoc h6#example {
152
+ color: #fff;
153
+ font-size: 29px;
154
+ }
155
+
156
+ /*
157
+ Notes
158
+ -----
159
+ */
160
+
161
+ .pdoc h6#notes {
162
+ font-size: 27px;
163
+ color: var(--orange);
164
+ }
165
+
166
+ /*
167
+ Returns
168
+ -------
169
+ */
170
+
171
+ .pdoc h6#returns {
172
+ font-size: 27px;
173
+ color: white;
174
+ }
175
+
176
+ /*
177
+ Parameters
178
+ ----------
179
+ */
180
+
181
+ .pdoc h6#parameters {
182
+ font-size: 27px;
183
+ color: white;
184
+ }
185
+
186
+ /*
187
+ Raises
188
+ ------
189
+ */
190
+
191
+ .pdoc h6#raises {
192
+ font-size: 27px;
193
+ color: rgb(218, 55, 55);
194
+ }
195
+
196
+ /* False, True, None */
197
+
198
+ .pdoc .kc {
199
+ color: var(--lightblue);
200
+ }
201
+
202
+ .pdoc li {
203
+ color: white;
204
+ }
205
+
206
+ /* Decorator module */
207
+
208
+ .pdoc .nd {
209
+ color: var(--lightblue);
210
+ }
211
+
212
+ /* markdown **bold** -- genuinely bold + bright white so it stands out (e.g. the benchmark
213
+ win-markers on the Benchmarks page); was font-weight:normal, which made bold invisible */
214
+ .pdoc b,
215
+ strong {
216
+ color: #fff;
217
+ font-weight: 800;
218
+ }
219
+
220
+ /* markdown *emphasis* -- a plain italic that inherits the body color/size so it blends into the prose
221
+ instead of shouting. (Admonitions are styled separately, via .pdoc-alert-note / -warning below.) */
222
+
223
+ .pdoc em {
224
+ font-style: italic;
225
+ }
226
+
227
+ /* Decorator color */
228
+
229
+ .pdoc div.decorator {
230
+ color: var(--lightblue);
231
+ }
232
+
233
+ div.pdoc-code.codehilite {
234
+ background-color: var(--code);
235
+ }
236
+
237
+ /* class / function names */
238
+
239
+ .pdoc span.name {
240
+ color: var(--purple);
241
+ font-weight: normal;
242
+ }
243
+
244
+ /* Inherited class name color */
245
+
246
+ .pdoc span.base {
247
+ color: whitesmoke;
248
+ }
249
+
250
+ /* Before inherited members colors, i.e., "builtins." */
251
+
252
+ .pdoc .inherited dt,
253
+ .pdoc .inherited dt::before {
254
+ color: var(--silver);
255
+ }
256
+
257
+
258
+ /* Commas that separates parameters "," color */
259
+ .pdoc .inherited dd:not(:last-child)::after {
260
+ color: #fff;
261
+ }
262
+
263
+ /* Commas that separates parameters "," color */
264
+
265
+ .pdoc .inherited dd:not(:last-child)::after {
266
+ color: #fff;
267
+ }
268
+
269
+ /* Contents, Submodules, API Documentations */
270
+ /* This also can be separated */
271
+ .pdoc h1,
272
+ .pdoc h2,
273
+ .pdoc h3 {
274
+ font-weight: 300;
275
+ margin: 0.3em 0;
276
+ padding: 0.2em 0;
277
+ color: white;
278
+ }
279
+
280
+ /* Top left nav button */
281
+ nav.pdoc .module-list-button {
282
+ display: inline-flex;
283
+ align-items: center;
284
+ margin-bottom: 1rem;
285
+ color: white;
286
+ border-color: white;
287
+ }
288
+
289
+ nav.pdoc .module-list-button:hover {
290
+ border-color: white;
291
+ color: white;
292
+ }
293
+
294
+ .pdoc pdoc-alert pdoc-alert-warning .p {
295
+ color: black;
296
+ }
297
+
298
+ .pdoc-code {
299
+ color: white;
300
+ }
301
+
302
+ .pdoc .pdoc-alert-warning {
303
+ color: black;
304
+ background-color: #d5a142;
305
+ border-color: black;
306
+ }
307
+
308
+ .pdoc .pdoc-alert-note {
309
+ color: rgb(7, 19, 24);
310
+ background-color: rgb(184, 231, 251) ;
311
+ border-color: var(--dark);
312
+ }
313
+
314
+ /* The nav bar */
315
+
316
+ nav.pdoc {
317
+ background-color: rgb(55, 58, 72);
318
+ }
319
+
320
+ .pdoc-code .k {
321
+ color: var(--red);
322
+ }
323
+
324
+ /* === opdoc base overrides (per-project extras are appended from docs/custom.css) ========= */
325
+ /* widen the content column (layout.css ships 54rem) by 1.5x so 120-char-wide code
326
+ examples render on a single line, without horizontal scrolling */
327
+ main, header { width: calc(81rem + var(--sidebar-width)); }
328
+ /* a Home link at the top of the sidebar, above search; styled like the "Modules"/"Contents" headings */
329
+ nav.pdoc a.home-button { display: inline-block; color: #fff; font-weight: 300; font-size: 2rem; margin: 0.3em 0; }
330
+ nav.pdoc a.home-button:hover { color: var(--lightblue); }
331
+ /* README-heading primitives stay code in the body, but render as plain text in the sidebar menu */
332
+ nav.pdoc code { font-family: inherit; background: none; color: inherit; padding: 0; border: 0; font-size: inherit; }
333
+ /* prose/README images must never overflow the content column */
334
+ .pdoc .docstring img { max-width: 100%; height: auto; }
335
+ /* prose tables (e.g. the Benchmarks page): pdoc's default has no visible borders. Make them
336
+ full-width + "schematic" (crisp grid), with a header band and a subtle per-row hover shade. */
337
+ .pdoc .docstring table {
338
+ display: table; width: 100%; max-width: 100%; table-layout: auto; /* fully override pdoc's
339
+ block/max-content/overflow default so the table fills the content column instead of shrink-wrapping */
340
+ overflow: visible; border-collapse: collapse; margin: 1rem 0;
341
+ }
342
+ .pdoc .docstring th, .pdoc .docstring td {
343
+ border: 1px solid rgba(255, 255, 255, 0.22); padding: 0.4rem 0.6rem; text-align: left; vertical-align: top;
344
+ }
345
+ .pdoc .docstring thead th { background: var(--accent); font-weight: 600; }
346
+ .pdoc .docstring tbody tr:hover { background: rgba(255, 255, 255, 0.06); }
347
+ /* Collapsible sidebar trees (Modules + the grouped nodes under Pages). Nodes that have
348
+ children are <details><summary>label</summary><ul>kids</ul></details>: collapsed by default, so
349
+ only the top level (microecs) shows; opening a node reveals its children, to any depth.
350
+ The path to the CURRENT page is emitted <details open>, so you always see where you are. Both
351
+ trees carry class="nav-tree". */
352
+ nav.pdoc > div > ul.nav-tree { margin-left: 0; } /* cancel pdoc's negative root margin */
353
+ /* the disclosure row: hide the browser's default triangle, draw our own ▸ that rotates to ▾ on open */
354
+ nav.pdoc .nav-tree summary { list-style: none; cursor: pointer; padding: .2rem 0; } /* match the
355
+ .2rem vertical rhythm of leaf link rows (nav.pdoc li a) so collapsible group rows aren't cramped */
356
+ nav.pdoc .nav-tree summary::-webkit-details-marker { display: none; }
357
+ nav.pdoc .nav-tree summary::before {
358
+ content: "\25B8"; display: inline-block; width: 1rem; color: var(--silver); transition: transform 100ms;
359
+ }
360
+ nav.pdoc .nav-tree details[open] > summary::before { transform: rotate(90deg); }
361
+ /* a link/label inside a summary flows inline right after the marker (no block padding) */
362
+ nav.pdoc .nav-tree summary a, nav.pdoc .nav-tree summary .tree-label { display: inline; padding-left: 0; }
363
+ /* leaf rows have no marker -- pad them by the marker width so their text aligns with expandable siblings */
364
+ nav.pdoc .nav-tree li > a { padding-left: 1rem; }
365
+ /* namespace labels (Examples & Tutorials) are plain text, not links -- match the link color */
366
+ nav.pdoc .tree-label { color: var(--purple); }
367
+ /* indentation + rails: each nested <ul> indents once and draws a faint vertical rail spanning its
368
+ children; hovering a node lights its subtree's rail (and its ancestors') so the subtree stands out */
369
+ nav.pdoc .nav-tree ul { padding-left: 0.6rem; border-left: 1px solid rgba(255, 255, 255, 0.18); }
370
+ nav.pdoc .nav-tree details:hover > ul { border-left-color: var(--lightblue); }
371
+ /* indent the "Current Page" ToC one tab under its heading, matching the Pages/Modules trees (the H1
372
+ title is now in the heading, so its sub-sections would otherwise sit flush against the left edge) */
373
+ nav.pdoc > div > ul.page-toc { margin-left: 0; }
374
+ nav.pdoc .page-toc li a { padding-left: 1rem; }
375
+ /* Expandable prose code blocks: a long fenced example starts clamped to a few lines with a fade-out and
376
+ a "Show more" toggle, so a page isn't one wall of code. Short blocks stay fully open (the JS removes
377
+ the clamp when a block doesn't actually overflow). Scoped to .docstring, so the API "View Source"
378
+ dropdowns keep their own separate toggle. */
379
+ .pdoc .docstring .pdoc-code.collapsible { position: relative; }
380
+ .pdoc .docstring .pdoc-code.collapsible.collapsed { max-height: 12em; overflow: hidden; }
381
+ .pdoc .docstring .pdoc-code.collapsible.collapsed::after {
382
+ content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 3.5em; pointer-events: none;
383
+ background: linear-gradient(to bottom, rgba(35, 38, 39, 0), var(--code));
384
+ }
385
+ .pdoc .docstring .code-toggle {
386
+ display: block; width: fit-content; margin: 0.5rem 1em 1.25rem; padding: 0.25rem 0.7rem; cursor: pointer;
387
+ position: relative; z-index: 1; /* sit above the position:relative code block, never behind it */
388
+ font: inherit; color: var(--lightblue); background: var(--code);
389
+ border: 1px solid rgba(255, 255, 255, 0.22); border-radius: 4px;
390
+ }
391
+ .pdoc .docstring .code-toggle:hover { color: #fff; border-color: var(--lightblue); }