feat: кубики/таймер/кисть/спонсоры и задел игровых систем
Показ анимации кубиков игрокам, закрытие оверлеев с окнами, ручной ввод количества, меньший радиус кисти, блок спонсоров в «О программе»; плюс черновик game systems / character sheet. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import { app, BrowserWindow, screen } from 'electron';
|
||||
import { app, BrowserWindow, screen, shell } from 'electron';
|
||||
|
||||
import type { PresentationSettings } from '../../shared/types/presentationSettings';
|
||||
import { readPresentationSettings } from '../preferences/appPreferences';
|
||||
@@ -66,13 +66,27 @@ const PLAYLISTS_WINDOW_HEIGHT = 800;
|
||||
const TIMER_WINDOW_WIDTH = 360;
|
||||
const TIMER_WINDOW_HEIGHT = 420;
|
||||
|
||||
/** Колбэк при закрытии окна таймера (Esc / крестик / programmatic). */
|
||||
let timerWindowClosedListener: (() => void) | null = null;
|
||||
|
||||
export function setTimerWindowClosedListener(listener: (() => void) | null): void {
|
||||
timerWindowClosedListener = listener;
|
||||
}
|
||||
|
||||
/** Окно трекера инициативы. */
|
||||
const INITIATIVE_WINDOW_WIDTH = 920;
|
||||
const INITIATIVE_WINDOW_HEIGHT = 420;
|
||||
|
||||
/** Компактное окно броска кубиков. */
|
||||
const DICE_WINDOW_WIDTH = 380;
|
||||
const DICE_WINDOW_HEIGHT = 420;
|
||||
const DICE_WINDOW_HEIGHT = 460;
|
||||
|
||||
/** Колбэк при закрытии окна кубиков (Esc / крестик / programmatic). */
|
||||
let diceWindowClosedListener: (() => void) | null = null;
|
||||
|
||||
export function setDiceWindowClosedListener(listener: (() => void) | null): void {
|
||||
diceWindowClosedListener = listener;
|
||||
}
|
||||
|
||||
/** Редактор НПС — как основной редактор, шире инспектор. */
|
||||
const NPCS_EDITOR_WINDOW_WIDTH = 1400;
|
||||
@@ -452,6 +466,17 @@ function createWindow(kind: WindowKind, opts?: CreateWindowOpts): BrowserWindow
|
||||
win.webContents.on('preload-error', (_event, preloadPath, error) => {
|
||||
safeConsoleError(`[preload-error] ${preloadPath}:`, error);
|
||||
});
|
||||
win.webContents.setWindowOpenHandler(({ url }) => {
|
||||
try {
|
||||
const parsed = new URL(url);
|
||||
if (parsed.protocol === 'http:' || parsed.protocol === 'https:') {
|
||||
void shell.openExternal(parsed.toString());
|
||||
}
|
||||
} catch {
|
||||
/* ignore invalid urls */
|
||||
}
|
||||
return { action: 'deny' };
|
||||
});
|
||||
win.webContents.on('did-fail-load', (_event, errorCode, errorDescription, validatedURL, isMainFrame) => {
|
||||
// -3 ERR_ABORTED: частый артефакт при navigate/maximize/закрытии — не шумим.
|
||||
if (errorCode === -3) return;
|
||||
@@ -491,6 +516,16 @@ function createWindow(kind: WindowKind, opts?: CreateWindowOpts): BrowserWindow
|
||||
});
|
||||
}
|
||||
win.on('closed', () => windows.delete(kind));
|
||||
if (kind === 'timer') {
|
||||
win.on('closed', () => {
|
||||
timerWindowClosedListener?.();
|
||||
});
|
||||
}
|
||||
if (kind === 'dice') {
|
||||
win.on('closed', () => {
|
||||
diceWindowClosedListener?.();
|
||||
});
|
||||
}
|
||||
if (kind === 'sceneEditor') {
|
||||
win.on('closed', () => {
|
||||
closeTokenPathEditorWindow();
|
||||
|
||||
Reference in New Issue
Block a user