kingkont 0.18.3 → 0.18.5
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/package.json +1 -1
- package/renderer/board.js +24 -0
- package/renderer/notifyPanel.js +67 -4
- package/renderer/styles.css +11 -4
package/package.json
CHANGED
package/renderer/board.js
CHANGED
|
@@ -976,6 +976,30 @@ function showCloudCardContextMenu(p, clientX, clientY) {
|
|
|
976
976
|
b.addEventListener('click', () => { menu.classList.add('hidden'); fn(); });
|
|
977
977
|
menu.appendChild(b);
|
|
978
978
|
};
|
|
979
|
+
add('✏️ Переименовать…', async () => {
|
|
980
|
+
const newName = await askName('Новое имя проекта:', '', p.name || '', { okText: 'Переименовать' });
|
|
981
|
+
if (!newName || newName.trim() === (p.name || '').trim()) return;
|
|
982
|
+
try {
|
|
983
|
+
// POST /api/projects/<id> с {name} → providers.updateProject → Chatium ~update.
|
|
984
|
+
const r = await fetch('/api/projects/' + encodeURIComponent(p.id), {
|
|
985
|
+
method: 'POST',
|
|
986
|
+
headers: { 'Content-Type': 'application/json' },
|
|
987
|
+
body: JSON.stringify({ name: newName.trim() }),
|
|
988
|
+
});
|
|
989
|
+
if (!r.ok) {
|
|
990
|
+
const err = await r.json().catch(() => ({}));
|
|
991
|
+
throw new Error(err.error || 'HTTP ' + r.status);
|
|
992
|
+
}
|
|
993
|
+
// Обновляем кэш локально, чтобы welcome не дёргал сеть для перерисовки.
|
|
994
|
+
try {
|
|
995
|
+
const cached = JSON.parse(localStorage.getItem('cloudProjectsCache') || '[]');
|
|
996
|
+
const i = cached.findIndex(c => c.id === p.id);
|
|
997
|
+
if (i >= 0) { cached[i].name = newName.trim(); cached[i].updatedAt = Date.now(); }
|
|
998
|
+
localStorage.setItem('cloudProjectsCache', JSON.stringify(cached));
|
|
999
|
+
} catch {}
|
|
1000
|
+
await renderWelcomeRecents();
|
|
1001
|
+
} catch (e) { alert('Не удалось переименовать: ' + (e?.message || e)); }
|
|
1002
|
+
});
|
|
979
1003
|
add('📂 Открыть в Finder', async () => {
|
|
980
1004
|
if (!window.cloudFs?.openInFinder) {
|
|
981
1005
|
alert('Доступно только в Electron-приложении');
|
package/renderer/notifyPanel.js
CHANGED
|
@@ -21,12 +21,46 @@
|
|
|
21
21
|
|
|
22
22
|
function _ensureUI() {
|
|
23
23
|
if ($('notifyBtn')) return;
|
|
24
|
+
// CSS animations для бейджа/панели/новых row'ов — иначе юзер не
|
|
25
|
+
// замечает что событие пришло (прошлая версия с display:none→flex
|
|
26
|
+
// была инстант, не привлекала внимание).
|
|
27
|
+
if (!$('notifyPanelStyles')) {
|
|
28
|
+
const style = document.createElement('style');
|
|
29
|
+
style.id = 'notifyPanelStyles';
|
|
30
|
+
style.textContent = `
|
|
31
|
+
@keyframes notifyBellPulse {
|
|
32
|
+
0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(227,51,119,0.55); background: rgba(60,30,50,0.92); }
|
|
33
|
+
40% { transform: scale(1.18); box-shadow: 0 0 0 8px rgba(227,51,119,0); background: rgba(120,40,80,0.95); }
|
|
34
|
+
100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(227,51,119,0); background: rgba(30,30,40,0.85); }
|
|
35
|
+
}
|
|
36
|
+
@keyframes notifyPanelSlide {
|
|
37
|
+
0% { transform: translateY(12px) scale(0.96); opacity: 0; }
|
|
38
|
+
100% { transform: translateY(0) scale(1); opacity: 1; }
|
|
39
|
+
}
|
|
40
|
+
@keyframes notifyRowFlash {
|
|
41
|
+
0% { box-shadow: 0 0 0 0 rgba(227,51,119,0.0), inset 0 0 0 0 rgba(227,51,119,0.0); transform: translateX(-6px); opacity: 0.4; }
|
|
42
|
+
18% { transform: translateX(0); opacity: 1; box-shadow: 0 0 14px 2px rgba(227,51,119,0.55), inset 2px 0 0 0 rgba(227,51,119,0.85); }
|
|
43
|
+
100% { box-shadow: 0 0 0 0 rgba(227,51,119,0), inset 2px 0 0 0 rgba(227,51,119,0); transform: translateX(0); opacity: 1; }
|
|
44
|
+
}
|
|
45
|
+
#notifyBtn.pulsing {
|
|
46
|
+
animation: notifyBellPulse 0.9s ease-out 1;
|
|
47
|
+
}
|
|
48
|
+
#notifyPanel.opening {
|
|
49
|
+
animation: notifyPanelSlide 0.22s cubic-bezier(0.18, 0.89, 0.32, 1.28) 1;
|
|
50
|
+
transform-origin: bottom left;
|
|
51
|
+
}
|
|
52
|
+
#notifyList .notify-row.is-new {
|
|
53
|
+
animation: notifyRowFlash 1.6s ease-out 1;
|
|
54
|
+
}
|
|
55
|
+
`;
|
|
56
|
+
document.head.appendChild(style);
|
|
57
|
+
}
|
|
24
58
|
// Кнопка-колокольчик в нижнем-левом углу — там есть свободное место,
|
|
25
59
|
// не перекрывает sidebar/canvas-toolbar/welcome-status.
|
|
26
60
|
const btn = document.createElement('button');
|
|
27
61
|
btn.id = 'notifyBtn';
|
|
28
62
|
btn.title = 'События (генерации, чат, ошибки)';
|
|
29
|
-
btn.style.cssText = 'position:fixed; bottom:12px; left:12px; z-index:9998; background:rgba(30,30,40,0.85); border:1px solid #444; color:#ccc; font-size:14px; width:32px; height:32px; border-radius:50%; cursor:pointer; backdrop-filter:blur(4px); display:flex; align-items:center; justify-content:center;';
|
|
63
|
+
btn.style.cssText = 'position:fixed; bottom:12px; left:12px; z-index:9998; background:rgba(30,30,40,0.85); border:1px solid #444; color:#ccc; font-size:14px; width:32px; height:32px; border-radius:50%; cursor:pointer; backdrop-filter:blur(4px); display:flex; align-items:center; justify-content:center; transition:background 0.2s;';
|
|
30
64
|
btn.innerHTML = '🔔';
|
|
31
65
|
document.body.appendChild(btn);
|
|
32
66
|
btn.addEventListener('click', toggle);
|
|
@@ -62,8 +96,21 @@
|
|
|
62
96
|
_ensureUI();
|
|
63
97
|
panelOpen = open;
|
|
64
98
|
openMode = open ? (mode || 'manual') : null;
|
|
65
|
-
$('notifyPanel')
|
|
66
|
-
|
|
99
|
+
const panel = $('notifyPanel');
|
|
100
|
+
panel.style.display = open ? 'flex' : 'none';
|
|
101
|
+
if (open) {
|
|
102
|
+
// Слайд-анимация при открытии — иначе display:none→flex слишком
|
|
103
|
+
// резкий, юзер не успевает заметить «панель появилась». Переустанавливаем
|
|
104
|
+
// класс через requestAnimationFrame чтобы анимация рестартовала
|
|
105
|
+
// даже если панель только что закрылась.
|
|
106
|
+
panel.classList.remove('opening');
|
|
107
|
+
requestAnimationFrame(() => {
|
|
108
|
+
requestAnimationFrame(() => panel.classList.add('opening'));
|
|
109
|
+
});
|
|
110
|
+
unread = 0;
|
|
111
|
+
updateBadge();
|
|
112
|
+
render();
|
|
113
|
+
}
|
|
67
114
|
}
|
|
68
115
|
function toggle() { setOpen(!panelOpen, 'manual'); }
|
|
69
116
|
function isManuallyOpen() { return panelOpen && openMode === 'manual'; }
|
|
@@ -96,6 +143,11 @@
|
|
|
96
143
|
const time = `${String(dt.getHours()).padStart(2,'0')}:${String(dt.getMinutes()).padStart(2,'0')}:${String(dt.getSeconds()).padStart(2,'0')}`;
|
|
97
144
|
const clickable = !!e.target;
|
|
98
145
|
row.style.cssText = `position:relative; padding:8px 10px 8px 10px; border-radius:4px; margin-bottom:4px; background:${c}22; border-left:3px solid ${c}; cursor:${clickable ? 'pointer' : 'default'}; transition:background 0.1s;`;
|
|
146
|
+
row.classList.add('notify-row');
|
|
147
|
+
// Свежее событие — flash-анимация. Окно 2.2s достаточное чтобы
|
|
148
|
+
// юзер заметил, и не настолько большое чтобы старый event при
|
|
149
|
+
// re-render'е (например после удаления) ложно мигнул.
|
|
150
|
+
if (Date.now() - e.ts < 2200) row.classList.add('is-new');
|
|
99
151
|
if (clickable) row.title = 'Перейти к объекту';
|
|
100
152
|
const targetHint = clickable ? `<span style="color:#9ab; font-size:10px; margin-left:6px;">↗</span>` : '';
|
|
101
153
|
row.innerHTML = `<div style="display:flex; justify-content:space-between; gap:8px; align-items:flex-start; padding-right:18px;">
|
|
@@ -164,11 +216,22 @@
|
|
|
164
216
|
} else {
|
|
165
217
|
render();
|
|
166
218
|
}
|
|
219
|
+
_pulseBell();
|
|
167
220
|
// Авто-открытие панели на 3.5s — юзер видит событие сразу, потом
|
|
168
221
|
// панель прячется. Если юзер сам открыл — не трогаем (проверка в
|
|
169
222
|
// openAuto). Каждое новое событие сбрасывает таймер.
|
|
170
223
|
_scheduleAutoFlash();
|
|
171
224
|
}
|
|
225
|
+
// Bell-pulse: одна короткая анимация. Перезапускаем класс через
|
|
226
|
+
// void offsetWidth — иначе подряд идущие события не «пульсируют»
|
|
227
|
+
// повторно (CSS animation не рестартует на тот же класс).
|
|
228
|
+
function _pulseBell() {
|
|
229
|
+
const btn = $('notifyBtn');
|
|
230
|
+
if (!btn) return;
|
|
231
|
+
btn.classList.remove('pulsing');
|
|
232
|
+
void btn.offsetWidth;
|
|
233
|
+
btn.classList.add('pulsing');
|
|
234
|
+
}
|
|
172
235
|
// Auto-flash: открыть в auto-режиме и закрыть через таймер. Срабатывает
|
|
173
236
|
// на КАЖДОЕ addEvent. Если юзер уже manual'но открыл — открытие no-op'ится.
|
|
174
237
|
let _autoFlashT = null;
|
|
@@ -179,7 +242,7 @@
|
|
|
179
242
|
_autoFlashT = setTimeout(() => {
|
|
180
243
|
_autoFlashT = null;
|
|
181
244
|
if (openMode === 'auto') setOpen(false);
|
|
182
|
-
},
|
|
245
|
+
}, 5000);
|
|
183
246
|
}
|
|
184
247
|
|
|
185
248
|
// Navigate from notification to scene/node.
|
package/renderer/styles.css
CHANGED
|
@@ -163,11 +163,18 @@
|
|
|
163
163
|
margin-left: 4px;
|
|
164
164
|
}
|
|
165
165
|
.sidebar-header .brand .sub { font-size: 10px; color: #888; letter-spacing: 0.5px; text-transform: uppercase; }
|
|
166
|
-
|
|
166
|
+
/* Когда проект открыт — название проекта показывается КРУПНО (juser попросил
|
|
167
|
+
«название проекта показывай крупным сверху-слева»). Title «KingKont vX»
|
|
168
|
+
остаётся, но визуально вторичен — sub перебивает по размеру. */
|
|
169
|
+
.sidebar-header .brand .sub.has-project {
|
|
170
|
+
color: #fff; text-transform: none; letter-spacing: 0;
|
|
171
|
+
font-size: 18px; font-weight: 600; line-height: 1.2;
|
|
172
|
+
word-break: break-word;
|
|
173
|
+
}
|
|
174
|
+
/* Имя выбранной сцены спрятано в шапке (юзер: «название выбранной сцены
|
|
175
|
+
скрывай»). Сама подсветка сцены остаётся в sidebar-list. */
|
|
167
176
|
.sidebar-header .brand .board {
|
|
168
|
-
|
|
169
|
-
word-break: break-all; line-height: 1.2;
|
|
170
|
-
display: flex; align-items: center; gap: 6px;
|
|
177
|
+
display: none !important;
|
|
171
178
|
}
|
|
172
179
|
.sidebar-header .brand .board .kind {
|
|
173
180
|
font-size: 9px; font-weight: 600; letter-spacing: 0.5px;
|