asciinema-rails 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +107 -0
- data/Rakefile +2 -0
- data/asciinema-rails.gemspec +27 -0
- data/lib/asciinema-rails.rb +1 -0
- data/lib/asciinema/asciicast.rb +44 -0
- data/lib/asciinema/asciicast_frames_file_updater.rb +24 -0
- data/lib/asciinema/asciicast_snapshot_updater.rb +20 -0
- data/lib/asciinema/brush.rb +78 -0
- data/lib/asciinema/cell.rb +33 -0
- data/lib/asciinema/cursor.rb +18 -0
- data/lib/asciinema/film.rb +40 -0
- data/lib/asciinema/frame.rb +26 -0
- data/lib/asciinema/frame_diff.rb +20 -0
- data/lib/asciinema/frame_diff_list.rb +26 -0
- data/lib/asciinema/grid.rb +51 -0
- data/lib/asciinema/json_file_writer.rb +21 -0
- data/lib/asciinema/rails.rb +8 -0
- data/lib/asciinema/rails/convertor.rb +97 -0
- data/lib/asciinema/rails/engine.rb +6 -0
- data/lib/asciinema/rails/version.rb +5 -0
- data/lib/asciinema/snapshot.rb +41 -0
- data/lib/asciinema/stdout.rb +101 -0
- data/lib/asciinema/terminal.rb +65 -0
- data/spec/.rspec +2 -0
- data/spec/asciinemosh_spec.rb +63 -0
- data/spec/fixtures/sudosh-script +8 -0
- data/spec/fixtures/sudosh-time +22 -0
- data/spec/spec_helper.rb +50 -0
- data/src/terminal +0 -0
- data/src/terminal.c +307 -0
- data/vendor/assets/javscripts/asciinema-rails.js +4 -0
- data/vendor/assets/javscripts/asciinema.org/asciinema-player.js +1149 -0
- data/vendor/assets/javscripts/asciinema.org/rAF.js +32 -0
- data/vendor/assets/javscripts/asciinema.org/react-0.10.0.js +17228 -0
- data/vendor/assets/javscripts/asciinema.org/screenfull.js +143 -0
- data/vendor/assets/stylesheets/asciinema-rails.css +4 -0
- data/vendor/assets/stylesheets/asciinema.org/asciinema-player.css +1732 -0
- data/vendor/assets/stylesheets/asciinema.org/themes/solarized-dark.css +35 -0
- data/vendor/assets/stylesheets/asciinema.org/themes/solarized-light.css +35 -0
- data/vendor/assets/stylesheets/asciinema.org/themes/tango.css +35 -0
- metadata +192 -0
data/spec/.rspec
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'asciinema-rails'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
describe Asciinema::Rails::Convertor do
|
6
|
+
describe '.to_infile' do
|
7
|
+
context 'with invalid arguments' do
|
8
|
+
it "should raise an ArgumentError" do
|
9
|
+
expect{ Asciinema::Rails::Convertor.to_infile }.to raise_error(ArgumentError)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
context 'with valid arguments' do
|
13
|
+
before do
|
14
|
+
@timing_file_location = "#{File.dirname(__FILE__)}/fixtures/sudosh-time"
|
15
|
+
@script_file_location = "#{File.dirname(__FILE__)}/fixtures/sudosh-script"
|
16
|
+
@outfile = Asciinema::Rails::Convertor.to_infile(@timing_file_location, @script_file_location, {original_terminal_cols: 180, original_terminal_rows: 40})
|
17
|
+
end
|
18
|
+
it 'returns a File or Tempfile' do
|
19
|
+
expect(@outfile).to respond_to(:close)
|
20
|
+
expect(@outfile).to respond_to(:read)
|
21
|
+
end
|
22
|
+
it 'returns file with valid JSON' do
|
23
|
+
json_output = JSON.parse(@outfile.read)
|
24
|
+
|
25
|
+
expect(json_output['version']).to eq 1
|
26
|
+
expect(json_output['width']).to eq 180
|
27
|
+
expect(json_output['height']).to eq 40
|
28
|
+
expect(json_output['stdout'].length).to eq 21
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '.to_outfile' do
|
34
|
+
context 'with valid arguments' do
|
35
|
+
before do
|
36
|
+
@timing_file_location = "#{File.dirname(__FILE__)}/fixtures/sudosh-time"
|
37
|
+
@script_file_location = "#{File.dirname(__FILE__)}/fixtures/sudosh-script"
|
38
|
+
|
39
|
+
infile = Asciinema::Rails::Convertor.to_infile(@timing_file_location, @script_file_location, {original_terminal_cols: 180, original_terminal_rows: 40})
|
40
|
+
@output = Asciinema::Rails::Convertor.to_outfile(infile.path, {outfile_location: '/tmp/index.json.erb'})
|
41
|
+
|
42
|
+
end
|
43
|
+
it "returns an array of three values" do
|
44
|
+
expect(@output).to be_kind_of(Array)
|
45
|
+
expect(@output.length).to eq 3
|
46
|
+
end
|
47
|
+
it 'returns File or Tempfile as first argument' do
|
48
|
+
file = @output[0]
|
49
|
+
expect(file).to respond_to(:close)
|
50
|
+
expect(file).to respond_to(:read)
|
51
|
+
end
|
52
|
+
it 'returns valid JSON as the second argument' do
|
53
|
+
snapshot = @output[1]
|
54
|
+
expect{ JSON.parse(snapshot)}.not_to raise_error
|
55
|
+
end
|
56
|
+
it 'returns a hash as the third argument' do
|
57
|
+
attributes = @output[2]
|
58
|
+
expect(attributes).to be_kind_of(Hash)
|
59
|
+
expect(attributes.length).to eq 3
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
.620510 18
|
2
|
+
0.106810 27
|
3
|
+
2.812022 2
|
4
|
+
0.000047 8
|
5
|
+
0.000008 3
|
6
|
+
0.000096 18
|
7
|
+
0.000031 19
|
8
|
+
2.317571 2
|
9
|
+
0.000050 6
|
10
|
+
0.000007 2
|
11
|
+
0.000007 3
|
12
|
+
0.000072 18
|
13
|
+
0.000047 19
|
14
|
+
1.754668 1
|
15
|
+
0.000056 9
|
16
|
+
0.000008 3
|
17
|
+
0.000084 18
|
18
|
+
0.000041 19
|
19
|
+
1.473598 1
|
20
|
+
0.000072 5
|
21
|
+
0.000007 8
|
22
|
+
0.000081 0
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'rails/all'
|
2
|
+
# require 'rspec/rails'
|
3
|
+
# require 'rspec/autorun'
|
4
|
+
|
5
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
6
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
7
|
+
# The generated `.rspec` file contains `--require spec_helper` which will cause
|
8
|
+
# this file to always be loaded, without a need to explicitly require it in any
|
9
|
+
# files.
|
10
|
+
#
|
11
|
+
# Given that it is always loaded, you are encouraged to keep this file as
|
12
|
+
# light-weight as possible. Requiring heavyweight dependencies from this file
|
13
|
+
# will add to the boot time of your test suite on EVERY test run, even for an
|
14
|
+
# individual file that may not need all of that loaded. Instead, consider making
|
15
|
+
# a separate helper file that requires the additional dependencies and performs
|
16
|
+
# the additional setup, and require it from the spec files that actually need
|
17
|
+
# it.
|
18
|
+
#
|
19
|
+
# The `.rspec` file also contains a few flags that are not defaults but that
|
20
|
+
# users commonly want.
|
21
|
+
#
|
22
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
23
|
+
RSpec.configure do |config|
|
24
|
+
# rspec-expectations config goes here. You can use an alternate
|
25
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
26
|
+
# assertions if you prefer.
|
27
|
+
config.expect_with :rspec do |expectations|
|
28
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
29
|
+
# and `failure_message` of custom matchers include text for helper methods
|
30
|
+
# defined using `chain`, e.g.:
|
31
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
32
|
+
# # => "be bigger than 2 and smaller than 4"
|
33
|
+
# ...rather than:
|
34
|
+
# # => "be bigger than 2"
|
35
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
36
|
+
end
|
37
|
+
|
38
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
39
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
40
|
+
config.mock_with :rspec do |mocks|
|
41
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
42
|
+
# a real object. This is generally recommended, and will default to
|
43
|
+
# `true` in RSpec 4.
|
44
|
+
mocks.verify_partial_doubles = true
|
45
|
+
end
|
46
|
+
|
47
|
+
config.filter_run :focus
|
48
|
+
config.run_all_when_everything_filtered = true
|
49
|
+
|
50
|
+
end
|
data/src/terminal
ADDED
Binary file
|
data/src/terminal.c
ADDED
@@ -0,0 +1,307 @@
|
|
1
|
+
#include <stdio.h>
|
2
|
+
#include <libtsm.h>
|
3
|
+
|
4
|
+
#define READ_BUF_SIZE 16 * 1024
|
5
|
+
|
6
|
+
static int RGB_LEVELS[6] = { 0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff };
|
7
|
+
|
8
|
+
struct tsm_screen_attr last_attr;
|
9
|
+
struct tsm_screen_attr *last_attr_ptr = &last_attr;
|
10
|
+
|
11
|
+
|
12
|
+
static int u8_wc_to_utf8(char *dest, uint32_t ch) {
|
13
|
+
if (ch < 0x80) {
|
14
|
+
dest[0] = (char)ch;
|
15
|
+
return 1;
|
16
|
+
}
|
17
|
+
|
18
|
+
if (ch < 0x800) {
|
19
|
+
dest[0] = (ch >> 6) | 0xC0;
|
20
|
+
dest[1] = (ch & 0x3F) | 0x80;
|
21
|
+
return 2;
|
22
|
+
}
|
23
|
+
|
24
|
+
if (ch < 0x10000) {
|
25
|
+
dest[0] = (ch >> 12) | 0xE0;
|
26
|
+
dest[1] = ((ch >> 6) & 0x3F) | 0x80;
|
27
|
+
dest[2] = (ch & 0x3F) | 0x80;
|
28
|
+
return 3;
|
29
|
+
}
|
30
|
+
|
31
|
+
if (ch < 0x110000) {
|
32
|
+
dest[0] = (ch >> 18) | 0xF0;
|
33
|
+
dest[1] = ((ch >> 12) & 0x3F) | 0x80;
|
34
|
+
dest[2] = ((ch >> 6) & 0x3F) | 0x80;
|
35
|
+
dest[3] = (ch & 0x3F) | 0x80;
|
36
|
+
return 4;
|
37
|
+
}
|
38
|
+
|
39
|
+
return 0;
|
40
|
+
}
|
41
|
+
|
42
|
+
void check_err(int err, const char *message) {
|
43
|
+
if (err) {
|
44
|
+
printf("%s\n", message);
|
45
|
+
exit(1);
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
void write_cb(struct tsm_vte *vte, const char *u8, size_t len, void *data) {}
|
50
|
+
|
51
|
+
int prepare_cb(struct tsm_screen *con, void *data) {
|
52
|
+
int *line = (int *)data;
|
53
|
+
|
54
|
+
*line = -1;
|
55
|
+
printf("[");
|
56
|
+
|
57
|
+
return 0;
|
58
|
+
}
|
59
|
+
|
60
|
+
void print_char(const uint32_t *ch) {
|
61
|
+
char str[5];
|
62
|
+
int size;
|
63
|
+
|
64
|
+
if (*ch == 0) {
|
65
|
+
printf(" ");
|
66
|
+
} else if (*ch == '"') {
|
67
|
+
printf("\\\"");
|
68
|
+
} else if (*ch == '\\') {
|
69
|
+
printf("\\\\");
|
70
|
+
} else {
|
71
|
+
size = u8_wc_to_utf8(str, *ch);
|
72
|
+
if (size > 0) {
|
73
|
+
str[size] = 0;
|
74
|
+
printf("%s", str);
|
75
|
+
} else {
|
76
|
+
printf("?");
|
77
|
+
}
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
int get_rgb_index(int value) {
|
82
|
+
int i;
|
83
|
+
|
84
|
+
for (i=0; i<6; i++) {
|
85
|
+
if (RGB_LEVELS[i] == value) return i;
|
86
|
+
}
|
87
|
+
|
88
|
+
return -1;
|
89
|
+
}
|
90
|
+
|
91
|
+
int get_rgb_color(int r, int g, int b) {
|
92
|
+
if (r == g && g == b && (r - 8) % 10 == 0) {
|
93
|
+
return 232 + (r - 8) / 10;
|
94
|
+
} else {
|
95
|
+
return 16 + get_rgb_index(r) * 36 + get_rgb_index(g) * 6 + get_rgb_index(b);
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
int get_fg(const struct tsm_screen_attr *attr) {
|
100
|
+
if (attr->fccode == -1) {
|
101
|
+
return get_rgb_color(attr->fr, attr->fg, attr->fb);
|
102
|
+
} else if (attr->fccode >= 0 && attr->fccode < 16) {
|
103
|
+
return attr->fccode;
|
104
|
+
} else {
|
105
|
+
return -1;
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
int get_bg(const struct tsm_screen_attr *attr) {
|
110
|
+
if (attr->bccode == -1) {
|
111
|
+
return get_rgb_color(attr->br, attr->bg, attr->bb);
|
112
|
+
} else if (attr->bccode >= 0 && attr->bccode < 16) {
|
113
|
+
return attr->bccode;
|
114
|
+
} else {
|
115
|
+
return -1;
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
void print_attr(const struct tsm_screen_attr *attr) {
|
120
|
+
bool comma_needed = false;
|
121
|
+
int color_code;
|
122
|
+
|
123
|
+
printf("{");
|
124
|
+
|
125
|
+
color_code = get_fg(attr);
|
126
|
+
if (color_code != -1) {
|
127
|
+
printf("\"fg\":%d", color_code);
|
128
|
+
comma_needed = true;
|
129
|
+
}
|
130
|
+
|
131
|
+
color_code = get_bg(attr);
|
132
|
+
if (color_code != -1) {
|
133
|
+
if (comma_needed) printf(",");
|
134
|
+
printf("\"bg\":%d", color_code);
|
135
|
+
comma_needed = true;
|
136
|
+
}
|
137
|
+
|
138
|
+
if (attr->bold) {
|
139
|
+
if (comma_needed) printf(",");
|
140
|
+
printf("\"bold\":true");
|
141
|
+
comma_needed = true;
|
142
|
+
}
|
143
|
+
|
144
|
+
if (attr->underline) {
|
145
|
+
if (comma_needed) printf(",");
|
146
|
+
printf("\"underline\":true");
|
147
|
+
comma_needed = true;
|
148
|
+
}
|
149
|
+
|
150
|
+
if (attr->inverse) {
|
151
|
+
if (comma_needed) printf(",");
|
152
|
+
printf("\"inverse\":true");
|
153
|
+
comma_needed = true;
|
154
|
+
}
|
155
|
+
|
156
|
+
if (attr->blink) {
|
157
|
+
if (comma_needed) printf(",");
|
158
|
+
printf("\"blink\":true");
|
159
|
+
comma_needed = true;
|
160
|
+
}
|
161
|
+
|
162
|
+
printf("}");
|
163
|
+
}
|
164
|
+
|
165
|
+
bool attr_eq(struct tsm_screen_attr *attr1, const struct tsm_screen_attr *attr2) {
|
166
|
+
return attr1->fccode == attr2->fccode &&
|
167
|
+
attr1->bccode == attr2->bccode &&
|
168
|
+
attr1->fr == attr2->fr &&
|
169
|
+
attr1->fg == attr2->fg &&
|
170
|
+
attr1->fb == attr2->fb &&
|
171
|
+
attr1->br == attr2->br &&
|
172
|
+
attr1->bg == attr2->bg &&
|
173
|
+
attr1->bb == attr2->bb &&
|
174
|
+
attr1->bold == attr2->bold &&
|
175
|
+
attr1->underline == attr2->underline &&
|
176
|
+
attr1->inverse == attr2->inverse &&
|
177
|
+
attr1->blink == attr2->blink;
|
178
|
+
}
|
179
|
+
|
180
|
+
void attr_cp(const struct tsm_screen_attr *src, struct tsm_screen_attr *dst) {
|
181
|
+
memcpy((void *)dst, (const void *)src, sizeof(last_attr));
|
182
|
+
}
|
183
|
+
|
184
|
+
void close_cell() {
|
185
|
+
printf("\",");
|
186
|
+
print_attr(last_attr_ptr);
|
187
|
+
printf("]");
|
188
|
+
}
|
189
|
+
|
190
|
+
void open_cell(const struct tsm_screen_attr *attr) {
|
191
|
+
printf("[\"");
|
192
|
+
attr_cp(attr, last_attr_ptr);
|
193
|
+
}
|
194
|
+
|
195
|
+
int draw_cb(struct tsm_screen *con, uint32_t id, const uint32_t *ch, size_t len,
|
196
|
+
unsigned int width, unsigned int posx, unsigned int posy,
|
197
|
+
const struct tsm_screen_attr *attr, void *data) {
|
198
|
+
|
199
|
+
int *line = (int *)data;
|
200
|
+
|
201
|
+
if (((signed int)posy) > *line) {
|
202
|
+
if (*line >= 0) {
|
203
|
+
close_cell();
|
204
|
+
printf("],"); // close line
|
205
|
+
}
|
206
|
+
printf("["); // open line
|
207
|
+
open_cell(attr);
|
208
|
+
}
|
209
|
+
|
210
|
+
*line = posy;
|
211
|
+
|
212
|
+
if (width == 0) return 0;
|
213
|
+
|
214
|
+
if (!(attr_eq(last_attr_ptr, attr))) {
|
215
|
+
close_cell();
|
216
|
+
printf(",");
|
217
|
+
open_cell(attr);
|
218
|
+
}
|
219
|
+
|
220
|
+
print_char(ch);
|
221
|
+
|
222
|
+
return 0;
|
223
|
+
}
|
224
|
+
|
225
|
+
int render_cb(struct tsm_screen *con, void *data) {
|
226
|
+
int *line = (int *)data;
|
227
|
+
|
228
|
+
if (*line >= 0) {
|
229
|
+
close_cell();
|
230
|
+
printf("]"); // close line
|
231
|
+
}
|
232
|
+
|
233
|
+
printf("]\n");
|
234
|
+
|
235
|
+
return 0;
|
236
|
+
}
|
237
|
+
|
238
|
+
int main(int argc, char *argv[]) {
|
239
|
+
int err;
|
240
|
+
int i;
|
241
|
+
struct tsm_screen *screen;
|
242
|
+
struct tsm_vte *vte;
|
243
|
+
int width, height;
|
244
|
+
char *line = NULL;
|
245
|
+
size_t size;
|
246
|
+
unsigned int n;
|
247
|
+
char *buffer = (char *) malloc(READ_BUF_SIZE);
|
248
|
+
int line_n, cursor_x, cursor_y;
|
249
|
+
char *cursor_visible;
|
250
|
+
unsigned int flags;
|
251
|
+
int m, read;
|
252
|
+
|
253
|
+
width = atoi(argv[1]);
|
254
|
+
height = atoi(argv[2]);
|
255
|
+
|
256
|
+
err = tsm_screen_new(&screen, NULL, NULL);
|
257
|
+
check_err(err, "can't create screen");
|
258
|
+
|
259
|
+
err = tsm_screen_resize(screen, width, height);
|
260
|
+
check_err(err, "can't resize screen");
|
261
|
+
|
262
|
+
err = tsm_vte_new(&vte, screen, write_cb, NULL, NULL, NULL);
|
263
|
+
check_err(err, "can't create vte");
|
264
|
+
|
265
|
+
for (;;) {
|
266
|
+
if (getline(&line, &size, stdin) == -1) break;
|
267
|
+
|
268
|
+
char action = line[0];
|
269
|
+
|
270
|
+
switch(action) {
|
271
|
+
case 'd':
|
272
|
+
if (getline(&line, &size, stdin) == -1) break;
|
273
|
+
n = atoi(line);
|
274
|
+
while (n > 0) {
|
275
|
+
if (n > READ_BUF_SIZE) {
|
276
|
+
m = READ_BUF_SIZE;
|
277
|
+
} else {
|
278
|
+
m = n;
|
279
|
+
}
|
280
|
+
read = fread(buffer, 1, m, stdin);
|
281
|
+
tsm_vte_input(vte, buffer, read);
|
282
|
+
n = n - read;
|
283
|
+
}
|
284
|
+
break;
|
285
|
+
case 'p':
|
286
|
+
tsm_screen_draw(screen, prepare_cb, draw_cb, render_cb, &line_n);
|
287
|
+
break;
|
288
|
+
case 'c':
|
289
|
+
cursor_x = tsm_screen_get_cursor_x(screen);
|
290
|
+
cursor_y = tsm_screen_get_cursor_y(screen);
|
291
|
+
flags = tsm_screen_get_flags(screen);
|
292
|
+
if (!(flags & TSM_SCREEN_HIDE_CURSOR)) {
|
293
|
+
cursor_visible = "true";
|
294
|
+
} else {
|
295
|
+
cursor_visible = "false";
|
296
|
+
}
|
297
|
+
printf("{\"x\":%d,\"y\":%d,\"visible\":%s}\n", cursor_x, cursor_y, cursor_visible);
|
298
|
+
|
299
|
+
break;
|
300
|
+
}
|
301
|
+
|
302
|
+
fflush(stdout);
|
303
|
+
}
|
304
|
+
|
305
|
+
return 0;
|
306
|
+
}
|
307
|
+
|