namespace gdjs { /** * A minimal utility to define DOM elements. * Also copied in InGameEditor.tsx. */ function h( tag: K, attrs: { style?: Partial; onClick?: () => void; }, ...nodes: (HTMLElement | string)[] ): HTMLElement { const node = document.createElement(tag); Object.keys(attrs).forEach((key) => { if (key === 'style') { for (const [styleName, value] of Object.entries(attrs.style!)) { node.style[styleName] = value; } } else if (key === 'onClick') { node.addEventListener('click', attrs[key]!); } else { node.setAttribute(key, '' + attrs[key]); } }); node.append(...nodes); return node; } const styles: { errorContainer: Partial; errorTitle: Partial; errorMessage: Partial; stacktrace: Partial; closeButton: Partial; } = { errorContainer: { padding: '15px', backgroundColor: '#a70000cc', borderRadius: '8px', position: 'absolute', bottom: '5px', left: '5px', right: '5px', color: 'white', display: 'flex', flexDirection: 'column', pointerEvents: 'all', }, errorTitle: { fontFamily: 'system-ui', fontSize: '20px', marginTop: '5px', marginBottom: '5px', userSelect: 'all', }, errorMessage: { fontFamily: 'system-ui', fontSize: '14px', userSelect: 'all', }, stacktrace: { fontSize: '14px', fontFamily: 'monospace', whiteSpace: 'pre', maxHeight: '510px', overflow: 'auto', userSelect: 'all', }, closeButton: { padding: '3px', width: '20px', fontSize: '18px', position: 'absolute', right: '5px', top: '5px', color: 'white', border: '1px solid white', borderRadius: '6px', backgroundColor: 'transparent', }, }; /** * Displays uncaught exceptions on top of the game. * Could be reworked in the future to support a minimal debugger inside the game. */ export class InGameDebugger { _runtimeGame: RuntimeGame; _uncaughtException: Error | null = null; _uncaughtExceptionElement: HTMLElement | null = null; constructor(runtimeGame: RuntimeGame) { this._runtimeGame = runtimeGame; } setUncaughtException(exception: Error | null) { // Only show the first uncaught exception and ignore anything else. if (this._uncaughtException && !!exception) return; this._uncaughtException = exception; this.render(); } render() { const domElementContainer = this._runtimeGame .getRenderer() .getDomElementContainer(); if (!domElementContainer) return; if (this._uncaughtExceptionElement) { domElementContainer.removeChild(this._uncaughtExceptionElement); this._uncaughtExceptionElement = null; } if (this._uncaughtException) { const errorIsInJs = gdjs.AbstractDebuggerClient.isErrorComingFromJavaScriptCode( this._uncaughtException ); this._uncaughtExceptionElement = (

{errorIsInJs ? 'An error happened in a JavaScript code event.' : 'A crash or error happened in the game.'}

{(errorIsInJs ? 'This error comes from a JavaScript code event. Verify your code to ensure no error is happening. You can use the Developer Tools (menu "View" > "Toggle Developer Tools"). Full error is: ' : "If you're using JavaScript, verify your code. Otherwise, this might be an issue with GDevelop - consider reporting a bug. Full error is: ") + this._uncaughtException.message}

              {this._uncaughtException.stack || '(No stracktrace).'}
            
); if (this._uncaughtExceptionElement) domElementContainer.appendChild(this._uncaughtExceptionElement); } } } }