// ReplayPlayer drives xterm.js playback of recorded session events. function ReplayPlayer(containerId, sessionId) { this.terminal = new Terminal({ cols: 80, rows: 24, convertEol: true, disableStdin: true, theme: { background: '#000000', foreground: '#ffffff' } }); this.terminal.open(document.getElementById(containerId)); this.sessionId = sessionId; this.events = []; this.index = 0; this.speed = 1; this.timers = []; this.playing = false; // Fetch events immediately. var self = this; fetch('/api/sessions/' + sessionId + '/events') .then(function(r) { return r.json(); }) .then(function(data) { self.events = data.events || []; }); } ReplayPlayer.prototype.play = function() { if (this.playing) return; if (this.events.length === 0) return; this.playing = true; this._schedule(); }; ReplayPlayer.prototype.pause = function() { this.playing = false; for (var i = 0; i < this.timers.length; i++) { clearTimeout(this.timers[i]); } this.timers = []; }; ReplayPlayer.prototype.reset = function() { this.pause(); this.index = 0; this.terminal.reset(); }; ReplayPlayer.prototype.setSpeed = function(speed) { this.speed = speed; if (this.playing) { this.pause(); this.play(); } }; ReplayPlayer.prototype._schedule = function() { var self = this; var baseT = this.index < this.events.length ? this.events[this.index].t : 0; for (var i = this.index; i < this.events.length; i++) { (function(idx) { var evt = self.events[idx]; var delay = (evt.t - baseT) / self.speed; var timer = setTimeout(function() { if (!self.playing) return; // Only write output events (d=1) to terminal; input is echoed in output. if (evt.d === 1) { var raw = atob(evt.data); self.terminal.write(raw); } self.index = idx + 1; if (self.index >= self.events.length) { self.playing = false; } }, delay); self.timers.push(timer); })(i); } };