kingkont 0.10.0 → 0.10.2
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 +7 -2
- package/renderer/chat.js +25 -11
- package/renderer/cloudProjects.js +56 -1
- package/renderer/styles.css +2 -2
package/package.json
CHANGED
package/renderer/board.js
CHANGED
|
@@ -213,8 +213,10 @@ async function renderWelcomeIdentity() {
|
|
|
213
213
|
let status = null;
|
|
214
214
|
try { status = await window.appChatium?.status?.(); } catch {}
|
|
215
215
|
if (status?.connected) {
|
|
216
|
-
// Имя: предпочитаем
|
|
217
|
-
|
|
216
|
+
// Имя: предпочитаем email (наиболее узнаваемый), затем login/name,
|
|
217
|
+
// в крайнем случае — userId. Subtitle = userId-обрезок если он
|
|
218
|
+
// отличается от показываемого имени.
|
|
219
|
+
const name = status.email || status.login || status.fullName || status.name || status.userId || 'KingKont';
|
|
218
220
|
const sub = status.userId && status.userId !== name ? `· ${status.userId.slice(0, 8)}` : '';
|
|
219
221
|
wrap.innerHTML = `
|
|
220
222
|
<span style="color:#5c5; font-size:13px; line-height:1;">●</span>
|
|
@@ -949,6 +951,9 @@ async function closeProject() {
|
|
|
949
951
|
// Помечаем что юзер вышел явно — на следующем старте autoload пропускается.
|
|
950
952
|
// Cmd+R после close = welcome, а не реоткрытие.
|
|
951
953
|
try { localStorage.setItem('welcomeOnNextStart', '1'); } catch {}
|
|
954
|
+
// Чат привязан к одному проекту — чистим историю при выходе чтобы
|
|
955
|
+
// не путал контекст следующего открытого проекта.
|
|
956
|
+
if (window.kingChat?.clear) window.kingChat.clear();
|
|
952
957
|
stopExternalWatcher();
|
|
953
958
|
// Сбрасываем UI таймлайна/превью — иначе при возврате через welcome
|
|
954
959
|
// в новый проект остаётся фрейм/дорожки прошлого.
|
package/renderer/chat.js
CHANGED
|
@@ -225,6 +225,7 @@
|
|
|
225
225
|
// ============== TOOL-CALL PARSER ==============
|
|
226
226
|
function parseToolCalls(text) {
|
|
227
227
|
const out = [];
|
|
228
|
+
if (typeof text !== 'string' || !text) return out;
|
|
228
229
|
const re = /<tool>\s*([\s\S]*?)\s*<\/tool>/g;
|
|
229
230
|
let m;
|
|
230
231
|
while ((m = re.exec(text)) !== null) {
|
|
@@ -238,6 +239,7 @@
|
|
|
238
239
|
return out;
|
|
239
240
|
}
|
|
240
241
|
function stripToolCalls(text) {
|
|
242
|
+
if (typeof text !== 'string') return '';
|
|
241
243
|
return text.replace(/<tool>[\s\S]*?<\/tool>/g, '').trim();
|
|
242
244
|
}
|
|
243
245
|
|
|
@@ -293,29 +295,41 @@
|
|
|
293
295
|
|
|
294
296
|
// ============== LLM call + tool-loop ==============
|
|
295
297
|
async function callLLM(messages, system) {
|
|
298
|
+
const body = {
|
|
299
|
+
messages, system,
|
|
300
|
+
model: 'anthropic/claude-sonnet-4.6',
|
|
301
|
+
};
|
|
302
|
+
console.log('[chat] → POST /api/text', { messages: messages.length, hasSystem: !!system });
|
|
296
303
|
const r = await fetch('/api/text', {
|
|
297
304
|
method: 'POST',
|
|
298
305
|
headers: { 'Content-Type': 'application/json' },
|
|
299
|
-
body: JSON.stringify(
|
|
300
|
-
messages, system,
|
|
301
|
-
model: 'anthropic/claude-sonnet-4.6',
|
|
302
|
-
}),
|
|
306
|
+
body: JSON.stringify(body),
|
|
303
307
|
});
|
|
308
|
+
const respText = await r.text();
|
|
309
|
+
let d; try { d = JSON.parse(respText); } catch { d = { _raw: respText }; }
|
|
304
310
|
if (!r.ok) {
|
|
305
|
-
|
|
306
|
-
throw new Error(
|
|
311
|
+
console.error('[chat] /api/text failed', r.status, d);
|
|
312
|
+
throw new Error(d?.error || d?._raw?.slice(0, 200) || `HTTP ${r.status}`);
|
|
307
313
|
}
|
|
308
|
-
|
|
309
|
-
return d
|
|
314
|
+
console.log('[chat] ← /api/text ok', { textLen: (d?.text || '').length });
|
|
315
|
+
return d?.text || '';
|
|
310
316
|
}
|
|
311
317
|
|
|
312
|
-
// Превращает internal history → массив для /api/text.
|
|
313
|
-
//
|
|
318
|
+
// Превращает internal history → массив для /api/text. Используем
|
|
319
|
+
// Anthropic-формат content-blocks (`[{type:'text', text:...}]`), потому
|
|
320
|
+
// что Chatium-side @start/sdk требует именно его — со строкой content
|
|
321
|
+
// падает «Cannot read properties of undefined (reading 'length')».
|
|
322
|
+
// OpenRouter этот формат тоже принимает (универсальный путь).
|
|
314
323
|
function historyToMessages() {
|
|
315
324
|
const out = [];
|
|
316
325
|
for (const m of history) {
|
|
317
326
|
if (m.role === 'system') continue;
|
|
318
|
-
|
|
327
|
+
const content = String(m.content || '');
|
|
328
|
+
if (!content) continue; // Anthropic не любит пустые messages
|
|
329
|
+
out.push({
|
|
330
|
+
role: m.role,
|
|
331
|
+
content: [{ type: 'text', text: content }],
|
|
332
|
+
});
|
|
319
333
|
}
|
|
320
334
|
return out;
|
|
321
335
|
}
|
|
@@ -196,7 +196,46 @@
|
|
|
196
196
|
async function openCloudProject(projectId, suggestedName, opts = {}) {
|
|
197
197
|
const s = await getSettings();
|
|
198
198
|
if (!isLoggedIn(s)) { alert('Войдите в KingKont'); return; }
|
|
199
|
-
|
|
199
|
+
|
|
200
|
+
// 0. Если этот cloud-проект ранее был сохранён ИЗ папочного — открываем
|
|
201
|
+
// оригинальную папку (с уже скачанными файлами) и не качаем ничего.
|
|
202
|
+
// Mapping cloudFolder:<projectId> → folderName ставит saveCloudProject.
|
|
203
|
+
// Папка должна быть granted в FSAH (юзер раз дал — handle живёт в IDB).
|
|
204
|
+
if (!opts.forceRefresh) {
|
|
205
|
+
try {
|
|
206
|
+
const folderName = await idbGet(`cloudFolder:${projectId}`);
|
|
207
|
+
if (folderName) {
|
|
208
|
+
const folderHandle = await idbGet(`handle:${folderName}`);
|
|
209
|
+
if (folderHandle) {
|
|
210
|
+
// Verify permission still granted (без user-gesture не запрашиваем).
|
|
211
|
+
let g = 'prompt';
|
|
212
|
+
try { g = await folderHandle.queryPermission({ mode: 'readwrite' }); } catch {}
|
|
213
|
+
if (g === 'granted') {
|
|
214
|
+
// Дополнительно проверим что в папке лежит meta с тем же cloudProjectId
|
|
215
|
+
// (юзер мог перенести/изменить — тогда fallback на download).
|
|
216
|
+
try {
|
|
217
|
+
const metaFh = await folderHandle.getFileHandle('.kingkont-meta.json');
|
|
218
|
+
const metaTxt = await (await metaFh.getFile()).text();
|
|
219
|
+
const meta = JSON.parse(metaTxt);
|
|
220
|
+
if (meta.cloudProjectId === projectId) {
|
|
221
|
+
state.cloudProjectId = projectId;
|
|
222
|
+
state.cloudDirty = false;
|
|
223
|
+
touchCloudOpened(projectId);
|
|
224
|
+
await openFilm(folderHandle);
|
|
225
|
+
setCloudButtonsVisibility();
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
} catch {}
|
|
229
|
+
// Permission granted но meta не совпала — обнуляем mapping
|
|
230
|
+
// (idbSet(null) — простой способ без отдельного idbDel).
|
|
231
|
+
try { await idbSet(`cloudFolder:${projectId}`, null); } catch {}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
} catch {}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// 1. Проверяем синхронизирован ли уже в userData/cloud-projects/<id>/.
|
|
200
239
|
if (window.cloudFs) {
|
|
201
240
|
try {
|
|
202
241
|
const metaText = await window.cloudFs.readText(projectId, '.kingkont-meta.json');
|
|
@@ -489,6 +528,22 @@
|
|
|
489
528
|
});
|
|
490
529
|
}
|
|
491
530
|
|
|
531
|
+
// 5) Если это ПАПОЧНЫЙ проект (не cloud-shim), запоминаем mapping
|
|
532
|
+
// cloudProjectId → folderName в IDB. Когда юзер позже откроет
|
|
533
|
+
// этот же cloud-проект из welcome, openCloudProject подхватит
|
|
534
|
+
// оригинальную папку (с уже скачанными файлами) и НЕ будет
|
|
535
|
+
// качать всё заново. Mapping живёт в той же IDB-схеме что
|
|
536
|
+
// handle:<name>: cloudFolder:<projectId> = name.
|
|
537
|
+
if (!window.cloudFsShim?.isCloudHandle?.(state.filmHandle)) {
|
|
538
|
+
try {
|
|
539
|
+
await idbSet(`cloudFolder:${projectId}`, state.filmHandle.name);
|
|
540
|
+
// Также запоминаем cloudProjectId на текущей сессии — чтобы
|
|
541
|
+
// следующий save без перезахода не ловил «папочный → новый cloud».
|
|
542
|
+
state.cloudProjectId = projectId;
|
|
543
|
+
if (window.cloudProjects?.setVisibility) window.cloudProjects.setVisibility();
|
|
544
|
+
} catch {}
|
|
545
|
+
}
|
|
546
|
+
|
|
492
547
|
PROGRESS.hide();
|
|
493
548
|
markClean();
|
|
494
549
|
const stats = reused
|
package/renderer/styles.css
CHANGED
|
@@ -243,8 +243,8 @@
|
|
|
243
243
|
.welcome-status-identity button:hover { background: rgba(255,255,255,0.06); color: #cde; }
|
|
244
244
|
.welcome-status-balances {
|
|
245
245
|
pointer-events: auto;
|
|
246
|
-
display: flex; flex-direction:
|
|
247
|
-
|
|
246
|
+
display: flex; flex-direction: column; gap: 6px;
|
|
247
|
+
align-items: flex-end;
|
|
248
248
|
}
|
|
249
249
|
/* Используем те же .balance-info pill'ы что и в sidebar-footer'е. */
|
|
250
250
|
|