Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ body:
attributes:
label: DocSprout version
description: Output of `docsprout --version`.
placeholder: docsprout 1.3.1
placeholder: docsprout 1.3.2
validations:
required: true
- type: input
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
- uses: actions/setup-python@v7
with:
python-version: '3.11'
- run: python -m pip install "git+https://github.com/ikelaiah/docsprout.git@v1.3.1"
- run: python -m pip install "git+https://github.com/ikelaiah/docsprout.git@v1.3.2"
- run: docsprout check
- if: inputs.versioned
run: docsprout check-release
Expand Down
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
# Changelog

## 1.3.2

### Fixed

- Inline Markdown keeps generated elements out of image alternative-text
attributes, preventing attribute injection through code or math in image
descriptions. Formatting no longer changes link destinations.
- Failed single-site rendering preserves the previous output, and a failed
first build leaves no partial site. Output ownership is checked before
rendering and again before publication.
- Local preview rebuilds when a configured logo, banner or custom stylesheet
changes outside `docs/`, including after changing the configured path.
- Scrolling updates the current section without reopening an outline that the
reader closed.

### Documentation

- Add an editable SVG build diagram, diagram-authoring guidance and contributor
instructions for testing Markdown context boundaries and browser behaviour.

## 1.3.1

A diagnostics and release-validation patch. No configuration, route,
Expand Down
34 changes: 30 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ docsprout build --output build/docs-site
| `docsprout/config.py` | configuration discovery, validation and the legacy `dockit.json` filename | `test_config`, `test_compatibility_corpus` |
| `docsprout/build.py` | page rendering, routes, assets and the search index | `test_build`, `test_route_collisions` |
| `docsprout/markdown.py` | safe Markdown subset and heading slugs | `test_markdown` |
| `docsprout/layout_markdown.py` | Markdown navigation outlines and line-specific diagnostics | `test_layout_markdown` |
| `docsprout/palette.py` | derived colours and contrast checks | `test_palette` |
| `docsprout/highlight.py` | offline syntax highlighting | `test_markdown` |
| `docsprout/audit.py` | read-only publication diagnostics | `test_audit` |
| `docsprout/github_pages.py` | managed Pages workflow rendering and inspection | `test_github_pages`, `test_github_pages_cli` |
Expand All @@ -52,10 +54,34 @@ and `tests/qualification_artifacts.py` verify packaged installs and archives.
Preview documentation changes with `docsprout serve`, and follow
[Troubleshooting](docs/troubleshooting.md) when a command reports an error.

## Make a small, verifiable improvement

For a renderer change, start with a failing example and run the focused tests:

```powershell
python -m unittest tests.test_markdown tests.test_inline_contexts -v
```

Keep raw Markdown, generated HTML and HTML attributes separate. Add cases for
quotes, code, math, image descriptions and link targets when changing inline
syntax. The [rendering boundary](docs/architecture.md#keep-rendering-contexts-separate)
explains why those combinations matter.

For a browser change, build `examples/visual-fixtures` and follow the
[visual inspection matrix](docs/visual-fixtures.md). Exercise a phone width,
keyboard navigation, light and dark modes, and the relevant empty or error
state. Generated-string assertions cannot prove that scrolling, focus or
layout works in a browser. Record what you actually observed, including any
tools or platforms you could not check.

Finish with the complete tests and lint, then the documentation checks above
when content or generated output changes. Explain the reader-visible result
and its verification in the change description.

Before a release, build and inspect both wheel and sdist, qualify each from a
fresh environment outside the checkout, run the historical `check-release` and
`build-all` rehearsal, and follow [the pre-publish checklist](docs/pre-publish-checklist.md).
Release branches are named `release/vX.Y.Z`; tags are annotated and are never
moved after publication. The maintainer opens the release PR against `main`,
waits for all required CI, merges only a green candidate, then tags the merge
commit and publishes the GitHub Release.
Release branches are named `dev/vX.Y.Z`. Tags are annotated and never moved
after publication. The maintainer opens the release PR against `main`, waits
for all required CI, merges only a green candidate, then tags the merge commit
and publishes the GitHub Release.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 🌱 DocSprout

[![CI](https://github.com/ikelaiah/docsprout/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/ikelaiah/docsprout/actions/workflows/ci.yml?query=branch%3Amain) [![Docs](https://img.shields.io/website?url=https%3A%2F%2Fikelaiah.github.io%2Fdocsprout%2F1.3.1%2F&label=docs)](https://ikelaiah.github.io/docsprout/1.3.1/) [![Latest release](https://img.shields.io/github/v/release/ikelaiah/docsprout?display_name=tag&sort=semver)](https://github.com/ikelaiah/docsprout/releases/latest) [![Python 3.10–3.14](https://img.shields.io/badge/python-3.10%E2%80%933.14-3776AB?logo=python&logoColor=white)](https://github.com/ikelaiah/docsprout/blob/main/docs/qualification.md#supported-python-versions) [![License: MIT](https://img.shields.io/github/license/ikelaiah/docsprout)](https://github.com/ikelaiah/docsprout/blob/main/LICENSE)
[![CI](https://github.com/ikelaiah/docsprout/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/ikelaiah/docsprout/actions/workflows/ci.yml?query=branch%3Amain) [![Docs](https://img.shields.io/website?url=https%3A%2F%2Fikelaiah.github.io%2Fdocsprout%2F1.3.2%2F&label=docs)](https://ikelaiah.github.io/docsprout/1.3.2/) [![Latest release](https://img.shields.io/github/v/release/ikelaiah/docsprout?display_name=tag&sort=semver)](https://github.com/ikelaiah/docsprout/releases/latest) [![Python 3.10–3.14](https://img.shields.io/badge/python-3.10%E2%80%933.14-3776AB?logo=python&logoColor=white)](https://github.com/ikelaiah/docsprout/blob/main/docs/qualification.md#supported-python-versions) [![License: MIT](https://img.shields.io/github/license/ikelaiah/docsprout)](https://github.com/ikelaiah/docsprout/blob/main/LICENSE)

![DocSprout banner — Build documentation people can use.](docs/assets/docsprout-banner.svg)

Expand All @@ -23,7 +23,7 @@ Open a terminal in your project's top-level folder—the folder that normally
contains files such as `README.md`, `src/` or `.git/`—and run:

```bash
python -m pip install "https://github.com/ikelaiah/docsprout/archive/refs/tags/v1.3.1.zip"
python -m pip install "https://github.com/ikelaiah/docsprout/archive/refs/tags/v1.3.2.zip"
docsprout init
docsprout serve
```
Expand Down
12 changes: 12 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,18 @@ dependency, or syntax change.
- [x] Add an existing-repository adoption guide and a CI/CD guide covering the
portable check/audit gates and static-host deployment.

## v1.3.2 — Rendering and build safety

- [x] Keep Markdown code, math, image descriptions and link destinations in
their correct output contexts, with regression coverage for composed
syntax.
- [x] Preserve the last working single-site build on render failure, and
rebuild the local preview when configured repository assets change.
- [x] Keep responsive outline tracking independent of the reader's disclosure
choice.
- [x] Improve the architecture and contributor guides with a readable build
diagram, Markdown safety notes and a concrete browser-check workflow.

## Quality bar

No release is complete unless it is easy to start, easy to understand, easy to
Expand Down
26 changes: 25 additions & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ identity; the installed package supplies the renderer, CSS and browser code.

## Follow one build

![Project files enter DocSprout for validation and rendering; a successful build replaces the static site.](assets/build-pipeline.svg)

The three stages are **project-owned inputs**, **DocSprout's build**, and
**a complete static site**. The image is a local SVG; the steps below provide
the same information in text and name the modules to explore.

1. `cli.py` chooses a command and project root. `check` calls the same builder
as `build`, but writes into a temporary directory.
2. `config.py` loads `docsprout.json` and the navigation layout, validates
Expand All @@ -19,6 +25,9 @@ identity; the installed package supplies the renderer, CSS and browser code.
5. `build.py` writes each page in the shared HTML shell, then writes the search
index and release metadata. `assets.py` supplies the site CSS and JavaScript;
the package also carries local KaTeX assets.
6. `safety.py` replaces the owned output directory only after the single-site
build succeeds. A failed render leaves the previous site available. Staging
beside the destination keeps directory renames on the same filesystem.

`serve` rebuilds that same site when source files change. `audit.py` adds
publication diagnostics, including warnings about image descriptions and
Expand All @@ -32,6 +41,21 @@ repository's `CONTRIBUTING.md` code map. Run the focused test while editing,
then the full suite. Changes to routes, configuration, generated formats or
command behavior also need a review of the contract below.

## Keep rendering contexts separate

The inline renderer treats prose, image descriptions and URL destinations as
different contexts. Code and math become elements in prose, plain text in an
image's alternative description, and exact source characters in a URL.
Completed elements are protected from later formatting passes. This prevents
emphasis from changing a destination and keeps generated markup out of HTML
attributes.

When extending Markdown, start with `tests/test_inline_contexts.py` and
`tests/test_markdown.py`. Exercise the new syntax inside a link label, an image
description, code and math, as well as ordinary prose. Parse the resulting HTML
to check element and attribute boundaries; a string containing `"` alone
does not prove that the final element is safe.

## Compatibility surface

The public compatibility contract consists of:
Expand Down Expand Up @@ -77,6 +101,6 @@ needs no custom CSS; the custom stylesheet mechanism is a deliberately bounded
escape hatch whose inclusion mechanics DocSprout owns and whose accessibility
the author owns.

Downstream projects should pin released tags such as `v1.3.1`, never the main
Downstream projects should pin released tags such as `v1.3.2`, never the main
branch. The stable boundary and deprecation policy are in [Machine-readable
contracts](machine-contracts.md).
40 changes: 40 additions & 0 deletions docs/assets/build-pipeline.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions docs/beginners-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ that normally contains `README.md` or `src/`.
Open a terminal in your project folder and run:

```bash
python -m pip install "https://github.com/ikelaiah/docsprout/archive/refs/tags/v1.3.1.zip"
python -m pip install "https://github.com/ikelaiah/docsprout/archive/refs/tags/v1.3.2.zip"
```

Then check that the command is available:
Expand Down Expand Up @@ -254,7 +254,7 @@ appear in the navigation.
in [How DocSprout renders Markdown](markdown-showcase.md).
- Learn the three configuration files in [Configuration](configuration.md).
- Copy a small working project from the
[minimal example](https://github.com/ikelaiah/docsprout/tree/v1.3.1/examples/minimal).
[minimal example](https://github.com/ikelaiah/docsprout/tree/v1.3.2/examples/minimal).
- When you truly want a public site, choose the simpler or historical path in
[GitHub Pages](github-pages.md).
- Look up unfamiliar words in the [glossary](glossary.md).
15 changes: 14 additions & 1 deletion docs/building.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ The build validates the configuration first, then writes the complete site to
`build/docs-site` and prints the page count. The output is offline-ready: every
asset is local, and `index.html` opens directly in a browser.

Single-site builds prepare their files beside the output folder and replace it
only after rendering succeeds. If a link, configuration or rendering error
stops a rebuild, the previous site stays available. A first build that fails
does not leave a partial site. This also keeps a working `serve` preview
readable while you fix the source.

The preview watches `README.md`, `docs/`, and the configured logo, banner and
custom stylesheet even when those assets live elsewhere in the repository.

| Option | Effect |
| --- | --- |
| `--output <folder>` | Write somewhere else, such as `dist/site`. DocSprout marks generated folders with `.docsprout-site` and refuses to replace a folder it does not own. |
Expand All @@ -24,7 +33,7 @@ asset is local, and `index.html` opens directly in a browser.
## Ship a ZIP

```bash
docsprout build --release 1.3.1 --offline-archive dist/docsprout-docs-1.3.1.zip
docsprout build --release 1.3.2 --offline-archive dist/docsprout-docs-1.3.2.zip
```

The release label used for an offline archive must start with a letter or
Expand All @@ -41,6 +50,10 @@ documentation and every declared historical release into one site, each under
its own path. The [historical documentation guide](historical-docs.md) covers
the release order, and `docsprout check-release` validates the manifest first.

Unlike a single-site build, `build-all` currently replaces its output before
building releases. Rehearse historical builds in a separate output folder;
keep the previous published artifact until the entire command succeeds.

## Start with `doctor`

`docsprout doctor` inspects the project without changing files. Its output is a
Expand Down
6 changes: 3 additions & 3 deletions docs/ci-cd.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
with:
python-version: "3.12"
- name: Install DocSprout
run: python -m pip install "https://github.com/ikelaiah/docsprout/archive/refs/tags/v1.3.1.zip"
run: python -m pip install "https://github.com/ikelaiah/docsprout/archive/refs/tags/v1.3.2.zip"
- name: Validate the site
run: docsprout check
- name: Audit publications
Expand All @@ -52,7 +52,7 @@ jobs:
path: audit.json
```

Pin a released tag (`v1.3.1` above), never a branch. `--format json` writes the
Pin a released tag (`v1.3.2` above), never a branch. `--format json` writes the
machine-readable report; the artifact keeps it after the job, including when
the audit fails.

Expand All @@ -76,7 +76,7 @@ is yours. The same checks in GitLab CI:
docs-checks:
image: python:3.12
script:
- python -m pip install "https://github.com/ikelaiah/docsprout/archive/refs/tags/v1.3.1.zip"
- python -m pip install "https://github.com/ikelaiah/docsprout/archive/refs/tags/v1.3.2.zip"
- docsprout check
- docsprout audit --strict
```
Expand Down
4 changes: 2 additions & 2 deletions docs/customisation.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ identity asset:

Fields that are not relevant here, such as `banner` and `homepage`, are
omitted for brevity. You can inspect the complete maintained
[`docs/docsprout.json`](https://github.com/ikelaiah/docsprout/blob/v1.3.1/docs/docsprout.json)
and its [local SVG asset](https://github.com/ikelaiah/docsprout/blob/v1.3.1/docs/assets/docsprout-mark.svg).
[`docs/docsprout.json`](https://github.com/ikelaiah/docsprout/blob/v1.3.2/docs/docsprout.json)
and its [local SVG asset](https://github.com/ikelaiah/docsprout/blob/v1.3.2/docs/assets/docsprout-mark.svg).
On generated DocSprout pages, purple is the accent and highlight colour; the
potted sprout mark (green leaves, brown soil and a beige pot) appears immediately
before the project name in the top header;
Expand Down
2 changes: 1 addition & 1 deletion docs/existing-repository.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Run both commands from the repository's top-level folder—the one that contains
`README.md` or `.git/`:

```bash
python -m pip install "https://github.com/ikelaiah/docsprout/archive/refs/tags/v1.3.1.zip"
python -m pip install "https://github.com/ikelaiah/docsprout/archive/refs/tags/v1.3.2.zip"
docsprout init
```

Expand Down
10 changes: 5 additions & 5 deletions docs/github-pages.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ from feature branches do not deploy Pages.
workflow are current, it makes no repository changes.

The generated workflow carries a DocSprout ownership marker and pins the reusable
workflow to the installed release, such as `@v1.3.1`. It never follows
workflow to the installed release, such as `@v1.3.2`. It never follows
`@main`. If a recognised managed workflow is older, ordinary setup reports the
version and leaves it unchanged. Update only that workflow deliberately:

Expand Down Expand Up @@ -101,13 +101,13 @@ jobs:
contents: read
pages: write
id-token: write
uses: ikelaiah/docsprout/.github/workflows/publish-docs.yml@v1.3.1
uses: ikelaiah/docsprout/.github/workflows/publish-docs.yml@v1.3.2
with:
versioned: false
release: latest
```

The maintained [single-version example](https://github.com/ikelaiah/docsprout/tree/v1.3.1/examples/single-version)
The maintained [single-version example](https://github.com/ikelaiah/docsprout/tree/v1.3.2/examples/single-version)
uses this form.

## Historical site for versioned projects
Expand All @@ -129,7 +129,7 @@ jobs:
contents: read
pages: write
id-token: write
uses: ikelaiah/docsprout/.github/workflows/publish-docs.yml@v1.3.1
uses: ikelaiah/docsprout/.github/workflows/publish-docs.yml@v1.3.2
```

Check a historical release locally after creating its tag and before pushing:
Expand All @@ -140,7 +140,7 @@ docsprout check-release
docsprout build-all --output build/docs-site
```

The maintained [historical example](https://github.com/ikelaiah/docsprout/tree/v1.3.1/examples/historical)
The maintained [historical example](https://github.com/ikelaiah/docsprout/tree/v1.3.2/examples/historical)
uses this form. Follow the [pre-publish checklist](pre-publish-checklist.md)
for the exact release order.

Expand Down
Loading
Loading