@ytspar/sweetlink 1.18.0 → 1.20.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 +40 -0
- package/dist/cli/outputSchemas.d.ts +57 -1
- package/dist/cli/outputSchemas.d.ts.map +1 -1
- package/dist/cli/outputSchemas.js +36 -1
- package/dist/cli/outputSchemas.js.map +1 -1
- package/dist/cli/sweetlink.js +537 -36
- package/dist/cli/sweetlink.js.map +1 -1
- package/dist/daemon/browser.d.ts.map +1 -1
- package/dist/daemon/browser.js.map +1 -1
- package/dist/daemon/client.d.ts +7 -0
- package/dist/daemon/client.d.ts.map +1 -1
- package/dist/daemon/client.js +16 -2
- package/dist/daemon/client.js.map +1 -1
- package/dist/daemon/demo.d.ts.map +1 -1
- package/dist/daemon/demo.js +6 -2
- package/dist/daemon/demo.js.map +1 -1
- package/dist/daemon/diff.d.ts.map +1 -1
- package/dist/daemon/diff.js +5 -3
- package/dist/daemon/diff.js.map +1 -1
- package/dist/daemon/evidence.d.ts.map +1 -1
- package/dist/daemon/evidence.js +5 -5
- package/dist/daemon/evidence.js.map +1 -1
- package/dist/daemon/index.js +1 -1
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/listeners.d.ts.map +1 -1
- package/dist/daemon/listeners.js +7 -5
- package/dist/daemon/listeners.js.map +1 -1
- package/dist/daemon/recording.d.ts +5 -0
- package/dist/daemon/recording.d.ts.map +1 -1
- package/dist/daemon/recording.js +34 -11
- package/dist/daemon/recording.js.map +1 -1
- package/dist/daemon/refs.d.ts.map +1 -1
- package/dist/daemon/refs.js.map +1 -1
- package/dist/daemon/server.d.ts.map +1 -1
- package/dist/daemon/server.js +419 -80
- package/dist/daemon/server.js.map +1 -1
- package/dist/daemon/summary.d.ts +1 -1
- package/dist/daemon/summary.d.ts.map +1 -1
- package/dist/daemon/types.d.ts +1 -1
- package/dist/daemon/types.d.ts.map +1 -1
- package/dist/daemon/types.js.map +1 -1
- package/dist/daemon/viewer.d.ts +1 -1
- package/dist/daemon/viewer.d.ts.map +1 -1
- package/dist/daemon/viewer.js +18 -10
- package/dist/daemon/viewer.js.map +1 -1
- package/dist/ruler.js +3 -1
- package/dist/ruler.js.map +1 -1
- package/dist/runs.d.ts +34 -0
- package/dist/runs.d.ts.map +1 -0
- package/dist/runs.js +61 -0
- package/dist/runs.js.map +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +20 -10
- package/dist/server/index.js.map +1 -1
- package/dist/simulator/android.d.ts +35 -0
- package/dist/simulator/android.d.ts.map +1 -0
- package/dist/simulator/android.js +127 -0
- package/dist/simulator/android.js.map +1 -0
- package/dist/simulator/ios.d.ts +39 -0
- package/dist/simulator/ios.d.ts.map +1 -0
- package/dist/simulator/ios.js +123 -0
- package/dist/simulator/ios.js.map +1 -0
- package/dist/term/ansi.d.ts +37 -0
- package/dist/term/ansi.d.ts.map +1 -0
- package/dist/term/ansi.js +205 -0
- package/dist/term/ansi.js.map +1 -0
- package/dist/term/player.d.ts +25 -0
- package/dist/term/player.d.ts.map +1 -0
- package/dist/term/player.js +243 -0
- package/dist/term/player.js.map +1 -0
- package/dist/term/recorder.d.ts +33 -0
- package/dist/term/recorder.d.ts.map +1 -0
- package/dist/term/recorder.js +77 -0
- package/dist/term/recorder.js.map +1 -0
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +8 -4
- package/dist/vite.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal ANSI escape parser for the terminal player.
|
|
3
|
+
*
|
|
4
|
+
* Handles the subset that test runners typically emit:
|
|
5
|
+
* - SGR (colour/style) codes: ESC[Nm ESC[N;Nm ESC[N;N;Nm
|
|
6
|
+
* - 256-color: ESC[38;5;Nm ESC[48;5;Nm
|
|
7
|
+
* - Truecolor: ESC[38;2;R;G;Bm ESC[48;2;R;G;Bm
|
|
8
|
+
* - Reset (plain ESC[m, ESC[0m)
|
|
9
|
+
* - Erase line (ESC[K) — just clear from cursor to EOL
|
|
10
|
+
*
|
|
11
|
+
* Cursor-positioning escapes (ESC[NA, ESC[H, etc.) are stripped — the
|
|
12
|
+
* player models the screen as a flat scrollback buffer, not a TUI grid.
|
|
13
|
+
*/
|
|
14
|
+
export function freshState() {
|
|
15
|
+
return { fg: null, bg: null, bold: false, italic: false, underline: false, dim: false };
|
|
16
|
+
}
|
|
17
|
+
const BASIC_FG = {
|
|
18
|
+
30: '#000',
|
|
19
|
+
31: '#cd3131',
|
|
20
|
+
32: '#0dbc79',
|
|
21
|
+
33: '#e5e510',
|
|
22
|
+
34: '#2472c8',
|
|
23
|
+
35: '#bc3fbc',
|
|
24
|
+
36: '#11a8cd',
|
|
25
|
+
37: '#e5e5e5',
|
|
26
|
+
90: '#666',
|
|
27
|
+
91: '#f14c4c',
|
|
28
|
+
92: '#23d18b',
|
|
29
|
+
93: '#f5f543',
|
|
30
|
+
94: '#3b8eea',
|
|
31
|
+
95: '#d670d6',
|
|
32
|
+
96: '#29b8db',
|
|
33
|
+
97: '#fff',
|
|
34
|
+
};
|
|
35
|
+
const BASIC_BG = {
|
|
36
|
+
40: '#000',
|
|
37
|
+
41: '#cd3131',
|
|
38
|
+
42: '#0dbc79',
|
|
39
|
+
43: '#e5e510',
|
|
40
|
+
44: '#2472c8',
|
|
41
|
+
45: '#bc3fbc',
|
|
42
|
+
46: '#11a8cd',
|
|
43
|
+
47: '#e5e5e5',
|
|
44
|
+
100: '#666',
|
|
45
|
+
101: '#f14c4c',
|
|
46
|
+
102: '#23d18b',
|
|
47
|
+
103: '#f5f543',
|
|
48
|
+
104: '#3b8eea',
|
|
49
|
+
105: '#d670d6',
|
|
50
|
+
106: '#29b8db',
|
|
51
|
+
107: '#fff',
|
|
52
|
+
};
|
|
53
|
+
function color256(n) {
|
|
54
|
+
if (n < 16) {
|
|
55
|
+
const lut = [
|
|
56
|
+
'#000',
|
|
57
|
+
'#cd3131',
|
|
58
|
+
'#0dbc79',
|
|
59
|
+
'#e5e510',
|
|
60
|
+
'#2472c8',
|
|
61
|
+
'#bc3fbc',
|
|
62
|
+
'#11a8cd',
|
|
63
|
+
'#e5e5e5',
|
|
64
|
+
'#666',
|
|
65
|
+
'#f14c4c',
|
|
66
|
+
'#23d18b',
|
|
67
|
+
'#f5f543',
|
|
68
|
+
'#3b8eea',
|
|
69
|
+
'#d670d6',
|
|
70
|
+
'#29b8db',
|
|
71
|
+
'#fff',
|
|
72
|
+
];
|
|
73
|
+
return lut[n];
|
|
74
|
+
}
|
|
75
|
+
if (n < 232) {
|
|
76
|
+
const i = n - 16;
|
|
77
|
+
const r = Math.floor(i / 36) * 51;
|
|
78
|
+
const g = Math.floor((i % 36) / 6) * 51;
|
|
79
|
+
const b = (i % 6) * 51;
|
|
80
|
+
return `rgb(${r},${g},${b})`;
|
|
81
|
+
}
|
|
82
|
+
const v = (n - 232) * 10 + 8;
|
|
83
|
+
return `rgb(${v},${v},${v})`;
|
|
84
|
+
}
|
|
85
|
+
function applyParams(state, params) {
|
|
86
|
+
let i = 0;
|
|
87
|
+
while (i < params.length) {
|
|
88
|
+
const p = params[i];
|
|
89
|
+
if (p === 0) {
|
|
90
|
+
Object.assign(state, freshState());
|
|
91
|
+
}
|
|
92
|
+
else if (p === 1)
|
|
93
|
+
state.bold = true;
|
|
94
|
+
else if (p === 2)
|
|
95
|
+
state.dim = true;
|
|
96
|
+
else if (p === 3)
|
|
97
|
+
state.italic = true;
|
|
98
|
+
else if (p === 4)
|
|
99
|
+
state.underline = true;
|
|
100
|
+
else if (p === 22) {
|
|
101
|
+
state.bold = false;
|
|
102
|
+
state.dim = false;
|
|
103
|
+
}
|
|
104
|
+
else if (p === 23)
|
|
105
|
+
state.italic = false;
|
|
106
|
+
else if (p === 24)
|
|
107
|
+
state.underline = false;
|
|
108
|
+
else if (p === 39)
|
|
109
|
+
state.fg = null;
|
|
110
|
+
else if (p === 49)
|
|
111
|
+
state.bg = null;
|
|
112
|
+
else if (BASIC_FG[p])
|
|
113
|
+
state.fg = BASIC_FG[p];
|
|
114
|
+
else if (BASIC_BG[p])
|
|
115
|
+
state.bg = BASIC_BG[p];
|
|
116
|
+
else if (p === 38 || p === 48) {
|
|
117
|
+
const isFg = p === 38;
|
|
118
|
+
const mode = params[i + 1];
|
|
119
|
+
if (mode === 5 && params[i + 2] !== undefined) {
|
|
120
|
+
const c = color256(params[i + 2]);
|
|
121
|
+
if (isFg)
|
|
122
|
+
state.fg = c;
|
|
123
|
+
else
|
|
124
|
+
state.bg = c;
|
|
125
|
+
i += 2;
|
|
126
|
+
}
|
|
127
|
+
else if (mode === 2 && params[i + 4] !== undefined) {
|
|
128
|
+
const c = `rgb(${params[i + 2]},${params[i + 3]},${params[i + 4]})`;
|
|
129
|
+
if (isFg)
|
|
130
|
+
state.fg = c;
|
|
131
|
+
else
|
|
132
|
+
state.bg = c;
|
|
133
|
+
i += 4;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
i++;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function styleOf(state) {
|
|
140
|
+
const parts = [];
|
|
141
|
+
if (state.fg)
|
|
142
|
+
parts.push(`color:${state.fg}`);
|
|
143
|
+
if (state.bg)
|
|
144
|
+
parts.push(`background:${state.bg}`);
|
|
145
|
+
if (state.bold)
|
|
146
|
+
parts.push('font-weight:600');
|
|
147
|
+
if (state.italic)
|
|
148
|
+
parts.push('font-style:italic');
|
|
149
|
+
if (state.underline)
|
|
150
|
+
parts.push('text-decoration:underline');
|
|
151
|
+
if (state.dim)
|
|
152
|
+
parts.push('opacity:0.7');
|
|
153
|
+
return parts.join(';');
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Parse a chunk of bytes into HTML-ready spans, advancing `state` so
|
|
157
|
+
* subsequent chunks pick up where this one left off.
|
|
158
|
+
*
|
|
159
|
+
* The returned string is HTML — caller must escape any user data NOT
|
|
160
|
+
* coming from the recorded stream (it's intended to be embedded inline).
|
|
161
|
+
*/
|
|
162
|
+
export function ansiToHtml(input, state) {
|
|
163
|
+
const ESC = '';
|
|
164
|
+
let i = 0;
|
|
165
|
+
let out = '';
|
|
166
|
+
let buffer = '';
|
|
167
|
+
const flush = () => {
|
|
168
|
+
if (!buffer)
|
|
169
|
+
return;
|
|
170
|
+
const escaped = buffer.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
171
|
+
const style = styleOf(state);
|
|
172
|
+
if (style) {
|
|
173
|
+
out += `<span style="${style}">${escaped}</span>`;
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
out += escaped;
|
|
177
|
+
}
|
|
178
|
+
buffer = '';
|
|
179
|
+
};
|
|
180
|
+
while (i < input.length) {
|
|
181
|
+
const ch = input[i];
|
|
182
|
+
if (ch === ESC && input[i + 1] === '[') {
|
|
183
|
+
flush();
|
|
184
|
+
// Read up to the final byte (a letter)
|
|
185
|
+
let j = i + 2;
|
|
186
|
+
while (j < input.length && !/[A-Za-z]/.test(input[j]))
|
|
187
|
+
j++;
|
|
188
|
+
const final = input[j];
|
|
189
|
+
const body = input.slice(i + 2, j);
|
|
190
|
+
if (final === 'm') {
|
|
191
|
+
const params = body.split(';').map((s) => parseInt(s, 10) || 0);
|
|
192
|
+
applyParams(state, params.length === 0 ? [0] : params);
|
|
193
|
+
}
|
|
194
|
+
// K, A, B, C, D, H, J, etc. are ignored (not modeled by the flat scrollback).
|
|
195
|
+
i = j + 1;
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
buffer += ch;
|
|
199
|
+
i++;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
flush();
|
|
203
|
+
return out;
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=ansi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ansi.js","sourceRoot":"","sources":["../../src/term/ansi.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-contained HTML player for asciicast v2 recordings.
|
|
3
|
+
*
|
|
4
|
+
* Inlines the cast events as JSON in the document — the player has no
|
|
5
|
+
* runtime dependencies, works offline, opens anywhere a browser does.
|
|
6
|
+
*
|
|
7
|
+
* UX: play/pause, 0.1×–4× speed control, seek bar, ANSI colour rendering.
|
|
8
|
+
* Carriage returns (\r) reposition the cursor to column 0 of the current
|
|
9
|
+
* line so progress bars overwrite cleanly. Form-feeds (\f) and bare
|
|
10
|
+
* cursor escapes are dropped — this is a flat scrollback, not a TUI grid.
|
|
11
|
+
*
|
|
12
|
+
* Rendering uses createElement + textContent (NOT innerHTML) — every
|
|
13
|
+
* character from the recorded stream lands as a text node, never as
|
|
14
|
+
* markup. Inline style attrs are built from a hardcoded colour palette
|
|
15
|
+
* with no user input.
|
|
16
|
+
*/
|
|
17
|
+
export interface PlayerOptions {
|
|
18
|
+
castPath: string;
|
|
19
|
+
/** Title shown in the player header; defaults to the filename. */
|
|
20
|
+
title?: string;
|
|
21
|
+
/** Output HTML path. Defaults to `<castPath without .cast>.html`. */
|
|
22
|
+
outputPath?: string;
|
|
23
|
+
}
|
|
24
|
+
export declare function generatePlayer(options: PlayerOptions): Promise<string>;
|
|
25
|
+
//# sourceMappingURL=player.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"player.d.ts","sourceRoot":"","sources":["../../src/term/player.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAUD,wBAAsB,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAgO5E"}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-contained HTML player for asciicast v2 recordings.
|
|
3
|
+
*
|
|
4
|
+
* Inlines the cast events as JSON in the document — the player has no
|
|
5
|
+
* runtime dependencies, works offline, opens anywhere a browser does.
|
|
6
|
+
*
|
|
7
|
+
* UX: play/pause, 0.1×–4× speed control, seek bar, ANSI colour rendering.
|
|
8
|
+
* Carriage returns (\r) reposition the cursor to column 0 of the current
|
|
9
|
+
* line so progress bars overwrite cleanly. Form-feeds (\f) and bare
|
|
10
|
+
* cursor escapes are dropped — this is a flat scrollback, not a TUI grid.
|
|
11
|
+
*
|
|
12
|
+
* Rendering uses createElement + textContent (NOT innerHTML) — every
|
|
13
|
+
* character from the recorded stream lands as a text node, never as
|
|
14
|
+
* markup. Inline style attrs are built from a hardcoded colour palette
|
|
15
|
+
* with no user input.
|
|
16
|
+
*/
|
|
17
|
+
import { promises as fs } from 'fs';
|
|
18
|
+
import * as path from 'path';
|
|
19
|
+
function escapeAttr(s) {
|
|
20
|
+
return s
|
|
21
|
+
.replace(/&/g, '&')
|
|
22
|
+
.replace(/</g, '<')
|
|
23
|
+
.replace(/>/g, '>')
|
|
24
|
+
.replace(/"/g, '"');
|
|
25
|
+
}
|
|
26
|
+
export async function generatePlayer(options) {
|
|
27
|
+
const cast = await fs.readFile(options.castPath, 'utf-8');
|
|
28
|
+
const lines = cast.split(/\r?\n/).filter(Boolean);
|
|
29
|
+
if (lines.length === 0)
|
|
30
|
+
throw new Error(`Empty .cast file: ${options.castPath}`);
|
|
31
|
+
const header = JSON.parse(lines[0]);
|
|
32
|
+
const events = [];
|
|
33
|
+
for (let i = 1; i < lines.length; i++) {
|
|
34
|
+
try {
|
|
35
|
+
const e = JSON.parse(lines[i]);
|
|
36
|
+
if (Array.isArray(e) && e.length === 3)
|
|
37
|
+
events.push(e);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
/* skip malformed lines */
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const totalDuration = header.duration ?? (events.length ? events[events.length - 1][0] : 0);
|
|
44
|
+
const titleText = options.title ?? header.title ?? path.basename(options.castPath);
|
|
45
|
+
const outputPath = options.outputPath ?? options.castPath.replace(/\.cast$/i, '') + '.html';
|
|
46
|
+
// Embed the events as an inline JSON string. The player JS reads it on
|
|
47
|
+
// load and renders incrementally with `setTimeout` driven by the speed
|
|
48
|
+
// slider — no live network deps, no asciinema runtime.
|
|
49
|
+
const dataJson = JSON.stringify({ width: header.width, height: header.height, events });
|
|
50
|
+
const html = `<!DOCTYPE html>
|
|
51
|
+
<html lang="en">
|
|
52
|
+
<head>
|
|
53
|
+
<meta charset="utf-8">
|
|
54
|
+
<title>${escapeAttr(titleText)}</title>
|
|
55
|
+
<style>
|
|
56
|
+
:root {
|
|
57
|
+
--bg: #1a1b26; --fg: #c0caf5; --muted: #565f89; --accent: #7aa2f7;
|
|
58
|
+
--panel: #16161e; --border: #2f334d;
|
|
59
|
+
}
|
|
60
|
+
* { box-sizing: border-box; }
|
|
61
|
+
body { margin: 0; background: var(--bg); color: var(--fg); font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; }
|
|
62
|
+
header { padding: 12px 16px; background: var(--panel); border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
|
|
63
|
+
header h1 { font-size: 13px; font-weight: 500; margin: 0; color: var(--fg); flex: 1 1 auto; min-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
64
|
+
button { background: var(--bg); color: var(--fg); border: 1px solid var(--border); padding: 4px 12px; border-radius: 4px; font: inherit; font-size: 12px; cursor: pointer; }
|
|
65
|
+
button:hover { background: var(--border); }
|
|
66
|
+
button.primary { background: var(--accent); color: #16161e; border-color: var(--accent); font-weight: 600; }
|
|
67
|
+
.speed { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); }
|
|
68
|
+
.speed select { background: var(--bg); color: var(--fg); border: 1px solid var(--border); border-radius: 4px; padding: 3px 6px; font: inherit; font-size: 12px; }
|
|
69
|
+
.time { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; min-width: 90px; text-align: right; }
|
|
70
|
+
main { padding: 16px; }
|
|
71
|
+
pre { margin: 0; padding: 16px; background: #0d0e14; border-radius: 6px; overflow: auto; line-height: 1.4; font-size: 13px; min-height: 480px; max-height: calc(100vh - 200px); white-space: pre-wrap; word-break: break-word; }
|
|
72
|
+
.scrubber { padding: 8px 16px; background: var(--panel); border-top: 1px solid var(--border); display: flex; align-items: center; gap: 12px; }
|
|
73
|
+
input[type=range] { flex: 1; }
|
|
74
|
+
.label { background: var(--accent); color: #16161e; padding: 1px 8px; border-radius: 10px; font-size: 11px; font-weight: 600; }
|
|
75
|
+
</style>
|
|
76
|
+
</head>
|
|
77
|
+
<body>
|
|
78
|
+
<header>
|
|
79
|
+
<span class="label">term</span>
|
|
80
|
+
<h1>${escapeAttr(titleText)}</h1>
|
|
81
|
+
<div class="speed">
|
|
82
|
+
<span>speed</span>
|
|
83
|
+
<select id="speed">
|
|
84
|
+
<option value="0.1">0.1×</option>
|
|
85
|
+
<option value="0.25">0.25×</option>
|
|
86
|
+
<option value="0.5">0.5×</option>
|
|
87
|
+
<option value="1" selected>1×</option>
|
|
88
|
+
<option value="2">2×</option>
|
|
89
|
+
<option value="4">4×</option>
|
|
90
|
+
</select>
|
|
91
|
+
</div>
|
|
92
|
+
<button id="play" class="primary">▶ play</button>
|
|
93
|
+
<button id="restart">⟲ restart</button>
|
|
94
|
+
<span class="time"><span id="t">0.0</span>s / ${totalDuration.toFixed(1)}s</span>
|
|
95
|
+
</header>
|
|
96
|
+
<main>
|
|
97
|
+
<pre id="screen"></pre>
|
|
98
|
+
</main>
|
|
99
|
+
<div class="scrubber">
|
|
100
|
+
<input id="seek" type="range" min="0" max="${totalDuration}" step="0.01" value="0" />
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<script>
|
|
104
|
+
const DATA = ${dataJson};
|
|
105
|
+
const TOTAL = ${totalDuration};
|
|
106
|
+
|
|
107
|
+
// --- ANSI parser (mirrors src/term/ansi.ts; minimal SGR + \\r) ---
|
|
108
|
+
const BASIC_FG = {30:'#000',31:'#cd3131',32:'#0dbc79',33:'#e5e510',34:'#2472c8',35:'#bc3fbc',36:'#11a8cd',37:'#e5e5e5',90:'#666',91:'#f14c4c',92:'#23d18b',93:'#f5f543',94:'#3b8eea',95:'#d670d6',96:'#29b8db',97:'#fff'};
|
|
109
|
+
const BASIC_BG = {40:'#000',41:'#cd3131',42:'#0dbc79',43:'#e5e510',44:'#2472c8',45:'#bc3fbc',46:'#11a8cd',47:'#e5e5e5',100:'#666',101:'#f14c4c',102:'#23d18b',103:'#f5f543',104:'#3b8eea',105:'#d670d6',106:'#29b8db',107:'#fff'};
|
|
110
|
+
function c256(n){if(n<16){return ['#000','#cd3131','#0dbc79','#e5e510','#2472c8','#bc3fbc','#11a8cd','#e5e5e5','#666','#f14c4c','#23d18b','#f5f543','#3b8eea','#d670d6','#29b8db','#fff'][n];}if(n<232){const i=n-16;return 'rgb('+(Math.floor(i/36)*51)+','+(Math.floor((i%36)/6)*51)+','+((i%6)*51)+')';}const v=(n-232)*10+8;return 'rgb('+v+','+v+','+v+')';}
|
|
111
|
+
function freshState(){return{fg:null,bg:null,bold:false,italic:false,underline:false,dim:false};}
|
|
112
|
+
function applyParams(s,p){for(let i=0;i<p.length;i++){const v=p[i];if(v===0)Object.assign(s,freshState());else if(v===1)s.bold=true;else if(v===2)s.dim=true;else if(v===3)s.italic=true;else if(v===4)s.underline=true;else if(v===22){s.bold=false;s.dim=false;}else if(v===23)s.italic=false;else if(v===24)s.underline=false;else if(v===39)s.fg=null;else if(v===49)s.bg=null;else if(BASIC_FG[v])s.fg=BASIC_FG[v];else if(BASIC_BG[v])s.bg=BASIC_BG[v];else if(v===38||v===48){const fg=v===38;const m=p[i+1];if(m===5&&p[i+2]!==undefined){const c=c256(p[i+2]);if(fg)s.fg=c;else s.bg=c;i+=2;}else if(m===2&&p[i+4]!==undefined){const c='rgb('+p[i+2]+','+p[i+3]+','+p[i+4]+')';if(fg)s.fg=c;else s.bg=c;i+=4;}}}}
|
|
113
|
+
function styleOf(s){const o=[];if(s.fg)o.push('color:'+s.fg);if(s.bg)o.push('background:'+s.bg);if(s.bold)o.push('font-weight:600');if(s.italic)o.push('font-style:italic');if(s.underline)o.push('text-decoration:underline');if(s.dim)o.push('opacity:0.7');return o.join(';');}
|
|
114
|
+
|
|
115
|
+
// --- Build the assembled scrollback up to time t ---
|
|
116
|
+
function assembleText(targetT) {
|
|
117
|
+
// Walk through events, building text. Treat \\r as "delete back to last \\n",
|
|
118
|
+
// so progress bars (e.g., "5%\\r10%\\r") overwrite cleanly.
|
|
119
|
+
let text = '';
|
|
120
|
+
for (const e of DATA.events) {
|
|
121
|
+
if (e[0] > targetT) break;
|
|
122
|
+
if (e[1] !== 'o') continue;
|
|
123
|
+
const chunk = e[2];
|
|
124
|
+
for (let i = 0; i < chunk.length; i++) {
|
|
125
|
+
const c = chunk[i];
|
|
126
|
+
if (c === '\\r' && chunk[i+1] !== '\\n') {
|
|
127
|
+
const lastNl = text.lastIndexOf('\\n');
|
|
128
|
+
text = lastNl === -1 ? '' : text.slice(0, lastNl + 1);
|
|
129
|
+
} else if (c === '\\r') {
|
|
130
|
+
// CRLF — let the LF do the work.
|
|
131
|
+
} else {
|
|
132
|
+
text += c;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return text;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// --- Parse + render: build segments, then DOM-construct safely with textContent ---
|
|
140
|
+
function parseSegments(text) {
|
|
141
|
+
const segments = [];
|
|
142
|
+
const state = freshState();
|
|
143
|
+
let buf = '';
|
|
144
|
+
const flush = () => { if (buf) { segments.push({ text: buf, style: styleOf(state) }); buf = ''; } };
|
|
145
|
+
let i = 0;
|
|
146
|
+
while (i < text.length) {
|
|
147
|
+
const ch = text[i];
|
|
148
|
+
if (ch === '\\u001b' && text[i+1] === '[') {
|
|
149
|
+
flush();
|
|
150
|
+
let j = i + 2;
|
|
151
|
+
while (j < text.length && !/[A-Za-z]/.test(text[j])) j++;
|
|
152
|
+
const final = text[j];
|
|
153
|
+
const body = text.slice(i + 2, j);
|
|
154
|
+
if (final === 'm') {
|
|
155
|
+
const params = body.split(';').map((x) => parseInt(x, 10) || 0);
|
|
156
|
+
applyParams(state, params.length === 0 ? [0] : params);
|
|
157
|
+
}
|
|
158
|
+
i = j + 1;
|
|
159
|
+
} else {
|
|
160
|
+
buf += ch;
|
|
161
|
+
i++;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
flush();
|
|
165
|
+
return segments;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function renderUpTo(targetT) {
|
|
169
|
+
const text = assembleText(targetT);
|
|
170
|
+
const segments = parseSegments(text);
|
|
171
|
+
const screen = document.getElementById('screen');
|
|
172
|
+
// Clear and rebuild via DOM nodes so every byte from the cast lands as
|
|
173
|
+
// text content, not parsed markup. Spans only carry our hardcoded
|
|
174
|
+
// inline-style palette — no user input flows into attributes.
|
|
175
|
+
screen.textContent = '';
|
|
176
|
+
for (const seg of segments) {
|
|
177
|
+
if (seg.style) {
|
|
178
|
+
const span = document.createElement('span');
|
|
179
|
+
span.setAttribute('style', seg.style);
|
|
180
|
+
span.textContent = seg.text;
|
|
181
|
+
screen.appendChild(span);
|
|
182
|
+
} else {
|
|
183
|
+
screen.appendChild(document.createTextNode(seg.text));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// --- Playback controller ---
|
|
189
|
+
let currentT = 0;
|
|
190
|
+
let playing = false;
|
|
191
|
+
let speed = 1;
|
|
192
|
+
let lastFrame = 0;
|
|
193
|
+
function tick(ts) {
|
|
194
|
+
if (!playing) return;
|
|
195
|
+
if (lastFrame) {
|
|
196
|
+
const dt = (ts - lastFrame) / 1000;
|
|
197
|
+
currentT = Math.min(TOTAL, currentT + dt * speed);
|
|
198
|
+
document.getElementById('seek').value = currentT;
|
|
199
|
+
document.getElementById('t').textContent = currentT.toFixed(1);
|
|
200
|
+
renderUpTo(currentT);
|
|
201
|
+
if (currentT >= TOTAL) {
|
|
202
|
+
playing = false;
|
|
203
|
+
document.getElementById('play').textContent = '▶ play';
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
lastFrame = ts;
|
|
207
|
+
if (playing) requestAnimationFrame(tick);
|
|
208
|
+
}
|
|
209
|
+
function play() {
|
|
210
|
+
if (currentT >= TOTAL) currentT = 0;
|
|
211
|
+
playing = true;
|
|
212
|
+
lastFrame = 0;
|
|
213
|
+
document.getElementById('play').textContent = '❚❚ pause';
|
|
214
|
+
requestAnimationFrame(tick);
|
|
215
|
+
}
|
|
216
|
+
function pause() {
|
|
217
|
+
playing = false;
|
|
218
|
+
document.getElementById('play').textContent = '▶ play';
|
|
219
|
+
}
|
|
220
|
+
document.getElementById('play').addEventListener('click', () => playing ? pause() : play());
|
|
221
|
+
document.getElementById('restart').addEventListener('click', () => { currentT = 0; renderUpTo(0); document.getElementById('seek').value = 0; document.getElementById('t').textContent = '0.0'; if (!playing) play(); });
|
|
222
|
+
document.getElementById('seek').addEventListener('input', (e) => { pause(); currentT = parseFloat(e.target.value); document.getElementById('t').textContent = currentT.toFixed(1); renderUpTo(currentT); });
|
|
223
|
+
document.getElementById('speed').addEventListener('change', (e) => { speed = parseFloat(e.target.value); });
|
|
224
|
+
|
|
225
|
+
// --- Pick a sensible default speed for long recordings ---
|
|
226
|
+
if (TOTAL > 180) {
|
|
227
|
+
document.getElementById('speed').value = '4';
|
|
228
|
+
speed = 4;
|
|
229
|
+
} else if (TOTAL > 60) {
|
|
230
|
+
document.getElementById('speed').value = '2';
|
|
231
|
+
speed = 2;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Render a small head end so the user sees something even before play.
|
|
235
|
+
renderUpTo(Math.min(0.5, TOTAL));
|
|
236
|
+
</script>
|
|
237
|
+
</body>
|
|
238
|
+
</html>
|
|
239
|
+
`;
|
|
240
|
+
await fs.writeFile(outputPath, html);
|
|
241
|
+
return outputPath;
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=player.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"player.js","sourceRoot":"","sources":["../../src/term/player.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal Recorder
|
|
3
|
+
*
|
|
4
|
+
* Spawns a child process and captures stdout/stderr chunks with timestamps,
|
|
5
|
+
* emitting an asciicast v2 file (https://docs.asciinema.org/manual/asciicast/v2/).
|
|
6
|
+
*
|
|
7
|
+
* Notes on PTY-vs-pipe: we don't ship a native node-pty dep, so we set
|
|
8
|
+
* `FORCE_COLOR`, `TERM=xterm-256color`, `CI=` env vars to coax most CLIs
|
|
9
|
+
* into emitting ANSI colour. Programs that strictly require a TTY (less,
|
|
10
|
+
* top, vim) won't render correctly — that's a documented limitation.
|
|
11
|
+
*/
|
|
12
|
+
export interface TerminalCaptureOptions {
|
|
13
|
+
command: string;
|
|
14
|
+
/** Output path for the .cast file. The .html player is written next to it. */
|
|
15
|
+
output: string;
|
|
16
|
+
/** Shell to run the command in. Defaults to /bin/sh. */
|
|
17
|
+
shell?: string;
|
|
18
|
+
cols?: number;
|
|
19
|
+
rows?: number;
|
|
20
|
+
cwd?: string;
|
|
21
|
+
env?: Record<string, string>;
|
|
22
|
+
/** Optional human-readable label embedded in the .cast title field. */
|
|
23
|
+
label?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface TerminalCaptureResult {
|
|
26
|
+
durationSec: number;
|
|
27
|
+
bytes: number;
|
|
28
|
+
exitCode: number;
|
|
29
|
+
events: number;
|
|
30
|
+
castPath: string;
|
|
31
|
+
}
|
|
32
|
+
export declare function captureTerminal(options: TerminalCaptureOptions): Promise<TerminalCaptureResult>;
|
|
33
|
+
//# sourceMappingURL=recorder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../src/term/recorder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAID,wBAAsB,eAAe,CACnC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,qBAAqB,CAAC,CAiEhC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal Recorder
|
|
3
|
+
*
|
|
4
|
+
* Spawns a child process and captures stdout/stderr chunks with timestamps,
|
|
5
|
+
* emitting an asciicast v2 file (https://docs.asciinema.org/manual/asciicast/v2/).
|
|
6
|
+
*
|
|
7
|
+
* Notes on PTY-vs-pipe: we don't ship a native node-pty dep, so we set
|
|
8
|
+
* `FORCE_COLOR`, `TERM=xterm-256color`, `CI=` env vars to coax most CLIs
|
|
9
|
+
* into emitting ANSI colour. Programs that strictly require a TTY (less,
|
|
10
|
+
* top, vim) won't render correctly — that's a documented limitation.
|
|
11
|
+
*/
|
|
12
|
+
import { spawn } from 'child_process';
|
|
13
|
+
import { promises as fs } from 'fs';
|
|
14
|
+
export async function captureTerminal(options) {
|
|
15
|
+
const startTime = Date.now();
|
|
16
|
+
const events = [];
|
|
17
|
+
const cols = options.cols ?? 120;
|
|
18
|
+
const rows = options.rows ?? 30;
|
|
19
|
+
// Coax common CLIs into emitting ANSI colour even though stdout isn't
|
|
20
|
+
// a TTY. We don't drop a real PTY in front of them — the trade-off is
|
|
21
|
+
// simpler installation (no native build) at the cost of TUI fidelity.
|
|
22
|
+
const env = {
|
|
23
|
+
...process.env,
|
|
24
|
+
...options.env,
|
|
25
|
+
FORCE_COLOR: '3',
|
|
26
|
+
CLICOLOR_FORCE: '1',
|
|
27
|
+
TERM: 'xterm-256color',
|
|
28
|
+
COLUMNS: String(cols),
|
|
29
|
+
LINES: String(rows),
|
|
30
|
+
};
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
const child = spawn(options.shell ?? '/bin/sh', ['-c', options.command], {
|
|
33
|
+
cwd: options.cwd,
|
|
34
|
+
env,
|
|
35
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
36
|
+
});
|
|
37
|
+
let totalBytes = 0;
|
|
38
|
+
const handleData = (data) => {
|
|
39
|
+
const t = (Date.now() - startTime) / 1000;
|
|
40
|
+
const text = data.toString('utf-8');
|
|
41
|
+
events.push([t, 'o', text]);
|
|
42
|
+
totalBytes += data.length;
|
|
43
|
+
};
|
|
44
|
+
child.stdout?.on('data', handleData);
|
|
45
|
+
child.stderr?.on('data', handleData);
|
|
46
|
+
child.on('error', reject);
|
|
47
|
+
child.on('close', async (code) => {
|
|
48
|
+
try {
|
|
49
|
+
const durationSec = (Date.now() - startTime) / 1000;
|
|
50
|
+
const header = {
|
|
51
|
+
version: 2,
|
|
52
|
+
width: cols,
|
|
53
|
+
height: rows,
|
|
54
|
+
timestamp: Math.floor(startTime / 1000),
|
|
55
|
+
duration: durationSec,
|
|
56
|
+
title: options.label ?? options.command.slice(0, 100),
|
|
57
|
+
env: { TERM: 'xterm-256color', SHELL: options.shell ?? '/bin/sh' },
|
|
58
|
+
};
|
|
59
|
+
const lines = [JSON.stringify(header)];
|
|
60
|
+
for (const e of events)
|
|
61
|
+
lines.push(JSON.stringify(e));
|
|
62
|
+
await fs.writeFile(options.output, lines.join('\n') + '\n');
|
|
63
|
+
resolve({
|
|
64
|
+
durationSec,
|
|
65
|
+
bytes: totalBytes,
|
|
66
|
+
exitCode: code ?? 0,
|
|
67
|
+
events: events.length,
|
|
68
|
+
castPath: options.output,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
reject(err);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=recorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recorder.js","sourceRoot":"","sources":["../../src/term/recorder.ts"],"names":[],"mappings":"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"}
|
package/dist/vite.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAInC,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,GAAE,sBAA2B,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAInC,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,GAAE,sBAA2B,GAAG,MAAM,CAyDtE;AAGD,eAAe,SAAS,CAAC"}
|
package/dist/vite.js
CHANGED
|
@@ -45,13 +45,17 @@ export function sweetlink(options = {}) {
|
|
|
45
45
|
// Auto-start daemon if configured
|
|
46
46
|
if (options.daemon) {
|
|
47
47
|
const url = `http://localhost:${vitePort}`;
|
|
48
|
-
import('./daemon/client.js')
|
|
49
|
-
|
|
48
|
+
import('./daemon/client.js')
|
|
49
|
+
.then(({ ensureDaemon }) => {
|
|
50
|
+
ensureDaemon(process.cwd(), url, { headed: options.headed })
|
|
51
|
+
.then((state) => {
|
|
50
52
|
console.log(`[Sweetlink] Daemon ready on port ${state.port} (target: ${url})`);
|
|
51
|
-
})
|
|
53
|
+
})
|
|
54
|
+
.catch((err) => {
|
|
52
55
|
console.warn('[Sweetlink] Daemon auto-start failed:', err instanceof Error ? err.message : err);
|
|
53
56
|
});
|
|
54
|
-
})
|
|
57
|
+
})
|
|
58
|
+
.catch(() => {
|
|
55
59
|
console.warn('[Sweetlink] Daemon module not available');
|
|
56
60
|
});
|
|
57
61
|
}
|
package/dist/vite.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite.js","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAwB5C;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,UAAkC,EAAE;IAC5D,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,KAAK,EAAE,OAAO,EAAE,uBAAuB;QAEvC,eAAe,CAAC,UAAU;YACxB,4CAA4C;YAC5C,UAAU,CAAC,UAAU,EAAE,IAAI,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE;gBAClD,0DAA0D;gBAC1D,MAAM,cAAc,EAAE,CAAC;gBAEvB,MAAM,OAAO,GAAG,UAAU,CAAC,UAAU,EAAE,OAAO,EAAE,CAAC;gBACjD,MAAM,QAAQ,GAAG,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;gBAE9E,gEAAgE;gBAChE,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,IAAI,QAAQ,GAAG,cAAc,CAAC;gBAEzD,aAAa,CAAC;oBACZ,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE,QAAQ;oBACjB,OAAO,EAAE,CAAC,UAAU,EAAE,EAAE;wBACtB,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;4BAC1B,OAAO,CAAC,GAAG,CAAC,0BAA0B,UAAU,KAAK,MAAM,cAAc,CAAC,CAAC;wBAC7E,CAAC;oBACH,CAAC;iBACF,CAAC,CAAC;gBAEH,OAAO,CAAC,GAAG,CAAC,uDAAuD,QAAQ,GAAG,CAAC,CAAC;gBAEhF,kCAAkC;gBAClC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;oBACnB,MAAM,GAAG,GAAG,oBAAoB,QAAQ,EAAE,CAAC;oBAC3C,MAAM,CAAC,oBAAoB,CAAC,
|
|
1
|
+
{"version":3,"file":"vite.js","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAwB5C;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,UAAkC,EAAE;IAC5D,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,KAAK,EAAE,OAAO,EAAE,uBAAuB;QAEvC,eAAe,CAAC,UAAU;YACxB,4CAA4C;YAC5C,UAAU,CAAC,UAAU,EAAE,IAAI,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE;gBAClD,0DAA0D;gBAC1D,MAAM,cAAc,EAAE,CAAC;gBAEvB,MAAM,OAAO,GAAG,UAAU,CAAC,UAAU,EAAE,OAAO,EAAE,CAAC;gBACjD,MAAM,QAAQ,GAAG,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;gBAE9E,gEAAgE;gBAChE,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,IAAI,QAAQ,GAAG,cAAc,CAAC;gBAEzD,aAAa,CAAC;oBACZ,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE,QAAQ;oBACjB,OAAO,EAAE,CAAC,UAAU,EAAE,EAAE;wBACtB,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;4BAC1B,OAAO,CAAC,GAAG,CAAC,0BAA0B,UAAU,KAAK,MAAM,cAAc,CAAC,CAAC;wBAC7E,CAAC;oBACH,CAAC;iBACF,CAAC,CAAC;gBAEH,OAAO,CAAC,GAAG,CAAC,uDAAuD,QAAQ,GAAG,CAAC,CAAC;gBAEhF,kCAAkC;gBAClC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;oBACnB,MAAM,GAAG,GAAG,oBAAoB,QAAQ,EAAE,CAAC;oBAC3C,MAAM,CAAC,oBAAoB,CAAC;yBACzB,IAAI,CAAC,CAAC,EAAE,YAAY,EAAE,EAAE,EAAE;wBACzB,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;6BACzD,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;4BACd,OAAO,CAAC,GAAG,CAAC,oCAAoC,KAAK,CAAC,IAAI,aAAa,GAAG,GAAG,CAAC,CAAC;wBACjF,CAAC,CAAC;6BACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;4BACb,OAAO,CAAC,IAAI,CACV,uCAAuC,EACvC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CACzC,CAAC;wBACJ,CAAC,CAAC,CAAC;oBACP,CAAC,CAAC;yBACD,KAAK,CAAC,GAAG,EAAE;wBACV,OAAO,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;oBAC1D,CAAC,CAAC,CAAC;gBACP,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;QAED,QAAQ;YACN,oEAAoE;YACpE,cAAc,EAAE,CAAC;QACnB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,iCAAiC;AACjC,eAAe,SAAS,CAAC"}
|