Skip to content

Commit 65fd70f

Browse files
authored
[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. #### d96ae5b - 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) #### 0d24eb4 - 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. #### 1d3d294 - 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". #### 57f45e1 - 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/secure/RapidBoard.jspa?rapidView=632&view=detail&selectedIssue=ZEPPELIN-6279)) in the New UI, I set up the connection by adding a Template Reference Variable and Event Binding in `notebook.component.html`. With this setup, `paragraph.component.ts` can invoke the **searchCode()** method of `action-bar.component.ts`. #### 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 <[email protected]>
1 parent 078b754 commit 65fd70f

File tree

8 files changed

+260
-144
lines changed

8 files changed

+260
-144
lines changed

zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.component.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
[currentRevision]="currentRevision"
2424
(tableHideChange)="setAllParagraphTableHide($event)"
2525
(editorHideChange)="setAllParagraphEditorHide($event)"
26+
#actionBar
2627
></zeppelin-notebook-action-bar>
2728
<div class="flex-container">
2829
<div
@@ -87,6 +88,7 @@
8788
(selected)="onParagraphSelect($event)"
8889
(triggerSaveParagraph)="saveParagraph($event)"
8990
(saveNoteTimer)="startSaveTimer()"
91+
(searchCode)="actionBar.searchCode()"
9092
></zeppelin-notebook-paragraph>
9193
</div>
9294
</div>

zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ export class NotebookComponent extends MessageListenersManager implements OnInit
198198
// Call when next tick
199199
setTimeout(() => {
200200
scrollIntoViewIfNeeded(paragraphComponent.getElement());
201+
paragraphComponent.focusEditor();
201202
});
202203
}
203204
}

zeppelin-web-angular/src/app/pages/workspace/notebook/paragraph/code-editor/code-editor.component.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export class NotebookParagraphCodeEditorComponent implements OnChanges, OnDestro
5757
@Output() readonly textChanged = new EventEmitter<string>();
5858
@Output() readonly editorBlur = new EventEmitter<void>();
5959
@Output() readonly editorFocus = new EventEmitter<void>();
60+
@Output() readonly toggleEditorShow = new EventEmitter<void>();
6061
private editor?: IStandaloneCodeEditor;
6162
private monacoDisposables: IDisposable[] = [];
6263
height = 18;
@@ -108,6 +109,72 @@ export class NotebookParagraphCodeEditorComponent implements OnChanges, OnDestro
108109
}
109110
}
110111

112+
// Handle Ctrl+Alt+E: Toggle editor show/hide
113+
handleToggleEditorShow() {
114+
this.toggleEditorShow.emit();
115+
}
116+
117+
// Handle Ctrl+K: Cut current line to clipboard
118+
async handleCutLine() {
119+
if (!this.editor) {
120+
return;
121+
}
122+
123+
const position = this.editor.getPosition();
124+
const model = this.editor.getModel();
125+
if (!position || !model) {
126+
return;
127+
}
128+
129+
const lineNumber = position.lineNumber;
130+
const lineContent = model.getLineContent(lineNumber);
131+
132+
if (!lineContent) {
133+
return;
134+
}
135+
136+
await navigator.clipboard.writeText(lineContent);
137+
138+
this.editor.executeEdits('cut-line', [
139+
{
140+
range: new monaco.Range(lineNumber, 1, lineNumber, lineContent.length + 1),
141+
text: '',
142+
forceMoveMarkers: true
143+
}
144+
]);
145+
}
146+
147+
// Handle Ctrl+Y: Paste from clipboard at current position
148+
async handlePasteFromClipboard() {
149+
if (!this.editor) {
150+
return;
151+
}
152+
153+
const text = await navigator.clipboard.readText();
154+
const position = this.editor.getPosition();
155+
if (position) {
156+
this.editor.executeEdits('my-source', [
157+
{
158+
range: new monaco.Range(position.lineNumber, position.column, position.lineNumber, position.column),
159+
text: text,
160+
forceMoveMarkers: true
161+
}
162+
]);
163+
}
164+
}
165+
166+
// Handle Ctrl+S: Show find widget
167+
handleShowFind() {
168+
if (this.editor) {
169+
this.editor.getAction('actions.find').run();
170+
171+
// Focus on the find widget input field
172+
const findInput = document.querySelector('.find-widget .input') as HTMLInputElement;
173+
findInput.focus();
174+
findInput.select();
175+
}
176+
}
177+
111178
initializedEditor(editor: IEditor) {
112179
this.editor = editor as IStandaloneCodeEditor;
113180
this.editor.addCommand(
@@ -119,6 +186,18 @@ export class NotebookParagraphCodeEditorComponent implements OnChanges, OnDestro
119186
},
120187
'!suggestWidgetVisible'
121188
);
189+
this.editor.addCommand(monaco.KeyMod.WinCtrl | monaco.KeyMod.Alt | monaco.KeyCode.KeyE, () => {
190+
this.handleToggleEditorShow();
191+
});
192+
this.editor.addCommand(monaco.KeyMod.WinCtrl | monaco.KeyCode.KeyK, () => {
193+
this.handleCutLine();
194+
});
195+
this.editor.addCommand(monaco.KeyMod.WinCtrl | monaco.KeyCode.KeyY, () => {
196+
this.handlePasteFromClipboard();
197+
});
198+
this.editor.addCommand(monaco.KeyMod.WinCtrl | monaco.KeyCode.KeyS, () => {
199+
this.handleShowFind();
200+
});
122201

123202
this.updateEditorOptions(this.editor);
124203
this.setParagraphMode();
@@ -161,6 +240,9 @@ export class NotebookParagraphCodeEditorComponent implements OnChanges, OnDestro
161240
scrollbar: {
162241
handleMouseWheel: false,
163242
alwaysConsumeMouseWheel: false
243+
},
244+
find: {
245+
addExtraSpaceOnTop: false
164246
}
165247
});
166248
}

zeppelin-web-angular/src/app/pages/workspace/notebook/paragraph/control/control.component.ts

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@ export class NotebookParagraphControlComponent implements OnInit, OnChanges {
8585
trigger(): void;
8686
}> = [];
8787

88+
formatShortcut(shortcut: string, isMac: boolean) {
89+
if (isMac) {
90+
return shortcut.replace('Alt', 'Option');
91+
}
92+
93+
return shortcut;
94+
}
95+
8896
updateListOfMenu() {
8997
this.listOfMenu = [
9098
{
@@ -93,23 +101,23 @@ export class NotebookParagraphControlComponent implements OnInit, OnChanges {
93101
disabled: this.isEntireNoteRunning,
94102
icon: 'play-circle',
95103
trigger: () => this.trigger(this.runParagraph),
96-
shortCut: this.isMac ? '⇧+⌘+Enter' : 'Shift+Ctrl+Enter'
104+
shortCut: this.formatShortcut('Shift+Enter', this.isMac)
97105
},
98106
{
99107
label: 'Run all above',
100108
show: !this.first,
101109
disabled: this.isEntireNoteRunning,
102110
icon: 'up-square',
103111
trigger: () => this.trigger(this.runAllAbove),
104-
shortCut: this.isMac ? '⇧+⌘+Enter' : 'Shift+Ctrl+Enter'
112+
shortCut: this.formatShortcut('Shift+Ctrl+Up', this.isMac)
105113
},
106114
{
107115
label: 'Run all below',
108116
show: !this.last,
109117
disabled: this.isEntireNoteRunning,
110118
icon: 'down-square',
111119
trigger: () => this.trigger(this.runAllBelowAndCurrent),
112-
shortCut: this.isMac ? '⇧+⌘+Enter' : 'Shift+Ctrl+Enter'
120+
shortCut: this.formatShortcut('Shift+Ctrl+Down', this.isMac)
113121
},
114122
{
115123
label: 'Link this paragraph',
@@ -119,79 +127,79 @@ export class NotebookParagraphControlComponent implements OnInit, OnChanges {
119127
trigger: () => {
120128
this.openSingleParagraph.emit(this.pid);
121129
},
122-
shortCut: this.isMac ? '⌥+⌘+T' : 'Alt+Ctrl+T'
130+
shortCut: this.formatShortcut('Ctrl+Alt+W', this.isMac)
123131
},
124132
{
125133
label: 'Clear output',
126134
show: true,
127135
disabled: this.isEntireNoteRunning,
128136
icon: 'fire',
129137
trigger: () => this.clearParagraphOutput(),
130-
shortCut: this.isMac ? '⌥+⌘+L' : 'Alt+Ctrl+L'
138+
shortCut: this.formatShortcut('Ctrl+Alt+L', this.isMac)
131139
},
132140
{
133141
label: 'Remove',
134142
show: this.paragraphLength > 1,
135143
disabled: this.isEntireNoteRunning,
136144
icon: 'delete',
137145
trigger: () => this.onRemoveParagraph(),
138-
shortCut: this.isMac ? '⇧+Del (Command)' : 'Shift+Del (Command)'
146+
shortCut: this.formatShortcut('Ctrl+Alt+D', this.isMac)
139147
},
140148
{
141-
label: 'Move up',
149+
label: 'Move paragraph up',
142150
show: !this.first,
143151
disabled: this.isEntireNoteRunning,
144152
icon: 'up',
145153
trigger: () => this.trigger(this.moveUp),
146-
shortCut: `${this.isMac ? '⌘' : 'Ctrl'}+K (Command)`
154+
shortCut: this.formatShortcut('Ctrl+Alt+K', this.isMac)
147155
},
148156
{
149-
label: 'Move down',
157+
label: 'Move paragraph down',
150158
show: !this.last,
151159
disabled: this.isEntireNoteRunning,
152160
icon: 'down',
153161
trigger: () => this.trigger(this.moveDown),
154-
shortCut: `${this.isMac ? '⌘' : 'Ctrl'}+J (Command)`
162+
shortCut: this.formatShortcut('Ctrl+Alt+J', this.isMac)
155163
},
156164
{
157165
label: 'Insert new',
158166
show: true,
159167
disabled: this.isEntireNoteRunning,
160168
icon: 'plus',
161169
trigger: () => this.trigger(this.insertNew),
162-
shortCut: `B (Command)`
170+
shortCut: this.formatShortcut('Ctrl+Alt+B', this.isMac)
163171
},
164172
{
165173
label: 'Clone paragraph',
166174
show: true,
167175
disabled: this.isEntireNoteRunning,
168176
icon: 'copy',
169177
trigger: () => this.trigger(this.cloneParagraph),
170-
shortCut: `C (Command)`
178+
shortCut: this.formatShortcut('Shift+Ctrl+C', this.isMac)
171179
},
172180
{
173181
label: this.title ? 'Hide Title' : 'Show Title',
174182
show: true,
175183
disabled: false,
176184
icon: 'font-colors',
177185
trigger: () => this.toggleTitle(),
178-
shortCut: `T (Command)`
186+
shortCut: this.formatShortcut('Ctrl+Alt+T', this.isMac)
179187
},
180188
{
181189
label: this.lineNumbers ? 'Hide line numbers' : 'Show line numbers',
182190
show: true,
183191
disabled: false,
184192
icon: 'ordered-list',
185193
trigger: () => this.toggleLineNumbers(),
186-
shortCut: `L (Command)`
194+
shortCut: this.formatShortcut('Ctrl+Alt+M', this.isMac)
187195
},
188196
{
189197
label: this.enabled ? 'Disable run' : 'Enable run',
190198
show: true,
191199
disabled: this.isEntireNoteRunning,
192200
icon: 'api',
193201
trigger: () => this.toggleEnabled(),
194-
shortCut: `R (Command)`
202+
shortCut: this.formatShortcut('Ctrl+Alt+R', this.isMac)
195203
}
196204
];
197205
}

zeppelin-web-angular/src/app/pages/workspace/notebook/paragraph/paragraph.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
(editorBlur)="onEditorBlur()"
8383
(editorFocus)="onEditorFocus()"
8484
(textChanged)="textChanged($event)"
85+
(toggleEditorShow)="toggleEditorShow()"
8586
></zeppelin-notebook-paragraph-code-editor>
8687
<zeppelin-notebook-paragraph-progress
8788
*ngIf="paragraph.status === 'RUNNING'"

0 commit comments

Comments
 (0)