shellfie 1.0.0 → 1.1.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +56 -2
- data/README.md +258 -97
- data/lib/shellfie/animation_frame_builder.rb +31 -7
- data/lib/shellfie/animation_timeline.rb +2 -1
- data/lib/shellfie/ansi_line_buffer.rb +20 -4
- data/lib/shellfie/ansi_normalizer.rb +18 -8
- data/lib/shellfie/ansi_parser.rb +120 -7
- data/lib/shellfie/cassette.rb +76 -0
- data/lib/shellfie/cli.rb +65 -2
- data/lib/shellfie/cli_authoring.rb +233 -0
- data/lib/shellfie/cli_generate.rb +244 -40
- data/lib/shellfie/cli_info.rb +106 -6
- data/lib/shellfie/cli_run.rb +167 -0
- data/lib/shellfie/config.rb +5 -1
- data/lib/shellfie/config_defaults.rb +21 -2
- data/lib/shellfie/config_validation.rb +93 -4
- data/lib/shellfie/dependency_checker.rb +74 -3
- data/lib/shellfie/errors.rb +1 -0
- data/lib/shellfie/ffmpeg_encoder.rb +46 -0
- data/lib/shellfie/font_resolver.rb +11 -1
- data/lib/shellfie/gif_generator.rb +157 -11
- data/lib/shellfie/gif_palette.rb +8 -4
- data/lib/shellfie/html_renderer.rb +54 -0
- data/lib/shellfie/line_layout.rb +19 -10
- data/lib/shellfie/output_writer.rb +7 -2
- data/lib/shellfie/parser.rb +82 -19
- data/lib/shellfie/parser_validation.rb +48 -15
- data/lib/shellfie/render_geometry.rb +2 -1
- data/lib/shellfie/render_segment.rb +17 -5
- data/lib/shellfie/renderer.rb +28 -11
- data/lib/shellfie/rendering/text_painter.rb +23 -15
- data/lib/shellfie/rendering/window_chrome.rb +2 -2
- data/lib/shellfie/reproducibility_manifest.rb +41 -0
- data/lib/shellfie/session.rb +111 -0
- data/lib/shellfie/session_config.rb +562 -0
- data/lib/shellfie/session_runner.rb +689 -0
- data/lib/shellfie/svg_renderer.rb +222 -0
- data/lib/shellfie/terminal_screen.rb +389 -0
- data/lib/shellfie/text_metrics.rb +74 -15
- data/lib/shellfie/transcript_renderer.rb +92 -0
- data/lib/shellfie/version.rb +1 -1
- data/lib/shellfie/yaml_safety.rb +147 -0
- data/lib/shellfie.rb +8 -1
- data/schema/shellfie-v1.schema.json +153 -0
- data/schema/shellfie-v2.schema.json +262 -0
- metadata +27 -24
- data/.rspec +0 -3
- data/Rakefile +0 -8
- data/docs/.nojekyll +0 -0
- data/docs/index.html +0 -205
- data/docs/scripts.js +0 -85
- data/docs/styles.css +0 -507
- data/examples/animation.yml +0 -33
- data/examples/colored.yml +0 -20
- data/examples/demo.gif +0 -0
- data/examples/demo.png +0 -0
- data/examples/demo_animation.yml +0 -31
- data/examples/headless.png +0 -0
- data/examples/headless.yml +0 -16
- data/examples/scrolling.yml +0 -48
- data/examples/simple.yml +0 -21
- data/examples/theme_macos.png +0 -0
- data/examples/theme_ubuntu.png +0 -0
- data/examples/theme_windows.png +0 -0
- data/shellfie.gemspec +0 -32
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0dbccd8edad54ded2bda6b8da245ab3f4ab3c9527ffd4bc75ef04cf7b6fc8713
|
|
4
|
+
data.tar.gz: 5fa83b5f80fae2d6281adc4ef685271af2b8c8f303d4116cbe26904ecf12c2a4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 473c51c3384978e8bc493b0a27cc1b5e9949c99c9f14d2cd6516cae963648c07bd281650175713f4028c09be1ce5d0140c119a4ffd7336da92e8a80075c43f30
|
|
7
|
+
data.tar.gz: fc061b6f9bff4c8f5bf7f512891ed8c13b39bf3342a14a5b98b967b15ac68a7d0dafc582ac884ea402e9d4ba61a0e549ca19d1883575c3af69a4e7e556b1d063
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,60 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 1.1.0 - 2026-08-29
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Add executable version 2 sessions with `run`, `record`, and `replay`; PTY sessions support command and key input, waits, assertions, hidden steps, redaction, named captures, editable recordings, and offline cassettes.
|
|
8
|
+
- Add native selectable-text SVG, accessible HTML, MP4, WebM, event-duration PNG sequences, plain and ANSI-preserving text, structured JSON, and asciinema v2 output. Legacy raster-backed SVG remains available as `svg-raster`.
|
|
9
|
+
- Add multiple configured outputs and capture-specific outputs from one session.
|
|
10
|
+
- Add JSON Schemas, file/line/column diagnostics, typo suggestions, and `new`, `format`, `compile`, `schema`, `completion`, and include-aware `watch` authoring commands.
|
|
11
|
+
- Add typed session variables, reusable and repeated step sets, and OS, shell, Ruby, and configured-environment step conditions.
|
|
12
|
+
- Add inferred output names, collision-checked output templates, common aspect presets, bounded parallel batch rendering with `--jobs`, and non-mutating output verification with `generate --check`.
|
|
13
|
+
- Add reproducibility manifests and JSON, SARIF, and JUnit validation reports for CI and editor integrations.
|
|
14
|
+
- Add a reusable GitHub Action for generating or checking outputs and Docker packaging for a consistent rendering environment.
|
|
15
|
+
- Add deterministic animation seeds, separate typing rate, frame timing, and playback speed controls, reverse and ping-pong playback, loop offsets, and format-specific GIF, WebP, and APNG controls.
|
|
16
|
+
- Add configurable East Asian Ambiguous width and report the active Unicode and width-table profile through inspection and manifests.
|
|
17
|
+
- Add broader terminal behavior for cursor movement, erase and insertion controls, alternate screens, scroll regions, colon-form colors, extended underline styles and colors, blink, conceal, and safe OSC 8 links.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Treat every frame `delay` as a post-action delay; a `type` frame's delay now overrides the global command delay, and `lines` initialize the screen when `frames` are also present.
|
|
22
|
+
- Treat an explicit WebP format as static unless the input has frames or `--animate` is supplied.
|
|
23
|
+
- Deprecate `--fps` in favor of `--framerate`; use `--typing-rate` to control input speed independently.
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- Correct terminal tab stops, backspace behavior, wide-cell overwrites, grapheme-cluster typing, split Unicode input, line feeds, autowrap, and scroll-region editing.
|
|
28
|
+
- Correct animation duration rounding, duplicate-frame timing, APNG tail duration, playback-speed timing, and minimum video duration.
|
|
29
|
+
- Prevent output collisions before rendering, stream stdout without an output-sized memory copy, preserve existing outputs on failure, and remove partial animation files after errors or interruption.
|
|
30
|
+
- Improve live-session prompt and exit synchronization, preserve asynchronous output, honor PTY dimensions and working directories, and terminate leftover child processes.
|
|
31
|
+
- Improve `doctor` and dependency checks with real rendering, format delegates, fonts, security policy, and temporary-storage diagnostics.
|
|
32
|
+
|
|
33
|
+
### Security and reliability
|
|
34
|
+
|
|
35
|
+
- Add include cycle and chain diagnostics, per-file and aggregate size limits, and an optional symlink-aware root policy for includes and session working directories.
|
|
36
|
+
- Add environment allowlists, total and per-step timeouts, bounded regular-expression evaluation, output and control-sequence limits, and rendering workload and temporary-disk budgets.
|
|
37
|
+
- Add opt-in rejection of unsupported terminal graphics and an explicit policy for preserving only bounded `http`, `https`, and `mailto` OSC 8 links.
|
|
38
|
+
|
|
39
|
+
## 1.0.0 - 2026-05-20
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
|
|
43
|
+
- Add static SVG and WebP output plus animated WebP and APNG output, with format selection by extension or `--format` and binary output through stdout.
|
|
44
|
+
- Add batch generation from multiple files and glob patterns, directory outputs, automatic output-directory creation, and `--quiet` and `--verbose` modes.
|
|
45
|
+
- Add `inspect` for resolved configuration and output dimensions, and `doctor` for Ruby, ImageMagick, output-directory, and encoding checks.
|
|
46
|
+
- Add custom themes, Dracula, One Dark, Solarized Dark, and Catppuccin Mocha color schemes, configurable colors and window decorations, background gradients, font fallbacks, and block, bar, or underline cursors.
|
|
47
|
+
- Add reusable YAML includes, line selection and semantic prompt, command, and output colors.
|
|
48
|
+
- Add clip, wrap, and scroll overflow modes, exact-size and trimmed canvases, maximum height and line controls, configurable tab width, and per-line or persistent ANSI state.
|
|
49
|
+
- Add animation typing jitter and chunking, output and final delays, frame limits, global, adaptive, or theme palettes, optional dithering, and configurable scroll easing.
|
|
50
|
+
- Add public Ruby APIs for parsing, validating, rendering, and inspecting configurations.
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
|
|
54
|
+
- Correct xterm 256-color values and malformed extended colors, and add dim, reverse, strikethrough, and overline styles plus carriage return, backspace, cursor movement, erase, and tab handling.
|
|
55
|
+
- Improve line measurement, clipping, wrapping, fixed-height scrolling, trailing-line preservation, stable animation dimensions, and Windows, Ubuntu, macOS, and headless window rendering.
|
|
56
|
+
- Refuse to replace an existing output unless `--force` is supplied, write outputs atomically, report errors on stderr, and distinguish filesystem failures with their own exit code.
|
|
57
|
+
- Reject unknown keys, invalid types, unsupported values, excessive lines, frames, characters, pixels, and rendered frames with clearer validation errors before rendering.
|
|
4
58
|
|
|
5
59
|
## 0.1.1 - 2026-01-12
|
|
6
60
|
|
|
@@ -8,4 +62,4 @@
|
|
|
8
62
|
|
|
9
63
|
## 0.1.0 - 2026-01-12
|
|
10
64
|
|
|
11
|
-
- Initial release.
|
|
65
|
+
- Initial release.
|
data/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/logo-header.svg" alt="Shellfie header logo">
|
|
3
|
+
</p>
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
|
-
<
|
|
6
|
+
<strong>Deterministic terminal visuals and executable sessions from YAML</strong>
|
|
7
7
|
</p>
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
@@ -14,38 +14,84 @@ Generate terminal screenshot-style images and animations from YAML.
|
|
|
14
14
|
</p>
|
|
15
15
|
|
|
16
16
|
<p align="center">
|
|
17
|
-
<
|
|
17
|
+
<a href="#features">Features</a> ·
|
|
18
|
+
<a href="#installation">Installation</a> ·
|
|
19
|
+
<a href="#quick-start">Quick Start</a> ·
|
|
20
|
+
<a href="#terminal-sessions">Terminal Sessions</a> ·
|
|
21
|
+
<a href="#configuration">Configuration</a> ·
|
|
22
|
+
<a href="#automation">Automation</a>
|
|
18
23
|
</p>
|
|
19
24
|
|
|
20
|
-
|
|
25
|
+
---
|
|
21
26
|
|
|
22
|
-
|
|
27
|
+
Shellfie compiles YAML into deterministic terminal images, animations, and semantic transcripts. When explicitly requested, it can also run real PTY sessions, assert their output, record cassettes, and replay them offline.
|
|
23
28
|
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
<p align="center">
|
|
30
|
+
<img src="examples/demo.gif" alt="Shellfie generating and validating an animated terminal demo" width="720">
|
|
31
|
+
<br>
|
|
32
|
+
<sub>Generated by Shellfie from <a href="examples/demo_animation.yml">examples/demo_animation.yml</a>.</sub>
|
|
33
|
+
</p>
|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
|
|
37
|
+
- Deterministic compose mode that never executes configuration content
|
|
38
|
+
- Optional PTY execution with waits, assertions, captures, and redaction
|
|
39
|
+
- Editable recordings and offline cassette replay
|
|
40
|
+
- PNG, WebP, native SVG, HTML, GIF, APNG, MP4, WebM, and PNG sequence output
|
|
41
|
+
- Plain text, ANSI, JSON, and asciinema v2 semantic transcripts
|
|
42
|
+
- macOS, Ubuntu, Windows, and custom window styles, plus a headless mode
|
|
43
|
+
- ANSI colors and controls, Unicode grapheme handling, and configurable ambiguous width
|
|
44
|
+
- JSON Schemas, source-aware diagnostics, formatting, compilation, completion, and watch commands
|
|
45
|
+
- Resource limits, path policies, dependency preflight, and atomic output replacement
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
Add Shellfie to your Gemfile:
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
gem "shellfie"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Then install:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
bundle install
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Or install it directly:
|
|
26
62
|
|
|
27
63
|
```bash
|
|
28
|
-
brew install imagemagick
|
|
29
64
|
gem install shellfie
|
|
30
65
|
```
|
|
31
66
|
|
|
32
|
-
|
|
67
|
+
### Requirements
|
|
33
68
|
|
|
34
|
-
|
|
35
|
-
|
|
69
|
+
- Ruby 3.0+
|
|
70
|
+
- ImageMagick
|
|
71
|
+
- ffmpeg for MP4, WebM, and APNG output
|
|
72
|
+
|
|
73
|
+
Install media dependencies on macOS:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
brew install imagemagick ffmpeg
|
|
36
77
|
```
|
|
37
78
|
|
|
79
|
+
On Ubuntu or Debian:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
sudo apt install imagemagick ffmpeg
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Static and animated composition works on macOS, Linux, and Windows. Live PTY sessions are not supported on native Windows.
|
|
86
|
+
|
|
38
87
|
## Quick Start
|
|
39
88
|
|
|
40
89
|
Create `terminal.yml`:
|
|
41
90
|
|
|
42
91
|
```yaml
|
|
92
|
+
version: 1
|
|
43
93
|
theme: macos
|
|
44
|
-
title: "Terminal
|
|
45
|
-
|
|
46
|
-
window:
|
|
47
|
-
width: 600
|
|
48
|
-
padding: 20
|
|
94
|
+
title: "Terminal"
|
|
49
95
|
|
|
50
96
|
lines:
|
|
51
97
|
- prompt: "$ "
|
|
@@ -59,82 +105,184 @@ Generate an image:
|
|
|
59
105
|
shellfie generate terminal.yml -o terminal.png
|
|
60
106
|
```
|
|
61
107
|
|
|
62
|
-
|
|
108
|
+
Without `-o`, Shellfie writes `terminal.png` beside the input. `shf` is available as a short alias for `shellfie`.
|
|
109
|
+
|
|
110
|
+
### Output Formats
|
|
111
|
+
|
|
112
|
+
| Kind | Formats |
|
|
113
|
+
| --- | --- |
|
|
114
|
+
| Static | PNG, WebP, native selectable-text SVG, accessible HTML |
|
|
115
|
+
| Animated | GIF, WebP, APNG, MP4, WebM, event-duration PNG sequence with `timeline.json` |
|
|
116
|
+
| Semantic | TXT, ANSI, JSON, asciinema v2 (`asciicast` or `cast`) |
|
|
117
|
+
|
|
118
|
+
Use `svg-raster` only when the legacy PNG-backed SVG behavior is required.
|
|
63
119
|
|
|
64
120
|
## CLI
|
|
65
121
|
|
|
122
|
+
| Command | Purpose |
|
|
123
|
+
| --- | --- |
|
|
124
|
+
| `generate` | Render a version 1 compose configuration |
|
|
125
|
+
| `run` | Execute and render a version 2 terminal session |
|
|
126
|
+
| `record` | Execute a session and save a cassette or editable YAML |
|
|
127
|
+
| `replay` | Render a cassette without executing its commands |
|
|
128
|
+
| `new` / `init` | Create or print starter configuration |
|
|
129
|
+
| `format` | Normalize YAML formatting |
|
|
130
|
+
| `compile` | Print the resolved configuration or session IR |
|
|
131
|
+
| `validate` | Validate input as text, JSON, SARIF, or JUnit |
|
|
132
|
+
| `inspect` | Show resolved settings, dimensions, fonts, and resource estimates |
|
|
133
|
+
| `schema` | Print the version 1 or version 2 JSON Schema |
|
|
134
|
+
| `completion` | Print bash, zsh, fish, or PowerShell completion |
|
|
135
|
+
| `watch` | Regenerate when a configuration or include changes |
|
|
136
|
+
| `themes` | List available themes and color schemes |
|
|
137
|
+
| `doctor` | Check media tools, formats, fonts, policies, and storage |
|
|
138
|
+
| `version` | Show the installed Shellfie version |
|
|
139
|
+
|
|
140
|
+
## Terminal Sessions
|
|
141
|
+
|
|
142
|
+
Version 2 sessions execute commands only through `run` or `record`; review a session before running it. A minimal session looks like this:
|
|
143
|
+
|
|
144
|
+
```yaml
|
|
145
|
+
version: 2
|
|
146
|
+
mode: run
|
|
147
|
+
title: "Recorded shell"
|
|
148
|
+
theme: macos
|
|
149
|
+
|
|
150
|
+
terminal:
|
|
151
|
+
shell: /bin/sh
|
|
152
|
+
columns: 80
|
|
153
|
+
rows: 16
|
|
154
|
+
timeout: 10s
|
|
155
|
+
|
|
156
|
+
steps:
|
|
157
|
+
- type: printf 'hello from shellfie\n'
|
|
158
|
+
speed: 30cps
|
|
159
|
+
- key: enter
|
|
160
|
+
- expect:
|
|
161
|
+
screen_contains: hello from shellfie
|
|
162
|
+
exit_status: 0
|
|
163
|
+
- capture: complete
|
|
164
|
+
|
|
165
|
+
outputs:
|
|
166
|
+
- path: session.svg
|
|
167
|
+
format: svg
|
|
168
|
+
capture: complete
|
|
169
|
+
- path: session.txt
|
|
170
|
+
format: txt
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Run, record, or replay it:
|
|
174
|
+
|
|
66
175
|
```bash
|
|
67
|
-
shellfie
|
|
68
|
-
shellfie
|
|
69
|
-
shellfie
|
|
70
|
-
shellfie
|
|
71
|
-
shellfie generate config.yml -o output.png --no-header
|
|
72
|
-
shellfie validate config.yml
|
|
73
|
-
shellfie themes
|
|
74
|
-
shellfie init
|
|
176
|
+
shellfie run session.yml
|
|
177
|
+
shellfie record session.yml --cassette session.json
|
|
178
|
+
shellfie record session.yml --yaml editable-recording.yml
|
|
179
|
+
shellfie replay session.json -o session.gif --animate
|
|
75
180
|
```
|
|
76
181
|
|
|
77
|
-
|
|
182
|
+
### Session Actions
|
|
78
183
|
|
|
79
|
-
|
|
|
184
|
+
| Action | Purpose |
|
|
80
185
|
| --- | --- |
|
|
81
|
-
|
|
|
82
|
-
|
|
|
83
|
-
|
|
|
84
|
-
|
|
|
85
|
-
|
|
|
86
|
-
|
|
|
87
|
-
|
|
|
88
|
-
|
|
|
89
|
-
| `--no-shadow` | Disable shadow |
|
|
90
|
-
| `--transparent` | Transparent background |
|
|
91
|
-
| `--no-header` | Headless output |
|
|
92
|
-
| `--force` | Overwrite existing files |
|
|
186
|
+
| `run` | Execute a command directly, hidden by default |
|
|
187
|
+
| `type` | Type visible text at a configurable rate |
|
|
188
|
+
| `key` | Send Enter, arrows, modifiers, and other terminal keys |
|
|
189
|
+
| `sleep` | Add a deterministic presentation pause |
|
|
190
|
+
| `wait` | Wait for screen, line, prompt, exit, or stable-screen conditions |
|
|
191
|
+
| `expect` | Assert screen text, lines, exit status, cursor, elapsed time, or a text golden |
|
|
192
|
+
| `hide` / `show` | Control whether preparation and cleanup are recorded |
|
|
193
|
+
| `capture` | Name an intermediate screen for a specific output |
|
|
93
194
|
|
|
94
|
-
|
|
95
|
-
Animated output supports `gif`, `webp`, and `apng`.
|
|
195
|
+
Sessions also support `requires`, step-level working directories, multiple outputs, reusable includes, environment allowlists, total and per-step timeouts, and redaction patterns. Set `terminal.cwd_policy: root` to confine working directories to the session root. Use `async: true` before interacting with a long-running or full-screen process, then synchronize with `wait`.
|
|
96
196
|
|
|
97
|
-
|
|
197
|
+
Variables use `{{name}}` placeholders. Reusable `step_sets` expand through `use`, `repeat` repeats a step or set, and `if` can select steps by OS, shell, Ruby requirement, or explicitly configured environment values. Expansion is bounded and cycles are rejected.
|
|
198
|
+
|
|
199
|
+
## Authoring
|
|
200
|
+
|
|
201
|
+
Create and inspect configurations without rendering them:
|
|
98
202
|
|
|
99
|
-
|
|
203
|
+
```bash
|
|
204
|
+
shellfie new terminal.yml --template static
|
|
205
|
+
shellfie new demo.yml --template animation
|
|
206
|
+
shellfie new session.yml --template run
|
|
207
|
+
shellfie format terminal.yml
|
|
208
|
+
shellfie compile terminal.yml --format json
|
|
209
|
+
shellfie validate terminal.yml --format sarif
|
|
210
|
+
shellfie inspect terminal.yml --json
|
|
211
|
+
shellfie watch terminal.yml -o terminal.png
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Templates include `static`, `animation`, `run`, `tui`, `ci`, and `theme-gallery`. JSON Schemas live in [`schema/`](schema/); add the following directive for editor validation:
|
|
100
215
|
|
|
101
216
|
```yaml
|
|
102
|
-
|
|
103
|
-
|
|
217
|
+
# yaml-language-server: $schema=../schema/shellfie-v1.schema.json
|
|
218
|
+
```
|
|
104
219
|
|
|
105
|
-
|
|
106
|
-
width: 600
|
|
107
|
-
padding: 20
|
|
108
|
-
visible_lines: 8
|
|
109
|
-
overflow: clip
|
|
220
|
+
Use schema version 2 for executable sessions. Run `shellfie schema 1` or `shellfie schema 2` to print either schema, and `shellfie completion zsh` to generate shell completion.
|
|
110
221
|
|
|
111
|
-
|
|
112
|
-
family: Monaco
|
|
113
|
-
size: 14
|
|
114
|
-
line_height: 1.4
|
|
222
|
+
## Automation
|
|
115
223
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
224
|
+
Use the Docker-based GitHub Action to generate an output or fail when a committed output is stale:
|
|
225
|
+
|
|
226
|
+
```yaml
|
|
227
|
+
- uses: ydah/shellfie@main
|
|
228
|
+
with:
|
|
229
|
+
input: examples/simple.yml
|
|
230
|
+
output: docs/terminal.png
|
|
231
|
+
check: "true"
|
|
122
232
|
```
|
|
123
233
|
|
|
124
|
-
|
|
234
|
+
Or build and run the included container locally:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
docker build -t shellfie .
|
|
238
|
+
docker run --rm -v "$PWD:/work" shellfie \
|
|
239
|
+
generate /work/examples/simple.yml -o /work/terminal.png --force
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Use `--manifest manifest.json` during generation to record configuration and output hashes, Ruby, OS, ImageMagick, ffmpeg, and resolved font fingerprints.
|
|
243
|
+
|
|
244
|
+
## Configuration
|
|
245
|
+
|
|
246
|
+
### Compose Configuration
|
|
247
|
+
|
|
248
|
+
| Key | Purpose |
|
|
249
|
+
| --- | --- |
|
|
250
|
+
| `theme` | `macos`, `ubuntu`, `windows`, or `custom` |
|
|
251
|
+
| `window_theme` | Window chrome to use with a custom theme |
|
|
252
|
+
| `color_scheme` | Built-in palette: `dracula`, `one_dark`, `solarized_dark`, or `catppuccin_mocha` |
|
|
253
|
+
| `colors` | Individual color overrides |
|
|
254
|
+
| `window` | Width, height, padding, wrapping, clipping, scrolling, and terminal policies |
|
|
255
|
+
| `font` | Family, fallback families, size, and line height |
|
|
256
|
+
| `animation` | Typing, timing, playback, loop, palette, and encoder controls |
|
|
257
|
+
| `cursor` | Cursor style and color |
|
|
258
|
+
| `headless` | Hide window chrome |
|
|
259
|
+
| `lines` | Initial or static terminal content |
|
|
260
|
+
| `frames` | Animated terminal events |
|
|
261
|
+
| `limits` | Source, output-frame, pixel, and temporary-storage ceilings |
|
|
262
|
+
|
|
263
|
+
Static content uses `lines`; animations add `frames`:
|
|
125
264
|
|
|
126
265
|
```yaml
|
|
266
|
+
version: 1
|
|
127
267
|
theme: macos
|
|
128
268
|
title: "Demo"
|
|
129
269
|
|
|
270
|
+
window:
|
|
271
|
+
width: 600
|
|
272
|
+
padding: 20
|
|
273
|
+
visible_lines: 8
|
|
274
|
+
overflow: clip
|
|
275
|
+
|
|
130
276
|
animation:
|
|
131
277
|
typing_speed: 50
|
|
132
|
-
|
|
278
|
+
framerate: 30
|
|
279
|
+
playback_speed: 1.0
|
|
133
280
|
cursor_blink: true
|
|
134
281
|
loop: true
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
282
|
+
seed: 1
|
|
283
|
+
|
|
284
|
+
lines:
|
|
285
|
+
- output: "Ready"
|
|
138
286
|
|
|
139
287
|
frames:
|
|
140
288
|
- prompt: "$ "
|
|
@@ -144,50 +292,63 @@ frames:
|
|
|
144
292
|
delay: 1000
|
|
145
293
|
```
|
|
146
294
|
|
|
147
|
-
|
|
295
|
+
A frame `delay` is applied after its action; on a `type` frame it overrides `animation.command_delay`. When both `lines` and `frames` are present, `lines` initialize the screen before animated events run.
|
|
148
296
|
|
|
149
|
-
|
|
150
|
-
| --- | --- |
|
|
151
|
-
| `theme` | `macos`, `ubuntu`, `windows`, or `custom` |
|
|
152
|
-
| `color_scheme` | Built-in color scheme such as `dracula` |
|
|
153
|
-
| `colors` | Theme color overrides |
|
|
154
|
-
| `window` | Size, padding, wrapping, clipping, scrolling |
|
|
155
|
-
| `font` | Font family, size, line height |
|
|
156
|
-
| `animation` | Typing speed, delays, loop, palette, easing |
|
|
157
|
-
| `cursor` | Cursor style and color |
|
|
158
|
-
| `headless` | Hide window chrome |
|
|
159
|
-
| `lines` | Static terminal content |
|
|
160
|
-
| `frames` | Animated terminal content |
|
|
297
|
+
### Generate Options
|
|
161
298
|
|
|
162
|
-
|
|
299
|
+
| Option | Description |
|
|
300
|
+
| --- | --- |
|
|
301
|
+
| `-o, --output PATH` | Output path or `{name}-{theme}-{scale}.{format}` template |
|
|
302
|
+
| `--preset NAME` | Exact `readme`, `ogp`, `widescreen`, `standard`, or `vertical` canvas |
|
|
303
|
+
| `-t, --theme NAME` | Override the theme |
|
|
304
|
+
| `-a, --animate` | Render animation |
|
|
305
|
+
| `-s, --scale FACTOR` | Output scale: `1`, `2`, or `3` |
|
|
306
|
+
| `-w, --width PIXELS` | Override width |
|
|
307
|
+
| `--format FORMAT` | Select an output format |
|
|
308
|
+
| `--check` | Fail if an existing output is stale without replacing it |
|
|
309
|
+
| `--jobs N` | Render up to 32 inputs in parallel after preflight |
|
|
310
|
+
| `--typing-rate CPS` | Override typing rate in characters per second |
|
|
311
|
+
| `--framerate FPS` | Set output timing precision |
|
|
312
|
+
| `--seed N` | Set the deterministic animation seed |
|
|
313
|
+
| `--playback-speed FACTOR` | Scale the final timeline |
|
|
314
|
+
| `--overflow MODE` | `clip`, `wrap`, or `scroll` |
|
|
315
|
+
| `--no-shadow` | Disable the window shadow |
|
|
316
|
+
| `--transparent` | Use a transparent background |
|
|
317
|
+
| `--no-header` | Hide window chrome |
|
|
318
|
+
| `--force` | Replace an existing output |
|
|
163
319
|
|
|
164
|
-
|
|
320
|
+
### Advanced Controls
|
|
165
321
|
|
|
166
|
-
|
|
322
|
+
- Set `animation.direction` to `forward`, `reverse`, or `ping_pong`; `animation.loop_offset` selects a different initial frame.
|
|
323
|
+
- Tune GIF colors and optimization, WebP quality and lossless mode, APNG prediction and loop count, and scroll easing under `animation`.
|
|
324
|
+
- Set `window.ambiguous_width` to `1` or `2`; inspection and manifests report the active Unicode and width-table profile.
|
|
325
|
+
- Set `window.osc_policy: preserve` to keep bounded `http`, `https`, and `mailto` OSC 8 links in SVG and HTML.
|
|
326
|
+
- Set `window.graphics_policy: error` to reject SIXEL, Kitty, or iTerm2 graphics instead of discarding them.
|
|
327
|
+
- Use `include` for shared YAML. Set `include_policy: root` to reject paths outside the root configuration directory, including symlink escapes.
|
|
328
|
+
- Set resource ceilings under `limits`; Shellfie checks source size, pixels, frame work, and temporary storage before expensive rendering.
|
|
167
329
|
|
|
168
|
-
|
|
169
|
-
- `ubuntu`
|
|
170
|
-
- `windows`
|
|
330
|
+
## How It Works
|
|
171
331
|
|
|
172
|
-
|
|
332
|
+
1. Configuration parsing resolves includes, validates input, and reports source locations.
|
|
333
|
+
2. Compose, live PTY, or cassette input is normalized into terminal events.
|
|
334
|
+
3. The terminal screen applies Unicode, ANSI, cursor, erase, alternate-screen, and scroll behavior.
|
|
335
|
+
4. Raster, SVG, HTML, video, or semantic renderers produce the requested outputs.
|
|
336
|
+
5. Output files are written atomically after dependency, collision, and resource preflight.
|
|
173
337
|
|
|
174
|
-
|
|
175
|
-
headless: true
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
or:
|
|
179
|
-
|
|
180
|
-
```bash
|
|
181
|
-
shellfie generate config.yml -o output.png --no-header
|
|
182
|
-
```
|
|
338
|
+
`generate` is deterministic and never runs configuration content. `run` and `record` opt into PTY execution. `replay` uses a cassette and does not execute the recorded commands.
|
|
183
339
|
|
|
184
340
|
## Development
|
|
185
341
|
|
|
186
342
|
```bash
|
|
187
343
|
bundle install
|
|
188
344
|
bundle exec rspec
|
|
345
|
+
gem build shellfie.gemspec
|
|
189
346
|
```
|
|
190
347
|
|
|
348
|
+
## Contributing
|
|
349
|
+
|
|
350
|
+
Bug reports and pull requests are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) and [SECURITY.md](SECURITY.md) before submitting changes or security reports.
|
|
351
|
+
|
|
191
352
|
## License
|
|
192
353
|
|
|
193
|
-
[MIT](LICENSE)
|
|
354
|
+
Released under the [MIT License](LICENSE).
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "animation_scroll_easing"
|
|
4
4
|
require_relative "animation_timeline"
|
|
5
|
+
require_relative "text_metrics"
|
|
5
6
|
|
|
6
7
|
module Shellfie
|
|
7
8
|
class AnimationFrameBuilder
|
|
8
9
|
def initialize(config)
|
|
9
10
|
@config = config
|
|
10
|
-
@random = Random.new(
|
|
11
|
+
@random = Random.new(config.animation[:seed])
|
|
11
12
|
@scroll_easing = AnimationScrollEasing.new(config)
|
|
12
13
|
end
|
|
13
14
|
|
|
@@ -15,9 +16,12 @@ module Shellfie
|
|
|
15
16
|
return [{ lines: @config.lines, delay: @config.animation[:final_delay] }] if @config.frames.empty?
|
|
16
17
|
|
|
17
18
|
frames = []
|
|
18
|
-
current_lines =
|
|
19
|
+
current_lines = @config.lines.flat_map { |line| line_data(line) }
|
|
19
20
|
AnimationTimeline.new(@config).each do |event|
|
|
20
21
|
case event.kind
|
|
22
|
+
when :screen
|
|
23
|
+
current_lines.replace(event.frame.screen.map { |line| { output: line } })
|
|
24
|
+
frames << { lines: build_display_lines(current_lines), delay: [event.frame.delay, 1].max }
|
|
21
25
|
when :command
|
|
22
26
|
frames.concat(command_frames(current_lines, event.frame))
|
|
23
27
|
when :output
|
|
@@ -62,7 +66,7 @@ module Shellfie
|
|
|
62
66
|
frames = []
|
|
63
67
|
prompt = frame.prompt || ""
|
|
64
68
|
command = frame.type
|
|
65
|
-
chars = command
|
|
69
|
+
chars = TextMetrics.graphemes(command)
|
|
66
70
|
chunk_size = @config.animation[:typing_chunk_size]
|
|
67
71
|
|
|
68
72
|
(chunk_size..chars.length).step(chunk_size).each do |index|
|
|
@@ -93,7 +97,11 @@ module Shellfie
|
|
|
93
97
|
end
|
|
94
98
|
|
|
95
99
|
def command_pause_frames(current_lines, frame)
|
|
96
|
-
delay =
|
|
100
|
+
delay = if frame.output
|
|
101
|
+
@config.animation[:command_delay]
|
|
102
|
+
else
|
|
103
|
+
frame.delay.positive? ? frame.delay : @config.animation[:command_delay]
|
|
104
|
+
end
|
|
97
105
|
return [] unless delay.positive?
|
|
98
106
|
return [{ lines: build_display_lines(current_lines), delay: delay }] unless @config.animation[:cursor_blink]
|
|
99
107
|
|
|
@@ -109,11 +117,11 @@ module Shellfie
|
|
|
109
117
|
output_delay = @config.animation[:output_delay]
|
|
110
118
|
|
|
111
119
|
if output_delay.positive?
|
|
112
|
-
output_lines.each_with_index.each_with_object([]) do |(line, index),
|
|
120
|
+
frames = output_lines.each_with_index.each_with_object([]) do |(line, index), result|
|
|
113
121
|
previous_count = current_lines.size
|
|
114
122
|
current_lines << { output: line, output_color: frame.output_color }
|
|
115
123
|
delay = @scroll_easing.output_delay(output_delay, index, output_lines.size)
|
|
116
|
-
|
|
124
|
+
result.concat(
|
|
117
125
|
@scroll_easing.transition_frames(
|
|
118
126
|
build_display_lines(current_lines),
|
|
119
127
|
delay: delay,
|
|
@@ -121,9 +129,11 @@ module Shellfie
|
|
|
121
129
|
)
|
|
122
130
|
)
|
|
123
131
|
end
|
|
132
|
+
frames << { lines: build_display_lines(current_lines), delay: frame.delay } if frame.delay.positive?
|
|
133
|
+
frames
|
|
124
134
|
else
|
|
125
135
|
output_lines.each { |line| current_lines << { output: line, output_color: frame.output_color } }
|
|
126
|
-
[{ lines: build_display_lines(current_lines), delay: frame.delay
|
|
136
|
+
[{ lines: build_display_lines(current_lines), delay: [frame.delay, 1].max }]
|
|
127
137
|
end
|
|
128
138
|
end
|
|
129
139
|
|
|
@@ -131,6 +141,20 @@ module Shellfie
|
|
|
131
141
|
{ prompt: prompt, command: command, cursor: cursor, prompt_color: prompt_color, command_color: command_color }
|
|
132
142
|
end
|
|
133
143
|
|
|
144
|
+
def line_data(line)
|
|
145
|
+
data = []
|
|
146
|
+
if line.prompt || line.command
|
|
147
|
+
data << command_line(
|
|
148
|
+
line.prompt,
|
|
149
|
+
line.command,
|
|
150
|
+
prompt_color: line.prompt_color,
|
|
151
|
+
command_color: line.command_color
|
|
152
|
+
)
|
|
153
|
+
end
|
|
154
|
+
data.concat(line.output.to_s.split("\n", -1).map { |output| { output: output, output_color: line.output_color } }) if line.output
|
|
155
|
+
data
|
|
156
|
+
end
|
|
157
|
+
|
|
134
158
|
def cursor_command_line(frame)
|
|
135
159
|
command_line(
|
|
136
160
|
frame.prompt || "",
|
|
@@ -12,6 +12,7 @@ module Shellfie
|
|
|
12
12
|
return enum_for(:each) unless block_given?
|
|
13
13
|
|
|
14
14
|
@config.frames.each do |frame|
|
|
15
|
+
yield Event.new(kind: :screen, frame: frame) if frame.screen
|
|
15
16
|
yield Event.new(kind: :command, frame: frame) if frame.type
|
|
16
17
|
yield Event.new(kind: :output, frame: frame) if frame.output
|
|
17
18
|
yield Event.new(kind: :pause, frame: frame) if pause_frame?(frame)
|
|
@@ -21,7 +22,7 @@ module Shellfie
|
|
|
21
22
|
private
|
|
22
23
|
|
|
23
24
|
def pause_frame?(frame)
|
|
24
|
-
frame.delay&.positive? && frame.output.nil? && frame.type.nil?
|
|
25
|
+
frame.delay&.positive? && frame.output.nil? && frame.type.nil? && frame.screen.nil?
|
|
25
26
|
end
|
|
26
27
|
end
|
|
27
28
|
end
|