@@ -330,39 +330,7 @@ export class HeaderFooterEditorManager extends EventEmitter {
330330 console . error ( '[HeaderFooterEditorManager] Editor initialization failed:' , error ) ;
331331 this . emit ( 'error' , { descriptor, error } ) ;
332332 } ) ;
333-
334- // Move editor container to the new editorHost if provided
335- // This is necessary because cached editors may have been appended elsewhere
336- if ( existing . container && options ?. editorHost ) {
337- // Only move if not already in the target host
338- if ( existing . container . parentElement !== options . editorHost ) {
339- options . editorHost . appendChild ( existing . container ) ;
340- }
341- }
342-
343- // Update editor options if provided
344- if ( existing . editor && options ) {
345- const updateOptions : Record < string , unknown > = { } ;
346- if ( options . currentPageNumber !== undefined ) {
347- updateOptions . currentPageNumber = options . currentPageNumber ;
348- }
349- if ( options . totalPageCount !== undefined ) {
350- updateOptions . totalPageCount = options . totalPageCount ;
351- }
352- if ( options . availableWidth !== undefined ) {
353- updateOptions . availableWidth = options . availableWidth ;
354- }
355- if ( options . availableHeight !== undefined ) {
356- updateOptions . availableHeight = options . availableHeight ;
357- }
358- if ( Object . keys ( updateOptions ) . length > 0 ) {
359- existing . editor . setOptions ( updateOptions ) ;
360- // Refresh page number display after option changes.
361- // NodeViews read editor.options but PM doesn't re-render them
362- // when only options change (no document transaction).
363- this . #refreshPageNumberDisplay( existing . editor ) ;
364- }
365- }
333+ this . #mountAndUpdateEntry( existing , options ) ;
366334
367335 return existing . editor ;
368336 }
@@ -380,7 +348,7 @@ export class HeaderFooterEditorManager extends EventEmitter {
380348 // Start creation and track the promise
381349 const creationPromise = ( async ( ) => {
382350 try {
383- const entry = await this . #createEditor ( descriptor , options ) ;
351+ const entry = this . #createEditorEntry ( descriptor , options ) ;
384352 if ( ! entry ) return null ;
385353
386354 this . #editorEntries. set ( descriptor . id , entry ) ;
@@ -406,6 +374,44 @@ export class HeaderFooterEditorManager extends EventEmitter {
406374 return creationPromise ;
407375 }
408376
377+ /**
378+ * Synchronously returns the cached editor for a descriptor, creating it on demand.
379+ *
380+ * Presentation-mode story activation needs a stable editor instance and DOM
381+ * target immediately so input can be forwarded into the hidden host without
382+ * waiting for the async `create` event. The normal lifecycle hooks still run
383+ * through the returned entry's `ready` promise.
384+ */
385+ ensureEditorSync (
386+ descriptor : HeaderFooterDescriptor ,
387+ options ?: {
388+ editorHost ?: HTMLElement ;
389+ availableWidth ?: number ;
390+ availableHeight ?: number ;
391+ currentPageNumber ?: number ;
392+ totalPageCount ?: number ;
393+ } ,
394+ ) : Editor | null {
395+ if ( ! descriptor ?. id ) return null ;
396+
397+ const existing = this . #editorEntries. get ( descriptor . id ) ;
398+ if ( existing ) {
399+ this . #cacheHits += 1 ;
400+ this . #updateAccessOrder( descriptor . id ) ;
401+ this . #mountAndUpdateEntry( existing , options ) ;
402+ return existing . editor ;
403+ }
404+
405+ const entry = this . #createEditorEntry( descriptor , options ) ;
406+ if ( ! entry ) return null ;
407+
408+ this . #cacheMisses += 1 ;
409+ this . #editorEntries. set ( descriptor . id , entry ) ;
410+ this . #updateAccessOrder( descriptor . id ) ;
411+ this . #enforceCacheSizeLimit( ) ;
412+ return entry . editor ;
413+ }
414+
409415 /**
410416 * Updates page number DOM elements to reflect current editor options.
411417 * Called after setOptions to sync NodeViews that read editor.options.
@@ -671,7 +677,7 @@ export class HeaderFooterEditorManager extends EventEmitter {
671677 this . #editorEntries. clear ( ) ;
672678 }
673679
674- async #createEditor (
680+ #createEditorEntry (
675681 descriptor : HeaderFooterDescriptor ,
676682 options ?: {
677683 editorHost ?: HTMLElement ;
@@ -680,7 +686,7 @@ export class HeaderFooterEditorManager extends EventEmitter {
680686 currentPageNumber ?: number ;
681687 totalPageCount ?: number ;
682688 } ,
683- ) : Promise < HeaderFooterEditorEntry | null > {
689+ ) : HeaderFooterEditorEntry | null {
684690 const json = this . getDocumentJson ( descriptor ) ;
685691 if ( ! json ) return null ;
686692
@@ -799,6 +805,45 @@ export class HeaderFooterEditorManager extends EventEmitter {
799805 } ;
800806 }
801807
808+ #mountAndUpdateEntry(
809+ entry : HeaderFooterEditorEntry ,
810+ options ?: {
811+ editorHost ?: HTMLElement ;
812+ availableWidth ?: number ;
813+ availableHeight ?: number ;
814+ currentPageNumber ?: number ;
815+ totalPageCount ?: number ;
816+ } ,
817+ ) : void {
818+ if ( entry . container && options ?. editorHost && entry . container . parentElement !== options . editorHost ) {
819+ options . editorHost . appendChild ( entry . container ) ;
820+ }
821+
822+ if ( ! options ) {
823+ return ;
824+ }
825+
826+ const updateOptions : Record < string , unknown > = { } ;
827+ if ( options . currentPageNumber !== undefined ) {
828+ updateOptions . currentPageNumber = options . currentPageNumber ;
829+ }
830+ if ( options . totalPageCount !== undefined ) {
831+ updateOptions . totalPageCount = options . totalPageCount ;
832+ }
833+ if ( options . availableWidth !== undefined ) {
834+ updateOptions . availableWidth = options . availableWidth ;
835+ }
836+ if ( options . availableHeight !== undefined ) {
837+ updateOptions . availableHeight = options . availableHeight ;
838+ }
839+ if ( Object . keys ( updateOptions ) . length > 0 ) {
840+ entry . editor . setOptions ( updateOptions ) ;
841+ // NodeViews that render PAGE / NUMPAGES read editor.options, so refresh
842+ // them when the presentation context changes without a document step.
843+ this . #refreshPageNumberDisplay( entry . editor ) ;
844+ }
845+ }
846+
802847 #createEditorContainer( ) : HTMLElement {
803848 const doc =
804849 ( this . #editor. options ?. element ?. ownerDocument as Document | undefined ) ?? globalThis . document ?? undefined ;
0 commit comments