kingkont 0.7.93 → 0.7.95
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 +39 -1
- package/renderer/styles.css +15 -3
package/package.json
CHANGED
package/renderer/settings.js
CHANGED
|
@@ -396,10 +396,22 @@ async function renderNodeBody(node, body) {
|
|
|
396
396
|
media = document.createElement('img');
|
|
397
397
|
media.src = url; media.alt = node.file; media.draggable = false;
|
|
398
398
|
media.decoding = 'async';
|
|
399
|
+
// Auto-fit высоты ноды под aspect картинки. Иначе после генерации
|
|
400
|
+
// (где node.width/height заморожены до прихода файла) или после
|
|
401
|
+
// замены картинки через history новый <img> с object-fit:contain
|
|
402
|
+
// оставляет пустое место сверху/снизу — node.height не совпадает
|
|
403
|
+
// с display-height image'а.
|
|
404
|
+
media.addEventListener('load', () => fitNodeHeightToMedia(node, nodeEl, media), { once: true });
|
|
399
405
|
} else {
|
|
400
406
|
media = document.createElement(node.type);
|
|
401
407
|
media.src = url;
|
|
402
|
-
if (node.type === 'video')
|
|
408
|
+
if (node.type === 'video') {
|
|
409
|
+
media.controls = true;
|
|
410
|
+
// То же для видео — но через loadedmetadata, который у preload:none
|
|
411
|
+
// выстрелит при первой попытке play. Когда юзер запустит — нода
|
|
412
|
+
// подстроится под реальный aspect.
|
|
413
|
+
media.addEventListener('loadedmetadata', () => fitNodeHeightToMedia(node, nodeEl, media), { once: true });
|
|
414
|
+
}
|
|
403
415
|
// preload="none" — заголовки video/audio НЕ читаются пока юзер не запустит play.
|
|
404
416
|
// Раньше "metadata" грузил chunk для duration/dimensions с каждой ноды на selectBoard.
|
|
405
417
|
media.preload = 'none';
|
|
@@ -488,6 +500,32 @@ async function renderNodeBody(node, body) {
|
|
|
488
500
|
}
|
|
489
501
|
}
|
|
490
502
|
|
|
503
|
+
// Подгоняет node.height под natural-aspect медиа (image/video). Сохраняет
|
|
504
|
+
// текущую node.width, пересчитывает height = width * (natH/natW) + chrome.
|
|
505
|
+
// Без этого после генерации (или history-undo на ноду с другим aspect'ом)
|
|
506
|
+
// нода оставалась прежнего размера, и <img width:100%; height:auto> не
|
|
507
|
+
// заполнял её по высоте → пустое место снизу.
|
|
508
|
+
function fitNodeHeightToMedia(node, nodeEl, mediaEl) {
|
|
509
|
+
if (!nodeEl || !mediaEl) return;
|
|
510
|
+
const natW = mediaEl.naturalWidth || mediaEl.videoWidth || 0;
|
|
511
|
+
const natH = mediaEl.naturalHeight || mediaEl.videoHeight || 0;
|
|
512
|
+
if (!natW || !natH) return;
|
|
513
|
+
// Chrome — header + footer (если есть). image-node padding=0, ничего не отъедает.
|
|
514
|
+
const headerH = nodeEl.querySelector('.node-header')?.offsetHeight || 0;
|
|
515
|
+
const footerH = nodeEl.querySelector('.node-footer')?.offsetHeight || 0;
|
|
516
|
+
const chromeH = headerH + footerH;
|
|
517
|
+
// Берём актуальную width — node.width если задано, иначе текущий offsetWidth.
|
|
518
|
+
const w = node.width || nodeEl.offsetWidth;
|
|
519
|
+
if (!w) return;
|
|
520
|
+
const newH = Math.round(w * (natH / natW) + chromeH);
|
|
521
|
+
// 2px tolerance — не шумим в save/connections при суб-пиксельных колебаниях.
|
|
522
|
+
if (Math.abs(newH - (node.height || 0)) <= 2) return;
|
|
523
|
+
node.height = newH;
|
|
524
|
+
nodeEl.style.height = newH + 'px';
|
|
525
|
+
if (typeof renderConnections === 'function') renderConnections();
|
|
526
|
+
scheduleSave();
|
|
527
|
+
}
|
|
528
|
+
|
|
491
529
|
function attachAnchor(node, el, anchor) {
|
|
492
530
|
anchor.addEventListener('mousedown', e => {
|
|
493
531
|
e.preventDefault();
|
package/renderer/styles.css
CHANGED
|
@@ -700,15 +700,27 @@
|
|
|
700
700
|
}
|
|
701
701
|
.add-menu button:hover { background: #2c2c2c; }
|
|
702
702
|
|
|
703
|
-
/* === Fullscreen viewer ===
|
|
703
|
+
/* === Fullscreen viewer ===
|
|
704
|
+
.fs-modal — фон + контейнер для absolute-позиционированных контролов
|
|
705
|
+
(× close, ← prev, → next).
|
|
706
|
+
.fs-stage — absolute inset:0 (гарантированный fill viewport'а), flex-
|
|
707
|
+
центровка картинки. Раньше .fs-modal был flex-row с align-items:center,
|
|
708
|
+
и .fs-stage с height:100% не растягивался на cross-axis (брал NATURAL
|
|
709
|
+
высоту), → картинка центрировалась не в полный viewport → казалось
|
|
710
|
+
что внизу куча пустого места. */
|
|
704
711
|
.fs-modal {
|
|
705
712
|
position: fixed; inset: 0; background: rgba(0,0,0,0.95);
|
|
706
|
-
z-index: 1000;
|
|
713
|
+
z-index: 1000;
|
|
707
714
|
}
|
|
708
715
|
.fs-modal.hidden { display: none; }
|
|
709
|
-
.fs-stage {
|
|
716
|
+
.fs-stage {
|
|
717
|
+
position: absolute; inset: 0;
|
|
718
|
+
display: flex; align-items: center; justify-content: center;
|
|
719
|
+
padding: 16px 64px; /* по бокам — место под nav-кнопки */
|
|
720
|
+
}
|
|
710
721
|
.fs-stage img, .fs-stage video {
|
|
711
722
|
max-width: 100%; max-height: 100%; object-fit: contain;
|
|
723
|
+
display: block;
|
|
712
724
|
}
|
|
713
725
|
.fs-modal #fsClose {
|
|
714
726
|
position: absolute; top: 16px; right: 16px;
|