kingkont 0.14.5 → 0.14.7
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 +33 -6
- package/renderer/styles.css +15 -1
package/package.json
CHANGED
package/renderer/chat.js
CHANGED
|
@@ -99,9 +99,9 @@
|
|
|
99
99
|
},
|
|
100
100
|
|
|
101
101
|
add_node: {
|
|
102
|
-
description: 'Добавить ноду на текущую доску.
|
|
103
|
-
params: '{"type":"image|video|audio|text","subKind":"music|sfx|voice
|
|
104
|
-
async handler({ type, subKind, name, prompt, x, y, text, modelKey, durationMs, aspectRatio }) {
|
|
102
|
+
description: 'Добавить ноду на текущую доску. Типы: image, video, audio, text, label.\n - image/video/audio с prompt → draft, юзер потом запустит generation (или используй generate_node).\n - audio: subKind="music"|"sfx"|"voice" (дефолт voice/TTS).\n - aspectRatio: "16:9"|"9:16"|"1:1"|"3:4"|"4:3"|"21:9" — переопределяет дефолт сцены. Если юзер сказал "горизонтальная"→16:9, "вертикальная"→9:16, "квадрат"→1:1.\n - text: длинный markdown-блок (несколько абзацев). Передай text="...".\n - label: КОРОТКАЯ подпись поверх холста (заголовок, метка). Передай text="..." (1-3 слова обычно). Опционально textStyle={fontSize, fontFamily, italic}.',
|
|
103
|
+
params: '{"type":"image|video|audio|text|label","subKind":"music|sfx|voice","name":"<optional>","prompt":"<for image/video/audio>","x":<optional>,"y":<optional>,"text":"<for text/label>","textStyle":{"fontSize":<12|14|18|24|32|48|64|96>,"italic":<bool>,"fontFamily":"default|pencil|handwritten|brush|marker|display|elegant|rounded|serif|mono"},"modelKey":"<optional>","durationMs":<for music/sfx>,"aspectRatio":"<optional>"}',
|
|
104
|
+
async handler({ type, subKind, name, prompt, x, y, text, textStyle, modelKey, durationMs, aspectRatio }) {
|
|
105
105
|
if (!state.currentBoard) throw new Error('доска не выбрана');
|
|
106
106
|
if (!['image','video','audio','text','label'].includes(type)) throw new Error(`unknown type: ${type}`);
|
|
107
107
|
// Поиск незанятого места: пытаемся справа от последней ноды,
|
|
@@ -149,6 +149,14 @@
|
|
|
149
149
|
node.text = text;
|
|
150
150
|
// .md создастся при saveBoardMetadata.
|
|
151
151
|
}
|
|
152
|
+
if (type === 'label') {
|
|
153
|
+
node.text = (typeof text === 'string' && text.trim()) ? text : 'Подпись';
|
|
154
|
+
// textStyle: дефолт совпадает с UI-добавленной подписью.
|
|
155
|
+
// Юзерские поля переопределяют по одному (не replace целиком).
|
|
156
|
+
const defaultStyle = { fontSize: 32, italic: false, fontFamily: 'pencil' };
|
|
157
|
+
node.textStyle = { ...defaultStyle, ...(textStyle || {}) };
|
|
158
|
+
// Label — auto-size по тексту, width/height не задаём.
|
|
159
|
+
}
|
|
152
160
|
if (prompt && type !== 'text' && type !== 'label') {
|
|
153
161
|
node.status = 'draft';
|
|
154
162
|
node.generated = { rawPrompt: prompt, prompt, modelKey: modelKey || undefined };
|
|
@@ -816,7 +824,8 @@
|
|
|
816
824
|
const parts = [];
|
|
817
825
|
if (ctx.scene) parts.push({ key: 'scene', label: `🎬 ${ctx.scene.name}`, removable: false });
|
|
818
826
|
// Группируем выделенные ноды по type. Если 1 — показываем имя/id;
|
|
819
|
-
// если 2+ — «N картинок» с локализацией.
|
|
827
|
+
// если 2+ — «N картинок» с локализацией. Клик по чипу — снимает
|
|
828
|
+
// выделение с этой ноды (или со всех нод этого type для группы).
|
|
820
829
|
const byType = {};
|
|
821
830
|
for (const sel of ctx.selected) {
|
|
822
831
|
(byType[sel.type] = byType[sel.type] || []).push(sel);
|
|
@@ -825,10 +834,28 @@
|
|
|
825
834
|
const icon = type === 'image' ? '🖼' : type === 'video' ? '🎬' : type === 'audio' ? '🎙' : type === 'text' ? '📝' : '◉';
|
|
826
835
|
if (items.length === 1) {
|
|
827
836
|
const s = items[0];
|
|
828
|
-
parts.push({
|
|
837
|
+
parts.push({
|
|
838
|
+
key: 'sel:' + s.id,
|
|
839
|
+
label: `${icon} ${s.name || s.id.slice(0, 6)}`,
|
|
840
|
+
removable: true,
|
|
841
|
+
onRemove: () => {
|
|
842
|
+
state.selectedNodeIds.delete(s.id);
|
|
843
|
+
if (typeof renderSelection === 'function') renderSelection();
|
|
844
|
+
renderContextRow();
|
|
845
|
+
},
|
|
846
|
+
});
|
|
829
847
|
} else {
|
|
830
848
|
const noun = _typeForms[type] || ['нода','ноды','нод'];
|
|
831
|
-
parts.push({
|
|
849
|
+
parts.push({
|
|
850
|
+
key: 'sel-grp:' + type,
|
|
851
|
+
label: `${icon} ${items.length} ${_ru_plural(items.length, noun)}`,
|
|
852
|
+
removable: true,
|
|
853
|
+
onRemove: () => {
|
|
854
|
+
for (const it of items) state.selectedNodeIds.delete(it.id);
|
|
855
|
+
if (typeof renderSelection === 'function') renderSelection();
|
|
856
|
+
renderContextRow();
|
|
857
|
+
},
|
|
858
|
+
});
|
|
832
859
|
}
|
|
833
860
|
}
|
|
834
861
|
for (const a of ctx.attachments) {
|
package/renderer/styles.css
CHANGED
|
@@ -260,6 +260,10 @@
|
|
|
260
260
|
box-shadow: -8px 0 32px rgba(0,0,0,0.4);
|
|
261
261
|
/* --chat-font задаётся inline в chat.js (Cmd+/Cmd-). Дефолт 13px. */
|
|
262
262
|
font-size: var(--chat-font, 13px);
|
|
263
|
+
/* Защита от horizontal-overflow: дочерние flex-элементы (длинные
|
|
264
|
+
имена чипов, tool-args в pre) могли push'ать ширину — fixed
|
|
265
|
+
position спасал, но без этого max-width дочерних не работал. */
|
|
266
|
+
overflow-x: hidden; box-sizing: border-box;
|
|
263
267
|
}
|
|
264
268
|
.chat-panel.chat-drag::before {
|
|
265
269
|
content: '📎 Отпусти, чтобы прикрепить файл к чату';
|
|
@@ -274,6 +278,11 @@
|
|
|
274
278
|
display: flex; flex-wrap: wrap; gap: 4px;
|
|
275
279
|
padding: 6px 10px;
|
|
276
280
|
background: #1a1a1a; border-top: 1px solid #2a2a2a;
|
|
281
|
+
/* min-width:0 — без этого flex-children не «сжимаются», и длинная
|
|
282
|
+
строка чипов раздвигает родителя (chat-panel получает горизонтальный
|
|
283
|
+
overflow). С flex-wrap:wrap + min-width:0 чипы корректно
|
|
284
|
+
переносятся на следующую строку. */
|
|
285
|
+
min-width: 0; max-width: 100%; box-sizing: border-box;
|
|
277
286
|
}
|
|
278
287
|
.chat-context-row:empty { display: none; }
|
|
279
288
|
.chat-chip {
|
|
@@ -281,7 +290,12 @@
|
|
|
281
290
|
background: #232a36; border: 1px solid #2c3848;
|
|
282
291
|
color: #aac; font-size: 11px;
|
|
283
292
|
padding: 2px 8px; border-radius: 999px;
|
|
284
|
-
max-width: 200px
|
|
293
|
+
max-width: 100%; /* был 200px — длинные имена ужимались, но
|
|
294
|
+
если width контейнера меньше 200px, чип всё
|
|
295
|
+
ещё мог push'нуть строку. 100% относительно
|
|
296
|
+
.chat-context-row → гарантированно влезает. */
|
|
297
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
298
|
+
min-width: 0; /* чтобы flex-shrink работал */
|
|
285
299
|
}
|
|
286
300
|
.chat-chip-x {
|
|
287
301
|
background: transparent; border: none; color: #889;
|