Skip to content

Commit 6bc60e6

Browse files
Add GitHub Actions CI/release, vhs demo tapes, and Makefile demo targets
- CI workflow runs pytest on push/PR to main - Release workflow triggers on v* tags: installs vhs, generates dll demo videos, and attaches them to the GitHub release - Makefile: demo-dll-dark, demo-dll-light, demo-dll targets - demos/dll-dark.tape and dll-light.tape (TokyoNight / Catppuccin Latte) - .gitignore: ignore generated demos/*.mp4 *.gif *.cast - common.py: explicitly focus Input widget on mount so vhs recordings work Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 11d334c commit 6bc60e6

7 files changed

Lines changed: 337 additions & 1 deletion

File tree

‎.github/workflows/ci.yml‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v4
14+
15+
- uses: actions/setup-python@v5
16+
with:
17+
python-version: '3.12'
18+
19+
- name: Install dependencies
20+
run: |
21+
python -m venv .venv
22+
.venv/bin/pip install -r requirements.txt
23+
24+
- name: Run tests
25+
run: .venv/bin/pytest tests/ -v

‎.github/workflows/release.yml‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v[0-9]*'
7+
8+
jobs:
9+
release:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: write
13+
14+
steps:
15+
- uses: actions/checkout@v4
16+
17+
- uses: actions/setup-python@v5
18+
with:
19+
python-version: '3.12'
20+
21+
- name: Install Python dependencies
22+
run: |
23+
python -m venv .venv
24+
.venv/bin/pip install -r requirements.txt
25+
26+
- name: Install ffmpeg and fonts
27+
run: |
28+
sudo apt-get update -q
29+
sudo apt-get install -y ffmpeg fonts-liberation fonts-dejavu-mono
30+
31+
- name: Install vhs
32+
run: |
33+
sudo mkdir -p /etc/apt/keyrings
34+
curl -fsSL https://repo.charm.sh/apt/gpg.key \
35+
| sudo gpg --dearmor -o /etc/apt/keyrings/charm.gpg
36+
echo "deb [signed-by=/etc/apt/keyrings/charm.gpg] https://repo.charm.sh/apt/ * *" \
37+
| sudo tee /etc/apt/sources.list.d/charm.list
38+
sudo apt-get update -q && sudo apt-get install -y vhs
39+
40+
- name: Generate demo videos
41+
run: make demo-dll
42+
43+
- name: Create GitHub release
44+
uses: softprops/action-gh-release@v2
45+
with:
46+
generate_release_notes: true
47+
files: |
48+
demos/dll-dark.mp4
49+
demos/dll-light.mp4

‎.gitignore‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,3 +166,8 @@ cython_debug/
166166
*.swo
167167
.*.swp
168168
.*.swo
169+
170+
# Generated demo videos / recordings
171+
demos/*.mp4
172+
demos/*.gif
173+
demos/*.cast

‎Makefile‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ TEST_ALIASES = test-bst test-deque test-dict test-dll test-graph test-heap \
99
test-queue-app test-deque-app test-list-app test-sll-app test-dll-app test-trie
1010

1111
.PHONY: install test $(RUN_ALIASES) $(TEST_ALIASES) \
12-
run-stack-app run-queue-app run-deque-app run-list-app run-sll-app run-dll-app
12+
run-stack-app run-queue-app run-deque-app run-list-app run-sll-app run-dll-app \
13+
demo-dll demo-dll-dark demo-dll-light
1314

1415
# Create the virtual environment
1516
.venv:
@@ -78,6 +79,15 @@ test-list-app: test-list_app
7879
test-dll-app: test-doubly_linked_list_app
7980
test-trie: test-trie_demo
8081

82+
# Demo videos (requires vhs: https://github.com/charmbracelet/vhs)
83+
demo-dll-dark:
84+
vhs demos/dll-dark.tape
85+
86+
demo-dll-light:
87+
vhs demos/dll-light.tape
88+
89+
demo-dll: demo-dll-dark demo-dll-light
90+
8191
# Run a single non-TUI demo module: make run-<name>
8292
# Note: demo files contain only class definitions (no __main__),
8393
# so these targets verify clean import but produce no output.

‎data_structures/tui/common.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,7 @@ def on_mount(self) -> None:
355355
self.query_one(RichLog).write(
356356
f"[bold]{self.STRUCTURE_NAME} Demo[/bold] — type [cyan]/help[/cyan] for commands."
357357
)
358+
self.query_one(Input).focus()
358359

359360
@on(Input.Submitted)
360361
def handle_command(self, event: Input.Submitted) -> None:

‎demos/dll-dark.tape‎

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# Doubly Linked List — interactive demo (dark theme)
2+
# Run from project root: vhs demos/dll-dark.tape
3+
4+
Output demos/dll-dark.mp4
5+
6+
Set Theme "TokyoNight"
7+
Set FontSize 16
8+
Set FontFamily "Monaco"
9+
Set Width 1800
10+
Set Height 900
11+
Set Framerate 30
12+
Set Padding 24
13+
Set WindowBar Colorful
14+
Set BorderRadius 8
15+
Set TypingSpeed 50ms
16+
Env TERM xterm-256color
17+
18+
# ── Launch ──────────────────────────────────────────────────────────────────
19+
Type ".venv/bin/python -m data_structures.tui.doubly_linked_list_app"
20+
Enter
21+
Sleep 10s
22+
23+
# ── Build initial list: nodes 1(10) 2(20) 3(30) 4(40) 5(50) ────────────────
24+
Type "/append 10 20 30 40 50"
25+
Enter
26+
Sleep 2s
27+
28+
# ── Inspect ─────────────────────────────────────────────────────────────────
29+
Type "/at 0"
30+
Enter
31+
Sleep 1.5s
32+
33+
Type "/at 2"
34+
Enter
35+
Sleep 1.5s
36+
37+
Type "/show-backward"
38+
Enter
39+
Sleep 2s
40+
41+
# ── Search ──────────────────────────────────────────────────────────────────
42+
Type "/search 30"
43+
Enter
44+
Sleep 1.5s
45+
46+
# ── Insert at head → node 6(5) ──────────────────────────────────────────────
47+
Type "/insert-at 0 5"
48+
Enter
49+
Sleep 2s
50+
51+
# ── Insert after node-1 (data=10) → node 7(15) ──────────────────────────────
52+
Type "/insert-after 1 15"
53+
Enter
54+
Sleep 2s
55+
56+
# ── Insert before node-3 (data=30) → node 8(25) ─────────────────────────────
57+
Type "/insert-before 3 25"
58+
Enter
59+
Sleep 2s
60+
61+
# List is now: 6(5) → 1(10) → 7(15) → 2(20) → 8(25) → 3(30) → 4(40) → 5(50)
62+
63+
# ── Remove by value ─────────────────────────────────────────────────────────
64+
Type "/remove-value 25"
65+
Enter
66+
Sleep 2s
67+
68+
# ── Remove by position (index 0 = node-6) ───────────────────────────────────
69+
Type "/remove-at 0"
70+
Enter
71+
Sleep 2s
72+
73+
# ── Remove by node id (node-7, data=15) ─────────────────────────────────────
74+
Type "/remove-node 7"
75+
Enter
76+
Sleep 2s
77+
78+
# List is now: 1(10) → 2(20) → 3(30) → 4(40) → 5(50)
79+
80+
# ── Undo / redo ─────────────────────────────────────────────────────────────
81+
Type "/undo"
82+
Enter
83+
Sleep 1.5s
84+
85+
Type "/undo"
86+
Enter
87+
Sleep 1.5s
88+
89+
Type "/redo"
90+
Enter
91+
Sleep 1.5s
92+
93+
# ── Save current state ───────────────────────────────────────────────────────
94+
Type "/save /tmp/dll-demo.json"
95+
Enter
96+
Sleep 1.5s
97+
98+
# ── Switch to strings and generate random values ─────────────────────────────
99+
Type "/clear"
100+
Enter
101+
Sleep 1s
102+
103+
Type "/type str"
104+
Enter
105+
Sleep 1s
106+
107+
Type "/random 5"
108+
Enter
109+
Sleep 2s
110+
111+
# ── Load the saved int session back ─────────────────────────────────────────
112+
Type "/clear"
113+
Enter
114+
Sleep 1s
115+
116+
Type "/load /tmp/dll-demo.json"
117+
Enter
118+
Sleep 2s
119+
120+
# ── Quit ────────────────────────────────────────────────────────────────────
121+
Type "/quit"
122+
Enter
123+
Sleep 1s

‎demos/dll-light.tape‎

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# Doubly Linked List — interactive demo (light theme)
2+
# Run from project root: vhs demos/dll-light.tape
3+
4+
Output demos/dll-light.mp4
5+
6+
Set Theme "Catppuccin Latte"
7+
Set FontSize 16
8+
Set FontFamily "Monaco"
9+
Set Width 1800
10+
Set Height 900
11+
Set Framerate 30
12+
Set Padding 24
13+
Set WindowBar Colorful
14+
Set BorderRadius 8
15+
Set TypingSpeed 50ms
16+
Env TERM xterm-256color
17+
18+
# ── Launch ──────────────────────────────────────────────────────────────────
19+
Type ".venv/bin/python -m data_structures.tui.doubly_linked_list_app"
20+
Enter
21+
Sleep 10s
22+
23+
# ── Build initial list: nodes 1(10) 2(20) 3(30) 4(40) 5(50) ────────────────
24+
Type "/append 10 20 30 40 50"
25+
Enter
26+
Sleep 2s
27+
28+
# ── Inspect ─────────────────────────────────────────────────────────────────
29+
Type "/at 0"
30+
Enter
31+
Sleep 1.5s
32+
33+
Type "/at 2"
34+
Enter
35+
Sleep 1.5s
36+
37+
Type "/show-backward"
38+
Enter
39+
Sleep 2s
40+
41+
# ── Search ──────────────────────────────────────────────────────────────────
42+
Type "/search 30"
43+
Enter
44+
Sleep 1.5s
45+
46+
# ── Insert at head → node 6(5) ──────────────────────────────────────────────
47+
Type "/insert-at 0 5"
48+
Enter
49+
Sleep 2s
50+
51+
# ── Insert after node-1 (data=10) → node 7(15) ──────────────────────────────
52+
Type "/insert-after 1 15"
53+
Enter
54+
Sleep 2s
55+
56+
# ── Insert before node-3 (data=30) → node 8(25) ─────────────────────────────
57+
Type "/insert-before 3 25"
58+
Enter
59+
Sleep 2s
60+
61+
# List is now: 6(5) → 1(10) → 7(15) → 2(20) → 8(25) → 3(30) → 4(40) → 5(50)
62+
63+
# ── Remove by value ─────────────────────────────────────────────────────────
64+
Type "/remove-value 25"
65+
Enter
66+
Sleep 2s
67+
68+
# ── Remove by position (index 0 = node-6) ───────────────────────────────────
69+
Type "/remove-at 0"
70+
Enter
71+
Sleep 2s
72+
73+
# ── Remove by node id (node-7, data=15) ─────────────────────────────────────
74+
Type "/remove-node 7"
75+
Enter
76+
Sleep 2s
77+
78+
# List is now: 1(10) → 2(20) → 3(30) → 4(40) → 5(50)
79+
80+
# ── Undo / redo ─────────────────────────────────────────────────────────────
81+
Type "/undo"
82+
Enter
83+
Sleep 1.5s
84+
85+
Type "/undo"
86+
Enter
87+
Sleep 1.5s
88+
89+
Type "/redo"
90+
Enter
91+
Sleep 1.5s
92+
93+
# ── Save current state ───────────────────────────────────────────────────────
94+
Type "/save /tmp/dll-demo.json"
95+
Enter
96+
Sleep 1.5s
97+
98+
# ── Switch to strings and generate random values ─────────────────────────────
99+
Type "/clear"
100+
Enter
101+
Sleep 1s
102+
103+
Type "/type str"
104+
Enter
105+
Sleep 1s
106+
107+
Type "/random 5"
108+
Enter
109+
Sleep 2s
110+
111+
# ── Load the saved int session back ─────────────────────────────────────────
112+
Type "/clear"
113+
Enter
114+
Sleep 1s
115+
116+
Type "/load /tmp/dll-demo.json"
117+
Enter
118+
Sleep 2s
119+
120+
# ── Quit ────────────────────────────────────────────────────────────────────
121+
Type "/quit"
122+
Enter
123+
Sleep 1s

0 commit comments

Comments
 (0)