Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e3cbf6d
feat(site) Publish durable Sphinx inventories
tony Oct 18, 2026
ae86291
fix(site) Preserve distinct signature targets
tony Oct 18, 2026
5df50f2
perf(site) Avoid inventory AST conversion
tony Oct 18, 2026
9b3ed8a
fix(site) Preserve retired table row fragments
tony Oct 18, 2026
b802404
test(site) Reset tooltip focus between viewports
tony Oct 18, 2026
f5b29bc
fix(site) Keep shared inventory pages at the root
tony Oct 18, 2026
db78ce7
fix(site) Preserve ordinary HTML inventory paths
tony Oct 18, 2026
0e3f89b
fix(api-model) Keep inventory records without captions
tony Oct 18, 2026
9b2c045
fix(site) Export owned documents without main regions
tony Oct 18, 2026
f7e0dba
fix(site) Distinguish native citation controls
tony Oct 18, 2026
48f8e99
chore(scripts) Manage Python tooling with uv
tony Oct 18, 2026
5bdeaf4
fix(scripts) Declare the uv package cooldown
tony Oct 18, 2026
d93cd87
fix(site) Limit port inventories to their mounted tree
tony Oct 18, 2026
712a1fe
fix(site) Place inventory help under meta
tony Oct 18, 2026
4b44079
fix(scripts) Mark Python CLI checks executable
tony Oct 18, 2026
bd201c7
test(site) Keep kind marks out of inventory names
tony Oct 18, 2026
193fa5b
fix(inventory) Resolve options in program contexts
tony Oct 19, 2026
6ff0ebf
fix(site) Compose compatibility from published APIs
tony Oct 19, 2026
3d064ee
test(site) Read scoped footer source tags
tony Oct 19, 2026
38fddf1
feat(site) Extend Sphinx inventories to tmux
tony Oct 20, 2026
1aec93f
fix(site) Separate meta and tmux sidebars
tony Oct 20, 2026
c6101e4
docs(go) Pin the runnable pane I/O example
tony Oct 20, 2026
32526a2
test(site) Use retained excerpt cache fixtures
tony Oct 21, 2026
5f95425
docs(site) Explain inventory rename migrations
tony Oct 22, 2026
d0fffbb
docs(site) Demonstrate tmux inventory links
tony Oct 22, 2026
7dbaf12
docs(site) Add documentation export overview
tony Oct 22, 2026
d92742c
merge(main) Merge main into sphinx-inventories
tony Oct 25, 2026
c66f735
test(site[footer]) Expect inventory links on the compact page-source …
tony Oct 25, 2026
d142a62
style(scripts[elixir]) Format the Elixir toolchain reader for Ruff
tony Oct 25, 2026
59884ad
fix(api-model[resolver]) Leave Sphinx inventory file names plain
tony Oct 25, 2026
a59c230
chore(site[mentions]) Regenerate the mention index for inventory file…
tony Oct 25, 2026
a166b50
fix(site[inventories]) Link only inventories a preview build publishes
tony Oct 26, 2026
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
19 changes: 17 additions & 2 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,17 @@ Install dependencies from the repository root:

```console
$ pnpm install
$ uv sync --locked
```

Python tooling lives in `pyproject.toml` and `uv.lock`. The `dev` group
includes `lint` and `testing`; `.python-version` selects the development
interpreter. Keep dependency changes in the manifest and regenerate the
lockfile with `uv lock`. The project applies a three-day package cooldown
so local and CI resolution use the same policy.
The `docs` group supplies the C++ native Sphinx renderer; the Python port
keeps its own source-bound uv environment.

Start the Astro development server:

```console
Expand All @@ -41,6 +50,11 @@ rules. Both are pinned centrally in the pnpm catalog. Package lint scripts
use `--type-aware` with their own `tsconfig.json`; the root command also runs
Oxlint over `scripts/`. Preserve each script's scope and exclusions.

The root lint command also runs Ruff's lint and format checks on Python
tooling under `scripts/`. Run `uv run --locked ruff format .` to format
those files; generated examples and documentation snippets keep their own
verification rules.

Run lint across the workspace and build scripts:

```console
Expand All @@ -63,8 +77,9 @@ $ pnpm run type-check
```

This runs `astro check` for the site and Oxlint's type-check mode for the
plain TypeScript packages. The theme also exposes `type-check:tsc` for a
direct compiler check; it is separate from the root gate.
plain TypeScript packages, plus `uv run --locked ty check` for Python
tooling. Ty treats warnings as failures. The theme also exposes
`type-check:tsc` for a direct compiler check; it is separate from the root gate.

Use the inner loop for cross-port mappings, native navigation, and native
asset normalization:
Expand Down
19 changes: 19 additions & 0 deletions .github/workflows/deploy-shell.yml
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,10 @@ jobs:
cp scripts/publish-default-versions.sh publish-default-versions.sh
cp scripts/merge-version-manifests.mjs merge-version-manifests.mjs
cp scripts/skip-unchanged.mjs skip-unchanged.mjs
cp scripts/compose-published-inventory.mjs compose-published-inventory.mjs
mkdir inventory-runtime
cp packages/api-model/src/{inventory,targets,modules}.ts inventory-runtime/
echo '{"type":"module"}' > inventory-runtime/package.json
cat reserved-prefixes.txt

- uses: actions/upload-artifact@v7
Expand All @@ -225,6 +229,8 @@ jobs:
publish-default-versions.sh
merge-version-manifests.mjs
skip-unchanged.mjs
compose-published-inventory.mjs
inventory-runtime/
retention-days: 1

check-publish:
Expand Down Expand Up @@ -331,6 +337,19 @@ jobs:
| sed 's/\.json$//' | sort | sed 's/^/- /'
} >> "$GITHUB_STEP_SUMMARY"

- uses: actions/setup-node@v7
if: matrix.locale == 'en'
with:
node-version: '26'

- name: Compose inventories from the published default versions
if: matrix.locale == 'en'
env:
BUCKET: ${{ secrets.LIBTMUX_DOCS_BUCKET }}
run: |
node compose-published-inventory.mjs --directory dist \
--runtime inventory-runtime --bucket "$BUCKET" --locale en

# Without this every deploy re-uploads the whole locale, unchanged or
# not. PUBLISH_POLICY_SINCE is the date publish-root.sh last changed a
# header it sets: an object written before it is re-uploaded whatever
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ jobs:

- run: pnpm install --frozen-lockfile

- uses: astral-sh/setup-uv@v10.2.0
with:
enable-cache: true
cache-dependency-glob: uv.lock

- name: Install Python tooling
run: uv sync --locked

- name: Install browser for the rendering audit
run: pnpm --filter @libtmux/site exec playwright install --with-deps chromium

Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
node_modules/
.venv/
.ruff_cache/
__pycache__/
*.py[cod]
dist/
.astro/
_site/
Expand Down
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.14
128 changes: 128 additions & 0 deletions notes/sphinx-inventories.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Sphinx inventories

The assembly publishes `objects-full.inv` and `targets.json` at the default
locale root. Each built port/version publishes `objects.inv` and `targets.json`
at its own root. Existing reference inventories retain their mapping bases.
The [reader guide](../site/src/content/docs/meta/intersphinx.md) shows consumer setup.

## Target ownership

`BaseLayout` declares a page's scope, product, version, source, and logical
document identity. Every owned page exports a document; its rendered `main`
element supplies fragment targets when present.
It exports documents and actual fragment destinations, including headings,
tables, examples, parameter lists, and nested protocol schemas. Demo pages,
search, redirects, and translation placeholders have no exported ownership.
Native Sphinx pages receive the same metadata through `NativeShell`.
Copies of site-wide pages inside port builds remain owned by the shared tree
and are excluded from those copies' inventories.

Specialized renderers attach `data-inventory-id`, `data-inventory-category`,
and `data-inventory-display` to their existing anchor elements. Native aliases
use `data-inventory-native`, a JSON array of `{ type, name }` records. `type`
is an inventory object type: a C++ struct exports `cpp:class`, and a variable
exports `cpp:member`. An unknown language or protocol uses the portable label.
Repeated member presentations set `data-inventory-ignore`; their own pages
publish the canonical API targets.
Generated dialogs and other controls are excluded. API sections inherit their
declaration identity, so changing a page slug does not rename its examples or
parameter sections. Named C enumerators use file and spelling rather than
Doxygen's anonymous-enum hash. Unnamed declarations retain producer identities
and need explicit aliases when those identities change.
Signatures use native method identities when supplied, otherwise a hash of
normalized native syntax or structured parameter semantics. Native syntax
keeps qualifiers and companion declarations distinct even when shared fields
omit them. Layout whitespace does not change these keys.

Native citation links are navigation controls; the named citations remain
targets. When Docutils reuses their automatic ID for an empty heading alias,
the catalog uses the heading's unique section fragment. Duplicate content IDs
still fail publication. Native article bytes remain unchanged.

The record contract lives in `packages/api-model/src/targets.ts`. Logical
identity, display, final URL, version, and provenance are separate fields.
The serializer does not derive page slugs. Portable keys escape bytes outside
lowercase ASCII letters, digits, and `-._~`, preserving case through `std:ref`.
Native aliases include scope and product; shorter names are emitted only
when they have one destination.
Command options include both human spellings and Sphinx's
`normalized-program.option` names. Subcommand whitespace becomes a hyphen,
and aliases that become ambiguous under that normalization are omitted.

## Authored targets

Set `inventoryId` in document frontmatter to preserve a document's identity
when its source or route moves. Preserve explicit HTML heading IDs when their
wording changes. Automatically generated IDs remain dependent on that wording.

Tables can declare semantic types by their section ID:

```yaml
inventoryTables:
root-keys:
type: std:confval
identity: schema/session
prefix: session.
```

The row-anchor plugin exports each name in the first cell at the row's real
fragment. Supported types are `std:confval`, `std:cmdoption`, `std:envvar`, and
`std:term`. Type only tables that document those entities. Generic table rows
retain portable labels independently of this adapter.

## Migrations

Keep a retired portable key in `site/src/data/inventory-aliases.json`:

```json
[
{
"scope": "rs",
"from": "rs:core:heading:guide%23old-heading",
"to": "rs:core:heading:guide%23current-heading",
"fragment": "old-heading"
}
]
```

`from` resolves to the current destination. Optional `fragment` also preserves
the retired HTML ID on that page; conflicting IDs and missing destinations
fail assembly. Use content `aliases` for retired page routes. Document
`inventoryAliases` adds historical portable keys for the document itself.
Explicitly review removed identities rather than generating aliases from
new display text.

Compatibility exports preserve prior raw API names. The root compatibility
inventory also retains historical Python-type aliases from the old aggregate;
new inventories use the correct domains. Historical case-sensitive raw labels
remain limited by `std:ref` normalization. New consumers use portable keys.
Root per-port compatibility inventories use the selected default catalogs,
retain current API destinations, and omit identities absent from those catalogs.

## Verification

Port builds compare exported API identities with the selected model. Assembly
selects the manifest's default version for the full export and records excluded
ports in `targets.json`. Inventory generation fails on conflicting identities,
duplicate anchors, unsupported built-in types, and escaping mapping bases.

The publication audit uses `scripts/check-inventories.py` to compare catalogs
with Sphinx's inventory reader, check every emitted destination and decoded
fragment, and build strict reStructuredText and MyST consumers over HTTP.
Plain Sphinx loads only `sphinx.ext.intersphinx`; no custom domain is required.
Focused contract tests remain in the ordinary unit suites.

## Publication order

Root publication merges the successfully published port manifests before
composing its aggregate. `compose-published-inventory.mjs` uses the local
catalog for a shell-owned default tree. For independently published trees or
versions absent from the shell artifact, it reads the selected version's
published catalog from the bucket. The aggregate records each catalog's hash.
This prevents a locally built `latest` tree from replacing the inventory of
a published `stable` default.

Publish updated port artifacts, including their root `objects.inv` and
`targets.json`, before the first root publication. A missing catalog fails
composition before the footer or aggregate is uploaded. The root publisher
does not manufacture another version's destinations from the current model.
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
"preview": "pnpm --filter @libtmux/site preview",
"build:site": "./scripts/build-site.sh",
"clean": "bash scripts/clean-build.sh",
"type-check": "pnpm run --recursive type-check",
"lint": "pnpm run --recursive lint && oxlint scripts",
"type-check": "pnpm run --recursive type-check && pnpm run type-check:python",
"type-check:python": "uv run --locked ty check",
"lint": "pnpm run --recursive lint && oxlint scripts && pnpm run lint:python",
"lint:python": "uv run --locked ruff check . && uv run --locked ruff format --check .",
"test": "node scripts/test-loop.mjs outer",
"test:inner": "node scripts/test-loop.mjs inner",
"test:medium": "node scripts/test-loop.mjs medium",
Expand Down
3 changes: 2 additions & 1 deletion packages/api-model/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ export { resolvePublicIds } from './reexports.ts'
export { SymbolIndex, keywordArgument, parameterId, typeParameterId, type LinkTarget, type KeywordHelp, type TypeParameterBinding } from './link.ts'
export { boundLabel, parseTypeParameter, parseTypeParameters, typeParameterList, typeParametersOf, withTypeParameterDocs } from './type-params.ts'
export { tokenizeDoc, docSummaryText, referencesIn, type DocSpan } from './doc/roles.ts'
export { writeInventory, readInventory, isSymbolEntry, symbolEntries, NON_SYMBOL_ROLES, type InventoryEntry } from './inventory.ts'
export { writeInventory, writeInventoryEntries, readInventory, inventoryType, isSymbolEntry, symbolEntries, NON_SYMBOL_ROLES, type InventoryEntry } from './inventory.ts'
export { targetKey, targetEntries, compatibilityEntries, type InventoryTarget } from './targets.ts'
export { Resolver, notASymbol, toPath, DEFAULT_TEMPLATES, DEFAULT_PRIMARY_MODULES } from './resolver.ts'
export type { Resolution, UrlTemplate } from './resolver.ts'
export { qualifiedNameOf, moduleOf, modulesIn } from './modules.ts'
Expand Down
Loading
Loading