From 5f3dbf9890a2addcd7396986abb8c20e0fe9aa22 Mon Sep 17 00:00:00 2001 From: Grant Nestor Date: Thu, 27 Apr 2017 13:31:08 -0700 Subject: [PATCH 01/28] Use different favicons for different components (notebook, terminal, file) --- notebook/static/base/images/favicon-file.ico | Bin 0 -> 1150 bytes notebook/static/base/images/favicon-notebook.ico | Bin 0 -> 1150 bytes notebook/static/base/images/favicon-terminal.ico | Bin 0 -> 1150 bytes notebook/templates/edit.html | 2 ++ notebook/templates/notebook.html | 2 ++ notebook/templates/terminal.html | 2 ++ 6 files changed, 6 insertions(+) create mode 100644 notebook/static/base/images/favicon-file.ico create mode 100644 notebook/static/base/images/favicon-notebook.ico create mode 100644 notebook/static/base/images/favicon-terminal.ico diff --git a/notebook/static/base/images/favicon-file.ico b/notebook/static/base/images/favicon-file.ico new file mode 100644 index 0000000000000000000000000000000000000000..8167018cd005ff4a24d8287c620539e23c69aac9 GIT binary patch literal 1150 zcmeHGu?@m75Ih7usc1=q#Ar}3g9{1u5G9Vze%<}~9qSED z_*E4+*VxQ}1%Nd{icIFY0Mu3#9X}t*S|qcPXdmjkckEEPihH)7%2nLouWvxzmEyD; z_Z}H)ul%aFKJ%B}O15MDW@wjZiljJOvMuwx&zB)}Yvgo9FK{i_b#L8MN~r!Z`{GSC cvToVotH@v7c?w*vZ?LC+B96p?o;l#&8_znXx&QzG literal 0 HcmV?d00001 diff --git a/notebook/static/base/images/favicon-notebook.ico b/notebook/static/base/images/favicon-notebook.ico new file mode 100644 index 0000000000000000000000000000000000000000..4537e2d989843ae1a96a0548aa0a7066a22e2698 GIT binary patch literal 1150 zcmd6kJxT;Y5QX2WpqU$)xPggaBK}}1>McBhsCWizK|@m`1#e)g;lhS0Vx(Z8XsF1B zf@t|QGc63$HWTg)-xU2`y`G|5YVo$);ya`5F=+Oz;Y!U7Un(3%^tjC*nw^$zt$lCLQe;7 z68RSTXM;T!>A8mbS(zU-$nVo*u$!1+qs)I$$1A@12wJ$KD_DZdiXRr{6L1H)bNs;% z_xMoD7kliL`IDM&zEXNy)YY0_vYhz#zuJz@P!dKp~(AL>x#lFaYILfs!CRtU&#LehmNA|NqHQ z|NlP@`~yXO{r??6b?qo(q;Ts0O91s>q0kHwp#F0dnjr+#{|<-?NpU`0ZTkWVxAGzYSw`z#PH=!+V$+80G`{{Xl#ih#x~Shz$}0 J$-&gZXaKG5Ui|<7 literal 0 HcmV?d00001 diff --git a/notebook/templates/edit.html b/notebook/templates/edit.html index 9cda9fbc7..8c69952be 100644 --- a/notebook/templates/edit.html +++ b/notebook/templates/edit.html @@ -2,6 +2,8 @@ {% block title %}{{page_title}}{% endblock %} +{% block favicon %}{% endblock %} + {% block stylesheet %} diff --git a/notebook/templates/notebook.html b/notebook/templates/notebook.html index 53e5ebbb4..dfa9610e6 100644 --- a/notebook/templates/notebook.html +++ b/notebook/templates/notebook.html @@ -1,5 +1,7 @@ {% extends "page.html" %} +{% block favicon %}{% endblock %} + {% block stylesheet %} {% if mathjax_url %} diff --git a/notebook/templates/terminal.html b/notebook/templates/terminal.html index 945e19070..0a3cfd05c 100644 --- a/notebook/templates/terminal.html +++ b/notebook/templates/terminal.html @@ -2,6 +2,8 @@ {% block title %}{{page_title}}{% endblock %} +{% block favicon %}{% endblock %} + {% block bodyclasses %}terminal-app {{super()}}{% endblock %} {% block params %} From 3e01e128f5082e70e665b446529adfb06912bd7e Mon Sep 17 00:00:00 2001 From: Grant Nestor Date: Thu, 27 Apr 2017 13:31:30 -0700 Subject: [PATCH 02/28] Use animated favicon for kernel busy indicator --- .../static/base/images/favicon-busy-1.ico | Bin 0 -> 1150 bytes .../static/base/images/favicon-busy-2.ico | Bin 0 -> 1150 bytes .../static/base/images/favicon-busy-3.ico | Bin 0 -> 1150 bytes notebook/static/base/images/favicon-busy.ico | Bin 32038 -> 0 bytes notebook/static/base/js/utils.js | 14 ++++++- .../static/notebook/js/notificationarea.js | 36 ++++++++++-------- 6 files changed, 34 insertions(+), 16 deletions(-) create mode 100644 notebook/static/base/images/favicon-busy-1.ico create mode 100644 notebook/static/base/images/favicon-busy-2.ico create mode 100644 notebook/static/base/images/favicon-busy-3.ico delete mode 100644 notebook/static/base/images/favicon-busy.ico diff --git a/notebook/static/base/images/favicon-busy-1.ico b/notebook/static/base/images/favicon-busy-1.ico new file mode 100644 index 0000000000000000000000000000000000000000..5b46a822610b0f49bad2579cbd0ff8bfbb80d0fd GIT binary patch literal 1150 zcmb_byH3L}6g^dGo28;tXBH$Rx;Ed$575>80V^QE&SR*=53qHun5k4Lm9n-QOtR82Nz_X^HRP`gV>i zs3~bABf?&*Z>V1;{4?rb3H^!sbwXcIe@y5(^@12TVw5woBl3p!pD=RAn0I1S(MEUf!g|U7v-&*>0|Gw9aAF6di9sM_PubX2Yvi@>L zG=DBG)~52O^%L_sK2<+@2kN}}8#OYZ9`u)Ep9UFL3Y z=FNL|``!U5p2Z?~Eu)nIN&u<^DKduV4WL$`DERxK^aD!kNZg(E<8rvYl2GY=ME#w! zw(*bYUlUyqR^&$kf0O!|$p6F$r}T+v{%D_s(tXA(h+IO;+Hiw}^DUIlGIm023Hu!z zu4hEnXG?hFpECBlm-9%!y#Ke?J`a;Ru7&K4-)nO;T-Fz-wDhkN(A(5Q{ge5r1^jjD yq4|-2z`w_)kM8-6n|-?n?i}71_Y3S4mTBO?X|p)4fLjl^7Tyr;a(P2M;(Y>vrxNA> literal 0 HcmV?d00001 diff --git a/notebook/static/base/images/favicon-busy-3.ico b/notebook/static/base/images/favicon-busy-3.ico new file mode 100644 index 0000000000000000000000000000000000000000..b5edce573303a1d750106afa0a3b17bb2e6f2fb7 GIT binary patch literal 1150 zcmb_bv1$TA5PiXDk|Jo8+9n_Y3x)Vi`2fLkKEXB-v{wTWzrfB?k~)Dv1Y6U^YVwA4 zPh|JbExhC1&b>Ep=D3{&E_l~!;IodWOW+PbgCIr5@mv6E_Y@6hzeINs)kOS7`=`~2 zd##}8S3>{KXm95qCHRL0{!W6wlk0ci@XQ)-L^;c|*l{lzmludum^&s`M4M>s<0;ol zo-N~@f5O}k!u*DJ;+078+xO3pa$U_Cxk1(^7KqyaTNAf|Y@E{`1I-hnwtrp!8myu{ z>P7o!^Yak+JR#boUNk>;2CjX2u!`PLkI!$~?ECW-uMspnP4L8GQ3AfNj`7zbAWlw`wvCu45I)5 literal 0 HcmV?d00001 diff --git a/notebook/static/base/images/favicon-busy.ico b/notebook/static/base/images/favicon-busy.ico deleted file mode 100644 index 85f9995a47b5d870432cdef19a388b33d5cdfb3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32038 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 R{Z=SPe-GgH^H)m({|`3Z&RPHf diff --git a/notebook/static/base/js/utils.js b/notebook/static/base/js/utils.js index b70b3ceb6..474b03426 100644 --- a/notebook/static/base/js/utils.js +++ b/notebook/static/base/js/utils.js @@ -1051,6 +1051,17 @@ define([ fn(); } } + + var change_favicon = function (src) { + var link = document.createElement('link'), + oldLink = document.getElementById('favicon'); + link.id = 'favicon'; + link.type = 'image/x-icon'; + link.rel = 'shortcut icon'; + link.href = utils.url_path_join(utils.get_body_data('baseUrl'), src); + if (oldLink) document.head.removeChild(oldLink); + document.head.appendChild(link); + }; var utils = { throttle: throttle, @@ -1101,7 +1112,8 @@ define([ format_datetime: format_datetime, datetime_sort_helper: datetime_sort_helper, dnd_contain_file: dnd_contain_file, - _ansispan:_ansispan + _ansispan:_ansispan, + change_favicon: change_favicon }; return utils; diff --git a/notebook/static/notebook/js/notificationarea.js b/notebook/static/notebook/js/notificationarea.js index 121fc6be2..89abc33e0 100644 --- a/notebook/static/notebook/js/notificationarea.js +++ b/notebook/static/notebook/js/notificationarea.js @@ -40,6 +40,23 @@ define([ var $modal_ind_icon = $("#modal_indicator"); var $readonly_ind_icon = $('#readonly-indicator'); var $body = $('body'); + var interval = 0; + + var set_busy_favicon = function(on) { + if (on && !interval) { + var i = 0; + var icons = ['favicon-busy-1.ico', 'favicon-busy-3.ico', 'favicon-busy-3.ico']; + interval = setInterval(function() { + var icon = icons[i % 3]; + utils.change_favicon('/static/base/images/' + icon); + i += 1; + }, 300); + } else { + clearInterval(interval); + utils.change_favicon('/static/base/images/favicon-notebook.ico'); + interval = 0; + } + }; // Listen for the notebook loaded event. Set readonly indicator. this.events.on('notebook_loaded.Notebook', function() { @@ -244,41 +261,30 @@ define([ knw.danger(short, undefined, showMsg); }); - var change_favicon = function (src) { - var link = document.createElement('link'), - oldLink = document.getElementById('favicon'); - link.id = 'favicon'; - link.type = 'image/x-icon'; - link.rel = 'shortcut icon'; - link.href = utils.url_path_join(utils.get_body_data('baseUrl'), src); - if (oldLink) document.head.removeChild(oldLink); - document.head.appendChild(link); - }; - this.events.on('kernel_starting.Kernel kernel_created.Session', function () { // window.document.title='(Starting) '+window.document.title; $kernel_ind_icon.attr('class','kernel_busy_icon').attr('title','Kernel Busy'); knw.set_message("Kernel starting, please wait..."); - change_favicon('/static/base/images/favicon-busy.ico'); + set_busy_favicon(true); }); this.events.on('kernel_ready.Kernel', function () { // that.save_widget.update_document_title(); $kernel_ind_icon.attr('class','kernel_idle_icon').attr('title','Kernel Idle'); knw.info("Kernel ready", 500); - change_favicon('/static/base/images/favicon.ico'); + set_busy_favicon(false); }); this.events.on('kernel_idle.Kernel', function () { // that.save_widget.update_document_title(); $kernel_ind_icon.attr('class','kernel_idle_icon').attr('title','Kernel Idle'); - change_favicon('/static/base/images/favicon.ico'); + set_busy_favicon(false); }); this.events.on('kernel_busy.Kernel', function () { // window.document.title='(Busy) '+window.document.title; $kernel_ind_icon.attr('class','kernel_busy_icon').attr('title','Kernel Busy'); - change_favicon('/static/base/images/favicon-busy.ico'); + set_busy_favicon(true); }); this.events.on('spec_match_found.Kernel', function (evt, data) { From 5a4b968e3b7f49bdc8abb6e107ab20c2a65c206d Mon Sep 17 00:00:00 2001 From: Min RK Date: Fri, 28 Apr 2017 12:41:47 +0200 Subject: [PATCH 03/28] sys.stdin can be None in which case we can't check if it's a tty or read from it --- notebook/notebookapp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebook/notebookapp.py b/notebook/notebookapp.py index 839102cdd..3985316f0 100755 --- a/notebook/notebookapp.py +++ b/notebook/notebookapp.py @@ -1206,7 +1206,7 @@ class NotebookApp(JupyterApp): log("Terminals not available (error was %s)", e) def init_signal(self): - if not sys.platform.startswith('win') and sys.stdin.isatty(): + if not sys.platform.startswith('win') and sys.stdin and sys.stdin.isatty(): signal.signal(signal.SIGINT, self._handle_sigint) signal.signal(signal.SIGTERM, self._signal_stop) if hasattr(signal, 'SIGUSR1'): From 122cbf678510c5c11901492afefbba1269aa1537 Mon Sep 17 00:00:00 2001 From: Min RK Date: Tue, 2 May 2017 11:04:18 +0200 Subject: [PATCH 04/28] typo in log command don't wrap format-arguments in a tuple --- notebook/services/contents/filemanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebook/services/contents/filemanager.py b/notebook/services/contents/filemanager.py index 92a135b4f..a05253ac3 100644 --- a/notebook/services/contents/filemanager.py +++ b/notebook/services/contents/filemanager.py @@ -281,7 +281,7 @@ class FileContentsManager(FileManagerMixin, ContentsManager): if e.errno == errno.ENOENT: self.log.warning("%s doesn't exist", os_path) else: - self.log.warning("Error stat-ing %s: %s", (os_path, e)) + self.log.warning("Error stat-ing %s: %s", os_path, e) continue if not stat.S_ISREG(st.st_mode) and not stat.S_ISDIR(st.st_mode): From a051f24e0d7ae5b98b0ae588401d07f4a604d993 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Wed, 3 May 2017 13:00:13 +0100 Subject: [PATCH 05/28] Don't call sys_info() at import time --- notebook/base/handlers.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/notebook/base/handlers.py b/notebook/base/handlers.py index 2c48d5cc1..cf3e183f0 100755 --- a/notebook/base/handlers.py +++ b/notebook/base/handlers.py @@ -39,7 +39,12 @@ from notebook.services.security import csp_report_uri #----------------------------------------------------------------------------- non_alphanum = re.compile(r'[^A-Za-z0-9]') -sys_info = json.dumps(get_sys_info()) +_sys_info_cache = None +def json_sys_info(): + global _sys_info_cache + if _sys_info_cache is None: + _sys_info_cache = json.dumps(get_sys_info()) + return _sys_info_cache def log(): if Application.initialized(): @@ -357,7 +362,7 @@ class IPythonHandler(AuthenticatedHandler): login_available=self.login_available, token_available=bool(self.token or self.one_time_token), static_url=self.static_url, - sys_info=sys_info, + sys_info=json_sys_info(), contents_js_source=self.contents_js_source, version_hash=self.version_hash, ignore_minified_js=self.ignore_minified_js, From 7454e8c954e21a9e9bfc667ab9cd2c4d6c510439 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Wed, 3 May 2017 13:04:21 +0100 Subject: [PATCH 06/28] Don't use shell when getting git commit ID --- notebook/_sysinfo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notebook/_sysinfo.py b/notebook/_sysinfo.py index ea237bbd2..bc976e282 100644 --- a/notebook/_sysinfo.py +++ b/notebook/_sysinfo.py @@ -46,10 +46,10 @@ def pkg_commit_hash(pkg_path): while cur_path != par_path: cur_path = par_path if p.exists(p.join(cur_path, '.git')): - proc = subprocess.Popen('git rev-parse --short HEAD', + proc = subprocess.Popen(['git', 'rev-parse', '--short', 'HEAD'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, - cwd=pkg_path, shell=True) + cwd=pkg_path) repo_commit, _ = proc.communicate() if repo_commit: return 'repository', repo_commit.strip().decode('ascii') From 83e1a23ce3d5952f38cee884835f1aa66a2ee0fd Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Wed, 3 May 2017 13:06:58 +0100 Subject: [PATCH 07/28] Catch errors calling git for commit ID Closes gh-2468 --- notebook/_sysinfo.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/notebook/_sysinfo.py b/notebook/_sysinfo.py index bc976e282..4e6a36626 100644 --- a/notebook/_sysinfo.py +++ b/notebook/_sysinfo.py @@ -46,11 +46,15 @@ def pkg_commit_hash(pkg_path): while cur_path != par_path: cur_path = par_path if p.exists(p.join(cur_path, '.git')): - proc = subprocess.Popen(['git', 'rev-parse', '--short', 'HEAD'], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - cwd=pkg_path) - repo_commit, _ = proc.communicate() + try: + proc = subprocess.Popen(['git', 'rev-parse', '--short', 'HEAD'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=pkg_path) + repo_commit, _ = proc.communicate() + except OSError: + repo_commit = None + if repo_commit: return 'repository', repo_commit.strip().decode('ascii') else: From 1d7e6007ba27256649e357ee62aab19e2566e173 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Thu, 4 May 2017 12:34:52 +0100 Subject: [PATCH 08/28] Avoid double-encoding item path for view button Closes gh-2476 --- notebook/static/tree/js/notebooklist.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebook/static/tree/js/notebooklist.js b/notebook/static/tree/js/notebooklist.js index 4164362d9..178c03a1c 100644 --- a/notebook/static/tree/js/notebooklist.js +++ b/notebook/static/tree/js/notebooklist.js @@ -1026,7 +1026,7 @@ define([ var item_path = utils.encode_uri_components(item.path); // Handle HTML files differently var item_type = item_path.endsWith('.html') ? 'view' : 'files'; - window.open(utils.url_path_join(that.base_url, item_type, utils.encode_uri_components(item_path)), IPython._target); + window.open(utils.url_path_join(that.base_url, item_type, item_path), IPython._target); }); }; From 0d124e054e261fdface607a4e21afd176bfb729b Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Thu, 4 May 2017 12:43:12 +0100 Subject: [PATCH 09/28] Catch trying to rename to an empty filename Act as if the OK button is diabled. Closes gh-2475 --- notebook/static/edit/js/savewidget.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/notebook/static/edit/js/savewidget.js b/notebook/static/edit/js/savewidget.js index 31d7cc65d..7df4ed4d6 100644 --- a/notebook/static/edit/js/savewidget.js +++ b/notebook/static/edit/js/savewidget.js @@ -79,6 +79,11 @@ define([ class: "btn-primary", click: function () { var new_name = d.find('input').val(); + if (!new_name) { + // Reset the message + d.find('.rename-message').text("Enter a new filename:"); + return false; + } d.find('.rename-message').text("Renaming..."); d.find('input[type="text"]').prop('disabled', true); that.editor.rename(new_name).then( From 9d839d5052cce995e851df01c810a47b3efc85d5 Mon Sep 17 00:00:00 2001 From: Grant Nestor Date: Thu, 4 May 2017 14:31:49 -0700 Subject: [PATCH 10/28] Fix text baseline alignment in save widget --- notebook/static/notebook/less/savewidget.less | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/notebook/static/notebook/less/savewidget.less b/notebook/static/notebook/less/savewidget.less index fe37e1980..07b54621c 100644 --- a/notebook/static/notebook/less/savewidget.less +++ b/notebook/static/notebook/less/savewidget.less @@ -1,12 +1,13 @@ span.save_widget { - margin-top: 6px; - max-width: 100%; + margin-top: 4px; display: flex; + justify-content: flex-start; + align-items: baseline; + width: calc(~"100% - 210px"); span.filename { height: 1em; line-height: 1em; - padding: 3px; margin-left: @padding-large-horizontal; border: none; font-size: 146.5%; From a49397bf989194641fbbc0daf8e3cef954316c0a Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 11 May 2017 21:31:14 +0200 Subject: [PATCH 11/28] expose passthrough for WebSocketHandler.get_compression_options adds `NotebookApp.websocket_compression_options` configurable, with a default of None (no compression) --- notebook/base/zmqhandlers.py | 3 +-- notebook/notebookapp.py | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/notebook/base/zmqhandlers.py b/notebook/base/zmqhandlers.py index 66e31eb18..8878948da 100644 --- a/notebook/base/zmqhandlers.py +++ b/notebook/base/zmqhandlers.py @@ -296,5 +296,4 @@ class AuthenticatedZMQStreamHandler(ZMQStreamHandler, IPythonHandler): self.session = Session(config=self.config) def get_compression_options(self): - # use deflate compress websocket - return {} + return self.settings.get('websocket_compression_options', None) diff --git a/notebook/notebookapp.py b/notebook/notebookapp.py index 3985316f0..2f139d10e 100755 --- a/notebook/notebookapp.py +++ b/notebook/notebookapp.py @@ -93,7 +93,7 @@ from jupyter_client.kernelspec import KernelSpecManager, NoSuchKernel, NATIVE_KE from jupyter_client.session import Session from nbformat.sign import NotebookNotary from traitlets import ( - Dict, Unicode, Integer, List, Bool, Bytes, Instance, + Any, Dict, Unicode, Integer, List, Bool, Bytes, Instance, TraitError, Type, Float, observe, default, validate ) from ipython_genutils import py3compat @@ -740,7 +740,18 @@ class NotebookApp(JupyterApp): tornado_settings = Dict(config=True, help="Supply overrides for the tornado.web.Application that the " "Jupyter notebook uses.") - + + websocket_compression_options = Any(None, config=True, + help=""" + Set the tornado compression options for websocket connections. + + This value will be returned from :meth:`WebSocketHandler.get_compression_options`. + None (default) will disable compression. + A dict (even an empty one) will enable compression. + + See the tornado docs for WebSocketHandler.get_compression_options for details. + """ + ) terminado_settings = Dict(config=True, help='Supply overrides for terminado. Currently only supports "shell_command".') @@ -1107,6 +1118,7 @@ class NotebookApp(JupyterApp): def init_webapp(self): """initialize tornado webapp and httpserver""" self.tornado_settings['allow_origin'] = self.allow_origin + self.tornado_settings['websocket_compression_options'] = self.websocket_compression_options if self.allow_origin_pat: self.tornado_settings['allow_origin_pat'] = re.compile(self.allow_origin_pat) self.tornado_settings['allow_credentials'] = self.allow_credentials From 96d5d963855a6acb75bd6da9afa047636a0531af Mon Sep 17 00:00:00 2001 From: 0x0L <0x0L@users.noreply.github.com> Date: Fri, 12 May 2017 21:03:24 +0200 Subject: [PATCH 12/28] Update fontawesome to latest version see https://github.com/jupyter/notebook/issues/2289 --- bower.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bower.json b/bower.json index e58ec9279..7ce891988 100644 --- a/bower.json +++ b/bower.json @@ -7,7 +7,7 @@ "bootstrap-tour": "0.9.0", "codemirror": "components/codemirror#~5.22.2", "es6-promise": "~1.0", - "font-awesome": "components/font-awesome#~4.2.0", + "font-awesome": "components/font-awesome#~4.7.0", "google-caja": "5669", "jquery": "components/jquery#~2.0", "jquery-typeahead": "~2.0.0", From 5af43fbae1374ffa6ce8fa20d00b4169570f4b13 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Sat, 13 May 2017 15:59:13 +0100 Subject: [PATCH 13/28] Fix test not to expect notebooks sorted PR gh-2281 removed the sorting step from the server. This can result in non-deterministic test failures, because the test checked against a list. --- notebook/services/contents/tests/test_contents_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notebook/services/contents/tests/test_contents_api.py b/notebook/services/contents/tests/test_contents_api.py index 439dffdfa..990b6ca51 100644 --- a/notebook/services/contents/tests/test_contents_api.py +++ b/notebook/services/contents/tests/test_contents_api.py @@ -249,8 +249,8 @@ class APITest(NotebookTestBase): self.assertEqual(nbnames, expected) nbs = notebooks_only(self.api.list('ordering').json()) - nbnames = [n['name'] for n in nbs] - expected = ['A.ipynb', 'b.ipynb', 'C.ipynb'] + nbnames = {n['name'] for n in nbs} + expected = {'A.ipynb', 'b.ipynb', 'C.ipynb'} self.assertEqual(nbnames, expected) def test_list_dirs(self): From 1550a04848bba4df6f269a5e170281a23225e394 Mon Sep 17 00:00:00 2001 From: Peter Parente Date: Tue, 16 May 2017 22:03:43 -0500 Subject: [PATCH 14/28] Add cull_busy, cull_connected options Additional logic to support culling or skipping kernels that are busy or that have connections. --- notebook/services/kernels/kernelmanager.py | 27 +++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/notebook/services/kernels/kernelmanager.py b/notebook/services/kernels/kernelmanager.py index e81148e10..9c852f973 100644 --- a/notebook/services/kernels/kernelmanager.py +++ b/notebook/services/kernels/kernelmanager.py @@ -15,7 +15,7 @@ from tornado.ioloop import IOLoop, PeriodicCallback from jupyter_client.session import Session from jupyter_client.multikernelmanager import MultiKernelManager -from traitlets import Dict, List, Unicode, TraitError, Integer, default, validate +from traitlets import Bool, Dict, List, Unicode, TraitError, Integer, default, validate from notebook.utils import to_os_path from notebook._tz import utcnow, isoformat @@ -71,6 +71,16 @@ class MappingKernelManager(MultiKernelManager): help="""The interval (in seconds) on which to check for idle kernels exceeding the cull timeout value.""" ) + cull_connected = Bool(True, config=True, + help="""Whether to consider culling kernels which have one or more connections. + Only effective if cull_interval is not 0.""" + ) + + cull_busy = Bool(True, config=True, + help="""Whether to consider culling kernels which are busy. + Only effective if cull_interval is not 0.""" + ) + #------------------------------------------------------------------------- # Methods for managing kernels and sessions #------------------------------------------------------------------------- @@ -273,6 +283,10 @@ class MappingKernelManager(MultiKernelManager): self.cull_kernels, 1000*self.cull_interval, loop) self.log.info("Culling kernels with idle durations > %s seconds at %s second intervals ...", self.cull_idle_timeout, self.cull_interval) + if self.cull_busy: + self.log.info("Culling kernels even if busy") + if self.cull_connected: + self.log.info("Culling kernels even with connected clients") self._culler_callback.start() self._initialized_culler = True @@ -294,8 +308,15 @@ class MappingKernelManager(MultiKernelManager): if kernel.last_activity is not None: dt_now = utcnow() dt_idle = dt_now - kernel.last_activity - if dt_idle > timedelta(seconds=self.cull_idle_timeout): # exceeds timeout, can be culled + # Compute idle properties + is_idle_time = dt_idle > timedelta(seconds=self.cull_idle_timeout) + is_idle_execute = self.cull_busy or (kernel.execution_state != 'busy') + connections = self._kernel_connections.get(kernel_id, 0) + is_idle_connected = self.cull_connected or not connections + # Cull the kernel if all three criteria are met + if (is_idle_time and is_idle_execute and is_idle_connected): idle_duration = int(dt_idle.total_seconds()) - self.log.warning("Culling kernel '%s' (%s) due to %s seconds of inactivity.", kernel.kernel_name, kernel_id, idle_duration) + self.log.warning("Culling '%s' kernel '%s' (%s) with %d connections due to %s seconds of inactivity.", + kernel.execution_state, kernel.kernel_name, kernel_id, connections, idle_duration) self.shutdown_kernel(kernel_id) From 6f05e770d81811447af1c5cc278fb8151f3836d7 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Wed, 17 May 2017 22:46:43 +0100 Subject: [PATCH 15/28] Use function to construct empty message in command palette This takes care of correctly escaping user input so it doesn't become HTML. --- notebook/static/notebook/js/commandpalette.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/notebook/static/notebook/js/commandpalette.js b/notebook/static/notebook/js/commandpalette.js index 73a79b2d9..8c18cb10f 100644 --- a/notebook/static/notebook/js/commandpalette.js +++ b/notebook/static/notebook/js/commandpalette.js @@ -164,7 +164,11 @@ define(function(require){ // now src is the right structure for typeahead input.typeahead({ - emptyTemplate: "No results found for
{{query}}
", + emptyTemplate: function(query) { + return $('
').text("Nao results found for").append( + $('
').text(query)
+            );
+          },
           maxItem: 1e3,
           minLength: 0,
           hint: true,

From ccba3bd5017637ea879fdc528c05cfe85f26ebbf Mon Sep 17 00:00:00 2001
From: Min RK 
Date: Wed, 17 May 2017 15:54:07 -0700
Subject: [PATCH 16/28] =?UTF-8?q?don=E2=80=99t=20start=20output=20areas=20?=
 =?UTF-8?q?collapsed?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

this sets metadata.collapsed = true metadata on cells with no output

causing churn in notebook files
---
 notebook/static/notebook/js/outputarea.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/notebook/static/notebook/js/outputarea.js b/notebook/static/notebook/js/outputarea.js
index 71c7b5726..be1ffa89d 100644
--- a/notebook/static/notebook/js/outputarea.js
+++ b/notebook/static/notebook/js/outputarea.js
@@ -77,7 +77,7 @@ define([
         this.prompt_overlay.addClass('out_prompt_overlay prompt');
         this.prompt_overlay.attr('title', 'click to expand output; double click to hide output');
         
-        this.collapse();
+        this.expand();
     };
 
     /**
@@ -976,6 +976,7 @@ define([
             this._display_id_targets = {};
             this.trusted = true;
             this.unscroll_area();
+            this.expand();
             return;
         }
     };

From 8413ba95928fca740cb176a74163fa50f3d3bb19 Mon Sep 17 00:00:00 2001
From: Min RK 
Date: Wed, 17 May 2017 15:54:07 -0700
Subject: [PATCH 17/28] =?UTF-8?q?don=E2=80=99t=20start=20output=20areas=20?=
 =?UTF-8?q?collapsed?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

this sets metadata.collapsed = true metadata on cells with no output

causing churn in notebook files
---
 notebook/static/notebook/js/outputarea.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/notebook/static/notebook/js/outputarea.js b/notebook/static/notebook/js/outputarea.js
index 71c7b5726..be1ffa89d 100644
--- a/notebook/static/notebook/js/outputarea.js
+++ b/notebook/static/notebook/js/outputarea.js
@@ -77,7 +77,7 @@ define([
         this.prompt_overlay.addClass('out_prompt_overlay prompt');
         this.prompt_overlay.attr('title', 'click to expand output; double click to hide output');
         
-        this.collapse();
+        this.expand();
     };
 
     /**
@@ -976,6 +976,7 @@ define([
             this._display_id_targets = {};
             this.trusted = true;
             this.unscroll_area();
+            this.expand();
             return;
         }
     };

From 560aa498841cc8e2e6edea5f5f3e86001a026dee Mon Sep 17 00:00:00 2001
From: Thomas Kluyver 
Date: Thu, 18 May 2017 12:41:15 +0100
Subject: [PATCH 18/28] Spelling correction

---
 notebook/static/notebook/js/commandpalette.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/notebook/static/notebook/js/commandpalette.js b/notebook/static/notebook/js/commandpalette.js
index 8c18cb10f..890179453 100644
--- a/notebook/static/notebook/js/commandpalette.js
+++ b/notebook/static/notebook/js/commandpalette.js
@@ -165,7 +165,7 @@ define(function(require){
 
         input.typeahead({
           emptyTemplate: function(query) {
-            return $('
').text("Nao results found for").append( + return $('
').text("No results found for").append( $('
').text(query)
             );
           },

From d9b4c99180ac2e5ab422fecf301db0cb467a5330 Mon Sep 17 00:00:00 2001
From: Peter Parente 
Date: Thu, 18 May 2017 10:11:51 -0500
Subject: [PATCH 19/28] Do not cull kernels doing work by default

---
 notebook/services/kernels/kernelmanager.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/notebook/services/kernels/kernelmanager.py b/notebook/services/kernels/kernelmanager.py
index 9c852f973..a431a0ec0 100644
--- a/notebook/services/kernels/kernelmanager.py
+++ b/notebook/services/kernels/kernelmanager.py
@@ -76,7 +76,7 @@ class MappingKernelManager(MultiKernelManager):
         Only effective if cull_interval is not 0."""
     )
 
-    cull_busy = Bool(True, config=True,
+    cull_busy = Bool(False, config=True,
         help="""Whether to consider culling kernels which are busy.
         Only effective if cull_interval is not 0."""
     )

From 576c3d9f9a66f395397318efd315f842d46ce9da Mon Sep 17 00:00:00 2001
From: Thomas Kluyver 
Date: Fri, 19 May 2017 12:15:33 +0100
Subject: [PATCH 20/28] Add /api/shutdown handler

---
 notebook/notebookapp.py       |  1 +
 notebook/services/shutdown.py | 15 +++++++++++++++
 2 files changed, 16 insertions(+)
 create mode 100644 notebook/services/shutdown.py

diff --git a/notebook/notebookapp.py b/notebook/notebookapp.py
index 3985316f0..2417b3c38 100755
--- a/notebook/notebookapp.py
+++ b/notebook/notebookapp.py
@@ -294,6 +294,7 @@ class NotebookWebApplication(web.Application):
         handlers.extend(load_handlers('services.nbconvert.handlers'))
         handlers.extend(load_handlers('services.kernelspecs.handlers'))
         handlers.extend(load_handlers('services.security.handlers'))
+        handlers.extend(load_handlers('services.shutdown'))
         
         handlers.append(
             (r"/nbextensions/(.*)", FileFindHandler, {
diff --git a/notebook/services/shutdown.py b/notebook/services/shutdown.py
new file mode 100644
index 000000000..78d1f2ad6
--- /dev/null
+++ b/notebook/services/shutdown.py
@@ -0,0 +1,15 @@
+"""HTTP handler to shut down the notebook server.
+"""
+from tornado import web, ioloop
+from notebook.base.handlers import IPythonHandler
+
+class ShutdownHandler(IPythonHandler):
+    @web.authenticated
+    def post(self):
+        self.log.info("Shutting down on /api/shutdown request.")
+        ioloop.IOLoop.current().stop()
+
+
+default_handlers = [
+    (r"/api/shutdown", ShutdownHandler),
+]

From 19d23e2de794c00d5a47bce71c963de87ba44151 Mon Sep 17 00:00:00 2001
From: "Steve (Gadget) Barnes" 
Date: Fri, 19 May 2017 17:25:22 +0100
Subject: [PATCH 21/28] Issue #2502: Add line break after The Jupyter Notebook
 is running at: (#2505)

* Issue #2502: Add line break after The Jupyter Notebook is running at:

* Revert "Issue #2502: Add line break after The Jupyter Notebook is running at:"

This reverts commit 3aa65e712006f2726ea57476dd8923753a13e4ef.

* Issue #2502: Reverted previous changes and redone without strip trailing spaces
---
 docs/source/security.rst | 3 ++-
 notebook/notebookapp.py  | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/docs/source/security.rst b/docs/source/security.rst
index 4c57ef0f5..f90f9d6da 100644
--- a/docs/source/security.rst
+++ b/docs/source/security.rst
@@ -31,7 +31,8 @@ When you start a notebook server with token authentication enabled (default),
 a token is generated to use for authentication.
 This token is logged to the terminal, so that you can copy/paste the URL into your browser::
 
-    [I 11:59:16.597 NotebookApp] The Jupyter Notebook is running at: http://localhost:8888/?token=c8de56fa4deed24899803e93c227592aef6538f93025fe01
+    [I 11:59:16.597 NotebookApp] The Jupyter Notebook is running at:
+    http://localhost:8888/?token=c8de56fa4deed24899803e93c227592aef6538f93025fe01
 
 
 If the notebook server is going to open your browser automatically
diff --git a/notebook/notebookapp.py b/notebook/notebookapp.py
index 3985316f0..59e589aed 100755
--- a/notebook/notebookapp.py
+++ b/notebook/notebookapp.py
@@ -1353,7 +1353,8 @@ class NotebookApp(JupyterApp):
         "Return the current working directory and the server url information"
         info = self.contents_manager.info_string() + "\n"
         info += "%d active kernels \n" % len(self.kernel_manager._kernels)
-        return info + "The Jupyter Notebook is running at: %s" % self.display_url
+        # Format the info so that the URL fits on a single line in 80 char display
+        return info + "The Jupyter Notebook is running at:\n\r%s" % self.display_url
 
     def server_info(self):
         """Return a JSONable dict of information about this server."""

From a5e64e10b782367a3fb87e7dcf00347ba287a310 Mon Sep 17 00:00:00 2001
From: Min RK 
Date: Fri, 19 May 2017 15:46:07 -0700
Subject: [PATCH 22/28] handle surrogate pairs

CodeMirror / javascript use utf16 code unit offsets,
but Jupyter protocol expects unicode *character* offsets,
so we need to translate back and forth.
---
 notebook/static/base/js/utils.js         | 32 ++++++++++++++++++++++++
 notebook/static/notebook/js/completer.js | 11 +++++++-
 2 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/notebook/static/base/js/utils.js b/notebook/static/base/js/utils.js
index 474b03426..bc53a6c53 100644
--- a/notebook/static/base/js/utils.js
+++ b/notebook/static/base/js/utils.js
@@ -1017,6 +1017,36 @@ define([
         }
     };
 
+
+    // javascript stores text as utf16 and string indices use "code units",
+    // which stores high-codepoint characters as "surrogate pairs",
+    // which occupy two indices in the javascript string.
+    // We need to translate cursor_pos in the protocol (in characters)
+    // to js offset (with surrogate pairs taking two spots).
+    function js_idx_to_char_idx (js_idx, text) {
+        var char_idx = js_idx;
+        for (var i = 0; i < text.length && i < js_idx; i++) {
+            var char_code = text.charCodeAt(i);
+            // check for the first half of a surrogate pair
+            if (char_code >= 0xD800 && char_code < 0xDC00) {
+                char_idx -= 1;
+            }
+        }
+        return char_idx;
+    }
+
+    function char_idx_to_js_idx (char_idx, text) {
+        var js_idx = char_idx;
+        for (var i = 0; i < text.length && i < js_idx; i++) {
+            var char_code = text.charCodeAt(i);
+            // check for the first half of a surrogate pair
+            if (char_code >= 0xD800 && char_code < 0xDC00) {
+                js_idx += 1;
+            }
+        }
+        return js_idx;
+    }
+
     // Test if a drag'n'drop event contains a file (as opposed to an HTML
     // element/text from the document)
     var dnd_contain_file = function(event) {
@@ -1112,6 +1142,8 @@ define([
         format_datetime: format_datetime,
         datetime_sort_helper: datetime_sort_helper,
         dnd_contain_file: dnd_contain_file,
+        js_idx_to_char_idx: js_idx_to_char_idx,
+        char_idx_to_js_idx: char_idx_to_js_idx,
         _ansispan:_ansispan,
         change_favicon: change_favicon
     };
diff --git a/notebook/static/notebook/js/completer.js b/notebook/static/notebook/js/completer.js
index 8eb92ca2e..21a6a3b32 100644
--- a/notebook/static/notebook/js/completer.js
+++ b/notebook/static/notebook/js/completer.js
@@ -153,6 +153,8 @@ define([
         // one kernel completion came back, finish_completing will be called with the results
         // we fork here and directly call finish completing if kernel is busy
         var cursor_pos = this.editor.indexFromPos(cur);
+        var text = this.editor.getValue();
+        cursor_pos = utils.js_idx_to_char_idx(cursor_pos, text);
         if (this.skip_kernel_completion) {
             this.finish_completing({ content: {
                 matches: [],
@@ -160,7 +162,7 @@ define([
                 cursor_end: cursor_pos,
             }});
         } else {
-            this.cell.kernel.complete(this.editor.getValue(), cursor_pos,
+            this.cell.kernel.complete(text, cursor_pos,
                 $.proxy(this.finish_completing, this)
             );
         }
@@ -175,6 +177,7 @@ define([
         var start = content.cursor_start;
         var end = content.cursor_end;
         var matches = content.matches;
+        console.log(content);
 
         var cur = this.editor.getCursor();
         if (end === null) {
@@ -187,7 +190,13 @@ define([
             } else if (start < 0) {
                 start = end + start;
             }
+        } else {
+            // handle surrogate pairs
+            var text = this.editor.getValue();
+            end = utils.char_idx_to_js_idx(end, text);
+            start = utils.char_idx_to_js_idx(start, text);
         }
+
         var results = CodeMirror.contextHint(this.editor);
         var filtered_results = [];
         //remove results from context completion

From 2da75c37bd73d9338ac5eef78f02153dcc9b6126 Mon Sep 17 00:00:00 2001
From: Min RK 
Date: Fri, 19 May 2017 15:48:27 -0700
Subject: [PATCH 23/28] handle surrogate pairs in tooltip cursor_pos

---
 notebook/static/notebook/js/tooltip.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/notebook/static/notebook/js/tooltip.js b/notebook/static/notebook/js/tooltip.js
index ba93a1d9c..c0d7e11e1 100644
--- a/notebook/static/notebook/js/tooltip.js
+++ b/notebook/static/notebook/js/tooltip.js
@@ -201,8 +201,8 @@ define([
         this.cancel_pending();
         var editor = cell.code_mirror;
         var cursor = editor.getCursor();
-        var cursor_pos = editor.indexFromPos(cursor);
         var text = cell.get_text();
+        var cursor_pos = utils.js_idx_to_char_idx(editor.indexFromPos(cursor), text);
 
         this._hide_if_no_docstring = hide_if_no_docstring;
 

From 8d1c94853cc25826c2adb3a0fa608f11b57c5020 Mon Sep 17 00:00:00 2001
From: Thomas Kluyver 
Date: Mon, 22 May 2017 13:28:03 +0100
Subject: [PATCH 24/28] Tweak formatting of 'no results' message

---
 notebook/static/notebook/js/commandpalette.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/notebook/static/notebook/js/commandpalette.js b/notebook/static/notebook/js/commandpalette.js
index 890179453..05de2e897 100644
--- a/notebook/static/notebook/js/commandpalette.js
+++ b/notebook/static/notebook/js/commandpalette.js
@@ -165,8 +165,8 @@ define(function(require){
 
         input.typeahead({
           emptyTemplate: function(query) {
-            return $('
').text("No results found for").append( - $('
').text(query)
+            return $('
').text("No results found for ").append( + $('').text(query) ); }, maxItem: 1e3, From 85ba2bdabd012c1fc2a8adf84ded6adc3540c003 Mon Sep 17 00:00:00 2001 From: Peter Parente Date: Mon, 22 May 2017 14:24:07 -0400 Subject: [PATCH 25/28] Fix help for cull_connected and cull_busy --- notebook/services/kernels/kernelmanager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notebook/services/kernels/kernelmanager.py b/notebook/services/kernels/kernelmanager.py index a431a0ec0..c92ae8310 100644 --- a/notebook/services/kernels/kernelmanager.py +++ b/notebook/services/kernels/kernelmanager.py @@ -73,12 +73,12 @@ class MappingKernelManager(MultiKernelManager): cull_connected = Bool(True, config=True, help="""Whether to consider culling kernels which have one or more connections. - Only effective if cull_interval is not 0.""" + Only effective if cull_idle_timeout is not 0.""" ) cull_busy = Bool(False, config=True, help="""Whether to consider culling kernels which are busy. - Only effective if cull_interval is not 0.""" + Only effective if cull_idle_timeout is not 0.""" ) #------------------------------------------------------------------------- From c8ba55da21932c3193fe34c8ebf26e9d7dc4368b Mon Sep 17 00:00:00 2001 From: Peter Parente Date: Thu, 25 May 2017 21:52:12 -0400 Subject: [PATCH 26/28] Do not cull connected kernels by default --- notebook/services/kernels/kernelmanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebook/services/kernels/kernelmanager.py b/notebook/services/kernels/kernelmanager.py index c92ae8310..569c60426 100644 --- a/notebook/services/kernels/kernelmanager.py +++ b/notebook/services/kernels/kernelmanager.py @@ -71,7 +71,7 @@ class MappingKernelManager(MultiKernelManager): help="""The interval (in seconds) on which to check for idle kernels exceeding the cull timeout value.""" ) - cull_connected = Bool(True, config=True, + cull_connected = Bool(False, config=True, help="""Whether to consider culling kernels which have one or more connections. Only effective if cull_idle_timeout is not 0.""" ) From c309c8513f69ef6bcab230ae1e22eb387b1e94e8 Mon Sep 17 00:00:00 2001 From: Grant Nestor Date: Mon, 29 May 2017 21:52:22 -0700 Subject: [PATCH 27/28] Fix clipping on filename --- notebook/static/notebook/less/savewidget.less | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/notebook/static/notebook/less/savewidget.less b/notebook/static/notebook/less/savewidget.less index 07b54621c..7d3835f53 100644 --- a/notebook/static/notebook/less/savewidget.less +++ b/notebook/static/notebook/less/savewidget.less @@ -1,4 +1,5 @@ span.save_widget { + height: 30px; margin-top: 4px; display: flex; justify-content: flex-start; @@ -6,7 +7,7 @@ span.save_widget { width: calc(~"100% - 210px"); span.filename { - height: 1em; + height: 100%; line-height: 1em; margin-left: @padding-large-horizontal; border: none; From 945a973da511d612aea282c1911f768ffc60caaf Mon Sep 17 00:00:00 2001 From: Grant Nestor Date: Mon, 29 May 2017 22:33:05 -0700 Subject: [PATCH 28/28] Clean up layout in #header-container --- notebook/static/base/less/page.less | 13 +++++++++---- notebook/static/notebook/less/kernelselector.less | 2 -- notebook/static/notebook/less/savewidget.less | 3 ++- notebook/templates/notebook.html | 2 +- notebook/templates/page.html | 7 ++++--- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/notebook/static/base/less/page.less b/notebook/static/base/less/page.less index 7a51af6a8..8bac02806 100644 --- a/notebook/static/base/less/page.less +++ b/notebook/static/base/less/page.less @@ -27,6 +27,10 @@ body > #header { z-index: 100; #header-container { + display: flex; + flex-direction: row; + justify-content: space-between; + padding: 5px; padding-bottom: 5px; padding-top: 5px; .border-box-sizing(); @@ -57,9 +61,6 @@ body > #header { padding-left: 0px; padding-top: (@navbar-height - @logo_height) / 2; padding-bottom: (@navbar-height - @logo_height) / 2; - @media (max-width: @screen-sm-max){ - margin-left: 10px; - } } @@ -99,8 +100,12 @@ input.ui-button { padding: 0.3em 0.9em; } +span#kernel_logo_widget { + margin: 0 10px; +} + span#login_widget { - float: right; + } span#login_widget > .button, diff --git a/notebook/static/notebook/less/kernelselector.less b/notebook/static/notebook/less/kernelselector.less index 33c68a34c..fab2cf745 100644 --- a/notebook/static/notebook/less/kernelselector.less +++ b/notebook/static/notebook/less/kernelselector.less @@ -1,6 +1,4 @@ #kernel_logo_widget { - .pull-right(); - .current_kernel_logo { display: none; .navbar-vertical-align(32px); diff --git a/notebook/static/notebook/less/savewidget.less b/notebook/static/notebook/less/savewidget.less index 7d3835f53..13f76b4d4 100644 --- a/notebook/static/notebook/less/savewidget.less +++ b/notebook/static/notebook/less/savewidget.less @@ -4,7 +4,8 @@ span.save_widget { display: flex; justify-content: flex-start; align-items: baseline; - width: calc(~"100% - 210px"); + width: 50%; + flex: 1; span.filename { height: 100%; diff --git a/notebook/templates/notebook.html b/notebook/templates/notebook.html index 53e5ebbb4..b86397192 100644 --- a/notebook/templates/notebook.html +++ b/notebook/templates/notebook.html @@ -36,7 +36,7 @@ data-notebook-path="{{notebook_path | urlencode}}" {% block headercontainer %} - + diff --git a/notebook/templates/page.html b/notebook/templates/page.html index e40b3147b..147f9a9d2 100644 --- a/notebook/templates/page.html +++ b/notebook/templates/page.html @@ -120,6 +120,9 @@ dir="ltr">
+ {% block headercontainer %} + {% endblock %} + {% block header_buttons %} {% block login_widget %} @@ -135,9 +138,7 @@ dir="ltr"> {% endblock %} {% endblock header_buttons %} - - {% block headercontainer %} - {% endblock %} +