diff --git a/IPython/html/notebookapp.py b/IPython/html/notebookapp.py index e8e920f34..f2d1c53b2 100644 --- a/IPython/html/notebookapp.py +++ b/IPython/html/notebookapp.py @@ -761,6 +761,15 @@ class NotebookApp(BaseIPythonApplication): proto = 'https' if self.certfile else 'http' return "%s://%s:%i%s" % (proto, ip, self.port, self.base_url) + def init_terminals(self): + try: + from .terminal import initialize + initialize(self.web_app) + self.web_app.terminals_available = True + except ImportError as e: + self.log.info("Terminals not available (error was %s)", e) + self.web_app.terminals_available = False + def init_signal(self): if not sys.platform.startswith('win'): signal.signal(signal.SIGINT, self._handle_sigint) @@ -840,6 +849,7 @@ class NotebookApp(BaseIPythonApplication): self.init_configurables() self.init_components() self.init_webapp() + self.init_terminals() self.init_signal() def cleanup_kernels(self): diff --git a/IPython/html/static/notebook/less/terminal.less b/IPython/html/static/notebook/less/terminal.less new file mode 100644 index 000000000..f20cefa9e --- /dev/null +++ b/IPython/html/static/notebook/less/terminal.less @@ -0,0 +1,17 @@ +.terminal { + float: left; + border: black solid 5px; + font-family: "DejaVu Sans Mono", "Liberation Mono", monospace; + font-size: 11px; + color: white; + background: black; +} + +.terminal-cursor { + color: black; + background: white; +} + +#terminado-container { + margin: 8px; +} diff --git a/IPython/html/static/style/style.less b/IPython/html/static/style/style.less index 0f8c37d14..ad5130dc9 100644 --- a/IPython/html/static/style/style.less +++ b/IPython/html/static/style/style.less @@ -26,4 +26,4 @@ // notebook @import "../notebook/less/style.less"; - +@import "../notebook/less/terminal.less"; diff --git a/IPython/html/static/style/style.min.css b/IPython/html/static/style/style.min.css index 8905b2f9b..bdddd754b 100644 --- a/IPython/html/static/style/style.min.css +++ b/IPython/html/static/style/style.min.css @@ -10483,4 +10483,19 @@ span#autosave_status { -ms-transform: rotate(45deg); -o-transform: rotate(45deg); } +.terminal { + float: left; + border: black solid 5px; + font-family: "DejaVu Sans Mono", "Liberation Mono", monospace; + font-size: 11px; + color: white; + background: black; +} +.terminal-cursor { + color: black; + background: white; +} +#terminado-container { + margin: 8px; +} /*# sourceMappingURL=../style/style.min.css.map */ \ No newline at end of file diff --git a/IPython/html/static/terminal/js/main.js b/IPython/html/static/terminal/js/main.js new file mode 100644 index 000000000..12c4e3767 --- /dev/null +++ b/IPython/html/static/terminal/js/main.js @@ -0,0 +1,53 @@ +// Copyright (c) IPython Development Team. +// Distributed under the terms of the Modified BSD License. + +require([ + 'jquery', + 'termjs', + 'base/js/utils', + 'base/js/page', + 'terminal/js/terminado', + 'custom/custom', +], function( + $, + termjs, + utils, + page, + terminado + ){ + page = new page.Page(); + // Test size: 25x80 + var termRowHeight = function(){ return 1.00 * $("#dummy-screen")[0].offsetHeight / 25;}; + // 1.02 here arrived at by trial and error to make the spacing look right + var termColWidth = function() { return 1.02 * $("#dummy-screen-rows")[0].offsetWidth / 80;}; + + var base_url = utils.get_body_data('baseUrl'); + var ws_path = utils.get_body_data('wsPath'); + var ws_url = location.protocol.replace('http', 'ws') + "//" + location.host + + base_url + ws_path; + + var header = $("#header")[0] + function calculate_size() { + height = window.innerHeight - header.offsetHeight; + width = window.innerWidth; + var rows = Math.min(1000, Math.max(20, Math.floor(height/termRowHeight())-1)); + var cols = Math.min(1000, Math.max(40, Math.floor(width/termColWidth())-1)); + console.log("resize to :", rows , 'rows by ', cols, 'columns'); + return {rows: rows, cols: cols}; + } + + page.show_header(); + + size = calculate_size(); + var terminal = terminado.make_terminal($("#terminado-container")[0], size, ws_url); + + page.show_site(); + + window.onresize = function() { + var geom = calculate_size(); + terminal.term.resize(geom.cols, geom.rows); + terminal.socket.send(JSON.stringify(["set_size", geom.rows, geom.cols, + window.innerHeight, window.innerWidth])); + }; + +}); diff --git a/IPython/html/static/terminal/js/terminado.js b/IPython/html/static/terminal/js/terminado.js new file mode 100644 index 000000000..2fda37a8c --- /dev/null +++ b/IPython/html/static/terminal/js/terminado.js @@ -0,0 +1,39 @@ +define ([], function() { + function make_terminal(element, size, ws_url) { + var ws = new WebSocket(ws_url); + var term = new Terminal({ + cols: size.cols, + rows: size.rows, + screenKeys: true, + useStyle: false + }); + ws.onopen = function(event) { + ws.send(JSON.stringify(["set_size", size.rows, size.cols, + window.innerHeight, window.innerWidth])); + term.on('data', function(data) { + ws.send(JSON.stringify(['stdin', data])); + }); + + term.on('title', function(title) { + document.title = title; + }); + + term.open(element); + + ws.onmessage = function(event) { + json_msg = JSON.parse(event.data); + switch(json_msg[0]) { + case "stdout": + term.write(json_msg[1]); + break; + case "disconnect": + term.write("\r\n\r\n[CLOSED]\r\n"); + break; + } + }; + }; + return {socket: ws, term: term}; + } + + return {make_terminal: make_terminal}; +}); diff --git a/IPython/html/templates/page.html b/IPython/html/templates/page.html index cc8adb1b2..b3c67b821 100644 --- a/IPython/html/templates/page.html +++ b/IPython/html/templates/page.html @@ -29,6 +29,7 @@ highlight: 'components/highlight.js/build/highlight.pack', moment: "components/moment/moment", codemirror: 'components/codemirror', + termjs: "components/term.js/src/term" }, shim: { underscore: { diff --git a/IPython/html/templates/terminal.html b/IPython/html/templates/terminal.html new file mode 100644 index 000000000..aa32d0479 --- /dev/null +++ b/IPython/html/templates/terminal.html @@ -0,0 +1,57 @@ +{% extends "page.html" %} + +{% block title %}{{page_title}}{% endblock %} + +{% block params %} + +data-base-url="{{base_url}}" +data-ws-path="{{ws_path}}" + +{% endblock %} + + +{% block site %} + +
+ +{% endblock %} + +{% block script %} + + + +0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+0
+1
+2
+3
+01234567890123456789012345678901234567890123456789012345678901234567890123456789
+
+