@sequent-org/moodboard 1.0.24 → 1.2.0
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 +2 -2
- package/src/assets/emodji//320/226/320/265/320/275/321/201/320/272/320/270/320/265 /321/215/320/274/320/276/321/206/320/270/320/270/1f645.png +0 -0
- package/src/assets/emodji//320/226/320/265/320/275/321/201/320/272/320/270/320/265 /321/215/320/274/320/276/321/206/320/270/320/270/1f646.png +0 -0
- package/src/assets/emodji//320/226/320/265/320/275/321/201/320/272/320/270/320/265 /321/215/320/274/320/276/321/206/320/270/320/270/1f64b.png +0 -0
- package/src/assets/emodji//320/226/320/265/320/275/321/201/320/272/320/270/320/265 /321/215/320/274/320/276/321/206/320/270/320/270/1f64d.png +0 -0
- package/src/assets/emodji//320/226/320/265/320/275/321/201/320/272/320/270/320/265 /321/215/320/274/320/276/321/206/320/270/320/270/1f64e.png +0 -0
- package/src/assets/emodji//320/226/320/265/321/201/321/202/321/213/1f446.png +0 -0
- package/src/assets/emodji//320/226/320/265/321/201/321/202/321/213/1f447.png +0 -0
- package/src/assets/emodji//320/226/320/265/321/201/321/202/321/213/1f448.png +0 -0
- package/src/assets/emodji//320/226/320/265/321/201/321/202/321/213/1f449.png +0 -0
- package/src/assets/emodji//320/226/320/265/321/201/321/202/321/213/1f44a.png +0 -0
- package/src/assets/emodji//320/226/320/265/321/201/321/202/321/213/1f44b.png +0 -0
- package/src/assets/emodji//320/226/320/265/321/201/321/202/321/213/1f44c.png +0 -0
- package/src/assets/emodji//320/226/320/265/321/201/321/202/321/213/1f450.png +0 -0
- package/src/assets/emodji//320/226/320/265/321/201/321/202/321/213/1f4aa.png +0 -0
- package/src/assets/emodji//320/226/320/265/321/201/321/202/321/213/1f590.png +0 -0
- package/src/assets/emodji//320/226/320/265/321/201/321/202/321/213/1f596.png +0 -0
- package/src/assets/emodji//320/226/320/265/321/201/321/202/321/213/1f64c.png +0 -0
- package/src/assets/emodji//320/226/320/265/321/201/321/202/321/213/1f64f.png +0 -0
- package/src/assets/emodji//320/226/320/265/321/201/321/202/321/213/261d.png +0 -0
- package/src/assets/emodji//320/226/320/265/321/201/321/202/321/213/270a.png +0 -0
- package/src/assets/emodji//320/226/320/265/321/201/321/202/321/213/270b.png +0 -0
- package/src/assets/emodji//320/226/320/265/321/201/321/202/321/213/270c.png +0 -0
- package/src/assets/emodji//320/226/320/265/321/201/321/202/321/213/270d.png +0 -0
- package/src/assets/emodji//320/232/320/276/321/202/320/270/320/272/320/270/1f638.png +0 -0
- package/src/assets/emodji//320/232/320/276/321/202/320/270/320/272/320/270/1f639.png +0 -0
- package/src/assets/emodji//320/232/320/276/321/202/320/270/320/272/320/270/1f63a.png +0 -0
- package/src/assets/emodji//320/232/320/276/321/202/320/270/320/272/320/270/1f63b.png +0 -0
- package/src/assets/emodji//320/232/320/276/321/202/320/270/320/272/320/270/1f63c.png +0 -0
- package/src/assets/emodji//320/232/320/276/321/202/320/270/320/272/320/270/1f63d.png +0 -0
- package/src/assets/emodji//320/232/320/276/321/202/320/270/320/272/320/270/1f63e.png +0 -0
- package/src/assets/emodji//320/232/320/276/321/202/320/270/320/272/320/270/1f63f.png +0 -0
- package/src/assets/emodji//320/232/320/276/321/202/320/270/320/272/320/270/1f640.png +0 -0
- package/src/assets/emodji//320/236/320/261/320/265/320/267/321/214/321/217/320/275/320/272/320/260/1f435.png +0 -0
- package/src/assets/emodji//320/236/320/261/320/265/320/267/321/214/321/217/320/275/320/272/320/260/1f648.png +0 -0
- package/src/assets/emodji//320/236/320/261/320/265/320/267/321/214/321/217/320/275/320/272/320/260/1f649.png +0 -0
- package/src/assets/emodji//320/236/320/261/320/265/320/267/321/214/321/217/320/275/320/272/320/260/1f64a.png +0 -0
- package/src/assets/emodji//320/240/320/260/320/267/320/275/320/276/320/265/1f440.png +0 -0
- package/src/assets/emodji//320/240/320/260/320/267/320/275/320/276/320/265/1f441.png +0 -0
- package/src/assets/emodji//320/240/320/260/320/267/320/275/320/276/320/265/1f499.png +0 -0
- package/src/assets/emodji//320/240/320/260/320/267/320/275/320/276/320/265/1f4a1.png +0 -0
- package/src/assets/emodji//320/240/320/260/320/267/320/275/320/276/320/265/1f4a3.png +0 -0
- package/src/assets/emodji//320/240/320/260/320/267/320/275/320/276/320/265/1f4a9.png +0 -0
- package/src/assets/emodji//320/240/320/260/320/267/320/275/320/276/320/265/1f4ac.png +0 -0
- package/src/assets/emodji//320/240/320/260/320/267/320/275/320/276/320/265/1f4af.png +0 -0
- package/src/assets/emodji//320/240/320/260/320/267/320/275/320/276/320/265/203c.png +0 -0
- package/src/assets/emodji//320/240/320/260/320/267/320/275/320/276/320/265/26d4.png +0 -0
- package/src/assets/emodji//320/240/320/260/320/267/320/275/320/276/320/265/2764.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f600.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f601.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f602.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f603.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f604.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f605.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f606.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f607.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f609.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f60a.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f60b.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f60c.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f60d.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f60e.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f60f.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f610.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f611.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f612.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f613.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f614.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f615.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f616.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f617.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f618.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f619.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f61a.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f61b.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f61c.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f61d.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f61e.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f61f.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f620.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f621.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f622.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f623.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f624.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f625.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f626.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f627.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f628.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f629.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f62a.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f62b.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f62c.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f62d.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f62e.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f62f.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f630.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f631.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f632.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f633.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f635.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f636.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f641.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/1f642.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/2639.png +0 -0
- package/src/assets/emodji//320/241/320/274/320/260/320/271/320/273/320/270/320/272/320/270/263a.png +0 -0
- package/src/assets/fonts/amatic-sc/AmaticSC-Bold.ttf +0 -0
- package/src/assets/fonts/amatic-sc/AmaticSC-Regular.ttf +0 -0
- package/src/assets/fonts/caveat/Caveat-Bold.ttf +0 -0
- package/src/assets/fonts/caveat/Caveat-Medium.ttf +0 -0
- package/src/assets/fonts/caveat/Caveat-Regular.ttf +0 -0
- package/src/assets/fonts/caveat/Caveat-SemiBold.ttf +0 -0
- package/src/assets/fonts/caveat/Caveat-VariableFont_wght.ttf +0 -0
- package/src/assets/fonts/great-vibes/GreatVibes-Regular.ttf +0 -0
- package/src/assets/fonts/lobster/Lobster-Regular.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif-Black.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif-BlackItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif-Bold.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif-BoldItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif-ExtraBold.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif-ExtraBoldItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif-ExtraLight.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif-ExtraLightItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif-Italic-VariableFont_wdth,wght.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif-Italic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif-Light.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif-LightItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif-Medium.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif-MediumItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif-Regular.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif-SemiBold.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif-SemiBoldItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif-Thin.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif-ThinItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif-VariableFont_wdth,wght.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_Condensed-Black.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_Condensed-BlackItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_Condensed-Bold.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_Condensed-BoldItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_Condensed-ExtraBold.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_Condensed-ExtraBoldItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_Condensed-ExtraLight.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_Condensed-ExtraLightItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_Condensed-Italic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_Condensed-Light.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_Condensed-LightItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_Condensed-Medium.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_Condensed-MediumItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_Condensed-Regular.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_Condensed-SemiBold.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_Condensed-SemiBoldItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_Condensed-Thin.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_Condensed-ThinItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_ExtraCondensed-Black.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_ExtraCondensed-BlackItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_ExtraCondensed-Bold.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_ExtraCondensed-BoldItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_ExtraCondensed-ExtraBold.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_ExtraCondensed-ExtraBoldItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_ExtraCondensed-ExtraLight.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_ExtraCondensed-ExtraLightItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_ExtraCondensed-Italic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_ExtraCondensed-Light.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_ExtraCondensed-LightItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_ExtraCondensed-Medium.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_ExtraCondensed-MediumItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_ExtraCondensed-Regular.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_ExtraCondensed-SemiBold.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_ExtraCondensed-SemiBoldItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_ExtraCondensed-Thin.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_ExtraCondensed-ThinItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_SemiCondensed-Black.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_SemiCondensed-BlackItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_SemiCondensed-Bold.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_SemiCondensed-BoldItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_SemiCondensed-ExtraBold.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_SemiCondensed-ExtraBoldItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_SemiCondensed-ExtraLight.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_SemiCondensed-ExtraLightItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_SemiCondensed-Italic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_SemiCondensed-Light.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_SemiCondensed-LightItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_SemiCondensed-Medium.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_SemiCondensed-MediumItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_SemiCondensed-Regular.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_SemiCondensed-SemiBold.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_SemiCondensed-SemiBoldItalic.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_SemiCondensed-Thin.ttf +0 -0
- package/src/assets/fonts/noto-serif/NotoSerif_SemiCondensed-ThinItalic.ttf +0 -0
- package/src/assets/fonts/oswald/Oswald-Bold.ttf +0 -0
- package/src/assets/fonts/oswald/Oswald-ExtraLight.ttf +0 -0
- package/src/assets/fonts/oswald/Oswald-Light.ttf +0 -0
- package/src/assets/fonts/oswald/Oswald-Medium.ttf +0 -0
- package/src/assets/fonts/oswald/Oswald-Regular.ttf +0 -0
- package/src/assets/fonts/oswald/Oswald-SemiBold.ttf +0 -0
- package/src/assets/fonts/oswald/Oswald-VariableFont_wght.ttf +0 -0
- package/src/assets/fonts/pacifico/Pacifico-Regular.ttf +0 -0
- package/src/assets/fonts/playfair/PlayfairDisplay-Black.ttf +0 -0
- package/src/assets/fonts/playfair/PlayfairDisplay-BlackItalic.ttf +0 -0
- package/src/assets/fonts/playfair/PlayfairDisplay-Bold.ttf +0 -0
- package/src/assets/fonts/playfair/PlayfairDisplay-BoldItalic.ttf +0 -0
- package/src/assets/fonts/playfair/PlayfairDisplay-ExtraBold.ttf +0 -0
- package/src/assets/fonts/playfair/PlayfairDisplay-ExtraBoldItalic.ttf +0 -0
- package/src/assets/fonts/playfair/PlayfairDisplay-Italic-VariableFont_wght.ttf +0 -0
- package/src/assets/fonts/playfair/PlayfairDisplay-Italic.ttf +0 -0
- package/src/assets/fonts/playfair/PlayfairDisplay-Medium.ttf +0 -0
- package/src/assets/fonts/playfair/PlayfairDisplay-MediumItalic.ttf +0 -0
- package/src/assets/fonts/playfair/PlayfairDisplay-Regular.ttf +0 -0
- package/src/assets/fonts/playfair/PlayfairDisplay-SemiBold.ttf +0 -0
- package/src/assets/fonts/playfair/PlayfairDisplay-SemiBoldItalic.ttf +0 -0
- package/src/assets/fonts/playfair/PlayfairDisplay-VariableFont_wght.ttf +0 -0
- package/src/assets/fonts/poiret-one/PoiretOne-Regular.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto-Black.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto-BlackItalic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto-Bold.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto-BoldItalic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto-ExtraBold.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto-ExtraBoldItalic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto-ExtraLight.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto-ExtraLightItalic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto-Italic-VariableFont_wdth,wght.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto-Italic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto-Light.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto-LightItalic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto-Medium.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto-MediumItalic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto-Regular.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto-SemiBold.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto-SemiBoldItalic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto-Thin.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto-ThinItalic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto-VariableFont_wdth,wght.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_Condensed-Black.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_Condensed-BlackItalic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_Condensed-Bold.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_Condensed-BoldItalic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_Condensed-ExtraBold.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_Condensed-ExtraBoldItalic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_Condensed-ExtraLight.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_Condensed-ExtraLightItalic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_Condensed-Italic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_Condensed-Light.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_Condensed-LightItalic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_Condensed-Medium.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_Condensed-MediumItalic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_Condensed-Regular.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_Condensed-SemiBold.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_Condensed-SemiBoldItalic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_Condensed-Thin.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_Condensed-ThinItalic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_SemiCondensed-Black.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_SemiCondensed-BlackItalic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_SemiCondensed-Bold.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_SemiCondensed-BoldItalic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_SemiCondensed-ExtraBold.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_SemiCondensed-ExtraBoldItalic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_SemiCondensed-ExtraLight.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_SemiCondensed-ExtraLightItalic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_SemiCondensed-Italic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_SemiCondensed-Light.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_SemiCondensed-LightItalic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_SemiCondensed-Medium.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_SemiCondensed-MediumItalic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_SemiCondensed-Regular.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_SemiCondensed-SemiBold.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_SemiCondensed-SemiBoldItalic.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_SemiCondensed-Thin.ttf +0 -0
- package/src/assets/fonts/roboto/Roboto_SemiCondensed-ThinItalic.ttf +0 -0
- package/src/assets/fonts/roboto-slab/RobotoSlab-Black.ttf +0 -0
- package/src/assets/fonts/roboto-slab/RobotoSlab-Bold.ttf +0 -0
- package/src/assets/fonts/roboto-slab/RobotoSlab-ExtraBold.ttf +0 -0
- package/src/assets/fonts/roboto-slab/RobotoSlab-ExtraLight.ttf +0 -0
- package/src/assets/fonts/roboto-slab/RobotoSlab-Light.ttf +0 -0
- package/src/assets/fonts/roboto-slab/RobotoSlab-Medium.ttf +0 -0
- package/src/assets/fonts/roboto-slab/RobotoSlab-Regular.ttf +0 -0
- package/src/assets/fonts/roboto-slab/RobotoSlab-SemiBold.ttf +0 -0
- package/src/assets/fonts/roboto-slab/RobotoSlab-Thin.ttf +0 -0
- package/src/assets/fonts/roboto-slab/RobotoSlab-VariableFont_wght.ttf +0 -0
- package/src/assets/fonts/rubik-mono-one/RubikMonoOne-Regular.ttf +0 -0
- package/src/assets/icons/arrows-up-down-left-right.svg +1 -0
- package/src/assets/icons/arrows-up-down.svg +1 -0
- package/src/assets/icons/attachments.svg +1 -3
- package/src/assets/icons/attachments2.svg +3 -0
- package/src/assets/icons/clear.svg +1 -5
- package/src/assets/icons/cursor-default-custom.svg +10 -0
- package/src/assets/icons/cursor-default.svg +1 -0
- package/src/assets/icons/cursor-default2.svg +1 -0
- package/src/assets/icons/emoji.svg +1 -6
- package/src/assets/icons/emoji2.svg +6 -0
- package/src/assets/icons/frame.svg +1 -3
- package/src/assets/icons/frame2.svg +3 -0
- package/src/assets/icons/i-cursor.svg +1 -0
- package/src/assets/icons/image.svg +1 -3
- package/src/assets/icons/image2.svg +3 -0
- package/src/assets/icons/note.svg +1 -3
- package/src/assets/icons/note2.svg +3 -0
- package/src/assets/icons/pencil.svg +1 -3
- package/src/assets/icons/pencil2.svg +3 -0
- package/src/assets/icons/rotate-icon.svg +3 -0
- package/src/assets/icons/select.svg +1 -1
- package/src/assets/icons/shapes.svg +1 -3
- package/src/assets/icons/shapes2.svg +3 -0
- package/src/assets/icons/text-add.svg +1 -3
- package/src/assets/icons/text-add2.svg +3 -0
- package/src/assets/icons/trash.svg +1 -0
- package/src/core/PixiEngine.js +32 -0
- package/src/core/commands/CopyObjectCommand.js +20 -9
- package/src/core/commands/GroupMoveCommand.js +49 -13
- package/src/core/commands/MoveObjectCommand.js +4 -24
- package/src/core/commands/PasteObjectCommand.js +26 -15
- package/src/core/events/Events.js +2 -0
- package/src/core/index.js +575 -47
- package/src/grid/GridFactory.js +3 -3
- package/src/grid/LineGrid.js +42 -20
- package/src/moodboard/MoodBoard.js +11 -0
- package/src/objects/DrawingObject.js +16 -7
- package/src/objects/FileObject.js +25 -11
- package/src/objects/FrameObject.js +38 -9
- package/src/objects/ImageObject.js +24 -5
- package/src/objects/NoteObject.js +227 -39
- package/src/objects/ShapeObject.js +9 -8
- package/src/objects/TextObject.js +2 -20
- package/src/services/BoardService.js +3 -3
- package/src/services/FrameService.js +95 -17
- package/src/services/ZoomPanController.js +14 -3
- package/src/tools/BaseTool.js +6 -1
- package/src/tools/ToolManager.js +24 -1
- package/src/tools/object-tools/DrawingTool.js +3 -1
- package/src/tools/object-tools/PlacementTool.js +366 -105
- package/src/tools/object-tools/SelectTool.js +508 -153
- package/src/tools/object-tools/TextTool.js +23 -2
- package/src/tools/object-tools/selection/BoxSelectController.js +5 -0
- package/src/ui/FilePropertiesPanel.js +9 -2
- package/src/ui/FramePropertiesPanel.js +239 -91
- package/src/ui/HtmlHandlesLayer.js +383 -126
- package/src/ui/HtmlTextLayer.js +122 -26
- package/src/ui/NotePropertiesPanel.js +128 -44
- package/src/ui/TextPropertiesPanel.js +100 -118
- package/src/ui/Toolbar.js +254 -65
- package/src/ui/Topbar.js +112 -10
- package/src/ui/ZoomPanel.js +8 -2
- package/src/ui/styles/index.css +5 -0
- package/src/ui/styles/panels.css +232 -0
- package/src/ui/styles/toolbar.css +77 -0
- package/src/ui/styles/topbar.css +113 -0
- package/src/ui/styles/workspace.css +481 -263
- package/src/utils/emojiResolver.js +121 -0
package/src/core/index.js
CHANGED
|
@@ -213,19 +213,115 @@ export class CoreMoodBoard {
|
|
|
213
213
|
if (this.clipboard.type === 'object') {
|
|
214
214
|
this.pasteObject({ x, y });
|
|
215
215
|
} else if (this.clipboard.type === 'group') {
|
|
216
|
-
// Вставляем группу с сохранением относительных позиций относительно клика
|
|
217
216
|
const group = this.clipboard;
|
|
218
217
|
const data = Array.isArray(group.data) ? group.data : [];
|
|
219
218
|
if (data.length === 0) return;
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
minX =
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
219
|
+
|
|
220
|
+
// Особая логика: если это бандл фрейма (фрейм + дети)
|
|
221
|
+
if (group.meta && group.meta.frameBundle) {
|
|
222
|
+
// Вычисляем топ-левт группы для относительного смещения клик-точки
|
|
223
|
+
let minX = Infinity, minY = Infinity;
|
|
224
|
+
data.forEach(o => {
|
|
225
|
+
if (!o || !o.position) return;
|
|
226
|
+
minX = Math.min(minX, o.position.x);
|
|
227
|
+
minY = Math.min(minY, o.position.y);
|
|
228
|
+
});
|
|
229
|
+
if (!isFinite(minX) || !isFinite(minY)) return;
|
|
230
|
+
const baseX = minX, baseY = minY;
|
|
231
|
+
|
|
232
|
+
// Ищем фрейм в бандле
|
|
233
|
+
const frames = data.filter(o => o && o.type === 'frame');
|
|
234
|
+
if (frames.length !== 1) {
|
|
235
|
+
// fallback к обычной вставке группы
|
|
236
|
+
const newIds = [];
|
|
237
|
+
let pending = data.length;
|
|
238
|
+
const onPasted = (payload) => {
|
|
239
|
+
if (!payload || !payload.newId) return;
|
|
240
|
+
newIds.push(payload.newId);
|
|
241
|
+
pending -= 1;
|
|
242
|
+
if (pending === 0) {
|
|
243
|
+
this.eventBus.off(Events.Object.Pasted, onPasted);
|
|
244
|
+
requestAnimationFrame(() => {
|
|
245
|
+
if (this.selectTool && newIds.length > 0) {
|
|
246
|
+
this.selectTool.setSelection(newIds);
|
|
247
|
+
this.selectTool.updateResizeHandles();
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
this.eventBus.on(Events.Object.Pasted, onPasted);
|
|
253
|
+
data.forEach(orig => {
|
|
254
|
+
const cloned = JSON.parse(JSON.stringify(orig));
|
|
255
|
+
const targetPos = {
|
|
256
|
+
x: x + (cloned.position.x - baseX),
|
|
257
|
+
y: y + (cloned.position.y - baseY)
|
|
258
|
+
};
|
|
259
|
+
this.clipboard = { type: 'object', data: cloned };
|
|
260
|
+
const cmd = new PasteObjectCommand(this, targetPos);
|
|
261
|
+
cmd.setEventBus(this.eventBus);
|
|
262
|
+
this.history.executeCommand(cmd);
|
|
263
|
+
});
|
|
264
|
+
this.clipboard = group;
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const frameOriginal = frames[0];
|
|
269
|
+
const children = data.filter(o => o && o.id !== frameOriginal.id);
|
|
270
|
+
const totalToPaste = 1 + children.length;
|
|
271
|
+
const newIds = [];
|
|
272
|
+
let pastedCount = 0;
|
|
273
|
+
let newFrameId = null;
|
|
274
|
+
|
|
275
|
+
const onPasted = (payload) => {
|
|
276
|
+
if (!payload || !payload.newId) return;
|
|
277
|
+
newIds.push(payload.newId);
|
|
278
|
+
pastedCount += 1;
|
|
279
|
+
// Как только вставили фрейм — вставляем детей с новым frameId
|
|
280
|
+
if (!newFrameId && payload.originalId === frameOriginal.id) {
|
|
281
|
+
newFrameId = payload.newId;
|
|
282
|
+
for (const child of children) {
|
|
283
|
+
const clonedChild = JSON.parse(JSON.stringify(child));
|
|
284
|
+
clonedChild.properties = clonedChild.properties || {};
|
|
285
|
+
clonedChild.properties.frameId = newFrameId;
|
|
286
|
+
const targetPos = {
|
|
287
|
+
x: x + (clonedChild.position.x - baseX),
|
|
288
|
+
y: y + (clonedChild.position.y - baseY)
|
|
289
|
+
};
|
|
290
|
+
this.clipboard = { type: 'object', data: clonedChild };
|
|
291
|
+
const cmdChild = new PasteObjectCommand(this, targetPos);
|
|
292
|
+
cmdChild.setEventBus(this.eventBus);
|
|
293
|
+
this.history.executeCommand(cmdChild);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (pastedCount === totalToPaste) {
|
|
297
|
+
this.eventBus.off(Events.Object.Pasted, onPasted);
|
|
298
|
+
requestAnimationFrame(() => {
|
|
299
|
+
if (this.selectTool && newIds.length > 0) {
|
|
300
|
+
this.selectTool.setSelection(newIds);
|
|
301
|
+
this.selectTool.updateResizeHandles();
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
this.eventBus.on(Events.Object.Pasted, onPasted);
|
|
307
|
+
|
|
308
|
+
// Вставляем фрейм первым
|
|
309
|
+
const frameClone = JSON.parse(JSON.stringify(frameOriginal));
|
|
310
|
+
this.clipboard = { type: 'object', data: frameClone };
|
|
311
|
+
const targetPosFrame = {
|
|
312
|
+
x: x + (frameClone.position.x - baseX),
|
|
313
|
+
y: y + (frameClone.position.y - baseY)
|
|
314
|
+
};
|
|
315
|
+
const cmdFrame = new PasteObjectCommand(this, targetPosFrame);
|
|
316
|
+
cmdFrame.setEventBus(this.eventBus);
|
|
317
|
+
this.history.executeCommand(cmdFrame);
|
|
318
|
+
|
|
319
|
+
// Возвращаем clipboard к группе для повторных вставок
|
|
320
|
+
this.clipboard = group;
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Обычная вставка группы (не фрейм-бандл)
|
|
229
325
|
const newIds = [];
|
|
230
326
|
let pending = data.length;
|
|
231
327
|
const onPasted = (payload) => {
|
|
@@ -246,15 +342,14 @@ export class CoreMoodBoard {
|
|
|
246
342
|
data.forEach(orig => {
|
|
247
343
|
const cloned = JSON.parse(JSON.stringify(orig));
|
|
248
344
|
const targetPos = {
|
|
249
|
-
x: x + (cloned.position.x -
|
|
250
|
-
y: y + (cloned.position.y -
|
|
345
|
+
x: x + (cloned.position.x - minX),
|
|
346
|
+
y: y + (cloned.position.y - minY)
|
|
251
347
|
};
|
|
252
348
|
this.clipboard = { type: 'object', data: cloned };
|
|
253
349
|
const cmd = new PasteObjectCommand(this, targetPos);
|
|
254
350
|
cmd.setEventBus(this.eventBus);
|
|
255
351
|
this.history.executeCommand(cmd);
|
|
256
352
|
});
|
|
257
|
-
// Возвращаем clipboard к группе для повторных вставок
|
|
258
353
|
this.clipboard = group;
|
|
259
354
|
}
|
|
260
355
|
});
|
|
@@ -456,7 +551,8 @@ export class CoreMoodBoard {
|
|
|
456
551
|
|
|
457
552
|
// События перетаскивания
|
|
458
553
|
this.eventBus.on(Events.Tool.DragStart, (data) => {
|
|
459
|
-
// Сохраняем начальную позицию как
|
|
554
|
+
// Сохраняем начальную позицию как левый-верх
|
|
555
|
+
// Все объекты используют pivot по центру, поэтому логика одинакова
|
|
460
556
|
const pixiObject = this.pixi.objects.get(data.object);
|
|
461
557
|
if (pixiObject) {
|
|
462
558
|
const halfW = (pixiObject.width || 0) / 2;
|
|
@@ -593,7 +689,8 @@ export class CoreMoodBoard {
|
|
|
593
689
|
}
|
|
594
690
|
}
|
|
595
691
|
if (moves.length > 0) {
|
|
596
|
-
|
|
692
|
+
// Обычное групповое перемещение - координаты центров PIXI
|
|
693
|
+
const cmd = new GroupMoveCommand(this, moves, false);
|
|
597
694
|
cmd.setEventBus(this.eventBus);
|
|
598
695
|
this.history.executeCommand(cmd);
|
|
599
696
|
}
|
|
@@ -618,7 +715,9 @@ export class CoreMoodBoard {
|
|
|
618
715
|
if (this.dragStartPosition) {
|
|
619
716
|
const pixiObject = this.pixi.objects.get(data.object);
|
|
620
717
|
if (pixiObject) {
|
|
621
|
-
|
|
718
|
+
// Берем финальную позицию из state, который обновлялся во время drag:update
|
|
719
|
+
const objState = this.state.state.objects.find(o => o.id === data.object);
|
|
720
|
+
const finalPosition = objState && objState.position ? { x: objState.position.x, y: objState.position.y } : { x: 0, y: 0 };
|
|
622
721
|
|
|
623
722
|
// Создаем команду только если позиция действительно изменилась
|
|
624
723
|
if (this.dragStartPosition.x !== finalPosition.x ||
|
|
@@ -642,7 +741,8 @@ export class CoreMoodBoard {
|
|
|
642
741
|
const to = { x: child.position.x, y: child.position.y };
|
|
643
742
|
moves.push({ id: childId, from, to });
|
|
644
743
|
}
|
|
645
|
-
|
|
744
|
+
// Frame перемещение - координаты уже левый-верх
|
|
745
|
+
const cmd = new GroupMoveCommand(this, moves, true);
|
|
646
746
|
cmd.setEventBus(this.eventBus);
|
|
647
747
|
this.history.executeCommand(cmd);
|
|
648
748
|
} else {
|
|
@@ -673,13 +773,95 @@ export class CoreMoodBoard {
|
|
|
673
773
|
const original = objects.find(obj => obj.id === originalId);
|
|
674
774
|
if (!original) return;
|
|
675
775
|
|
|
676
|
-
//
|
|
776
|
+
// Если дублируем фрейм — копируем вместе с его содержимым
|
|
777
|
+
if (original.type === 'frame') {
|
|
778
|
+
const frame = JSON.parse(JSON.stringify(original));
|
|
779
|
+
const dx = (position?.x ?? frame.position.x) - frame.position.x;
|
|
780
|
+
const dy = (position?.y ?? frame.position.y) - frame.position.y;
|
|
781
|
+
|
|
782
|
+
// Дети фрейма
|
|
783
|
+
const children = (this.state.state.objects || []).filter(o => o && o.properties && o.properties.frameId === originalId);
|
|
784
|
+
|
|
785
|
+
// После вставки фрейма вставим детей, перепривязав к новому frameId
|
|
786
|
+
const onFramePasted = (payload) => {
|
|
787
|
+
if (!payload || payload.originalId !== originalId) return;
|
|
788
|
+
const newFrameId = payload.newId;
|
|
789
|
+
this.eventBus.off(Events.Object.Pasted, onFramePasted);
|
|
790
|
+
for (const child of children) {
|
|
791
|
+
const clonedChild = JSON.parse(JSON.stringify(child));
|
|
792
|
+
clonedChild.properties = clonedChild.properties || {};
|
|
793
|
+
clonedChild.properties.frameId = newFrameId;
|
|
794
|
+
const targetPos = {
|
|
795
|
+
x: (child.position?.x || 0) + dx,
|
|
796
|
+
y: (child.position?.y || 0) + dy
|
|
797
|
+
};
|
|
798
|
+
this.clipboard = { type: 'object', data: clonedChild };
|
|
799
|
+
const cmdChild = new PasteObjectCommand(this, targetPos);
|
|
800
|
+
cmdChild.setEventBus(this.eventBus);
|
|
801
|
+
this.history.executeCommand(cmdChild);
|
|
802
|
+
}
|
|
803
|
+
};
|
|
804
|
+
this.eventBus.on(Events.Object.Pasted, onFramePasted);
|
|
805
|
+
|
|
806
|
+
// Подготовим буфер для фрейма (с новым названием)
|
|
807
|
+
const frameClone = JSON.parse(JSON.stringify(frame));
|
|
808
|
+
try {
|
|
809
|
+
const arr = this.state.state.objects || [];
|
|
810
|
+
let maxNum = 0;
|
|
811
|
+
for (const o of arr) {
|
|
812
|
+
if (!o || o.type !== 'frame') continue;
|
|
813
|
+
const t = o?.properties?.title || '';
|
|
814
|
+
const m = t.match(/^\s*Фрейм\s+(\d+)\s*$/i);
|
|
815
|
+
if (m) {
|
|
816
|
+
const n = parseInt(m[1], 10);
|
|
817
|
+
if (Number.isFinite(n)) maxNum = Math.max(maxNum, n);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
const next = maxNum + 1;
|
|
821
|
+
frameClone.properties = frameClone.properties || {};
|
|
822
|
+
frameClone.properties.title = `Фрейм ${next}`;
|
|
823
|
+
} catch (_) {}
|
|
824
|
+
this.clipboard = { type: 'object', data: frameClone };
|
|
825
|
+
const cmdFrame = new PasteObjectCommand(this, { x: frame.position.x + dx, y: frame.position.y + dy });
|
|
826
|
+
cmdFrame.setEventBus(this.eventBus);
|
|
827
|
+
this.history.executeCommand(cmdFrame);
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// Обычная логика для остальных типов
|
|
677
832
|
this.clipboard = {
|
|
678
833
|
type: 'object',
|
|
679
834
|
data: JSON.parse(JSON.stringify(original))
|
|
680
835
|
};
|
|
836
|
+
// Запоминаем исходное название фрейма, чтобы не менять его
|
|
837
|
+
try {
|
|
838
|
+
if (original.type === 'frame') {
|
|
839
|
+
this._dupTitleMap = this._dupTitleMap || new Map();
|
|
840
|
+
const prevTitle = (original.properties && typeof original.properties.title !== 'undefined') ? original.properties.title : undefined;
|
|
841
|
+
this._dupTitleMap.set(originalId, prevTitle);
|
|
842
|
+
}
|
|
843
|
+
} catch (_) {}
|
|
844
|
+
// Если фрейм — проставим будущий заголовок в буфер
|
|
845
|
+
try {
|
|
846
|
+
if (this.clipboard.data && this.clipboard.data.type === 'frame') {
|
|
847
|
+
const arr = this.state.state.objects || [];
|
|
848
|
+
let maxNum = 0;
|
|
849
|
+
for (const o of arr) {
|
|
850
|
+
if (!o || o.type !== 'frame') continue;
|
|
851
|
+
const t = o?.properties?.title || '';
|
|
852
|
+
const m = t.match(/^\s*Фрейм\s+(\d+)\s*$/i);
|
|
853
|
+
if (m) {
|
|
854
|
+
const n = parseInt(m[1], 10);
|
|
855
|
+
if (Number.isFinite(n)) maxNum = Math.max(maxNum, n);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
const next = maxNum + 1;
|
|
859
|
+
this.clipboard.data.properties = this.clipboard.data.properties || {};
|
|
860
|
+
this.clipboard.data.properties.title = `Фрейм ${next}`;
|
|
861
|
+
}
|
|
862
|
+
} catch (_) {}
|
|
681
863
|
|
|
682
|
-
// Вызываем вставку
|
|
864
|
+
// Вызываем вставку по указанной позиции (под курсором)
|
|
683
865
|
this.pasteObject(position);
|
|
684
866
|
});
|
|
685
867
|
|
|
@@ -715,14 +897,77 @@ export class CoreMoodBoard {
|
|
|
715
897
|
this.eventBus.on(Events.Object.Pasted, handler);
|
|
716
898
|
// Кладем в clipboard объект, затем вызываем PasteObjectCommand с текущей позицией
|
|
717
899
|
this.clipboard = { type: 'object', data: JSON.parse(JSON.stringify(obj)) };
|
|
900
|
+
// Запомним оригинальные названия фреймов
|
|
901
|
+
try {
|
|
902
|
+
if (obj.type === 'frame') {
|
|
903
|
+
this._dupTitleMap = this._dupTitleMap || new Map();
|
|
904
|
+
const prevTitle = (obj.properties && typeof obj.properties.title !== 'undefined') ? obj.properties.title : undefined;
|
|
905
|
+
this._dupTitleMap.set(obj.id, prevTitle);
|
|
906
|
+
}
|
|
907
|
+
} catch (_) { /* no-op */ }
|
|
908
|
+
// Если фрейм — сразу проставим новый заголовок в буфер
|
|
909
|
+
try {
|
|
910
|
+
if (this.clipboard.data && this.clipboard.data.type === 'frame') {
|
|
911
|
+
const arr = this.state.state.objects || [];
|
|
912
|
+
let maxNum = 0;
|
|
913
|
+
for (const o2 of arr) {
|
|
914
|
+
if (!o2 || o2.type !== 'frame') continue;
|
|
915
|
+
const t2 = o2?.properties?.title || '';
|
|
916
|
+
const m2 = t2.match(/^\s*Фрейм\s+(\d+)\s*$/i);
|
|
917
|
+
if (m2) {
|
|
918
|
+
const n2 = parseInt(m2[1], 10);
|
|
919
|
+
if (Number.isFinite(n2)) maxNum = Math.max(maxNum, n2);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
const next2 = maxNum + 1;
|
|
923
|
+
this.clipboard.data.properties = this.clipboard.data.properties || {};
|
|
924
|
+
this.clipboard.data.properties.title = `Фрейм ${next2}`;
|
|
925
|
+
}
|
|
926
|
+
} catch (_) { /* no-op */ }
|
|
718
927
|
const cmd = new PasteObjectCommand(this, { x: obj.position.x, y: obj.position.y });
|
|
719
928
|
cmd.setEventBus(this.eventBus);
|
|
720
929
|
this.history.executeCommand(cmd);
|
|
721
930
|
}
|
|
722
931
|
});
|
|
723
932
|
|
|
724
|
-
// Когда объект вставлен (из PasteObjectCommand)
|
|
933
|
+
// Когда объект вставлен (из PasteObjectCommand)
|
|
725
934
|
this.eventBus.on(Events.Object.Pasted, ({ originalId, newId }) => {
|
|
935
|
+
try {
|
|
936
|
+
const arr = this.state.state.objects || [];
|
|
937
|
+
const newObj = arr.find(o => o.id === newId);
|
|
938
|
+
const origObj = arr.find(o => o.id === originalId);
|
|
939
|
+
if (newObj && newObj.type === 'frame') {
|
|
940
|
+
// Рассчитываем следующий номер среди уже существующих (кроме только что вставленного)
|
|
941
|
+
let maxNum = 0;
|
|
942
|
+
for (const o of arr) {
|
|
943
|
+
if (!o || o.id === newId || o.type !== 'frame') continue;
|
|
944
|
+
const t = o?.properties?.title || '';
|
|
945
|
+
const m = t.match(/^\s*Фрейм\s+(\d+)\s*$/i);
|
|
946
|
+
if (m) {
|
|
947
|
+
const n = parseInt(m[1], 10);
|
|
948
|
+
if (Number.isFinite(n)) maxNum = Math.max(maxNum, n);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
const next = maxNum + 1;
|
|
952
|
+
// Присваиваем новое имя только НОВОМУ
|
|
953
|
+
newObj.properties = newObj.properties || {};
|
|
954
|
+
newObj.properties.title = `Фрейм ${next}`;
|
|
955
|
+
const pixNew = this.pixi.objects.get(newId);
|
|
956
|
+
if (pixNew && pixNew._mb?.instance?.setTitle) pixNew._mb.instance.setTitle(newObj.properties.title);
|
|
957
|
+
// Восстанавливаем исходное имя оригинала, если оно было записано
|
|
958
|
+
if (this._dupTitleMap && this._dupTitleMap.has(originalId) && origObj && origObj.type === 'frame') {
|
|
959
|
+
const prev = this._dupTitleMap.get(originalId);
|
|
960
|
+
origObj.properties = origObj.properties || {};
|
|
961
|
+
// Если prev undefined, очистим title
|
|
962
|
+
origObj.properties.title = prev;
|
|
963
|
+
const pixOrig = this.pixi.objects.get(originalId);
|
|
964
|
+
if (pixOrig && pixOrig._mb?.instance?.setTitle) pixOrig._mb.instance.setTitle(prev);
|
|
965
|
+
this._dupTitleMap.delete(originalId);
|
|
966
|
+
}
|
|
967
|
+
this.state.markDirty();
|
|
968
|
+
}
|
|
969
|
+
} catch (_) { /* no-op */ }
|
|
970
|
+
// Сообщаем SelectTool id нового объекта для переключения drag
|
|
726
971
|
this.eventBus.emit(Events.Tool.DuplicateReady, { originalId, newId });
|
|
727
972
|
});
|
|
728
973
|
|
|
@@ -733,6 +978,13 @@ export class CoreMoodBoard {
|
|
|
733
978
|
const object = objects.find(obj => obj.id === data.object);
|
|
734
979
|
if (object) {
|
|
735
980
|
this.resizeStartSize = { width: object.width, height: object.height };
|
|
981
|
+
// Сохраняем контекст активного ресайза для расчёта позиции, если она не будет передана
|
|
982
|
+
this._activeResize = {
|
|
983
|
+
objectId: data.object,
|
|
984
|
+
handle: data.handle,
|
|
985
|
+
startSize: { width: object.width, height: object.height },
|
|
986
|
+
startPosition: { x: object.position.x, y: object.position.y }
|
|
987
|
+
};
|
|
736
988
|
}
|
|
737
989
|
});
|
|
738
990
|
|
|
@@ -814,13 +1066,187 @@ export class CoreMoodBoard {
|
|
|
814
1066
|
const objects = this.state.getObjects();
|
|
815
1067
|
const object = objects.find(obj => obj.id === data.object);
|
|
816
1068
|
const objectType = object ? object.type : null;
|
|
817
|
-
|
|
818
|
-
|
|
1069
|
+
|
|
1070
|
+
// Сохраняем пропорции для фреймов, кроме произвольных (lockedAspect=false)
|
|
1071
|
+
if ((objectType === 'frame' || (objectType === 'image' && object?.properties?.isEmojiIcon)) && data.size) {
|
|
1072
|
+
const lockedAspect = !!(object?.properties && (object.properties.lockedAspect === true));
|
|
1073
|
+
const isEmoji = (objectType === 'image' && object?.properties?.isEmojiIcon);
|
|
1074
|
+
if (!lockedAspect && !isEmoji) {
|
|
1075
|
+
// произвольные фреймы — без ограничений
|
|
1076
|
+
} else {
|
|
1077
|
+
const start = this._activeResize?.startSize || { width: object.width, height: object.height };
|
|
1078
|
+
const aspect = isEmoji ? 1 : ((start.width > 0 && start.height > 0) ? (start.width / start.height) : (object.width / Math.max(1, object.height)));
|
|
1079
|
+
let w = Math.max(1, data.size.width);
|
|
1080
|
+
let h = Math.max(1, data.size.height);
|
|
1081
|
+
const hndl = (this._activeResize?.handle || '').toLowerCase();
|
|
1082
|
+
if (isEmoji) {
|
|
1083
|
+
// Делаем квадрат, фиксируя противоположную сторону к старту
|
|
1084
|
+
const s = Math.max(w, h);
|
|
1085
|
+
// Вычислим позицию сразу, чтобы не было дрейфа правой/нижней границы
|
|
1086
|
+
if (!data.position && this._activeResize && this._activeResize.objectId === data.object) {
|
|
1087
|
+
const startPos = this._activeResize.startPosition;
|
|
1088
|
+
const sw = this._activeResize.startSize.width;
|
|
1089
|
+
const sh = this._activeResize.startSize.height;
|
|
1090
|
+
let x = startPos.x;
|
|
1091
|
+
let y = startPos.y;
|
|
1092
|
+
if (hndl.includes('w')) { x = startPos.x + (sw - s); }
|
|
1093
|
+
if (hndl.includes('n')) { y = startPos.y + (sh - s); }
|
|
1094
|
+
const isEdge = ['n','s','e','w'].includes(hndl);
|
|
1095
|
+
if (isEdge) {
|
|
1096
|
+
if (hndl === 'n' || hndl === 's') x = startPos.x + Math.round((sw - s) / 2);
|
|
1097
|
+
if (hndl === 'e' || hndl === 'w') y = startPos.y + Math.round((sh - s) / 2);
|
|
1098
|
+
}
|
|
1099
|
+
data.position = { x: Math.round(x), y: Math.round(y) };
|
|
1100
|
+
}
|
|
1101
|
+
w = s; h = s;
|
|
1102
|
+
} else {
|
|
1103
|
+
// Обычная поддержка аспекта для фреймов
|
|
1104
|
+
const dw = Math.abs(w - start.width);
|
|
1105
|
+
const dh = Math.abs(h - start.height);
|
|
1106
|
+
if (dw >= dh) { h = Math.round(w / aspect); } else { w = Math.round(h * aspect); }
|
|
1107
|
+
}
|
|
1108
|
+
// Минимальная площадь — только для фреймов
|
|
1109
|
+
if (!isEmoji) {
|
|
1110
|
+
const minArea = 1800;
|
|
1111
|
+
const area = Math.max(1, w * h);
|
|
1112
|
+
if (area < minArea) {
|
|
1113
|
+
const scale = Math.sqrt(minArea / area);
|
|
1114
|
+
w = Math.round(w * scale);
|
|
1115
|
+
h = Math.round(h * scale);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
data.size = { width: w, height: h };
|
|
1119
|
+
|
|
1120
|
+
// Если позиция известна (фиксированная противоположная сторона) — откорректируем её
|
|
1121
|
+
if (!data.position && this._activeResize && this._activeResize.objectId === data.object) {
|
|
1122
|
+
const hndl = (this._activeResize.handle || '').toLowerCase();
|
|
1123
|
+
const startPos = this._activeResize.startPosition;
|
|
1124
|
+
const sw = this._activeResize.startSize.width;
|
|
1125
|
+
const sh = this._activeResize.startSize.height;
|
|
1126
|
+
let x = startPos.x;
|
|
1127
|
+
let y = startPos.y;
|
|
1128
|
+
// Базовая привязка противоположной стороны
|
|
1129
|
+
if (hndl.includes('w')) { x = startPos.x + (sw - w); }
|
|
1130
|
+
if (hndl.includes('n')) { y = startPos.y + (sh - h); }
|
|
1131
|
+
// Симметрическая компенсация по перпендикулярной оси для edge-хэндлов
|
|
1132
|
+
const isEdge = ['n','s','e','w'].includes(hndl);
|
|
1133
|
+
if (isEdge) {
|
|
1134
|
+
if (hndl === 'n' || hndl === 's') {
|
|
1135
|
+
// Вверх/вниз: верх или низ фиксируется, ширина меняется симметрично относительно центра
|
|
1136
|
+
x = startPos.x + Math.round((sw - w) / 2);
|
|
1137
|
+
} else if (hndl === 'e' || hndl === 'w') {
|
|
1138
|
+
// Вправо/влево: левая или правая фиксируется, высота меняется симметрично относительно центра
|
|
1139
|
+
y = startPos.y + Math.round((sh - h) / 2);
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
data.position = { x: Math.round(x), y: Math.round(y) };
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
// Если позиция не пришла из UI, вычислим её из контекста активной ручки
|
|
1148
|
+
let position = data.position;
|
|
1149
|
+
if (!position && this._activeResize && this._activeResize.objectId === data.object) {
|
|
1150
|
+
const h = (this._activeResize.handle || '').toLowerCase();
|
|
1151
|
+
const start = this._activeResize.startPosition;
|
|
1152
|
+
const startSize = this._activeResize.startSize;
|
|
1153
|
+
const dw = (data.size?.width || startSize.width) - startSize.width;
|
|
1154
|
+
const dh = (data.size?.height || startSize.height) - startSize.height;
|
|
1155
|
+
let nx = start.x;
|
|
1156
|
+
let ny = start.y;
|
|
1157
|
+
// Для левых/верхних ручек смещаем топ-лев на полную величину изменения
|
|
1158
|
+
if (h.includes('w')) nx = start.x + dw;
|
|
1159
|
+
if (h.includes('n')) ny = start.y + dh;
|
|
1160
|
+
// Для правых/нижних ручек топ-лев остаётся стартовым (nx, ny уже равны start)
|
|
1161
|
+
position = { x: nx, y: ny };
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
// Для фреймов с произвольным аспектом также обеспечим минимальную площадь
|
|
1165
|
+
if (objectType === 'frame' && data.size) {
|
|
1166
|
+
const minArea = 1800;
|
|
1167
|
+
const w0 = Math.max(1, data.size.width);
|
|
1168
|
+
const h0 = Math.max(1, data.size.height);
|
|
1169
|
+
const area0 = w0 * h0;
|
|
1170
|
+
if (area0 < minArea) {
|
|
1171
|
+
const scale = Math.sqrt(minArea / Math.max(1, area0));
|
|
1172
|
+
const w = Math.round(w0 * scale);
|
|
1173
|
+
const h = Math.round(h0 * scale);
|
|
1174
|
+
data.size = { width: w, height: h };
|
|
1175
|
+
// позиция будет скорректирована ниже общей логикой (уже рассчитана выше при необходимости)
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
this.updateObjectSizeAndPositionDirect(data.object, data.size, position, objectType);
|
|
819
1180
|
});
|
|
820
1181
|
|
|
821
1182
|
this.eventBus.on(Events.Tool.ResizeEnd, (data) => {
|
|
822
1183
|
// В конце создаем одну команду изменения размера
|
|
823
1184
|
if (this.resizeStartSize && data.oldSize && data.newSize) {
|
|
1185
|
+
// Принудительно сохраняем пропорции для фреймов (если lockedAspect=true)
|
|
1186
|
+
const objects = this.state.getObjects();
|
|
1187
|
+
const object = objects.find(obj => obj.id === data.object);
|
|
1188
|
+
const objectType = object ? object.type : null;
|
|
1189
|
+
if (objectType === 'frame' && !!(object?.properties && object.properties.lockedAspect === true)) {
|
|
1190
|
+
const start = this._activeResize?.startSize || { width: object.width, height: object.height };
|
|
1191
|
+
const aspect = (start.width > 0 && start.height > 0) ? (start.width / start.height) : (object.width / Math.max(1, object.height));
|
|
1192
|
+
let w = Math.max(1, data.newSize.width);
|
|
1193
|
+
let h = Math.max(1, data.newSize.height);
|
|
1194
|
+
const dw = Math.abs(w - start.width);
|
|
1195
|
+
const dh = Math.abs(h - start.height);
|
|
1196
|
+
if (dw >= dh) { h = Math.round(w / aspect); } else { w = Math.round(h * aspect); }
|
|
1197
|
+
// Минимальная площадь фрейма ~1800px²
|
|
1198
|
+
const minArea = 1800;
|
|
1199
|
+
const area = Math.max(1, w * h);
|
|
1200
|
+
if (area < minArea) {
|
|
1201
|
+
const scale = Math.sqrt(minArea / area);
|
|
1202
|
+
w = Math.round(w * scale);
|
|
1203
|
+
h = Math.round(h * scale);
|
|
1204
|
+
}
|
|
1205
|
+
data.newSize = { width: w, height: h };
|
|
1206
|
+
if (!data.newPosition && this._activeResize && this._activeResize.objectId === data.object) {
|
|
1207
|
+
const hndl = (this._activeResize.handle || '').toLowerCase();
|
|
1208
|
+
const startPos = this._activeResize.startPosition;
|
|
1209
|
+
const sw = this._activeResize.startSize.width;
|
|
1210
|
+
const sh = this._activeResize.startSize.height;
|
|
1211
|
+
let x = startPos.x;
|
|
1212
|
+
let y = startPos.y;
|
|
1213
|
+
if (hndl.includes('w')) { x = startPos.x + (sw - w); }
|
|
1214
|
+
if (hndl.includes('n')) { y = startPos.y + (sh - h); }
|
|
1215
|
+
const isEdge = ['n','s','e','w'].includes(hnl = hndl);
|
|
1216
|
+
if (isEdge) {
|
|
1217
|
+
if (hnl === 'n' || hnl === 's') {
|
|
1218
|
+
x = startPos.x + Math.round((sw - w) / 2);
|
|
1219
|
+
} else if (hnl === 'e' || hnl === 'w') {
|
|
1220
|
+
y = startPos.y + Math.round((sh - h) / 2);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
data.newPosition = { x: Math.round(x), y: Math.round(y) };
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
// Для произвольных фреймов также обеспечим минимальную площадь
|
|
1227
|
+
if (objectType === 'frame' && data.newSize && !(object?.properties && object.properties.lockedAspect === true)) {
|
|
1228
|
+
const minArea = 1800;
|
|
1229
|
+
const w0 = Math.max(1, data.newSize.width);
|
|
1230
|
+
const h0 = Math.max(1, data.newSize.height);
|
|
1231
|
+
const area0 = w0 * h0;
|
|
1232
|
+
if (area0 < minArea) {
|
|
1233
|
+
const scale = Math.sqrt(minArea / Math.max(1, area0));
|
|
1234
|
+
const w = Math.round(w0 * scale);
|
|
1235
|
+
const h = Math.round(h0 * scale);
|
|
1236
|
+
data.newSize = { width: w, height: h };
|
|
1237
|
+
if (!data.newPosition && this._activeResize && this._activeResize.objectId === data.object) {
|
|
1238
|
+
const hndl2 = (this._activeResize.handle || '').toLowerCase();
|
|
1239
|
+
const startPos2 = this._activeResize.startPosition;
|
|
1240
|
+
const sw2 = this._activeResize.startSize.width;
|
|
1241
|
+
const sh2 = this._activeResize.startSize.height;
|
|
1242
|
+
let x2 = startPos2.x;
|
|
1243
|
+
let y2 = startPos2.y;
|
|
1244
|
+
if (hndl2.includes('w')) { x2 = startPos2.x + (sw2 - w); }
|
|
1245
|
+
if (hndl2.includes('n')) { y2 = startPos2.y + (sh2 - h); }
|
|
1246
|
+
data.newPosition = { x: Math.round(x2), y: Math.round(y2) };
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
824
1250
|
// Создаем команду только если размер действительно изменился
|
|
825
1251
|
if (data.oldSize.width !== data.newSize.width ||
|
|
826
1252
|
data.oldSize.height !== data.newSize.height) {
|
|
@@ -833,19 +1259,33 @@ export class CoreMoodBoard {
|
|
|
833
1259
|
newPosition: data.newPosition
|
|
834
1260
|
});
|
|
835
1261
|
|
|
1262
|
+
// Гарантируем согласованность позиции: если UI не передал, вычислим
|
|
1263
|
+
let oldPos = data.oldPosition;
|
|
1264
|
+
let newPos = data.newPosition;
|
|
1265
|
+
if ((!oldPos || !newPos) && this._activeResize && this._activeResize.objectId === data.object) {
|
|
1266
|
+
const h = (this._activeResize.handle || '').toLowerCase();
|
|
1267
|
+
const start = this._activeResize.startPosition;
|
|
1268
|
+
const startSize = this._activeResize.startSize;
|
|
1269
|
+
const dw = (data.newSize?.width || startSize.width) - startSize.width;
|
|
1270
|
+
const dh = (data.newSize?.height || startSize.height) - startSize.height;
|
|
1271
|
+
const calcNew = { x: start.x + (h.includes('w') ? dw : 0), y: start.y + (h.includes('n') ? dh : 0) };
|
|
1272
|
+
if (!oldPos) oldPos = { x: start.x, y: start.y };
|
|
1273
|
+
if (!newPos) newPos = calcNew;
|
|
1274
|
+
}
|
|
836
1275
|
const command = new ResizeObjectCommand(
|
|
837
1276
|
this,
|
|
838
1277
|
data.object,
|
|
839
1278
|
data.oldSize,
|
|
840
1279
|
data.newSize,
|
|
841
|
-
|
|
842
|
-
|
|
1280
|
+
oldPos,
|
|
1281
|
+
newPos
|
|
843
1282
|
);
|
|
844
1283
|
command.setEventBus(this.eventBus);
|
|
845
1284
|
this.history.executeCommand(command);
|
|
846
1285
|
}
|
|
847
1286
|
}
|
|
848
1287
|
this.resizeStartSize = null;
|
|
1288
|
+
this._activeResize = null;
|
|
849
1289
|
});
|
|
850
1290
|
|
|
851
1291
|
// === ОБРАБОТЧИКИ СОБЫТИЙ ВРАЩЕНИЯ ===
|
|
@@ -960,9 +1400,9 @@ export class CoreMoodBoard {
|
|
|
960
1400
|
|
|
961
1401
|
// Обновляем ручки когда объект изменяется через команды (Undo/Redo)
|
|
962
1402
|
this.eventBus.on(Events.Object.TransformUpdated, (data) => {
|
|
963
|
-
console.log(`🔄 Объект ${data.objectId} был изменен через команду, обновляем ручки`);
|
|
964
1403
|
// Обновляем ручки если объект выделен
|
|
965
|
-
if (this.selectTool && this.selectTool.
|
|
1404
|
+
if (this.selectTool && this.selectTool.selection && this.selectTool.selection.has(data.objectId)) {
|
|
1405
|
+
console.log(`🔄 Core: Объект ${data.objectId} изменен через команду, обновляем ручки SelectTool`);
|
|
966
1406
|
this.selectTool.updateResizeHandles();
|
|
967
1407
|
}
|
|
968
1408
|
});
|
|
@@ -974,27 +1414,21 @@ export class CoreMoodBoard {
|
|
|
974
1414
|
});
|
|
975
1415
|
|
|
976
1416
|
// Получение позиции объекта (левый-верх логических координат)
|
|
1417
|
+
// Используем размеры PIXI для согласованности с updateObjectPositionDirect
|
|
977
1418
|
this.eventBus.on(Events.Tool.GetObjectPosition, (data) => {
|
|
978
1419
|
const pixiObject = this.pixi.objects.get(data.objectId);
|
|
979
|
-
if (pixiObject)
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
1420
|
+
if (!pixiObject) return;
|
|
1421
|
+
|
|
1422
|
+
// Всегда используем размеры из PIXI для согласованности
|
|
1423
|
+
const halfW = (pixiObject.width || 0) / 2;
|
|
1424
|
+
const halfH = (pixiObject.height || 0) / 2;
|
|
1425
|
+
data.position = { x: pixiObject.x - halfW, y: pixiObject.y - halfH };
|
|
984
1426
|
});
|
|
985
1427
|
|
|
986
1428
|
// Получение PIXI объекта
|
|
987
1429
|
this.eventBus.on(Events.Tool.GetObjectPixi, (data) => {
|
|
988
|
-
console.log(`🔍 Запрос PIXI объекта для ${data.objectId}`);
|
|
989
|
-
console.log('📋 Доступные PIXI объекты:', Array.from(this.pixi.objects.keys()));
|
|
990
|
-
|
|
991
1430
|
const pixiObject = this.pixi.objects.get(data.objectId);
|
|
992
|
-
|
|
993
|
-
console.log(`✅ PIXI объект найден для ${data.objectId}`);
|
|
994
|
-
data.pixiObject = pixiObject;
|
|
995
|
-
} else {
|
|
996
|
-
console.log(`❌ PIXI объект НЕ найден для ${data.objectId}`);
|
|
997
|
-
}
|
|
1431
|
+
data.pixiObject = pixiObject || null;
|
|
998
1432
|
});
|
|
999
1433
|
|
|
1000
1434
|
// Получение списка всех объектов (с их PIXI и логическими границами)
|
|
@@ -1119,6 +1553,9 @@ export class CoreMoodBoard {
|
|
|
1119
1553
|
if (updates.properties.fontSize !== undefined) {
|
|
1120
1554
|
styleUpdates.fontSize = updates.properties.fontSize;
|
|
1121
1555
|
}
|
|
1556
|
+
if (updates.properties.fontFamily !== undefined) {
|
|
1557
|
+
styleUpdates.fontFamily = updates.properties.fontFamily;
|
|
1558
|
+
}
|
|
1122
1559
|
|
|
1123
1560
|
if (Object.keys(styleUpdates).length > 0) {
|
|
1124
1561
|
instance.setStyle(styleUpdates);
|
|
@@ -1293,7 +1730,61 @@ export class CoreMoodBoard {
|
|
|
1293
1730
|
group.meta.pasteCount = (group.meta.pasteCount || 0) + 1;
|
|
1294
1731
|
const dx = offsetStep * group.meta.pasteCount;
|
|
1295
1732
|
const dy = offsetStep * group.meta.pasteCount;
|
|
1296
|
-
|
|
1733
|
+
|
|
1734
|
+
// Особая логика: фрейм-бандл (фрейм + дети)
|
|
1735
|
+
if (group.meta && group.meta.frameBundle) {
|
|
1736
|
+
const frames = data.filter(o => o && o.type === 'frame');
|
|
1737
|
+
if (frames.length === 1) {
|
|
1738
|
+
const frameOriginal = frames[0];
|
|
1739
|
+
const children = data.filter(o => o && o.id !== frameOriginal.id);
|
|
1740
|
+
const totalToPaste = 1 + children.length;
|
|
1741
|
+
let pastedCount = 0;
|
|
1742
|
+
const newIds = [];
|
|
1743
|
+
let newFrameId = null;
|
|
1744
|
+
|
|
1745
|
+
const onPasted = (payload) => {
|
|
1746
|
+
if (!payload || !payload.newId) return;
|
|
1747
|
+
newIds.push(payload.newId);
|
|
1748
|
+
pastedCount += 1;
|
|
1749
|
+
if (!newFrameId && payload.originalId === frameOriginal.id) {
|
|
1750
|
+
newFrameId = payload.newId;
|
|
1751
|
+
for (const child of children) {
|
|
1752
|
+
const clonedChild = JSON.parse(JSON.stringify(child));
|
|
1753
|
+
clonedChild.properties = clonedChild.properties || {};
|
|
1754
|
+
clonedChild.properties.frameId = newFrameId;
|
|
1755
|
+
const targetPos = {
|
|
1756
|
+
x: (clonedChild.position?.x || 0) + dx,
|
|
1757
|
+
y: (clonedChild.position?.y || 0) + dy
|
|
1758
|
+
};
|
|
1759
|
+
this.clipboard = { type: 'object', data: clonedChild };
|
|
1760
|
+
const cmdChild = new PasteObjectCommand(this, targetPos);
|
|
1761
|
+
cmdChild.setEventBus(this.eventBus);
|
|
1762
|
+
this.history.executeCommand(cmdChild);
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
if (pastedCount === totalToPaste) {
|
|
1766
|
+
this.eventBus.off(Events.Object.Pasted, onPasted);
|
|
1767
|
+
if (this.selectTool && newIds.length > 0) {
|
|
1768
|
+
requestAnimationFrame(() => {
|
|
1769
|
+
this.selectTool.setSelection(newIds);
|
|
1770
|
+
this.selectTool.updateResizeHandles();
|
|
1771
|
+
});
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
};
|
|
1775
|
+
this.eventBus.on(Events.Object.Pasted, onPasted);
|
|
1776
|
+
|
|
1777
|
+
const frameClone = JSON.parse(JSON.stringify(frameOriginal));
|
|
1778
|
+
this.clipboard = { type: 'object', data: frameClone };
|
|
1779
|
+
const cmdFrame = new PasteObjectCommand(this, { x: (frameClone.position?.x || 0) + dx, y: (frameClone.position?.y || 0) + dy });
|
|
1780
|
+
cmdFrame.setEventBus(this.eventBus);
|
|
1781
|
+
this.history.executeCommand(cmdFrame);
|
|
1782
|
+
this.clipboard = group;
|
|
1783
|
+
return;
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
// Обычная вставка группы
|
|
1297
1788
|
let pending = data.length;
|
|
1298
1789
|
const newIds = [];
|
|
1299
1790
|
const onPasted = (payload) => {
|
|
@@ -1302,7 +1793,6 @@ export class CoreMoodBoard {
|
|
|
1302
1793
|
pending -= 1;
|
|
1303
1794
|
if (pending === 0) {
|
|
1304
1795
|
this.eventBus.off(Events.Object.Pasted, onPasted);
|
|
1305
|
-
// Выделяем новую группу и показываем рамку с ручками
|
|
1306
1796
|
if (this.selectTool && newIds.length > 0) {
|
|
1307
1797
|
requestAnimationFrame(() => {
|
|
1308
1798
|
this.selectTool.setSelection(newIds);
|
|
@@ -1313,22 +1803,18 @@ export class CoreMoodBoard {
|
|
|
1313
1803
|
};
|
|
1314
1804
|
this.eventBus.on(Events.Object.Pasted, onPasted);
|
|
1315
1805
|
|
|
1316
|
-
// Вставляем каждый объект группы, сохраняя относительное расположение + общее смещение
|
|
1317
1806
|
for (const original of data) {
|
|
1318
1807
|
const cloned = JSON.parse(JSON.stringify(original));
|
|
1319
1808
|
const targetPos = {
|
|
1320
1809
|
x: (cloned.position?.x || 0) + dx,
|
|
1321
1810
|
y: (cloned.position?.y || 0) + dy
|
|
1322
1811
|
};
|
|
1323
|
-
// Используем существующую логику PasteObjectCommand поверх clipboard типа object
|
|
1324
1812
|
this.clipboard = { type: 'object', data: cloned };
|
|
1325
1813
|
const cmd = new PasteObjectCommand(this, targetPos);
|
|
1326
1814
|
cmd.setEventBus(this.eventBus);
|
|
1327
1815
|
this.history.executeCommand(cmd);
|
|
1328
1816
|
}
|
|
1329
|
-
// После вставки возвращаем clipboard к группе, чтобы можно было ещё раз вставлять с новым смещением
|
|
1330
1817
|
this.clipboard = group;
|
|
1331
|
-
// Рамка появится по завершении обработки всех событий object:pasted
|
|
1332
1818
|
}
|
|
1333
1819
|
});
|
|
1334
1820
|
|
|
@@ -1409,7 +1895,8 @@ export class CoreMoodBoard {
|
|
|
1409
1895
|
* Используется во время перетаскивания для плавного движения
|
|
1410
1896
|
*/
|
|
1411
1897
|
updateObjectPositionDirect(objectId, position) {
|
|
1412
|
-
// position — левый верх (state); приводим к центру в PIXI
|
|
1898
|
+
// position — левый верх (state); приводим к центру в PIXI, используя размеры PIXI объекта
|
|
1899
|
+
// Все объекты используют pivot по центру, поэтому логика одинакова для всех
|
|
1413
1900
|
const pixiObject = this.pixi.objects.get(objectId);
|
|
1414
1901
|
if (pixiObject) {
|
|
1415
1902
|
const halfW = (pixiObject.width || 0) / 2;
|
|
@@ -1494,6 +1981,47 @@ export class CoreMoodBoard {
|
|
|
1494
1981
|
};
|
|
1495
1982
|
const initialWidth = (properties && typeof properties.width === 'number') ? properties.width : 100;
|
|
1496
1983
|
const initialHeight = (properties && typeof properties.height === 'number') ? properties.height : 100;
|
|
1984
|
+
// Зафиксировать пропорции для эмоджи-иконок (квадрат)
|
|
1985
|
+
if (type === 'image' && properties && properties.isEmojiIcon) {
|
|
1986
|
+
const s = Math.max(1, Math.round((initialWidth + initialHeight) / 2));
|
|
1987
|
+
properties.lockedAspect = true;
|
|
1988
|
+
properties.aspect = 1; // квадрат
|
|
1989
|
+
properties.width = s;
|
|
1990
|
+
properties.height = s;
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
// Если создаём НЕ фрейм — проверим, попадает ли центр нового объекта внутрь какого-либо фрейма.
|
|
1994
|
+
// Если да, сразу прикрепляем объект к этому фрейму (properties.frameId)
|
|
1995
|
+
if (type !== 'frame' && position && this.pixi && typeof this.pixi.findObjectByPosition === 'function') {
|
|
1996
|
+
const center = {
|
|
1997
|
+
x: position.x + initialWidth / 2,
|
|
1998
|
+
y: position.y + initialHeight / 2
|
|
1999
|
+
};
|
|
2000
|
+
try {
|
|
2001
|
+
const hostFrame = this.pixi.findObjectByPosition(center, 'frame');
|
|
2002
|
+
if (hostFrame && hostFrame.id) {
|
|
2003
|
+
properties = { ...(properties || {}), frameId: hostFrame.id };
|
|
2004
|
+
}
|
|
2005
|
+
} catch (e) {
|
|
2006
|
+
// fail-safe: не мешаем созданию при ошибке поиска
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
// Именование фреймов: "Фрейм N", где N = количество уже пронумерованных фреймов + 1
|
|
2011
|
+
if (type === 'frame') {
|
|
2012
|
+
try {
|
|
2013
|
+
const objects = this.state?.state?.objects || [];
|
|
2014
|
+
const numberedCount = objects.filter(o => o && o.type === 'frame').reduce((acc, o) => {
|
|
2015
|
+
const t = o?.properties?.title || '';
|
|
2016
|
+
// Считаем только пронумерованные: "Фрейм <число>"
|
|
2017
|
+
return (/^\s*Фрейм\s+\d+\s*$/i.test(t)) ? acc + 1 : acc;
|
|
2018
|
+
}, 0);
|
|
2019
|
+
const nextIndex = numberedCount + 1;
|
|
2020
|
+
properties = { ...(properties || {}), title: `Фрейм ${nextIndex}` };
|
|
2021
|
+
} catch (_) {
|
|
2022
|
+
properties = { ...(properties || {}), title: 'Фрейм 1' };
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
1497
2025
|
const objectData = {
|
|
1498
2026
|
id: generateObjectId(exists),
|
|
1499
2027
|
type,
|