From fd2588612a718171461235f43b4479171ea7de08 Mon Sep 17 00:00:00 2001 From: Matthias Bussonnier Date: Fri, 28 Nov 2014 11:22:31 +0100 Subject: [PATCH 01/37] return API path instead of filesystem --- IPython/html/services/contents/filemanager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/IPython/html/services/contents/filemanager.py b/IPython/html/services/contents/filemanager.py index 92465e14a..18ae7d8b3 100644 --- a/IPython/html/services/contents/filemanager.py +++ b/IPython/html/services/contents/filemanager.py @@ -606,9 +606,9 @@ class FileContentsManager(ContentsManager): return "Serving notebooks from local directory: %s" % self.root_dir def get_kernel_path(self, path, model=None): - """Return the initial working dir a kernel associated with a given notebook""" + """Return the initial API path of a kernel associated with a given notebook""" if '/' in path: parent_dir = path.rsplit('/', 1)[0] else: parent_dir = '' - return self._get_os_path(parent_dir) + return parent_dir From d131650e2543919ec6c0dc90c3c510ba7281e670 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Tue, 2 Dec 2014 10:32:56 +0100 Subject: [PATCH 02/37] gh-7053: check for OSError during rehashx() --- IPython/testing/iptestcontroller.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/IPython/testing/iptestcontroller.py b/IPython/testing/iptestcontroller.py index fed140501..b03e144ba 100644 --- a/IPython/testing/iptestcontroller.py +++ b/IPython/testing/iptestcontroller.py @@ -15,6 +15,7 @@ import argparse import json import multiprocessing.pool import os +import stat import re import requests import shutil @@ -170,6 +171,18 @@ class PyTestController(TestController): # This means we won't get odd effects from our own matplotlib config self.env['MPLCONFIGDIR'] = workingdir.name + # Add a non-accessible directory to PATH (see gh-7053) + noaccess = os.path.join(self.workingdir.name, "_no_access_") + self.noaccess = noaccess + os.mkdir(noaccess, 0) + + PATH = os.environ.get('PATH', '') + if PATH: + PATH = noaccess + os.pathsep + PATH + else: + PATH = noaccess + self.env['PATH'] = PATH + # From options: if self.options.xunit: self.add_xunit() @@ -178,6 +191,14 @@ class PyTestController(TestController): self.env['IPTEST_SUBPROC_STREAMS'] = self.options.subproc_streams self.cmd.extend(self.options.extra_args) + def cleanup(self): + """ + Make the non-accessible directory created in setup() accessible + again, otherwise deleting the workingdir will fail. + """ + os.chmod(self.noaccess, stat.S_IRWXU) + TestController.cleanup(self) + @property def will_run(self): try: From d273b56044f9a313be9c17afc9e443f89c9ea803 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Tue, 2 Dec 2014 11:54:19 -0800 Subject: [PATCH 03/37] Clean up get_kernel_path logic --- IPython/html/services/contents/manager.py | 6 +++++- IPython/html/services/kernels/kernelmanager.py | 6 +----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/IPython/html/services/contents/manager.py b/IPython/html/services/contents/manager.py index 491401f79..1c87064ca 100644 --- a/IPython/html/services/contents/manager.py +++ b/IPython/html/services/contents/manager.py @@ -187,8 +187,12 @@ class ContentsManager(LoggingConfigurable): KernelManagers can turn this value into a filesystem path, or ignore it altogether. + + The default value here will start kernels in the directory of the + notebook server. FileContentsManager overrides this to use the + directory containing the notebook. """ - return path + return '' def increment_filename(self, filename, path='', insert=''): """Increment a filename until it is unique. diff --git a/IPython/html/services/kernels/kernelmanager.py b/IPython/html/services/kernels/kernelmanager.py index e1bd5c256..db73aa4c3 100644 --- a/IPython/html/services/kernels/kernelmanager.py +++ b/IPython/html/services/kernels/kernelmanager.py @@ -54,14 +54,10 @@ class MappingKernelManager(MultiKernelManager): def cwd_for_path(self, path): """Turn API path into absolute OS path.""" - # short circuit for NotebookManagers that pass in absolute paths - if os.path.exists(path): - return path - os_path = to_os_path(path, self.root_dir) # in the case of notebooks and kernels not being on the same filesystem, # walk up to root_dir if the paths don't exist - while not os.path.exists(os_path) and os_path != self.root_dir: + while not os.path.isdir(os_path) and os_path != self.root_dir: os_path = os.path.dirname(os_path) return os_path From 13c1b95fbd8f8ab729d925e6039d07140dc7cf5f Mon Sep 17 00:00:00 2001 From: Jason Grout Date: Wed, 3 Dec 2014 19:30:05 +0000 Subject: [PATCH 04/37] Make the model.views dict a dict of promises for views Otherwise, there is a race condition where the model doesn't know about views until they are actually created. --- IPython/html/static/widgets/js/manager.js | 1 + IPython/html/static/widgets/js/widget.js | 13 ++++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/IPython/html/static/widgets/js/manager.js b/IPython/html/static/widgets/js/manager.js index c0f17fbdb..a368b4a97 100644 --- a/IPython/html/static/widgets/js/manager.js +++ b/IPython/html/static/widgets/js/manager.js @@ -104,6 +104,7 @@ define([ return Promise.resolve(view.render()).then(function() {return view;}); }).catch(utils.reject("Couldn't create a view for model id '" + String(model.id) + "'", true)); }); + model.views[utils.uuid()] = model.state_change; return model.state_change; }; diff --git a/IPython/html/static/widgets/js/widget.js b/IPython/html/static/widgets/js/widget.js index 3336bceed..e953acaaf 100644 --- a/IPython/html/static/widgets/js/widget.js +++ b/IPython/html/static/widgets/js/widget.js @@ -59,11 +59,12 @@ define(["widgets/js/manager", delete this.comm.model; // Delete ref so GC will collect widget model. delete this.comm; delete this.model_id; // Delete id from model so widget manager cleans up. - for (var id in this.views) { - if (this.views.hasOwnProperty(id)) { - this.views[id].remove(); - } - } + _.each(this.views, function(v, id, views) { + v.then(function(view) { + view.remove(); + delete views[id]; + }); + }); }, _handle_comm_msg: function (msg) { @@ -292,8 +293,6 @@ define(["widgets/js/manager", // Public constructor. this.model.on('change',this.update,this); this.options = parameters.options; - this.id = this.id || utils.uuid(); - this.model.views[this.id] = this; this.on('displayed', function() { this.is_displayed = true; }, this); From 353a9afd34c6ba60495d4d292cfb08d5c2d8c68c Mon Sep 17 00:00:00 2001 From: Jonathan Frederic Date: Fri, 21 Nov 2014 13:03:14 -0800 Subject: [PATCH 05/37] Kill the layout manager --- IPython/html/static/base/js/page.js | 10 ++- IPython/html/static/base/less/page.less | 32 +++++-- IPython/html/static/notebook/js/main.js | 10 +-- IPython/html/static/notebook/js/menubar.js | 15 ++-- IPython/html/static/notebook/js/pager.js | 49 +---------- .../html/static/notebook/less/menubar.less | 3 +- .../html/static/notebook/less/notebook.less | 21 +++-- IPython/html/static/notebook/less/pager.less | 38 +++++---- .../html/static/notebook/less/toolbar.less | 9 +- .../html/static/notebook/less/variables.less | 2 +- IPython/html/static/style/style.min.css | 83 ++++++++++++------- IPython/html/templates/notebook.html | 28 ++++--- IPython/html/templates/page.html | 9 +- 13 files changed, 165 insertions(+), 144 deletions(-) diff --git a/IPython/html/static/base/js/page.js b/IPython/html/static/base/js/page.js index 7c2528564..52e89b7e6 100644 --- a/IPython/html/static/base/js/page.js +++ b/IPython/html/static/base/js/page.js @@ -4,14 +4,17 @@ define([ 'base/js/namespace', 'jquery', -], function(IPython, $){ + 'base/js/events', +], function(IPython, $, events){ "use strict"; var Page = function () { this.bind_events(); + this._resize_header(); }; Page.prototype.bind_events = function () { + events.on('resize-header.Page', $.proxy(this._resize_header, this)); }; Page.prototype.show = function () { @@ -41,6 +44,11 @@ define([ $('div#site').css('display','block'); }; + Page.prototype._resize_header = function() { + // Update the header's size. + $('#header-spacer').height($('#header').height()); + }; + // Register self in the global namespace for convenience. IPython.Page = Page; return {'Page': Page}; diff --git a/IPython/html/static/base/less/page.less b/IPython/html/static/base/less/page.less index 772a4bbf4..baa93a166 100644 --- a/IPython/html/static/base/less/page.less +++ b/IPython/html/static/base/less/page.less @@ -20,11 +20,33 @@ body { div#header { /* Initially hidden to prevent FLOUC */ display: none; - margin-bottom: 0px; - padding-left: 30px; - padding-bottom: 5px; - border-bottom: 1px solid @navbar-default-border; - .border-box-sizing(); + margin-bottom: -6px; + position: fixed; + top: 0; + width: 100%; + background-color: @body-bg; + min-height: 31px; + + /* Display over codemirror */ + z-index: 100; + + #header-container { + margin-bottom: 0px; + padding-left: 30px; + padding-bottom: 5px; + .border-box-sizing(); + } + + .header-bar { + width: 100%; + height: 0px; + border-bottom: 1px solid @navbar-default-border; + } +} + +#header-spacer { + width: 100%; + visibility: hidden; } #ipython_notebook { diff --git a/IPython/html/static/notebook/js/main.js b/IPython/html/static/notebook/js/main.js index fb325bc03..a6c826408 100644 --- a/IPython/html/static/notebook/js/main.js +++ b/IPython/html/static/notebook/js/main.js @@ -9,7 +9,6 @@ require([ 'services/config', 'base/js/utils', 'base/js/page', - 'notebook/js/layoutmanager', 'base/js/events', 'auth/js/loginwidget', 'notebook/js/maintoolbar', @@ -34,7 +33,6 @@ require([ configmod, utils, page, - layoutmanager, events, loginwidget, maintoolbar, @@ -66,9 +64,7 @@ require([ var user_config = $.extend({}, config.default_config); var page = new page.Page(); - var layout_manager = new layoutmanager.LayoutManager(); - var pager = new pager.Pager('div#pager', 'div#pager_splitter', { - layout_manager: layout_manager, + var pager = new pager.Pager('div#pager', { events: events}); var acts = new actions.init(); var keyboard_manager = new keyboardmanager.KeyboardManager({ @@ -104,7 +100,6 @@ require([ var menubar = new menubar.MenuBar('#menubar', $.extend({ notebook: notebook, contents: contents, - layout_manager: layout_manager, events: events, save_widget: save_widget, quick_help: quick_help}, @@ -132,9 +127,7 @@ require([ page.show(); - layout_manager.do_resize(); var first_load = function () { - layout_manager.do_resize(); var hash = document.location.hash; if (hash) { document.location.hash = ''; @@ -147,7 +140,6 @@ require([ events.on('notebook_loaded.Notebook', first_load); IPython.page = page; - IPython.layout_manager = layout_manager; IPython.notebook = notebook; IPython.contents = contents; IPython.pager = pager; diff --git a/IPython/html/static/notebook/js/menubar.js b/IPython/html/static/notebook/js/menubar.js index a0bf0e099..59b199e08 100644 --- a/IPython/html/static/notebook/js/menubar.js +++ b/IPython/html/static/notebook/js/menubar.js @@ -24,7 +24,6 @@ define([ * Dictionary of keyword arguments. * notebook: Notebook instance * contents: ContentManager instance - * layout_manager: LayoutManager instance * events: $(Events) instance * save_widget: SaveWidget instance * quick_help: QuickHelp instance @@ -37,7 +36,6 @@ define([ this.selector = selector; this.notebook = options.notebook; this.contents = options.contents; - this.layout_manager = options.layout_manager; this.events = options.events; this.save_widget = options.save_widget; this.quick_help = options.quick_help; @@ -88,6 +86,13 @@ define([ } }; + MenuBar.prototype._size_header = function() { + /** + * Update header spacer size. + */ + this.events.trigger('resize-header.Page'); + }; + MenuBar.prototype.bind_events = function () { /** * File @@ -218,12 +223,12 @@ define([ // View this.element.find('#toggle_header').click(function () { - $('div#header').toggle(); - that.layout_manager.do_resize(); + $('div#header-container').toggle(); + that._size_header(); }); this.element.find('#toggle_toolbar').click(function () { $('div#maintoolbar').toggle(); - that.layout_manager.do_resize(); + that._size_header(); }); // Insert this.element.find('#insert_cell_above').click(function () { diff --git a/IPython/html/static/notebook/js/pager.js b/IPython/html/static/notebook/js/pager.js index 9656fd4a5..0787a12a9 100644 --- a/IPython/html/static/notebook/js/pager.js +++ b/IPython/html/static/notebook/js/pager.js @@ -8,48 +8,21 @@ define([ ], function(IPython, $, utils) { "use strict"; - var Pager = function (pager_selector, pager_splitter_selector, options) { + var Pager = function (pager_selector, options) { /** * Constructor * * Parameters: * pager_selector: string - * pager_splitter_selector: string * options: dictionary * Dictionary of keyword arguments. * events: $(Events) instance - * layout_manager: LayoutManager instance */ this.events = options.events; this.pager_element = $(pager_selector); this.pager_button_area = $('#pager_button_area'); - var that = this; - this.percentage_height = 0.40; - options.layout_manager.pager = this; - this.pager_splitter_element = $(pager_splitter_selector) - .draggable({ - containment: 'window', - axis:'y', - helper: null , - drag: function(event, ui) { - /** - * recalculate the amount of space the pager should take - */ - var pheight = ($(document.body).height()-event.clientY-4); - var downprct = pheight/options.layout_manager.app_height(); - downprct = Math.min(0.9, downprct); - if (downprct < 0.1) { - that.percentage_height = 0.1; - that.collapse({'duration':0}); - } else if (downprct > 0.2) { - that.percentage_height = downprct; - that.expand({'duration':0}); - } - options.layout_manager.do_resize(); - } - }); + this.pager_element.resizable({handles: 'n'}); this.expanded = false; - this.style(); this.create_button_area(); this.bind_events(); }; @@ -78,11 +51,6 @@ define([ ); }; - Pager.prototype.style = function () { - this.pager_splitter_element.addClass('ui-widget ui-state-default'); - this.pager_splitter_element.attr('title', 'Click to Show/Hide pager area, drag to Resize'); - }; - Pager.prototype.bind_events = function () { var that = this; @@ -103,19 +71,6 @@ define([ that.pager_element.show(time); }); - this.pager_splitter_element.hover( - function () { - that.pager_splitter_element.addClass('ui-state-hover'); - }, - function () { - that.pager_splitter_element.removeClass('ui-state-hover'); - } - ); - - this.pager_splitter_element.click(function () { - that.toggle(); - }); - this.events.on('open_with_text.Pager', function (event, payload) { // FIXME: support other mime types if (payload.data['text/plain'] && payload.data['text/plain'] !== "") { diff --git a/IPython/html/static/notebook/less/menubar.less b/IPython/html/static/notebook/less/menubar.less index 63e784aad..f7df46811 100644 --- a/IPython/html/static/notebook/less/menubar.less +++ b/IPython/html/static/notebook/less/menubar.less @@ -1,12 +1,13 @@ #menubar { margin-top: 0px; - margin-bottom: -19px; + margin-bottom: -24px; position: relative; .border-box-sizing(); .navbar { border-top: 1px; border-radius: 0px 0px @border-radius-base @border-radius-base; + margin-bottom: 23px; } .navbar-toggle { diff --git a/IPython/html/static/notebook/less/notebook.less b/IPython/html/static/notebook/less/notebook.less index 87f1503f7..58ac2df88 100644 --- a/IPython/html/static/notebook/less/notebook.less +++ b/IPython/html/static/notebook/less/notebook.less @@ -3,10 +3,6 @@ body { background-color: @body-bg; } -body.notebook_app { - overflow: hidden; -} - @media (max-width: 767px) { // remove bootstrap-responsive's body padding on small screens body.notebook_app { @@ -36,19 +32,17 @@ span#notebook_name { div#notebook_panel { margin: 0px 0px 0px 0px; padding: 0px; - .box-shadow(@notebook-shadow); .border-box-sizing(); } div#notebook { font-size: @notebook_font_size; line-height: @notebook_line_height; - overflow-y: scroll; + overflow-y: hidden; overflow-x: auto; width: 100%; /* This spaces the cell away from the edge of the notebook area */ - padding: 1em 0 1em 0; + padding: 2em 0 2em 0; margin: 0px; - border-top: 1px solid @navbar-default-border; outline: none; .border-box-sizing(); } @@ -86,3 +80,14 @@ p { .end_space { height: 200px; } + +.lower-header-bar { + width: 100%; + height: 0px; + border-bottom: 1px solid @navbar-default-border; + margin-bottom: -1px; +} + +.notebook_app #header { + .box-shadow(@notebook-shadow); +} diff --git a/IPython/html/static/notebook/less/pager.less b/IPython/html/static/notebook/less/pager.less index 41ca682df..d9e040e60 100644 --- a/IPython/html/static/notebook/less/pager.less +++ b/IPython/html/static/notebook/less/pager.less @@ -1,25 +1,31 @@ -div#pager_splitter { - height: 8px; - .border-box-sizing(); -} - -#pager-container { - position: relative; - padding: 15px 0px; - .border-box-sizing(); -} - div#pager { + background-color: @body-bg; font-size: @notebook_font_size; line-height: @notebook_line_height; overflow: auto; display: none; + position: fixed; + bottom: 0px; + width: 100%; + border-top: 1px solid @navbar-default-border; + + /* Display over codemirror */ + z-index: 100; + + /* Hack which prevents jquery ui resizable from changing top. */ + top: inherit !important; pre { - line-height: @code_line_height; - color: @text-color; - background-color: @cell_background; - padding: @code_padding; + line-height: @code_line_height; + color: @text-color; + background-color: @cell_background; + padding: @code_padding; + } + + #pager-container { + position: relative; + padding: 15px 0px; + .border-box-sizing(); } - .border-box-sizing(); + } diff --git a/IPython/html/static/notebook/less/toolbar.less b/IPython/html/static/notebook/less/toolbar.less index 26f7cf67e..059902146 100644 --- a/IPython/html/static/notebook/less/toolbar.less +++ b/IPython/html/static/notebook/less/toolbar.less @@ -2,6 +2,7 @@ padding: 0px; margin-left: -5px; margin-top: -5px; + margin-bottom: 5px; select, label { width: auto; @@ -33,8 +34,8 @@ border: 0px; min-height: 27px; margin-left: 32px; - padding-top: 6px; - padding-bottom: 8px; + padding-top: 11px; + padding-bottom: 3px; .navbar-text { float: none; @@ -44,10 +45,6 @@ margin-right: 0px; margin-top: 0px; } - - .toolbar { - margin-top: 0px; - } } .select-xs { diff --git a/IPython/html/static/notebook/less/variables.less b/IPython/html/static/notebook/less/variables.less index c11250e03..466cb090f 100644 --- a/IPython/html/static/notebook/less/variables.less +++ b/IPython/html/static/notebook/less/variables.less @@ -10,7 +10,7 @@ @notebook_line_height: 20px; @code_line_height: 1.21429em; // changed from 1.231 to get 17px even @code_padding: 0.4em; // 5.6 px -@notebook-shadow: inset 1px 4px 9px -6px rgba(0,0,0,.25); +@notebook-shadow: 1px 4px 9px -6px rgba(0,0,0,.25); @rendered_html_border_color: black; @input_prompt_color: navy; @output_prompt_color: darkred; diff --git a/IPython/html/static/style/style.min.css b/IPython/html/static/style/style.min.css index 8c276b37e..906c25cdd 100644 --- a/IPython/html/static/style/style.min.css +++ b/IPython/html/static/style/style.min.css @@ -7751,14 +7751,32 @@ body { div#header { /* Initially hidden to prevent FLOUC */ display: none; + margin-bottom: -6px; + position: fixed; + top: 0; + width: 100%; + background-color: #ffffff; + min-height: 31px; + /* Display over codemirror */ + z-index: 100; +} +div#header #header-container { margin-bottom: 0px; padding-left: 30px; padding-bottom: 5px; - border-bottom: 1px solid #e7e7e7; box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; } +div#header .header-bar { + width: 100%; + height: 0px; + border-bottom: 1px solid #e7e7e7; +} +#header-spacer { + width: 100%; + visibility: hidden; +} #ipython_notebook { padding-left: 0px; } @@ -9461,9 +9479,6 @@ h6:hover .anchor-link { body { background-color: #ffffff; } -body.notebook_app { - overflow: hidden; -} @media (max-width: 767px) { body.notebook_app { padding-left: 0px; @@ -9489,8 +9504,6 @@ span#notebook_name:hover { div#notebook_panel { margin: 0px 0px 0px 0px; padding: 0px; - -webkit-box-shadow: inset 1px 4px 9px -6px rgba(0, 0, 0, 0.25); - box-shadow: inset 1px 4px 9px -6px rgba(0, 0, 0, 0.25); box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; @@ -9498,13 +9511,12 @@ div#notebook_panel { div#notebook { font-size: 14px; line-height: 20px; - overflow-y: scroll; + overflow-y: hidden; overflow-x: auto; width: 100%; /* This spaces the cell away from the edge of the notebook area */ - padding: 1em 0 1em 0; + padding: 2em 0 2em 0; margin: 0px; - border-top: 1px solid #e7e7e7; outline: none; box-sizing: border-box; -moz-box-sizing: border-box; @@ -9542,6 +9554,16 @@ p { .end_space { height: 200px; } +.lower-header-bar { + width: 100%; + height: 0px; + border-bottom: 1px solid #e7e7e7; + margin-bottom: -1px; +} +.notebook_app #header { + -webkit-box-shadow: 1px 4px 9px -6px rgba(0, 0, 0, 0.25); + box-shadow: 1px 4px 9px -6px rgba(0, 0, 0, 0.25); +} /* CSS for the cell toolbar */ .celltoolbar { border: thin solid #CFCFCF; @@ -9779,7 +9801,7 @@ fieldset[disabled] #kernel_selector_widget > button.active { } #menubar { margin-top: 0px; - margin-bottom: -19px; + margin-bottom: -24px; position: relative; box-sizing: border-box; -moz-box-sizing: border-box; @@ -9788,6 +9810,7 @@ fieldset[disabled] #kernel_selector_widget > button.active { #menubar .navbar { border-top: 1px; border-radius: 0px 0px 4px 4px; + margin-bottom: 23px; } #menubar .navbar-toggle { float: left; @@ -10256,27 +10279,20 @@ fieldset[disabled] .notification_widget.danger.active { color: #d9534f; background-color: #ffffff; } -div#pager_splitter { - height: 8px; - box-sizing: border-box; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; -} -#pager-container { - position: relative; - padding: 15px 0px; - box-sizing: border-box; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; -} div#pager { + background-color: #ffffff; font-size: 14px; line-height: 20px; overflow: auto; display: none; - box-sizing: border-box; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; + position: fixed; + bottom: 0px; + width: 100%; + border-top: 1px solid #e7e7e7; + /* Display over codemirror */ + z-index: 100; + /* Hack which prevents jquery ui resizable from changing top. */ + top: inherit !important; } div#pager pre { line-height: 1.21429em; @@ -10284,6 +10300,13 @@ div#pager pre { background-color: #f7f7f7; padding: 0.4em; } +div#pager #pager-container { + position: relative; + padding: 15px 0px; + box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; +} .quickhelp { /* Old browsers */ display: -webkit-box; @@ -10350,6 +10373,7 @@ span#autosave_status { padding: 0px; margin-left: -5px; margin-top: -5px; + margin-bottom: 5px; box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; @@ -10380,8 +10404,8 @@ span#autosave_status { border: 0px; min-height: 27px; margin-left: 32px; - padding-top: 6px; - padding-bottom: 8px; + padding-top: 11px; + padding-bottom: 3px; } #maintoolbar .navbar-text { float: none; @@ -10391,9 +10415,6 @@ span#autosave_status { margin-right: 0px; margin-top: 0px; } -#maintoolbar .toolbar { - margin-top: 0px; -} .select-xs { height: 24px; } diff --git a/IPython/html/templates/notebook.html b/IPython/html/templates/notebook.html index 1f8b554c7..efd71e27e 100644 --- a/IPython/html/templates/notebook.html +++ b/IPython/html/templates/notebook.html @@ -32,7 +32,7 @@ class="notebook_app" {% endblock %} -{% block header %} +{% block headercontainer %} @@ -50,11 +50,9 @@ class="notebook_app" -{% endblock %} - - -{% block site %} +{% endblock headercontainer %} +{% block header %} + -
+
+{% endblock header %} +{% block site %} + + +
-
-
-
-
-
-
+
+
+
+
+
+ diff --git a/IPython/html/templates/page.html b/IPython/html/templates/page.html index ea7274cc1..a5f70f342 100644 --- a/IPython/html/templates/page.html +++ b/IPython/html/templates/page.html @@ -81,7 +81,7 @@ From 0cfd8731a039ff6c0a3bfa6219d394c4387e22fa Mon Sep 17 00:00:00 2001 From: Min RK Date: Sat, 15 Nov 2014 13:10:19 -0800 Subject: [PATCH 28/37] include current kernel name in new notebook dropdown --- IPython/html/static/tree/js/newnotebook.js | 1 + IPython/html/templates/tree.html | 1 + 2 files changed, 2 insertions(+) diff --git a/IPython/html/static/tree/js/newnotebook.js b/IPython/html/static/tree/js/newnotebook.js index ae24c217e..8a0ed631d 100644 --- a/IPython/html/static/tree/js/newnotebook.js +++ b/IPython/html/static/tree/js/newnotebook.js @@ -67,6 +67,7 @@ define([ if (spec) { display_name = spec.display_name; localStorage.default_kernel_name = kernel_name; + this.element.find("#current-kernel").text(display_name); } else { display_name = 'default kernel'; delete localStorage.default_kernel_name; diff --git a/IPython/html/templates/tree.html b/IPython/html/templates/tree.html index cf1765f03..117a61bbe 100644 --- a/IPython/html/templates/tree.html +++ b/IPython/html/templates/tree.html @@ -49,6 +49,7 @@ data-terminals-available="{{terminals_available}}" New Notebook From d9767e8c948f0c5841d452d49bba60632195da96 Mon Sep 17 00:00:00 2001 From: Min RK Date: Mon, 17 Nov 2014 12:34:11 -0800 Subject: [PATCH 29/37] separate selecting default kernel from new notebook with specified kernel adds checkmark to indicate selection --- IPython/html/static/style/style.min.css | 11 +++++++ IPython/html/static/tree/js/newnotebook.js | 34 +++++++++++++++++----- IPython/html/static/tree/less/tree.less | 16 +++++++++- IPython/html/templates/tree.html | 2 +- 4 files changed, 54 insertions(+), 9 deletions(-) diff --git a/IPython/html/static/style/style.min.css b/IPython/html/static/style/style.min.css index a2ccd2f7b..5f51faff9 100644 --- a/IPython/html/static/style/style.min.css +++ b/IPython/html/static/style/style.min.css @@ -8168,6 +8168,17 @@ ul#new-notebook-menu { left: auto; right: 0; } +.kernel-menu-icon { + padding-right: 12px; + width: 24px; + content: "\f096"; +} +.kernel-menu-icon:before { + content: "\f096"; +} +.kernel-menu-icon-current:before { + content: "\f00c"; +} /*! * * IPython notebook diff --git a/IPython/html/static/tree/js/newnotebook.js b/IPython/html/static/tree/js/newnotebook.js index 8a0ed631d..37ff15d29 100644 --- a/IPython/html/static/tree/js/newnotebook.js +++ b/IPython/html/static/tree/js/newnotebook.js @@ -43,13 +43,21 @@ define([ for (var i = 0; i < data.length; i++) { var ks = data[i]; this.kernelspecs[ks.name] = ks; - var ksentry = $("
  • ").attr("id", "kernel-" +ks.name).append($('') - .attr('href', '#') - .click($.proxy(this.new_with_kernel, this, ks.name)) - .text(ks.display_name) - .attr('title', 'Create a new notebook with ' + ks.display_name) + var li = $("
  • ") + .attr("id", "kernel-" +ks.name) + .data('kernelspec', ks).append( + $('').attr('href', '#').append($('') + .addClass('kernel-menu-icon fa') + .attr('href', '#') + .click($.proxy(this.select_kernel, this, ks.name)) + ).append($('') + .attr('href', '#') + .click($.proxy(this.new_notebook, this, ks.name)) + .text(ks.display_name) + .attr('title', 'Create a new notebook with ' + ks.display_name) + ) ); - menu.append(ksentry); + menu.append(li); } this._load_default_kernelspec(); }; @@ -75,11 +83,23 @@ define([ this.element.find("#new_notebook").attr('title', 'Create a new notebook with ' + display_name ); + this.element.find("li").map(function (i, li) { + li = $(li); + var ks = li.data('kernelspec'); + if (ks.name == kernel_name) { + li.find(".kernel-menu-icon") + .attr('title', display_name + ' is the default kernel') + .addClass("kernel-menu-icon-current"); + } else { + li.find(".kernel-menu-icon") + .attr('title', 'Make ' + ks.display_name + ' the default kernel') + .removeClass("kernel-menu-icon-current"); + } + }); }; NewNotebookWidget.prototype.new_with_kernel = function (kernel_name) { /** record current selection and open a new notebook */ - this.select_kernel(kernel_name); this.new_notebook(kernel_name); }; diff --git a/IPython/html/static/tree/less/tree.less b/IPython/html/static/tree/less/tree.less index 23cfc31ce..8ed509a0c 100644 --- a/IPython/html/static/tree/less/tree.less +++ b/IPython/html/static/tree/less/tree.less @@ -159,4 +159,18 @@ ul#new-notebook-menu { // align right instead of left left: auto; right: 0; -} \ No newline at end of file +} + +.kernel-menu-icon { + padding-right: 12px; + width: 24px; + content: @fa-var-square-o; +} + +.kernel-menu-icon:before { + content: @fa-var-square-o; +} + +.kernel-menu-icon-current:before { + content: @fa-var-check; +} diff --git a/IPython/html/templates/tree.html b/IPython/html/templates/tree.html index 117a61bbe..bf814302f 100644 --- a/IPython/html/templates/tree.html +++ b/IPython/html/templates/tree.html @@ -49,7 +49,7 @@ data-terminals-available="{{terminals_available}}" New Notebook From 11ef669ec63d831560b13917bb0300098f6d519b Mon Sep 17 00:00:00 2001 From: Min RK Date: Mon, 17 Nov 2014 13:04:21 -0800 Subject: [PATCH 30/37] kernelspecs is a dict sort menus client-side --- IPython/html/services/kernelspecs/handlers.py | 13 +++++----- .../kernelspecs/tests/test_kernelspecs_api.py | 18 +++++++++----- .../html/static/notebook/js/kernelselector.js | 23 +++++++++++++----- IPython/html/static/tree/js/newnotebook.js | 24 +++++++++++++------ 4 files changed, 53 insertions(+), 25 deletions(-) diff --git a/IPython/html/services/kernelspecs/handlers.py b/IPython/html/services/kernelspecs/handlers.py index f8104147d..2d1a75fc2 100644 --- a/IPython/html/services/kernelspecs/handlers.py +++ b/IPython/html/services/kernelspecs/handlers.py @@ -7,8 +7,6 @@ from tornado import web from ...base.handlers import IPythonHandler, json_errors -from IPython.kernel.kernelspec import _pythonfirst - class MainKernelSpecHandler(IPythonHandler): SUPPORTED_METHODS = ('GET',) @@ -17,18 +15,21 @@ class MainKernelSpecHandler(IPythonHandler): @json_errors def get(self): ksm = self.kernel_spec_manager - results = [] - for kernel_name in sorted(ksm.find_kernel_specs(), key=_pythonfirst): + km = self.kernel_manager + model = {} + model['default'] = km.default_kernel_name + model['kernelspecs'] = specs = {} + for kernel_name in ksm.find_kernel_specs(): try: d = ksm.get_kernel_spec(kernel_name).to_dict() except Exception: self.log.error("Failed to load kernel spec: '%s'", kernel_name, exc_info=True) continue d['name'] = kernel_name - results.append(d) + specs[kernel_name] = d self.set_header("Content-Type", 'application/json') - self.finish(json.dumps(results)) + self.finish(json.dumps(model)) class KernelSpecHandler(IPythonHandler): diff --git a/IPython/html/services/kernelspecs/tests/test_kernelspecs_api.py b/IPython/html/services/kernelspecs/tests/test_kernelspecs_api.py index a82a751b5..0d43fb1de 100644 --- a/IPython/html/services/kernelspecs/tests/test_kernelspecs_api.py +++ b/IPython/html/services/kernelspecs/tests/test_kernelspecs_api.py @@ -78,16 +78,22 @@ class APITest(NotebookTestBase): with open(pjoin(bad_kernel_dir, 'kernel.json'), 'w') as f: f.write("garbage") - specs = self.ks_api.list().json() - assert isinstance(specs, list) + model = self.ks_api.list().json() + assert isinstance(model, dict) + self.assertEqual(model['default'], NATIVE_KERNEL_NAME) + specs = model['kernelspecs'] + assert isinstance(specs, dict) # 2: the sample kernelspec created in setUp, and the native Python kernel self.assertGreaterEqual(len(specs), 2) shutil.rmtree(bad_kernel_dir) def test_list_kernelspecs(self): - specs = self.ks_api.list().json() - assert isinstance(specs, list) + model = self.ks_api.list().json() + assert isinstance(model, dict) + self.assertEqual(model['default'], NATIVE_KERNEL_NAME) + specs = model['kernelspecs'] + assert isinstance(specs, dict) # 2: the sample kernelspec created in setUp, and the native Python kernel self.assertGreaterEqual(len(specs), 2) @@ -98,8 +104,8 @@ class APITest(NotebookTestBase): def is_default_kernelspec(s): return s['name'] == NATIVE_KERNEL_NAME and s['display_name'].startswith("IPython") - assert any(is_sample_kernelspec(s) for s in specs), specs - assert any(is_default_kernelspec(s) for s in specs), specs + assert any(is_sample_kernelspec(s) for s in specs.values()), specs + assert any(is_default_kernelspec(s) for s in specs.values()), specs def test_get_kernelspec(self): spec = self.ks_api.kernel_spec_info('Sample').json() # Case insensitive diff --git a/IPython/html/static/notebook/js/kernelselector.js b/IPython/html/static/notebook/js/kernelselector.js index 00da81928..46d488289 100644 --- a/IPython/html/static/notebook/js/kernelselector.js +++ b/IPython/html/static/notebook/js/kernelselector.js @@ -25,16 +25,27 @@ define([ KernelSelector.prototype.request_kernelspecs = function() { var url = utils.url_join_encode(this.notebook.base_url, 'api/kernelspecs'); - $.ajax(url, {success: $.proxy(this._got_kernelspecs, this)}); + utils.promising_ajax(url).then($.proxy(this._got_kernelspecs, this)); }; - KernelSelector.prototype._got_kernelspecs = function(data, status, xhr) { - this.kernelspecs = {}; + KernelSelector.prototype._got_kernelspecs = function(data) { + this.kernelspecs = data.kernelspecs; var menu = this.element.find("#kernel_selector"); var change_kernel_submenu = $("#menu-change-kernel-submenu"); - for (var i = 0; i < data.length; i++) { - var ks = data[i]; - this.kernelspecs[ks.name] = ks; + var keys = Object.keys(data.kernelspecs).sort(function (a, b) { + // sort by display_name + var da = data.kernelspecs[a].display_name; + var db = data.kernelspecs[b].display_name; + if (da === db) { + return 0; + } else if (da > db) { + return 1; + } else { + return -1; + } + }); + for (var i = 0; i < keys.length; i++) { + var ks = this.kernelspecs[keys[i]]; var ksentry = $("
  • ").attr("id", "kernel-" +ks.name).append($('') .attr('href', '#') .click($.proxy(this.change_kernel, this, ks.name)) diff --git a/IPython/html/static/tree/js/newnotebook.js b/IPython/html/static/tree/js/newnotebook.js index 37ff15d29..6a5039149 100644 --- a/IPython/html/static/tree/js/newnotebook.js +++ b/IPython/html/static/tree/js/newnotebook.js @@ -38,11 +38,21 @@ define([ NewNotebookWidget.prototype._load_kernelspecs = function (data) { /** load kernelspec list */ - this.kernelspecs = {}; + this.kernelspecs = data.kernelspecs; var menu = this.element.find("#new-notebook-menu"); - for (var i = 0; i < data.length; i++) { - var ks = data[i]; - this.kernelspecs[ks.name] = ks; + var keys = Object.keys(data.kernelspecs).sort(function (a, b) { + var da = data.kernelspecs[a].display_name; + var db = data.kernelspecs[b].display_name; + if (da === db) { + return 0; + } else if (da > db) { + return 1; + } else { + return -1; + } + }); + for (var i = 0; i < keys.length; i++) { + var ks = this.kernelspecs[keys[i]]; var li = $("
  • ") .attr("id", "kernel-" +ks.name) .data('kernelspec', ks).append( @@ -59,12 +69,12 @@ define([ ); menu.append(li); } - this._load_default_kernelspec(); + this._load_default_kernelspec(data['default']); }; - NewNotebookWidget.prototype._load_default_kernelspec = function () { + NewNotebookWidget.prototype._load_default_kernelspec = function (default_name) { /** load default kernelspec name from localStorage, if defined */ - this.select_kernel(localStorage.default_kernel_name); + this.select_kernel(localStorage.default_kernel_name || default_name); }; NewNotebookWidget.prototype.select_kernel = function (kernel_name) { From 8eb6ec00d8c3de720de4b1fa62965deeeaec521f Mon Sep 17 00:00:00 2001 From: Min RK Date: Mon, 17 Nov 2014 13:16:32 -0800 Subject: [PATCH 31/37] store current kernel selection in frontend config instead of localStorage --- IPython/html/static/tree/js/main.js | 6 ++++++ IPython/html/static/tree/js/newnotebook.js | 19 +++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/IPython/html/static/tree/js/main.js b/IPython/html/static/tree/js/main.js index 7eb99c9aa..6982b6ff1 100644 --- a/IPython/html/static/tree/js/main.js +++ b/IPython/html/static/tree/js/main.js @@ -8,6 +8,7 @@ require([ 'base/js/events', 'base/js/page', 'base/js/utils', + 'services/config', 'contents', 'tree/js/notebooklist', 'tree/js/clusterlist', @@ -27,6 +28,7 @@ require([ events, page, utils, + config, contents_service, notebooklist, clusterlist, @@ -43,6 +45,10 @@ require([ base_url: utils.get_body_data("baseUrl"), notebook_path: utils.get_body_data("notebookPath"), }; + var cfg = new config.ConfigSection('tree', common_options); + cfg.load(); + common_options.config = cfg; + var session_list = new sesssionlist.SesssionList($.extend({ events: events}, common_options)); diff --git a/IPython/html/static/tree/js/newnotebook.js b/IPython/html/static/tree/js/newnotebook.js index 6a5039149..a860712ea 100644 --- a/IPython/html/static/tree/js/newnotebook.js +++ b/IPython/html/static/tree/js/newnotebook.js @@ -15,6 +15,7 @@ define([ this.notebook_path = options.notebook_path; this.contents = options.contents; this.current_selection = null; + this.config = options.config; this.kernelspecs = {}; if (this.selector !== undefined) { this.element = $(selector); @@ -38,6 +39,7 @@ define([ NewNotebookWidget.prototype._load_kernelspecs = function (data) { /** load kernelspec list */ + var that = this; this.kernelspecs = data.kernelspecs; var menu = this.element.find("#new-notebook-menu"); var keys = Object.keys(data.kernelspecs).sort(function (a, b) { @@ -69,17 +71,30 @@ define([ ); menu.append(li); } - this._load_default_kernelspec(data['default']); + this.config.loaded.then(function () { + that._load_default_kernelspec(data['default']); + }); }; NewNotebookWidget.prototype._load_default_kernelspec = function (default_name) { /** load default kernelspec name from localStorage, if defined */ - this.select_kernel(localStorage.default_kernel_name || default_name); + if (this.config.data.NewNotebookWidget && + this.config.data.NewNotebookWidget.current_selection && + this.kernelspecs[this.config.data.NewNotebookWidget.current_selection] !== undefined + ) { + default_name = this.config.data.NewNotebookWidget.current_selection; + } + this.select_kernel(default_name); }; NewNotebookWidget.prototype.select_kernel = function (kernel_name) { /** select the current default kernel */ this.current_selection = kernel_name; + this.config.update({ + NewNotebookWidget: { + current_selection: kernel_name + } + }); var spec = this.kernelspecs[kernel_name]; var display_name; if (spec) { From fbaae651ad731e2abc5d749d30e7fc0dad1a2129 Mon Sep 17 00:00:00 2001 From: Min RK Date: Fri, 5 Dec 2014 12:15:46 -0800 Subject: [PATCH 32/37] remove UI for setting default kernel can be set with: IPython.new_notebook_widget.set_default_kernel('kernel_name') --- IPython/html/static/tree/js/main.js | 1 + IPython/html/static/tree/js/newnotebook.js | 35 ++++------------------ 2 files changed, 6 insertions(+), 30 deletions(-) diff --git a/IPython/html/static/tree/js/main.js b/IPython/html/static/tree/js/main.js index 6982b6ff1..986ebb58b 100644 --- a/IPython/html/static/tree/js/main.js +++ b/IPython/html/static/tree/js/main.js @@ -130,6 +130,7 @@ require([ IPython.session_list = session_list; IPython.kernel_list = kernel_list; IPython.login_widget = login_widget; + IPython.new_notebook_widget = nnw; events.trigger('app_initialized.DashboardApp'); diff --git a/IPython/html/static/tree/js/newnotebook.js b/IPython/html/static/tree/js/newnotebook.js index a860712ea..575331644 100644 --- a/IPython/html/static/tree/js/newnotebook.js +++ b/IPython/html/static/tree/js/newnotebook.js @@ -58,17 +58,12 @@ define([ var li = $("
  • ") .attr("id", "kernel-" +ks.name) .data('kernelspec', ks).append( - $('').attr('href', '#').append($('') - .addClass('kernel-menu-icon fa') - .attr('href', '#') - .click($.proxy(this.select_kernel, this, ks.name)) - ).append($('') + $('') .attr('href', '#') .click($.proxy(this.new_notebook, this, ks.name)) .text(ks.display_name) .attr('title', 'Create a new notebook with ' + ks.display_name) - ) - ); + ); menu.append(li); } this.config.loaded.then(function () { @@ -77,17 +72,17 @@ define([ }; NewNotebookWidget.prototype._load_default_kernelspec = function (default_name) { - /** load default kernelspec name from localStorage, if defined */ + /** load default kernelspec name from config, if defined */ if (this.config.data.NewNotebookWidget && this.config.data.NewNotebookWidget.current_selection && this.kernelspecs[this.config.data.NewNotebookWidget.current_selection] !== undefined ) { default_name = this.config.data.NewNotebookWidget.current_selection; } - this.select_kernel(default_name); + this.set_default_kernel(default_name); }; - NewNotebookWidget.prototype.select_kernel = function (kernel_name) { + NewNotebookWidget.prototype.set_default_kernel = function (kernel_name) { /** select the current default kernel */ this.current_selection = kernel_name; this.config.update({ @@ -99,33 +94,13 @@ define([ var display_name; if (spec) { display_name = spec.display_name; - localStorage.default_kernel_name = kernel_name; this.element.find("#current-kernel").text(display_name); } else { display_name = 'default kernel'; - delete localStorage.default_kernel_name; } this.element.find("#new_notebook").attr('title', 'Create a new notebook with ' + display_name ); - this.element.find("li").map(function (i, li) { - li = $(li); - var ks = li.data('kernelspec'); - if (ks.name == kernel_name) { - li.find(".kernel-menu-icon") - .attr('title', display_name + ' is the default kernel') - .addClass("kernel-menu-icon-current"); - } else { - li.find(".kernel-menu-icon") - .attr('title', 'Make ' + ks.display_name + ' the default kernel') - .removeClass("kernel-menu-icon-current"); - } - }); - }; - - NewNotebookWidget.prototype.new_with_kernel = function (kernel_name) { - /** record current selection and open a new notebook */ - this.new_notebook(kernel_name); }; NewNotebookWidget.prototype.new_notebook = function (kernel_name) { From d1bda196b761daf0a769ff8bfdcd1694da05275d Mon Sep 17 00:00:00 2001 From: Min RK Date: Fri, 5 Dec 2014 12:16:45 -0800 Subject: [PATCH 33/37] remove some weird tabs How did they get there? We may never know... --- IPython/html/static/tree/js/main.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/IPython/html/static/tree/js/main.js b/IPython/html/static/tree/js/main.js index 986ebb58b..194bd385b 100644 --- a/IPython/html/static/tree/js/main.js +++ b/IPython/html/static/tree/js/main.js @@ -89,18 +89,18 @@ require([ */ session_list.load_sessions(); cluster_list.load_list(); - if (terminal_list) { - terminal_list.load_terminals(); - } + if (terminal_list) { + terminal_list.load_terminals(); + } if (!interval_id){ interval_id = setInterval(function(){ - session_list.load_sessions(); - cluster_list.load_list(); - if (terminal_list) { - terminal_list.load_terminals(); - } - }, time_refresh*1000); - } + session_list.load_sessions(); + cluster_list.load_list(); + if (terminal_list) { + terminal_list.load_terminals(); + } + }, time_refresh*1000); + } }; var disable_autorefresh = function(){ From 371b031f409d2ff6dbf4443698e6f07466c262e1 Mon Sep 17 00:00:00 2001 From: Min RK Date: Fri, 5 Dec 2014 12:21:39 -0800 Subject: [PATCH 34/37] informative hover text for current selection --- IPython/html/static/tree/js/newnotebook.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/IPython/html/static/tree/js/newnotebook.js b/IPython/html/static/tree/js/newnotebook.js index 575331644..b550854a4 100644 --- a/IPython/html/static/tree/js/newnotebook.js +++ b/IPython/html/static/tree/js/newnotebook.js @@ -94,7 +94,9 @@ define([ var display_name; if (spec) { display_name = spec.display_name; - this.element.find("#current-kernel").text(display_name); + this.element.find("#current-kernel") + .text(display_name) + .attr('title', display_name + " is the default kernel for new notebooks"); } else { display_name = 'default kernel'; } From 6a1cebe4c0b8f425c0b68f711f3c95a65c84e1b7 Mon Sep 17 00:00:00 2001 From: Min RK Date: Fri, 5 Dec 2014 12:31:13 -0800 Subject: [PATCH 35/37] minor reflow of tree header - remove absolutely positioned text (absolute text doesn't wrap correctly) - associate hidden upload directly with 'click here' via span - remove column split, rely on regular reflow --- IPython/html/static/style/style.min.css | 13 +++++++------ IPython/html/static/tree/less/altuploadform.less | 14 ++++++++------ IPython/html/templates/tree.html | 15 ++++++++------- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/IPython/html/static/style/style.min.css b/IPython/html/static/style/style.min.css index 5f51faff9..15ee5ca86 100644 --- a/IPython/html/static/style/style.min.css +++ b/IPython/html/static/style/style.min.css @@ -7999,14 +7999,15 @@ span#login_widget > .button .badge, margin: 0; } .alternate_upload input.fileinput { - background-color: red; - position: relative; + display: inline; opacity: 0; z-index: 2; - width: 295px; - margin-left: 163px; - cursor: pointer; - height: 26px; + width: 12ex; + margin-right: -12ex; +} +.alternate_upload .input-overlay { + display: inline-block; + font-weight: bold; } /** * Primary styles diff --git a/IPython/html/static/tree/less/altuploadform.less b/IPython/html/static/tree/less/altuploadform.less index 70e865d57..1ce7c22c1 100644 --- a/IPython/html/static/tree/less/altuploadform.less +++ b/IPython/html/static/tree/less/altuploadform.less @@ -15,12 +15,14 @@ .alternate_upload input.fileinput { - background-color:red; - position:relative; + display: inline; opacity: 0; z-index: 2; - width: 295px; - margin-left:163px; - cursor: pointer; - height: 26px; + width: 12ex; + margin-right: -12ex; +} + +.alternate_upload .input-overlay { + display: inline-block; + font-weight: bold; } diff --git a/IPython/html/templates/tree.html b/IPython/html/templates/tree.html index bf814302f..22e63bc37 100644 --- a/IPython/html/templates/tree.html +++ b/IPython/html/templates/tree.html @@ -34,15 +34,16 @@ data-terminals-available="{{terminals_available}}"
    -
    -
    - - To import a notebook, drag the file onto the listing below or click here. +
    + + + To import a notebook, drag the file onto the listing below or + + + click here. + - -
    -