kingkont 0.7.83 → 0.7.85
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/assets/fonts/NOTICE.md +1 -0
- package/assets/fonts/neucha-cyrillic-400.woff2 +0 -0
- package/assets/fonts/neucha-latin-400.woff2 +0 -0
- package/package.json +1 -1
- package/renderer/board.js +8 -2
- package/renderer/generate.js +3 -2
- package/renderer/settings.js +38 -1
- package/renderer/styles.css +87 -28
package/assets/fonts/NOTICE.md
CHANGED
|
@@ -14,6 +14,7 @@ beautiful Cyrillic-aware label nodes without needing a network connection.
|
|
|
14
14
|
| Comfortaa | Johan Aakerlund | <https://fonts.google.com/specimen/Comfortaa> |
|
|
15
15
|
| PT Serif | ParaType | <https://fonts.google.com/specimen/PT+Serif> |
|
|
16
16
|
| PT Mono | ParaType | <https://fonts.google.com/specimen/PT+Mono> |
|
|
17
|
+
| Neucha | Jovanny Lemonad | <https://fonts.google.com/specimen/Neucha> |
|
|
17
18
|
|
|
18
19
|
Files were downloaded from the @fontsource jsDelivr mirror in the
|
|
19
20
|
`cyrillic-400-normal` and `latin-400-normal` subsets.
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
package/renderer/board.js
CHANGED
|
@@ -1638,8 +1638,14 @@ async function createNodeEl(node) {
|
|
|
1638
1638
|
el.dataset.id = node.id;
|
|
1639
1639
|
el.style.left = node.x + 'px';
|
|
1640
1640
|
el.style.top = node.y + 'px';
|
|
1641
|
-
|
|
1642
|
-
|
|
1641
|
+
// Label-ноды всегда auto-размер (по тексту) — фиксированный width/height
|
|
1642
|
+
// создавал «невидимые боксы» вокруг текста, что ломало hit-testing когда
|
|
1643
|
+
// несколько label оказывались рядом (клик попадал в перекрывающий, а не
|
|
1644
|
+
// в видимый текст). Игнорируем сохранённые width/height для label-нод.
|
|
1645
|
+
if (node.type !== 'label') {
|
|
1646
|
+
if (node.width) el.style.width = node.width + 'px';
|
|
1647
|
+
if (node.height) el.style.height = node.height + 'px';
|
|
1648
|
+
}
|
|
1643
1649
|
|
|
1644
1650
|
const header = document.createElement('div');
|
|
1645
1651
|
header.className = 'node-header';
|
package/renderer/generate.js
CHANGED
|
@@ -211,9 +211,10 @@ async function addLabelAt(pos) {
|
|
|
211
211
|
id: crypto.randomUUID(),
|
|
212
212
|
type: 'label',
|
|
213
213
|
text: '',
|
|
214
|
-
textStyle: { fontSize: 32, italic: false, fontFamily: '
|
|
214
|
+
textStyle: { fontSize: 32, italic: false, fontFamily: 'pencil' },
|
|
215
215
|
x, y,
|
|
216
|
-
width
|
|
216
|
+
// width/height не задаём — label всегда auto-размер по тексту
|
|
217
|
+
// (см. createNodeEl: для type='label' inline-style не применяется).
|
|
217
218
|
};
|
|
218
219
|
state.currentBoard.metadata.nodes.push(node);
|
|
219
220
|
const el = await createNodeEl(node);
|
package/renderer/settings.js
CHANGED
|
@@ -66,7 +66,8 @@ $('settingsRegen').addEventListener('click', () => {
|
|
|
66
66
|
// сохранённые сцены. Новые красивые шрифты добавляем под новыми id.
|
|
67
67
|
const LABEL_FONTS = [
|
|
68
68
|
{ id: 'default', label: 'Обычный' },
|
|
69
|
-
{ id: '
|
|
69
|
+
{ id: 'pencil', label: 'Грифель' }, // Neucha — шероховатый карандашный
|
|
70
|
+
{ id: 'handwritten', label: 'Прописью' }, // Caveat — гладкий рукописный
|
|
70
71
|
{ id: 'brush', label: 'Скорописью' }, // Bad Script
|
|
71
72
|
{ id: 'marker', label: 'Кистью' }, // Pacifico
|
|
72
73
|
{ id: 'display', label: 'Декор' }, // Lobster
|
|
@@ -618,6 +619,42 @@ function attachResize(el, node, handle) {
|
|
|
618
619
|
const startX = e.clientX, startY = e.clientY;
|
|
619
620
|
const startW = el.offsetWidth, startH = el.offsetHeight;
|
|
620
621
|
|
|
622
|
+
// Label: ресайз-хендл МАСШТАБИРУЕТ шрифт, а не меняет размеры бокса.
|
|
623
|
+
// Бокс auto-sized по тексту, поэтому при увеличении fontSize он
|
|
624
|
+
// автоматически растёт. Используем диагональную дистанцию (max от dx/dy)
|
|
625
|
+
// — даёт интуитивный «оттянуть угол → больше».
|
|
626
|
+
if (node.type === 'label') {
|
|
627
|
+
if (!node.textStyle) node.textStyle = { fontSize: 32, italic: false, fontFamily: 'pencil' };
|
|
628
|
+
const startSize = node.textStyle.fontSize || 32;
|
|
629
|
+
const labelTextEl = el.querySelector('.label-text');
|
|
630
|
+
const sizeSel = el.querySelector('.label-toolbar select[title="Размер шрифта"]');
|
|
631
|
+
const onMoveL = ev => {
|
|
632
|
+
const dx = (ev.clientX - startX) / state.zoom;
|
|
633
|
+
const dy = (ev.clientY - startY) / state.zoom;
|
|
634
|
+
// Линейная зависимость: ~0.6px шрифта на пиксель диагонального drag.
|
|
635
|
+
// Берём «среднее с уклоном на больший» — чтобы юзер мог тянуть как
|
|
636
|
+
// вправо-вниз (увеличивать), так и влево-вверх (уменьшать).
|
|
637
|
+
const delta = (dx + dy) * 0.6;
|
|
638
|
+
const newSize = Math.max(8, Math.min(200, Math.round(startSize + delta)));
|
|
639
|
+
node.textStyle.fontSize = newSize;
|
|
640
|
+
if (labelTextEl) labelTextEl.style.fontSize = newSize + 'px';
|
|
641
|
+
// Синхронизируем dropdown в тулбаре, если есть подходящий option.
|
|
642
|
+
if (sizeSel) {
|
|
643
|
+
const has = Array.from(sizeSel.options).some(o => o.value === String(newSize));
|
|
644
|
+
if (has) sizeSel.value = String(newSize);
|
|
645
|
+
}
|
|
646
|
+
renderConnections();
|
|
647
|
+
};
|
|
648
|
+
const onUpL = () => {
|
|
649
|
+
document.removeEventListener('mousemove', onMoveL);
|
|
650
|
+
document.removeEventListener('mouseup', onUpL);
|
|
651
|
+
scheduleSave();
|
|
652
|
+
};
|
|
653
|
+
document.addEventListener('mousemove', onMoveL);
|
|
654
|
+
document.addEventListener('mouseup', onUpL);
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
|
|
621
658
|
// Aspect-lock для image/video: соблюдаем ratio оригинала. Берём nat-
|
|
622
659
|
// ural размеры media-элемента, считаем chrome (header + footer + padding)
|
|
623
660
|
// как разницу высоты ноды и высоты media. На этапе resize меняем ту ось,
|
package/renderer/styles.css
CHANGED
|
@@ -82,6 +82,16 @@
|
|
|
82
82
|
src: url('../assets/fonts/pt-mono-latin-400.woff2') format('woff2');
|
|
83
83
|
unicode-range: U+0000-00FF;
|
|
84
84
|
}
|
|
85
|
+
@font-face {
|
|
86
|
+
font-family: 'KK Neucha'; font-style: normal; font-weight: 400; font-display: block;
|
|
87
|
+
src: url('../assets/fonts/neucha-cyrillic-400.woff2') format('woff2');
|
|
88
|
+
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
|
89
|
+
}
|
|
90
|
+
@font-face {
|
|
91
|
+
font-family: 'KK Neucha'; font-style: normal; font-weight: 400; font-display: block;
|
|
92
|
+
src: url('../assets/fonts/neucha-latin-400.woff2') format('woff2');
|
|
93
|
+
unicode-range: U+0000-00FF;
|
|
94
|
+
}
|
|
85
95
|
|
|
86
96
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
87
97
|
html, body { height: 100%; }
|
|
@@ -415,48 +425,86 @@
|
|
|
415
425
|
}
|
|
416
426
|
|
|
417
427
|
/* === Label-node — плавающая текст-аннотация поверх холста ===
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
428
|
+
Auto-размер по тексту: .node автоматически сжимается до контента,
|
|
429
|
+
никаких «невидимых боксов» вокруг текста. Header / toolbar /
|
|
430
|
+
resize / anchor вынесены за пределы видимой области (absolute) и
|
|
431
|
+
показываются на hover. */
|
|
421
432
|
.node.label-node {
|
|
422
433
|
background: transparent;
|
|
423
434
|
border: none; box-shadow: none;
|
|
424
435
|
padding: 0;
|
|
436
|
+
overflow: visible; /* absolute-дети могут выйти за рамки */
|
|
437
|
+
width: auto !important; /* override любого inline-width из старых сцен */
|
|
438
|
+
height: auto !important;
|
|
439
|
+
min-width: 30px;
|
|
440
|
+
max-width: 800px; /* лимит чтобы не получить однострочного монстра */
|
|
441
|
+
display: block; /* отключаем flex-column — нам не нужно */
|
|
442
|
+
contain: none; /* отменяем content-visibility-оптимизацию */
|
|
443
|
+
contain-intrinsic-size: auto;
|
|
425
444
|
}
|
|
426
445
|
.node.label-node.selected {
|
|
427
446
|
outline: 1px dashed rgba(90,168,255,0.8);
|
|
428
447
|
outline-offset: 2px;
|
|
429
448
|
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
.node.label-node .
|
|
433
|
-
|
|
434
|
-
|
|
449
|
+
|
|
450
|
+
/* Header (drag-handle + имя + ×) — absolute над текстом, hover-only. */
|
|
451
|
+
.node.label-node .node-header {
|
|
452
|
+
position: absolute;
|
|
453
|
+
bottom: 100%; /* над label, без занятия layout */
|
|
454
|
+
left: 0; right: 0;
|
|
455
|
+
background: rgba(20,20,20,0.92);
|
|
456
|
+
border-radius: 4px 4px 0 0;
|
|
457
|
+
margin-bottom: 2px;
|
|
435
458
|
opacity: 0; transition: opacity 0.15s;
|
|
459
|
+
pointer-events: none; /* hover-only включаем ниже */
|
|
460
|
+
}
|
|
461
|
+
/* Footer для label не используется (нет сгенерированной информации) — скрываем. */
|
|
462
|
+
.node.label-node .node-footer { display: none; }
|
|
463
|
+
/* Anchor для label не нужен — это просто аннотация, не источник для генерации. */
|
|
464
|
+
.node.label-node .anchor { display: none; }
|
|
465
|
+
/* Resize-handle для label НЕ ресайзит ширину/высоту, а МАСШТАБИРУЕТ шрифт.
|
|
466
|
+
Видимый правый-нижний уголок, hover-only, чтобы не отвлекать. */
|
|
467
|
+
.node.label-node .resize-handle {
|
|
468
|
+
position: absolute;
|
|
469
|
+
right: -2px; bottom: -2px;
|
|
470
|
+
width: 14px; height: 14px;
|
|
471
|
+
background: rgba(90,168,255,0.85);
|
|
472
|
+
border: 1px solid rgba(255,255,255,0.4);
|
|
473
|
+
border-radius: 3px;
|
|
474
|
+
cursor: nwse-resize;
|
|
475
|
+
opacity: 0; transition: opacity 0.15s;
|
|
476
|
+
z-index: 11;
|
|
436
477
|
}
|
|
437
|
-
.node.label-node:hover .node-header,
|
|
438
|
-
.node.label-node:hover .node-footer,
|
|
439
478
|
.node.label-node:hover .resize-handle,
|
|
440
|
-
.node.label-node:
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
.node.label-node .node-header {
|
|
444
|
-
background: rgba(20,20,20,0.85);
|
|
445
|
-
border-radius: 6px 6px 0 0;
|
|
446
|
-
}
|
|
479
|
+
.node.label-node:focus-within .resize-handle { opacity: 1; }
|
|
480
|
+
|
|
481
|
+
/* Body — wrapper без визуального вклада. */
|
|
447
482
|
.node.label-node .node-body {
|
|
448
483
|
padding: 0; background: transparent;
|
|
449
|
-
display:
|
|
484
|
+
display: block; min-height: 0;
|
|
485
|
+
position: relative;
|
|
450
486
|
}
|
|
451
487
|
|
|
452
|
-
/* Тулбар форматирования
|
|
488
|
+
/* Тулбар форматирования — absolute под текстом, hover-only. */
|
|
453
489
|
.node.label-node .label-toolbar {
|
|
490
|
+
position: absolute;
|
|
491
|
+
top: 100%; /* под label */
|
|
492
|
+
left: 0;
|
|
493
|
+
margin-top: 4px;
|
|
454
494
|
display: flex; align-items: center; gap: 4px;
|
|
455
495
|
padding: 3px 6px; background: rgba(20,20,20,0.92);
|
|
456
|
-
border-radius: 4px;
|
|
496
|
+
border-radius: 4px;
|
|
457
497
|
font-size: 11px; flex-shrink: 0;
|
|
458
|
-
|
|
459
|
-
pointer-events:
|
|
498
|
+
opacity: 0; transition: opacity 0.15s;
|
|
499
|
+
pointer-events: none;
|
|
500
|
+
white-space: nowrap;
|
|
501
|
+
z-index: 10;
|
|
502
|
+
}
|
|
503
|
+
.node.label-node:hover .node-header,
|
|
504
|
+
.node.label-node:hover .label-toolbar,
|
|
505
|
+
.node.label-node:focus-within .node-header,
|
|
506
|
+
.node.label-node:focus-within .label-toolbar {
|
|
507
|
+
opacity: 1; pointer-events: auto;
|
|
460
508
|
}
|
|
461
509
|
.node.label-node .label-toolbar select,
|
|
462
510
|
.node.label-node .label-toolbar button {
|
|
@@ -471,15 +519,23 @@
|
|
|
471
519
|
}
|
|
472
520
|
.node.label-node .label-toolbar .tt-italic { font-style: italic; min-width: 22px; }
|
|
473
521
|
|
|
474
|
-
/* Сам
|
|
475
|
-
|
|
522
|
+
/* Сам текст — auto-ширина по контенту, до max-width.
|
|
523
|
+
line-height: 1.5 — чтобы descender'ы рукописных шрифтов (Caveat g/p/y,
|
|
524
|
+
Pacifico, Bad Script) не клипались по нижнему краю. padding 8px вверху
|
|
525
|
+
и внизу — на случай если у конкретной семьи descender лезет ещё дальше. */
|
|
476
526
|
.node.label-node .label-text {
|
|
477
|
-
|
|
478
|
-
|
|
527
|
+
display: inline-block;
|
|
528
|
+
padding: 8px 10px;
|
|
529
|
+
color: #eaeaea; line-height: 1.5;
|
|
479
530
|
word-break: break-word; white-space: pre-wrap;
|
|
480
531
|
outline: none; cursor: text;
|
|
481
532
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
482
533
|
text-shadow: 0 1px 2px rgba(0,0,0,0.6);
|
|
534
|
+
min-width: 60px;
|
|
535
|
+
max-width: 100%;
|
|
536
|
+
/* min-height в em — гарантирует что пустая нода (только placeholder)
|
|
537
|
+
всё равно имеет высоту хотя бы одной строки выбранного шрифта. */
|
|
538
|
+
min-height: 1.5em;
|
|
483
539
|
}
|
|
484
540
|
.node.label-node .label-text:empty::before {
|
|
485
541
|
content: attr(data-placeholder);
|
|
@@ -487,10 +543,13 @@
|
|
|
487
543
|
}
|
|
488
544
|
/* === Label-шрифты ===
|
|
489
545
|
Все варианты — bundled woff2 (см. assets/fonts/), кириллица поддержана. */
|
|
546
|
+
.node.label-node .label-text[data-font="pencil"] {
|
|
547
|
+
/* Грифель — шероховатый «как написано карандашом» (Neucha). */
|
|
548
|
+
font-family: 'KK Neucha', 'Neucha', 'Marker Felt', 'Comic Sans MS', cursive;
|
|
549
|
+
}
|
|
490
550
|
.node.label-node .label-text[data-font="handwritten"] {
|
|
491
|
-
/*
|
|
551
|
+
/* Прописью — гладкий рукописный (Caveat). */
|
|
492
552
|
font-family: 'KK Caveat', 'Caveat', 'Marker Felt', cursive;
|
|
493
|
-
line-height: 1.1;
|
|
494
553
|
}
|
|
495
554
|
.node.label-node .label-text[data-font="brush"] {
|
|
496
555
|
/* Скорописью — каллиграфия в русском стиле (Bad Script). */
|