kingkont 0.11.1 → 0.11.3
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/chat.js +45 -9
- package/renderer/styles.css +7 -7
- package/renderer/templates.js +22 -1
package/package.json
CHANGED
package/renderer/chat.js
CHANGED
|
@@ -269,9 +269,13 @@
|
|
|
269
269
|
// <tool>JSON</tool> — наш command-protocol (модель так зовёт tools)
|
|
270
270
|
// <tool_result>JSON</tool_result> — модель иногда его галюцинирует
|
|
271
271
|
// в outputе, копируя format из user-msg.
|
|
272
|
+
// После стрипа нормализуем whitespace — иначе остаются «дыры» и
|
|
273
|
+
// подряд идущие space'ы (3+ → 1, blank lines 3+ → 2).
|
|
272
274
|
return text
|
|
273
275
|
.replace(/<tool>[\s\S]*?<\/tool>/g, '')
|
|
274
276
|
.replace(/<tool_result>[\s\S]*?<\/tool_result>/g, '')
|
|
277
|
+
.replace(/[ \t]{2,}/g, ' ')
|
|
278
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
275
279
|
.trim();
|
|
276
280
|
}
|
|
277
281
|
|
|
@@ -518,26 +522,42 @@
|
|
|
518
522
|
for (const m of history) {
|
|
519
523
|
if (m.role === 'system') continue;
|
|
520
524
|
if (m.role === 'user' && m.content?.startsWith('<tool_result>')) continue; // системный turn
|
|
525
|
+
// Пустой assistant без tools — не рендерим (бывает при streaming/parse-fail).
|
|
526
|
+
const hasContent = !!(m.content && m.content.trim());
|
|
527
|
+
const hasTools = Array.isArray(m.tools) && m.tools.length;
|
|
528
|
+
if (!hasContent && !hasTools) continue;
|
|
521
529
|
const div = document.createElement('div');
|
|
522
530
|
div.className = 'chat-msg chat-msg-' + m.role;
|
|
523
531
|
const lbl = document.createElement('div');
|
|
524
532
|
lbl.className = 'chat-msg-role';
|
|
525
533
|
lbl.textContent = m.role === 'user' ? 'Вы' : 'Claude';
|
|
526
534
|
div.appendChild(lbl);
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
535
|
+
if (hasContent) {
|
|
536
|
+
const body = document.createElement('div');
|
|
537
|
+
body.className = 'chat-msg-body';
|
|
538
|
+
body.textContent = m.content;
|
|
539
|
+
div.appendChild(body);
|
|
540
|
+
}
|
|
541
|
+
if (hasTools) {
|
|
532
542
|
for (const tc of m.tools) {
|
|
533
543
|
const t = document.createElement('details');
|
|
534
544
|
t.className = 'chat-tool';
|
|
535
545
|
const sum = document.createElement('summary');
|
|
536
|
-
|
|
546
|
+
// Компактный summary: имя tool + ключевой arg (если короткий) + статус.
|
|
547
|
+
const argHint = _argHint(tc.args);
|
|
548
|
+
const status = tc._error ? ' ⚠' : tc._ok ? ' ✓' : '';
|
|
549
|
+
sum.textContent = `🔧 ${tc.name}${argHint}${status}`;
|
|
537
550
|
t.appendChild(sum);
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
551
|
+
// pre добавляем только если есть что показать (args/result/error не пусты).
|
|
552
|
+
const dump = {};
|
|
553
|
+
if (tc.args && Object.keys(tc.args).length) dump.args = tc.args;
|
|
554
|
+
if (tc.result !== undefined) dump.result = tc.result;
|
|
555
|
+
if (tc._error) dump.error = tc._error;
|
|
556
|
+
if (Object.keys(dump).length) {
|
|
557
|
+
const pre = document.createElement('pre');
|
|
558
|
+
pre.textContent = JSON.stringify(dump, null, 2);
|
|
559
|
+
t.appendChild(pre);
|
|
560
|
+
}
|
|
541
561
|
div.appendChild(t);
|
|
542
562
|
}
|
|
543
563
|
}
|
|
@@ -545,6 +565,22 @@
|
|
|
545
565
|
}
|
|
546
566
|
list.scrollTop = list.scrollHeight;
|
|
547
567
|
}
|
|
568
|
+
|
|
569
|
+
// Короткая подсказка про args для summary tool-блока.
|
|
570
|
+
// 'add_node({type:image, name:"..."})' — без полного JSON.
|
|
571
|
+
function _argHint(args) {
|
|
572
|
+
if (!args || typeof args !== 'object') return '';
|
|
573
|
+
const keys = Object.keys(args);
|
|
574
|
+
if (!keys.length) return '';
|
|
575
|
+
const items = [];
|
|
576
|
+
for (const k of keys) {
|
|
577
|
+
const v = args[k];
|
|
578
|
+
let s = typeof v === 'string' ? `"${v.length > 30 ? v.slice(0, 30) + '…' : v}"` : String(v);
|
|
579
|
+
items.push(`${k}:${s}`);
|
|
580
|
+
if (items.join(',').length > 60) break;
|
|
581
|
+
}
|
|
582
|
+
return ` (${items.join(', ')})`;
|
|
583
|
+
}
|
|
548
584
|
// Перебиндим
|
|
549
585
|
renderHistory = renderHistoryFiltered;
|
|
550
586
|
|
package/renderer/styles.css
CHANGED
|
@@ -269,13 +269,13 @@
|
|
|
269
269
|
}
|
|
270
270
|
.chat-header button:hover { background: #2a2a2a; color: #fff; }
|
|
271
271
|
.chat-list {
|
|
272
|
-
flex: 1; overflow-y: auto; padding:
|
|
273
|
-
display: flex; flex-direction: column; gap:
|
|
272
|
+
flex: 1; overflow-y: auto; padding: 10px;
|
|
273
|
+
display: flex; flex-direction: column; gap: 6px;
|
|
274
274
|
}
|
|
275
275
|
.chat-msg {
|
|
276
|
-
display: flex; flex-direction: column; gap:
|
|
277
|
-
padding: 10px
|
|
278
|
-
font-size: 13px; line-height: 1.
|
|
276
|
+
display: flex; flex-direction: column; gap: 2px;
|
|
277
|
+
padding: 6px 10px; border-radius: 6px;
|
|
278
|
+
font-size: 13px; line-height: 1.45;
|
|
279
279
|
}
|
|
280
280
|
.chat-msg-user { background: #1f2a3a; border: 1px solid #2a3a4a; }
|
|
281
281
|
.chat-msg-assistant { background: #1f1f1f; border: 1px solid #2a2a2a; }
|
|
@@ -289,8 +289,8 @@
|
|
|
289
289
|
color: #e0e0e0; white-space: pre-wrap; word-break: break-word;
|
|
290
290
|
}
|
|
291
291
|
.chat-tool {
|
|
292
|
-
margin-top:
|
|
293
|
-
border-radius:
|
|
292
|
+
margin-top: 2px; background: #0e0e0e; border: 1px solid #2a2a2a;
|
|
293
|
+
border-radius: 3px; padding: 2px 6px; font-size: 11px;
|
|
294
294
|
}
|
|
295
295
|
.chat-tool summary {
|
|
296
296
|
cursor: pointer; color: #aac; outline: none;
|
package/renderer/templates.js
CHANGED
|
@@ -599,7 +599,7 @@ async function openProjectTemplate(templateId, suggestedName) {
|
|
|
599
599
|
'Имя нового проекта:',
|
|
600
600
|
suggestedName || 'Из шаблона',
|
|
601
601
|
suggestedName || '',
|
|
602
|
-
{ okText: 'Создать
|
|
602
|
+
{ okText: 'Создать проект' },
|
|
603
603
|
);
|
|
604
604
|
if (!projectName) return;
|
|
605
605
|
|
|
@@ -662,6 +662,27 @@ async function openProjectTemplate(templateId, suggestedName) {
|
|
|
662
662
|
} else {
|
|
663
663
|
throw new Error('cloudProjects.open недоступен');
|
|
664
664
|
}
|
|
665
|
+
|
|
666
|
+
// Auto-select первую сцену (или первый character/location если сцен нет).
|
|
667
|
+
// Юзер ожидает что после открытия проекта-из-шаблона он сразу видит
|
|
668
|
+
// содержимое, а не пустой холст.
|
|
669
|
+
try {
|
|
670
|
+
// Приоритет: episode → character → location.
|
|
671
|
+
const order = ['episode', 'character', 'location'];
|
|
672
|
+
const firstByOrder = order
|
|
673
|
+
.map(k => cloudBoards.find(b => b.kind === k))
|
|
674
|
+
.find(Boolean);
|
|
675
|
+
const target = firstByOrder || cloudBoards[0];
|
|
676
|
+
if (target && state.filmHandle) {
|
|
677
|
+
const list = target.kind === 'character' ? await listCharacters(state.filmHandle)
|
|
678
|
+
: target.kind === 'location' ? await listLocations(state.filmHandle)
|
|
679
|
+
: await listEpisodes(state.filmHandle);
|
|
680
|
+
const found = list.find(x => x.name === target.name);
|
|
681
|
+
if (found) await selectBoard({ kind: target.kind, ...found });
|
|
682
|
+
}
|
|
683
|
+
} catch (e) {
|
|
684
|
+
console.warn('auto-select first board failed:', e?.message || e);
|
|
685
|
+
}
|
|
665
686
|
} catch (e) {
|
|
666
687
|
TPL_PROGRESS.hide();
|
|
667
688
|
tplStatus('Ошибка открытия проекта: ' + e.message, true);
|