@@ -194,7 +194,104 @@ async function requestServer(
194194 ServerConnection . makeRequest ( url , init , settings )
195195 . then ( async response => {
196196 if ( ! response . ok ) {
197- promise . reject ( await ServerConnection . ResponseError . create ( response ) ) ;
197+ if ( response . status === 300 ) {
198+ let replyUrl = response . headers . get ( 'Location' ) || '' ;
199+
200+ if ( ! replyUrl . startsWith ( settings . baseUrl ) ) {
201+ replyUrl = URLExt . join ( settings . baseUrl , replyUrl ) ;
202+ }
203+ const { parent_header, input_request } = await response . json ( ) ;
204+ // TODO only the client sending the snippet will be prompted for the input
205+ // we can have a deadlock if its connection is lost.
206+ const panel = new Panel ( ) ;
207+ panel . addClass ( 'jp-OutputArea-child' ) ;
208+ panel . addClass ( 'jp-OutputArea-stdin-item' ) ;
209+
210+ const prompt = new OutputPrompt ( ) ;
211+ prompt . addClass ( 'jp-OutputArea-prompt' ) ;
212+ panel . addWidget ( prompt ) ;
213+
214+ const input = new Stdin ( {
215+ future : Object . freeze ( {
216+ sendInputReply : (
217+ content : KernelMessage . IInputReply ,
218+ parent_header : KernelMessage . IHeader < 'input_request' >
219+ ) => {
220+ ServerConnection . makeRequest (
221+ replyUrl ,
222+ {
223+ method : 'POST' ,
224+ body : JSON . stringify ( { input : content . value } )
225+ } ,
226+ settings
227+ ) . catch ( error => {
228+ console . error (
229+ `Failed to set input to ${ JSON . stringify ( content ) } .` ,
230+ error
231+ ) ;
232+ } ) ;
233+ }
234+ } ) as any ,
235+ parent_header,
236+ password : input_request . password ,
237+ prompt : input_request . prompt ,
238+ translator
239+ } ) ;
240+ input . addClass ( 'jp-OutputArea-output' ) ;
241+ panel . addWidget ( input ) ;
242+
243+ // Get the input node to ensure focus after updating the model upon user reply.
244+ const inputNode = input . node . getElementsByTagName ( 'input' ) [ 0 ] ;
245+
246+ void input . value . then ( value => {
247+ panel . addClass ( 'jp-OutputArea-stdin-hiding' ) ;
248+
249+ // FIXME this is not great as the model should not be modified on the client.
250+ // Use stdin as the stream so it does not get combined with stdout.
251+ // Note: because it modifies DOM it may (will) shift focus away from the input node.
252+ cell . outputArea . model . add ( {
253+ output_type : 'stream' ,
254+ name : 'stdin' ,
255+ text : value + '\n'
256+ } ) ;
257+ // Refocus the input node after it lost focus due to update of the model.
258+ inputNode . focus ( ) ;
259+
260+ // Keep the input in view for a little while; this (along refocusing)
261+ // ensures that we can avoid the cell editor stealing the focus, and
262+ // leading to user inadvertently modifying editor content when executing
263+ // consecutive commands in short succession.
264+ window . setTimeout ( async ( ) => {
265+ // Tack currently focused element to ensure that it remains on it
266+ // after disposal of the panel with the old input
267+ // (which modifies DOM and can lead to focus jump).
268+ const focusedElement = document . activeElement ;
269+ // Dispose the old panel with no longer needed input box.
270+ panel . dispose ( ) ;
271+ // Refocus the element that was focused before.
272+ if ( focusedElement && focusedElement instanceof HTMLElement ) {
273+ focusedElement . focus ( ) ;
274+ }
275+
276+ try {
277+ const response = await requestServer (
278+ cell ,
279+ url ,
280+ init ,
281+ settings ,
282+ translator
283+ ) ;
284+ promise . resolve ( response ) ;
285+ } catch ( error ) {
286+ promise . reject ( error ) ;
287+ }
288+ } , 500 ) ;
289+ } ) ;
290+
291+ cell . outputArea . layout . addWidget ( panel ) ;
292+ } else {
293+ promise . reject ( await ServerConnection . ResponseError . create ( response ) ) ;
294+ }
198295 } else if ( response . status === 202 ) {
199296 let redirectUrl = response . headers . get ( 'Location' ) || url ;
200297
@@ -234,98 +331,6 @@ async function requestServer(
234331 // Evanescent interval
235332 Math . min ( MAX_POLLING_INTERVAL , interval * 2 )
236333 ) ;
237- } else if ( response . status === 300 ) {
238- let replyUrl = response . headers . get ( 'Location' ) || '' ;
239-
240- if ( ! replyUrl . startsWith ( settings . baseUrl ) ) {
241- replyUrl = URLExt . join ( settings . baseUrl , replyUrl ) ;
242- }
243- const { parent_header, input_request } = await response . json ( ) ;
244- // TODO only the client sending the snippet will be prompted for the input
245- // we can have a deadlock if its connection is lost.
246- const panel = new Panel ( ) ;
247- panel . addClass ( 'jp-OutputArea-child' ) ;
248- panel . addClass ( 'jp-OutputArea-stdin-item' ) ;
249-
250- const prompt = new OutputPrompt ( ) ;
251- prompt . addClass ( 'jp-OutputArea-prompt' ) ;
252- panel . addWidget ( prompt ) ;
253-
254- const input = new Stdin ( {
255- future : Object . freeze ( {
256- sendInputReply : (
257- content : KernelMessage . IInputReply ,
258- parent_header : KernelMessage . IHeader < 'input_request' >
259- ) => {
260- ServerConnection . makeRequest (
261- replyUrl ,
262- { method : 'POST' } ,
263- settings
264- ) . catch ( error => {
265- console . error (
266- `Failed to set input to ${ JSON . stringify ( content ) } .` ,
267- error
268- ) ;
269- } ) ;
270- }
271- } ) as any ,
272- parent_header,
273- password : input_request . password ,
274- prompt : input_request . prompt ,
275- translator
276- } ) ;
277- input . addClass ( 'jp-OutputArea-output' ) ;
278- panel . addWidget ( input ) ;
279-
280- // Get the input node to ensure focus after updating the model upon user reply.
281- const inputNode = input . node . getElementsByTagName ( 'input' ) [ 0 ] ;
282-
283- void input . value . then ( value => {
284- panel . addClass ( 'jp-OutputArea-stdin-hiding' ) ;
285-
286- // FIXME this is not great as the model should not be modified on the client.
287- // Use stdin as the stream so it does not get combined with stdout.
288- // Note: because it modifies DOM it may (will) shift focus away from the input node.
289- cell . outputArea . model . add ( {
290- output_type : 'stream' ,
291- name : 'stdin' ,
292- text : value + '\n'
293- } ) ;
294- // Refocus the input node after it lost focus due to update of the model.
295- inputNode . focus ( ) ;
296-
297- // Keep the input in view for a little while; this (along refocusing)
298- // ensures that we can avoid the cell editor stealing the focus, and
299- // leading to user inadvertently modifying editor content when executing
300- // consecutive commands in short succession.
301- window . setTimeout ( async ( ) => {
302- // Tack currently focused element to ensure that it remains on it
303- // after disposal of the panel with the old input
304- // (which modifies DOM and can lead to focus jump).
305- const focusedElement = document . activeElement ;
306- // Dispose the old panel with no longer needed input box.
307- panel . dispose ( ) ;
308- // Refocus the element that was focused before.
309- if ( focusedElement && focusedElement instanceof HTMLElement ) {
310- focusedElement . focus ( ) ;
311- }
312-
313- try {
314- const response = await requestServer (
315- cell ,
316- url ,
317- init ,
318- settings ,
319- translator
320- ) ;
321- promise . resolve ( response ) ;
322- } catch ( error ) {
323- promise . reject ( error ) ;
324- }
325- } , 500 ) ;
326- } ) ;
327-
328- cell . outputArea . layout . addWidget ( panel ) ;
329334 } else {
330335 promise . resolve ( response ) ;
331336 }
0 commit comments