fix: unblock startup on network and external font
- Make license status snapshot non-blocking (revocation check in background) - Speed boot by not awaiting license network and capping editor ready wait - Stop disabling GPU by default on Win packaged builds - Remove external font fetch; bundle local Inter Made-with: Cursor
This commit is contained in:
+13
-10
@@ -28,10 +28,10 @@ import {
|
||||
} from './windows/createWindows';
|
||||
|
||||
/**
|
||||
* На части конфигураций Windows окно Electron с `file://` остаётся чёрным из‑за GPU/композитора.
|
||||
* Отключаем аппаратное ускорение в упакованном приложении; отключить обход: `DND_DISABLE_GPU=0`.
|
||||
* Отключение GPU ломает скорость вторичных окон (презентация/пульт — WebGL). По умолчанию не трогаем.
|
||||
* При чёрном экране в упакованной сборке: `DND_DISABLE_GPU=1`.
|
||||
*/
|
||||
if (process.platform === 'win32' && app.isPackaged && process.env.DND_DISABLE_GPU !== '0') {
|
||||
if (process.platform === 'win32' && app.isPackaged && process.env.DND_DISABLE_GPU === '1') {
|
||||
app.disableHardwareAcceleration();
|
||||
}
|
||||
|
||||
@@ -144,17 +144,20 @@ async function runStartupAfterHandlers(licenseService: LicenseService): Promise<
|
||||
console.error('[boot] ensureRoots', e);
|
||||
}
|
||||
|
||||
setBootWindowStatus(splash, 'Устанавливаем связь…');
|
||||
setBootWindowStatus(splash, 'Проверка лицензии…');
|
||||
try {
|
||||
await licenseService.getStatus();
|
||||
} catch (e) {
|
||||
console.error('[boot] license getStatus', e);
|
||||
}
|
||||
// Сеть в `getStatus()` не блокируем старт: синхронный снимок, отзыв — в фоне.
|
||||
licenseService.getStatusSync();
|
||||
queueMicrotask(() => {
|
||||
licenseService.getStatus();
|
||||
});
|
||||
|
||||
setBootWindowStatus(splash, 'Загрузка редактора…');
|
||||
const editor = createEditorWindowDeferred();
|
||||
await waitForEditorWindowReady(editor);
|
||||
const bootEditorMs = 2000;
|
||||
await Promise.race([
|
||||
waitForEditorWindowReady(editor),
|
||||
new Promise<void>((resolve) => setTimeout(resolve, bootEditorMs)),
|
||||
]);
|
||||
setBootWindowStatus(splash, 'Готово');
|
||||
destroyBootWindow(splash);
|
||||
if (!editor.isDestroyed()) {
|
||||
|
||||
@@ -113,12 +113,17 @@ export class LicenseService {
|
||||
return normalizeLicenseTokenInput(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Онлайн-проверка отзыва. Не вызывать через `await` из UI-пути: без VPN/DNS до сервера
|
||||
* лицензий TCP может висеть до таймаута (см. fetch), из‑за чего главное окно долго «чёрное».
|
||||
*/
|
||||
private async maybeRefreshRemoteRevocation(payload: LicensePayloadV1): Promise<void> {
|
||||
const base = process.env.DND_LICENSE_STATUS_URL?.trim();
|
||||
if (!base) return;
|
||||
const now = Date.now();
|
||||
if (now - this.lastRemoteRevokeCheckMs < 60_000) return;
|
||||
this.lastRemoteRevokeCheckMs = now;
|
||||
const wasRevoked = this.lastRemoteRevoked;
|
||||
try {
|
||||
const u = new URL('v1/status', base.endsWith('/') ? base : `${base}/`);
|
||||
u.searchParams.set('sub', payload.sub);
|
||||
@@ -129,6 +134,9 @@ export class LicenseService {
|
||||
} catch {
|
||||
/* offline: не блокируем */
|
||||
}
|
||||
if (wasRevoked !== this.lastRemoteRevoked) {
|
||||
emitLicenseStatusChanged();
|
||||
}
|
||||
}
|
||||
|
||||
getStatusSync(): LicenseSnapshot {
|
||||
@@ -212,7 +220,8 @@ export class LicenseService {
|
||||
};
|
||||
}
|
||||
|
||||
async getStatus(): Promise<LicenseSnapshot> {
|
||||
/** Снимок для UI/IPC: без ожидания сети (проверка отзыва уходит в фон). */
|
||||
getStatus(): LicenseSnapshot {
|
||||
if (this.isSkipLicense()) return this.getStatusSync();
|
||||
const base = this.getStatusSync();
|
||||
if (!base.active || !base.summary) return base;
|
||||
@@ -223,7 +232,7 @@ export class LicenseService {
|
||||
deviceId: this.deviceId,
|
||||
});
|
||||
if (!v.ok) return this.getStatusSync();
|
||||
await this.maybeRefreshRemoteRevocation(v.payload);
|
||||
void this.maybeRefreshRemoteRevocation(v.payload);
|
||||
return this.getStatusSync();
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
|
||||
/* --- Типографика --- */
|
||||
--font:
|
||||
'Nimbus Sans', 'Nimbus Sans L', 'Nimbus Sans OT', 'Nimbus Sans PS', ui-sans-serif, system-ui,
|
||||
Inter, 'Nimbus Sans', 'Nimbus Sans L', 'Nimbus Sans OT', 'Nimbus Sans PS', ui-sans-serif, system-ui,
|
||||
-apple-system, 'Segoe UI', Roboto, Helvetica, Arial, 'Apple Color Emoji', 'Segoe UI Emoji';
|
||||
--text-xs: 12px;
|
||||
--text-sm: 13px;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@import '../styles/variables.css';
|
||||
@import url('https://fonts.cdnfonts.com/css/nimbus-sans');
|
||||
@import '@fontsource/inter/latin.css';
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import test from 'node:test';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..', '..');
|
||||
|
||||
/** Регресс: `await` сетевой проверки отзыва блокировал IPC `license.getStatus` до 8 с без доступа к серверу. */
|
||||
void test('licenseService: getStatus не ждёт await проверки отзыва', () => {
|
||||
const src = fs.readFileSync(path.join(root, 'app', 'main', 'license', 'licenseService.ts'), 'utf8');
|
||||
assert.match(src, /void this\.maybeRefreshRemoteRevocation\(/);
|
||||
assert.doesNotMatch(src, /await this\.maybeRefreshRemoteRevocation\(/);
|
||||
});
|
||||
Generated
+12
-3
@@ -1,14 +1,15 @@
|
||||
{
|
||||
"name": "dnd_player",
|
||||
"name": "DndGamePlayer",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "dnd_player",
|
||||
"name": "DndGamePlayer",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@fontsource/inter": "^5.2.8",
|
||||
"pixi.js": "^8.18.1",
|
||||
"react": "^19.2.5",
|
||||
"react-dom": "^19.2.5",
|
||||
@@ -1410,6 +1411,15 @@
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@fontsource/inter": {
|
||||
"version": "5.2.8",
|
||||
"resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-5.2.8.tgz",
|
||||
"integrity": "sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg==",
|
||||
"license": "OFL-1.1",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ayuhito"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanfs/core": {
|
||||
"version": "0.19.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
||||
@@ -10144,7 +10154,6 @@
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
|
||||
+2
-1
@@ -10,7 +10,7 @@
|
||||
"build:obfuscate": "node scripts/build.mjs --production --obfuscate",
|
||||
"lint": "eslint . --max-warnings 0",
|
||||
"typecheck": "tsc -p tsconfig.eslint.json --noEmit",
|
||||
"test": "tsx --test app/renderer/shared/ui/controls.tooltip.test.ts app/shared/ipc/contracts.mediaRemoval.test.ts app/shared/effectEraserHitTest.test.ts app/renderer/control/controlApp.effectsPanel.test.ts app/renderer/shared/effects/PxiEffectsOverlay.pointer.test.ts app/main/windows/createWindows.editorClose.test.ts app/main/windows/bootWindow.test.ts app/main/effects/effectsStore.test.ts app/main/project/assetPrune.test.ts app/main/project/zipRead.test.ts app/shared/package.build.test.ts app/shared/license/canonicalJson.test.ts app/shared/license/productKey.test.ts app/main/license/verifyLicenseToken.test.ts && node --test scripts/build-env.test.mjs scripts/obfuscate-main.test.mjs",
|
||||
"test": "tsx --test app/renderer/shared/ui/controls.tooltip.test.ts app/shared/ipc/contracts.mediaRemoval.test.ts app/shared/effectEraserHitTest.test.ts app/renderer/control/controlApp.effectsPanel.test.ts app/renderer/shared/effects/PxiEffectsOverlay.pointer.test.ts app/main/windows/createWindows.editorClose.test.ts app/main/windows/bootWindow.test.ts app/main/effects/effectsStore.test.ts app/main/project/assetPrune.test.ts app/main/project/zipRead.test.ts app/shared/package.build.test.ts app/shared/license/canonicalJson.test.ts app/shared/license/productKey.test.ts app/shared/license/licenseService.networkRegression.test.ts app/main/license/verifyLicenseToken.test.ts && node --test scripts/build-env.test.mjs scripts/obfuscate-main.test.mjs",
|
||||
"format": "prettier . --check",
|
||||
"format:write": "prettier . --write",
|
||||
"release:info": "node scripts/print-release-info.mjs",
|
||||
@@ -24,6 +24,7 @@
|
||||
"license": "ISC",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@fontsource/inter": "^5.2.8",
|
||||
"pixi.js": "^8.18.1",
|
||||
"react": "^19.2.5",
|
||||
"react-dom": "^19.2.5",
|
||||
|
||||
Reference in New Issue
Block a user