kingkont 0.7.80 → 0.7.81
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/settings.js +96 -6
- package/renderer/styles.css +47 -2
package/package.json
CHANGED
package/renderer/settings.js
CHANGED
|
@@ -52,6 +52,101 @@ $('settingsRegen').addEventListener('click', () => {
|
|
|
52
52
|
regenerateNode(node); // там редактируются ВСЕ поля (промпт, модель, голос)
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
+
// =================== Text-node body (с форматированием) ===================
|
|
56
|
+
// Шрифты — system-only, чтобы не тащить Google Fonts CDN (приложение
|
|
57
|
+
// должно работать офлайн). На macOS все четыре варианта выглядят отлично;
|
|
58
|
+
// на Windows/Linux fallback'и держат смысл (cursive/fantasy/serif/mono).
|
|
59
|
+
const TEXT_NODE_FONTS = [
|
|
60
|
+
{ id: 'default', label: 'Обычный' },
|
|
61
|
+
{ id: 'handwritten', label: 'Карандашом' },
|
|
62
|
+
{ id: 'marker', label: 'Краской' },
|
|
63
|
+
{ id: 'serif', label: 'С засечками' },
|
|
64
|
+
{ id: 'mono', label: 'Моноширинный' },
|
|
65
|
+
];
|
|
66
|
+
const TEXT_NODE_FONT_SIZES = [12, 14, 18, 24, 32, 48, 64];
|
|
67
|
+
const TEXT_NODE_DEFAULT_STYLE = { fontSize: 14, italic: false, fontFamily: 'default' };
|
|
68
|
+
|
|
69
|
+
function getTextNodeStyle(node) {
|
|
70
|
+
return { ...TEXT_NODE_DEFAULT_STYLE, ...(node.textStyle || {}) };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function applyTextNodeStyle(ta, style) {
|
|
74
|
+
ta.style.fontSize = (style.fontSize || 14) + 'px';
|
|
75
|
+
ta.style.fontStyle = style.italic ? 'italic' : 'normal';
|
|
76
|
+
ta.dataset.font = style.fontFamily || 'default';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function renderTextNodeBody(node, body) {
|
|
80
|
+
// Гарантируем что у ноды есть textStyle (без spread — реальный объект).
|
|
81
|
+
if (!node.textStyle) node.textStyle = { ...TEXT_NODE_DEFAULT_STYLE };
|
|
82
|
+
|
|
83
|
+
// Тулбар форматирования: шрифт, размер, italic.
|
|
84
|
+
const tb = document.createElement('div');
|
|
85
|
+
tb.className = 'text-toolbar';
|
|
86
|
+
// Не блокируем drag-mousedown по тулбару — но клики по контролам
|
|
87
|
+
// не должны таскать ноду. stopPropagation на mousedown решает это.
|
|
88
|
+
tb.addEventListener('mousedown', e => e.stopPropagation());
|
|
89
|
+
|
|
90
|
+
// Селект шрифта.
|
|
91
|
+
const fontSel = document.createElement('select');
|
|
92
|
+
fontSel.title = 'Шрифт';
|
|
93
|
+
for (const f of TEXT_NODE_FONTS) {
|
|
94
|
+
const o = document.createElement('option');
|
|
95
|
+
o.value = f.id;
|
|
96
|
+
o.textContent = f.label;
|
|
97
|
+
fontSel.appendChild(o);
|
|
98
|
+
}
|
|
99
|
+
fontSel.value = node.textStyle.fontFamily || 'default';
|
|
100
|
+
fontSel.addEventListener('change', () => {
|
|
101
|
+
node.textStyle.fontFamily = fontSel.value;
|
|
102
|
+
applyTextNodeStyle(ta, node.textStyle);
|
|
103
|
+
scheduleSave();
|
|
104
|
+
});
|
|
105
|
+
tb.appendChild(fontSel);
|
|
106
|
+
|
|
107
|
+
// Селект размера.
|
|
108
|
+
const sizeSel = document.createElement('select');
|
|
109
|
+
sizeSel.title = 'Размер шрифта';
|
|
110
|
+
for (const s of TEXT_NODE_FONT_SIZES) {
|
|
111
|
+
const o = document.createElement('option');
|
|
112
|
+
o.value = String(s);
|
|
113
|
+
o.textContent = s + 'px';
|
|
114
|
+
sizeSel.appendChild(o);
|
|
115
|
+
}
|
|
116
|
+
sizeSel.value = String(node.textStyle.fontSize || 14);
|
|
117
|
+
sizeSel.addEventListener('change', () => {
|
|
118
|
+
node.textStyle.fontSize = parseInt(sizeSel.value, 10) || 14;
|
|
119
|
+
applyTextNodeStyle(ta, node.textStyle);
|
|
120
|
+
scheduleSave();
|
|
121
|
+
});
|
|
122
|
+
tb.appendChild(sizeSel);
|
|
123
|
+
|
|
124
|
+
// Italic toggle.
|
|
125
|
+
const italicBtn = document.createElement('button');
|
|
126
|
+
italicBtn.className = 'tt-italic';
|
|
127
|
+
italicBtn.textContent = 'I';
|
|
128
|
+
italicBtn.title = 'Курсив';
|
|
129
|
+
if (node.textStyle.italic) italicBtn.classList.add('active');
|
|
130
|
+
italicBtn.addEventListener('click', e => {
|
|
131
|
+
e.stopPropagation();
|
|
132
|
+
node.textStyle.italic = !node.textStyle.italic;
|
|
133
|
+
italicBtn.classList.toggle('active', node.textStyle.italic);
|
|
134
|
+
applyTextNodeStyle(ta, node.textStyle);
|
|
135
|
+
scheduleSave();
|
|
136
|
+
});
|
|
137
|
+
tb.appendChild(italicBtn);
|
|
138
|
+
|
|
139
|
+
body.appendChild(tb);
|
|
140
|
+
|
|
141
|
+
const ta = document.createElement('textarea');
|
|
142
|
+
ta.value = node.text || '';
|
|
143
|
+
ta.placeholder = 'Текст ноды...';
|
|
144
|
+
applyTextNodeStyle(ta, node.textStyle);
|
|
145
|
+
ta.addEventListener('input', () => { node.text = ta.value; scheduleSave(); });
|
|
146
|
+
ta.addEventListener('mousedown', e => e.stopPropagation());
|
|
147
|
+
body.appendChild(ta);
|
|
148
|
+
}
|
|
149
|
+
|
|
55
150
|
async function renderNodeBody(node, body) {
|
|
56
151
|
body.innerHTML = '';
|
|
57
152
|
if (node.status === 'generating') {
|
|
@@ -186,12 +281,7 @@ async function renderNodeBody(node, body) {
|
|
|
186
281
|
}
|
|
187
282
|
|
|
188
283
|
if (node.type === 'text') {
|
|
189
|
-
|
|
190
|
-
ta.value = node.text || '';
|
|
191
|
-
ta.placeholder = 'Текст ноды...';
|
|
192
|
-
ta.addEventListener('input', () => { node.text = ta.value; scheduleSave(); });
|
|
193
|
-
ta.addEventListener('mousedown', e => e.stopPropagation());
|
|
194
|
-
body.appendChild(ta);
|
|
284
|
+
renderTextNodeBody(node, body);
|
|
195
285
|
} else if (['video','audio','image'].includes(node.type) && node.file) {
|
|
196
286
|
const nodeEl = body.closest('.node');
|
|
197
287
|
nodeEl?.classList.toggle('image-node', node.type === 'image');
|
package/renderer/styles.css
CHANGED
|
@@ -323,11 +323,56 @@
|
|
|
323
323
|
box-shadow: 0 0 0 2px #5aa8ff, 0 8px 24px rgba(90,168,255,0.4);
|
|
324
324
|
}
|
|
325
325
|
.node-body { flex: 1; min-height: 0; }
|
|
326
|
-
.node.text-node .node-body { padding: 0; display: flex; }
|
|
326
|
+
.node.text-node .node-body { padding: 0; display: flex; flex-direction: column; }
|
|
327
327
|
.node.text-node .node-body textarea {
|
|
328
|
-
width: 100%;
|
|
328
|
+
width: 100%; resize: none; min-height: 80px;
|
|
329
329
|
border: none; border-radius: 0; padding: 12px; flex: 1;
|
|
330
|
+
background: transparent; color: #eaeaea;
|
|
331
|
+
font-size: 14px; font-style: normal;
|
|
332
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
333
|
+
line-height: 1.4;
|
|
334
|
+
}
|
|
335
|
+
/* Декоративные шрифты для текстовых нод. */
|
|
336
|
+
.node.text-node .node-body textarea[data-font="handwritten"] {
|
|
337
|
+
/* «Карандашом» — рукописные шрифты, мягкие, наклонные. */
|
|
338
|
+
font-family: 'Caveat', 'Marker Felt', 'Bradley Hand', 'Snell Roundhand', cursive;
|
|
339
|
+
line-height: 1.2;
|
|
340
|
+
}
|
|
341
|
+
.node.text-node .node-body textarea[data-font="marker"] {
|
|
342
|
+
/* «Краской/маркером» — крупные мазки, plakat-стиль. */
|
|
343
|
+
font-family: 'Permanent Marker', 'Chalkduster', 'Marker Felt', 'Comic Sans MS', fantasy;
|
|
344
|
+
letter-spacing: 0.02em;
|
|
345
|
+
}
|
|
346
|
+
.node.text-node .node-body textarea[data-font="serif"] {
|
|
347
|
+
font-family: Georgia, 'Times New Roman', serif;
|
|
348
|
+
}
|
|
349
|
+
.node.text-node .node-body textarea[data-font="mono"] {
|
|
350
|
+
font-family: ui-monospace, 'SF Mono', Menlo, 'Courier New', monospace;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/* Тулбар форматирования — компактный, появляется на hover, чтобы не
|
|
354
|
+
отвлекать когда юзер просто читает. */
|
|
355
|
+
.node.text-node .text-toolbar {
|
|
356
|
+
display: flex; align-items: center; gap: 4px;
|
|
357
|
+
padding: 4px 6px; background: #181818; border-bottom: 1px solid #2a2a2a;
|
|
358
|
+
font-size: 11px; opacity: 0.35; transition: opacity 0.15s;
|
|
359
|
+
flex-shrink: 0;
|
|
330
360
|
}
|
|
361
|
+
.node.text-node:hover .text-toolbar,
|
|
362
|
+
.node.text-node .text-toolbar:focus-within { opacity: 1; }
|
|
363
|
+
.node.text-node .text-toolbar select,
|
|
364
|
+
.node.text-node .text-toolbar button {
|
|
365
|
+
background: #1e1e1e; border: 1px solid #383838; color: #ccc;
|
|
366
|
+
border-radius: 3px; padding: 2px 6px; font-size: 11px;
|
|
367
|
+
cursor: pointer; line-height: 1.2;
|
|
368
|
+
}
|
|
369
|
+
.node.text-node .text-toolbar select:hover,
|
|
370
|
+
.node.text-node .text-toolbar button:hover { background: #2c2c2c; color: #fff; }
|
|
371
|
+
.node.text-node .text-toolbar button.active {
|
|
372
|
+
background: #3a5a8a; color: #fff; border-color: #4a6a9a;
|
|
373
|
+
}
|
|
374
|
+
.node.text-node .text-toolbar .tt-spacer { flex: 1; }
|
|
375
|
+
.node.text-node .text-toolbar .tt-italic { font-style: italic; min-width: 22px; }
|
|
331
376
|
.node.video-node .node-body { padding: 0; overflow: hidden; border-radius: 0 0 8px 8px; }
|
|
332
377
|
.node.video-node .node-body video { width: 100%; height: auto; max-height: none; background: transparent; border-radius: 0; display: block; }
|
|
333
378
|
.audio-speed { display: flex; gap: 2px; margin-top: 6px; }
|