diff --git a/1.jpg b/1.jpg
new file mode 100644
index 0000000..214930b
Binary files /dev/null and b/1.jpg differ
diff --git a/2.jpg b/2.jpg
new file mode 100644
index 0000000..c5ebfdf
Binary files /dev/null and b/2.jpg differ
diff --git a/@shared/imagesloaded.pkgd.min.js b/@shared/imagesloaded.pkgd.min.js
new file mode 100644
index 0000000..c10ec1c
--- /dev/null
+++ b/@shared/imagesloaded.pkgd.min.js
@@ -0,0 +1,12 @@
+/*!
+ * imagesLoaded PACKAGED v5.0.0
+ * JavaScript is all like "You images are done yet or what?"
+ * MIT License
+ */
+!function(t,e){"object"==typeof module&&module.exports?module.exports=e():t.EvEmitter=e()}("undefined"!=typeof window?window:this,(function(){function t(){}let e=t.prototype;return e.on=function(t,e){if(!t||!e)return this;let i=this._events=this._events||{},s=i[t]=i[t]||[];return s.includes(e)||s.push(e),this},e.once=function(t,e){if(!t||!e)return this;this.on(t,e);let i=this._onceEvents=this._onceEvents||{};return(i[t]=i[t]||{})[e]=!0,this},e.off=function(t,e){let i=this._events&&this._events[t];if(!i||!i.length)return this;let s=i.indexOf(e);return-1!=s&&i.splice(s,1),this},e.emitEvent=function(t,e){let i=this._events&&this._events[t];if(!i||!i.length)return this;i=i.slice(0),e=e||[];let s=this._onceEvents&&this._onceEvents[t];for(let n of i){s&&s[n]&&(this.off(t,n),delete s[n]),n.apply(this,e)}return this},e.allOff=function(){return delete this._events,delete this._onceEvents,this},t})),
+/*!
+ * imagesLoaded v5.0.0
+ * JavaScript is all like "You images are done yet or what?"
+ * MIT License
+ */
+function(t,e){"object"==typeof module&&module.exports?module.exports=e(t,require("ev-emitter")):t.imagesLoaded=e(t,t.EvEmitter)}("undefined"!=typeof window?window:this,(function(t,e){let i=t.jQuery,s=t.console;function n(t,e,o){if(!(this instanceof n))return new n(t,e,o);let r=t;var h;("string"==typeof t&&(r=document.querySelectorAll(t)),r)?(this.elements=(h=r,Array.isArray(h)?h:"object"==typeof h&&"number"==typeof h.length?[...h]:[h]),this.options={},"function"==typeof e?o=e:Object.assign(this.options,e),o&&this.on("always",o),this.getImages(),i&&(this.jqDeferred=new i.Deferred),setTimeout(this.check.bind(this))):s.error(`Bad element for imagesLoaded ${r||t}`)}n.prototype=Object.create(e.prototype),n.prototype.getImages=function(){this.images=[],this.elements.forEach(this.addElementImages,this)};const o=[1,9,11];n.prototype.addElementImages=function(t){"IMG"===t.nodeName&&this.addImage(t),!0===this.options.background&&this.addElementBackgroundImages(t);let{nodeType:e}=t;if(!e||!o.includes(e))return;let i=t.querySelectorAll("img");for(let t of i)this.addImage(t);if("string"==typeof this.options.background){let e=t.querySelectorAll(this.options.background);for(let t of e)this.addElementBackgroundImages(t)}};const r=/url\((['"])?(.*?)\1\)/gi;function h(t){this.img=t}function d(t,e){this.url=t,this.element=e,this.img=new Image}return n.prototype.addElementBackgroundImages=function(t){let e=getComputedStyle(t);if(!e)return;let i=r.exec(e.backgroundImage);for(;null!==i;){let s=i&&i[2];s&&this.addBackground(s,t),i=r.exec(e.backgroundImage)}},n.prototype.addImage=function(t){let e=new h(t);this.images.push(e)},n.prototype.addBackground=function(t,e){let i=new d(t,e);this.images.push(i)},n.prototype.check=function(){if(this.progressedCount=0,this.hasAnyBroken=!1,!this.images.length)return void this.complete();let t=(t,e,i)=>{setTimeout((()=>{this.progress(t,e,i)}))};this.images.forEach((function(e){e.once("progress",t),e.check()}))},n.prototype.progress=function(t,e,i){this.progressedCount++,this.hasAnyBroken=this.hasAnyBroken||!t.isLoaded,this.emitEvent("progress",[this,t,e]),this.jqDeferred&&this.jqDeferred.notify&&this.jqDeferred.notify(this,t),this.progressedCount===this.images.length&&this.complete(),this.options.debug&&s&&s.log(`progress: ${i}`,t,e)},n.prototype.complete=function(){let t=this.hasAnyBroken?"fail":"done";if(this.isComplete=!0,this.emitEvent(t,[this]),this.emitEvent("always",[this]),this.jqDeferred){let t=this.hasAnyBroken?"reject":"resolve";this.jqDeferred[t](this)}},h.prototype=Object.create(e.prototype),h.prototype.check=function(){this.getIsImageComplete()?this.confirm(0!==this.img.naturalWidth,"naturalWidth"):(this.proxyImage=new Image,this.img.crossOrigin&&(this.proxyImage.crossOrigin=this.img.crossOrigin),this.proxyImage.addEventListener("load",this),this.proxyImage.addEventListener("error",this),this.img.addEventListener("load",this),this.img.addEventListener("error",this),this.proxyImage.src=this.img.currentSrc||this.img.src)},h.prototype.getIsImageComplete=function(){return this.img.complete&&this.img.naturalWidth},h.prototype.confirm=function(t,e){this.isLoaded=t;let{parentNode:i}=this.img,s="PICTURE"===i.nodeName?i:this.img;this.emitEvent("progress",[this,s,e])},h.prototype.handleEvent=function(t){let e="on"+t.type;this[e]&&this[e](t)},h.prototype.onload=function(){this.confirm(!0,"onload"),this.unbindEvents()},h.prototype.onerror=function(){this.confirm(!1,"onerror"),this.unbindEvents()},h.prototype.unbindEvents=function(){this.proxyImage.removeEventListener("load",this),this.proxyImage.removeEventListener("error",this),this.img.removeEventListener("load",this),this.img.removeEventListener("error",this)},d.prototype=Object.create(h.prototype),d.prototype.check=function(){this.img.addEventListener("load",this),this.img.addEventListener("error",this),this.img.src=this.url,this.getIsImageComplete()&&(this.confirm(0!==this.img.naturalWidth,"naturalWidth"),this.unbindEvents())},d.prototype.unbindEvents=function(){this.img.removeEventListener("load",this),this.img.removeEventListener("error",this)},d.prototype.confirm=function(t,e){this.isLoaded=t,this.emitEvent("progress",[this,this.element,e])},n.makeJQueryPlugin=function(e){(e=e||t.jQuery)&&(i=e,i.fn.imagesLoaded=function(t,e){return new n(this,t,e).jqDeferred.promise(i(this))})},n.makeJQueryPlugin(),n}));
diff --git a/@shared/konva-utils.js b/@shared/konva-utils.js
new file mode 100644
index 0000000..a9fabf0
--- /dev/null
+++ b/@shared/konva-utils.js
@@ -0,0 +1,48 @@
+// konva-utils.js
+export function attachStageResizer(root, stage) {
+ const observer = new ResizeObserver(() => {
+ if (!stage) return;
+
+ stage.width(root.clientWidth);
+ stage.height(root.clientHeight);
+ stage.batchDraw();
+ });
+
+ observer.observe(root);
+ return observer;
+}
+
+// масштабирует картинку по stage
+export function fitImageToStage(konvaImage, stage) {
+ const img = konvaImage.image();
+ const sw = stage.width();
+ const sh = stage.height();
+ const iw = img.naturalWidth;
+ const ih = img.naturalHeight;
+ const scale = Math.min(sw / iw, sh / ih);
+
+ konvaImage.width(iw);
+ konvaImage.height(ih);
+ konvaImage.scale({ x: scale, y: scale });
+
+ // центрируем
+ konvaImage.x((sw - iw * scale) / 2);
+ konvaImage.y((sh - ih * scale) / 2);
+}
+
+export function coverImageToStage(konvaImage, stage) {
+ const img = konvaImage.image();
+ const sw = stage.width();
+ const sh = stage.height();
+ const iw = img.naturalWidth;
+ const ih = img.naturalHeight;
+ const scale = Math.max(sw / iw, sh / ih);
+
+ konvaImage.width(iw);
+ konvaImage.height(ih);
+ konvaImage.scale({ x: scale, y: scale });
+
+ // центрируем и обрезаем по центру
+ konvaImage.x((sw - iw * scale) / 2);
+ konvaImage.y((sh - ih * scale) / 2);
+}
diff --git a/README.md b/README.md
index a45c4fa..3e49ff7 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,7 @@
# konva-editor
-nue2 + konva
\ No newline at end of file
+nue2 + konva
+
+## Особенности
+- вынес часть кода из компонента в отдельный файл.
+- 23.12.2025 - картинка загружается, пока бех масштабироваания
diff --git a/konva-editor.html b/konva-editor.html
index e575ed5..bd21599 100644
--- a/konva-editor.html
+++ b/konva-editor.html
@@ -1,5 +1,13 @@
+
+