Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a15adfc3b1 | |||
| 82baef2b04 | |||
| e4b989c60f |
@@ -67,6 +67,19 @@ jobs:
|
|||||||
|
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
|
|
||||||
|
# Не используем `npm install`: на Linux npm падает с EBADPLATFORM для win32-пакета.
|
||||||
|
- name: sharp (@img/sharp-win32-x64) для Windows-артефакта при сборке на Linux
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
tmp="$(mktemp -d)"
|
||||||
|
npm pack @img/sharp-win32-x64@0.34.5 --pack-destination "$tmp"
|
||||||
|
mkdir -p node_modules/@img/sharp-win32-x64
|
||||||
|
tar -xzf "$tmp/img-sharp-win32-x64-0.34.5.tgz" -C "$tmp"
|
||||||
|
cp -a "$tmp/package/." node_modules/@img/sharp-win32-x64/
|
||||||
|
test -f node_modules/@img/sharp-win32-x64/lib/sharp-win32-x64.node
|
||||||
|
rm -rf "$tmp"
|
||||||
|
|
||||||
- run: npm run build
|
- run: npm run build
|
||||||
|
|
||||||
- name: electron-builder (win)
|
- name: electron-builder (win)
|
||||||
|
|||||||
+2
-1
@@ -252,6 +252,7 @@ async function main() {
|
|||||||
registerHandler(ipcChannels.app.getVersion, () => ({
|
registerHandler(ipcChannels.app.getVersion, () => ({
|
||||||
version: getAppSemanticVersion(),
|
version: getAppSemanticVersion(),
|
||||||
buildNumber: getOptionalBuildNumber(),
|
buildNumber: getOptionalBuildNumber(),
|
||||||
|
packaged: app.isPackaged,
|
||||||
}));
|
}));
|
||||||
registerHandler(ipcChannels.license.getStatus, () => licenseService.getStatus());
|
registerHandler(ipcChannels.license.getStatus, () => licenseService.getStatus());
|
||||||
registerHandler(ipcChannels.license.setToken, async ({ token }) => licenseService.setToken(token));
|
registerHandler(ipcChannels.license.setToken, async ({ token }) => licenseService.setToken(token));
|
||||||
@@ -526,9 +527,9 @@ async function main() {
|
|||||||
return { ok: true };
|
return { ok: true };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
installAutoUpdater(licenseService, registerHandler);
|
||||||
installIpcRouter();
|
installIpcRouter();
|
||||||
applyDockIconIfNeeded();
|
applyDockIconIfNeeded();
|
||||||
installAutoUpdater(licenseService);
|
|
||||||
await runStartupAfterHandlers(licenseService);
|
await runStartupAfterHandlers(licenseService);
|
||||||
|
|
||||||
app.on('activate', () => {
|
app.on('activate', () => {
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ export function registerHandler<K extends keyof IpcInvokeMap>(channel: K, handle
|
|||||||
handlers.set(channelStr, inner);
|
handlers.set(channelStr, inner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type IpcRegisterHandler = typeof registerHandler;
|
||||||
|
|
||||||
export function installIpcRouter(): void {
|
export function installIpcRouter(): void {
|
||||||
for (const [channel, handler] of handlers.entries()) {
|
for (const [channel, handler] of handlers.entries()) {
|
||||||
ipcMain.handle(channel, async (_event, payload: unknown) => handler(payload));
|
ipcMain.handle(channel, async (_event, payload: unknown) => handler(payload));
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
import { app, dialog } from 'electron';
|
import { app, dialog } from 'electron';
|
||||||
import { autoUpdater } from 'electron-updater';
|
import { autoUpdater } from 'electron-updater';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ipcChannels,
|
||||||
|
type UpdaterCheckResponse,
|
||||||
|
type UpdaterDownloadResponse,
|
||||||
|
} from '../../shared/ipc/contracts';
|
||||||
|
import type { IpcRegisterHandler } from '../ipc/router';
|
||||||
import { addLicenseChangeListener } from '../license/licenseService';
|
import { addLicenseChangeListener } from '../license/licenseService';
|
||||||
import type { LicenseService } from '../license/licenseService';
|
import type { LicenseService } from '../license/licenseService';
|
||||||
|
|
||||||
@@ -9,6 +15,10 @@ const STARTUP_CHECK_DELAY_MS = 12_000;
|
|||||||
const RE_CHECK_COOLDOWN_MS = 30_000;
|
const RE_CHECK_COOLDOWN_MS = 30_000;
|
||||||
|
|
||||||
let lastCheckAt = 0;
|
let lastCheckAt = 0;
|
||||||
|
/** Ручная установка: не показывать второй диалог из `update-downloaded`. */
|
||||||
|
let suppressAutoInstallDialog = false;
|
||||||
|
|
||||||
|
type RegisterFn = IpcRegisterHandler;
|
||||||
|
|
||||||
function isLicensedForUpdates(licenseService: LicenseService): boolean {
|
function isLicensedForUpdates(licenseService: LicenseService): boolean {
|
||||||
const snap = licenseService.getStatusSync();
|
const snap = licenseService.getStatusSync();
|
||||||
@@ -24,11 +34,53 @@ function maybeCheckForUpdates(licenseService: LicenseService, ignoreCooldown: bo
|
|||||||
void autoUpdater.checkForUpdates().catch(() => undefined);
|
void autoUpdater.checkForUpdates().catch(() => undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function runManualUpdaterCheck(licenseService: LicenseService): Promise<UpdaterCheckResponse> {
|
||||||
|
if (!app.isPackaged) {
|
||||||
|
return { outcome: 'not_packaged' };
|
||||||
|
}
|
||||||
|
if (!isLicensedForUpdates(licenseService)) {
|
||||||
|
return { outcome: 'no_license' };
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const result = await autoUpdater.checkForUpdates();
|
||||||
|
if (result && result.isUpdateAvailable && result.updateInfo.version) {
|
||||||
|
return { outcome: 'available', version: result.updateInfo.version };
|
||||||
|
}
|
||||||
|
return { outcome: 'current', currentVersion: app.getVersion() };
|
||||||
|
} catch (e) {
|
||||||
|
const message = e instanceof Error ? e.message : String(e);
|
||||||
|
return { outcome: 'error', message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runManualDownloadAndRestart(): Promise<UpdaterDownloadResponse> {
|
||||||
|
if (!app.isPackaged) {
|
||||||
|
return { ok: false, message: 'NOT_PACKAGED' };
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
suppressAutoInstallDialog = true;
|
||||||
|
await autoUpdater.downloadUpdate();
|
||||||
|
autoUpdater.quitAndInstall(false, true);
|
||||||
|
return { ok: true };
|
||||||
|
} catch (e) {
|
||||||
|
suppressAutoInstallDialog = false;
|
||||||
|
const message = e instanceof Error ? e.message : String(e);
|
||||||
|
return { ok: false, message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerUpdaterHandlers(register: RegisterFn, licenseService: LicenseService): void {
|
||||||
|
register(ipcChannels.updater.check, () => runManualUpdaterCheck(licenseService));
|
||||||
|
register(ipcChannels.updater.downloadAndRestart, () => runManualDownloadAndRestart());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Проверка обновлений: только упакованное приложение, только при активной лицензии.
|
* Проверка обновлений: только упакованное приложение, только при активной лицензии.
|
||||||
* Канал и URL задаются при сборке (`publish` → `app-update.yml` внутри установки).
|
* Канал и URL задаются при сборке (`publish` → `app-update.yml` внутри установки).
|
||||||
*/
|
*/
|
||||||
export function installAutoUpdater(licenseService: LicenseService): void {
|
export function installAutoUpdater(licenseService: LicenseService, register: RegisterFn): void {
|
||||||
|
registerUpdaterHandlers(register, licenseService);
|
||||||
|
|
||||||
if (!app.isPackaged) return;
|
if (!app.isPackaged) return;
|
||||||
|
|
||||||
const feedOverride = process.env.DND_UPDATE_FEED_URL?.trim();
|
const feedOverride = process.env.DND_UPDATE_FEED_URL?.trim();
|
||||||
@@ -41,6 +93,10 @@ export function installAutoUpdater(licenseService: LicenseService): void {
|
|||||||
autoUpdater.autoInstallOnAppQuit = true;
|
autoUpdater.autoInstallOnAppQuit = true;
|
||||||
|
|
||||||
autoUpdater.on('update-downloaded', (info) => {
|
autoUpdater.on('update-downloaded', (info) => {
|
||||||
|
if (suppressAutoInstallDialog) {
|
||||||
|
suppressAutoInstallDialog = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
void dialog
|
void dialog
|
||||||
.showMessageBox({
|
.showMessageBox({
|
||||||
type: 'info',
|
type: 'info',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import React, { startTransition, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
import { ipcChannels } from '../../shared/ipc/contracts';
|
import { ipcChannels, type UpdaterCheckResponse } from '../../shared/ipc/contracts';
|
||||||
import { EULA_CURRENT_VERSION } from '../../shared/license/eulaVersion';
|
import { EULA_CURRENT_VERSION } from '../../shared/license/eulaVersion';
|
||||||
import type { LicenseSnapshot } from '../../shared/license/licenseSnapshot';
|
import type { LicenseSnapshot } from '../../shared/license/licenseSnapshot';
|
||||||
import type { AssetId, MediaAsset, Project, ProjectId, SceneAudioRef, SceneId } from '../../shared/types';
|
import type { AssetId, MediaAsset, Project, ProjectId, SceneAudioRef, SceneId } from '../../shared/types';
|
||||||
@@ -76,6 +76,8 @@ export function EditorApp() {
|
|||||||
const [previewBusy, setPreviewBusy] = useState(false);
|
const [previewBusy, setPreviewBusy] = useState(false);
|
||||||
const [presentationOpen, setPresentationOpen] = useState(false);
|
const [presentationOpen, setPresentationOpen] = useState(false);
|
||||||
const [licenseSnap, setLicenseSnap] = useState<LicenseSnapshot | null>(null);
|
const [licenseSnap, setLicenseSnap] = useState<LicenseSnapshot | null>(null);
|
||||||
|
const [checkUpdatesOpen, setCheckUpdatesOpen] = useState(false);
|
||||||
|
const [appPackaged, setAppPackaged] = useState(false);
|
||||||
const [licenseKeyModalOpen, setLicenseKeyModalOpen] = useState(false);
|
const [licenseKeyModalOpen, setLicenseKeyModalOpen] = useState(false);
|
||||||
const [eulaModalOpen, setEulaModalOpen] = useState(false);
|
const [eulaModalOpen, setEulaModalOpen] = useState(false);
|
||||||
const [aboutLicenseOpen, setAboutLicenseOpen] = useState(false);
|
const [aboutLicenseOpen, setAboutLicenseOpen] = useState(false);
|
||||||
@@ -305,6 +307,7 @@ export function EditorApp() {
|
|||||||
const r = await getDndApi().invoke(ipcChannels.app.getVersion, {});
|
const r = await getDndApi().invoke(ipcChannels.app.getVersion, {});
|
||||||
const label = r.buildNumber ? `v${r.version} · ${r.buildNumber}` : `v${r.version}`;
|
const label = r.buildNumber ? `v${r.version} · ${r.buildNumber}` : `v${r.version}`;
|
||||||
setAppVersionText(label);
|
setAppVersionText(label);
|
||||||
|
setAppPackaged(r.packaged);
|
||||||
} catch {
|
} catch {
|
||||||
setAppVersionText(null);
|
setAppVersionText(null);
|
||||||
}
|
}
|
||||||
@@ -647,6 +650,20 @@ export function EditorApp() {
|
|||||||
>
|
>
|
||||||
{t('menu.aboutLicense')}
|
{t('menu.aboutLicense')}
|
||||||
</button>
|
</button>
|
||||||
|
{licenseActive && appPackaged ? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
role="menuitem"
|
||||||
|
className={styles.fileMenuItem}
|
||||||
|
onClick={() => {
|
||||||
|
setSettingsMenuOpen(false);
|
||||||
|
setSettingsLangSubOpen(false);
|
||||||
|
setCheckUpdatesOpen(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('menu.checkUpdates')}
|
||||||
|
</button>
|
||||||
|
) : null}
|
||||||
<div className={styles.fileMenuSubHost} role="presentation">
|
<div className={styles.fileMenuSubHost} role="presentation">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -819,6 +836,7 @@ export function EditorApp() {
|
|||||||
await actions.exportProject(projectId);
|
await actions.exportProject(projectId);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<CheckUpdatesModal open={checkUpdatesOpen} onClose={() => setCheckUpdatesOpen(false)} />
|
||||||
<SimpleMessageModal
|
<SimpleMessageModal
|
||||||
open={appNotice !== null}
|
open={appNotice !== null}
|
||||||
title={appNotice?.title ?? t('common.message')}
|
title={appNotice?.title ?? t('common.message')}
|
||||||
@@ -941,6 +959,136 @@ function ExportProjectModal({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CheckUpdatesModalProps = {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
function CheckUpdatesModal({ open, onClose }: CheckUpdatesModalProps) {
|
||||||
|
const { t } = useEditorI18n();
|
||||||
|
const [phase, setPhase] = useState<'idle' | 'checking' | 'done'>('idle');
|
||||||
|
const [res, setRes] = useState<UpdaterCheckResponse | null>(null);
|
||||||
|
const [downloadBusy, setDownloadBusy] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!open) return;
|
||||||
|
startTransition(() => {
|
||||||
|
setPhase('checking');
|
||||||
|
setRes(null);
|
||||||
|
});
|
||||||
|
void getDndApi()
|
||||||
|
.invoke(ipcChannels.updater.check, {})
|
||||||
|
.then((r) => {
|
||||||
|
setRes(r);
|
||||||
|
setPhase('done');
|
||||||
|
})
|
||||||
|
.catch((e: unknown) => {
|
||||||
|
const message = e instanceof Error ? e.message : String(e);
|
||||||
|
setRes({ outcome: 'error', message });
|
||||||
|
setPhase('done');
|
||||||
|
});
|
||||||
|
}, [open]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!open) return;
|
||||||
|
const onKey = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === 'Escape' && !downloadBusy) onClose();
|
||||||
|
};
|
||||||
|
window.addEventListener('keydown', onKey);
|
||||||
|
return () => window.removeEventListener('keydown', onKey);
|
||||||
|
}, [downloadBusy, onClose, open]);
|
||||||
|
|
||||||
|
if (!open) return null;
|
||||||
|
|
||||||
|
const body =
|
||||||
|
phase === 'checking' || res === null ? (
|
||||||
|
<div className={styles.muted}>{t('updates.checking')}</div>
|
||||||
|
) : res.outcome === 'available' ? (
|
||||||
|
<div className={styles.muted}>{t('updates.available', { version: res.version })}</div>
|
||||||
|
) : res.outcome === 'current' ? (
|
||||||
|
<div className={styles.muted}>{t('updates.current', { version: res.currentVersion })}</div>
|
||||||
|
) : res.outcome === 'error' ? (
|
||||||
|
<div className={styles.muted}>{t('updates.error', { message: res.message })}</div>
|
||||||
|
) : res.outcome === 'not_packaged' ? (
|
||||||
|
<div className={styles.muted}>{t('updates.notPackaged')}</div>
|
||||||
|
) : (
|
||||||
|
<div className={styles.muted}>{t('updates.noLicense')}</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const showUpdateIdle = phase === 'done' && res !== null && res.outcome === 'available' && !downloadBusy;
|
||||||
|
const showUpdateBusy = phase === 'done' && res !== null && res.outcome === 'available' && downloadBusy;
|
||||||
|
|
||||||
|
return createPortal(
|
||||||
|
<>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-label={t('common.close')}
|
||||||
|
onClick={() => {
|
||||||
|
if (!downloadBusy) onClose();
|
||||||
|
}}
|
||||||
|
className={styles.modalBackdrop}
|
||||||
|
/>
|
||||||
|
<div role="dialog" aria-modal="true" className={styles.modalDialog}>
|
||||||
|
<div className={styles.modalHeader}>
|
||||||
|
<div className={styles.modalTitle}>{t('updates.dialogTitle')}</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-label={t('common.close')}
|
||||||
|
disabled={downloadBusy}
|
||||||
|
onClick={onClose}
|
||||||
|
className={styles.modalClose}
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className={styles.fieldGrid}>{body}</div>
|
||||||
|
<div className={styles.modalFooter}>
|
||||||
|
{showUpdateIdle ? (
|
||||||
|
<>
|
||||||
|
<Button variant="ghost" disabled={downloadBusy} onClick={onClose}>
|
||||||
|
{t('common.close')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
disabled={downloadBusy}
|
||||||
|
onClick={() => {
|
||||||
|
setDownloadBusy(true);
|
||||||
|
void getDndApi()
|
||||||
|
.invoke(ipcChannels.updater.downloadAndRestart, {})
|
||||||
|
.then((r) => {
|
||||||
|
if (!r.ok) {
|
||||||
|
setDownloadBusy(false);
|
||||||
|
setRes({ outcome: 'error', message: r.message });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((e: unknown) => {
|
||||||
|
setDownloadBusy(false);
|
||||||
|
setRes({
|
||||||
|
outcome: 'error',
|
||||||
|
message: e instanceof Error ? e.message : String(e),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('updates.download')}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
) : showUpdateBusy ? (
|
||||||
|
<Button variant="primary" disabled>
|
||||||
|
{t('updates.downloading')}
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button variant="primary" disabled={downloadBusy} onClick={onClose}>
|
||||||
|
{t('common.close')}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>,
|
||||||
|
document.body,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
type SimpleMessageModalProps = {
|
type SimpleMessageModalProps = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
title?: string;
|
title?: string;
|
||||||
|
|||||||
@@ -114,10 +114,21 @@ export const EDITOR_MESSAGES: Record<EditorLocale, Record<string, string>> = {
|
|||||||
|
|
||||||
'menu.enterKey': 'Указать ключ',
|
'menu.enterKey': 'Указать ключ',
|
||||||
'menu.aboutLicense': 'О лицензии',
|
'menu.aboutLicense': 'О лицензии',
|
||||||
|
'menu.checkUpdates': 'Проверить обновления',
|
||||||
'menu.language': 'Язык',
|
'menu.language': 'Язык',
|
||||||
'menu.langRu': 'Русский',
|
'menu.langRu': 'Русский',
|
||||||
'menu.langEn': 'English',
|
'menu.langEn': 'English',
|
||||||
|
|
||||||
|
'updates.dialogTitle': 'Обновления',
|
||||||
|
'updates.checking': 'Проверка наличия обновлений…',
|
||||||
|
'updates.available': 'Доступна новая версия {version}.',
|
||||||
|
'updates.current': 'У вас установлена актуальная версия ({version}).',
|
||||||
|
'updates.error': 'Не удалось проверить обновления: {message}',
|
||||||
|
'updates.notPackaged': 'Проверка доступна только в установленной версии приложения.',
|
||||||
|
'updates.noLicense': 'Нужна активная лицензия.',
|
||||||
|
'updates.download': 'Обновить',
|
||||||
|
'updates.downloading': 'Загрузка…',
|
||||||
|
|
||||||
'projectMenu.home': 'Начальный экран',
|
'projectMenu.home': 'Начальный экран',
|
||||||
'projectMenu.import': 'Импорт',
|
'projectMenu.import': 'Импорт',
|
||||||
'projectMenu.export': 'Экспорт',
|
'projectMenu.export': 'Экспорт',
|
||||||
@@ -330,10 +341,21 @@ export const EDITOR_MESSAGES: Record<EditorLocale, Record<string, string>> = {
|
|||||||
|
|
||||||
'menu.enterKey': 'Enter license key',
|
'menu.enterKey': 'Enter license key',
|
||||||
'menu.aboutLicense': 'About license',
|
'menu.aboutLicense': 'About license',
|
||||||
|
'menu.checkUpdates': 'Check for updates',
|
||||||
'menu.language': 'Language',
|
'menu.language': 'Language',
|
||||||
'menu.langRu': 'Русский',
|
'menu.langRu': 'Русский',
|
||||||
'menu.langEn': 'English',
|
'menu.langEn': 'English',
|
||||||
|
|
||||||
|
'updates.dialogTitle': 'Updates',
|
||||||
|
'updates.checking': 'Checking for updates…',
|
||||||
|
'updates.available': 'A new version is available: {version}.',
|
||||||
|
'updates.current': 'You have the latest version ({version}).',
|
||||||
|
'updates.error': 'Could not check for updates: {message}',
|
||||||
|
'updates.notPackaged': 'Updates can only be checked in the installed application.',
|
||||||
|
'updates.noLicense': 'An active license is required.',
|
||||||
|
'updates.download': 'Update',
|
||||||
|
'updates.downloading': 'Downloading…',
|
||||||
|
|
||||||
'projectMenu.home': 'Home',
|
'projectMenu.home': 'Home',
|
||||||
'projectMenu.import': 'Import',
|
'projectMenu.import': 'Import',
|
||||||
'projectMenu.export': 'Export',
|
'projectMenu.export': 'Export',
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ export const ipcChannels = {
|
|||||||
quit: 'app.quit',
|
quit: 'app.quit',
|
||||||
getVersion: 'app.getVersion',
|
getVersion: 'app.getVersion',
|
||||||
},
|
},
|
||||||
|
updater: {
|
||||||
|
check: 'updater.check',
|
||||||
|
downloadAndRestart: 'updater.downloadAndRestart',
|
||||||
|
},
|
||||||
project: {
|
project: {
|
||||||
list: 'project.list',
|
list: 'project.list',
|
||||||
create: 'project.create',
|
create: 'project.create',
|
||||||
@@ -84,6 +88,15 @@ export type ZipProgressEvent = {
|
|||||||
detail?: string;
|
detail?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type UpdaterCheckResponse =
|
||||||
|
| { outcome: 'not_packaged' }
|
||||||
|
| { outcome: 'no_license' }
|
||||||
|
| { outcome: 'current'; currentVersion: string }
|
||||||
|
| { outcome: 'available'; version: string }
|
||||||
|
| { outcome: 'error'; message: string };
|
||||||
|
|
||||||
|
export type UpdaterDownloadResponse = { ok: true } | { ok: false; message: string };
|
||||||
|
|
||||||
export type IpcEventMap = {
|
export type IpcEventMap = {
|
||||||
[ipcChannels.session.stateChanged]: { state: SessionState };
|
[ipcChannels.session.stateChanged]: { state: SessionState };
|
||||||
[ipcChannels.effects.stateChanged]: { state: EffectsState };
|
[ipcChannels.effects.stateChanged]: { state: EffectsState };
|
||||||
@@ -101,7 +114,15 @@ export type IpcInvokeMap = {
|
|||||||
};
|
};
|
||||||
[ipcChannels.app.getVersion]: {
|
[ipcChannels.app.getVersion]: {
|
||||||
req: Record<string, never>;
|
req: Record<string, never>;
|
||||||
res: { version: string; buildNumber: string | null };
|
res: { version: string; buildNumber: string | null; packaged: boolean };
|
||||||
|
};
|
||||||
|
[ipcChannels.updater.check]: {
|
||||||
|
req: Record<string, never>;
|
||||||
|
res: UpdaterCheckResponse;
|
||||||
|
};
|
||||||
|
[ipcChannels.updater.downloadAndRestart]: {
|
||||||
|
req: Record<string, never>;
|
||||||
|
res: UpdaterDownloadResponse;
|
||||||
};
|
};
|
||||||
[ipcChannels.project.list]: {
|
[ipcChannels.project.list]: {
|
||||||
req: Record<string, never>;
|
req: Record<string, never>;
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "DndGamePlayer",
|
"name": "DndGamePlayer",
|
||||||
"version": "1.0.2",
|
"version": "1.0.3",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "DndGamePlayer",
|
"name": "DndGamePlayer",
|
||||||
"version": "1.0.2",
|
"version": "1.0.3",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "DndGamePlayer",
|
"name": "DndGamePlayer",
|
||||||
"version": "1.0.2",
|
"version": "1.0.3",
|
||||||
"description": "DNDGamePlayer — редактор и проигрыватель игр",
|
"description": "DNDGamePlayer — редактор и проигрыватель игр",
|
||||||
"main": "dist/main/index.cjs",
|
"main": "dist/main/index.cjs",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
Reference in New Issue
Block a user