btape 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +191 -67
  3. data/lib/btape/version.rb +1 -1
  4. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f7ea547bca5c9c270e16d779e170c68b0ab30b8bc4156cde58d4cfe7560ebbe4
4
- data.tar.gz: fb55fff428c21bfbc889aa653708a1d02c77ffe795c515238716ad9e246fecf6
3
+ metadata.gz: 3f37303b9cf610d9df7cf05199599bee524a462812e328e73bfda3f003138aec
4
+ data.tar.gz: 62151779b2bbe5ae60c645c66e4349b6a7f6655cf6828999c3b3d1c4bba4ff73
5
5
  SHA512:
6
- metadata.gz: 715e86c7d8e3d18834de6350eacac2ddad337d031faf3fae46f57d86364c445679509d9ef9db25e74688df5ac5c8c341a34418d85fb597b82ec312f2d02fd9d9
7
- data.tar.gz: 3b67abd203cd1619bc2de88fb4259437a994ec970ef58e81c937ca68efc0ebebc71588942942efa06e6dc56191af8935891c945d5241d87df3c5440787fa18be
6
+ metadata.gz: 82c0c748046529933359f27c5c46dc821f64f6b8b8bfee78b27b477155cf33f446f17ddc8b89b17eadda4afc1f643b588b6ecab88a64499dc4aa91afc38f3156
7
+ data.tar.gz: 52f9314a98ab708877b04832bd52aa5f7732c20999b597e8c4abe936d7fa285d49fabe9624e73a55b75c32d62dbf3a2b40e95701bd26462f671df8851fd3577c
data/README.md CHANGED
@@ -4,16 +4,105 @@
4
4
  <img src="assets/logo.png" alt="btape logo" width="480">
5
5
  </p>
6
6
 
7
- `btape` is a small, VHS-inspired Ruby CLI that runs browser actions from a
8
- `.tape` file and records them as an animated GIF. Ferrum controls Chromium
9
- and captures PNG frames, and a pure-Ruby encoder produces the GIF. It
10
- does not require Playwright, Selenium, ffmpeg, or an external service.
7
+ <p align="center">
8
+ <img src="assets/demo.gif" alt="btape recording a login flow into an animated GIF" width="720">
9
+ </p>
10
+
11
+ <p align="center">
12
+ <sub>Run a <code>.tape</code> file, get that GIF. This one is <code>examples/demo.tape</code>.</sub>
13
+ </p>
14
+
15
+ ## What is btape
16
+
17
+ A `.tape` file lists the browser actions you would otherwise click through by
18
+ hand. `btape` performs them against a real browser and records what the page
19
+ did as an animated GIF:
20
+
21
+ ```text
22
+ Output demo.gif
23
+ Viewport 1280x720
24
+
25
+ Goto https://example.com
26
+ Click "text=Login"
27
+ Type "#email" "demo@example.com"
28
+ Sleep 2s
29
+ ```
30
+
31
+ It is a small, VHS-inspired Ruby CLI. Ferrum controls Chromium and captures
32
+ PNG frames, and a pure-Ruby encoder produces the GIF. It does not require
33
+ Playwright, Selenium, ffmpeg, or an external service.
11
34
 
12
35
  Tapes are written by hand, or asked of a language model running on the same
13
36
  machine: `btape generate` describes the language to LM Studio, Ollama or
14
37
  anything else speaking their API, and holds the answer to the parser before
15
38
  handing it over.
16
39
 
40
+ ## Demo
41
+
42
+ The GIF above was recorded by btape itself, from `examples/demo.tape` driving
43
+ `examples/demo_app.html` — a static page bundled with the repository, so the
44
+ demo needs no other service running:
45
+
46
+ ```text
47
+ Output demo.gif
48
+ Viewport 1280x720
49
+
50
+ Goto file:///app/examples/demo_app.html
51
+ Sleep 500ms
52
+
53
+ Click "text=Login"
54
+ Sleep 300ms
55
+ Type "#email" "demo@example.com"
56
+ Type "#password" "password"
57
+ Click "text=Sign in"
58
+ Sleep 2s
59
+ ```
60
+
61
+ `file:///app/…` is where the development container mounts the checkout, so
62
+ `dip demo` records it as it stands. Outside the container, point `Goto` at
63
+ your own copy of that file — or at any URL at all, which is the point.
64
+
65
+ ## Install
66
+
67
+ ```sh
68
+ gem install btape
69
+ ```
70
+
71
+ or, in a Gemfile:
72
+
73
+ ```ruby
74
+ gem 'btape'
75
+ ```
76
+
77
+ Chromium — or Chrome — must be installed and discoverable by Ferrum:
78
+ `brew install --cask chromium` on macOS, `apt-get install chromium` on Debian
79
+ or Ubuntu. A browser installed somewhere unusual is named by `BROWSER_PATH`,
80
+ and one running on another machine by `--ws-url`; see
81
+ [A browser running somewhere else](#a-browser-running-somewhere-else).
82
+
83
+ ## Quick start
84
+
85
+ Write `demo.tape`:
86
+
87
+ ```text
88
+ Output demo.gif
89
+ Viewport 1280x720
90
+
91
+ Goto https://example.com
92
+ Sleep 1s
93
+ ```
94
+
95
+ and run it:
96
+
97
+ ```sh
98
+ btape demo.tape
99
+ # Created /path/to/demo.gif
100
+ ```
101
+
102
+ `Output` is where the GIF goes, resolved relative to the tape file. Point
103
+ `Goto` at your own application and add `Click`, `Type` and `WaitFor` lines
104
+ until the recording shows what you want it to. The whole language is below.
105
+
17
106
  ## Commands
18
107
 
19
108
  ```text
@@ -32,27 +121,53 @@ Screenshot [name]
32
121
  Sleep <number>ms|s
33
122
  ```
34
123
 
124
+ | Command | What it does |
125
+ | --- | --- |
126
+ | `Output` | Where the GIF is written. Required, and read before the run wherever it appears |
127
+ | `Viewport` | The size the page is rendered at, and so the size of the GIF before `Scale` |
128
+ | `Set` | Configures the run rather than acting on the page; the names are in [Settings](#settings) |
129
+ | `Goto` | Navigates the page, and puts the following commands back in the main frame |
130
+ | `Click` | Clicks the first element the selector matches |
131
+ | `Type` | Focuses the first element the selector matches and types the text into it |
132
+ | `Press` | Presses a key by name — `Enter`, `Tab`, `Right` — once, or `count` times |
133
+ | `Frame` | Points the following commands at an iframe; `Frame main` returns to the page |
134
+ | `Evaluate` | Runs JavaScript in the current frame |
135
+ | `WaitFor` | Polls until the selector matches something, then carries on |
136
+ | `WaitForJS` | Polls until the JavaScript returns something truthy |
137
+ | `Screenshot` | Captures one frame now, and with a name puts it at a predictable path too |
138
+ | `Sleep` | Holds where it is for the duration, recording all the while |
139
+
35
140
  Arguments containing spaces must be quoted. Empty lines and lines beginning
36
141
  with `#` are ignored. `Output` is required; `Viewport` defaults to `1280x720`.
37
- Output paths are resolved relative to the tape file.
38
-
39
- ```text
40
- Output demo.gif
41
- Viewport 1280x720
42
- Goto http://localhost:3000
43
- Click "text=Login"
44
- Type "#email" "demo@example.com"
45
- Sleep 1s
46
- ```
47
-
48
- `Evaluate` runs JavaScript in the current frame, which is how a tape reaches
49
- an API the page exposes rather than clicking at it. `Frame` points the
50
- commands that follow at an iframe, and `Frame main` returns to the page;
51
- navigating returns to the page too, since the frame belonged to the page that
52
- was left. `WaitFor` and `WaitForJS` poll instead of guessing at a `Sleep`.
53
-
54
- `Screenshot` captures a frame there and then. With a name it also lands at a
55
- predictable path, for picking one particular frame out of a run.
142
+ Output paths are resolved relative to the tape file. Selectors are CSS, or
143
+ `text=Some text` to match an element by the text it shows. Durations are a
144
+ number followed by `ms` or `s` — `500ms`, `1.5s`.
145
+
146
+ `Evaluate` is how a tape reaches an API the page exposes rather than clicking
147
+ at it. It runs in whatever frame is current, which is the page until a `Frame`
148
+ says otherwise; navigating returns to the page too, since the frame belonged
149
+ to the page that was left.
150
+
151
+ `WaitFor` and `WaitForJS` poll instead of guessing at a `Sleep`. Both check
152
+ every `WaitInterval`, and are satisfied only once `WaitStable` checks in a row
153
+ have passed; a check that fails puts the count back to zero, so a page that
154
+ reports itself ready before it has settled is caught by raising `WaitStable`
155
+ above 1. A duration on the line is how long that one wait gets, and
156
+ `WaitTimeout` is how long the ones without get. Neither raises for a check
157
+ that failed only running out of time does, against the line that was
158
+ waiting. An expression that raises counts as not-yet-true rather than as an
159
+ error, but the last thing it raised is named in the timeout, so an expression
160
+ that could never be true still says why.
161
+
162
+ `Screenshot` captures a frame there and then. It is what `Set CaptureMode
163
+ manual` records with — one frame per page of a deck, rather than a few hundred
164
+ near-identical ones — and a name makes that one frame findable afterwards, as
165
+ `frame-NAME.png` under `--frames-dir` and as `result.named_frames['NAME']`
166
+ from Ruby.
167
+
168
+ `Sleep` is for holding a finished frame on screen long enough to be seen,
169
+ which is a different job from waiting for the page: interval recording carries
170
+ on throughout one, so a `Sleep` is what gives a GIF its pauses.
56
171
 
57
172
  ## Settings
58
173
 
@@ -76,15 +191,7 @@ can run in more than one place.
76
191
  | `WaitStable` | `1` | How many checks in a row must pass before a wait is satisfied |
77
192
  | `MaxFrames` | `600` | Stop rather than record a hung page until the disk fills |
78
193
 
79
- ## Install and run
80
-
81
- Chromium must be installed and discoverable by Ferrum. Then:
82
-
83
- ```sh
84
- bundle install
85
- bundle exec btape demo.tape
86
- bundle exec rake spec
87
- ```
194
+ ## Command line
88
195
 
89
196
  ```text
90
197
  Usage: btape [options] SCRIPT.tape
@@ -100,6 +207,35 @@ Subcommands:
100
207
 
101
208
  `BTAPE_WS_URL` is used when neither `--ws-url` nor `--set WsUrl=` is given.
102
209
 
210
+ ## Advanced usage
211
+
212
+ ### A browser running somewhere else
213
+
214
+ btape launches its own Chromium by default. Point it at one that is already
215
+ running — a `browserless`/`chrome` container, say — and no browser needs to be
216
+ in the image btape runs from:
217
+
218
+ ```sh
219
+ btape --ws-url ws://chrome:3000 examples/thumbnails.tape
220
+ ```
221
+
222
+ Each connection gets its own browser context, so concurrent runs against one
223
+ shared browser do not see each other. The viewport is applied over the wire,
224
+ since a browser that is already running cannot be told its window size at
225
+ launch.
226
+
227
+ `examples/thumbnails.tape` is the other shape of run: one frame per page of a
228
+ deck, captured with `Set CaptureMode manual` against a remote browser.
229
+
230
+ ### Frames, not just the GIF
231
+
232
+ Frames are normally written to a temporary directory and removed as the run
233
+ unwinds. `--frames-dir` keeps them:
234
+
235
+ ```sh
236
+ btape --frames-dir frames examples/thumbnails.tape
237
+ ```
238
+
103
239
  ### Fonts, and text that is not Latin
104
240
 
105
241
  Glyphs come from the fonts the browser can see, which is not necessarily the
@@ -166,31 +302,7 @@ WaitForJS "(() => { const c = document.createElement('canvas').getContext('2d');
166
302
  Tape files themselves are read as UTF-8 whatever the locale says, so a `Type`
167
303
  line or a `text=` selector can be written in any script.
168
304
 
169
- ### A browser running somewhere else
170
-
171
- btape launches its own Chromium by default. Point it at one that is already
172
- running — a `browserless`/`chrome` container, say — and no browser needs to be
173
- in the image btape runs from:
174
-
175
- ```sh
176
- btape --ws-url ws://chrome:3000 examples/thumbnails.tape
177
- ```
178
-
179
- Each connection gets its own browser context, so concurrent runs against one
180
- shared browser do not see each other. The viewport is applied over the wire,
181
- since a browser that is already running cannot be told its window size at
182
- launch.
183
-
184
- ### Frames, not just the GIF
185
-
186
- Frames are normally written to a temporary directory and removed as the run
187
- unwinds. `--frames-dir` keeps them:
188
-
189
- ```sh
190
- btape --frames-dir frames examples/thumbnails.tape
191
- ```
192
-
193
- ## Writing a tape with a local model
305
+ ### Writing a tape with a local model
194
306
 
195
307
  `btape generate` describes the language to a model running on your own
196
308
  machine and asks it for a tape:
@@ -238,7 +350,7 @@ when told which line. What it cannot know is your markup, so a tape it wrote
238
350
  still names selectors that have to be checked against the page. Read it before
239
351
  you run it, the way you would read anything else generated for you.
240
352
 
241
- ### A model that is not on this machine
353
+ #### A model that is not on this machine
242
354
 
243
355
  `--llm-url` is the whole of the configuration, so a hosted endpoint speaking
244
356
  the same API works as well as a local one. Name the model rather than leaving
@@ -261,7 +373,7 @@ model keeps the description and the `--context` file on the machine that ran
261
373
  the command; a hosted one is handed both, and a context file is usually a page
262
374
  of your own markup rather than something you would have published.
263
375
 
264
- ## From Ruby
376
+ ### From Ruby
265
377
 
266
378
  `Runner#run` returns a `Btape::Result`:
267
379
 
@@ -310,10 +422,19 @@ tape = generator.call('record the dashboard loading', context: page_markup)
310
422
  Btape::Runner.new.run(Btape::Parser.new.parse(tape), base_directory: '.', output: buffer)
311
423
  ```
312
424
 
313
- ## Container development with dip or wip
425
+ ## Development
314
426
 
315
- The development image contains Ruby, Chromium and Latin fonts; tapes that
316
- record other scripts need fonts for them added to it.
427
+ Working on btape itself, rather than using it, starts from a checkout:
428
+
429
+ ```sh
430
+ bundle install
431
+ bundle exec rake spec
432
+ bundle exec rake rubocop
433
+ bundle exec exe/btape examples/demo.tape
434
+ ```
435
+
436
+ Or from the development image, which contains Ruby, Chromium and Latin fonts;
437
+ tapes that record other scripts need fonts for them added to it:
317
438
 
318
439
  ```sh
319
440
  dip provision
@@ -325,11 +446,9 @@ wip dispatch demo
325
446
  wip dispatch btape examples/demo.tape
326
447
  ```
327
448
 
328
- `examples/demo.tape` drives a small static page bundled at
329
- `examples/demo_app.html`, so the demo is self-contained and needs no other
330
- service running. Edit the tape (or point `Goto` at a different URL) to record
331
- something else. `examples/thumbnails.tape` shows the other shape of run: one
332
- frame per page of a deck, against a browser running elsewhere.
449
+ CI runs the specs and RuboCop across Ruby 3.2 to 4.0, and records
450
+ `examples/demo.tape` against a real Chromium to check that a GIF still comes
451
+ out the other end. [CONTRIBUTING.md](CONTRIBUTING.md) has the rest.
333
452
 
334
453
  ## Limitations
335
454
 
@@ -356,3 +475,8 @@ This project follows the [Code of Conduct](CODE_OF_CONDUCT.md).
356
475
  ## License
357
476
 
358
477
  [MIT](LICENSE)
478
+
479
+ ## Repository topics
480
+
481
+ Suggested GitHub topics for this repository: `browser-automation`, `chromium`,
482
+ `cli`, `ruby`, `developer-tools`, `screen-recording`, `gif`, `ferrum`, `vhs`.
data/lib/btape/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Btape
4
- VERSION = '0.3.0'
4
+ VERSION = '0.4.1'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: btape
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - btape contributors