Skip to content

Commit fee1668

Browse files
authored
feat(terminal): add scroll-to-bottom button, write coalescer, and E2E test infra (#31)
Adds a visible scroll-to-bottom button when the user scrolls up in the terminal, a write coalescer that batches rapid output writes per animation frame for smoother rendering, and auto-scroll on user input. Also sets up Playwright E2E test infrastructure with a CI job across all OS targets. Fixes the npm test/coverage scripts to exclude e2e tests that run via Playwright separately. Closes #30
1 parent 5b8eb13 commit fee1668

10 files changed

Lines changed: 1290 additions & 13 deletions

File tree

.github/skills/publish/SKILL.md

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
---
2+
name: publish
3+
description: >
4+
Use this skill when the user wants to "publish", "release", "ship",
5+
"push a release", "deploy a new version", "create a PR", "open a PR",
6+
or "submit changes" for TermBeam.
7+
It orchestrates the full publish workflow: local tests, docs check,
8+
push to main (or PR flow), CI verification, and triggering the release workflow.
9+
---
10+
11+
# TermBeam Publish Workflow
12+
13+
You are executing the TermBeam publish workflow. Follow every step in order.
14+
Do NOT skip steps. If any step fails, stop and report the failure to the user.
15+
16+
## Step 1 — Run tests locally
17+
18+
```bash
19+
cd <repo-root>
20+
npm test
21+
```
22+
23+
All tests must pass. If any test fails, stop and report the failure.
24+
25+
## Step 2 — Lint check
26+
27+
```bash
28+
npm run lint
29+
```
30+
31+
Must exit cleanly. If it fails, stop and report.
32+
33+
## Step 3 — Run coverage check
34+
35+
```bash
36+
npm run test:coverage
37+
```
38+
39+
Coverage must meet the 80% threshold. If it drops below 80%, stop and report
40+
which files/areas lost coverage. The coverage summary is written to
41+
`coverage/coverage-summary.json` — you can inspect it for details.
42+
43+
## Step 4 — Check documentation is up to date
44+
45+
Review the staged/unstaged changes by running:
46+
47+
```bash
48+
git --no-pager diff HEAD
49+
```
50+
51+
Compare the changes against the documentation files. Check:
52+
53+
- **`README.md`** — if any CLI flags, features, or defaults changed, README must reflect them.
54+
- **`docs/configuration.md`** — if CLI flags or env vars changed.
55+
- **`docs/security.md`** — if auth, headers, or security behavior changed.
56+
- **`docs/api.md`** — if HTTP or WebSocket API changed.
57+
- **`docs/architecture.md`** — if system design or module responsibilities changed.
58+
- **`docs/getting-started.md`** — if installation or first-run steps changed.
59+
and any other docs files relevant to the changes.
60+
61+
If docs are outdated, update them before proceeding. Show the user what you updated.
62+
63+
If the changes are purely UI/cosmetic (e.g., CSS, HTML template changes in `public/`),
64+
docs updates are likely NOT needed — use your judgment.
65+
66+
## Step 5 — Commit and push
67+
68+
Stage all changes (including any doc updates from Step 4), commit with a
69+
conventional commit message:
70+
71+
```bash
72+
git add -A
73+
git commit -m "<type>(<scope>): <description>"
74+
```
75+
76+
Use the appropriate conventional commit type based on the changes:
77+
`feat`, `fix`, `docs`, `refactor`, `test`, `chore`, `perf`.
78+
If there are multiple types of changes, use the most significant one.
79+
80+
### Choosing the push strategy
81+
82+
- **Default (direct push):** If the user did NOT ask to create a PR, push
83+
directly to `main` and continue to Step 6.
84+
85+
```bash
86+
git push origin main
87+
```
88+
89+
- **PR flow:** If the user explicitly asked to create a PR:
90+
1. **Ensure an issue exists.** Every PR must reference an issue. If the
91+
user mentioned an issue number, use it. If not, create one:
92+
93+
```bash
94+
gh issue create --title "<short description>" --body "<brief context>"
95+
```
96+
97+
Note the issue number for the branch name and PR body.
98+
99+
2. **Create the branch** using the naming convention:
100+
101+
```
102+
<github-username>/<type>/<issue-number>/<short-kebab-description>
103+
```
104+
105+
- `<github-username>` — the repo owner's GitHub handle (get via
106+
`gh api user --jq .login` if unknown)
107+
- `<type>` — matches the commit type: `feature`, `fix`, `docs`,
108+
`refactor`, `test`, `chore`, `perf`
109+
- `<issue-number>` — the linked issue number
110+
- `<short-kebab-description>` — 2-4 word kebab-case summary
111+
112+
Example: `dorlu/feature/123/kill-pty`
113+
114+
```bash
115+
git checkout -b <branch-name>
116+
git push origin <branch-name>
117+
```
118+
119+
3. **Open the PR.** The description should be minimal, conversational,
120+
no emojis — just a plain explanation of what changed and why.
121+
122+
```bash
123+
gh pr create --base main --head <branch-name> \
124+
--title "<commit message>" \
125+
--body "<1-3 plain sentences describing what this PR changes. Reference the issue with Closes #N.>"
126+
```
127+
128+
4. **Watch CI on the PR.** If CI fails:
129+
- Fetch logs: `gh run view <run-id> --log-failed`
130+
- If the fix is straightforward, apply it automatically, push, and
131+
watch CI again.
132+
- If the failure requires major code changes, **stop and ask the user**:
133+
> CI failed due to X. Do you want me to attempt an automatic fix,
134+
> or would you like to review the issues and provide instructions?
135+
136+
5. Once CI passes, **ask the user for approval** before merging.
137+
Do NOT continue to the release steps until the user approves.
138+
139+
6. After approval, merge the PR:
140+
141+
```bash
142+
gh pr merge <pr-number> --squash --delete-branch
143+
```
144+
145+
Then continue to Step 6 (CI on `main`).
146+
147+
## Step 6 — Wait for CI to pass
148+
149+
Use the GitHub CLI to find the workflow runs triggered by the push and poll
150+
until they complete:
151+
152+
```bash
153+
# Get the latest CI run on main
154+
gh run list --workflow=ci.yml --branch=main --limit=1 --json databaseId,status,conclusion
155+
156+
# Watch it until it completes (timeout after 10 minutes)
157+
gh run watch <run-id> --exit-status
158+
```
159+
160+
If CI fails:
161+
162+
1. Fetch the failed job logs: `gh run view <run-id> --log-failed`
163+
2. Report the failure to the user with the relevant error output.
164+
3. Do NOT proceed to the release step.
165+
166+
## Step 7 — Wait for docs pages deployment (if applicable)
167+
168+
Only if docs files (`docs/**` or `mkdocs.yml`) were changed in the push:
169+
170+
```bash
171+
gh run list --workflow=pages.yml --branch=main --limit=1 --json databaseId,status,conclusion
172+
gh run watch <run-id> --exit-status
173+
```
174+
175+
If the docs deployment fails, report it but still allow the release to proceed
176+
(docs failures are non-blocking for npm releases).
177+
178+
## Step 8 — Determine version bump type
179+
180+
Check if the user already specified the bump type in their prompt
181+
(e.g., "publish patch", "release minor", "ship major").
182+
183+
If the bump type was already provided, use it directly — do NOT ask again.
184+
185+
If the bump type was NOT specified, ask the user:
186+
187+
> What type of version bump is this?
188+
189+
Provide choices: `patch`, `minor`, `major`
190+
191+
Explain briefly:
192+
193+
- **patch** — bug fixes, cosmetic changes, small improvements
194+
- **minor** — new features, non-breaking additions
195+
- **major** — breaking changes
196+
197+
Wait for the user's response before proceeding.
198+
199+
## Step 9 — Trigger the release workflow
200+
201+
Once the user has chosen the bump type, trigger the release:
202+
203+
```bash
204+
gh workflow run release.yml -f bump=<chosen-bump-type> -f dry-run=false
205+
```
206+
207+
Then watch the release workflow:
208+
209+
```bash
210+
# Wait a moment for the run to appear
211+
sleep 5
212+
gh run list --workflow=release.yml --limit=1 --json databaseId,status,conclusion
213+
gh run watch <run-id> --exit-status
214+
```
215+
216+
## Step 10 — Report success
217+
218+
Once the release workflow completes successfully, report to the user:
219+
220+
- The new version number (from the release workflow output or by checking
221+
the latest git tag: `git fetch --tags && git describe --tags --abbrev=0`)
222+
- Link to the GitHub release page
223+
- Link to the npm package page: https://www.npmjs.com/package/termbeam
224+
225+
If the release workflow fails, fetch logs and report the error.

.github/workflows/ci.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ jobs:
1515
name: Test (${{ matrix.os }}, Node ${{ matrix.node-version }})
1616
runs-on: ${{ matrix.os }}
1717
strategy:
18+
fail-fast: false
1819
matrix:
1920
os: [ubuntu-latest, windows-latest]
2021
node-version: [18, 20, 22]
@@ -90,3 +91,42 @@ jobs:
9091
cache: npm
9192
- run: npm ci
9293
- run: node --check src/*.js bin/*.js
94+
95+
e2e:
96+
name: E2E (${{ matrix.os }})
97+
runs-on: ${{ matrix.os }}
98+
strategy:
99+
fail-fast: false
100+
matrix:
101+
os: [ubuntu-latest, windows-latest, macos-latest]
102+
steps:
103+
- uses: actions/checkout@v6
104+
- uses: actions/setup-node@v6
105+
with:
106+
node-version: 22
107+
cache: npm
108+
- run: npm ci
109+
- name: Install Playwright (Linux)
110+
if: runner.os == 'Linux'
111+
run: npx playwright install --with-deps chromium
112+
- name: Install Playwright
113+
if: runner.os != 'Linux'
114+
run: npx playwright install chromium
115+
- name: Run E2E tests
116+
run: npx playwright test
117+
env:
118+
CI: true
119+
- name: Upload Playwright report
120+
if: failure()
121+
uses: actions/upload-artifact@v4
122+
with:
123+
name: playwright-report-${{ matrix.os }}
124+
path: playwright-report/
125+
retention-days: 7
126+
- name: Upload test traces
127+
if: failure()
128+
uses: actions/upload-artifact@v4
129+
with:
130+
name: playwright-traces-${{ matrix.os }}
131+
path: test-results/
132+
retention-days: 7

package-lock.json

Lines changed: 64 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
"scripts": {
1010
"start": "node bin/termbeam.js",
1111
"dev": "node bin/termbeam.js --generate-password",
12-
"test": "node -e \"require('child_process').execFileSync(process.execPath,['--test',...require('fs').readdirSync('test').filter(f=>f.endsWith('.test.js')).map(f=>'test/'+f)],{stdio:'inherit'})\"",
13-
"test:coverage": "c8 --exclude=src/tunnel.js --reporter=text --reporter=lcov --reporter=json-summary --reporter=json node -e \"require('child_process').execFileSync(process.execPath,['--test','--test-reporter=spec','--test-reporter-destination=stdout',...require('fs').readdirSync('test').filter(f=>f.endsWith('.test.js')).map(f=>'test/'+f)],{stdio:'inherit'})\"",
12+
"test": "node -e \"require('child_process').execFileSync(process.execPath,['--test',...require('fs').readdirSync('test').filter(f=>f.endsWith('.test.js')&&!f.startsWith('e2e-')).map(f=>'test/'+f)],{stdio:'inherit'})\"",
13+
"test:coverage": "c8 --exclude=src/tunnel.js --reporter=text --reporter=lcov --reporter=json-summary --reporter=json node -e \"require('child_process').execFileSync(process.execPath,['--test','--test-reporter=spec','--test-reporter-destination=stdout',...require('fs').readdirSync('test').filter(f=>f.endsWith('.test.js')&&!f.startsWith('e2e-')).map(f=>'test/'+f)],{stdio:'inherit'})\"",
1414
"prepare": "husky",
1515
"format": "prettier --write .",
1616
"lint": "node --check src/*.js bin/*.js",
@@ -73,6 +73,7 @@
7373
"ws": "^8.19.0"
7474
},
7575
"devDependencies": {
76+
"@playwright/test": "^1.58.2",
7677
"c8": "^11.0.0",
7778
"husky": "^9.1.7",
7879
"lint-staged": "^16.2.7",

0 commit comments

Comments
 (0)