kingkont 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.html +72 -16
- package/main.js +85 -0
- package/package.json +1 -1
- package/preload.js +7 -0
- package/settings.html +6 -6
package/index.html
CHANGED
|
@@ -949,7 +949,7 @@
|
|
|
949
949
|
<aside class="sidebar">
|
|
950
950
|
<div class="sidebar-header">
|
|
951
951
|
<div class="brand">
|
|
952
|
-
<img src="assets/logo-square.svg" alt="" draggable="false">
|
|
952
|
+
<img src="assets/logo-square.svg" alt="" draggable="false" id="brandLogo" title="Дабл-клик — настройки" style="cursor:pointer;">
|
|
953
953
|
<div style="min-width:0; flex:1;">
|
|
954
954
|
<div class="title">KingKont</div>
|
|
955
955
|
<div class="sub" id="brandSub">Видео-редактор</div>
|
|
@@ -979,7 +979,7 @@
|
|
|
979
979
|
<div class="sidebar-list" id="episodeList"></div>
|
|
980
980
|
</div>
|
|
981
981
|
<div class="sidebar-footer">
|
|
982
|
-
<span id="balanceInfo" class="balance-info" style="display:none;" title="Баланс
|
|
982
|
+
<span id="balanceInfo" class="balance-info" style="display:none;" title="Баланс KingKont-аккаунта · клик — лог списаний" onclick="window.openTxLog && window.openTxLog()">
|
|
983
983
|
<span class="dot"></span>
|
|
984
984
|
<span id="balanceValue">— credits</span>
|
|
985
985
|
</span>
|
|
@@ -991,7 +991,7 @@
|
|
|
991
991
|
<!-- Welcome-экран: виден только когда body.no-project -->
|
|
992
992
|
<div class="welcome" id="welcome">
|
|
993
993
|
<div class="welcome-inner">
|
|
994
|
-
<img class="welcome-logo" src="assets/logo-square.svg" alt="" draggable="false">
|
|
994
|
+
<img class="welcome-logo" src="assets/logo-square.svg" alt="" draggable="false" id="welcomeLogo" title="Дабл-клик — настройки" style="cursor:pointer;">
|
|
995
995
|
<h1 class="welcome-title">KingKont</h1>
|
|
996
996
|
<div class="welcome-sub">Видео-редактор</div>
|
|
997
997
|
<button id="welcomeOpen" class="welcome-open primary">Открыть проект</button>
|
|
@@ -1301,6 +1301,14 @@
|
|
|
1301
1301
|
<button class="seg" data-img-model="sdxl-lightning" type="button" title="Очень быстрая (3-5с)">⚡ SDXL Lightning</button>
|
|
1302
1302
|
</div>
|
|
1303
1303
|
</label>
|
|
1304
|
+
<label id="videoModelRow" style="display: none;">Модель для видео
|
|
1305
|
+
<div class="seg-control">
|
|
1306
|
+
<button class="seg active" data-vid-model="seedance-2" type="button" title="ByteDance Seedance 2 — основная">Seedance 2</button>
|
|
1307
|
+
<button class="seg" data-vid-model="seedance-2-fast" type="button" title="Быстрее, чуть проще качество">⚡ Seedance 2 Fast</button>
|
|
1308
|
+
<button class="seg" data-vid-model="kling-o1" type="button" title="Kling O1 — креативный, поддерживает reference-видео">Kling O1</button>
|
|
1309
|
+
<button class="seg" data-vid-model="kling-3.0" type="button" title="Kling 3.0 — multi-shot до 15с">Kling 3.0</button>
|
|
1310
|
+
</div>
|
|
1311
|
+
</label>
|
|
1304
1312
|
<label id="videoOptionsRow" style="display: none;">Параметры видео
|
|
1305
1313
|
<div style="display: flex; gap: 12px; flex-wrap: wrap; margin-top: 6px;">
|
|
1306
1314
|
<div style="flex: 1; min-width: 200px;">
|
|
@@ -1719,7 +1727,8 @@ const state = {
|
|
|
1719
1727
|
filmHandle: null,
|
|
1720
1728
|
currentBoard: null, // { kind, name, key, handle, metadata, urls }
|
|
1721
1729
|
genKind: 'image',
|
|
1722
|
-
imageModel: 'nano-banana-2', // 'nano-banana-2' | 'grok'
|
|
1730
|
+
imageModel: 'nano-banana-2', // 'nano-banana-2' | 'grok' | ...
|
|
1731
|
+
videoModel: localStorage.getItem('videoModel') || 'seedance-2', // 'seedance-2' | 'kling-o1' | 'kling-3.0' | ...
|
|
1723
1732
|
videoDuration: +(localStorage.getItem('videoDuration') || 5),
|
|
1724
1733
|
videoResolution: localStorage.getItem('videoResolution') || '720p',
|
|
1725
1734
|
videoAspect: localStorage.getItem('videoAspect') || '9:16',
|
|
@@ -1968,7 +1977,7 @@ window.addEventListener('DOMContentLoaded', async () => {
|
|
|
1968
1977
|
// Восстановить состояние панелей таймлайна/превью/реплик
|
|
1969
1978
|
const tlOpen = localStorage.getItem('timelineOpen') === '1';
|
|
1970
1979
|
const pvOpen = localStorage.getItem('previewOpen') === '1';
|
|
1971
|
-
const rqOpen = localStorage.getItem('repliquesOpen') === '1';
|
|
1980
|
+
const rqOpen = false; // localStorage.getItem('repliquesOpen') === '1'; — панель реплик скрыта
|
|
1972
1981
|
if (tlOpen) $('timelinePanel').classList.remove('hidden');
|
|
1973
1982
|
// Превью больше не скрывается — состояние управляется previewCollapsed
|
|
1974
1983
|
if (rqOpen) $('repliquesPanel').classList.remove('hidden');
|
|
@@ -2006,6 +2015,13 @@ window.addEventListener('DOMContentLoaded', async () => {
|
|
|
2006
2015
|
// Стартуем обновление баланса. Сразу + каждые 60 сек.
|
|
2007
2016
|
refreshBalance().catch(() => {});
|
|
2008
2017
|
setInterval(() => { refreshBalance().catch(() => {}); }, 60_000);
|
|
2018
|
+
|
|
2019
|
+
// Дабл-клик на логотипе (sidebar или welcome) → открытие окна настроек.
|
|
2020
|
+
const openSettingsFromLogo = () => {
|
|
2021
|
+
if (window.appSettings?.openSettingsWindow) window.appSettings.openSettingsWindow();
|
|
2022
|
+
};
|
|
2023
|
+
document.getElementById('brandLogo')?.addEventListener('dblclick', openSettingsFromLogo);
|
|
2024
|
+
document.getElementById('welcomeLogo')?.addEventListener('dblclick', openSettingsFromLogo);
|
|
2009
2025
|
});
|
|
2010
2026
|
|
|
2011
2027
|
// === Баланс Chatium ===
|
|
@@ -2896,13 +2912,14 @@ async function selectBoard(board) {
|
|
|
2896
2912
|
}
|
|
2897
2913
|
emptyState.classList.add('hidden');
|
|
2898
2914
|
$('charSettingsBtn').style.display = board.kind === 'character' ? '' : 'none';
|
|
2899
|
-
//
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2915
|
+
// Панель реплик скрыта (юзер попросил убрать из карточки персонажа).
|
|
2916
|
+
// Сами данные реплик в scene.json не трогаем — таймлайн и character-озвучка
|
|
2917
|
+
// продолжают работать. Если понадобится вернуть — расскомментируй ниже.
|
|
2918
|
+
$('repliquesPanel').classList.add('hidden');
|
|
2919
|
+
localStorage.setItem('repliquesOpen', '0');
|
|
2920
|
+
// if (board.kind === 'character') {
|
|
2921
|
+
// openRepliquesFor(board.name).catch(e => console.warn('replicas open failed', e));
|
|
2922
|
+
// }
|
|
2906
2923
|
localStorage.setItem(`lastBoard:${state.filmHandle.name}`,
|
|
2907
2924
|
JSON.stringify({ kind: board.kind, name: board.name }));
|
|
2908
2925
|
await refreshSidebar();
|
|
@@ -3740,6 +3757,8 @@ async function openGenerateForRef(fromNode, clientX, clientY, forceKind) {
|
|
|
3740
3757
|
b.classList.toggle('active', b.dataset.kind === forceKind));
|
|
3741
3758
|
$('imageModelRow').style.display = forceKind === 'image' ? '' : 'none';
|
|
3742
3759
|
$('videoOptionsRow').style.display = forceKind === 'video' ? '' : 'none';
|
|
3760
|
+
|
|
3761
|
+
$('videoModelRow').style.display = forceKind === 'video' ? '' : 'none';
|
|
3743
3762
|
$('voiceRow').style.display = forceKind === 'audio' ? '' : 'none';
|
|
3744
3763
|
$('tonesRow').style.display = forceKind === 'audio' ? '' : 'none';
|
|
3745
3764
|
const titleEl = $('genTitle');
|
|
@@ -4990,6 +5009,8 @@ async function regenerateNode(node) {
|
|
|
4990
5009
|
b.classList.toggle('active', b.dataset.kind === state.genKind));
|
|
4991
5010
|
$('imageModelRow').style.display = state.genKind === 'image' ? '' : 'none';
|
|
4992
5011
|
$('videoOptionsRow').style.display = state.genKind === 'video' ? '' : 'none';
|
|
5012
|
+
|
|
5013
|
+
$('videoModelRow').style.display = state.genKind === 'video' ? '' : 'none';
|
|
4993
5014
|
$('voiceRow').style.display = state.genKind === 'audio' ? '' : 'none';
|
|
4994
5015
|
|
|
4995
5016
|
if (g.modelKey && state.genKind === 'image') {
|
|
@@ -5002,7 +5023,9 @@ async function regenerateNode(node) {
|
|
|
5002
5023
|
if (g.duration) state.videoDuration = g.duration;
|
|
5003
5024
|
if (g.resolution) state.videoResolution = g.resolution;
|
|
5004
5025
|
if (g.aspectRatio) state.videoAspect = g.aspectRatio;
|
|
5026
|
+
if (g.modelKey) state.videoModel = g.modelKey;
|
|
5005
5027
|
syncVideoOptionsActive();
|
|
5028
|
+
syncVideoModelActive();
|
|
5006
5029
|
}
|
|
5007
5030
|
if (state.genKind === 'audio') {
|
|
5008
5031
|
await loadVoices();
|
|
@@ -5092,7 +5115,7 @@ async function regenerateInto(node, kind, rawPrompt, opts = {}) {
|
|
|
5092
5115
|
}
|
|
5093
5116
|
resolvedPrompt = resolveMentions(rawPrompt, refs);
|
|
5094
5117
|
} else {
|
|
5095
|
-
modelKey = kind === 'image' ? state.imageModel : 'seedance-2';
|
|
5118
|
+
modelKey = kind === 'image' ? state.imageModel : (state.videoModel || 'seedance-2');
|
|
5096
5119
|
refs = gatherMediaRefs(rawPrompt);
|
|
5097
5120
|
// Подмешиваем "исходный кадр" — текущий файл ноды как референс при редактировании
|
|
5098
5121
|
let sourceMarker = '';
|
|
@@ -5133,7 +5156,12 @@ async function regenerateInto(node, kind, rawPrompt, opts = {}) {
|
|
|
5133
5156
|
'flux-schnell': 'flux/schnell',
|
|
5134
5157
|
'sdxl-lightning': 'sdxl/lightning',
|
|
5135
5158
|
'nano-banana-2': 'nano-banana-2' }[modelKey] || 'nano-banana-2')
|
|
5136
|
-
: kind === 'video' ?
|
|
5159
|
+
: kind === 'video' ? ({
|
|
5160
|
+
'seedance-2': 'bytedance/seedance-2',
|
|
5161
|
+
'seedance-2-fast': 'bytedance/seedance-2-fast',
|
|
5162
|
+
'kling-o1': 'kwaivgi/kling-o1',
|
|
5163
|
+
'kling-3.0': 'kling-3.0/video',
|
|
5164
|
+
}[modelKey] || 'bytedance/seedance-2')
|
|
5137
5165
|
: kind === 'text' ? modelKey // полный slug OpenRouter
|
|
5138
5166
|
: 'eleven_v3'; // audio
|
|
5139
5167
|
|
|
@@ -5930,6 +5958,8 @@ function openPhraseFor(charInfo) {
|
|
|
5930
5958
|
b.classList.toggle('active', b.dataset.kind === 'audio'));
|
|
5931
5959
|
$('imageModelRow').style.display = 'none';
|
|
5932
5960
|
$('videoOptionsRow').style.display = 'none';
|
|
5961
|
+
|
|
5962
|
+
$('videoModelRow').style.display = 'none';
|
|
5933
5963
|
$('voiceRow').style.display = '';
|
|
5934
5964
|
$('tonesRow').style.display = '';
|
|
5935
5965
|
loadVoices().then(() => {
|
|
@@ -6109,6 +6139,8 @@ async function openGenModal(kind) {
|
|
|
6109
6139
|
// Видимость рядов под текущий kind
|
|
6110
6140
|
$('imageModelRow').style.display = kind === 'image' ? '' : 'none';
|
|
6111
6141
|
$('videoOptionsRow').style.display = kind === 'video' ? '' : 'none';
|
|
6142
|
+
|
|
6143
|
+
$('videoModelRow').style.display = kind === 'video' ? '' : 'none';
|
|
6112
6144
|
$('voiceRow').style.display = kind === 'audio' ? '' : 'none';
|
|
6113
6145
|
$('tonesRow').style.display = kind === 'audio' ? '' : 'none';
|
|
6114
6146
|
// Заголовок модалки = действие
|
|
@@ -6527,6 +6559,8 @@ document.querySelectorAll('#genModal [data-kind]').forEach(b => {
|
|
|
6527
6559
|
state.genKind = b.dataset.kind;
|
|
6528
6560
|
$('imageModelRow').style.display = state.genKind === 'image' ? '' : 'none';
|
|
6529
6561
|
$('videoOptionsRow').style.display = state.genKind === 'video' ? '' : 'none';
|
|
6562
|
+
|
|
6563
|
+
$('videoModelRow').style.display = state.genKind === 'video' ? '' : 'none';
|
|
6530
6564
|
$('voiceRow').style.display = state.genKind === 'audio' ? '' : 'none';
|
|
6531
6565
|
$('tonesRow').style.display = state.genKind === 'audio' ? '' : 'none';
|
|
6532
6566
|
if (state.genKind === 'audio') { loadVoices(); renderTones(); }
|
|
@@ -6782,6 +6816,20 @@ document.querySelectorAll('#genModal [data-img-model]').forEach(b => {
|
|
|
6782
6816
|
state.imageModel = b.dataset.imgModel;
|
|
6783
6817
|
});
|
|
6784
6818
|
});
|
|
6819
|
+
// Переключатель модели видео
|
|
6820
|
+
document.querySelectorAll('#genModal [data-vid-model]').forEach(b => {
|
|
6821
|
+
b.addEventListener('click', () => {
|
|
6822
|
+
document.querySelectorAll('#genModal [data-vid-model]').forEach(x => x.classList.remove('active'));
|
|
6823
|
+
b.classList.add('active');
|
|
6824
|
+
state.videoModel = b.dataset.vidModel;
|
|
6825
|
+
localStorage.setItem('videoModel', state.videoModel);
|
|
6826
|
+
});
|
|
6827
|
+
});
|
|
6828
|
+
// Подсветить активную video-модель при открытии modal'а
|
|
6829
|
+
function syncVideoModelActive() {
|
|
6830
|
+
document.querySelectorAll('#genModal [data-vid-model]').forEach(b =>
|
|
6831
|
+
b.classList.toggle('active', b.dataset.vidModel === state.videoModel));
|
|
6832
|
+
}
|
|
6785
6833
|
// Переключатели длительности и разрешения для видео
|
|
6786
6834
|
document.querySelectorAll('#genModal [data-vid-dur]').forEach(b => {
|
|
6787
6835
|
b.addEventListener('click', () => {
|
|
@@ -6818,6 +6866,7 @@ function syncVideoOptionsActive() {
|
|
|
6818
6866
|
b.classList.toggle('active', b.dataset.vidAsp === state.videoAspect));
|
|
6819
6867
|
}
|
|
6820
6868
|
syncVideoOptionsActive();
|
|
6869
|
+
syncVideoModelActive();
|
|
6821
6870
|
|
|
6822
6871
|
$('genCancel').addEventListener('click', () => {
|
|
6823
6872
|
state.pendingConnectionFrom = null;
|
|
@@ -7014,7 +7063,7 @@ $('genSubmit').addEventListener('click', async () => {
|
|
|
7014
7063
|
kind,
|
|
7015
7064
|
prompt: resolvedPrompt,
|
|
7016
7065
|
rawPrompt,
|
|
7017
|
-
modelKey: kind === 'image' ? state.imageModel : 'seedance-2',
|
|
7066
|
+
modelKey: kind === 'image' ? state.imageModel : (state.videoModel || 'seedance-2'),
|
|
7018
7067
|
model: kind === 'image'
|
|
7019
7068
|
? ({
|
|
7020
7069
|
'grok': 'grok-imagine/text-to-image',
|
|
@@ -7022,7 +7071,12 @@ $('genSubmit').addEventListener('click', async () => {
|
|
|
7022
7071
|
'seedream-5-lite': 'seedream/5-lite-text-to-image',
|
|
7023
7072
|
'nano-banana-2': 'nano-banana-2',
|
|
7024
7073
|
}[state.imageModel] || 'nano-banana-2')
|
|
7025
|
-
:
|
|
7074
|
+
: ({
|
|
7075
|
+
'seedance-2': 'bytedance/seedance-2',
|
|
7076
|
+
'seedance-2-fast': 'bytedance/seedance-2-fast',
|
|
7077
|
+
'kling-o1': 'kwaivgi/kling-o1',
|
|
7078
|
+
'kling-3.0': 'kling-3.0/video',
|
|
7079
|
+
}[state.videoModel || 'seedance-2'] || 'bytedance/seedance-2'),
|
|
7026
7080
|
refs: mediaRefs.map(r => ({ name: r.name, type: r.type, file: r.file })),
|
|
7027
7081
|
// Связь с родительской нодой (выведена через "вытягивание"). Хранится
|
|
7028
7082
|
// даже если sourceRef.use=false в state — сохраняем структурную связь.
|
|
@@ -7728,6 +7782,8 @@ async function openGenAudioForTimeline(charInfo, track, time) {
|
|
|
7728
7782
|
b.classList.toggle('active', b.dataset.kind === 'audio'));
|
|
7729
7783
|
$('imageModelRow').style.display = 'none';
|
|
7730
7784
|
$('videoOptionsRow').style.display = 'none';
|
|
7785
|
+
|
|
7786
|
+
$('videoModelRow').style.display = 'none';
|
|
7731
7787
|
$('voiceRow').style.display = '';
|
|
7732
7788
|
$('tonesRow').style.display = '';
|
|
7733
7789
|
$('sourceRefRow').style.display = 'none';
|
package/main.js
CHANGED
|
@@ -346,6 +346,85 @@ function openSettingsWindow() {
|
|
|
346
346
|
ipcMain.handle('settings-window:close', () => {
|
|
347
347
|
if (settingsWin && !settingsWin.isDestroyed()) settingsWin.close();
|
|
348
348
|
});
|
|
349
|
+
ipcMain.handle('settings-window:open', () => openSettingsWindow());
|
|
350
|
+
|
|
351
|
+
// ===== Updates window + npm version check =====
|
|
352
|
+
let updatesWin = null;
|
|
353
|
+
function openUpdatesWindow() {
|
|
354
|
+
if (updatesWin && !updatesWin.isDestroyed()) {
|
|
355
|
+
updatesWin.focus();
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
updatesWin = new BrowserWindow({
|
|
359
|
+
width: 480,
|
|
360
|
+
height: 380,
|
|
361
|
+
title: 'Обновления',
|
|
362
|
+
parent: win || undefined,
|
|
363
|
+
modal: false,
|
|
364
|
+
resizable: false,
|
|
365
|
+
minimizable: false,
|
|
366
|
+
maximizable: false,
|
|
367
|
+
backgroundColor: '#1a1a1a',
|
|
368
|
+
webPreferences: {
|
|
369
|
+
contextIsolation: true,
|
|
370
|
+
nodeIntegration: false,
|
|
371
|
+
preload: path.join(__dirname, 'preload.js'),
|
|
372
|
+
},
|
|
373
|
+
});
|
|
374
|
+
updatesWin.removeMenu();
|
|
375
|
+
updatesWin.loadFile(path.join(__dirname, 'updates.html'));
|
|
376
|
+
updatesWin.on('closed', () => { updatesWin = null; });
|
|
377
|
+
}
|
|
378
|
+
ipcMain.handle('updates-window:close', () => {
|
|
379
|
+
if (updatesWin && !updatesWin.isDestroyed()) updatesWin.close();
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
// Простой semver-сравнитель: возвращает true если b > a (есть более свежая).
|
|
383
|
+
// Поддерживает X.Y.Z и pre-release suffix через '-' (rc/beta пропускаем как not-newer).
|
|
384
|
+
function isNewerVersion(a, b) {
|
|
385
|
+
const pa = String(a).split('-')[0].split('.').map(n => parseInt(n, 10) || 0);
|
|
386
|
+
const pb = String(b).split('-')[0].split('.').map(n => parseInt(n, 10) || 0);
|
|
387
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
388
|
+
const x = pa[i] || 0, y = pb[i] || 0;
|
|
389
|
+
if (y > x) return true;
|
|
390
|
+
if (y < x) return false;
|
|
391
|
+
}
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
async function fetchLatestVersion() {
|
|
396
|
+
// npm registry GET /<pkg>/latest → { version, ... }
|
|
397
|
+
const r = await fetch('https://registry.npmjs.org/kingkont/latest', {
|
|
398
|
+
headers: { 'Accept': 'application/json' },
|
|
399
|
+
});
|
|
400
|
+
if (!r.ok) throw new Error(`npm registry HTTP ${r.status}`);
|
|
401
|
+
const d = await r.json();
|
|
402
|
+
if (!d.version) throw new Error('registry без поля version');
|
|
403
|
+
return d.version;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
ipcMain.handle('updates:check', async () => {
|
|
407
|
+
const current = app.getVersion();
|
|
408
|
+
const latest = await fetchLatestVersion();
|
|
409
|
+
return { current, latest, isNew: isNewerVersion(current, latest) };
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
// Фоновая проверка при старте: если есть свежая — открываем окно автоматом.
|
|
413
|
+
// Чтобы не доставать юзера до открытия проекта, ждём 5 сек после createWindow.
|
|
414
|
+
async function backgroundCheckUpdates() {
|
|
415
|
+
try {
|
|
416
|
+
const current = app.getVersion();
|
|
417
|
+
const latest = await fetchLatestVersion();
|
|
418
|
+
if (isNewerVersion(current, latest)) {
|
|
419
|
+
console.log(`[updates] новая версия ${latest} (текущая ${current})`);
|
|
420
|
+
openUpdatesWindow();
|
|
421
|
+
} else {
|
|
422
|
+
console.log(`[updates] актуально (${current})`);
|
|
423
|
+
}
|
|
424
|
+
} catch (e) {
|
|
425
|
+
console.warn('[updates] check failed:', e?.message);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
349
428
|
|
|
350
429
|
// ===== Recents persisted в userData/recents.json (переживает quota-reset IDB) =====
|
|
351
430
|
function recentsPath() {
|
|
@@ -405,6 +484,7 @@ function buildMenu() {
|
|
|
405
484
|
label: 'KingKont',
|
|
406
485
|
submenu: [
|
|
407
486
|
{ label: 'О программе KingKont', role: 'about' },
|
|
487
|
+
{ label: 'Проверить обновления…', click: () => openUpdatesWindow() },
|
|
408
488
|
{ type: 'separator' },
|
|
409
489
|
{ label: 'Настройки…', accelerator: 'Cmd+,', click: () => openSettingsWindow() },
|
|
410
490
|
{ type: 'separator' },
|
|
@@ -453,6 +533,7 @@ function buildMenu() {
|
|
|
453
533
|
...(isMac ? [] : [
|
|
454
534
|
{ type: 'separator' },
|
|
455
535
|
{ label: 'Настройки…', accelerator: 'CmdOrCtrl+,', click: () => openSettingsWindow() },
|
|
536
|
+
{ label: 'Проверить обновления…', click: () => openUpdatesWindow() },
|
|
456
537
|
{ type: 'separator' },
|
|
457
538
|
{ role: 'quit' },
|
|
458
539
|
]),
|
|
@@ -579,6 +660,10 @@ app.whenReady().then(async () => {
|
|
|
579
660
|
await createWindow();
|
|
580
661
|
buildMenu();
|
|
581
662
|
|
|
663
|
+
// Background check обновлений: ждём чтобы юзер увидел welcome/restore,
|
|
664
|
+
// потом тихо ходим в npm registry. Если есть свежая — показываем окно.
|
|
665
|
+
setTimeout(() => { backgroundCheckUpdates(); }, 5000);
|
|
666
|
+
|
|
582
667
|
app.on('activate', () => {
|
|
583
668
|
if (BrowserWindow.getAllWindows().length === 0) createWindow();
|
|
584
669
|
});
|
package/package.json
CHANGED
package/preload.js
CHANGED
|
@@ -45,6 +45,13 @@ contextBridge.exposeInMainWorld('appSettings', {
|
|
|
45
45
|
get: () => ipcRenderer.invoke('settings:get'),
|
|
46
46
|
save: (partial) => ipcRenderer.invoke('settings:save', partial),
|
|
47
47
|
closeSettingsWindow: () => ipcRenderer.invoke('settings-window:close'),
|
|
48
|
+
openSettingsWindow: () => ipcRenderer.invoke('settings-window:open'),
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Проверка обновлений через npm registry (в main-процессе).
|
|
52
|
+
contextBridge.exposeInMainWorld('appUpdates', {
|
|
53
|
+
check: () => ipcRenderer.invoke('updates:check'),
|
|
54
|
+
closeWindow: () => ipcRenderer.invoke('updates-window:close'),
|
|
48
55
|
});
|
|
49
56
|
|
|
50
57
|
// Авторизация в Chatium через loopback OAuth-flow.
|
package/settings.html
CHANGED
|
@@ -101,26 +101,26 @@
|
|
|
101
101
|
<div class="wrap">
|
|
102
102
|
<h1>Настройки</h1>
|
|
103
103
|
|
|
104
|
-
<h2>
|
|
104
|
+
<h2>KingKont</h2>
|
|
105
105
|
<div class="card" id="chatiumCard">
|
|
106
106
|
<label class="use-toggle" for="useChatium">
|
|
107
107
|
<input type="checkbox" id="useChatium" />
|
|
108
|
-
<span class="label-text">Использовать
|
|
108
|
+
<span class="label-text">Использовать KingKont <span class="sub">— текст / аудио / видео через kingkont.ru</span></span>
|
|
109
109
|
</label>
|
|
110
110
|
<div class="chatium-state">
|
|
111
111
|
<div class="dot off" id="chatiumDot"></div>
|
|
112
112
|
<div class="who" id="chatiumWho">
|
|
113
113
|
<b>Не подключено</b>
|
|
114
|
-
<small>Авторизация нужна для генерации текста, аудио и видео через
|
|
114
|
+
<small>Авторизация нужна для генерации текста, аудио и видео через KingKont.</small>
|
|
115
115
|
</div>
|
|
116
116
|
</div>
|
|
117
117
|
<div class="row">
|
|
118
|
-
<button id="chatiumLogin">Войти в
|
|
118
|
+
<button id="chatiumLogin">Войти в KingKont</button>
|
|
119
119
|
<button id="chatiumLogout" class="danger" style="display:none;">Выйти</button>
|
|
120
120
|
<button id="chatiumRefresh" class="secondary">Проверить</button>
|
|
121
121
|
</div>
|
|
122
122
|
<div class="error" id="chatiumError" style="display:none;"></div>
|
|
123
|
-
<div class="hint">Откроется браузер для входа в
|
|
123
|
+
<div class="hint">Откроется браузер для входа в KingKont. После подтверждения авторизация сохранится локально и будет использоваться приложением для запросов к нейросетям.</div>
|
|
124
124
|
</div>
|
|
125
125
|
|
|
126
126
|
<h2>Прямые коннекторы</h2>
|
|
@@ -231,7 +231,7 @@ function renderChatiumState(chatium) {
|
|
|
231
231
|
logoutBtn.style.display = '';
|
|
232
232
|
} else {
|
|
233
233
|
dot.classList.remove('on'); dot.classList.add('off');
|
|
234
|
-
who.innerHTML = `<b>Не подключено</b><small>Авторизация нужна для генерации текста, аудио и видео через
|
|
234
|
+
who.innerHTML = `<b>Не подключено</b><small>Авторизация нужна для генерации текста, аудио и видео через KingKont.</small>`;
|
|
235
235
|
loginBtn.style.display = '';
|
|
236
236
|
logoutBtn.style.display = 'none';
|
|
237
237
|
}
|