فهرست منبع

HUE-9001 [editor] Move editor related actions to dropdown button underneath the editor and improve the layout in notebook 2

- Format, explain, clear etc. moved from the left-hand side to a dropdown underneath the editor,
- The switch between text and file input moved into the settings panel
- The upper right settings button moved to the dropdown menu below
- Editor margins reduced
- Editor is now 100% height
Johan Ahlen 6 سال پیش
والد
کامیت
075b8ac0cb
20فایلهای تغییر یافته به همراه1752 افزوده شده و 215 حذف شده
  1. 1 0
      Gruntfile.js
  2. 196 0
      desktop/core/src/desktop/js/apps/notebook2/components/ko.snippetEditorActions.js
  3. 15 4
      desktop/core/src/desktop/js/apps/notebook2/components/ko.snippetExecuteActions.js
  4. 58 0
      desktop/core/src/desktop/js/apps/notebook2/components/spec/ko.snippetExecuteActions.spec.js
  5. 3 1
      desktop/core/src/desktop/js/apps/notebook2/execution/executor.js
  6. 0 7
      desktop/core/src/desktop/js/apps/notebook2/notebook.js
  7. 5 74
      desktop/core/src/desktop/js/apps/notebook2/snippet.js
  8. 1 0
      desktop/core/src/desktop/js/spec/jasmineSetup.js
  9. 0 0
      desktop/core/src/desktop/static/desktop/css/hue.css
  10. 0 0
      desktop/core/src/desktop/static/desktop/css/hue3-extra.css
  11. 34 0
      desktop/core/src/desktop/static/desktop/less/components/hue-snippet-editor-actions.less
  12. 2 5
      desktop/core/src/desktop/static/desktop/less/components/hue-snippet-execute-actions.less
  13. 1 0
      desktop/core/src/desktop/static/desktop/less/hue-cross-version.less
  14. 1 0
      desktop/core/src/desktop/templates/global_js_constants.mako
  15. 16 0
      desktop/libs/notebook/src/notebook/static/notebook/css/notebook2.css
  16. 1340 0
      desktop/libs/notebook/src/notebook/static/notebook/less/notebook2.less
  17. 12 6
      desktop/libs/notebook/src/notebook/templates/editor.mako
  18. 1 1
      desktop/libs/notebook/src/notebook/templates/editor_components.mako
  19. 64 115
      desktop/libs/notebook/src/notebook/templates/editor_components2.mako
  20. 2 2
      desktop/libs/notebook/src/notebook/templates/notebook.mako

+ 1 - 0
Gruntfile.js

@@ -43,6 +43,7 @@ module.exports = function(grunt) {
           'desktop/core/src/desktop/static/desktop/css/httperrors.css': 'desktop/core/src/desktop/static/desktop/less/httperrors.less',
           'apps/metastore/src/metastore/static/metastore/css/metastore.css': 'apps/metastore/src/metastore/static/metastore/less/metastore.less',
           'desktop/libs/notebook/src/notebook/static/notebook/css/notebook.css': 'desktop/libs/notebook/src/notebook/static/notebook/less/notebook.less',
+          'desktop/libs/notebook/src/notebook/static/notebook/css/notebook2.css': 'desktop/libs/notebook/src/notebook/static/notebook/less/notebook2.less',
           'desktop/libs/notebook/src/notebook/static/notebook/css/notebook-layout.css': 'desktop/libs/notebook/src/notebook/static/notebook/less/notebook-layout.less',
           'apps/oozie/src/oozie/static/oozie/css/workflow-editor.css': 'apps/oozie/src/oozie/static/oozie/less/workflow-editor.less',
           'apps/oozie/src/oozie/static/oozie/css/workflow.css': 'apps/oozie/src/oozie/static/oozie/less/workflow.less',

+ 196 - 0
desktop/core/src/desktop/js/apps/notebook2/components/ko.snippetEditorActions.js

@@ -0,0 +1,196 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import $ from 'jquery';
+import ko from 'knockout';
+
+import 'ko/bindings/ko.publish';
+
+import apiHelper from 'api/apiHelper';
+import componentUtils from 'ko/components/componentUtils';
+import hueAnalytics from 'utils/hueAnalytics';
+import I18n from 'utils/i18n';
+import { notebookToContextJSON, snippetToContextJSON } from 'apps/notebook2/notebookSerde';
+import { STATUS } from 'apps/notebook2/snippet';
+
+const TEMPLATE = `
+<div class="snippet-editor-actions">
+  <div class="btn-group">
+    <button class="btn btn-mini btn-editor dropdown-toggle" data-toggle="dropdown">
+      <i class="fa fa-fw fa-ellipsis-v"></i> ${I18n('More')}
+      <span class="caret"></span>
+    </button>
+    <ul class="dropdown-menu pull-right">
+      <li>
+        <a href="javascript:void(0)" data-bind="click: explain, css: {'disabled': !explainEnabled() }" title="${I18n(
+          'Explain the current SQL query'
+        )}">
+          <i class="fa fa-fw fa-map-o"></i> ${I18n('Explain')}
+        </a>
+      </li>
+      <li>
+        <a href="javascript:void(0)" data-bind="click: format, css: { 'disabled': !formatEnabled() }" title="${I18n(
+          'Format the current SQL query'
+        )}">
+          <i class="fa fa-fw fa-indent"></i> ${I18n('Format')}
+        </a>
+      </li>
+      <li>
+        <a href="javascript:void(0)" data-bind="click: clear, css: {'disabled': !clearEnabled() }" title="${I18n(
+          'Clear the current editor'
+        )}">
+          <i class="fa fa-fw fa-eraser"></i> ${I18n('Clear')}
+        </a>
+      </li>
+      <!-- ko if: window.HAS_OPTIMIZER -->
+      <li>
+        <a href="javascript:void(0)" data-bind="click: checkCompatibility, css: {'disabled': !compatibilityEnabled() }" title="${I18n(
+          'Get hints on how to port SQL from other databases'
+        )}">
+          <i class="fa fa-fw fa-random"></i> ${I18n('Check compatibility')}
+        </a>
+      </li>
+      <!-- ko if: window.DJANGO_DEBUG_MODE && window.USER_IS_ADMIN -->
+      <li>
+        <a href="javascript:void(0)" data-bind="publish: 'editor.upload.history'" title="${I18n(
+          'Load recent queries in order to improve recommendations'
+        )}">
+          <i class="fa fa-fw fa-cloud-upload"></i> ${I18n('Upload optimizer history')}
+        </a>
+      </li>
+      <!-- /ko -->
+      <!-- /ko -->
+      <li>
+        <a href="javascript:void(0)" data-bind="toggle: snippet.settingsVisible, visible: snippet.hasProperties" title="${I18n(
+          'Query settings'
+        )}">
+          <i class="fa fa-fw fa-cog"></i> ${I18n('Settings')}
+        </a>
+      </li>
+    </ul>
+  </div>
+</div>
+`;
+
+class SnippetEditorActions {
+  constructor(params) {
+    this.snippet = params.snippet;
+
+    this.clearEnabled = this.snippet.isReady;
+
+    this.compatibilityEnabled = ko.pureComputed(
+      () => this.snippet.type() === 'hive' || this.snippet.type() === 'impala'
+    );
+
+    this.explainEnabled = ko.pureComputed(
+      () =>
+        this.snippet.isReady() &&
+        this.snippet.statement() !== '' &&
+        this.snippet.status() !== STATUS.running &&
+        this.snippet.status() !== STATUS.loading
+    );
+
+    this.formatEnabled = ko.pureComputed(
+      () =>
+        this.snippet.isReady() &&
+        this.snippet.isSqlDialect() &&
+        this.snippet.statement_raw() &&
+        this.snippet.statement_raw().length < 400000
+    );
+  }
+
+  checkCompatibility() {
+    if (!this.compatibilityEnabled()) {
+      return;
+    }
+    hueAnalytics.log('notebook', 'compatibility');
+
+    // TODO: Move compatibility check logic here
+    this.snippet.checkCompatibility();
+  }
+
+  clear() {
+    if (!this.clearEnabled()) {
+      return;
+    }
+    hueAnalytics.log('notebook', 'clear');
+
+    this.snippet.ace().setValue('', 1);
+    this.snippet.result.clear();
+    this.snippet.status(STATUS.ready);
+  }
+
+  explain() {
+    if (!this.explainEnabled()) {
+      return;
+    }
+    hueAnalytics.log('notebook', 'explain');
+
+    this.snippet.result.explanation('');
+    this.snippet.errors([]);
+    this.snippet.progress(0);
+    this.snippet.status(STATUS.ready);
+
+    $.post('/notebook/api/explain', {
+      notebook: notebookToContextJSON(this.snippet.parentNotebook),
+      snippet: snippetToContextJSON(this.snippet)
+    }).then(data => {
+      if (data.status === 0) {
+        this.snippet.currentQueryTab('queryExplain');
+        this.snippet.result.fetchedOnce(true);
+        this.snippet.result.explanation(data.explanation);
+      } else {
+        this.snippet.handleAjaxError(data);
+      }
+    });
+  }
+
+  format() {
+    if (!this.formatEnabled()) {
+      return;
+    }
+
+    hueAnalytics.log('notebook', 'format');
+    apiHelper
+      .formatSql({
+        statements:
+          this.snippet.ace().getSelectedText() !== ''
+            ? this.snippet.ace().getSelectedText()
+            : this.snippet.statement_raw()
+      })
+      .done(data => {
+        if (data.status === 0) {
+          if (this.snippet.ace().getSelectedText() !== '') {
+            this.snippet
+              .ace()
+              .session.replace(
+                this.snippet.ace().session.selection.getRange(),
+                data.formatted_statements
+              );
+          } else {
+            this.snippet.statement_raw(data.formatted_statements);
+            this.snippet.ace().setValue(this.snippet.statement_raw(), 1);
+          }
+        } else {
+          this.snippet.handleAjaxError(data);
+        }
+      });
+  }
+
+  dispose() {}
+}
+
+componentUtils.registerComponent('snippet-editor-actions', SnippetEditorActions, TEMPLATE);

+ 15 - 4
desktop/core/src/desktop/js/apps/notebook2/components/ko.snippetExecuteButtonActions.js → desktop/core/src/desktop/js/apps/notebook2/components/ko.snippetExecuteActions.js

@@ -19,7 +19,10 @@ import ko from 'knockout';
 import componentUtils from 'ko/components/componentUtils';
 import huePubSub from 'utils/huePubSub';
 import I18n from 'utils/i18n';
-import { EXECUTION_STATUS } from '../execution/executableStatement';
+import { EXECUTION_STATUS } from 'apps/notebook2/execution/executableStatement';
+import { EXECUTOR_UPDATED_EVENT } from 'apps/notebook2/execution/executor';
+
+export const NAME = 'snippet-execute-actions';
 
 const TEMPLATE = `
 <div class="snippet-execute-actions">
@@ -47,14 +50,18 @@ const TEMPLATE = `
 
 class SnippetExecuteActions {
   constructor(params) {
+    this.disposals = [];
     this.stopping = ko.observable(false);
     this.snippet = params.snippet;
     this.status = ko.observable(EXECUTION_STATUS.ready);
-    huePubSub.subscribe('hue.executor.updated', executorUpdate => {
+    const updateSub = huePubSub.subscribe('hue.executor.updated', executorUpdate => {
       if (this.snippet.executor() === executorUpdate.executor) {
         this.status(executorUpdate.executable.status);
       }
     });
+    this.disposals.push(() => {
+      updateSub.remove();
+    });
   }
 
   async stop() {
@@ -72,7 +79,11 @@ class SnippetExecuteActions {
     this.snippet.execute();
   }
 
-  dispose() {}
+  dispose() {
+    while (this.disposals.length) {
+      this.disposals.pop()();
+    }
+  }
 }
 
-componentUtils.registerComponent('snippet-execute-actions', SnippetExecuteActions, TEMPLATE);
+componentUtils.registerComponent(NAME, SnippetExecuteActions, TEMPLATE);

+ 58 - 0
desktop/core/src/desktop/js/apps/notebook2/components/spec/ko.snippetExecuteActions.spec.js

@@ -0,0 +1,58 @@
+import huePubSub from 'utils/huePubSub';
+import { koSetup } from 'spec/jasmineSetup';
+import { NAME } from '../ko.snippetExecuteActions';
+import { EXECUTION_STATUS } from 'apps/notebook2/execution/executableStatement';
+import { EXECUTOR_UPDATED_EVENT } from 'apps/notebook2/execution/executor';
+
+describe('ko.snippetExecuteActions.js', () => {
+  const setup = koSetup();
+
+  it('should render component', async () => {
+    const element = await setup.renderComponent(NAME, {});
+
+    expect(element.querySelector('.snippet-execute-actions')).toBeTruthy();
+  });
+
+  it('should handle play and stop clicks', async () => {
+    let executeCalled = false;
+    let cancelCalled = false;
+    const mockExecutor = {
+      cancel: () => {
+        cancelCalled = true;
+      }
+    };
+    const snippet = {
+      executor: () => mockExecutor,
+      execute: () => {
+        executeCalled = true;
+      }
+    };
+    const wrapper = await setup.renderComponent(NAME, {
+      snippet: snippet
+    });
+
+    // Click play
+    expect(executeCalled).toBeFalsy();
+    expect(wrapper.querySelector('.fa-play')).toBeTruthy();
+    expect(wrapper.querySelector('.fa-stop')).toBeFalsy();
+    wrapper.querySelector('.fa-play').click();
+
+    expect(executeCalled).toBeTruthy();
+    huePubSub.publish(EXECUTOR_UPDATED_EVENT, {
+      executor: mockExecutor,
+      executable: {
+        status: EXECUTION_STATUS.running
+      }
+    });
+
+    await setup.waitForKoUpdate();
+
+    // Click stop
+    expect(cancelCalled).toBeFalsy();
+    expect(wrapper.querySelector('.fa-play')).toBeFalsy();
+    expect(wrapper.querySelector('.fa-stop')).toBeTruthy();
+    wrapper.querySelector('.fa-stop').click();
+
+    expect(cancelCalled).toBeTruthy();
+  });
+});

+ 3 - 1
desktop/core/src/desktop/js/apps/notebook2/execution/executor.js

@@ -28,6 +28,8 @@ const EXECUTION_FLOW = {
   // batchNoBreak: 'batchNoBreak'
 };
 
+export const EXECUTOR_UPDATED_EVENT = 'hue.executor.updated';
+
 const BATCHABLE_STATEMENT_TYPES = /ALTER|CREATE|DELETE|DROP|GRANT|INSERT|INVALIDATE|LOAD|SET|TRUNCATE|UPDATE|UPSERT|USE/i;
 
 class Executor {
@@ -91,7 +93,7 @@ class Executor {
         executable === this.currentExecutable ||
         this.executed.some(executed => executed === executable)
       ) {
-        huePubSub.publish('hue.executor.updated', {
+        huePubSub.publish(EXECUTOR_UPDATED_EVENT, {
           executable: executable,
           executor: this
         });

+ 0 - 7
desktop/core/src/desktop/js/apps/notebook2/notebook.js

@@ -226,13 +226,6 @@ export default class Notebook {
     return newSnippet;
   }
 
-  cancelExecutingAll() {
-    const index = this.executingAllIndex();
-    if (this.isExecutingAll() && this.snippets()[index]) {
-      this.snippets()[index].cancel();
-    }
-  }
-
   clearHistory() {
     hueAnalytics.log('notebook', 'clearHistory');
     apiHelper

+ 5 - 74
desktop/core/src/desktop/js/apps/notebook2/snippet.js

@@ -19,19 +19,20 @@ import ko from 'knockout';
 import komapping from 'knockout.mapping';
 import { markdown } from 'markdown';
 
+import 'apps/notebook2/components/ko.snippetEditorActions';
+import 'apps/notebook2/components/ko.snippetExecuteActions';
+
 import AceAutocompleteWrapper from 'apps/notebook/aceAutocompleteWrapper';
 import apiHelper from 'api/apiHelper';
 import dataCatalog from 'catalog/dataCatalog';
-import { ExecutableStatement } from 'apps/notebook2/execution/executableStatement';
 import Executor from 'apps/notebook2/execution/executor';
 import hueAnalytics from 'utils/hueAnalytics';
 import huePubSub from 'utils/huePubSub';
 import hueUtils from 'utils/hueUtils';
 import Result from 'apps/notebook2/result';
 import sessionManager from 'apps/notebook2/execution/sessionManager';
-
-import 'apps/notebook2/components/ko.snippetExecuteButtonActions';
-import { notebookToContextJSON, snippetToContextJSON } from './notebookSerde';
+import { ExecutableStatement } from 'apps/notebook2/execution/executableStatement';
+import { notebookToContextJSON, snippetToContextJSON } from 'apps/notebook2/notebookSerde';
 
 // TODO: Remove. Temporary here for debug
 window.ExecutableStatement = ExecutableStatement;
@@ -1098,11 +1099,6 @@ export default class Snippet {
     this.lastExecutedStatements = undefined;
     this.lastExecutedSelectionRange = undefined;
 
-    this.formatEnabled = ko.pureComputed(
-      () =>
-        this.statement_raw && this.statement_raw() != null && this.statement_raw().length < 400000
-    );
-
     this.lastCompatibilityRequest = undefined;
 
     this.isFetchingData = false;
@@ -1308,13 +1304,6 @@ export default class Snippet {
     _getLogs(activeStatus.indexOf(this.status()) < 0);
   }
 
-  clear() {
-    hueAnalytics.log('notebook', 'clear');
-    this.ace().setValue('', 1);
-    this.result.clear();
-    this.status(STATUS.ready);
-  }
-
   close() {
     if (this.checkStatusTimeout != null) {
       clearTimeout(this.checkStatusTimeout);
@@ -1402,36 +1391,6 @@ export default class Snippet {
     }
   }
 
-  explain() {
-    hueAnalytics.log('notebook', 'explain');
-
-    if (
-      this.statement() === '' ||
-      this.status() === STATUS.running ||
-      this.status() === STATUS.loading
-    ) {
-      return;
-    }
-
-    this.result.explanation('');
-    this.errors([]);
-    this.progress(0);
-    this.status(STATUS.ready);
-
-    $.post('/notebook/api/explain', {
-      notebook: notebookToContextJSON(this.parentNotebook),
-      snippet: snippetToContextJSON(this)
-    }).then(data => {
-      if (data.status === 0) {
-        this.currentQueryTab('queryExplain');
-        this.result.fetchedOnce(true);
-        this.result.explanation(data.explanation);
-      } else {
-        this.handleAjaxError(data);
-      }
-    });
-  }
-
   async exportHistory() {
     const historyResponse = await apiHelper.getHistory({ type: this.type(), limit: 500 });
 
@@ -1582,34 +1541,6 @@ export default class Snippet {
       });
   }
 
-  format() {
-    if (this.isSqlDialect()) {
-      apiHelper
-        .formatSql({
-          statements:
-            this.ace().getSelectedText() !== ''
-              ? this.ace().getSelectedText()
-              : this.statement_raw()
-        })
-        .done(data => {
-          if (data.status === 0) {
-            if (this.ace().getSelectedText() !== '') {
-              this.ace().session.replace(
-                this.ace().session.selection.getRange(),
-                data.formatted_statements
-              );
-            } else {
-              this.statement_raw(data.formatted_statements);
-              this.ace().setValue(this.statement_raw(), 1);
-            }
-          } else {
-            this.handleAjaxError(data);
-          }
-        });
-    }
-    hueAnalytics.log('notebook', 'format');
-  }
-
   getExternalStatement() {
     this.externalStatementLoaded(false);
     apiHelper

+ 1 - 0
desktop/core/src/desktop/js/spec/jasmineSetup.js

@@ -60,6 +60,7 @@ export const koSetup = () => {
   afterEach(() => {
     ko.components.defaultLoader.loadTemplate = originalLoadTemplate;
     wrapper.parentNode.removeChild(wrapper);
+    ko.cleanNode(wrapper);
   });
 
   return {

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
desktop/core/src/desktop/static/desktop/css/hue.css


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
desktop/core/src/desktop/static/desktop/css/hue3-extra.css


+ 34 - 0
desktop/core/src/desktop/static/desktop/less/components/hue-snippet-editor-actions.less

@@ -0,0 +1,34 @@
+/*
+ Licensed to Cloudera, Inc. under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  Cloudera, Inc. licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+@import (reference) "cui/colors.less";
+@import (reference) "cui/extra-variables.less";
+@import (reference) "hue-mixins.less";
+
+.snippet-editor-actions {
+  display: inline-block;
+
+  .btn-group {
+    .btn-editor {
+      font-size: 11px;
+      border-radius: 2px;
+      padding-top: 2px;
+      padding-bottom: 1px;
+    }
+  }
+}

+ 2 - 5
desktop/core/src/desktop/static/desktop/less/components/hue-snippet-execute-actions.less

@@ -23,17 +23,14 @@
 .snippet-execute-actions {
   display: inline-block;
 
-  .dropdown-toggle {
-    padding-top: 1px;
-    padding-bottom: 1px;
-  }
-
   .btn-group {
     .btn {
+      font-size: 11px;
       border-bottom-color: @hue-primary-color-dark;
     }
 
     .btn-execute {
+      padding-top: 2px;
       border-radius: 2px;
     }
 

+ 1 - 0
desktop/core/src/desktop/static/desktop/less/hue-cross-version.less

@@ -39,6 +39,7 @@
 @import "components/hue-selector.less";
 @import "components/hue-shepherd.less";
 @import "components/hue-simple-ace.less";
+@import "components/hue-snippet-editor-actions.less";
 @import "components/hue-snippet-execute-actions.less";
 @import "components/hue-spinner.less";
 @import "components/hue-tables.less";

+ 1 - 0
desktop/core/src/desktop/templates/global_js_constants.mako

@@ -509,6 +509,7 @@
 
   window.USER_VIEW_EDIT_USER_ENABLED = '${ user.has_hue_permission(action="access_view:useradmin:edit_user", app="useradmin") or is_admin(user) }' === 'True';
   window.USER_IS_ADMIN = '${ is_admin(user) }' === 'True';
+  window.DJANGO_DEBUG_MODE = '${ conf.DJANGO_DEBUG_MODE.get() }' === 'True';
   window.IS_LDAP_SETUP = '${ 'desktop.auth.backend.LdapBackend' in conf.AUTH.BACKEND.get() }' === 'True';
   window.LOGGED_USERNAME = '${ user.username }';
   window.LOGGED_USER_ID = ${ user.id };

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 16 - 0
desktop/libs/notebook/src/notebook/static/notebook/css/notebook2.css


+ 1340 - 0
desktop/libs/notebook/src/notebook/static/notebook/less/notebook2.less

@@ -0,0 +1,1340 @@
+/*
+ Licensed to Cloudera, Inc. under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  Cloudera, Inc. licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+@import (reference) "cui/colors.less";
+@import (reference) "cui/extra-variables.less";
+@import (reference) "hue-mixins.less";
+
+#editorComponents,
+#embeddable_editor,
+.editor-mode,
+.vertical-full,
+.content-panel {
+  height: 100%;
+}
+
+.notebook {
+  .snippet-move-placeholder {
+    margin: 10px;
+    height: 100px;
+    background-color: @cui-gray-050;
+    border: 2px dotted @cui-default-border-color;
+  }
+
+  .snippet-move-helper {
+    margin: 10px;
+    background-color: @cui-white;
+    border: 1px solid @cui-purple-gray-050;
+  }
+
+  .resizer {
+    position: absolute;
+    height: 100%;
+    width: 4px;
+    cursor: col-resize;
+  }
+
+  .resize-bar {
+    position: absolute;
+    height: 100%;
+    width: 2px;
+    background-color: @cui-default-border-color;
+  }
+
+  .question {
+    margin: 10px;
+  }
+
+  .snippet {
+    padding-bottom: 10px;
+    margin-left: 18px;
+    margin-right: 18px;
+  }
+
+  .notebook-snippet {
+    border-bottom: 1px solid @cui-default-border-color;
+    border-radius: 0;
+  }
+
+  .snippet.editor-mode {
+    border-bottom: none;
+  }
+
+  .snippet-body {
+    z-index: 90;
+    min-height: 40px;
+  }
+
+  .snippet-text .snippet-body {
+    min-height: 1px;
+  }
+
+  .snippet-text.card-widget {
+    min-height: 1px;
+  }
+
+  .snippet-body h2 {
+    padding-left: 0 !important;
+  }
+
+  .snippet-settings {
+    background-color: @cui-white;
+    border-left: 1px solid @cui-default-border-color;
+    border-right: 1px solid @cui-default-border-color;
+    border-bottom: 1px solid @cui-default-border-color;
+    .hue-box-shadow-bottom;
+  }
+
+  .snippet-settings-header > h4 {
+    margin: 0;
+    padding: 10px;
+  }
+
+  .snippet-settings-body {
+    padding: 10px;
+  }
+
+  .CodeMirror {
+    height: auto;
+  }
+
+  .card .card-heading.simple {
+    border: none;
+  }
+
+  .add-snippet {
+    margin: 10px;
+    margin-top: 60px;
+    margin-bottom: 60px;
+  }
+
+  .add-snippet-button {
+    z-index: 300;
+    margin-left: auto;
+    margin-right: auto;
+    width: 220px;
+    padding: 20px;
+  }
+
+  .add-snippet-button > i {
+    color: @cui-gray-400;
+    cursor: pointer;
+  }
+
+  .select-snippet-button {
+    z-index: 500;
+    position: absolute;
+    top: 62px;
+    left: 62px;
+    width: 23px;
+    height: 18px;
+    line-height: 12px;
+    background-color: @cui-gray-400;
+    cursor: pointer;
+    border: 2px solid @cui-white;
+    color: @cui-white;
+    border-radius: 7px;
+  }
+
+  .add-snippet-alt {
+    z-index: 400;
+    position: absolute;
+    background-color: @hue-primary-color-dark;
+    color: @cui-blue-gray-100;
+    height: 54px;
+    width: 54px;
+    border-radius: 28px;
+    border: 1px solid;
+  }
+
+  .add-snippet-alt > div {
+    display: table-cell;
+    text-align: center;
+    vertical-align: middle;
+    line-height: 14px;
+    width: 50px;
+    height: 54px;
+    padding-left:2px;
+    font-size: 11px;
+  }
+
+  .snippet-list-alts {
+    margin: 0;
+    padding: 0;
+    list-style: none;
+  }
+
+  .snippet-list-alts > li {
+    padding: 5px 0 5px 10px;
+    cursor: pointer;
+    color: @cui-gray-800;
+    margin-bottom: 2px;
+  }
+
+  .snippet-list-alts > li:hover {
+    padding: 5px 0 5px 10px;
+    background-color: @cui-purple-gray-050;
+  }
+
+  .select-snippet-button,
+  .add-snippet-button > i,
+  .add-snippet-alt {
+    -webkit-transition: color 0.3s ease, -webkit-transform 0.3s;
+    -moz-transition: color 0.3s ease, -moz-transform 0.3s;
+    -ms-transition: color 0.3s ease, -ms-transform 0.3s;
+    transition: color 0.3s ease, transform 0.3s;
+  }
+
+  .add-snippet-alt:hover {
+    color: @cui-white;
+  }
+
+  .select-snippet-button:hover,
+  .add-snippet-button > i:hover,
+  .add-snippet-alt:hover {
+    -webkit-transform: scale(1.3, 1.3);
+    -moz-transform: scale(1.3, 1.3);
+    -ms-transform: scale(1.3, 1.3);
+    transform: scale(1.3, 1.3);
+  }
+
+  .spark-btn {
+    border-left: none;
+    border-right: none;
+    width: 14px;
+    opacity: 0.8;
+    border-radius: 0;
+  }
+
+  .spark-btn:hover {
+    opacity: 1;
+  }
+
+  .codeMirror-overlaybtn {
+    position: absolute;
+    margin-top: -30px;
+    margin-left: 0;
+    z-index: 999;
+  }
+
+
+  .nav {
+    margin-bottom: 0;
+  }
+
+  .nav i {
+    line-height: 20px !important;
+  }
+
+  .nav-list > li > a, .nav-list .nav-header {
+    padding-left: 0;
+    margin-left: 0;
+    margin-bottom: -7px;
+    margin-top: 10px !important;
+  }
+
+  .result-settings {
+    .nav-list .nav-header {
+      margin-bottom: -3px;
+      padding-left: 3px;
+    }
+    .checkbox {
+      padding-top: 0;
+    }
+  }
+
+  .nav-list {
+    padding-left: 0;
+  }
+
+  .dropdown-menu li {
+    font-size: 14px;
+  }
+
+  .dropdown a.dropdown-toggle {
+    padding-left: 10px;
+    padding-right: 10px;
+  }
+
+  .search-bar {
+    background-color: @cui-gray-050;
+    margin: 0;
+    padding: 4px 20px 0;
+    text-align: left;
+    width: 100%;
+    position: fixed;
+    top: 28px;
+    z-index: 1000;
+  }
+
+  .search-bar input {
+    margin-left: 10px;
+    padding-right: 20px;
+  }
+
+  .search-bar ul li {
+    text-align: right;
+  }
+
+  .search-bar ul.dropdown-menu li {
+    text-align: left;
+  }
+
+  .tab-content {
+    background-color: @cui-white;
+    border-left: 1px solid @cui-default-border-color;
+    border-right: 1px solid @cui-default-border-color;
+  }
+
+  .editor-help {
+    b {
+      font-weight: 500;
+    }
+    .nav-list > li > a {
+      padding: 8px 15px !important;
+      margin-bottom: 2px;
+      margin-top: 2px !important;
+      margin-left: -15px;
+    }
+    .help-list-spacing li {
+      margin-bottom: 15px;
+    }
+    .tab-content {
+      border: none;
+    }
+    .tab-pane > .tab-content {
+      padding: 20px 10px;
+      line-height: 24px;
+    }
+  }
+
+  .two-pane {
+    .display-flex();
+
+    .tab-content {
+      .flex(1);
+    }
+  }
+
+  .two-pane-left {
+    .flex(0 0 150px);
+    border-right: 1px solid @cui-gray-100;
+    margin-right: 10px;
+  }
+
+  .empty-title {
+    font-weight: normal;
+  }
+
+  .snippet-icon-image {
+    height: 16px;
+    width: 16px;
+    -webkit-filter: grayscale(1);
+    -webkit-filter: grayscale(100%);
+    filter: grayscale(100%);
+    -webkit-transition: -webkit-filter 0.2s ease;
+    -moz-transition: filter 0.2s ease;
+    -ms-transition: filter 0.2s ease;
+  }
+
+  .inactive-action:hover > .snippet-icon-image {
+    -webkit-filter: grayscale(0);
+    -webkit-filter: grayscale(0);
+    filter: grayscale(0);
+  }
+
+  .snippet-icon {
+    color: @cui-gray-500;
+    height: 16px;
+    width: 16px;
+    -webkit-transition: color 0.2s ease;
+    -moz-transition: color 0.2s ease;
+    -ms-transition: color 0.2s ease;
+    transition: color 0.2s ease;
+  }
+
+  .inactive-action:hover > .snippet-icon {
+    color: @hue-primary-color-dark !important;
+  }
+
+  .row-container.is-editing {
+    border: none;
+  }
+
+  .ui-sortable {
+    background-color: @cui-white;
+    min-height: 0;
+  }
+
+  .editable-click, a.editable-click, a.editable-click:hover {
+    cursor: pointer !important;
+  }
+
+  .editable-click.editable-disabled, a.editable-click.editable-disabled, a.editable-click.editable-disabled:hover {
+    cursor: default !important;
+  }
+
+  .results table tr td {
+    white-space: nowrap;
+  }
+
+  [contenteditable]:focus {
+    outline: none;
+  }
+
+  .snippet-progress-container {
+    height: 3px;
+    overflow: hidden;
+    margin-bottom: 2px;
+  }
+
+  .snippet-logs-btn {
+    margin-top: -9px;
+    margin-left: -3px;
+    font-size: 20px;
+  }
+
+  .snippet-execution-status {
+    margin-top: 10px;
+    margin-bottom: 4px;
+  }
+
+  .ace-container-resizable {
+    position: relative;
+    min-height: 128px !important;
+  }
+
+  .ace-editor-resizable {
+    position: absolute;
+    top: 0;
+    left:0;
+    bottom:0;
+    right: 0;
+    overflow: hidden !important;
+  }
+
+  .snippet-code-resizer, .snippet-log-resizer {
+    border-top: 1px solid @cui-default-border-color;
+    cursor: row-resize;
+    text-align: center;
+    font-size: 12px;
+    height: 8px;
+    margin-bottom: 5px;
+    color: @cui-gray-200;
+  }
+
+  .snippet-code-resizer > i, .snippet-log-resizer > i {
+    -ms-transform: scale(1.3,1); /* IE 9 */
+    -webkit-transform: scale(1.3,1); /* Safari */
+    transform: scale(1.3,1);
+  }
+
+  .snippet-log-expander {
+    position: absolute;
+    text-align: center;
+    cursor: pointer;
+    bottom: 4px;
+    width: 100%;
+  }
+
+  .snippet-log-expander:hover {
+    background-color: @cui-white;
+    opacity:.5;
+  }
+
+  .snippet-footer-actions-bar {
+    padding: 0 3px;
+    height: 29px;
+  }
+
+  .text-snippet h2 {
+    padding-left: 0 !important;
+  }
+
+  .span12.nomargin {
+    margin-left: 0;
+  }
+
+  .show-assist,
+  .show-assist-right {
+    position: fixed;
+    top: 80px;
+    background-color: @cui-white;
+    width: 16px;
+    height: 24px;
+    line-height: 24px;
+    text-align: center;
+    -webkit-border-top-right-radius: 3px;
+    -webkit-border-bottom-right-radius: 3px;
+    -moz-border-radius-topright: 3px;
+    -moz-border-radius-bottomright: 3px;
+    border-top-right-radius: 3px;
+    border-bottom-right-radius: 3px;
+    z-index: 1000;
+  }
+
+  .show-assist {
+    left: 8px;
+    margin-left: -2px;
+    -webkit-transition: margin-left 0.2s linear;
+    -moz-transition: margin-left 0.2s linear;
+    -ms-transition: margin-left 0.2s linear;
+    transition: margin-left 0.2s linear;
+  }
+
+  .show-assist-right {
+    right: 8px;
+    margin-right: -2px;
+    -webkit-transition: margin-right 0.2s linear;
+    -moz-transition: margin-right 0.2s linear;
+    -ms-transition: margin-right 0.2s linear;
+    transition: margin-right 0.2s linear;
+  }
+
+  .show-assist:hover {
+    margin-left: 0;
+  }
+
+  .show-assist-right:hover {
+    margin-right: 0;
+  }
+
+  .hide-assist,
+  .hide-assist-right {
+    position: absolute;
+    top: 2px;
+    z-index: 1000;
+    color: @cui-gray-500;
+    font-size: 12px;
+  }
+
+  .hide-assist {
+    right: 4px;
+    -webkit-transition: margin-right 0.2s linear, color 0.5s ease;
+    -moz-transition: margin-right 0.2s linear, color 0.5s ease;
+    -ms-transition: margin-right 0.2s linear, color 0.5s ease;
+    transition: margin-right 0.2s linear, color 0.5s ease;
+  }
+
+  .hide-assist-right {
+    left: 4px;
+    -webkit-transition: margin-left 0.2s linear, color 0.5s ease;
+    -moz-transition: margin-left 0.2s linear, color 0.5s ease;
+    -ms-transition: margin-left 0.2s linear, color 0.5s ease;
+    transition: margin-left 0.2s linear, color 0.5s ease;
+  }
+
+  .hide-assist:hover {
+    margin-right: 2px;
+    color: @hue-primary-color-dark;
+  }
+
+  .hide-assist-right:hover {
+    margin-left: 2px;
+    color: @hue-primary-color-dark;
+  }
+
+
+  .logs {
+    padding: 9.5px;
+    font-size: 11px;
+    height: 80px;
+    overflow-y: auto;
+    margin-bottom: 0;
+  }
+
+  h1.empty {
+    margin-top: 50px;
+    text-align: center;
+    color: @cui-gray-300;
+  }
+
+  .toggle-result-settings {
+    float: left;
+    padding: 3px;
+    margin-top: 31px;
+    width: 10px;
+    text-align: center;
+    cursor: pointer;
+  }
+
+  .toggle-result-settings .close {
+    margin-right: 2px;
+  }
+
+  .toggle-result-settings .open {
+    margin-left: 2px;
+  }
+
+  .chart-icon {
+    width: 15px;
+    text-align: center;
+  }
+
+  .resize-panel a {
+    cursor: ns-resize;
+    color: @cui-gray-700;
+    position: absolute;
+  }
+
+  .progress-snippet {
+    background-image: none;
+    box-shadow: none;
+  }
+
+  .progress-snippet .bar {
+    background-image: none;
+  }
+
+  .progress .bar {
+    background-repeat: repeat-x;
+  }
+
+  .progress, .progress-neutral .bar {
+    background-color: @cui-gray-300;
+  }
+
+  .progress .bar {
+    background-color: @cui-white;
+  }
+
+  .progress .bar-info {
+    background-color: @hue-primary-color-dark;
+  }
+
+  .progress-starting .bar {
+    background-color: @hue-primary-color-dark;
+  }
+
+  @-webkit-keyframes pulsate {
+    0% {
+      margin-left: 0;
+    }
+    50% {
+      margin-left: 30px;
+    }
+    100% {
+      margin-left: 0;
+    }
+  }
+
+  @-moz-keyframes pulsate {
+    0% {
+      margin-left: 0;
+    }
+    50% {
+      margin-left: 30px;
+    }
+    100% {
+      margin-left: 0;
+    }
+  }
+
+  @-o-keyframes pulsate {
+    0% {
+      margin-left: 0;
+    }
+    50% {
+      margin-left: 30px;
+    }
+    100% {
+      margin-left: 0;
+    }
+  }
+
+  @keyframes pulsate {
+    0% {
+      margin-left: 0;
+    }
+    50% {
+      margin-left: 30px;
+    }
+    100% {
+      margin-left: 0;
+    }
+  }
+
+  .progress-starting {
+    -webkit-animation: pulsate 1s infinite;
+    -moz-animation: pulsate 1s infinite;
+    -o-animation: pulsate 1s infinite;
+    animation: pulsate 1s infinite;
+  }
+
+  .progress-success .bar, .progress .bar-success {
+    background-color: @cui-green-400;
+  }
+
+  .progress-warning .bar, .progress .bar-warning {
+    background-color: @cui-orange-200;
+  }
+
+  .progress-danger .bar, .progress .bar-danger {
+    background-color: @cui-red-400;
+  }
+
+  .text-snippet {
+    padding-left: 4px;
+  }
+
+  .medium-editor-toolbar-form label {
+    min-height: 20px;
+  }
+
+  .medium-editor-toolbar-form input[type="checkbox"] {
+    float: left;
+    margin-left: 0;
+    margin-right: 4px;
+  }
+
+  .medium-editor-toolbar li button {
+    padding: 0 !important;
+    height: 40px !important;
+    min-width: 40px !important;
+  }
+
+  .medium-toolbar-arrow-under:after {
+    top: 40px !important;
+  }
+
+  .medium-editor-placeholder:after {
+    padding-left: 5px;
+    font-size: 13px !important;
+    font-family: @font-family-monospace !important;
+    color: @cui-gray-500 !important;
+    font-style: normal !important;
+  }
+
+  .card-heading-print {
+    display: none;
+  }
+
+  .no-underline:hover {
+    text-decoration: none !important;
+  }
+
+  input[type="text"].filechooser-input {
+    border-right: none;
+    margin-bottom: 0;
+  }
+
+  table.airy tr td {
+    padding: 4px;
+  }
+
+  .ace-filechooser {
+    position: absolute;
+    display: none;
+    z-index: 20000;
+    background-color: @cui-white;
+    padding: 10px;
+    min-width: 350px;
+  }
+
+  .hover-dropdown {
+    display: inline-block;
+    position: relative;
+  }
+
+  .results.alert-error, .alert-error-gradient {
+    border: none;
+    margin-top: -3px;
+    background: -moz-linear-gradient(left, rgba(242, 222, 222, 1) 0, rgba(255, 255, 255, 0) 100%);
+    background: -webkit-linear-gradient(left, rgba(242, 222, 222, 1) 0, rgba(255, 255, 255, 0) 100%);
+    background: -o-linear-gradient(left, rgba(242, 222, 222, 1) 0, rgba(255, 255, 255, 0) 100%);
+    background: -ms-linear-gradient(left, rgba(242, 222, 222, 1) 0, rgba(255, 255, 255, 0) 100%);
+    background: linear-gradient(to right, rgba(242, 222, 222, 1) 0, rgba(255, 255, 255, 0) 100%);
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='@cui-pink-050', endColorstr='#00ffffff', GradientType=1)
+  }
+
+
+  .results-images {
+    border-left: 5px solid @cui-default-border-color;
+    background-color: @cui-gray-050;
+    padding: 5px;
+  }
+
+  .alert-notebook, .alert-gradient {
+    border: none;
+    margin-top: -3px;
+    background: -moz-linear-gradient(left, rgba(252, 248, 227, 1) 0, rgba(255, 255, 255, 0) 100%);
+    background: -webkit-linear-gradient(left, rgba(252, 248, 227, 1) 0, rgba(255, 255, 255, 0) 100%);
+    background: -o-linear-gradient(left, rgba(252, 248, 227, 1) 0, rgba(255, 255, 255, 0) 100%);
+    background: -ms-linear-gradient(left, rgba(252, 248, 227, 1) 0, rgba(255, 255, 255, 0) 100%);
+    background: linear-gradient(to right, rgba(252, 248, 227, 1) 0, rgba(255, 255, 255, 0) 100%);
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='@cui-yellow-050', endColorstr='#00ffffff', GradientType=1)
+  }
+
+  .hoverMsg {
+    background: @cui-black;
+    height: 100%;
+    left: 0;
+    opacity: .5;
+    position: fixed;
+    width: 100%;
+    top:0;
+    z-index: 10000;
+  }
+
+  .hoverText {
+    color: @cui-white;
+    font-size: 5em;
+    position: relative;
+    text-align: center;
+    top: 45%;
+    font-weight: 700;
+    text-shadow: 1px 1px 1px rgba(0,0,0,.3);
+    -webkit-animation-name: bounceInDown;
+    animation-name: bounceInDown;
+    -webkit-animation-duration: 1s;
+    animation-duration: 1s;
+    -webkit-animation-fill-mode: both;
+    animation-fill-mode: both;
+  }
+
+  .snippet-row {
+    position: relative;
+    display: block;
+    height: auto;
+    width: 100%;
+  }
+
+  .snippet-side-btn {
+    color: @cui-blue-gray-100;
+    display: inline-block;
+    line-height: 27px;
+    height: 27px;
+    min-height: 27px;
+    margin: 0;
+    font-size: 13px;
+    cursor: pointer;
+    .hue-ease-transition(color);
+  }
+
+  .snippet-side-btn.disabled {
+    cursor: default !important;
+    opacity: 0.5;
+  }
+
+  .snippet-side-btn:focus {
+    color: @cui-blue-gray-100;
+  }
+
+  .snippet-side-btn.active {
+    color: @hue-primary-color-dark;
+  }
+
+  .snippet-side-btn:hover {
+    color: @hue-primary-color-dark;
+  }
+
+  .snippet-side-single {
+    padding: 0 3px;
+  }
+
+  .execution-timer {
+    color: @cui-gray-500;
+  }
+
+  .resultTable th {
+    color: @cui-white;
+  }
+
+  .dataTables_wrapper .resultTable th {
+    color: @cui-gray-800;
+  }
+
+  .navbar .editor-nav > li > a {
+    max-width: 600px;
+  }
+
+  .navbar .editor-nav > li > a, .navbar .editor-nav > li > a:hover {
+    color: @cui-gray-700;
+    cursor: default;
+  }
+
+  .navbar .editor-nav > li.query-name {
+    font-weight: bold;
+  }
+
+  .editor-nav .editable-click:hover {
+    border-bottom: dashed 1px @cui-blue-700;
+  }
+
+  .editor-nav .editable-click, .editor-nav a.editable-click {
+    border-bottom: 1px solid transparent;
+  }
+
+  .editable-empty, .editable-empty:hover {
+    color: @cui-gray-700 !important;
+  }
+
+  h4.header {
+    font-weight: bold;
+    color: @cui-gray-600;
+    font-size: 14px;
+    text-transform: uppercase;
+    padding-left: 4px;
+    border-bottom: 1px solid @cui-default-border-color;
+    padding-bottom: 3px;
+  }
+
+  .history-table tbody tr {
+    border-left: 2px solid transparent;
+    border-right: 2px solid transparent;
+
+    td {
+      vertical-align: top;
+    }
+  }
+
+  .history-table tbody tr:hover {
+    border-left: 2px solid @hue-primary-color-light;
+    border-right: 2px solid @hue-primary-color-light;
+  }
+
+  .history-table tbody tr:hover td, .history-table tbody tr:hover th {
+    background-color: @cui-gray-050;
+  }
+
+  .history-table tbody tr.highlight {
+    background-color: @cui-gray-050;
+    border-left: 2px solid @hue-primary-color-light;
+    border-right: 2px solid @hue-primary-color-light;
+  }
+
+  .history-table .ace-highlight {
+    padding: 4px 0;
+  }
+
+  .ace-highlight {
+    .ace_layer {
+      pointer-events: all;
+    }
+    .ace-hue {
+      background-color: transparent !important;
+    }
+    .ace_invalid {
+      display: none;
+    }
+    .ace_line {
+      white-space: normal;
+    }
+    .ace-highlight-pre {
+      white-space: pre !important;
+    }
+  }
+
+  .history-status {
+    width: 20px;
+  }
+
+  .history-status .fa-check {
+    color: @cui-lime-200;
+  }
+
+  .history-status .fa-fighter-jet {
+    color: @cui-orange-200;
+  }
+
+  .table-results table > thead > tr > td:first-child, .table-results table > tbody > tr > td:first-child {
+    background-color: @cui-gray-050;
+  }
+
+  #helpModal th:first-child {
+    width: 25%;
+  }
+
+  #helpModal th:last-child {
+    width: 50%;
+  }
+
+  #helpModal .tab-content {
+    min-height: 310px;
+    max-height: 310px;
+  }
+
+  .tooltip {
+    z-index: 3000 !important;
+  }
+
+  .saveResultsModal .overlay {
+    background: black;
+    opacity: 0.5;
+    position: absolute;
+    top: 0;
+    right: 0;
+    left: 0;
+    bottom: 0;
+    z-index: 100;
+  }
+
+  .saveResultsModal .loader {
+    text-align: center;
+    position: absolute;
+    top: 0;
+    right: 0;
+    left: 0;
+    bottom: 0;
+  }
+
+  .saveResultsModal i.fa-spinner, .saveResultsModal img.spinner {
+    margin-top: -30px;
+    margin-left: -30px;
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    z-index: 101;
+  }
+
+  .saveResultsModal i.fa-spinner {
+    font-size: 60px;
+    color: @cui-gray-300;
+  }
+
+  .notebook-name-desc {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    max-width: 200px;
+  }
+
+  .notebook-name-input {
+    width: 220px;
+  }
+
+  pre {
+    font-size: 11px;
+  }
+
+  .inline-tab-filter {
+    border-radius: 5px !important;
+    min-height: 17px !important;
+    height: 17px !important;
+    margin: 0 3px 0 -2px !important;
+    padding: 0 18px 0 5px !important;
+  }
+
+  .no-horiz-padding a {
+    padding-left: 0 !important;
+    padding-right: 0 !important;
+  }
+
+  .no-horiz-padding.muted a {
+   color: @cui-gray-500 !important;
+  }
+
+  .no-horiz-padding:not(:first-child) a {
+    margin-left: 0 !important;
+  }
+
+  .collapse-results {
+    right: 20px;
+    top: 20px;
+  }
+
+  .meta-title {
+    line-height: 22px;
+  }
+
+  .meta-filter {
+    width: 118px;
+    margin-bottom: 0;
+  }
+
+  .meta-noresults {
+    font-style: italic;
+  }
+
+  #schedulerEditor .container-fluid, #schedulerEditor .card-heading, #schedulerEditor .card-body {
+    padding: 0;
+  }
+
+  #schedulerEditor .dataset-input {
+    width: 270px;
+  }
+
+  #schedulerEditor .form-horizontal .control-label {
+    width: 100px;
+    text-align: left;
+  }
+
+  #schedulerEditor .form-horizontal .controls {
+    margin-left: 110px;
+  }
+
+  #schedulerEditor .workflow-parameter {
+    padding: 10px;
+    margin-left: -10px;
+    margin-right: -10px;
+  }
+
+  #schedulerEditor .workflow-parameter:hover {
+    background-color: @cui-gray-050;
+  }
+
+  .warning {
+    color: @state-warning-text;
+  }
+
+  .nav-header-like {
+    padding: 6px;
+    margin-top: 1px;
+    overflow: hidden;
+    font-size: 14px;
+    line-height: 1.4;
+    color: @hue-primary-color-dark;
+    text-overflow: ellipsis;
+    text-shadow: none;
+    background-color: @cui-white;
+    border-left: 2px solid transparent;
+    -webkit-transition: border .218s;
+    -moz-transition: border .218s;
+    -o-transition: border .218s;
+    transition: border .218s;
+    text-transform: uppercase;
+  }
+
+  .mCSB_scrollTools.mCSB_scrollTools_horizontal .mCSB_dragger .mCSB_dragger_bar {
+    height: 9px !important;
+    margin: 0 !important;
+    margin-top: 3px !important;
+  }
+
+  .mCSB_scrollTools.mCSB_scrollTools_horizontal {
+    margin: 0 !important;
+  }
+
+  .mCSB_scrollTools.mCSB_scrollTools_vertical .mCSB_dragger .mCSB_dragger_bar {
+    width: 8px !important;
+    margin-left: 10px !important;
+  }
+
+  .alert-info-gradient {
+    border: none;
+    margin-top: -3px;
+    background: -moz-linear-gradient(left, @cui-blue-050 0, rgba(255, 255, 255, 0) 100%);
+    background: -webkit-linear-gradient(left, @cui-blue-050 0, rgba(255, 255, 255, 0) 100%);
+    background: -o-linear-gradient(left, @cui-blue-050 0, rgba(255, 255, 255, 0) 100%);
+    background: -ms-linear-gradient(left, @cui-blue-050 0, rgba(255, 255, 255, 0) 100%);
+    background: linear-gradient(to right, @cui-blue-050 0, rgba(255, 255, 255, 0) 100%);
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='@cui-blue-050', endColorstr='#00ffffff', GradientType=1);
+  }
+
+  .alert-success-gradient {
+    border: none;
+    margin-top: -3px;
+    background: -moz-linear-gradient(left, @cui-lime-050 0, rgba(255, 255, 255, 0) 100%);
+    background: -webkit-linear-gradient(left, @cui-lime-050 0, rgba(255, 255, 255, 0) 100%);
+    background: -o-linear-gradient(left, @cui-lime-050 0, rgba(255, 255, 255, 0) 100%);
+    background: -ms-linear-gradient(left, @cui-lime-050 0, rgba(255, 255, 255, 0) 100%);
+    background: linear-gradient(to right, @cui-lime-050 0, rgba(255, 255, 255, 0) 100%);
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='@cui-lime-050', endColorstr='#00ffffff', GradientType=1);
+  }
+
+  .alert-neutral-gradient {
+    border: none;
+    border-left: 1px solid @cui-blue-050;
+    margin-top: -3px;
+    background: -moz-linear-gradient(left, @cui-purple-gray-050 0, rgba(255, 255, 255, 0) 100%);
+    background: -webkit-linear-gradient(left, @cui-purple-gray-050 0, rgba(255, 255, 255, 0) 100%);
+    background: -o-linear-gradient(left, @cui-purple-gray-050 0, rgba(255, 255, 255, 0) 100%);
+    background: -ms-linear-gradient(left, @cui-purple-gray-050 0, rgba(255, 255, 255, 0) 100%);
+    background: linear-gradient(to right, @cui-purple-gray-050 0, rgba(255, 255, 255, 0) 100%);
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='@cui-purple-gray-050', endColorstr='#00ffffff', GradientType=1);
+  }
+
+  .alert-warning {
+    background-color: @cui-yellow-050;
+    border: 1px solid @cui-orange-200;
+    color: @state-warning-text;
+  }
+
+  .optimizer-container {
+    margin-top: -11px;
+    font-size: 15px;
+  }
+
+  .optimizer-icon {
+    display: inline-block;
+    width: 16px;
+    height: 16px;
+    text-align: center;
+    line-height: 16px;
+    font-size: 14px;
+    opacity: .8;
+    cursor: pointer;
+    &.empty {
+      width: 1px;
+      cursor: default;
+    }
+    &.idle {
+      color: @cui-gray-400;
+      cursor: default;
+    }
+    &:hover {
+      opacity: 1;
+    }
+    &.success {
+      color: @cui-green-600;
+    }
+    &.warning {
+      color: @cui-orange-200;
+    }
+    &.error {
+      color: @cui-red-400;
+    }
+  }
+
+  .active .optimizer-icon {
+    opacity: 1;
+  }
+
+  .alert-neutral {
+    border: none;
+    margin-top: -3px;
+    background: transparent;
+    margin-bottom: 0;
+    padding: 0;
+    padding-right: 4px;
+    display: inline-block;
+  }
+
+  .alert-empty {
+    border: none;
+    margin-bottom: 0;
+    background: transparent;
+    padding-left: 4px;
+  }
+
+  .optimizer-explanation {
+    margin-left: 4px;
+  }
+
+  .popover {
+    z-index: 1030;
+  }
+
+  .close-logs-overlay {
+    position: absolute;
+    top: 2px;
+    right: 10px;
+    font-size: 20px;
+    display: none;
+    background-color: @cui-gray-050;
+  }
+
+  .snippet-log-container:hover .close-logs-overlay {
+    display: block;
+  }
+
+  .jobs-overlay {
+    background-color: @cui-white;
+    opacity: 0.8;
+    position: absolute;
+    top: 10px;
+    right: 20px;
+
+    .progress-job {
+      height: 2px;
+      margin-bottom: 0;
+    }
+
+    li {
+      background-color: @cui-white;
+      padding: 5px;
+
+      a {
+        margin: 6px;
+      }
+    }
+
+    &:hover {
+      opacity: 1;
+    }
+  }
+
+  // TODO: Switch to the context popover css for the file chooser
+  .ace-filechooser {
+    border-radius: @hue-panel-border-radius;
+    background-color: @cui-white;
+    .hue-box-shadow-bottom;
+  }
+
+  .ace-filechooser-content .filechooser-tree {
+    position: relative;
+    margin-right: 10px;
+  }
+
+  .ace-filechooser-close {
+    position: absolute;
+    right: 10px;
+    top: 10px;
+    z-index: 1000;
+  }
+
+  .query-name {
+    margin-left: 28px;
+  }
+
+  .statement-type-selector > .hue-drop-down-container {
+    margin-left: -100px;
+  }
+
+  .variables {
+    .selectize-control {
+      display: inline-block;
+      width: 164px;
+    }
+
+    .selectize-input {
+      line-height: 19px;
+    }
+
+    .selectize-input input[type="text"] {
+      height: 19px;
+    }
+
+    .input-prepend {
+      line-height: 0;
+    }
+
+    input:not(:focus)[value=""] {
+      border-color: @cui-orange-400;
+    }
+
+    input[type="checkbox"] {
+      margin: 12px 0 0 10px;
+    }
+
+    div.selectize-input {
+      padding: 5px 23px 6px 8px;
+      text-overflow: ellipsis;
+    }
+  }
+
+  .nav-tabs-editor {
+    .visible-on-hover {
+      opacity: 0;
+      .hue-ease-transition(opacity);
+    }
+    li:hover .visible-on-hover {
+       opacity: 1;
+    }
+  }
+}

+ 12 - 6
desktop/libs/notebook/src/notebook/templates/editor.mako

@@ -26,19 +26,25 @@
 <%namespace name="notebookKoComponents" file="/common_notebook_ko_components.mako" />
 <%namespace name="hueAceAutocompleter" file="/hue_ace_autocompleter.mako" />
 
-<span id="editorComponents" class="editorComponents notebook">
+<div id="editorComponents" class="editorComponents notebook">
 %if ENABLE_NOTEBOOK_2.get():
-${ editorComponents2.includes(is_embeddable=is_embeddable, suffix='editor') }
-${ editorComponents2.topBar(suffix='editor') }
-${ editorComponents2.commonHTML(is_embeddable=is_embeddable, suffix='editor') }
-${ editorComponents2.commonJS(is_embeddable=is_embeddable, suffix='editor') }
+<div style="display: flex; flex-direction:column; height: 100%; width: 100%">
+  <div style="flex: 0 0 auto;">
+  ${ editorComponents2.includes(is_embeddable=is_embeddable, suffix='editor') }
+  ${ editorComponents2.topBar(suffix='editor') }
+  </div>
+  <div style="flex: 1;">
+  ${ editorComponents2.commonHTML(is_embeddable=is_embeddable, suffix='editor') }
+  </div>
+  ${ editorComponents2.commonJS(is_embeddable=is_embeddable, suffix='editor') }
+</div>
 %else:
 ${ editorComponents.includes(is_embeddable=is_embeddable, suffix='editor') }
 ${ editorComponents.topBar(suffix='editor') }
 ${ editorComponents.commonHTML(is_embeddable=is_embeddable, suffix='editor') }
 ${ editorComponents.commonJS(is_embeddable=is_embeddable, suffix='editor') }
 %endif
-</span>
+</div>
 
 %if not is_embeddable:
 ${ commonfooter(request, messages) | n,unicode }

+ 1 - 1
desktop/libs/notebook/src/notebook/templates/editor_components.mako

@@ -687,7 +687,7 @@ ${ sqlSyntaxDropdown.sqlSyntaxDropdown() }
       </ul>
 
       <div class="tab-content" style="border: none; overflow-x: hidden">
-        <div class="tab-pane" id="queryHistory" data-bind="css: {'active': currentQueryTab() == 'queryHistory'}, style: { 'height' : $parent.historyInitialHeight() > 0 ? Math.max($parent.historyInitialHeight(), 40) + 'px' : '' }">
+        <div class="tab-pane" style="min-height:80px;" id="queryHistory" data-bind="css: {'active': currentQueryTab() == 'queryHistory'}, style: { 'height' : $parent.historyInitialHeight() > 0 ? Math.max($parent.historyInitialHeight(), 40) + 'px' : '' }">
           <!-- ko if: $parent.loadingHistory -->
           <div class="margin-top-10 margin-left-10">
             <i class="fa fa-spinner fa-spin muted"></i>

+ 64 - 115
desktop/libs/notebook/src/notebook/templates/editor_components2.mako

@@ -33,7 +33,7 @@
   % if not is_embeddable:
     <link rel="stylesheet" href="${ static('notebook/css/notebook-layout.css') }">
   % endif
-  <link rel="stylesheet" href="${ static('notebook/css/notebook.css') }">
+  <link rel="stylesheet" href="${ static('notebook/css/notebook2.css') }">
   <link rel="stylesheet" href="${ static('desktop/ext/css/bootstrap-editable.css') }">
 
   <link rel="stylesheet" href="${ static('desktop/ext/css/medium-editor.min.css') }">
@@ -392,11 +392,7 @@
 
 
   <div data-bind="css: {'main-content': true, 'editor-mode': $root.editorMode()}">
-    <div class="vertical-full container-fluid" data-bind="style: { 'padding-left' : $root.isLeftPanelVisible() || $root.isPresentationMode() || $root.isResultFullScreenMode() ? '0' : '20px' }" >
-      <div class="vertical-full">
-        <div class="vertical-full tab-pane row-fluid panel-container" data-bind="css: { active: selectedNotebook() === $data }, template: { name: 'notebook${ suffix }'}">
-        </div>
-      </div>
+    <div class="vertical-full tab-pane row-fluid panel-container" data-bind="css: { active: selectedNotebook() === $data }, template: { name: 'notebook${ suffix }'}">
     </div>
   </div>
 
@@ -428,56 +424,54 @@
       % endif
 
     <div class="content-panel" data-bind="event: { scroll: function(){ var ls = $(MAIN_SCROLLABLE).data('lastScroll'); if (ls && ls != $(MAIN_SCROLLABLE).scrollTop()){ $(document).trigger('hideAutocomplete'); }; $(MAIN_SCROLLABLE).data('lastScroll', $(MAIN_SCROLLABLE).scrollTop()) } }, with: selectedNotebook">
-      <div>
-        <div class="row-fluid row-container sortable-snippets" data-bind="css: {'is-editing': $root.isEditing, 'margin-left-10': $root.isPresentationMode},
-        sortable: {
-          template: 'snippet${ suffix }',
-          data: snippets,
-          isEnabled: true,
-          options: {
-            'handle': '.move-widget',
-            'axis' : 'y',
-            'opacity': 0.8,
-            'placeholder': 'snippet-move-placeholder',
-            'greedy': true,
-            'stop': function(event, ui) {
-              var $element = $(event.target);
-              $element.find('.snippet-body').slideDown('fast', function () { $(MAIN_SCROLLABLE).scrollTop(lastWindowScrollPosition); });
-            },
-            'helper': function(event) {
-              lastWindowScrollPosition = $(MAIN_SCROLLABLE).scrollTop();
-              var $element = $(event.target);
-              $element.find('.snippet-body').slideUp('fast', function () {
-                $('.sortable-snippets').sortable('refreshPositions')
-              });
-              var _par = $('<div>')
-                .css('overflow', 'hidden')
-                .addClass('card-widget snippet-move-helper')
-                .width($element.parents('.snippet').width());
-              $('<h2>')
-                .addClass('card-heading')
-                .html($element.parents('h2').html())
-                .appendTo(_par)
-                .find('.hover-actions, .snippet-actions')
-                .removeClass('hover-actions')
-                .removeClass('snippet-actions');
-              $('<pre>')
-                .addClass('dragging-pre muted')
-                .html(ko.dataFor($element.parents('.card-widget')[0]).statement())
-                .appendTo(_par);
-              _par.css('height', '100px');
-              return _par;
-            }
+      <div class="row-fluid row-container sortable-snippets" data-bind="css: {'is-editing': $root.isEditing},
+      sortable: {
+        template: 'snippet${ suffix }',
+        data: snippets,
+        isEnabled: true,
+        options: {
+          'handle': '.move-widget',
+          'axis' : 'y',
+          'opacity': 0.8,
+          'placeholder': 'snippet-move-placeholder',
+          'greedy': true,
+          'stop': function(event, ui) {
+            var $element = $(event.target);
+            $element.find('.snippet-body').slideDown('fast', function () { $(MAIN_SCROLLABLE).scrollTop(lastWindowScrollPosition); });
           },
-          dragged: function (widget) {
-            $('.snippet-body').slideDown('fast', function () { $(MAIN_SCROLLABLE).scrollTop(lastWindowScrollPosition); });
+          'helper': function(event) {
+            lastWindowScrollPosition = $(MAIN_SCROLLABLE).scrollTop();
+            var $element = $(event.target);
+            $element.find('.snippet-body').slideUp('fast', function () {
+              $('.sortable-snippets').sortable('refreshPositions')
+            });
+            var _par = $('<div>')
+              .css('overflow', 'hidden')
+              .addClass('card-widget snippet-move-helper')
+              .width($element.parents('.snippet').width());
+            $('<h2>')
+              .addClass('card-heading')
+              .html($element.parents('h2').html())
+              .appendTo(_par)
+              .find('.hover-actions, .snippet-actions')
+              .removeClass('hover-actions')
+              .removeClass('snippet-actions');
+            $('<pre>')
+              .addClass('dragging-pre muted')
+              .html(ko.dataFor($element.parents('.card-widget')[0]).statement())
+              .appendTo(_par);
+            _par.css('height', '100px');
+            return _par;
           }
-        }">
-        </div>
-        % if hasattr(caller, "addSnippetHTML"):
-          ${ caller.addSnippetHTML() }
-        % endif
+        },
+        dragged: function (widget) {
+          $('.snippet-body').slideDown('fast', function () { $(MAIN_SCROLLABLE).scrollTop(lastWindowScrollPosition); });
+        }
+      }">
       </div>
+      % if hasattr(caller, "addSnippetHTML"):
+        ${ caller.addSnippetHTML() }
+      % endif
     </div>
   </script>
 
@@ -617,7 +611,7 @@
         </ul>
 
         <div class="tab-content" style="border: none; overflow-x: hidden">
-          <div class="tab-pane" id="queryHistory" data-bind="css: {'active': currentQueryTab() == 'queryHistory'}, style: { 'height' : $parent.historyInitialHeight() > 0 ? Math.max($parent.historyInitialHeight(), 40) + 'px' : '' }">
+          <div class="tab-pane" id="queryHistory" style="min-height: 80px;" data-bind="css: {'active': currentQueryTab() == 'queryHistory'}, style: { 'height' : $parent.historyInitialHeight() > 0 ? Math.max($parent.historyInitialHeight(), 40) + 'px' : '' }">
             <!-- ko if: $parent.loadingHistory -->
             <div class="margin-top-10 margin-left-10">
               <i class="fa fa-spinner fa-spin muted"></i>
@@ -807,7 +801,6 @@
       <span class="execution-timer" data-bind="visible: type() != 'text' && status() != 'ready' && status() != 'loading', text: result.executionTime().toHHMMSS()" title="${ _('Execution time') }"></span>
 
       <!-- ko template: { name: 'snippet-header-database-selection' } --><!-- /ko -->
-      <!-- ko template: { name: 'snippet-header-statement-type${ suffix }' } --><!-- /ko -->
 
       <a class="inactive-action move-widget" href="javascript:void(0)"><i class="fa fa-arrows"></i></a>
       <a class="inactive-action" href="javascript:void(0)" data-bind="toggle: settingsVisible, visible: hasProperties, css: { 'blue' : settingsVisible }" title="${ _('Snippet settings') }"><i class="fa fa-cog"></i></a>
@@ -823,9 +816,7 @@
       <span class="execution-timer" data-bind="visible: type() != 'text' && status() != 'ready' && status() != 'loading', text: result.executionTime().toHHMMSS()" title="${ _('Execution time') }"></span>
 
       <!-- ko template: { name: 'snippet-header-database-selection' } --><!-- /ko -->
-      <!-- ko template: { name: 'snippet-header-statement-type${ suffix }' } --><!-- /ko -->
 
-      <a class="inactive-action margin-left-10" href="javascript:void(0)" data-bind="toggle: settingsVisible, visible: hasProperties, css: { 'blue' : settingsVisible }" title="${ _('Query settings') }"><i class="fa fa-cog"></i></a>
       <a class="inactive-action margin-left-10 pointer" title="${ _('Show editor help') }" data-toggle="modal" data-target="#helpModal${ suffix }"><i class="fa fa-question"></i></a>
     </div>
   </script>
@@ -846,15 +837,6 @@
     <!-- /ko -->
   </script>
 
-  <script type="text/html" id="snippet-header-statement-type${ suffix }">
-      % if ENABLE_EXTERNAL_STATEMENT.get():
-        <!-- ko if: isSqlDialect() -->
-        <span class="editor-header-title">${ _('Type') }</span>
-        <div data-bind="component: { name: 'hue-drop-down', params: { value: statementType, entries: statementTypes, linkTitle: '${ _ko('Statement type') }' } }" style="display: inline-block"></div>
-        <!-- /ko -->
-      % endif
-  </script>
-
   <script type="text/html" id="snippet${ suffix }">
     <div data-bind="visibleOnHover: { override: inFocus() || settingsVisible() || dbSelectionVisible() || $root.editorMode() || saveResultsModalVisible(), selector: '.hover-actions' }">
       <div class="snippet-container row-fluid" data-bind="visibleOnHover: { override: $root.editorMode() || inFocus() || saveResultsModalVisible(), selector: '.snippet-actions' }">
@@ -873,7 +855,7 @@
                 <!-- ko template: { if: type() == 'markdown', name: 'markdown-snippet-body${ suffix }' } --><!-- /ko -->
                 <!-- ko template: { if: ['java', 'distcp', 'shell', 'mapreduce', 'jar', 'py', 'spark2'].indexOf(type()) != -1, name: 'executable-snippet-body${ suffix }' } --><!-- /ko -->
               </div>
-              <div style="position: absolute; top:25px; margin-left:35px; width: calc(100% - 35px)" data-bind="style: { 'z-index': 400 - $index() }">
+              <div style="position: absolute; top:25px; width: 100%" data-bind="style: { 'z-index': 400 - $index() }">
                 <!-- ko template: 'snippet-settings${ suffix }' --><!-- /ko -->
               </div>
             </div>
@@ -907,6 +889,16 @@
       </div>
       <div class="snippet-settings-body">
         <form class="form-horizontal">
+          % if ENABLE_EXTERNAL_STATEMENT.get():
+            <!-- ko if: isSqlDialect -->
+            <div class="config-property">
+              <label class="config-label">${ _('Type') }</label>
+              <div class="config-controls">
+                <div style="padding-top: 4px; display: inline-block;" data-bind="component: { name: 'hue-drop-down', params: { value: statementType, entries: statementTypes, linkTitle: '${ _ko('Statement type') }' } }"></div>
+              </div>
+            </div>
+            <!-- /ko -->
+          % endif
           <!-- ko template: { if: typeof properties().driverCores != 'undefined', name: 'property', data: { type: 'number', label: '${ _ko('Driver Cores') }', value: properties().driverCores, title: '${ _ko('Number of cores used by the driver, only in cluster mode (Default: 1)') }'}} --><!-- /ko -->
           <!-- ko template: { if: typeof properties().executorCores != 'undefined', name: 'property', data: { type: 'number', label: '${ _ko('Executor Cores') }', value: properties().executorCores, title: '${ _ko('Number of cores per executor (Default: 1)') }' }} --><!-- /ko -->
           <!-- ko template: { if: typeof properties().numExecutors != 'undefined', name: 'property', data: { type: 'number', label: '${ _ko('Executors') }', value: properties().numExecutors, title: '${ _ko('Number of executors to launch (Default: 2)') }' }} --><!-- /ko -->
@@ -1655,9 +1647,11 @@
   </script>
 
   <script type ="text/html" id="snippet-execution-controls${ suffix }">
-    <div class="snippet-actions">
-      <!-- ko component: { name: 'snippet-execute-actions', params: { snippet: $data } } --><!-- /ko -->
-
+    <div class="snippet-actions clearfix">
+      <div class="pull-left" data-bind="component: { name: 'snippet-execute-actions', params: { snippet: $data } }" />
+      <!-- ko if: isSqlDialect() && !$root.isPresentationMode() -->
+      <div class="pull-right" data-bind="component: { name: 'snippet-editor-actions', params: { snippet: $data } }" />
+      <!-- /ko -->
       <div class="pull-right">
         <!-- ko if: status() === 'loading' -->
         <i class="fa fa-fw fa-spinner fa-spin"></i> ${ _('Creating session') }
@@ -1719,51 +1713,6 @@
 ##           <!-- /ko -->
 ##         % endif
 ##       </div>
-
-      <!-- ko if: isSqlDialect && ! $root.isPresentationMode() -->
-      <div style="display: inline-block" class="inactive-action dropdown hover-actions pointer" data-bind="css: {'disabled': ! isReady() || status() === 'running' || status() === 'loading' }">
-        <a class="snippet-side-btn" style="padding-right:0; padding-left: 2px;" href="javascript: void(0)" data-bind="click: explain, css: {'disabled': ! isReady() || status() === 'running' || status() === 'loading', 'blue': currentQueryTab() == 'queryExplain' }" title="${ _('Explain the current SQL query') }">
-          <i class="fa fa-fw fa-map-o"></i>
-        </a>
-        <a class="dropdown-toggle snippet-side-btn" style="padding:0" data-toggle="dropdown" href="javascript: void(0)" data-bind="css: {'disabled': ! isReady(), 'blue': currentQueryTab() == 'queryExplain' }">
-          <i class="fa fa-caret-down"></i>
-        </a>
-        <ul class="dropdown-menu less-padding">
-          <li>
-            <a href="javascript:void(0)" data-bind="click: explain, style: { color: ! isReady() || status() === 'running' || status() === 'loading' ? '#999' : ''}, css: {'disabled': ! isReady() || status() === 'running' || status() === 'loading' }" title="${ _('Explain the current SQL query') }">
-              <i class="fa fa-fw fa-map-o"></i> ${_('Explain')}
-            </a>
-          </li>
-          <!-- ko if: formatEnabled -->
-          <li>
-            <a href="javascript:void(0)" data-bind="click: format, css: {'disabled': ! isReady() }" title="${ _('Format the current SQL query') }">
-              <i class="fa fa-fw fa-indent"></i> ${_('Format')}
-            </a>
-          </li>
-          <!-- /ko -->
-          <li>
-            <a href="javascript:void(0)" data-bind="click: clear, css: {'disabled': ! isReady() }" title="${ _('Clear the current editor') }">
-              <i class="fa fa-fw fa-eraser"></i> ${_('Clear')}
-            </a>
-          </li>
-          <!-- ko if: HAS_OPTIMIZER -->
-          <li class="divider"></li>
-          <li>
-            <a href="javascript:void(0)" data-bind="click: checkCompatibility, visible: type() == 'hive' || type() == 'impala'" title="${ _('Get hints on how to port SQL from other databases') }">
-              <i class="fa fa-fw fa-random"></i> ${_('Check compatibility')}
-            </a>
-          </li>
-          % if conf.DJANGO_DEBUG_MODE.get() and is_admin(user):
-            <li>
-              <a href="javascript:void(0)" data-bind="click: function() { huePubSub.publish('editor.upload.history'); }" title="${ _('Load recent queries in order to improve recommendations') }">
-                <i class="fa fa-fw fa-cloud-upload"></i> ${_('Upload history')}
-              </a>
-            </li>
-          % endif
-          <!-- /ko -->
-        </ul>
-      </div>
-      <!-- /ko -->
     </div>
   </script>
 

+ 2 - 2
desktop/libs/notebook/src/notebook/templates/notebook.mako

@@ -27,7 +27,7 @@
 <%namespace name="hueAceAutocompleter" file="/hue_ace_autocompleter.mako" />
 
 
-<span id="notebookComponents" class="notebook">
+<div id="notebookComponents" class="notebook">
 %if ENABLE_NOTEBOOK_2.get():
   ${ editorComponents2.includes(is_embeddable=is_embeddable, suffix='notebook') }
   ${ editorComponents2.topBar(suffix='notebook') }
@@ -79,4 +79,4 @@
 
   ${ editorComponents.commonJS(is_embeddable=is_embeddable, bindableElement='notebookComponents', suffix='notebook') }
 %endif
-</span>
+</div>

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است