Yicong-Huang commented on code in PR #6437:
URL: https://github.com/apache/texera/pull/6437#discussion_r3787837123
##########
frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.ts:
##########
@@ -1717,6 +1764,205 @@ export class WorkflowEditorComponent implements OnInit,
AfterViewInit, OnDestroy
return
this.operatorSummaries.get(operatorId)?.sampleRecords?.[0]?.["__is_visualization__"]
=== true;
}
+ /**
+ * Ambient operator recommender (apache/texera#5240). When the user drops an
+ * operator onto the canvas, ask the recommender for likely next operators
and
+ * float them as suggestion chips on the operator's output port; clicking one
+ * materializes it. The whole feature is opt-in and self-effacing: if it is
+ * disabled or the backend returns nothing, the canvas is untouched.
+ */
+ private handleNextOperatorSuggestions(): void {
+ if (!this.operatorRecommendationService.isEnabled()) {
+ return;
+ }
+
+ // Repositioning is throttled: change:position fires once per drag frame.
+ this.repositionNextOperatorSuggestion$
+ .pipe(auditTime(100), untilDestroyed(this))
+ .subscribe(() => this.repositionNextOperatorSuggestions());
+
+ // Every suggestion request — from a drop or from chaining after a click —
+ // goes through this one pipeline. switchMap unsubscribes the previous
+ // request, so a slow response can neither overwrite newer suggestions nor
+ // re-open the overlay after the user dismissed it; `null` means "cancel".
+ this.nextOperatorSuggestionRequest$
+ .pipe(
+ switchMap(operator =>
+ operator === null
+ ? of(null)
+ : this.operatorRecommendationService
+ .getRecommendations(operator)
+ .pipe(map(recommendations => ({ operator, recommendations })))
+ ),
+ untilDestroyed(this)
+ )
+ .subscribe(result => this.showNextOperatorSuggestions(result));
+
+ // Trigger: the user interactively dropped an operator onto the canvas.
+ // Deliberately not the graph's operator-add stream, which also fires on
+ // workflow load, undo/redo, paste, and remote co-editor edits — none of
+ // which are a user authoring a next step.
+ this.dragDropService.operatorDropStream
+ .pipe(untilDestroyed(this))
+ .subscribe(operator => this.requestNextOperatorSuggestionsFor(operator));
+
+ // Dismiss when the user clicks on blank canvas.
+ fromJointPaperEvent(this.paper, "blank:pointerdown")
+ .pipe(untilDestroyed(this))
+ .subscribe(() => this.closeNextOperatorSuggestions());
+
+ // Dismiss if the anchor operator is deleted out from under the
suggestions.
+ this.workflowActionService
+ .getTexeraGraph()
+ .getOperatorDeleteStream()
+ .pipe(untilDestroyed(this))
+ .subscribe(({ deletedOperatorID }) => {
+ if (this.nextOperatorSuggestion?.operatorId === deletedOperatorID) {
+ this.closeNextOperatorSuggestions();
+ }
+ });
+
+ // Keep the suggestions anchored to the operator's output port as it moves.
+ this.paper.model.on("change:position", (cell: joint.dia.Cell) => {
Review Comment:
Verified: 1844-1846 goes through the helper with `untilDestroyed(this)`, and
the result selector is right — `change:position` emits `(cell, position, opt)`,
so without it `fromEventPattern` would hand you the arg array.
Follow-up for 501 and 1678, not here. Both are in `main` and neither is what
this PR is about; converting them would put an unrelated lifecycle fix inside a
feature diff that is already five rounds deep.
One note on the helper's signature, left as a separate comment on
`joint-ui.service.ts`.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]