fix(npcs): faster editor open, save progress, black screen crash

Warm and show the NPC window sooner, lazy-load ReactFlow/TipTap, overlay save progress like materials, and fix the undefined controlStyles crash after creating an NPC.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-07-30 08:50:05 +08:00
parent c7bf7cf449
commit e687303c57
10 changed files with 247 additions and 106 deletions
@@ -57,6 +57,7 @@ void test('createWindows: окно НПС закрывается с multi-window
assert.ok(src.includes('openNpcsWindow'));
assert.ok(src.includes('closeNpcsWindow'));
assert.ok(src.includes('openNpcsEditorWindow'));
assert.ok(src.includes('warmNpcsEditorWindow'));
assert.ok(src.includes("createWindow('npcs'"));
assert.ok(src.includes("createWindow('npcsEditor'"));
assert.match(src, /export function closeMultiWindow[\s\S]*closeNpcsWindow/);
+39 -16
View File
@@ -163,7 +163,7 @@ export function applyDockIconIfNeeded(): void {
type CreateWindowOpts = {
/** Дочернее окно (например пульт) держится над родителем (экран просмотра). */
parent?: BrowserWindow;
/** Только редактор: не показывать окно до `show()` (экран загрузки). */
/** Не показывать окно до явного `show()` (экран загрузки / прогрев НПС). */
deferVisibility?: boolean;
};
@@ -202,7 +202,7 @@ function windowSizeForKind(kind: WindowKind): { width: number; height: number }
}
function createWindow(kind: WindowKind, opts?: CreateWindowOpts): BrowserWindow {
const deferEditor = kind === 'editor' && opts?.deferVisibility === true;
const deferShow = opts?.deferVisibility === true;
const icon = loadBrandingWindowIcon();
const size = windowSizeForKind(kind);
const win = new BrowserWindow({
@@ -300,7 +300,7 @@ function createWindow(kind: WindowKind, opts?: CreateWindowOpts): BrowserWindow
safeConsoleError('[render-process-gone]', details.reason, details.exitCode);
});
if (!deferEditor) {
if (!deferShow) {
ensureWindowBecomesVisible(win);
}
loadWindowPage(win, kind);
@@ -527,19 +527,8 @@ export function openMaterialsWindow(): void {
});
}
/** Редактор НПС: отдельное окно с графом и инспектором. */
export function openNpcsEditorWindow(): void {
const existing = windows.get('npcsEditor');
if (existing && !existing.isDestroyed()) {
if (existing.isMinimized()) existing.restore();
existing.show();
existing.focus();
existing.moveTop();
return;
}
function positionNpcsEditorWindow(win: BrowserWindow): void {
const parent = windows.get('editor');
const win = createWindow('npcsEditor', parent ? { parent } : undefined);
const { width, height } = win.getBounds();
const display = screen.getDisplayMatching(parent?.getBounds() ?? win.getBounds());
const { x, y, width: dw, height: dh } = display.workArea;
@@ -549,9 +538,43 @@ export function openNpcsEditorWindow(): void {
width,
height,
});
}
/** Прогрев окна НПС в фоне после открытия проекта — клик «НПС» не ждёт холодной загрузки. */
export function warmNpcsEditorWindow(): void {
const existing = windows.get('npcsEditor');
if (existing && !existing.isDestroyed()) return;
const parent = windows.get('editor');
createWindow('npcsEditor', {
...(parent ? { parent } : {}),
deferVisibility: true,
});
}
/** Редактор НПС: отдельное окно с графом и инспектором. */
export function openNpcsEditorWindow(): void {
const existing = windows.get('npcsEditor');
if (existing && !existing.isDestroyed()) {
if (existing.isMinimized()) existing.restore();
positionNpcsEditorWindow(existing);
existing.show();
existing.focus();
existing.moveTop();
return;
}
const parent = windows.get('editor');
const win = createWindow('npcsEditor', {
...(parent ? { parent } : {}),
deferVisibility: true,
});
positionNpcsEditorWindow(win);
// Показываем сразу (тёмный фон), не дожидаясь полной загрузки React/ReactFlow.
win.show();
win.focus();
win.moveTop();
win.webContents.once('did-finish-load', () => {
if (!win.isDestroyed()) {
win.show();
win.focus();
win.moveTop();
}