-
Notifications
You must be signed in to change notification settings - Fork 2.8k
[ZEPPELIN-6294] Made "Clone paragraph" button work properly in New UI #5044
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for taking a look! From my side, data.paragraph.text appears on PARAGRAPH_ADDED in both UIs, but I might be mistaken. If there’s a case where it’s empty, I’d love the exact repro steps.
One possibility is that the New UI is skipping a valid PARAGRAPH as if it were short-circuited when another request bumps lastMsgIdSeq before the final reply—PARAGRAPH
message (see message.ts, L187). I noticed EDITOR_SETTING
messages being sent from the client and suspect they might be the trigger. If the only intended short-circuit is RUN_PARAGRAPH → synthetic PARAGRAPH_STATUS, would it make sense to remove the second filter() at L187 rather than adding a special-case branch at the NotebookServer
? Happy to adjust based on your feedback.
I really appreciate about your review. 👍 👍 As you mentioned, the issue was caused by EDITOR_SETTING being triggered before PARAGRAPH, which led to short-circuit processing. However, modifying the condition in message.ts, L187 didn’t seem optimal from a performance standpoint.(I'm not entirely sure if that's really the case) So I adjusted the timing in the relevant section of the app/pages/workspace/notebook/paragraph/code-editor/code-editor.component.ts instead. Because the After reverted NotebookServer code, everything worked as intended. Thanks for pointing out a better way to improve it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice fix! Since the setTimeout(..., 200)
is a heuristic to dodge a race, could we add a short comment explaining (1) why it's needed and (2) a TODO o how to make this deterministic or order-independent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the thorough update — I’ve left some questions and requested changes.
@@ -54,6 +55,7 @@ export abstract class ParagraphBase extends MessageListenersManager { | |||
|
|||
// Initialized by `ViewChildren` in the class which extends ParagraphBase | |||
notebookParagraphResultComponents!: QueryList<NotebookParagraphResultComponent>; | |||
notebookParagraphCodeEditorComponent!: NotebookParagraphCodeEditorComponent; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this non-null assertion should be removed and changed to optional. I found it to be undefined
when paragraph.config.editorHide === true
in the %md
editor case. Maybe you could check some errors after running %md
interpreter.
(zeppelin-web-angular/src/app/pages/workspace/notebook/paragraph/paragraph.component.html
)
<zeppelin-notebook-paragraph-code-editor
*ngIf="!paragraph.config.editorHide && !viewOnly"
...
I mistakenly added non-null assertion for notebookParagraphCodeEditorComponent
in NotebookParagraphComponent
in a previous commit, but that was incorrect. Sorry for the confusion.
@@ -109,6 +109,10 @@ export class NotebookComponent extends MessageListenersManager implements OnInit | |||
|
|||
@MessageListener(OP.INTERPRETER_BINDINGS) | |||
loadInterpreterBindings(data: MessageReceiveDataTypeMap[OP.INTERPRETER_BINDINGS]) { | |||
this.listOfNotebookParagraphComponent.forEach(item => { | |||
item.notebookParagraphCodeEditorComponent.editorSettingTriggerAllowed = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should handle some case where notebookParagraphCodeEditorComponent
is undefined.
Also, should we trigger editor settings for all paragraphs here? Would calling this.setParagraphMode(true)
in editor.onDidChangeModelContent()
be sufficient to get editor settings only to the paragraph whose interpreter has changed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In testing, the interpreter setting works correctly when this.setParagraphMode(true)
is executed inside editor.onDidChangeModelContent()
.
Since this runs before the code that executes when receiving INTERPRETER_BINDINGS
above (editor.onDidChangeModelContent()
), the code that runs after editorSettingTriggerAllowed = true
was modified to directly call getEditorSetting()
instead of using setParagraphMode(true)
when receive PARAGRAPH
or INTERPRETER_BINDINGS
.
Because if the interpreter is already set inside this.setParagraphMode(true)
, getEditorSetting()
will not be executed due to the condition, and since setParagraphMode(true)
has already run, there is no need to execute it again.
@@ -142,6 +144,8 @@ export abstract class ParagraphBase extends MessageListenersManager { | |||
} | |||
this.cdr.markForCheck(); | |||
}); | |||
this.notebookParagraphCodeEditorComponent.editorSettingTriggerAllowed = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should handle the case where this.notebookParagraphCodeEditorComponent
is undefined
...-web-angular/src/app/pages/workspace/notebook/paragraph/code-editor/code-editor.component.ts
Show resolved
Hide resolved
Behavior
Implementation
Reasoning
|
5e35dbf
to
648c6ef
Compare
I rebased due to conflicts caused by merged PRs. |
definedNote.paragraphs[paragraphIndex].focus = true; | ||
const addedParagraph = this.listOfNotebookParagraphComponent.find((_, index) => index === paragraphIndex) | ||
?.notebookParagraphCodeEditorComponent; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think something like this.cdr.detectChanges()
should be called, so that the paragraph component is added for the new paragraph in definedNote.paragraph
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Upon review, components using ChangeDetectionStrategy.OnPush
—as you mentioned—do not detect changes in the same reference array unless the reference itself is replaced. Since I am currently modifying the array with splice
(which does change the reference in place), Angular does not automatically recognize the change. Therefore, calling detectChanges
or markForCheck
is necessary.
The reason why Clone Paragraph or Add Paragraph (Insert Paragraph) in the current PR appeared to work correctly is due to Angular’s behavior of updating the view during events triggered within the component. Consequently, when executed via a button click, the focus handling works as expected, but when triggered via a shortcut key, the focus does not update properly.
To address this, after receiving PARAGRAPH_ADDED
and performing a splice
to update the array, we now explicitly call detectChanges
to force change detection, which has been confirmed to work correctly.
Even after handling it this way, the focus event did not work correctly when triggered via shortcut keys. To address this, in paragraph.component.ts
, I added a blur event on the current paragraph when executing cloneParagraph
and insertParagraph
.
I rebased again due to conflicts caused by merging the latest PR.
### What is this PR for? **Description:** Cursor behavior in the New UI’s paragraph needs to be fixed for several cursor related actions, including double-clicking, running all above/below, adding(clone), and removing paragraphs. When **cloneParagraph()** is called, it internally calls **addParagraph()**, which has already been tested. The same addParagraph-related code is also applied in #5044. If either PR is merged first, I will rebase accordingly. I have also confirmed that **cloneParagraph()** works correctly through #5044. Due to timing issues, I used `setTimeout` for **removeParagraph()** and **doubleClickParagraph()**. Since this is in the UI area, it likely won’t have major side effects, but I will look into it further. **Expected:** - When **doubleClickParagraph()** is executed, the cursor should move to the end of the paragraph. - When **runAllAbove()** or **runAllBelow()** is executed, the current cursor position should be remembered, and after execution, focus should return to the previous cursor position. - When **addParagraph()** is executed, the newly added paragraph should receive focus. - When **removeParagraph()** is executed, focus should move to the paragraph that takes the deleted paragraph’s place. **Actual (New UI):** - When **doubleClickParagraph()** is executed, the cursor moves to the beginning instead of the end. - After **runAllAbove()** or **runAllBelow()**, focus is lost completely. - When **addParagraph()** is executed, the new paragraph does not automatically receive focus. - After **removeParagraph()**, focus may not move to the correct paragraph. **[Appropriate action - Classic UI]** https://github.com/user-attachments/assets/fc0066f7-4e03-4e3b-9d5b-2f33df415ba7 Run all above -> Run all below -> Double click .md paragraph -> Add paragraph -> Delete paragraph **[AS-IS]** https://github.com/user-attachments/assets/f699f788-cf29-4c4c-8c47-2ef34d7962f0 Run all above -> Run all below -> Double click .md paragraph -> Add paragraph -> Delete paragraph **[TO-BE]** https://github.com/user-attachments/assets/1206c524-103f-4328-85ee-04408073b628 Run all above -> Run all below -> Double click .md paragraph -> Add paragraph -> Delete paragraph ### What type of PR is it? Bug Fix ### Todos ### What is the Jira issue? * [[ZEPPELIN-6298](https://issues.apache.org/jira/browse/ZEPPELIN-6298)] ### 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 #5057 from dididy/fix/ZEPPELIN-6298. Signed-off-by: ChanHo Lee <[email protected]>
### What is this PR for? **Description:** Cursor behavior in the New UI’s paragraph needs to be fixed for several cursor related actions, including double-clicking, running all above/below, adding(clone), and removing paragraphs. When **cloneParagraph()** is called, it internally calls **addParagraph()**, which has already been tested. The same addParagraph-related code is also applied in #5044. If either PR is merged first, I will rebase accordingly. I have also confirmed that **cloneParagraph()** works correctly through #5044. Due to timing issues, I used `setTimeout` for **removeParagraph()** and **doubleClickParagraph()**. Since this is in the UI area, it likely won’t have major side effects, but I will look into it further. **Expected:** - When **doubleClickParagraph()** is executed, the cursor should move to the end of the paragraph. - When **runAllAbove()** or **runAllBelow()** is executed, the current cursor position should be remembered, and after execution, focus should return to the previous cursor position. - When **addParagraph()** is executed, the newly added paragraph should receive focus. - When **removeParagraph()** is executed, focus should move to the paragraph that takes the deleted paragraph’s place. **Actual (New UI):** - When **doubleClickParagraph()** is executed, the cursor moves to the beginning instead of the end. - After **runAllAbove()** or **runAllBelow()**, focus is lost completely. - When **addParagraph()** is executed, the new paragraph does not automatically receive focus. - After **removeParagraph()**, focus may not move to the correct paragraph. **[Appropriate action - Classic UI]** https://github.com/user-attachments/assets/fc0066f7-4e03-4e3b-9d5b-2f33df415ba7 Run all above -> Run all below -> Double click .md paragraph -> Add paragraph -> Delete paragraph **[AS-IS]** https://github.com/user-attachments/assets/f699f788-cf29-4c4c-8c47-2ef34d7962f0 Run all above -> Run all below -> Double click .md paragraph -> Add paragraph -> Delete paragraph **[TO-BE]** https://github.com/user-attachments/assets/1206c524-103f-4328-85ee-04408073b628 Run all above -> Run all below -> Double click .md paragraph -> Add paragraph -> Delete paragraph ### What type of PR is it? Bug Fix ### Todos ### What is the Jira issue? * [[ZEPPELIN-6298](https://issues.apache.org/jira/browse/ZEPPELIN-6298)] ### 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 #5057 from dididy/fix/ZEPPELIN-6298. Signed-off-by: ChanHo Lee <[email protected]> (cherry picked from commit 4fbfaec) Signed-off-by: ChanHo Lee <[email protected]>
cecec31
to
5aab81a
Compare
… functions cloneParagraph, insterParagraph
…raph is not work properly The clone paragraph functionality wasn’t working correctly at the current position, so I moved it.
c496536
to
dcd95c3
Compare
What is this PR for?
Description:
Clicking the paragraph control’s Clone paragraph button that trigger COPY_PARAGRAPH. Upon receiving PARAGRAPH_ADDED, the Classic UI includes data.paragraph.text as expected, but the New UI receives an empty data.paragraph.text. Additionally, after cloning, the cursor is not positioned in the cloned paragraph in the New UI.
Expected:
Actual (New UI):
[Appropriate action - Classic UI]
2025-08-24.11.40.28.mov
[AS-IS]
2025-08-24.11.49.07.mov
[TO-BE]
2025-08-25.12.26.32.mov
What type of PR is it?
Bug Fix
Todos
What is the Jira issue?
How should this be tested?
Screenshots (if appropriate)
Questions: