Ext.define('PVE.qemu.Monitor', { extend: 'Ext.panel.Panel', alias: 'widget.pveQemuMonitor', // start to trim saved command output once there are *both*, more than `commandLimit` commands // executed and the total of saved in+output is over `lineLimit` lines; repeat by dropping one // full command output until either condition is false again commandLimit: 10, lineLimit: 5000, initComponent: function() { var me = this; var nodename = me.pveSelNode.data.node; if (!nodename) { throw "no node name specified"; } var vmid = me.pveSelNode.data.vmid; if (!vmid) { throw "no VM ID specified"; } var history = []; var histNum = -1; let commands = []; var textbox = Ext.createWidget('panel', { region: 'center', xtype: 'panel', autoScroll: true, border: true, margins: '5 5 5 5', bodyStyle: 'font-family: monospace;', }); var scrollToEnd = function() { var el = textbox.getTargetEl(); var dom = Ext.getDom(el); var clientHeight = dom.clientHeight; // BrowserBug: clientHeight reports 0 in IE9 StrictMode // Instead we are using offsetHeight and hardcoding borders if (Ext.isIE9 && Ext.isStrict) { clientHeight = dom.offsetHeight + 2; } dom.scrollTop = dom.scrollHeight - clientHeight; }; var refresh = function() { textbox.update(`
${commands.flat(2).join('\n')}
`); scrollToEnd(); }; let recordInput = line => { commands.push([line]); // drop oldest commands and their output until we're not over both limits anymore while (commands.length > me.commandLimit && commands.flat(2).length > me.lineLimit) { commands.shift(); } }; let addResponse = lines => commands[commands.length - 1].push(lines); var executeCmd = function(cmd) { recordInput("# " + Ext.htmlEncode(cmd), true); if (cmd) { history.unshift(cmd); if (history.length > 20) { history.splice(20); } } histNum = -1; refresh(); Proxmox.Utils.API2Request({ params: { command: cmd }, url: '/nodes/' + nodename + '/qemu/' + vmid + "/monitor", method: 'POST', waitMsgTarget: me, success: function(response, opts) { var res = response.result.data; addResponse(res.split('\n').map(line => Ext.htmlEncode(line))); refresh(); }, failure: function(response, opts) { Ext.Msg.alert('Error', response.htmlStatus); }, }); }; Ext.apply(me, { layout: { type: 'border' }, border: false, items: [ textbox, { region: 'south', margins: '0 5 5 5', border: false, xtype: 'textfield', name: 'cmd', value: '', fieldStyle: 'font-family: monospace;', allowBlank: true, listeners: { afterrender: function(f) { f.focus(false); recordInput("Type 'help' for help."); refresh(); }, specialkey: function(f, e) { var key = e.getKey(); switch (key) { case e.ENTER: var cmd = f.getValue(); f.setValue(''); executeCmd(cmd); break; case e.PAGE_UP: textbox.scrollBy(0, -0.9*textbox.getHeight(), false); break; case e.PAGE_DOWN: textbox.scrollBy(0, 0.9*textbox.getHeight(), false); break; case e.UP: if (histNum + 1 < history.length) { f.setValue(history[++histNum]); } e.preventDefault(); break; case e.DOWN: if (histNum > 0) { f.setValue(history[--histNum]); } e.preventDefault(); break; default: break; } }, }, }, ], listeners: { show: function() { var field = me.query('textfield[name="cmd"]')[0]; field.focus(false, true); }, }, }); me.callParent(); }, });