@jsenv/terminal-recorder 1.0.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.
- package/README.md +51 -0
- package/dist/css/xterm.css +177 -0
- package/dist/js/terminal_recorder.js +1470 -0
- package/dist/xterm.html +26 -0
- package/package.json +47 -0
- package/src/client/gif_encoder.js +398 -0
- package/src/client/gif_encoder_dev/gif_encoder.js +567 -0
- package/src/client/gif_encoder_dev/gif_encoder_dev.html +130 -0
- package/src/client/gif_encoder_dev/lzwencoder.js +275 -0
- package/src/client/gif_encoder_dev/neuquant.js +510 -0
- package/src/client/gif_encoder_jsenv_dev/gif_encoder_jsenv_dev.html +94 -0
- package/src/client/lzw_encoder.js +253 -0
- package/src/client/neuquant.js +498 -0
- package/src/client/terminal_recorder.js +317 -0
- package/src/client/xterm.html +26 -0
- package/src/client/xterm_dev.html +48 -0
- package/src/main.js +4 -0
- package/src/svg/parse_ansi.js +375 -0
- package/src/svg/render_terminal_html.js +5 -0
- package/src/svg/render_terminal_svg.js +300 -0
- package/src/svg/xml_generator.js +149 -0
- package/src/terminal_recording.js +205 -0
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# terminal snapshot [](https://www.npmjs.com/package/@jsenv/terminal-recorder)
|
|
2
|
+
|
|
3
|
+
Help to generate beautiful terminal snapshots:
|
|
4
|
+
|
|
5
|
+
## svg
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
## gif
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
## Example
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
import { writeFileSync } from "node:fs";
|
|
17
|
+
|
|
18
|
+
import { startTerminalRecording } from "@jsenv/terminal-recorder";
|
|
19
|
+
|
|
20
|
+
const terminalRecorder = await startTerminalRecording({
|
|
21
|
+
svg: {
|
|
22
|
+
title: "Terminal",
|
|
23
|
+
},
|
|
24
|
+
video: true,
|
|
25
|
+
gif: true,
|
|
26
|
+
});
|
|
27
|
+
const datas = [
|
|
28
|
+
`[31mred[39m `,
|
|
29
|
+
`[33myellow[39m `,
|
|
30
|
+
`[32mgreen[39m `,
|
|
31
|
+
`[36mcyan[39m `,
|
|
32
|
+
`[34mblue[39m `,
|
|
33
|
+
`[35mmagenta[39m`,
|
|
34
|
+
];
|
|
35
|
+
for (const data of datas) {
|
|
36
|
+
terminalRecorder.write(data);
|
|
37
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
38
|
+
}
|
|
39
|
+
const result = await terminalRecorder.stop();
|
|
40
|
+
const svg = await result.svg();
|
|
41
|
+
writeFileSync(new URL("./terminal.svg", import.meta.url), svg);
|
|
42
|
+
const gif = await result.gif();
|
|
43
|
+
writeFileSync(new URL("./terminal.gif", import.meta.url), gif);
|
|
44
|
+
const webm = await result.webm();
|
|
45
|
+
writeFileSync(new URL("./terminal.webm", import.meta.url), webm);
|
|
46
|
+
const mp4 = await result.mp4();
|
|
47
|
+
writeFileSync(new URL("./terminal.mp4", import.meta.url), mp4);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The animated formats (.gif, .webm, .mp4) are rendered by xterm in chrome headless using playwright.
|
|
51
|
+
Xterm is used by VsCode integrated terminal, it supports ansi, unicode and so on.
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
.xterm {
|
|
2
|
+
cursor: text;
|
|
3
|
+
user-select: none;
|
|
4
|
+
position: relative;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.xterm.focus, .xterm:focus {
|
|
8
|
+
outline: none;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.xterm .xterm-helpers {
|
|
12
|
+
z-index: 5;
|
|
13
|
+
position: absolute;
|
|
14
|
+
top: 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.xterm .xterm-helper-textarea {
|
|
18
|
+
opacity: 0;
|
|
19
|
+
z-index: -5;
|
|
20
|
+
white-space: nowrap;
|
|
21
|
+
resize: none;
|
|
22
|
+
border: 0;
|
|
23
|
+
width: 0;
|
|
24
|
+
height: 0;
|
|
25
|
+
margin: 0;
|
|
26
|
+
padding: 0;
|
|
27
|
+
position: absolute;
|
|
28
|
+
top: 0;
|
|
29
|
+
left: -9999em;
|
|
30
|
+
overflow: hidden;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.xterm .composition-view {
|
|
34
|
+
color: #fff;
|
|
35
|
+
white-space: nowrap;
|
|
36
|
+
z-index: 1;
|
|
37
|
+
background: #000;
|
|
38
|
+
display: none;
|
|
39
|
+
position: absolute;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.xterm .composition-view.active {
|
|
43
|
+
display: block;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.xterm .xterm-viewport {
|
|
47
|
+
cursor: default;
|
|
48
|
+
background-color: #000;
|
|
49
|
+
position: absolute;
|
|
50
|
+
inset: 0;
|
|
51
|
+
overflow-y: scroll;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.xterm .xterm-screen {
|
|
55
|
+
position: relative;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.xterm .xterm-screen canvas {
|
|
59
|
+
position: absolute;
|
|
60
|
+
top: 0;
|
|
61
|
+
left: 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.xterm .xterm-scroll-area {
|
|
65
|
+
visibility: hidden;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.xterm-char-measure-element {
|
|
69
|
+
visibility: hidden;
|
|
70
|
+
line-height: normal;
|
|
71
|
+
display: inline-block;
|
|
72
|
+
position: absolute;
|
|
73
|
+
top: 0;
|
|
74
|
+
left: -9999em;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.xterm.enable-mouse-events {
|
|
78
|
+
cursor: default;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.xterm.xterm-cursor-pointer, .xterm .xterm-cursor-pointer {
|
|
82
|
+
cursor: pointer;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.xterm.column-select.focus {
|
|
86
|
+
cursor: crosshair;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.xterm .xterm-accessibility, .xterm .xterm-message {
|
|
90
|
+
z-index: 10;
|
|
91
|
+
color: #0000;
|
|
92
|
+
pointer-events: none;
|
|
93
|
+
position: absolute;
|
|
94
|
+
inset: 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.xterm .live-region {
|
|
98
|
+
width: 1px;
|
|
99
|
+
height: 1px;
|
|
100
|
+
position: absolute;
|
|
101
|
+
left: -9999px;
|
|
102
|
+
overflow: hidden;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.xterm-dim {
|
|
106
|
+
opacity: 1 !important;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.xterm-underline-1 {
|
|
110
|
+
text-decoration: underline;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.xterm-underline-2 {
|
|
114
|
+
text-decoration: underline double;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.xterm-underline-3 {
|
|
118
|
+
text-decoration: underline wavy;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.xterm-underline-4 {
|
|
122
|
+
text-decoration: underline dotted;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.xterm-underline-5 {
|
|
126
|
+
text-decoration: underline dashed;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.xterm-overline {
|
|
130
|
+
text-decoration: overline;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.xterm-overline.xterm-underline-1 {
|
|
134
|
+
text-decoration: underline overline;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.xterm-overline.xterm-underline-2 {
|
|
138
|
+
text-decoration: overline double underline;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.xterm-overline.xterm-underline-3 {
|
|
142
|
+
text-decoration: overline wavy underline;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.xterm-overline.xterm-underline-4 {
|
|
146
|
+
text-decoration: overline dotted underline;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.xterm-overline.xterm-underline-5 {
|
|
150
|
+
text-decoration: overline dashed underline;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.xterm-strikethrough {
|
|
154
|
+
text-decoration: line-through;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.xterm-screen .xterm-decoration-container .xterm-decoration {
|
|
158
|
+
z-index: 6;
|
|
159
|
+
position: absolute;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer {
|
|
163
|
+
z-index: 7;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.xterm-decoration-overview-ruler {
|
|
167
|
+
z-index: 8;
|
|
168
|
+
pointer-events: none;
|
|
169
|
+
position: absolute;
|
|
170
|
+
top: 0;
|
|
171
|
+
right: 0;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.xterm-decoration-top {
|
|
175
|
+
z-index: 2;
|
|
176
|
+
position: relative;
|
|
177
|
+
}
|