This is an automated email from the ASF dual-hosted git repository.

chanholee pushed a commit to branch branch-0.12
in repository https://gitbox.apache.org/repos/asf/zeppelin.git


The following commit(s) were added to refs/heads/branch-0.12 by this push:
     new 356bf8d6a5 [ZEPPELIN-6197] 6229] Fixed the shortcuts in the New UI so 
that they match the Classic UI and work correctly
356bf8d6a5 is described below

commit 356bf8d6a5ccceeb0e2c3d6ab3d82e2858878f50
Author: YONGJAE LEE(이용재) <dev.yongjae...@gmail.com>
AuthorDate: Sun Sep 7 13:03:34 2025 +0900

    [ZEPPELIN-6197] 6229] Fixed the shortcuts in the New UI so that they match 
the Classic UI and work correctly
    
    ### What is this PR for?
    Unlike the Classic UI, the New UI either lacked implementations for several 
shortcuts or had shortcuts assigned differently from the Classic UI. I consider 
this a significant issue, and after addressing other related derivative issues, 
I am finally submitting it.
    
    #### d96ae5bde - Remove the top margin in the editor search
    [AS-IS]
    <img width="671" height="136" alt="스크린샷 2025-09-01 오후 11 26 14" 
src="https://github.com/user-attachments/assets/fcd0a107-4b22-42f2-978f-f0781c5fd675";
 />
    [TO-BE]
    <img width="669" height="136" alt="스크린샷 2025-09-01 오후 11 26 23" 
src="https://github.com/user-attachments/assets/475c05e6-ffae-4410-920f-7b0a9e44388d";
 />
    
    Fixed an issue where the top area increased by 33px when search was 
activated. <- 
[referece](https://github.com/microsoft/monaco-editor/blob/5a7ba61be909ae9e4889768a3453ebb0dec392e2/monaco-editor/typedoc/monaco.d.ts#L3473)
    
    #### 0d24eb420 - Handle focus on PARAGRAPH_MOVED receive
    When performing "Move Paragraph Up" or "Move Paragraph Down," the paragraph 
moves up or down after receiving `PARAGRAPH_MOVED`. During this process, focus 
was being skipped, so I added it.
    
    #### 1d3d294bc - Overriding shortcuts conflict with the editor
    In the New UI, we use the Monaco editor, where reserved shortcuts had to be 
explicitly overridden. I added shortcut functionality for four actions: "Toggle 
Editor", "Cut the Line", "Paste the Line", and "Search Inside the Code".
    
    #### 57f45e1e8 - Make action bar searchCode accessible to siblings
    There are `paragraph.component.ts` and `action-bar.component.ts` that share 
`notebook.component.ts` as their parent. In the Classic UI, "Find in Code" is a 
shortcut that activates the search button in the action-bar. Although this 
functionality is currently marked as 
[TODO](https://github.com/dididy/zeppelin/blob/fix/ZEPPELIN-6197/6229/zeppelin-web-angular/src/app/pages/workspace/notebook/action-bar/action-bar.component.ts#L220-L222)([ZEPPELIN-6279](https://issues.apache.org/jira/secu
 [...]
    
    #### b027a33 - Ensure shortcut keys work correctly
    <img width="605" height="1088" alt="image" 
src="https://github.com/user-attachments/assets/2c4bc264-07e5-4abe-93bb-a2656a86527b";
 />
    
    All shortcut orders have been updated based on the Classic UI. There was 
logic to handle key bindings differently for macOS, but upon review, it was 
unnecessary and has been removed. On macOS, typing a combination of Alt and an 
English letter can produce a different character, so to ensure correct 
behavior, this resulting character combination is included in the shortcut 
handling. I added several actions that were not yet implemented.
    
    The distinction between command mode and edit mode for shortcuts was 
meaningless, so it was removed. The original implementation seemed intended to 
mimic a vi-like behavior, but this would have made the shortcut system 
different from the Classic UI.
    
    In the Classic UI, "Auto Completion" had a shortcut to trigger it manually. 
In the New UI, auto completion is triggered automatically when typing, so "Auto 
Completion" shortcut was unnecessary and has been removed in this PR.
    
    ### What type of PR is it?
    Bug Fix
    
    ### Todos
    
    ### What is the Jira issue?
    * [[ZEPPELIN-6197](https://issues.apache.org/jira/browse/ZEPPELIN-6197)]
    * [[ZEPPELIN-6229](https://issues.apache.org/jira/browse/ZEPPELIN-6229)]
    
    ### How should this be tested?
    
    ### Screenshots (if appropriate)
    
    ### Questions:
    * Does the license files need to update? N
    * Is there breaking changes for older versions? N
    * Does this needs documentation? N
    
    Closes #5064 from dididy/fix/ZEPPELIN-6197/6229.
    
    Signed-off-by: ChanHo Lee <chanho...@apache.org>
    (cherry picked from commit 65fd70fd3521f607eee066a7bf3ad5c71f9cd69b)
    Signed-off-by: ChanHo Lee <chanho...@apache.org>
---
 .../workspace/notebook/notebook.component.html     |   2 +
 .../pages/workspace/notebook/notebook.component.ts |   1 +
 .../paragraph/code-editor/code-editor.component.ts |  82 +++++++++
 .../paragraph/control/control.component.ts         |  38 +++--
 .../notebook/paragraph/paragraph.component.html    |   1 +
 .../notebook/paragraph/paragraph.component.ts      | 183 ++++++++++++---------
 .../src/app/services/shortcut.service.ts           |  91 +++++-----
 .../app/notebook/paragraph/paragraph-control.html  |   6 +-
 8 files changed, 260 insertions(+), 144 deletions(-)

diff --git 
a/zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.component.html 
b/zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.component.html
index 9cca1743d2..301ff35e4a 100644
--- 
a/zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.component.html
+++ 
b/zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.component.html
@@ -23,6 +23,7 @@
     [currentRevision]="currentRevision"
     (tableHideChange)="setAllParagraphTableHide($event)"
     (editorHideChange)="setAllParagraphEditorHide($event)"
+    #actionBar
   ></zeppelin-notebook-action-bar>
   <div class="flex-container">
     <div
@@ -87,6 +88,7 @@
             (selected)="onParagraphSelect($event)"
             (triggerSaveParagraph)="saveParagraph($event)"
             (saveNoteTimer)="startSaveTimer()"
+            (searchCode)="actionBar.searchCode()"
           ></zeppelin-notebook-paragraph>
         </div>
       </div>
diff --git 
a/zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.component.ts 
b/zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.component.ts
index 4bf9cae4f6..f7511cd75c 100644
--- 
a/zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.component.ts
+++ 
b/zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.component.ts
@@ -198,6 +198,7 @@ export class NotebookComponent extends 
MessageListenersManager implements OnInit
           // Call when next tick
           setTimeout(() => {
             scrollIntoViewIfNeeded(paragraphComponent.getElement());
+            paragraphComponent.focusEditor();
           });
         }
       }
diff --git 
a/zeppelin-web-angular/src/app/pages/workspace/notebook/paragraph/code-editor/code-editor.component.ts
 
b/zeppelin-web-angular/src/app/pages/workspace/notebook/paragraph/code-editor/code-editor.component.ts
index b19e91d1df..5cf9d2623f 100644
--- 
a/zeppelin-web-angular/src/app/pages/workspace/notebook/paragraph/code-editor/code-editor.component.ts
+++ 
b/zeppelin-web-angular/src/app/pages/workspace/notebook/paragraph/code-editor/code-editor.component.ts
@@ -57,6 +57,7 @@ export class NotebookParagraphCodeEditorComponent implements 
OnChanges, OnDestro
   @Output() readonly textChanged = new EventEmitter<string>();
   @Output() readonly editorBlur = new EventEmitter<void>();
   @Output() readonly editorFocus = new EventEmitter<void>();
+  @Output() readonly toggleEditorShow = new EventEmitter<void>();
   private editor?: IStandaloneCodeEditor;
   private monacoDisposables: IDisposable[] = [];
   height = 18;
@@ -108,6 +109,72 @@ export class NotebookParagraphCodeEditorComponent 
implements OnChanges, OnDestro
     }
   }
 
+  // Handle Ctrl+Alt+E: Toggle editor show/hide
+  handleToggleEditorShow() {
+    this.toggleEditorShow.emit();
+  }
+
+  // Handle Ctrl+K: Cut current line to clipboard
+  async handleCutLine() {
+    if (!this.editor) {
+      return;
+    }
+
+    const position = this.editor.getPosition();
+    const model = this.editor.getModel();
+    if (!position || !model) {
+      return;
+    }
+
+    const lineNumber = position.lineNumber;
+    const lineContent = model.getLineContent(lineNumber);
+
+    if (!lineContent) {
+      return;
+    }
+
+    await navigator.clipboard.writeText(lineContent);
+
+    this.editor.executeEdits('cut-line', [
+      {
+        range: new monaco.Range(lineNumber, 1, lineNumber, lineContent.length 
+ 1),
+        text: '',
+        forceMoveMarkers: true
+      }
+    ]);
+  }
+
+  // Handle Ctrl+Y: Paste from clipboard at current position
+  async handlePasteFromClipboard() {
+    if (!this.editor) {
+      return;
+    }
+
+    const text = await navigator.clipboard.readText();
+    const position = this.editor.getPosition();
+    if (position) {
+      this.editor.executeEdits('my-source', [
+        {
+          range: new monaco.Range(position.lineNumber, position.column, 
position.lineNumber, position.column),
+          text: text,
+          forceMoveMarkers: true
+        }
+      ]);
+    }
+  }
+
+  // Handle Ctrl+S: Show find widget
+  handleShowFind() {
+    if (this.editor) {
+      this.editor.getAction('actions.find').run();
+
+      // Focus on the find widget input field
+      const findInput = document.querySelector('.find-widget .input') as 
HTMLInputElement;
+      findInput.focus();
+      findInput.select();
+    }
+  }
+
   initializedEditor(editor: IEditor) {
     this.editor = editor as IStandaloneCodeEditor;
     this.editor.addCommand(
@@ -119,6 +186,18 @@ export class NotebookParagraphCodeEditorComponent 
implements OnChanges, OnDestro
       },
       '!suggestWidgetVisible'
     );
+    this.editor.addCommand(monaco.KeyMod.WinCtrl | monaco.KeyMod.Alt | 
monaco.KeyCode.KeyE, () => {
+      this.handleToggleEditorShow();
+    });
+    this.editor.addCommand(monaco.KeyMod.WinCtrl | monaco.KeyCode.KeyK, () => {
+      this.handleCutLine();
+    });
+    this.editor.addCommand(monaco.KeyMod.WinCtrl | monaco.KeyCode.KeyY, () => {
+      this.handlePasteFromClipboard();
+    });
+    this.editor.addCommand(monaco.KeyMod.WinCtrl | monaco.KeyCode.KeyS, () => {
+      this.handleShowFind();
+    });
 
     this.updateEditorOptions(this.editor);
     this.setParagraphMode();
@@ -161,6 +240,9 @@ export class NotebookParagraphCodeEditorComponent 
implements OnChanges, OnDestro
       scrollbar: {
         handleMouseWheel: false,
         alwaysConsumeMouseWheel: false
+      },
+      find: {
+        addExtraSpaceOnTop: false
       }
     });
   }
diff --git 
a/zeppelin-web-angular/src/app/pages/workspace/notebook/paragraph/control/control.component.ts
 
b/zeppelin-web-angular/src/app/pages/workspace/notebook/paragraph/control/control.component.ts
index 3d39dcc1af..741c9288c0 100644
--- 
a/zeppelin-web-angular/src/app/pages/workspace/notebook/paragraph/control/control.component.ts
+++ 
b/zeppelin-web-angular/src/app/pages/workspace/notebook/paragraph/control/control.component.ts
@@ -85,6 +85,14 @@ export class NotebookParagraphControlComponent implements 
OnInit, OnChanges {
     trigger(): void;
   }> = [];
 
+  formatShortcut(shortcut: string, isMac: boolean) {
+    if (isMac) {
+      return shortcut.replace('Alt', 'Option');
+    }
+
+    return shortcut;
+  }
+
   updateListOfMenu() {
     this.listOfMenu = [
       {
@@ -93,7 +101,7 @@ export class NotebookParagraphControlComponent implements 
OnInit, OnChanges {
         disabled: this.isEntireNoteRunning,
         icon: 'play-circle',
         trigger: () => this.trigger(this.runParagraph),
-        shortCut: this.isMac ? '⇧+⌘+Enter' : 'Shift+Ctrl+Enter'
+        shortCut: this.formatShortcut('Shift+Enter', this.isMac)
       },
       {
         label: 'Run all above',
@@ -101,7 +109,7 @@ export class NotebookParagraphControlComponent implements 
OnInit, OnChanges {
         disabled: this.isEntireNoteRunning,
         icon: 'up-square',
         trigger: () => this.trigger(this.runAllAbove),
-        shortCut: this.isMac ? '⇧+⌘+Enter' : 'Shift+Ctrl+Enter'
+        shortCut: this.formatShortcut('Shift+Ctrl+Up', this.isMac)
       },
       {
         label: 'Run all below',
@@ -109,7 +117,7 @@ export class NotebookParagraphControlComponent implements 
OnInit, OnChanges {
         disabled: this.isEntireNoteRunning,
         icon: 'down-square',
         trigger: () => this.trigger(this.runAllBelowAndCurrent),
-        shortCut: this.isMac ? '⇧+⌘+Enter' : 'Shift+Ctrl+Enter'
+        shortCut: this.formatShortcut('Shift+Ctrl+Down', this.isMac)
       },
       {
         label: 'Link this paragraph',
@@ -119,7 +127,7 @@ export class NotebookParagraphControlComponent implements 
OnInit, OnChanges {
         trigger: () => {
           this.openSingleParagraph.emit(this.pid);
         },
-        shortCut: this.isMac ? '⌥+⌘+T' : 'Alt+Ctrl+T'
+        shortCut: this.formatShortcut('Ctrl+Alt+W', this.isMac)
       },
       {
         label: 'Clear output',
@@ -127,7 +135,7 @@ export class NotebookParagraphControlComponent implements 
OnInit, OnChanges {
         disabled: this.isEntireNoteRunning,
         icon: 'fire',
         trigger: () => this.clearParagraphOutput(),
-        shortCut: this.isMac ? '⌥+⌘+L' : 'Alt+Ctrl+L'
+        shortCut: this.formatShortcut('Ctrl+Alt+L', this.isMac)
       },
       {
         label: 'Remove',
@@ -135,23 +143,23 @@ export class NotebookParagraphControlComponent implements 
OnInit, OnChanges {
         disabled: this.isEntireNoteRunning,
         icon: 'delete',
         trigger: () => this.onRemoveParagraph(),
-        shortCut: this.isMac ? '⇧+Del (Command)' : 'Shift+Del (Command)'
+        shortCut: this.formatShortcut('Ctrl+Alt+D', this.isMac)
       },
       {
-        label: 'Move up',
+        label: 'Move paragraph up',
         show: !this.first,
         disabled: this.isEntireNoteRunning,
         icon: 'up',
         trigger: () => this.trigger(this.moveUp),
-        shortCut: `${this.isMac ? '⌘' : 'Ctrl'}+K (Command)`
+        shortCut: this.formatShortcut('Ctrl+Alt+K', this.isMac)
       },
       {
-        label: 'Move down',
+        label: 'Move paragraph down',
         show: !this.last,
         disabled: this.isEntireNoteRunning,
         icon: 'down',
         trigger: () => this.trigger(this.moveDown),
-        shortCut: `${this.isMac ? '⌘' : 'Ctrl'}+J (Command)`
+        shortCut: this.formatShortcut('Ctrl+Alt+J', this.isMac)
       },
       {
         label: 'Insert new',
@@ -159,7 +167,7 @@ export class NotebookParagraphControlComponent implements 
OnInit, OnChanges {
         disabled: this.isEntireNoteRunning,
         icon: 'plus',
         trigger: () => this.trigger(this.insertNew),
-        shortCut: `B (Command)`
+        shortCut: this.formatShortcut('Ctrl+Alt+B', this.isMac)
       },
       {
         label: 'Clone paragraph',
@@ -167,7 +175,7 @@ export class NotebookParagraphControlComponent implements 
OnInit, OnChanges {
         disabled: this.isEntireNoteRunning,
         icon: 'copy',
         trigger: () => this.trigger(this.cloneParagraph),
-        shortCut: `C (Command)`
+        shortCut: this.formatShortcut('Shift+Ctrl+C', this.isMac)
       },
       {
         label: this.title ? 'Hide Title' : 'Show Title',
@@ -175,7 +183,7 @@ export class NotebookParagraphControlComponent implements 
OnInit, OnChanges {
         disabled: false,
         icon: 'font-colors',
         trigger: () => this.toggleTitle(),
-        shortCut: `T (Command)`
+        shortCut: this.formatShortcut('Ctrl+Alt+T', this.isMac)
       },
       {
         label: this.lineNumbers ? 'Hide line numbers' : 'Show line numbers',
@@ -183,7 +191,7 @@ export class NotebookParagraphControlComponent implements 
OnInit, OnChanges {
         disabled: false,
         icon: 'ordered-list',
         trigger: () => this.toggleLineNumbers(),
-        shortCut: `L (Command)`
+        shortCut: this.formatShortcut('Ctrl+Alt+M', this.isMac)
       },
       {
         label: this.enabled ? 'Disable run' : 'Enable run',
@@ -191,7 +199,7 @@ export class NotebookParagraphControlComponent implements 
OnInit, OnChanges {
         disabled: this.isEntireNoteRunning,
         icon: 'api',
         trigger: () => this.toggleEnabled(),
-        shortCut: `R (Command)`
+        shortCut: this.formatShortcut('Ctrl+Alt+R', this.isMac)
       }
     ];
   }
diff --git 
a/zeppelin-web-angular/src/app/pages/workspace/notebook/paragraph/paragraph.component.html
 
b/zeppelin-web-angular/src/app/pages/workspace/notebook/paragraph/paragraph.component.html
index 5892aee551..63ecfa5639 100644
--- 
a/zeppelin-web-angular/src/app/pages/workspace/notebook/paragraph/paragraph.component.html
+++ 
b/zeppelin-web-angular/src/app/pages/workspace/notebook/paragraph/paragraph.component.html
@@ -82,6 +82,7 @@
       (editorBlur)="onEditorBlur()"
       (editorFocus)="onEditorFocus()"
       (textChanged)="textChanged($event)"
+      (toggleEditorShow)="toggleEditorShow()"
     ></zeppelin-notebook-paragraph-code-editor>
     <zeppelin-notebook-paragraph-progress
       *ngIf="paragraph.status === 'RUNNING'"
diff --git 
a/zeppelin-web-angular/src/app/pages/workspace/notebook/paragraph/paragraph.component.ts
 
b/zeppelin-web-angular/src/app/pages/workspace/notebook/paragraph/paragraph.component.ts
index 1d9cb4675f..d740b9317f 100644
--- 
a/zeppelin-web-angular/src/app/pages/workspace/notebook/paragraph/paragraph.component.ts
+++ 
b/zeppelin-web-angular/src/app/pages/workspace/notebook/paragraph/paragraph.component.ts
@@ -84,6 +84,7 @@ export class NotebookParagraphComponent extends ParagraphBase 
implements OnInit,
   @Output() readonly triggerSaveParagraph = new EventEmitter<string>();
   @Output() readonly selected = new EventEmitter<string>();
   @Output() readonly selectAtIndex = new EventEmitter<number>();
+  @Output() readonly searchCode = new EventEmitter();
 
   private destroy$ = new Subject();
   private mode: Mode = 'command';
@@ -155,6 +156,11 @@ export class NotebookParagraphComponent extends 
ParagraphBase implements OnInit,
     }
   }
 
+  toggleEditorShow() {
+    this.setEditorHide(!this.paragraph.config.editorHide);
+    this.commitParagraph();
+  }
+
   saveParagraph() {
     const dirtyText = this.paragraph.text;
     if (dirtyText === undefined || dirtyText === this.originalText) {
@@ -356,7 +362,7 @@ export class NotebookParagraphComponent extends 
ParagraphBase implements OnInit,
     this.cdr.markForCheck();
   }
 
-  moveUpParagraph() {
+  moveCursorUp() {
     const newIndex = this.note.paragraphs.findIndex(p => p.id === 
this.paragraph.id) - 1;
     if (newIndex < 0 || newIndex >= this.note.paragraphs.length) {
       return;
@@ -369,7 +375,7 @@ export class NotebookParagraphComponent extends 
ParagraphBase implements OnInit,
     this.messageService.moveParagraph(this.paragraph.id, newIndex);
   }
 
-  moveDownParagraph() {
+  moveCursorDown() {
     const newIndex = this.note.paragraphs.findIndex(p => p.id === 
this.paragraph.id) + 1;
     if (newIndex < 0 || newIndex >= this.note.paragraphs.length) {
       return;
@@ -382,6 +388,28 @@ export class NotebookParagraphComponent extends 
ParagraphBase implements OnInit,
     this.messageService.moveParagraph(this.paragraph.id, newIndex);
   }
 
+  moveParagraphUp() {
+    const newIndex = this.note.paragraphs.findIndex(p => p.id === 
this.paragraph.id) - 1;
+    if (newIndex < 0 || newIndex >= this.note.paragraphs.length) {
+      return;
+    }
+    this.messageService.moveParagraph(this.paragraph.id, newIndex);
+  }
+
+  moveParagraphDown() {
+    const newIndex = this.note.paragraphs.findIndex(p => p.id === 
this.paragraph.id) + 1;
+    if (newIndex < 0 || newIndex >= this.note.paragraphs.length) {
+      return;
+    }
+    this.messageService.moveParagraph(this.paragraph.id, newIndex);
+  }
+
+  clearParagraphOutput() {
+    if (!this.isEntireNoteRunning) {
+      this.messageService.paragraphClearOutput(this.paragraph.id);
+    }
+  }
+
   changeColWidth(needCommit: boolean, updateResult = true) {
     if (needCommit) {
       this.commitParagraph();
@@ -485,89 +513,15 @@ export class NotebookParagraphComponent extends 
ParagraphBase implements OnInit,
           return; // ignore shortcut to make input work
         }
 
-        if (this.mode === 'command') {
-          switch (action) {
-            case ParagraphActions.InsertAbove:
-              this.insertParagraph('above');
-              break;
-            case ParagraphActions.InsertBelow:
-              this.insertParagraph('below');
-              break;
-            case ParagraphActions.SwitchEditorShow:
-              this.setEditorHide(!this.paragraph.config.editorHide);
-              this.commitParagraph();
-              break;
-            case ParagraphActions.SwitchOutputShow:
-              this.setTableHide(!this.paragraph.config.tableHide);
-              this.commitParagraph();
-              break;
-            case ParagraphActions.SwitchTitleShow:
-              this.paragraph.config.title = !this.paragraph.config.title;
-              this.commitParagraph();
-              break;
-            case ParagraphActions.SwitchLineNumber:
-              this.paragraph.config.lineNumbers = 
!this.paragraph.config.lineNumbers;
-              this.commitParagraph();
-              break;
-            case ParagraphActions.MoveToUp:
-              event.preventDefault();
-              this.moveUpParagraph();
-              break;
-            case ParagraphActions.MoveToDown:
-              event.preventDefault();
-              this.moveDownParagraph();
-              break;
-            case ParagraphActions.SwitchEnable:
-              this.paragraph.config.enabled = !this.paragraph.config.enabled;
-              this.commitParagraph();
-              break;
-            case ParagraphActions.ReduceWidth:
-              if (!this.paragraph.config.colWidth) {
-                throw new Error('colWidth is required');
-              }
-              this.paragraph.config.colWidth = Math.max(1, 
this.paragraph.config.colWidth - 1);
-              this.cdr.markForCheck();
-              this.changeColWidth(true);
-              break;
-            case ParagraphActions.IncreaseWidth:
-              if (!this.paragraph.config.colWidth) {
-                throw new Error('colWidth is required');
-              }
-              this.paragraph.config.colWidth = Math.min(12, 
this.paragraph.config.colWidth + 1);
-              this.cdr.markForCheck();
-              this.changeColWidth(true);
-              break;
-            case ParagraphActions.Delete:
-              this.removeParagraph();
-              break;
-            case ParagraphActions.SelectAbove:
-              event.preventDefault();
-              this.selectAtIndex.emit(this.index - 1);
-              break;
-            case ParagraphActions.SelectBelow:
-              event.preventDefault();
-              this.selectAtIndex.emit(this.index + 1);
-              break;
-            default:
-              break;
-          }
-        }
         switch (action) {
-          case ParagraphActions.Link:
-            this.openSingleParagraph(this.paragraph.id);
-            break;
-          case ParagraphActions.EditMode:
-            if (this.mode === 'command') {
-              event.preventDefault();
-            }
-            if (!this.paragraph.config.editorHide) {
-              this.switchMode('edit');
-            }
-            break;
           case ParagraphActions.Run:
             event.preventDefault();
             this.runParagraph();
             break;
+          case ParagraphActions.RunAbove:
+            this.waitConfirmFromEdit = true;
+            this.runAllAbove();
+            break;
           case ParagraphActions.RunBelow:
             this.waitConfirmFromEdit = true;
             this.runAllBelowAndCurrent();
@@ -576,6 +530,75 @@ export class NotebookParagraphComponent extends 
ParagraphBase implements OnInit,
             event.preventDefault();
             this.cancelParagraph();
             break;
+          case ParagraphActions.MoveCursorUp:
+            event.preventDefault();
+            this.moveCursorUp();
+            break;
+          case ParagraphActions.MoveCursorDown:
+            event.preventDefault();
+            this.moveCursorDown();
+            break;
+          case ParagraphActions.Delete:
+            this.removeParagraph();
+            break;
+          case ParagraphActions.InsertAbove:
+            this.insertParagraph('above');
+            break;
+          case ParagraphActions.InsertBelow:
+            this.insertParagraph('below');
+            break;
+          case ParagraphActions.InsertCopyOfParagraphBelow:
+            this.cloneParagraph('below');
+            break;
+          case ParagraphActions.MoveParagraphUp:
+            event.preventDefault();
+            this.moveParagraphUp();
+            break;
+          case ParagraphActions.MoveParagraphDown:
+            event.preventDefault();
+            this.moveParagraphDown();
+            break;
+          case ParagraphActions.SwitchEnable:
+            this.paragraph.config.enabled = !this.paragraph.config.enabled;
+            this.commitParagraph();
+            break;
+          case ParagraphActions.SwitchOutputShow:
+            this.setTableHide(!this.paragraph.config.tableHide);
+            this.commitParagraph();
+            break;
+          case ParagraphActions.SwitchLineNumber:
+            this.paragraph.config.lineNumbers = 
!this.paragraph.config.lineNumbers;
+            this.commitParagraph();
+            break;
+          case ParagraphActions.SwitchTitleShow:
+            this.paragraph.config.title = !this.paragraph.config.title;
+            this.commitParagraph();
+            break;
+          case ParagraphActions.Clear:
+            this.clearParagraphOutput();
+            break;
+          case ParagraphActions.Link:
+            this.openSingleParagraph(this.paragraph.id);
+            break;
+          case ParagraphActions.ReduceWidth:
+            if (!this.paragraph.config.colWidth) {
+              throw new Error('colWidth is required');
+            }
+            this.paragraph.config.colWidth = Math.max(1, 
this.paragraph.config.colWidth - 1);
+            this.cdr.markForCheck();
+            this.changeColWidth(true);
+            break;
+          case ParagraphActions.IncreaseWidth:
+            if (!this.paragraph.config.colWidth) {
+              throw new Error('colWidth is required');
+            }
+            this.paragraph.config.colWidth = Math.min(12, 
this.paragraph.config.colWidth + 1);
+            this.cdr.markForCheck();
+            this.changeColWidth(true);
+            break;
+          case ParagraphActions.FindInCode:
+            this.searchCode.emit();
+            break;
           default:
             break;
         }
diff --git a/zeppelin-web-angular/src/app/services/shortcut.service.ts 
b/zeppelin-web-angular/src/app/services/shortcut.service.ts
index 135de34388..f3cdb96620 100644
--- a/zeppelin-web-angular/src/app/services/shortcut.service.ts
+++ b/zeppelin-web-angular/src/app/services/shortcut.service.ts
@@ -16,56 +16,61 @@ import { EventManager } from '@angular/platform-browser';
 import { Observable } from 'rxjs';
 
 export enum ParagraphActions {
-  EditMode = 'Paragraph:EditMode',
-  CommandMode = 'Paragraph:CommandMode',
   Run = 'Paragraph:Run',
+  RunAbove = 'Paragraph:RunAbove',
   RunBelow = 'Paragraph:RunBelow',
   Cancel = 'Paragraph:Cancel',
-  Clear = 'Paragraph:Clear',
-  ReduceWidth = 'Paragraph:ReduceWidth',
-  IncreaseWidth = 'Paragraph:IncreaseWidth',
+  MoveCursorUp = 'Paragraph:MoveCursorUp',
+  MoveCursorDown = 'Paragraph:MoveCursorDown',
   Delete = 'Paragraph:Delete',
-  MoveToUp = 'Paragraph:MoveToUp',
-  MoveToDown = 'Paragraph:MoveToDown',
-  SelectAbove = 'Paragraph:SelectAbove',
-  SelectBelow = 'Paragraph:SelectBelow',
   InsertAbove = 'Paragraph:InsertAbove',
   InsertBelow = 'Paragraph:InsertBelow',
+  InsertCopyOfParagraphBelow = 'Paragraph:InsertCopyOfParagraphBelow',
+  MoveParagraphUp = 'Paragraph:MoveParagraphUp',
+  MoveParagraphDown = 'Paragraph:MoveParagraphDown',
+  SwitchEnable = 'Paragraph:SwitchEnable',
+  SwitchOutputShow = 'Paragraph:SwitchOutputShow',
   SwitchLineNumber = 'Paragraph:SwitchLineNumber',
   SwitchTitleShow = 'Paragraph:SwitchTitleShow',
-  SwitchOutputShow = 'Paragraph:SwitchOutputShow',
-  SwitchEditorShow = 'Paragraph:SwitchEditorShow',
-  SwitchEnable = 'Paragraph:SwitchEnable',
-  Link = 'Paragraph:Link'
+  Clear = 'Paragraph:Clear',
+  Link = 'Paragraph:Link',
+  ReduceWidth = 'Paragraph:ReduceWidth',
+  IncreaseWidth = 'Paragraph:IncreaseWidth',
+  FindInCode = 'Paragraph:FindInCode'
 }
 
+// On macOS, pressing Option(Alt) + a letter produces a non-ASCII character
+// Shortcuts must use this resulting character instead of the plain letter for 
macOS
 export const ShortcutsMap = {
-  [ParagraphActions.EditMode]: 'enter',
-  [ParagraphActions.CommandMode]: 'esc',
-  [ParagraphActions.Run]: 'shift.enter',
-  [ParagraphActions.RunBelow]: 'shift.ctrlCmd.enter',
-  [ParagraphActions.Cancel]: 'shift.ctrlCmd.c',
-  // Need register special character `¬` in MacOS
-  [ParagraphActions.Clear]: ['alt.ctrlCmd.l', 'alt.ctrlCmd.¬'],
-  // Need register special character `†` in MacOS
-  [ParagraphActions.Link]: ['alt.ctrlCmd.t', 'alt.ctrlCmd.†'],
-  // Need register special character `®` in MacOS
-  [ParagraphActions.SwitchEnable]: ['alt.ctrlCmd.r', 'alt.ctrlCmd.®'],
-  // Need register special character `–` in MacOS
-  [ParagraphActions.ReduceWidth]: ['alt.ctrlCmd.-', 'alt.ctrlCmd.–'],
-  // Need register special character `≠` in MacOS
-  [ParagraphActions.IncreaseWidth]: ['alt.ctrlCmd.+', 'alt.ctrlCmd.≠'],
-  [ParagraphActions.Delete]: 'shift.delete',
-  [ParagraphActions.MoveToUp]: ['ctrlCmd.k', 'ctrlCmd.arrowup', 
'ctrlCmd.arrowleft'],
-  [ParagraphActions.MoveToDown]: ['ctrlCmd.j', 'ctrlCmd.arrowdown', 
'ctrlCmd.arrowright'],
-  [ParagraphActions.SelectAbove]: ['k', 'arrowup', 'arrowleft'],
-  [ParagraphActions.SelectBelow]: ['j', 'arrowdown', 'arrowright'],
-  [ParagraphActions.SwitchLineNumber]: 'l',
-  [ParagraphActions.SwitchTitleShow]: 't',
-  [ParagraphActions.SwitchOutputShow]: 'o',
-  [ParagraphActions.SwitchEditorShow]: 'e',
-  [ParagraphActions.InsertAbove]: 'a',
-  [ParagraphActions.InsertBelow]: 'b'
+  [ParagraphActions.Run]: 'shift.enter', // Run paragraph
+  [ParagraphActions.RunAbove]: 'control.shift.arrowup', // Run all above 
paragraphs (exclusive)
+  [ParagraphActions.RunBelow]: 'control.shift.arrowdown', // Run all below 
paragraphs (inclusive)
+  [ParagraphActions.Cancel]: ['control.alt.c', 'control.alt.ç'], // Cancel
+  [ParagraphActions.MoveCursorUp]: 'control.p', // Move cursor Up
+  [ParagraphActions.MoveCursorDown]: 'control.n', // Move cursor Down
+  [ParagraphActions.Delete]: ['control.alt.d', 'control.alt.∂'], // Remove 
paragraph
+  [ParagraphActions.InsertAbove]: ['control.alt.a', 'control.alt.å'], // 
Insert new paragraph above
+  [ParagraphActions.InsertBelow]: ['control.alt.b', 'control.alt.∫'], // 
Insert new paragraph below
+  [ParagraphActions.InsertCopyOfParagraphBelow]: 'control.shift.c', // Insert 
copy of paragraph below
+  [ParagraphActions.MoveParagraphUp]: ['control.alt.k', 'control.alt.˚'], // 
Move paragraph Up
+  [ParagraphActions.MoveParagraphDown]: ['control.alt.j', 'control.alt.∆'], // 
Move paragraph Down
+  [ParagraphActions.SwitchEnable]: ['control.alt.r', 'control.alt.®'], // 
Enable/Disable run paragraph
+  [ParagraphActions.SwitchOutputShow]: ['control.alt.o', 'control.alt.ø'], // 
Toggle output
+  // Toggle editor - Shortcut logic is implemented in the editor component
+  [ParagraphActions.SwitchLineNumber]: ['control.alt.m', 'control.alt.µ'], // 
Toggle line number
+  [ParagraphActions.SwitchTitleShow]: ['control.alt.t', 'control.alt.†'], // 
Toggle title
+  [ParagraphActions.Clear]: ['control.alt.l', 'control.alt.¬'], // Clear output
+  [ParagraphActions.Link]: ['control.alt.w', 'control.alt.∑'], // Link this 
paragraph
+  [ParagraphActions.ReduceWidth]: 'control.shift._', // Reduce paragraph width
+  [ParagraphActions.IncreaseWidth]: 'control.shift.=', // Increase paragraph 
width
+  // Auto-completion - No longer needed; always applied now
+  // Cut the line - Shortcut logic is implemented in the editor component
+  // Paste the line - Shortcut logic is implemented in the editor component
+  // Search inside the code - Shortcut logic is implemented in the editor 
component
+  // Move cursor to the beginning - System shortcut
+  // Move cursor at the end - System shortcut
+  // TODO: Check after the search code is implemented in 
action-bar.component.ts
+  [ParagraphActions.FindInCode]: ['control.alt.f', 'control.alt.ƒ'] // Find in 
code
 };
 
 export interface ShortcutEvent {
@@ -78,10 +83,6 @@ export interface ShortcutOption {
   keybindings: string;
 }
 
-function isMacOS() {
-  return navigator.platform.indexOf('Mac') > -1;
-}
-
 @Injectable({
   providedIn: 'root'
 })
@@ -99,9 +100,7 @@ export class ShortcutService {
 
   bindShortcut(option: ShortcutOption): Observable<ShortcutEvent> {
     const host = option.scope || this.element;
-    // `ctrlCmd` is special symbol, will be replaced `meta` in MacOS, 
'control' in Windows/Linux
-    const keybindings = option.keybindings.replace(/ctrlCmd/g, isMacOS() ? 
'meta' : 'control');
-    const eventName = `keydown.${keybindings}`;
+    const eventName = `keydown.${option.keybindings}`;
     // tslint:disable-next-line:ban-types
     let dispose: Function;
     return new Observable<ShortcutEvent>(observer => {
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph-control.html 
b/zeppelin-web/src/app/notebook/paragraph/paragraph-control.html
index 81d711155d..1621a17410 100644
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph-control.html
+++ b/zeppelin-web/src/app/notebook/paragraph/paragraph-control.html
@@ -141,7 +141,7 @@ limitations under the License.
             <span class="icon-action-redo shortcut-icon"
                   style="position: relative; transform: rotate(-90deg); left: 
-4px;">
             </span>
-            <span class="shortcut-keys">Ctrl+Shift+Enter</span>
+            <span class="shortcut-keys">Ctrl+Shift+Up</span>
             Run all above
           </a>
         </li>
@@ -150,7 +150,7 @@ limitations under the License.
             <span class="icon-action-undo shortcut-icon"
                   style="position: relative; transform: rotate(-90deg); left: 
-4px;">
             </span>
-            <span class="shortcut-keys">Ctrl+Shift+Enter</span>
+            <span class="shortcut-keys">Ctrl+Shift+Down</span>
              Run all below
           </a>
         </li>
@@ -189,7 +189,7 @@ limitations under the License.
         <li>
           <a ng-click="toggleEnableDisable(paragraph)" 
ng-class="{'item-disable' : isNoteRunning}">
             <span class="icon-control-play shortcut-icon"></span>
-            <span class="shortcut-keys">Ctrl+ {{ isMac ? 'Option' : 
'Alt'}}+R</span>
+            <span class="shortcut-keys">Ctrl+{{ isMac ? 'Option' : 
'Alt'}}+R</span>
             {{paragraph.config.enabled ? "Disable" : "Enable"}} run
           </a>
         </li>


Reply via email to