Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
03e449b
ci(format) Replace Biome with oxfmt
tony Oct 4, 2026
449d176
style(site) Format source with oxfmt
tony Oct 4, 2026
8959050
chore(git) Ignore the oxfmt reformat in blame
tony Oct 4, 2026
9c80916
ci(format) Gate the site source on oxfmt
tony Oct 4, 2026
543f5d5
docs(workspace[environment]) Wrap printf commands
tony Oct 4, 2026
0c8a3ca
docs(workspace[gallery]) Shorten two samples
tony Oct 4, 2026
c88464e
docs(workspace[installation]) Wrap build commands
tony Oct 4, 2026
de17368
docs(concepts[queries]) Fit the Java session query
tony Oct 4, 2026
318d573
docs(concepts[queries]) Wrap the C++ comment
tony Oct 4, 2026
6d9234e
docs(concepts[queries]) Break the Swift filter
tony Oct 4, 2026
efdbc19
docs(concepts[hierarchy]) Fit Java pane print
tony Oct 4, 2026
3aa081d
docs(concepts[transports]) Fit the Rust one-shot
tony Oct 4, 2026
e2c2227
docs(concepts[transports]) Fit the Go send call
tony Oct 4, 2026
8c5bb4b
docs(concepts[transports]) Fit the C# session call
tony Oct 4, 2026
a0ef364
docs(concepts[workspaces]) Fit snippets in 80
tony Oct 4, 2026
26fc2df
docs(topics[architecture]) Fit Go SendKeys in 80
tony Oct 4, 2026
cba3a66
docs(topics[context-managers]) Fit C# and Go lines
tony Oct 4, 2026
cbfdeb9
docs(topics[environment]) Fit Go and Swift setters
tony Oct 4, 2026
56e4878
docs(topics[errors]) Fit retry guards in 80
tony Oct 4, 2026
e4f5817
docs(topics[format-tokens]) Move trailing comments
tony Oct 4, 2026
21cf266
docs(topics[options-hooks]) Fit option, hook lines
tony Oct 4, 2026
17d92df
docs(topics[pane-interaction]) Move Swift comment
tony Oct 4, 2026
71f8f39
docs(topics[sockets]) Fit C# named server in 80
tony Oct 4, 2026
68f6dda
docs(topics[traversal]) Fit Go and C++ traversal
tony Oct 4, 2026
b960e83
docs(topics[waiting]) Fit Go and Rust wait lines
tony Oct 4, 2026
7938747
docs(workspace[internals]) Fit C++ snippets in 80
tony Oct 4, 2026
705be9f
docs(workspace[internals]) Fit C# snippets in 80
tony Oct 4, 2026
d44e9dd
docs(workspace[internals]) Fit Java snippets in 80
tony Oct 4, 2026
0f20d6f
docs(ci) Fit the tag-prefix comment in 80
tony Oct 4, 2026
4162b95
docs(upstream[cxx]) Fit the Doxygen probe in 80
tony Oct 4, 2026
8cb7c5e
docs(concepts[queries]) Fit F# queries in 80
tony Oct 4, 2026
0f7f898
docs(concepts[hierarchy]) Fit F# hierarchy in 80
tony Oct 4, 2026
8f41634
docs(concepts[transports]) Fit F# transports in 80
tony Oct 4, 2026
84cd149
docs(concepts[workspaces]) Fit F# workspaces in 80
tony Oct 4, 2026
c2ef67d
docs(examples[capture]) Fit F# capture in 80
tony Oct 4, 2026
6001b8a
docs(guides[attaching]) Fit the F# connect in 80
tony Oct 4, 2026
c7e7a23
docs(examples[capture]) Fit the C# platform guard
tony Oct 4, 2026
d979af5
docs(guides[attaching]) Fit the C# socket check
tony Oct 4, 2026
31af0b2
docs(examples[capture]) Fit Java capture in 80
tony Oct 4, 2026
f129c42
docs(guides[attaching]) Fit Java connect in 80
tony Oct 4, 2026
41668e8
docs(mcp[guides]) Fit the Java launcher recipe
tony Oct 4, 2026
0e7755c
docs(concepts[queries]) Fit Kotlin filters in 80
tony Oct 4, 2026
7eb3411
docs(concepts[hierarchy]) Fit Kotlin reread in 80
tony Oct 4, 2026
e79ef5b
docs(concepts[transports]) Fit Kotlin lines in 80
tony Oct 4, 2026
d7f7307
docs(concepts[workspaces]) Fit Kotlin lines in 80
tony Oct 4, 2026
0a648e2
docs(concepts[queries]) Fit Scala filters in 80
tony Oct 4, 2026
77b178b
docs(concepts[hierarchy]) Fit Scala lines in 80
tony Oct 4, 2026
ddf83b4
docs(concepts[transports]) Fit Scala lines in 80
tony Oct 4, 2026
d682236
docs(concepts[workspaces]) Fit Scala lines in 80
tony Oct 4, 2026
c4038a6
docs(examples[capture]) Fit Scala capture in 80
tony Oct 4, 2026
25aeedc
docs(guides[attaching]) Fit Scala connect in 80
tony Oct 4, 2026
749e344
docs(mcp[examples]) Fit the Java client in 80
tony Oct 4, 2026
0f7597a
docs(concepts[queries]) Fit Go programs in 80
tony Oct 4, 2026
54013e3
docs(concepts[hierarchy]) Fit Go programs in 80
tony Oct 4, 2026
9359172
docs(examples[capture]) Fit Go SendKeys in 80
tony Oct 4, 2026
609503e
docs(workspace[internals]) Fit the Go builder
tony Oct 4, 2026
f5ecdaa
docs(examples[capture]) Fit Rust new_session in 80
tony Oct 4, 2026
d3016ba
docs(workspace[internals]) Fit the Rust builder
tony Oct 4, 2026
9b9d5a0
docs(guides[capturing-output]) Fit script in 80
tony Oct 4, 2026
0678c4a
docs(concepts[queries]) Fit TS filters in 80
tony Oct 4, 2026
d88cbf5
docs(guides[attaching]) Fit TS socket guard
tony Oct 4, 2026
67e8797
docs(mcp[examples]) Fit the TS MCP example
tony Oct 4, 2026
8770a0b
docs(workspace[internals]) Fit the TS builder
tony Oct 4, 2026
6a9089c
docs(examples[capture]) Fit the C++ capture clock
tony Oct 4, 2026
007e54a
docs(guides[attaching]) Fit the C++ attach guard
tony Oct 4, 2026
90e63a1
docs(examples[capture]) Fit capture.lua in 80
tony Oct 4, 2026
150d537
docs(guides[attaching]) Fit connect.lua in 80
tony Oct 4, 2026
819bd64
docs(guides[attaching]) Fit connect.rb in 80
tony Oct 4, 2026
bef7412
docs(examples[capture]) Fit Capture.swift in 80
tony Oct 4, 2026
226ecf6
docs(guides[attaching]) Fit Connect.swift in 80
tony Oct 4, 2026
4330ac3
docs(mcp[examples]) Wrap the Swift fetch recipe
tony Oct 4, 2026
15cabae
docs(mcp[guides]) Wrap the Swift build recipe
tony Oct 4, 2026
216db78
docs(workspace[internals]) Fit the Swift builder
tony Oct 4, 2026
14b08cb
fix(fixtures[capture]) Build Rust from src/main.rs
tony Oct 4, 2026
c1312ce
docs(workspace[internals]) Wrap the Gradle line
tony Oct 4, 2026
9e0379a
docs(mcp[csharp]) Fit the session inspector in 80
tony Oct 10, 2026
319bf17
docs(mcp[csharp]) Fit the command runner in 80
tony Oct 10, 2026
4baf442
docs(mcp[go]) Fit the session inspector in 80
tony Oct 10, 2026
4536713
docs(mcp[go]) Fit the command runner in 80
tony Oct 10, 2026
1696593
docs(mcp[ruby]) Fit the async cleanup report
tony Oct 10, 2026
cf4f373
docs(mcp[rs]) Fit the session inspector in 80
tony Oct 10, 2026
e8bc69e
docs(concepts[rs]) Fit the query programs in 80
tony Oct 10, 2026
04f1f2e
docs(concepts[rs]) Fit the subprocess program
tony Oct 10, 2026
7f34aa6
docs(guides[rs]) Fit the batching program in 80
tony Oct 10, 2026
a47a2d1
docs(guides[rs]) Fit the control-mode program
tony Oct 10, 2026
f026a95
docs(guides[attaching]) Split the JVM run command
tony Oct 11, 2026
9109f7b
fix(site) Fit 80 code columns at laptop widths
tony Oct 4, 2026
cb5e1d0
docs(writing) Add the shared example style rules
tony Oct 4, 2026
6493770
ai(rules[AGENTS]) Route examples to WRITING.md
tony Oct 4, 2026
a09aa57
ci(examples) Check hand-written fences stay in 80
tony Oct 4, 2026
fbc5234
ci(examples) Compare shared rules across ports
tony Oct 4, 2026
df0d2ff
test(site[code-width]) Measure 80 columns
tony Oct 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 2 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Format source with oxfmt
449d17627fc405743130964a45b184b2fb6c6af0
49 changes: 49 additions & 0 deletions .github/example-width.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
width = 80
markdown = ["**/*.md"]
shared_digest = "28b74bae1f7a06731fea5a150cbde4430179b7788b7564ea1644c3951df8531b"
checker_digest = "eb87afceb3fc8f11ef5a478cd38f901ef3950e89ff94fbe0ca281c0cfc1a0e2e"
allow_ceiling = 8

[[exclude]]
glob = "notes/research/**"
reason = "dated research notes with pasted tool output and probes, kept as written; no reader copies code from them"

[[allow]]
path = "site/src/content/docs/ports/py/examples/capture-pane-output.md"
line = " --with 'libtmux @ git+https://github.com/tmux-python/libtmux@9fdd083a8181a827889337b63cd4e6daea661a8c' \\"
reason = "one quoted PEP 508 requirement pinning libtmux to a commit; the URL and revision cannot break"

[[allow]]
path = "site/src/content/docs/ports/py/guides/attaching-to-tmux.md"
line = " --with 'libtmux @ git+https://github.com/tmux-python/libtmux@9fdd083a8181a827889337b63cd4e6daea661a8c' \\"
reason = "one quoted PEP 508 requirement pinning libtmux to a commit; the URL and revision cannot break"

[[allow]]
path = "WRITING.md"
line = "Session session = await server.CreateSessionAsync(new NewSessionRequest(name: \"work\"));"
reason = "the Bad example in the port block; it shows the width problem the Good example fixes"

[[allow]]
path = "site/src/content/docs/ports/julia/examples/observe-output.md"
line = "with_server(; tmux=get(ENV, \"LIBTMUX_TEST_TMUX\", \"tmux\"), env=environment) do server"
reason = "a copy of libtmux-julia's examples/output_stream.jl at the revision site/test/fixtures/program-examples.json pins (6f62bd7), checked by sha256; libtmux-julia#7 fits the source, and this line follows when the pin moves"

[[allow]]
path = "site/src/content/docs/ports/julia/examples/owned-capture.md"
line = " println(\"Captured \", string(pane.id), \": \", ncodeunits(screen), \" screen bytes\")"
reason = "a copy of libtmux-julia's examples/owned_capture.jl at the revision site/test/fixtures/program-examples.json pins (6f62bd7), checked by sha256; libtmux-julia#7 fits the source, and this line follows when the pin moves"

[[allow]]
path = "site/src/content/docs/ports/julia/examples/shared-windows.md"
line = " only(windows(onlymatch(SessionWhere(name=\"ops\"), sessions(captured)))).ref"
reason = "a copy of libtmux-julia's examples/shared_windows.jl at the revision site/test/fixtures/program-examples.json pins (6f62bd7), checked by sha256; libtmux-julia#7 fits the source, and this line follows when the pin moves"

[[allow]]
path = "site/src/content/docs/ports/julia/examples/shared-windows.md"
line = " selected = filter(PaneWhere(active=true, window=WindowWhere(name=\"api\")), panes(snap))"
reason = "a copy of libtmux-julia's examples/shared_windows.jl at the revision site/test/fixtures/program-examples.json pins (6f62bd7), checked by sha256; libtmux-julia#7 fits the source, and this line follows when the pin moves"

[[allow]]
path = "site/src/content/docs/ports/julia/examples/shared-windows.md"
line = " only(Iterators.filter(p -> p.active && p.window.name == \"api\", panes(snap))).ref"
reason = "a copy of libtmux-julia's examples/shared_windows.jl at the revision site/test/fixtures/program-examples.json pins (6f62bd7), checked by sha256; libtmux-julia#7 fits the source, and this line follows when the pin moves"
36 changes: 36 additions & 0 deletions .github/workflows/shared-examples.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: shared-examples

# The example rules are one text and one checker in every libtmux port. This
# job compares them with each port's default branch, so it needs the network
# and stays out of the local test loops. It runs on a change to the shared
# files or this gate, and weekly; a port that drifts on its own surfaces in
# the weekly run instead of failing unrelated pull requests here.

on:
push:
branches: [main]
paths: &shared
- WRITING.md
- scripts/check_example_width.py
- scripts/check_shared_examples.py
- .github/workflows/shared-examples.yml
pull_request:
paths: *shared
schedule:
- cron: '17 6 * * 1'
workflow_dispatch:

permissions:
contents: read

jobs:
drift:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- run: python3 scripts/check_shared_examples.py --self-test
- run: python3 scripts/check_shared_examples.py
env:
GITHUB_TOKEN: ${{ github.token }}
21 changes: 21 additions & 0 deletions .oxfmtrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"semi": false,
"singleQuote": true,
"printWidth": 120,
"ignorePatterns": [
"pnpm-lock.yaml",
"site/src/data/**",
"site/public/**",
"_site/**",
"dist/**",
".port-sources/**",
"infra/**",
"**/*.json",
"**/*.yml",
"**/*.yaml",
"**/*.cff",
"**/*.css",
"**/*.md",
"**/*.mdx"
]
}
5 changes: 4 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ requested work.

- Environment, Oxlint, formatting, tests, and pull requests:
[CONTRIBUTING.md](CONTRIBUTING.md).
- Documentation, user-facing text, comments, and commits: [WRITING.md](WRITING.md).
- Documentation, user-facing text, comments, commits, and any code example
(example programs, console recipes, Markdown code blocks):
[WRITING.md](WRITING.md), and its [Documented examples that
run](WRITING.md#documented-examples-that-run) section for code a reader sees.

Each guide is the single home for its subject.

Expand Down
65 changes: 62 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,68 @@ Run lint across the workspace and build scripts:
$ pnpm run lint
```

The theme package retains Biome in its formatting script; it is not the
workspace lint gate. This command writes changes, so review its diff:
`oxfmt` formats the site's own TypeScript, JavaScript, and Astro source at
120 columns, with no semicolons and single quotes. It is pinned in the pnpm
catalog; `.oxfmtrc.json` lists what it skips. Markdown stays out because
`site/test/complete-examples.test.ts` pins fenced programs by hash. Check
formatting, or rewrite files in place and review the diff:

```console
$ pnpm --filter @libtmux/theme format
$ pnpm run format:check
```

```console
$ pnpm run format
```

## Checks

Hold hand-written code fences to 80 columns. The check reads
`.github/example-width.toml`, which lists the surfaces it covers and each
line allowed to stay wider, with its reason:

```console
$ python3 scripts/check_example_width.py
```

The `--self-test` flag plants a failure for every rule the check enforces,
plus clean controls.
The medium loop and the publication audit run both.

The checker and the shared section of `WRITING.md` must match every other
libtmux port. CI compares them with each port's default branch weekly, and
on pushes to `main` and pull requests that change them or the comparison
itself. The comparison needs the network, so it is not part of the local
loops:

```console
$ python3 scripts/check_shared_examples.py
```

A port with no checker or shared section fails, unless the script's
`PENDING` list names it with a reason and the date it was listed. Every
port is pending until its example-style change merges; a pending port that
already has both files fails until it leaves the list, and an entry older
than 30 days fails until it is merged or given a new reason and date.
To roll out a change to the shared text or checker, record the digests
the ports still carry in the script's `PREVIOUS` with the last day they
are accepted, so ports can merge one at a time.

A port the script cannot read, such as a private repository, goes in
`NOT_FETCHED` with the place it is compared instead, and the script
prints a notice for it; every port is public, so the list is empty. The
script also lists the public, non-archived repositories of the `libtmux`
organisation through the GitHub REST API, and fails for any repository
outside `PORTS` whose `WRITING.md` carries the shared section, so a new
port cannot adopt it unseen. Set `GITHUB_TOKEN` to avoid the
unauthenticated rate limit.

The script's `--self-test` runs without the network. It plants checker
and text drift, a missing file, a stale pending entry, an entry for an
unknown port, a pending port with drift or with both files, a previous
digest inside and past its window, a port it must not fetch, an unlisted
adopter, and an unlisted repository without the markers.

Run workspace type checks:

```console
Expand Down Expand Up @@ -118,6 +171,12 @@ Install the pinned Chromium build for local browser checks with
that build and its system dependencies before the publication audit.
The standard ten-second browser timeout increases to twenty seconds on CI.

`site/test/code-block-width.outer.test.ts` opens a built page at 1024 and
1280 pixels and checks that its code blocks fit 80 columns. It measures in
Chrome, because the bundled Chromium draws the code font narrower than the
browsers readers use. It reads the assembled `_site`, so only the
publication audit runs it; the loops skip it.

Keep the complete assembly, all output suites, link audits, source/model
freshness, and full browser matrix in the publication audit:

Expand Down
92 changes: 88 additions & 4 deletions WRITING.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,10 +167,92 @@ project and `main.cpp`, installs the library with its public testing component,
and builds against that installed prefix. The native source suite checks the
same files against private tmux servers, including operation failure and cleanup.

Put explanatory comments on separate lines above the code they describe.
Limit example comments to 100 columns, including indentation; prefer shorter
lines that fit the code panel. Put long source links and attribution in prose
outside the code block.
Put long source links and attribution in prose outside the code block.

<!-- shared:examples -->

An example is code written for a reader: a program under `examples/`, code in
a doc comment or docstring, and every fenced block in a README or docs page.
Shell blocks also follow [Code blocks](#code-blocks).

The text between the shared markers is the same in every libtmux port.
Change it in all of them together.

### Width

- **Examples stay within 80 columns.** They render in fixed-width boxes that
scroll sideways, and 80 columns fits a libtmux.org code block in a
laptop-width window. Comments inside examples wrap at 80 too.
- **The width check enforces it.** It reads the tracked files that
`.github/example-width.toml` names and fails on a wider line. It measures
the whole source line, so code in a doc comment counts its indent and
comment marker. It skips output (a fence tagged `text`, and what a
`console` block prints), hidden setup lines, and a line that is only a URL;
an untagged fence counts as code.
- **A line that must stay wider is listed there with its reason.** An entry
that no longer matches a line fails the check, so no stale entry stays.
- **The formatter's width is the hard limit for all other source.** Example
directories set their formatter to 80 where the formatter takes a width.

### Reaching 80

- **Change the code, not the line breaks.** A formatter rejoins any line that
fits its width. Name a sub-expression, use a short example name, hide setup
the reader does not need, or print less.
- **Break at the outermost level when a break is still needed:** after an
opening parenthesis with one argument per line, one call per line in a
chain, one field per line in a literal.
- **Put a comment on its own line above the code it explains.** Never trail
one after code in an example, unless the repository's example runner reads
it there, as with an assertion marker.
- **Break a long string at a word boundary,** never inside a tmux format
(`#{...}`) or an escape sequence; the joined text stays the same.
- **Continue a long command in a `console` block the way its shell does:**
`\` after a `$ ` prompt, a backtick after `PS> `, one flag per continuation
line.

### What never breaks

- **Output a test compares.** Wrapping it changes what the test expects.
- **A block copied from a source file.** Fix the width in the source, then
refresh the copy with the sync command the port block names, or by hand
where it names none; never fix the copy alone.
- **Marker lines and URLs,** which tools and readers take whole.

<!-- /shared:examples -->

### In this repository

- **Hard limit:** oxfmt (`.oxfmtrc.json`, `printWidth`) formats the site's
own source and has no example directory to hold to 80. The width check is
`python3 scripts/check_example_width.py`.
- **Not formatted:** Markdown, which oxfmt skips because
`site/test/complete-examples.test.ts` pins fenced programs by sha256. Hold
every fence to 80 by hand.
- **Runs, compiles, exempt:**
[Documented examples that run](#documented-examples-that-run) decides how
each kind of fence runs; every kind is held to the width, including blocks
quoted by hand and plain fences that do not run.
- **Compared output and copied blocks:** output a program's run compares
never breaks. A program or console recipe pinned in
`site/test/fixtures/*-examples.json` changes only together with its new
hash or `shellRecipe` and a new native run on both recorded tmux versions.
Fix a quoted block in its port source, never on the page, and refresh the
`file=` quotes with `node scripts/gen-example-sources.mjs` once the port's
pinned revision moves.

Bad, over 80:

```csharp
Session session = await server.CreateSessionAsync(new NewSessionRequest(name: "work"));
```

Good, the request named in its own statement:

```csharp
NewSessionRequest request = new(name: "work");
Session session = await server.CreateSessionAsync(request);
```

### Examples across ports

Expand Down Expand Up @@ -322,6 +404,8 @@ Reserve GitHub alert syntax for GitHub-only material and only when a warning
is necessary; the warning's text must stand alone. Put blank lines around
lists and headings, and preserve existing anchors when restructuring prose.

### Code blocks

Code blocks are paste-and-run units:

- Put one command in each block. An explicit `&&`, `;`, or `\` chain counts
Expand Down
3 changes: 2 additions & 1 deletion docs/ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,8 @@ jobs:
uses: libtmux/docs/.github/workflows/port-docs.yml@<sha>
with:
port: rs
tag-prefix: libtmux@ # stripped from a release tag; omit when tags are bare
# stripped from a release tag; omit when tags are bare
tag-prefix: libtmux@
source-ref: ${{ inputs.source-ref }}
version: ${{ inputs.version }}
version-kind: ${{ inputs.version-kind }}
Expand Down
4 changes: 3 additions & 1 deletion notes/upstream/libtmux-cxx-doxygen-comments.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ $ python3 -c "
import re, glob
tot = empty = 0
for f in glob.glob('xml/*.xml'):
for m in re.finditer(r'<briefdescription>(.*?)</briefdescription>', open(f).read(), re.S):
text = open(f).read()
pattern = r'<briefdescription>(.*?)</briefdescription>'
for m in re.finditer(pattern, text, re.S):
tot += 1
empty += not m.group(1).strip()
print(tot, empty)"
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
"preview": "pnpm --filter @libtmux/site preview",
"build:site": "./scripts/build-site.sh",
"type-check": "pnpm run --recursive type-check",
"format": "oxfmt --write .",
"format:check": "oxfmt --check .",
"lint": "pnpm run --recursive lint && oxlint scripts",
"test": "node scripts/test-loop.mjs outer",
"test:inner": "node scripts/test-loop.mjs inner",
Expand All @@ -21,11 +23,11 @@
},
"packageManager": "pnpm@12.7.0",
"devDependencies": {
"@biomejs/biome": "catalog:",
"entities": "catalog:",
"happy-dom": "catalog:",
"mdast-util-from-markdown": "2.0.3",
"mdast-util-to-markdown": "2.1.3",
"oxfmt": "catalog:",
"oxlint": "catalog:",
"typescript": "catalog:"
}
Expand Down
Loading
Loading