44 *--------------------------------------------------------------------------------------------*/
55
66import { Event } from '../../../../../base/common/event.js' ;
7- import { ISettableObservable , observableFromEvent , observableValue } from '../../../../../base/common/observable.js' ;
7+ import { ISettableObservable , observableValue } from '../../../../../base/common/observable.js' ;
88import { ITextModelService } from '../../../../../editor/common/services/resolverService.js' ;
99import { NotebookCellTextModel } from '../../../notebook/common/model/notebookCellTextModel.js' ;
1010import { CellKind } from '../../../notebook/common/notebookCommon.js' ;
@@ -15,12 +15,12 @@ import { IPositronNotebookCodeCell, NotebookCellOutputs } from './IPositronNoteb
1515import { IPositronWebviewPreloadService } from '../../../../services/positronWebviewPreloads/browser/positronWebviewPreloadService.js' ;
1616import { pickPreferredOutputItem } from './notebookOutputUtils.js' ;
1717import { getWebviewMessageType , isComplexHtml } from '../../../../services/positronIPyWidgets/common/webviewPreloadUtils.js' ;
18- import { INotebookExecutionStateService } from '../../../notebook/common/notebookExecutionStateService.js' ;
18+ import { INotebookExecutionStateService , NotebookExecutionType } from '../../../notebook/common/notebookExecutionStateService.js' ;
1919import { IPositronCellOutputViewModel } from '../IPositronNotebookEditor.js' ;
2020
2121export class PositronNotebookCodeCell extends PositronNotebookCellGeneral implements IPositronNotebookCodeCell {
2222 override kind : CellKind . Code = CellKind . Code ;
23- private readonly _outputs ;
23+ private readonly _outputs : ISettableObservable < NotebookCellOutputs [ ] > ;
2424
2525 // Output collapse state
2626 private readonly _outputIsCollapsed : ISettableObservable < boolean > ;
@@ -55,9 +55,61 @@ export class PositronNotebookCodeCell extends PositronNotebookCellGeneral implem
5555 undefined
5656 ) ;
5757
58- this . _outputs = observableFromEvent ( this , Event . any ( this . model . onDidChangeOutputs , this . model . onDidChangeOutputItems ) , ( ) => {
59- /** @description cellOutputs */
60- return this . parseCellOutputs ( ) ;
58+ // Initialize with current outputs. Uses observableValue (manually updated)
59+ // instead of observableFromEvent so we can defer output clearing during
60+ // execution to prevent a visual flash when re-running cells.
61+ this . _outputs = observableValue ( 'cellOutputs' , this . parseCellOutputs ( ) ) ;
62+
63+ // Debounce output clearing during cell execution to prevent visual flash.
64+ // When a cell is re-run, the runtime immediately clears outputs before
65+ // sending new ones. Without debouncing, the output container briefly
66+ // disappears and reappears, causing a distracting flash.
67+ let clearOutputsTimer : ReturnType < typeof setTimeout > | undefined ;
68+ this . _register ( Event . any ( this . model . onDidChangeOutputs , this . model . onDidChangeOutputItems ) ( ( ) => {
69+ const newOutputs = this . parseCellOutputs ( ) ;
70+
71+ // Cancel any pending deferred clear.
72+ if ( clearOutputsTimer !== undefined ) {
73+ clearTimeout ( clearOutputsTimer ) ;
74+ clearOutputsTimer = undefined ;
75+ }
76+
77+ if ( newOutputs . length > 0 ) {
78+ // New outputs available -- show immediately.
79+ this . _outputs . set ( newOutputs , undefined ) ;
80+ } else if ( _executionStateService . getCellExecution ( this . uri ) ) {
81+ // Outputs cleared during active execution (re-run). Defer the
82+ // visual clear so fast-completing executions never flash.
83+ clearOutputsTimer = setTimeout ( ( ) => {
84+ clearOutputsTimer = undefined ;
85+ this . _outputs . set ( this . parseCellOutputs ( ) , undefined ) ;
86+ } , 150 ) ;
87+ } else {
88+ // Outputs cleared outside execution (user action) -- immediately.
89+ this . _outputs . set ( newOutputs , undefined ) ;
90+ }
91+ } ) ) ;
92+
93+ // When execution ends, flush any deferred clear immediately.
94+ // Only re-parse when a timer is pending to avoid redundant calls to
95+ // parseCellOutputs() which has side effects (addNotebookOutput).
96+ this . _register ( _executionStateService . onDidChangeExecution ( e => {
97+ if ( e . type === NotebookExecutionType . cell && e . affectsCell ( this . model . uri ) && ! e . changed ) {
98+ if ( clearOutputsTimer !== undefined ) {
99+ clearTimeout ( clearOutputsTimer ) ;
100+ clearOutputsTimer = undefined ;
101+ this . _outputs . set ( this . parseCellOutputs ( ) , undefined ) ;
102+ }
103+ }
104+ } ) ) ;
105+
106+ // Clean up timer on dispose.
107+ this . _register ( {
108+ dispose : ( ) => {
109+ if ( clearOutputsTimer !== undefined ) {
110+ clearTimeout ( clearOutputsTimer ) ;
111+ }
112+ }
61113 } ) ;
62114
63115 // Reset collapse state when outputs are cleared so new outputs aren't born collapsed
0 commit comments