(error?: Error | number | null)
| 1453 | reconciler.flushSyncWork(); |
| 1454 | } |
| 1455 | unmount(error?: Error | number | null): void { |
| 1456 | if (this.isUnmounted) { |
| 1457 | return; |
| 1458 | } |
| 1459 | this.onRender(); |
| 1460 | this.unsubscribeExit(); |
| 1461 | if (typeof this.restoreConsole === 'function') { |
| 1462 | this.restoreConsole(); |
| 1463 | } |
| 1464 | this.restoreStderr?.(); |
| 1465 | this.unsubscribeTTYHandlers?.(); |
| 1466 | |
| 1467 | // Non-TTY environments don't handle erasing ansi escapes well, so it's better to |
| 1468 | // only render last frame of non-static output |
| 1469 | const diff = this.log.renderPreviousOutput_DEPRECATED(this.frontFrame); |
| 1470 | writeDiffToTerminal(this.terminal, optimize(diff)); |
| 1471 | |
| 1472 | // Clean up terminal modes synchronously before process exit. |
| 1473 | // React's componentWillUnmount won't run in time when process.exit() is called, |
| 1474 | // so we must reset terminal modes here to prevent escape sequence leakage. |
| 1475 | // Use writeSync to stdout (fd 1) to ensure writes complete before exit. |
| 1476 | // We unconditionally send all disable sequences because terminal detection |
| 1477 | // may not work correctly (e.g., in tmux, screen) and these are no-ops on |
| 1478 | // terminals that don't support them. |
| 1479 | /* eslint-disable custom-rules/no-sync-fs -- process exiting; async writes would be dropped */ |
| 1480 | if (this.options.stdout.isTTY) { |
| 1481 | if (this.altScreenActive) { |
| 1482 | // <AlternateScreen>'s unmount effect won't run during signal-exit. |
| 1483 | // Exit alt screen FIRST so other cleanup sequences go to the main screen. |
| 1484 | writeSync(1, EXIT_ALT_SCREEN); |
| 1485 | } |
| 1486 | // Disable mouse tracking — unconditional because altScreenActive can be |
| 1487 | // stale if AlternateScreen's unmount (which flips the flag) raced a |
| 1488 | // blocked event loop + SIGINT. No-op if tracking was never enabled. |
| 1489 | writeSync(1, DISABLE_MOUSE_TRACKING); |
| 1490 | // Drain stdin so in-flight mouse events don't leak to the shell |
| 1491 | this.drainStdin(); |
| 1492 | // Disable extended key reporting (both kitty and modifyOtherKeys) |
| 1493 | writeSync(1, DISABLE_MODIFY_OTHER_KEYS); |
| 1494 | writeSync(1, DISABLE_KITTY_KEYBOARD); |
| 1495 | // Disable focus events (DECSET 1004) |
| 1496 | writeSync(1, DFE); |
| 1497 | // Disable bracketed paste mode |
| 1498 | writeSync(1, DBP); |
| 1499 | // Show cursor |
| 1500 | writeSync(1, SHOW_CURSOR); |
| 1501 | // Clear iTerm2 progress bar |
| 1502 | writeSync(1, CLEAR_ITERM2_PROGRESS); |
| 1503 | // Clear tab status (OSC 21337) so a stale dot doesn't linger |
| 1504 | if (supportsTabStatus()) writeSync(1, wrapForMultiplexer(CLEAR_TAB_STATUS)); |
| 1505 | } |
| 1506 | /* eslint-enable custom-rules/no-sync-fs */ |
| 1507 | |
| 1508 | this.isUnmounted = true; |
| 1509 | |
| 1510 | // Cancel any pending throttled renders to prevent accessing freed Yoga nodes |
| 1511 | this.scheduleRender.cancel?.(); |
| 1512 | if (this.drainTimer !== null) { |
no test coverage detected