@@ -27,6 +27,8 @@ import { createEmptyEditorState } from './utils';
2727templateSettings . interpolate = / { { ( [ \S \s ] + ?) } } / g;
2828
2929export class Kernel extends EventEmitter implements IEditorKernel {
30+ // Global hot reload flag
31+ private static globalHotReloadMode : boolean | undefined = undefined ;
3032 private dataTypeMap : Map < string , DataSource > ;
3133 private plugins : Array < IEditorPluginConstructor < any > & { __config : any } > = [ ] ;
3234 private pluginsInstances : Array < IEditorPlugin < any > > = [ ] ;
@@ -47,7 +49,61 @@ export class Kernel extends EventEmitter implements IEditorKernel {
4749 super ( ) ;
4850 this . dataTypeMap = new Map < string , DataSource > ( ) ;
4951 // Enable hot reload mode in development
50- this . hotReloadMode = typeof process !== 'undefined' && process . env ?. NODE_ENV === 'development' ;
52+ this . hotReloadMode = this . detectDevelopmentMode ( ) ;
53+ }
54+
55+ private detectDevelopmentMode ( ) : boolean {
56+ // Check global override first
57+ if ( Kernel . globalHotReloadMode !== undefined ) {
58+ return Kernel . globalHotReloadMode ;
59+ }
60+
61+ // Multiple ways to detect development mode
62+ if ( typeof process !== 'undefined' && process . env ?. NODE_ENV === 'development' ) {
63+ return true ;
64+ }
65+
66+ // Check for common development indicators
67+ if ( typeof window !== 'undefined' ) {
68+ // Webpack HMR
69+ if ( ( window as any ) . webpackHotUpdate ) {
70+ return true ;
71+ }
72+ // Vite HMR
73+ if ( ( window as any ) . __vite_plugin_react_preamble_installed__ ) {
74+ return true ;
75+ }
76+ // Next.js development
77+ if ( ( window as any ) . __NEXT_DATA__ ?. buildId === 'development' ) {
78+ return true ;
79+ }
80+ }
81+
82+ // Check for localhost or development URLs
83+ if ( typeof window !== 'undefined' && window . location ) {
84+ const hostname = window . location . hostname ;
85+ if ( hostname === 'localhost' || hostname === '127.0.0.1' || hostname . endsWith ( '.local' ) ) {
86+ return true ;
87+ }
88+ }
89+
90+ // Default to development mode for better DX
91+ return true ;
92+ }
93+
94+ /**
95+ * Globally enable or disable hot reload mode for all kernel instances
96+ * @param enabled Whether to enable hot reload mode globally
97+ */
98+ static setGlobalHotReloadMode ( enabled : boolean ) : void {
99+ Kernel . globalHotReloadMode = enabled ;
100+ }
101+
102+ /**
103+ * Reset global hot reload mode to automatic detection
104+ */
105+ static resetGlobalHotReloadMode ( ) : void {
106+ Kernel . globalHotReloadMode = undefined ;
51107 }
52108
53109 getLexicalEditor ( ) : LexicalEditor | null {
@@ -72,10 +128,21 @@ export class Kernel extends EventEmitter implements IEditorKernel {
72128 }
73129
74130 setRootElement ( dom : HTMLElement ) {
75- for ( const plugin of this . plugins ) {
76- const instance = new plugin ( this , plugin . __config ) ;
77- this . pluginsInstances . push ( instance ) ;
131+ // Check if editor is already initialized to prevent re-initialization
132+ if ( this . editor ) {
133+ console . warn ( '[Editor] Editor is already initialized, updating root element only' ) ;
134+ this . editor . setRootElement ( dom ) ;
135+ return this . editor ;
78136 }
137+
138+ // Initialize plugins if not already done
139+ if ( this . pluginsInstances . length === 0 ) {
140+ for ( const plugin of this . plugins ) {
141+ const instance = new plugin ( this , plugin . __config ) ;
142+ this . pluginsInstances . push ( instance ) ;
143+ }
144+ }
145+
79146 const editor = ( this . editor = createEditor ( {
80147 // @ts -expect-error Inject into lexical editor instance
81148 __kernel : this ,
@@ -164,13 +231,29 @@ export class Kernel extends EventEmitter implements IEditorKernel {
164231 const index = this . plugins . findIndex ( ( p ) => p . pluginName === plugin . pluginName ) ;
165232 if ( index !== - 1 ) {
166233 this . plugins . splice ( index , 1 ) ;
234+ // Also remove corresponding plugin instance if it exists
235+ const instanceIndex = this . pluginsInstances . findIndex ( ( instance ) => {
236+ return ( instance . constructor as any ) . pluginName === plugin . pluginName ;
237+ } ) ;
238+ if ( instanceIndex !== - 1 ) {
239+ const oldInstance = this . pluginsInstances [ instanceIndex ] ;
240+ if ( oldInstance . destroy ) {
241+ oldInstance . destroy ( ) ;
242+ }
243+ this . pluginsInstances . splice ( instanceIndex , 1 ) ;
244+ }
167245 }
168246 } else {
169247 throw new Error (
170248 `Plugin with name "${ plugin . pluginName } " is already registered with a different implementation.` ,
171249 ) ;
172250 }
173251 } else {
252+ // Same plugin, just update config if provided
253+ if ( config !== undefined ) {
254+ // @ts -expect-error not error
255+ plugin . __config = config ;
256+ }
174257 return this ; // If plugin already exists, don't register again
175258 }
176259 }
@@ -197,15 +280,35 @@ export class Kernel extends EventEmitter implements IEditorKernel {
197280 }
198281
199282 registerService < T > ( serviceId : IServiceID < T > , service : T ) : void {
200- if ( this . serviceMap . has ( serviceId . __serviceId ) ) {
283+ const serviceIdString = serviceId . __serviceId ;
284+
285+ if ( this . serviceMap . has ( serviceIdString ) ) {
201286 if ( this . hotReloadMode ) {
202287 // In hot reload mode, allow service override with warning
203- console . warn ( `[Hot Reload] Overriding service with ID "${ serviceId . __serviceId } "` ) ;
288+ console . warn ( `[Hot Reload] Overriding service with ID "${ serviceIdString } "` ) ;
289+ this . serviceMap . set ( serviceIdString , service ) ;
290+ return ;
204291 } else {
205- throw new Error ( `Service with ID "${ serviceId . __serviceId } " is already registered.` ) ;
292+ // Check if it's the same service instance
293+ const existingService = this . serviceMap . get ( serviceIdString ) ;
294+ if ( existingService === service ) {
295+ // Same service instance, no need to re-register
296+ console . warn (
297+ `[Editor] Service "${ serviceIdString } " is already registered with the same instance` ,
298+ ) ;
299+ return ;
300+ }
301+
302+ // Different service instance in production mode
303+ console . error (
304+ `[Editor] Attempting to register duplicate service "${ serviceIdString } ". Enable hot reload mode if this is intended.` ,
305+ ) ;
306+ throw new Error ( `Service with ID "${ serviceIdString } " is already registered.` ) ;
206307 }
207308 }
208- this . serviceMap . set ( serviceId . __serviceId , service ) ;
309+
310+ this . serviceMap . set ( serviceIdString , service ) ;
311+ console . debug ( `[Editor] Registered service: ${ serviceIdString } ` ) ;
209312 }
210313
211314 /**
0 commit comments