docs-combiner 0.1.16 → 0.1.17

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/dist/renderer.js CHANGED
@@ -2987,6 +2987,29 @@ __webpack_require__.r(__webpack_exports__);
2987
2987
 
2988
2988
  /***/ }),
2989
2989
 
2990
+ /***/ "./node_modules/@mui/icons-material/esm/ContentPaste.js":
2991
+ /*!**************************************************************!*\
2992
+ !*** ./node_modules/@mui/icons-material/esm/ContentPaste.js ***!
2993
+ \**************************************************************/
2994
+ /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
2995
+
2996
+ "use strict";
2997
+ __webpack_require__.r(__webpack_exports__);
2998
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
2999
+ /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
3000
+ /* harmony export */ });
3001
+ /* harmony import */ var _utils_createSvgIcon_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utils/createSvgIcon.js */ "./node_modules/@mui/material/esm/utils/createSvgIcon.js");
3002
+ /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react/jsx-runtime */ "./node_modules/react/jsx-runtime.js");
3003
+ "use client";
3004
+
3005
+
3006
+
3007
+ /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_utils_createSvgIcon_js__WEBPACK_IMPORTED_MODULE_0__["default"])(/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)("path", {
3008
+ d: "M19 2h-4.18C14.4.84 13.3 0 12 0S9.6.84 9.18 2H5c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2m-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1m7 18H5V4h2v3h10V4h2z"
3009
+ }), 'ContentPaste'));
3010
+
3011
+ /***/ }),
3012
+
2990
3013
  /***/ "./node_modules/@mui/icons-material/esm/DeleteOutline.js":
2991
3014
  /*!***************************************************************!*\
2992
3015
  !*** ./node_modules/@mui/icons-material/esm/DeleteOutline.js ***!
@@ -3056,6 +3079,29 @@ __webpack_require__.r(__webpack_exports__);
3056
3079
 
3057
3080
  /***/ }),
3058
3081
 
3082
+ /***/ "./node_modules/@mui/icons-material/esm/Fullscreen.js":
3083
+ /*!************************************************************!*\
3084
+ !*** ./node_modules/@mui/icons-material/esm/Fullscreen.js ***!
3085
+ \************************************************************/
3086
+ /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
3087
+
3088
+ "use strict";
3089
+ __webpack_require__.r(__webpack_exports__);
3090
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
3091
+ /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
3092
+ /* harmony export */ });
3093
+ /* harmony import */ var _utils_createSvgIcon_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utils/createSvgIcon.js */ "./node_modules/@mui/material/esm/utils/createSvgIcon.js");
3094
+ /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react/jsx-runtime */ "./node_modules/react/jsx-runtime.js");
3095
+ "use client";
3096
+
3097
+
3098
+
3099
+ /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_utils_createSvgIcon_js__WEBPACK_IMPORTED_MODULE_0__["default"])(/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)("path", {
3100
+ d: "M7 14H5v5h5v-2H7zm-2-4h2V7h3V5H5zm12 7h-3v2h5v-5h-2zM14 5v2h3v3h2V5z"
3101
+ }), 'Fullscreen'));
3102
+
3103
+ /***/ }),
3104
+
3059
3105
  /***/ "./node_modules/@mui/icons-material/esm/KeyboardArrowDown.js":
3060
3106
  /*!*******************************************************************!*\
3061
3107
  !*** ./node_modules/@mui/icons-material/esm/KeyboardArrowDown.js ***!
@@ -3171,6 +3217,29 @@ __webpack_require__.r(__webpack_exports__);
3171
3217
 
3172
3218
  /***/ }),
3173
3219
 
3220
+ /***/ "./node_modules/@mui/icons-material/esm/OpenInNew.js":
3221
+ /*!***********************************************************!*\
3222
+ !*** ./node_modules/@mui/icons-material/esm/OpenInNew.js ***!
3223
+ \***********************************************************/
3224
+ /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
3225
+
3226
+ "use strict";
3227
+ __webpack_require__.r(__webpack_exports__);
3228
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
3229
+ /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
3230
+ /* harmony export */ });
3231
+ /* harmony import */ var _utils_createSvgIcon_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utils/createSvgIcon.js */ "./node_modules/@mui/material/esm/utils/createSvgIcon.js");
3232
+ /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react/jsx-runtime */ "./node_modules/react/jsx-runtime.js");
3233
+ "use client";
3234
+
3235
+
3236
+
3237
+ /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ((0,_utils_createSvgIcon_js__WEBPACK_IMPORTED_MODULE_0__["default"])(/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)("path", {
3238
+ d: "M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3z"
3239
+ }), 'OpenInNew'));
3240
+
3241
+ /***/ }),
3242
+
3174
3243
  /***/ "./node_modules/@mui/icons-material/esm/Replay.js":
3175
3244
  /*!********************************************************!*\
3176
3245
  !*** ./node_modules/@mui/icons-material/esm/Replay.js ***!
@@ -100845,64 +100914,70 @@ __webpack_require__.r(__webpack_exports__);
100845
100914
  /* harmony export */ });
100846
100915
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "./node_modules/react/index.js");
100847
100916
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
100848
- /* harmony import */ var _prompts__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./prompts */ "./src/prompts.ts");
100849
- /* harmony import */ var _models__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./models */ "./src/models.ts");
100850
- /* harmony import */ var _landingPrompts__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./landingPrompts */ "./src/landingPrompts.ts");
100851
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/CssBaseline/CssBaseline.js");
100852
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Box/Box.js");
100853
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Typography/Typography.js");
100854
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Container/Container.js");
100855
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/IconButton/IconButton.js");
100856
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Card/Card.js");
100857
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/CardContent/CardContent.js");
100858
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Stack/Stack.js");
100859
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/TextField/TextField.js");
100860
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/FormHelperText/FormHelperText.js");
100861
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/CircularProgress/CircularProgress.js");
100862
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Alert/Alert.js");
100863
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Divider/Divider.js");
100864
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Accordion/Accordion.js");
100865
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/AccordionSummary/AccordionSummary.js");
100866
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/AccordionDetails/AccordionDetails.js");
100867
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Button/Button.js");
100868
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/FormControlLabel/FormControlLabel.js");
100869
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Checkbox/Checkbox.js");
100870
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/InputAdornment/InputAdornment.js");
100871
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Tooltip/Tooltip.js");
100872
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/FormControl/FormControl.js");
100873
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/InputLabel/InputLabel.js");
100874
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Select/Select.js");
100875
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/MenuItem/MenuItem.js");
100876
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/ToggleButtonGroup/ToggleButtonGroup.js");
100877
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/ToggleButton/ToggleButton.js");
100878
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Paper/Paper.js");
100917
+ /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react-dom */ "./node_modules/react-dom/index.js");
100918
+ /* harmony import */ var _prompts__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./prompts */ "./src/prompts.ts");
100919
+ /* harmony import */ var _models__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./models */ "./src/models.ts");
100920
+ /* harmony import */ var _landingPrompts__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./landingPrompts */ "./src/landingPrompts.ts");
100921
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/CssBaseline/CssBaseline.js");
100922
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Box/Box.js");
100923
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Typography/Typography.js");
100924
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Container/Container.js");
100925
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/IconButton/IconButton.js");
100926
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Card/Card.js");
100927
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/CardContent/CardContent.js");
100928
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Stack/Stack.js");
100929
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Accordion/Accordion.js");
100930
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/AccordionSummary/AccordionSummary.js");
100931
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/AccordionDetails/AccordionDetails.js");
100932
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/TextField/TextField.js");
100933
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/FormHelperText/FormHelperText.js");
100934
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/CircularProgress/CircularProgress.js");
100935
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Alert/Alert.js");
100936
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Divider/Divider.js");
100937
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Button/Button.js");
100938
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/InputAdornment/InputAdornment.js");
100939
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Tooltip/Tooltip.js");
100940
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/FormControlLabel/FormControlLabel.js");
100941
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Checkbox/Checkbox.js");
100942
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/FormControl/FormControl.js");
100943
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/InputLabel/InputLabel.js");
100944
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Select/Select.js");
100945
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/MenuItem/MenuItem.js");
100946
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/ToggleButtonGroup/ToggleButtonGroup.js");
100947
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/ToggleButton/ToggleButton.js");
100879
100948
  /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Dialog/Dialog.js");
100880
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/DialogTitle/DialogTitle.js");
100881
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/DialogContent/DialogContent.js");
100882
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/DialogActions/DialogActions.js");
100883
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/styles/createTheme.js");
100884
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/styles/ThemeProvider.js");
100885
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/AccountBalance.js");
100886
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_39__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/AttachMoney.js");
100887
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/AutoAwesome.js");
100888
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Brightness4.js");
100889
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Brightness7.js");
100890
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/CheckCircle.js");
100891
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_44__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/CloudDownload.js");
100892
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_45__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/ContentCopy.js");
100893
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/DeleteOutline.js");
100894
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/ExpandMore.js");
100895
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Login.js");
100896
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Logout.js");
100897
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_50__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/NoteAdd.js");
100898
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Replay.js");
100899
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Settings.js");
100900
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Visibility.js");
100901
- /* harmony import */ var _PromptManagerDialog__WEBPACK_IMPORTED_MODULE_54__ = __webpack_require__(/*! ./PromptManagerDialog */ "./src/PromptManagerDialog.tsx");
100902
- /* harmony import */ var _promptOverrides__WEBPACK_IMPORTED_MODULE_55__ = __webpack_require__(/*! ./promptOverrides */ "./src/promptOverrides.ts");
100903
- /* harmony import */ var xlsx__WEBPACK_IMPORTED_MODULE_56__ = __webpack_require__(/*! xlsx */ "./node_modules/xlsx/xlsx.mjs");
100904
- /* harmony import */ var jszip__WEBPACK_IMPORTED_MODULE_57__ = __webpack_require__(/*! jszip */ "./node_modules/jszip/dist/jszip.min.js");
100905
- /* harmony import */ var jszip__WEBPACK_IMPORTED_MODULE_57___default = /*#__PURE__*/__webpack_require__.n(jszip__WEBPACK_IMPORTED_MODULE_57__);
100949
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Paper/Paper.js");
100950
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/DialogTitle/DialogTitle.js");
100951
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/DialogContent/DialogContent.js");
100952
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/DialogActions/DialogActions.js");
100953
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/styles/createTheme.js");
100954
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/styles/ThemeProvider.js");
100955
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_39__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/AccountBalance.js");
100956
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/AttachMoney.js");
100957
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/AutoAwesome.js");
100958
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Brightness4.js");
100959
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Brightness7.js");
100960
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_44__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/CheckCircle.js");
100961
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_45__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Close.js");
100962
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/CloudDownload.js");
100963
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/ContentCopy.js");
100964
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/ContentPaste.js");
100965
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/DeleteOutline.js");
100966
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_50__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/ExpandMore.js");
100967
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Fullscreen.js");
100968
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Login.js");
100969
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Logout.js");
100970
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_54__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/NoteAdd.js");
100971
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_55__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/OpenInNew.js");
100972
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_56__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Replay.js");
100973
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_57__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Settings.js");
100974
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_58__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Visibility.js");
100975
+ /* harmony import */ var _PromptManagerDialog__WEBPACK_IMPORTED_MODULE_59__ = __webpack_require__(/*! ./PromptManagerDialog */ "./src/PromptManagerDialog.tsx");
100976
+ /* harmony import */ var _promptOverrides__WEBPACK_IMPORTED_MODULE_60__ = __webpack_require__(/*! ./promptOverrides */ "./src/promptOverrides.ts");
100977
+ /* harmony import */ var xlsx__WEBPACK_IMPORTED_MODULE_61__ = __webpack_require__(/*! xlsx */ "./node_modules/xlsx/xlsx.mjs");
100978
+ /* harmony import */ var jszip__WEBPACK_IMPORTED_MODULE_62__ = __webpack_require__(/*! jszip */ "./node_modules/jszip/dist/jszip.min.js");
100979
+ /* harmony import */ var jszip__WEBPACK_IMPORTED_MODULE_62___default = /*#__PURE__*/__webpack_require__.n(jszip__WEBPACK_IMPORTED_MODULE_62__);
100980
+
100906
100981
 
100907
100982
 
100908
100983
 
@@ -100972,13 +101047,34 @@ function randomHex8() {
100972
101047
  crypto.getRandomValues(a);
100973
101048
  return Array.from(a, b => b.toString(16).padStart(2, '0')).join('');
100974
101049
  }
100975
- /** Имя файла при загрузке крео: N_xxxxxxxx.png N = номер строки подхода в UI (1–10). */
100976
- function creativeImageUploadFilename(creoApproachUiNumber) {
100977
- return `${creoApproachUiNumber}_${randomHex8()}.png`;
101050
+ /** Из имени файла крео: метка соотношения для каталога (`1_1` / `2_3`), если есть суффикс `_11` / `_23` перед расширением. */
101051
+ function catalogImageAspectUrlValueFromFileName(fileName) {
101052
+ const base = fileName.replace(/\.[^/.]+$/i, '').trim();
101053
+ if (/_11$/i.test(base))
101054
+ return '1_1';
101055
+ if (/_23$/i.test(base))
101056
+ return '2_3';
101057
+ return null;
101058
+ }
101059
+ /** 4 случайные латинские буквы (a–z): один раз на выгрузку каталога, чтобы id не совпадали между разными каталогами при том же бренде/гео. */
101060
+ function randomCatalogCreativeSalt4() {
101061
+ const alphabet = 'abcdefghijklmnopqrstuvwxyz';
101062
+ const bytes = new Uint8Array(4);
101063
+ crypto.getRandomValues(bytes);
101064
+ let s = '';
101065
+ for (let i = 0; i < 4; i++) {
101066
+ s += alphabet[bytes[i] % 26];
101067
+ }
101068
+ return s;
101069
+ }
101070
+ /** Имя файла при загрузке крео: N_xxxxxxxx_11|23.png — суффикс для метки 1:1 / 2:3 в URL каталога. */
101071
+ function creativeImageUploadFilename(creoApproachUiNumber, aspectRatio = '1:1') {
101072
+ const tail = aspectRatio === '2:3' ? '23' : '11';
101073
+ return `${creoApproachUiNumber}_${randomHex8()}_${tail}.png`;
100978
101074
  }
100979
101075
  /**
100980
101076
  * Из имени файла картинки на Drive — номер строки подхода к крео (1–10) для каталога/трекера.
100981
- * Формат: N_xxxxxxxx; снимаются расширение, суффиксы « (N)» от дубликатов Google.
101077
+ * Формат: N_xxxxxxxx или N_xxxxxxxx_11|23; снимаются расширение, суффиксы « (N)» от дубликатов Google.
100982
101078
  * Старый формат (текст_hex8) — возвращается «лейбл» после снятия hex (не только цифры).
100983
101079
  */
100984
101080
  function parseCreoApproachLabelFromImageFileName(fileName) {
@@ -100988,12 +101084,13 @@ function parseCreoApproachLabelFromImageFileName(fileName) {
100988
101084
  while (/\s+\(\d+\)$/.test(s)) {
100989
101085
  s = s.replace(/\s+\(\d+\)$/, '').trim();
100990
101086
  }
100991
- const m = /^(\d+)_([0-9a-fA-F]{8})$/i.exec(s);
101087
+ const m = /^(\d+)_([0-9a-fA-F]{8})(?:_11|_23)?$/i.exec(s);
100992
101088
  if (m)
100993
101089
  return m[1];
100994
- const legacy = s.replace(/_[0-9a-fA-F]{8}$/i, '').trim();
101090
+ const legacy = s.replace(/_[0-9a-fA-F]{8}(?:_11|_23)?$/i, '').trim();
100995
101091
  return legacy || undefined;
100996
101092
  }
101093
+ const DEFAULT_CATALOG_IMAGE_ASPECT_PARAM = 'sub18';
100997
101094
  const DEFAULT_CATALOG_TEXT_APPROACH_PARAM = 'sub19';
100998
101095
  const DEFAULT_CATALOG_CREO_APPROACH_PARAM = 'sub20';
100999
101096
  /** Имя query-параметра для трекера: латиница, цифры, _, - */
@@ -101013,6 +101110,10 @@ function appendCreativeIdToCatalogLink(baseUrl, creativeId, catalogAnalytics, ex
101013
101110
  const q = [`creative_id=${encodeURIComponent(creativeId)}`];
101014
101111
  const textKey = sanitizeCatalogUrlParamKey(catalogAnalytics?.textParamKey ?? '', DEFAULT_CATALOG_TEXT_APPROACH_PARAM);
101015
101112
  const creoKey = sanitizeCatalogUrlParamKey(catalogAnalytics?.creoParamKey ?? '', DEFAULT_CATALOG_CREO_APPROACH_PARAM);
101113
+ const aspectKey = sanitizeCatalogUrlParamKey(catalogAnalytics?.imageAspectParamKey ?? '', DEFAULT_CATALOG_IMAGE_ASPECT_PARAM);
101114
+ if (catalogAnalytics?.imageAspectTag) {
101115
+ q.push(`${aspectKey}=${encodeURIComponent(catalogAnalytics.imageAspectTag)}`);
101116
+ }
101016
101117
  if (catalogAnalytics?.textApproach) {
101017
101118
  q.push(`${textKey}=${encodeURIComponent(catalogAnalytics.textApproach)}`);
101018
101119
  }
@@ -101042,6 +101143,19 @@ function extractChatCompletionText(choice) {
101042
101143
  }
101043
101144
  return '';
101044
101145
  }
101146
+ /** Обрыв ответа по лимиту токенов (OpenRouter / OpenAI-совместимый completion). */
101147
+ function isCompletionTruncatedByTokenLimit(choice) {
101148
+ if (!choice)
101149
+ return false;
101150
+ const fr = choice.finish_reason;
101151
+ const nfr = choice.native_finish_reason;
101152
+ if (fr === 'length')
101153
+ return true;
101154
+ if (nfr === 'max_output_tokens')
101155
+ return true;
101156
+ const ns = typeof nfr === 'string' ? nfr.toLowerCase() : '';
101157
+ return ns === 'length' || ns.includes('max_output');
101158
+ }
101045
101159
  function stripMarkdownJsonFence(raw) {
101046
101160
  let s = raw.trim();
101047
101161
  if (!s.startsWith('```'))
@@ -101082,6 +101196,15 @@ function normalizeValidationErrorText(s) {
101082
101196
  }
101083
101197
  return t;
101084
101198
  }
101199
+ /** Ссылка на файл Google Drive → `uc?export=view` для vision API (как в generateImageWithDALLE). */
101200
+ function toVisionApiImageUrl(url) {
101201
+ const trimmed = url.trim();
101202
+ const fileIdMatch = trimmed.match(/\/file\/d\/([a-zA-Z0-9_-]+)/);
101203
+ if (fileIdMatch) {
101204
+ return `https://drive.google.com/uc?export=view&id=${fileIdMatch[1]}`;
101205
+ }
101206
+ return trimmed;
101207
+ }
101085
101208
  /** Одна и та же формулировка из ответа модели не дублируется в UI и в промпте переделки. */
101086
101209
  function dedupeValidationErrors(errors) {
101087
101210
  const seen = new Set();
@@ -101098,6 +101221,23 @@ function dedupeValidationErrors(errors) {
101098
101221
  }
101099
101222
  return out;
101100
101223
  }
101224
+ /** Один раз на слот: автопеределка при сбое запроса к валидатору или при needs_rebuild с замечаниями модели. */
101225
+ function shouldAutoRemakeAfterValidation(autoRemakeEnabled, validationDisabled, validationResult, imageUrl, validatorAutoRemakeAlreadyDone) {
101226
+ if (!autoRemakeEnabled || validationDisabled)
101227
+ return false;
101228
+ if (!imageUrl || !String(imageUrl).trim())
101229
+ return false;
101230
+ if (validatorAutoRemakeAlreadyDone)
101231
+ return false;
101232
+ if (validationResult.status !== 'needs_rebuild')
101233
+ return false;
101234
+ if (validationResult.checkFailed === true)
101235
+ return true;
101236
+ return validationResult.errors.length > 0;
101237
+ }
101238
+ function formatValidationErrorsForRegeneratePrompt(errors) {
101239
+ return errors.map(err => err.replace(/^ОШИБКА:\s*/i, '')).join('\n').trim();
101240
+ }
101101
101241
  /** Строка «ОШИБКА: нет» при статусе пересборки — противоречие; не считать содержательной ошибкой. */
101102
101242
  function isValidationNegationLine(s) {
101103
101243
  const t = normalizeValidationErrorText(s).toLowerCase();
@@ -101218,7 +101358,7 @@ function formatPairApproachesForDisplay(indices) {
101218
101358
  return [...indices]
101219
101359
  .sort((x, y) => x - y)
101220
101360
  .map(i => {
101221
- const p = _prompts__WEBPACK_IMPORTED_MODULE_1__.PAIR_APPROACH_POOL[i];
101361
+ const p = _prompts__WEBPACK_IMPORTED_MODULE_2__.PAIR_APPROACH_POOL[i];
101222
101362
  const label = p?.name ?? '?';
101223
101363
  return `${i + 1}. ${label}`;
101224
101364
  })
@@ -101226,7 +101366,7 @@ function formatPairApproachesForDisplay(indices) {
101226
101366
  }
101227
101367
  function formatImageCountsForDisplay(counts) {
101228
101368
  const lines = counts
101229
- .map((c, i) => (c > 0 ? `${_prompts__WEBPACK_IMPORTED_MODULE_1__.CREO_APPROACHES[i]?.name ?? `№${i + 1}`}: ×${c}` : null))
101369
+ .map((c, i) => (c > 0 ? `${_prompts__WEBPACK_IMPORTED_MODULE_2__.CREO_APPROACHES[i]?.name ?? `№${i + 1}`}: ×${c}` : null))
101230
101370
  .filter((x) => x != null);
101231
101371
  return lines.length > 0 ? lines.join('\n') : 'ни один подход (все 0)';
101232
101372
  }
@@ -101280,6 +101420,8 @@ function App() {
101280
101420
  // Переводы сгенерированных пар на русский: ключ = 0-based индекс пары
101281
101421
  const [pairTranslations, setPairTranslations] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)({});
101282
101422
  const [translatingPairs, setTranslatingPairs] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
101423
+ /** Ошибка отдельного запроса перевода пар на русский (показать кнопку повтора). */
101424
+ const [pairTranslationFailed, setPairTranslationFailed] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
101283
101425
  /** Папка оффера: загрузка/сохранение контента и крео (как раньше) */
101284
101426
  const [driveFolderUrl, setDriveFolderUrl] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
101285
101427
  /** Корневая папка: только поле + проверка общего доступа и подпапки OFFERS (отдельно от папки оффера) */
@@ -101296,23 +101438,63 @@ function App() {
101296
101438
  const [generateAdditionalInfo, setGenerateAdditionalInfo] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
101297
101439
  const [generatePriceWithCurrency, setGeneratePriceWithCurrency] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
101298
101440
  const [imageModels, setImageModels] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)([]);
101299
- const [selectedImageModel, setSelectedImageModel] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(() => {
101300
- // Load from localStorage or use default
101301
- const saved = localStorage.getItem('selectedImageModel');
101302
- return saved || _models__WEBPACK_IMPORTED_MODULE_2__.MODELS.imageGeneration;
101441
+ const [selectedImageModelSquare, setSelectedImageModelSquare] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(() => {
101442
+ try {
101443
+ return (localStorage.getItem('selectedImageModelSquare') ||
101444
+ localStorage.getItem('selectedImageModel') ||
101445
+ _models__WEBPACK_IMPORTED_MODULE_3__.MODELS.imageGeneration);
101446
+ }
101447
+ catch {
101448
+ return _models__WEBPACK_IMPORTED_MODULE_3__.MODELS.imageGeneration;
101449
+ }
101450
+ });
101451
+ const [selectedImageModelRect, setSelectedImageModelRect] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(() => {
101452
+ try {
101453
+ return (localStorage.getItem('selectedImageModelRect') ||
101454
+ localStorage.getItem('selectedImageModel') ||
101455
+ _models__WEBPACK_IMPORTED_MODULE_3__.MODELS.imageGeneration);
101456
+ }
101457
+ catch {
101458
+ return _models__WEBPACK_IMPORTED_MODULE_3__.MODELS.imageGeneration;
101459
+ }
101303
101460
  });
101304
101461
  const [loadingImageModels, setLoadingImageModels] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
101305
101462
  const [validationModels, setValidationModels] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)([]);
101463
+ const [chatModels, setChatModels] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)([]);
101306
101464
  const [selectedValidationModel, setSelectedValidationModel] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(() => {
101307
101465
  // Load from localStorage or use default
101308
101466
  const saved = localStorage.getItem('selectedValidationModel');
101309
- return saved || _models__WEBPACK_IMPORTED_MODULE_2__.MODELS.creativeValidation;
101467
+ return saved || _models__WEBPACK_IMPORTED_MODULE_3__.MODELS.creativeValidation;
101468
+ });
101469
+ const [selectedContentModel, setSelectedContentModel] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(() => {
101470
+ try {
101471
+ return localStorage.getItem('selectedContentModel') || _models__WEBPACK_IMPORTED_MODULE_3__.MODELS.contentGeneration;
101472
+ }
101473
+ catch {
101474
+ return _models__WEBPACK_IMPORTED_MODULE_3__.MODELS.contentGeneration;
101475
+ }
101476
+ });
101477
+ const [selectedLandingModel, setSelectedLandingModel] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(() => {
101478
+ try {
101479
+ return localStorage.getItem('selectedLandingModel') || _models__WEBPACK_IMPORTED_MODULE_3__.MODELS.landingGeneration;
101480
+ }
101481
+ catch {
101482
+ return _models__WEBPACK_IMPORTED_MODULE_3__.MODELS.landingGeneration;
101483
+ }
101310
101484
  });
101311
101485
  const [loadingValidationModels, setLoadingValidationModels] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
101312
101486
  const [validationDisabled, setValidationDisabled] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(() => {
101313
101487
  const saved = localStorage.getItem('validationDisabled');
101314
101488
  return saved === 'true';
101315
101489
  });
101490
+ const [autoRemakeOnValidatorError, setAutoRemakeOnValidatorError] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(() => {
101491
+ try {
101492
+ return localStorage.getItem('autoRemakeOnValidatorError') === 'true';
101493
+ }
101494
+ catch {
101495
+ return false;
101496
+ }
101497
+ });
101316
101498
  const [imageAspectRatio, setImageAspectRatio] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(() => {
101317
101499
  const saved = localStorage.getItem('imageAspectRatio');
101318
101500
  // Migrate legacy values (4:5, 9:16) to 2:3
@@ -101375,6 +101557,8 @@ function App() {
101375
101557
  'huf': 'HUF',
101376
101558
  'zloty': 'PLN',
101377
101559
  'zlotys': 'PLN',
101560
+ 'zł': 'PLN',
101561
+ 'zl': 'PLN',
101378
101562
  'pln': 'PLN',
101379
101563
  'lei': 'RON',
101380
101564
  'ron': 'RON',
@@ -101436,6 +101620,31 @@ function App() {
101436
101620
  const { price, currency } = parsePriceAndCurrency(value);
101437
101621
  return price.trim() !== '' && currency.trim() !== '';
101438
101622
  };
101623
+ /** Meta product catalog: `12.34 EUR` — number with `.` as decimal, space, 3-letter ISO 4217 (see INSTRUCTION_ROW). */
101624
+ const formatPriceForMetaCatalog = (value) => {
101625
+ const { price, currency } = parsePriceAndCurrency(value);
101626
+ const iso = currency.trim().toUpperCase();
101627
+ if (!price.trim() || !iso || iso.length !== 3 || !/^[A-Z]{3}$/.test(iso))
101628
+ return null;
101629
+ const normalized = price.replace(/\s/g, '').replace(/,/g, '.');
101630
+ if (!/^\d+(?:\.\d+)?$/.test(normalized))
101631
+ return null;
101632
+ const n = parseFloat(normalized);
101633
+ if (!Number.isFinite(n) || n < 0)
101634
+ return null;
101635
+ return `${normalized} ${iso}`;
101636
+ };
101637
+ /** Цена для колонки каталога: валидный ISO из поля или число + USD с предупреждением в вызывающем коде. */
101638
+ const resolveCatalogPriceForExport = (raw) => {
101639
+ const trimmed = raw.trim();
101640
+ const direct = formatPriceForMetaCatalog(trimmed);
101641
+ if (direct)
101642
+ return { cell: direct, usedUsdFallback: false };
101643
+ if (!trimmed)
101644
+ return { cell: null, usedUsdFallback: false };
101645
+ const n = extractLeadingPriceNumber(trimmed);
101646
+ return { cell: `${n} USD`, usedUsdFallback: true };
101647
+ };
101439
101648
  // Transliterate to Latin and slugify (lowercase, dashes)
101440
101649
  const transliterateToSlug = (text) => {
101441
101650
  const cyrillicMap = {
@@ -101486,6 +101695,10 @@ function App() {
101486
101695
  const [generatedImagesData, setGeneratedImagesData] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)([]);
101487
101696
  const [checkingImages, setCheckingImages] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
101488
101697
  const [uploadingImages, setUploadingImages] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
101698
+ /** Индекс крео, для которого идёт удаление файла с Drive (кнопка «Удалить с Диска»). */
101699
+ const [deletingDriveImageIndex, setDeletingDriveImageIndex] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
101700
+ /** Полноэкранный просмотр сгенерированного крео (url + подпись). */
101701
+ const [creoFullscreen, setCreoFullscreen] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
101489
101702
  const [uploadingProduct, setUploadingProduct] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
101490
101703
  const [folderFilesInfo, setFolderFilesInfo] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
101491
101704
  const [rootFolderInfo, setRootFolderInfo] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
@@ -101521,7 +101734,10 @@ function App() {
101521
101734
  const [catalogUrlTextApproachParam, setCatalogUrlTextApproachParam] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(DEFAULT_CATALOG_TEXT_APPROACH_PARAM);
101522
101735
  /** Имя query-параметра для подхода к крео (по умолчанию sub20) */
101523
101736
  const [catalogUrlCreoApproachParam, setCatalogUrlCreoApproachParam] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(DEFAULT_CATALOG_CREO_APPROACH_PARAM);
101524
- /** Query-хвост после creative_id / sub19 / sub20 (редактируемый; пресет RedTrack). */
101737
+ /** Тип изображения в URL каталога: значения 1_1 и 2_3 (параметр по умолчанию sub18). */
101738
+ const [catalogUrlIncludeImageAspect, setCatalogUrlIncludeImageAspect] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(true);
101739
+ const [catalogUrlImageAspectParam, setCatalogUrlImageAspectParam] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(DEFAULT_CATALOG_IMAGE_ASPECT_PARAM);
101740
+ /** Query-хвост после creative_id / sub18 / sub19 / sub20 (редактируемый; пресет RedTrack). */
101525
101741
  const [catalogLinkExtraMacros, setCatalogLinkExtraMacros] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(DEFAULT_CATALOG_LINK_EXTRA_MACROS);
101526
101742
  const [loading, setLoading] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
101527
101743
  const [authLoading, setAuthLoading] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
@@ -101608,7 +101824,7 @@ function App() {
101608
101824
  }
101609
101825
  };
101610
101826
  // Create theme based on mode
101611
- const theme = react__WEBPACK_IMPORTED_MODULE_0___default().useMemo(() => (0,_mui_material__WEBPACK_IMPORTED_MODULE_36__["default"])({
101827
+ const theme = react__WEBPACK_IMPORTED_MODULE_0___default().useMemo(() => (0,_mui_material__WEBPACK_IMPORTED_MODULE_37__["default"])({
101612
101828
  palette: {
101613
101829
  mode: darkMode ? 'dark' : 'light',
101614
101830
  ...(darkMode
@@ -101714,7 +101930,7 @@ function App() {
101714
101930
  };
101715
101931
  loadKey();
101716
101932
  // Load prompt overrides from Electron config
101717
- (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_55__.loadOverridesFromElectron)();
101933
+ (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_60__.loadOverridesFromElectron)();
101718
101934
  }, []);
101719
101935
  // Save form fields to localStorage whenever they change
101720
101936
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
@@ -101781,8 +101997,10 @@ function App() {
101781
101997
  link,
101782
101998
  catalogUrlIncludeTextApproach,
101783
101999
  catalogUrlIncludeCreoApproach,
102000
+ catalogUrlIncludeImageAspect,
101784
102001
  catalogUrlTextApproachParam,
101785
102002
  catalogUrlCreoApproachParam,
102003
+ catalogUrlImageAspectParam,
101786
102004
  catalogLinkExtraMacros,
101787
102005
  driveFolderUrl,
101788
102006
  loadingContentFromDrive
@@ -101803,6 +102021,7 @@ function App() {
101803
102021
  setUploadedLink('');
101804
102022
  setCatalogLinkExtraMacros(DEFAULT_CATALOG_LINK_EXTRA_MACROS);
101805
102023
  setPairTranslations({});
102024
+ setPairTranslationFailed(false);
101806
102025
  setApproachLoadChoice(null);
101807
102026
  return;
101808
102027
  }
@@ -101821,6 +102040,7 @@ function App() {
101821
102040
  setUploadedLink('');
101822
102041
  setCatalogLinkExtraMacros(DEFAULT_CATALOG_LINK_EXTRA_MACROS);
101823
102042
  setPairTranslations({});
102043
+ setPairTranslationFailed(false);
101824
102044
  setApproachLoadChoice(null);
101825
102045
  return;
101826
102046
  }
@@ -101835,6 +102055,7 @@ function App() {
101835
102055
  setUploadedLink('');
101836
102056
  setCatalogLinkExtraMacros(DEFAULT_CATALOG_LINK_EXTRA_MACROS);
101837
102057
  setPairTranslations({});
102058
+ setPairTranslationFailed(false);
101838
102059
  setApproachLoadChoice(null);
101839
102060
  setLoadingContentFromDrive(true);
101840
102061
  setDriveFilesFound({ content: false });
@@ -102425,12 +102646,43 @@ function App() {
102425
102646
  saveConfig(undefined, undefined, data.access_token, newRefreshToken);
102426
102647
  }
102427
102648
  catch (err) {
102428
- alert('Login failed: ' + err.message);
102649
+ const msg = err?.message ?? String(err);
102650
+ if (msg === 'AUTH_SUPERSEDED' || msg === 'LOGIN_CANCELLED') {
102651
+ return;
102652
+ }
102653
+ alert('Login failed: ' + msg);
102429
102654
  }
102430
102655
  finally {
102431
102656
  setAuthLoading(false);
102432
102657
  }
102433
102658
  };
102659
+ const handleReopenAuthBrowser = async () => {
102660
+ const api = getElectronAPI();
102661
+ if (!api?.reopenPendingAuthBrowser) {
102662
+ return;
102663
+ }
102664
+ try {
102665
+ const { ok } = await api.reopenPendingAuthBrowser();
102666
+ if (!ok) {
102667
+ alert('Сначала нажмите «Login with Google», чтобы начать вход.');
102668
+ }
102669
+ }
102670
+ catch (e) {
102671
+ alert('Не удалось открыть браузер: ' + (e?.message ?? String(e)));
102672
+ }
102673
+ };
102674
+ const handleCancelPendingAuth = async () => {
102675
+ const api = getElectronAPI();
102676
+ if (!api?.cancelPendingAuth) {
102677
+ return;
102678
+ }
102679
+ try {
102680
+ await api.cancelPendingAuth();
102681
+ }
102682
+ catch {
102683
+ /* ignore */
102684
+ }
102685
+ };
102434
102686
  const handleLogout = () => {
102435
102687
  setAccessToken('');
102436
102688
  setRefreshToken('');
@@ -102451,18 +102703,39 @@ function App() {
102451
102703
  setImageModels([]);
102452
102704
  }
102453
102705
  };
102454
- const handleImageModelChange = (modelId) => {
102455
- setSelectedImageModel(modelId);
102456
- localStorage.setItem('selectedImageModel', modelId);
102706
+ const handleImageModelSquareChange = (modelId) => {
102707
+ setSelectedImageModelSquare(modelId);
102708
+ localStorage.setItem('selectedImageModelSquare', modelId);
102709
+ };
102710
+ const handleImageModelRectChange = (modelId) => {
102711
+ setSelectedImageModelRect(modelId);
102712
+ localStorage.setItem('selectedImageModelRect', modelId);
102457
102713
  };
102458
102714
  const handleValidationModelChange = (modelId) => {
102459
102715
  setSelectedValidationModel(modelId);
102460
102716
  localStorage.setItem('selectedValidationModel', modelId);
102461
102717
  };
102718
+ const handleContentModelChange = (modelId) => {
102719
+ setSelectedContentModel(modelId);
102720
+ localStorage.setItem('selectedContentModel', modelId);
102721
+ };
102722
+ const handleLandingModelChange = (modelId) => {
102723
+ setSelectedLandingModel(modelId);
102724
+ localStorage.setItem('selectedLandingModel', modelId);
102725
+ };
102462
102726
  const handleValidationDisabledChange = (checked) => {
102463
102727
  setValidationDisabled(checked);
102464
102728
  localStorage.setItem('validationDisabled', String(checked));
102465
102729
  };
102730
+ const handleAutoRemakeOnValidatorErrorChange = (checked) => {
102731
+ setAutoRemakeOnValidatorError(checked);
102732
+ try {
102733
+ localStorage.setItem('autoRemakeOnValidatorError', String(checked));
102734
+ }
102735
+ catch {
102736
+ /* ignore */
102737
+ }
102738
+ };
102466
102739
  // Fetch OpenRouter account balance and key limit
102467
102740
  const fetchOpenRouterBalance = async (apiKey) => {
102468
102741
  const keyToUse = apiKey ?? openaiApiKey;
@@ -102716,17 +102989,19 @@ function App() {
102716
102989
  }
102717
102990
  logToTerminal('log', `✅ Found ${imageGenModels.length} image generation models`);
102718
102991
  setImageModels(imageGenModels);
102719
- // If we have models and the current selection is not in the list, reset to default
102992
+ // Если выбранной модели нет в списке API сброс на дефолт (отдельно для 1:1 и 2:3)
102720
102993
  if (imageGenModels.length > 0) {
102721
- const currentModelExists = imageGenModels.some((m) => m.id === selectedImageModel);
102722
- if (!currentModelExists) {
102723
- // Try to use default model, or first available
102724
- const defaultModel = imageGenModels.find((m) => m.id === _models__WEBPACK_IMPORTED_MODULE_2__.MODELS.imageGeneration) || imageGenModels[0];
102994
+ const defaultModel = imageGenModels.find((m) => m.id === _models__WEBPACK_IMPORTED_MODULE_3__.MODELS.imageGeneration) || imageGenModels[0];
102995
+ const ensureInList = (currentId, setId, storageKey) => {
102996
+ if (imageGenModels.some((m) => m.id === currentId))
102997
+ return;
102725
102998
  if (defaultModel) {
102726
- setSelectedImageModel(defaultModel.id);
102727
- localStorage.setItem('selectedImageModel', defaultModel.id);
102999
+ setId(defaultModel.id);
103000
+ localStorage.setItem(storageKey, defaultModel.id);
102728
103001
  }
102729
- }
103002
+ };
103003
+ ensureInList(selectedImageModelSquare, setSelectedImageModelSquare, 'selectedImageModelSquare');
103004
+ ensureInList(selectedImageModelRect, setSelectedImageModelRect, 'selectedImageModelRect');
102730
103005
  }
102731
103006
  }
102732
103007
  catch (error) {
@@ -102742,6 +103017,7 @@ function App() {
102742
103017
  const keyToUse = apiKey ?? openaiApiKey;
102743
103018
  if (!keyToUse) {
102744
103019
  setValidationModels([]);
103020
+ setChatModels([]);
102745
103021
  return;
102746
103022
  }
102747
103023
  setLoadingValidationModels(true);
@@ -102759,10 +103035,26 @@ function App() {
102759
103035
  if (!response.ok) {
102760
103036
  logToTerminal('warn', `⚠️ Failed to fetch validation models. Status: ${response.status}`);
102761
103037
  setValidationModels([]);
103038
+ setChatModels([]);
102762
103039
  return;
102763
103040
  }
102764
103041
  const data = await response.json();
102765
103042
  logToTerminal('log', `✅ Fetched ${data.data?.length || 0} models from OpenRouter`);
103043
+ // Chat completions (text): exclude dedicated text→image-only endpoints
103044
+ const chatModelsList = (data.data || [])
103045
+ .filter((model) => {
103046
+ const modality = String(model.architecture?.modality || '');
103047
+ if (modality === 'text->image')
103048
+ return false;
103049
+ return true;
103050
+ })
103051
+ .map((model) => ({
103052
+ id: model.id,
103053
+ name: model.name || model.id
103054
+ }))
103055
+ .sort((a, b) => a.name.localeCompare(b.name));
103056
+ logToTerminal('log', `✅ Found ${chatModelsList.length} chat/text models`);
103057
+ setChatModels(chatModelsList);
102766
103058
  // Filter models that support image analysis (vision) - they need to accept image_url in content
102767
103059
  const validationModelsList = (data.data || [])
102768
103060
  .filter((model) => {
@@ -102782,22 +103074,36 @@ function App() {
102782
103074
  .sort((a, b) => a.name.localeCompare(b.name));
102783
103075
  logToTerminal('log', `✅ Found ${validationModelsList.length} validation models`);
102784
103076
  setValidationModels(validationModelsList);
102785
- // If we have models and the current selection is not in the list, reset to default
102786
- if (validationModelsList.length > 0) {
102787
- const currentModelExists = validationModelsList.some((m) => m.id === selectedValidationModel);
102788
- if (!currentModelExists) {
102789
- // Try to use default model, or first available
102790
- const defaultModel = validationModelsList.find((m) => m.id === _models__WEBPACK_IMPORTED_MODULE_2__.MODELS.creativeValidation) || validationModelsList[0];
102791
- if (defaultModel) {
102792
- setSelectedValidationModel(defaultModel.id);
102793
- localStorage.setItem('selectedValidationModel', defaultModel.id);
102794
- }
103077
+ const ensureVisionSelection = (currentId, defaultId, setId, storageKey) => {
103078
+ if (validationModelsList.length === 0)
103079
+ return;
103080
+ if (validationModelsList.some((m) => m.id === currentId))
103081
+ return;
103082
+ const fallback = validationModelsList.find((m) => m.id === defaultId) || validationModelsList[0];
103083
+ if (fallback) {
103084
+ setId(fallback.id);
103085
+ localStorage.setItem(storageKey, fallback.id);
102795
103086
  }
102796
- }
103087
+ };
103088
+ ensureVisionSelection(selectedValidationModel, _models__WEBPACK_IMPORTED_MODULE_3__.MODELS.creativeValidation, setSelectedValidationModel, 'selectedValidationModel');
103089
+ const ensureChatSelection = (currentId, defaultId, setId, storageKey) => {
103090
+ if (chatModelsList.length === 0)
103091
+ return;
103092
+ if (chatModelsList.some((m) => m.id === currentId))
103093
+ return;
103094
+ const fallback = chatModelsList.find((m) => m.id === defaultId) || chatModelsList[0];
103095
+ if (fallback) {
103096
+ setId(fallback.id);
103097
+ localStorage.setItem(storageKey, fallback.id);
103098
+ }
103099
+ };
103100
+ ensureChatSelection(selectedContentModel, _models__WEBPACK_IMPORTED_MODULE_3__.MODELS.contentGeneration, setSelectedContentModel, 'selectedContentModel');
103101
+ ensureChatSelection(selectedLandingModel, _models__WEBPACK_IMPORTED_MODULE_3__.MODELS.landingGeneration, setSelectedLandingModel, 'selectedLandingModel');
102797
103102
  }
102798
103103
  catch (error) {
102799
103104
  logToTerminal('error', '❌ Failed to fetch validation models:', error instanceof Error ? error.message : String(error));
102800
103105
  setValidationModels([]);
103106
+ setChatModels([]);
102801
103107
  }
102802
103108
  finally {
102803
103109
  setLoadingValidationModels(false);
@@ -102825,10 +103131,10 @@ function App() {
102825
103131
  if (additionalInfo.trim()) {
102826
103132
  logMsg('log', `Additional Info: ${additionalInfo}`);
102827
103133
  }
102828
- const systemPrompt = type === 'titles' ? (0,_prompts__WEBPACK_IMPORTED_MODULE_1__.getTitlesSystemPrompt)(geo, false, count) : (0,_prompts__WEBPACK_IMPORTED_MODULE_1__.getTextsSystemPrompt)(geo, false, count);
102829
- const userPrompt = (0,_prompts__WEBPACK_IMPORTED_MODULE_1__.getUserPrompt)(product, geo, additionalInfo, type, false, count);
103134
+ const systemPrompt = type === 'titles' ? (0,_prompts__WEBPACK_IMPORTED_MODULE_2__.getTitlesSystemPrompt)(geo, false, count) : (0,_prompts__WEBPACK_IMPORTED_MODULE_2__.getTextsSystemPrompt)(geo, false, count);
103135
+ const userPrompt = (0,_prompts__WEBPACK_IMPORTED_MODULE_2__.getUserPrompt)(product, geo, additionalInfo, type, false, count);
102830
103136
  const requestBody = {
102831
- model: _models__WEBPACK_IMPORTED_MODULE_2__.MODELS.contentGeneration,
103137
+ model: selectedContentModel,
102832
103138
  messages: [
102833
103139
  { role: 'system', content: systemPrompt },
102834
103140
  { role: 'user', content: userPrompt }
@@ -103137,10 +103443,10 @@ function App() {
103137
103443
  if (!product.trim() || !geo.trim())
103138
103444
  throw new Error('Product and Geo fields are required');
103139
103445
  logMsg('log', `=== Starting pairs generation (${count} pairs in one request) ===`);
103140
- const systemPrompt = (0,_prompts__WEBPACK_IMPORTED_MODULE_1__.getPairsSystemPrompt)(geo, false, count, selectedIndices);
103141
- const userPrompt = (0,_prompts__WEBPACK_IMPORTED_MODULE_1__.getPairsUserPrompt)(product, geo, additionalInfo, false, count, selectedIndices);
103446
+ const systemPrompt = (0,_prompts__WEBPACK_IMPORTED_MODULE_2__.getPairsSystemPrompt)(geo, false, count, selectedIndices);
103447
+ const userPrompt = (0,_prompts__WEBPACK_IMPORTED_MODULE_2__.getPairsUserPrompt)(product, geo, additionalInfo, false, count, selectedIndices);
103142
103448
  const requestBody = {
103143
- model: _models__WEBPACK_IMPORTED_MODULE_2__.MODELS.contentGeneration,
103449
+ model: selectedContentModel,
103144
103450
  messages: [
103145
103451
  { role: 'system', content: systemPrompt },
103146
103452
  { role: 'user', content: userPrompt }
@@ -103252,19 +103558,20 @@ function App() {
103252
103558
  };
103253
103559
  /**
103254
103560
  * Переводит массив сгенерированных пар на русский одним запросом.
103255
- * Результат записывается в pairTranslations (тихо ошибки не показываются юзеру).
103561
+ * При ошибке выставляется pairTranslationFailed (кнопка «Запросить перевод»).
103256
103562
  */
103257
103563
  const translatePairsToRussian = async (pairs) => {
103258
103564
  const validPairs = pairs.filter(p => p.title || p.text);
103259
103565
  if (!openaiApiKey || validPairs.length === 0)
103260
103566
  return;
103261
103567
  setTranslatingPairs(true);
103568
+ setPairTranslationFailed(false);
103262
103569
  try {
103263
103570
  const userContent = pairs
103264
103571
  .map((p, i) => `Пара ${i + 1}:\nЗаголовок: ${p.title || '—'}\nТекст: ${p.text.replace(/\n/g, ' ') || '—'}`)
103265
103572
  .join('\n\n');
103266
103573
  const requestBody = {
103267
- model: _models__WEBPACK_IMPORTED_MODULE_2__.MODELS.contentGeneration,
103574
+ model: selectedContentModel,
103268
103575
  messages: [
103269
103576
  {
103270
103577
  role: 'system',
@@ -103287,6 +103594,7 @@ function App() {
103287
103594
  });
103288
103595
  if (!response.ok) {
103289
103596
  logToTerminal('warn', '[Translate RU] HTTP', response.status, '— перевод пар пропущен');
103597
+ setPairTranslationFailed(true);
103290
103598
  return;
103291
103599
  }
103292
103600
  const data = await response.json();
@@ -103296,11 +103604,13 @@ function App() {
103296
103604
  '';
103297
103605
  if (!raw.trim()) {
103298
103606
  logToTerminal('warn', '[Translate RU] Пустой ответ модели (content), перевод не выполнен');
103607
+ setPairTranslationFailed(true);
103299
103608
  return;
103300
103609
  }
103301
103610
  const parsed = parsePairTranslationsJson(raw);
103302
- if (!parsed || parsed.length === 0) {
103303
- logToTerminal('warn', '[Translate RU] Не удалось разобрать JSON-массив переводов, превью:', raw.substring(0, 280));
103611
+ if (!parsed || parsed.length !== pairs.length) {
103612
+ logToTerminal('warn', '[Translate RU] Неверный JSON или число элементов (ожидалось', pairs.length, '), превью:', raw.substring(0, 280));
103613
+ setPairTranslationFailed(true);
103304
103614
  return;
103305
103615
  }
103306
103616
  const translations = {};
@@ -103308,15 +103618,31 @@ function App() {
103308
103618
  translations[idx] = { titleRu: item.titleRu || '', textRu: item.textRu || '' };
103309
103619
  });
103310
103620
  setPairTranslations(translations);
103621
+ setPairTranslationFailed(false);
103311
103622
  logToTerminal('log', '[Translate RU] OK, пар переведено:', parsed.length);
103312
103623
  }
103313
103624
  catch {
103314
103625
  logToTerminal('warn', '[Translate RU] Ошибка запроса или разбора — см. консоль / лог');
103626
+ setPairTranslationFailed(true);
103315
103627
  }
103316
103628
  finally {
103317
103629
  setTranslatingPairs(false);
103318
103630
  }
103319
103631
  };
103632
+ const handleRetryPairTranslation = () => {
103633
+ if (!openaiApiKey?.trim()) {
103634
+ alert('Укажите API-ключ OpenRouter для запроса перевода');
103635
+ return;
103636
+ }
103637
+ const len = Math.max(generatedTitlesData.length, generatedTextsData.length);
103638
+ const pairs = Array.from({ length: len }, (_, i) => ({
103639
+ title: generatedTitlesData[i]?.title ?? '',
103640
+ text: generatedTextsData[i]?.text ?? ''
103641
+ }));
103642
+ if (!pairs.some(p => (p.title || p.text).trim()))
103643
+ return;
103644
+ translatePairsToRussian(pairs);
103645
+ };
103320
103646
  /** Удалить одну сгенерированную пару (заголовок + текст) и сдвинуть индексы. */
103321
103647
  const handleDeleteGeneratedPair = (pairIndex) => {
103322
103648
  if (!window.confirm(`Удалить пару ${pairIndex + 1}?`))
@@ -103366,8 +103692,9 @@ function App() {
103366
103692
  setTitles('');
103367
103693
  setTexts(['']);
103368
103694
  setPairTranslations({});
103695
+ setPairTranslationFailed(false);
103369
103696
  // Read pairs count from settings (3–10, default 3)
103370
- const pairsCountInit = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_55__.getPairsCount)();
103697
+ const pairsCountInit = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_60__.getPairsCount)();
103371
103698
  // Initialize placeholders
103372
103699
  const initialTitles = Array.from({ length: pairsCountInit }, (_, index) => ({
103373
103700
  index: index + 1,
@@ -103394,13 +103721,85 @@ function App() {
103394
103721
  if (generateAdditionalInfo.trim()) {
103395
103722
  addLog(formatLogMessage('log', `ℹ️ Additional Info: ${generateAdditionalInfo}`));
103396
103723
  }
103397
- // Generate all pairs (title + text) in a single request
103724
+ // Generate all pairs (title + text) in a single request; при ошибке или неполном ответе — автоповтор
103725
+ const MAX_CONTENT_GEN_ATTEMPTS = 3;
103726
+ const CONTENT_GEN_RETRY_DELAY_MS = 2000;
103398
103727
  addLog(formatLogMessage('log', '📋 Generating title+text pairs in a single request...'));
103399
- const selectedIndices = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_55__.getSelectedPairApproaches)();
103400
- setLastUsedApproachIndices(selectedIndices);
103401
- const pairsCount = selectedIndices.length;
103402
- addLog(formatLogMessage('log', `⚙️ Generating ${pairsCount} pairs (approaches: ${selectedIndices.join(', ')})`));
103403
- const pairsResult = await generatePairsWithGPT(generateProduct, generateGeo, generateAdditionalInfo, pairsCount, addLog, selectedIndices);
103728
+ let pairsResult = null;
103729
+ let lastContentGenError = null;
103730
+ for (let attempt = 1; attempt <= MAX_CONTENT_GEN_ATTEMPTS; attempt++) {
103731
+ if (attempt > 1) {
103732
+ addLog(formatLogMessage('warn', `🔄 Повторная генерация текстов (попытка ${attempt}/${MAX_CONTENT_GEN_ATTEMPTS})...`));
103733
+ setGeneratedTitlesData(Array.from({ length: pairsCountInit }, (_, index) => ({
103734
+ index: index + 1,
103735
+ title: '',
103736
+ generating: true,
103737
+ failed: false
103738
+ })));
103739
+ setGeneratedTextsData(Array.from({ length: pairsCountInit }, (_, index) => ({
103740
+ index: index + 1,
103741
+ text: '',
103742
+ generating: true,
103743
+ failed: false
103744
+ })));
103745
+ await new Promise(r => setTimeout(r, CONTENT_GEN_RETRY_DELAY_MS));
103746
+ }
103747
+ const selectedIndices = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_60__.getSelectedPairApproaches)();
103748
+ setLastUsedApproachIndices(selectedIndices);
103749
+ const pairsCount = selectedIndices.length;
103750
+ if (attempt === 1) {
103751
+ addLog(formatLogMessage('log', `⚙️ Generating ${pairsCount} pairs (approaches: ${selectedIndices.join(', ')})`));
103752
+ }
103753
+ try {
103754
+ const result = await generatePairsWithGPT(generateProduct, generateGeo, generateAdditionalInfo, pairsCount, addLog, selectedIndices);
103755
+ pairsResult = result;
103756
+ lastContentGenError = null;
103757
+ const allComplete = result.length > 0 && result.every(p => p.title.trim().length > 0 && p.text.trim().length > 0);
103758
+ if (allComplete) {
103759
+ break;
103760
+ }
103761
+ if (attempt < MAX_CONTENT_GEN_ATTEMPTS) {
103762
+ const nt = result.filter(p => p.title.trim()).length;
103763
+ const nx = result.filter(p => p.text.trim()).length;
103764
+ addLog(formatLogMessage('warn', `⚠️ Ответ неполный (${nt}/${result.length} заголовков, ${nx}/${result.length} текстов). Повтор...`));
103765
+ continue;
103766
+ }
103767
+ addLog(formatLogMessage('warn', '⚠️ Последняя попытка: сохраняем то, что удалось разобрать.'));
103768
+ break;
103769
+ }
103770
+ catch (err) {
103771
+ const msg = err?.message || String(err);
103772
+ lastContentGenError = err instanceof Error ? err : new Error(msg);
103773
+ addLog(formatLogMessage('error', `❌ ${msg}`));
103774
+ if (attempt < MAX_CONTENT_GEN_ATTEMPTS) {
103775
+ addLog(formatLogMessage('warn', `⏳ Повтор через ${CONTENT_GEN_RETRY_DELAY_MS / 1000} с...`));
103776
+ continue;
103777
+ }
103778
+ break;
103779
+ }
103780
+ }
103781
+ if (!pairsResult) {
103782
+ const errorMessage = lastContentGenError?.message || 'Unknown error';
103783
+ addLog(formatLogMessage('error', '❌ === Ошибка генерации текстов (все попытки исчерпаны) ==='));
103784
+ setGeneratedTitlesData(Array.from({ length: pairsCountInit }, (_, index) => ({
103785
+ index: index + 1,
103786
+ title: '',
103787
+ generating: false,
103788
+ failed: true,
103789
+ errorMessage
103790
+ })));
103791
+ setGeneratedTextsData(Array.from({ length: pairsCountInit }, (_, index) => ({
103792
+ index: index + 1,
103793
+ text: '',
103794
+ generating: false,
103795
+ failed: true,
103796
+ errorMessage
103797
+ })));
103798
+ setTitles('');
103799
+ setTexts(['']);
103800
+ alert('Error generating content: ' + errorMessage);
103801
+ return;
103802
+ }
103404
103803
  const titlesResults = pairsResult.map(p => p.title);
103405
103804
  const descriptionsResults = pairsResult.map(p => p.text);
103406
103805
  // Update state
@@ -103539,11 +103938,12 @@ function App() {
103539
103938
  // Build image_config based on the model family.
103540
103939
  // When aspectRatioOverride is provided (e.g. in 'both' mode), use it. Otherwise use imageAspectRatio.
103541
103940
  const effectiveRatio = aspectRatioOverride ?? (imageAspectRatio === 'both' ? '1:1' : imageAspectRatio);
103941
+ const imageGenModelId = effectiveRatio === '2:3' ? selectedImageModelRect : selectedImageModelSquare;
103542
103942
  // openai/gpt-5-image* models use OpenAI's gpt-image-1 underneath, which only supports
103543
103943
  // three discrete sizes: 1024x1024, 1024x1536, 1536x1024.
103544
103944
  // The generic OpenRouter `aspect_ratio` param is ignored for these models.
103545
103945
  // All other image models (Flux, Gemini, Riverflow…) support the `aspect_ratio` param.
103546
- const isGptImageModel = selectedImageModel.includes('gpt-5-image') || selectedImageModel.includes('gpt-image-1');
103946
+ const isGptImageModel = imageGenModelId.includes('gpt-5-image') || imageGenModelId.includes('gpt-image-1');
103547
103947
  let imageConfig;
103548
103948
  let imageAspectRatioParam; // for logging only
103549
103949
  if (isGptImageModel) {
@@ -103566,7 +103966,7 @@ function App() {
103566
103966
  imageConfig = { aspect_ratio: imageAspectRatioParam };
103567
103967
  }
103568
103968
  const requestBody = {
103569
- model: selectedImageModel,
103969
+ model: imageGenModelId,
103570
103970
  messages: [
103571
103971
  {
103572
103972
  role: 'user',
@@ -103747,68 +104147,10 @@ function App() {
103747
104147
  }
103748
104148
  return imageUrl;
103749
104149
  };
103750
- const checkImageWithGPT = async (imageUrl, product, geo) => {
103751
- if (!openaiApiKey) {
103752
- throw new Error('OpenRouter API key is not set');
103753
- }
103754
- const checkPrompt = (0,_prompts__WEBPACK_IMPORTED_MODULE_1__.getImageCheckPrompt)(product, geo);
103755
- const requestBody = {
103756
- model: _models__WEBPACK_IMPORTED_MODULE_2__.MODELS.imageCheck,
103757
- messages: [
103758
- {
103759
- role: 'user',
103760
- content: [
103761
- { type: 'text', text: checkPrompt },
103762
- { type: 'image_url', image_url: { url: imageUrl } }
103763
- ]
103764
- }
103765
- ],
103766
- max_tokens: 1000
103767
- };
103768
- const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
103769
- method: 'POST',
103770
- headers: {
103771
- 'Content-Type': 'application/json',
103772
- 'Authorization': `Bearer ${openaiApiKey}`,
103773
- 'HTTP-Referer': window.location.origin || 'https://docs-combiner.app',
103774
- 'X-Title': 'Docs Combiner'
103775
- },
103776
- body: JSON.stringify(requestBody)
103777
- });
103778
- const responseText = await response.text();
103779
- if (!response.ok) {
103780
- let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
103781
- try {
103782
- const error = JSON.parse(responseText);
103783
- errorMessage = error.error?.message || error.message || errorMessage;
103784
- }
103785
- catch (e) {
103786
- errorMessage = `${errorMessage}. Response: ${responseText.substring(0, 200)}`;
103787
- }
103788
- throw new Error(errorMessage);
103789
- }
103790
- let data;
103791
- try {
103792
- data = JSON.parse(responseText);
103793
- }
103794
- catch (e) {
103795
- throw new Error(`Invalid JSON response: ${responseText.substring(0, 200)}`);
103796
- }
103797
- const content = data.choices?.[0]?.message?.content || '{}';
103798
- // Update balance after successful API call
103799
- fetchOpenRouterBalance();
103800
- try {
103801
- return JSON.parse(content);
103802
- }
103803
- catch (e) {
103804
- // If parsing fails, assume approved
103805
- return { approved: true };
103806
- }
103807
- };
103808
104150
  // NOTE: Validator is intentionally MORE PERMISSIVE than the generation prompt.
103809
104151
  // We keep the generation prompt strict to shape outputs, but we soften the validator to avoid false negatives
103810
104152
  // (allowed here: 2-line headline, benefit/promise headline, CTA not centered, discount as strong as CTA if CTA stays readable).
103811
- const validateCreativeImage = async (imageUrl, product, geo, addLog, approachName) => {
104153
+ const validateCreativeImage = async (imageUrl, product, geo, addLog, approachName, productReferenceUrl) => {
103812
104154
  if (!openaiApiKey) {
103813
104155
  throw new Error('OpenRouter API key is not set');
103814
104156
  }
@@ -103822,17 +104164,18 @@ function App() {
103822
104164
  logMsg('log', `🌍 GEO: ${geo}`);
103823
104165
  logMsg('log', `🖼️ Image URL тип: ${imageUrl.startsWith('data:') ? 'data URL (base64)' : imageUrl.startsWith('http') ? 'HTTP URL' : 'другой'}`);
103824
104166
  logMsg('log', `🖼️ Image URL длина: ${imageUrl.length} символов`);
103825
- // Получаем ключевое слово для GEO (примеры из промпта)
103826
- const geoKeywords = {
103827
- 'HU': ['prostata', 'paraziták', 'ízület', 'potencia', 'kilók', 'látás'],
103828
- 'ES': ['próstata', 'parásitos', 'articulación', 'potencia', 'kilos', 'visión'],
103829
- 'PL': ['prostata', 'pasożyty', 'stawy', 'potencja', 'kilo', 'wzrok'],
103830
- 'RO': ['prostată', 'paraziți', 'articulații', 'potență', 'kilo', 'vedere'],
103831
- 'CZ': ['prostata', 'paraziti', 'klouby', 'potence', 'kilo', 'zrak'],
103832
- 'SK': ['prostata', 'parazity', 'kĺby', 'potencia', 'kilo', 'zrak']
103833
- };
103834
- const keywords = geoKeywords[geo.toUpperCase()] || ['продукт', 'проблема'];
103835
- logMsg('log', `🔑 Ключевые слова: ${keywords.join(', ')}`);
104167
+ const refRaw = productReferenceUrl?.trim() || '';
104168
+ const referenceVisionUrl = refRaw ? toVisionApiImageUrl(refRaw) : '';
104169
+ const withProductReference = Boolean(referenceVisionUrl);
104170
+ if (withProductReference) {
104171
+ logMsg('log', `🖼️ Эталон продукта: ${referenceVisionUrl.startsWith('data:') ? 'data URL' : 'URL'} (длина ${referenceVisionUrl.length})`);
104172
+ }
104173
+ else {
104174
+ logMsg('log', '🖼️ Эталон продукта не передан — проверка только по креативу');
104175
+ }
104176
+ // Тема для валидатора — из брифа продукта; фиксированные списки по GEO убраны (давали ложные якоря вроде «простата» для всех ниш).
104177
+ const keywords = [];
104178
+ logMsg('log', '🔑 Тема заголовка: сверка с полем продукта/брифа (без общего списка ключей по GEO)');
103836
104179
  const { price: briefPrice, currency: briefCurrency, currencySymbol } = parsePriceAndCurrency(generatePriceWithCurrency);
103837
104180
  let priceBriefForValidation = '';
103838
104181
  if (briefPrice && briefCurrency) {
@@ -103847,19 +104190,25 @@ function App() {
103847
104190
  else {
103848
104191
  logMsg('log', '💶 Поле цены в приложении пустое — валидатор сверяет только визуал двух цен');
103849
104192
  }
103850
- const validationPrompt = (0,_prompts__WEBPACK_IMPORTED_MODULE_1__.getValidationPrompt)(product, geo, keywords, approachName, undefined, priceBriefForValidation);
104193
+ const validationPrompt = (withProductReference ? `${(0,_prompts__WEBPACK_IMPORTED_MODULE_2__.getValidationProductReferencePreamble)()}\n` : '') +
104194
+ (0,_prompts__WEBPACK_IMPORTED_MODULE_2__.getValidationPrompt)(product, geo, keywords, approachName, undefined, priceBriefForValidation);
104195
+ const visionContent = [
104196
+ { type: 'text', text: validationPrompt },
104197
+ { type: 'image_url', image_url: { url: imageUrl } }
104198
+ ];
104199
+ if (withProductReference) {
104200
+ visionContent.push({ type: 'image_url', image_url: { url: referenceVisionUrl } });
104201
+ }
103851
104202
  const requestBody = {
103852
104203
  model: selectedValidationModel,
103853
104204
  messages: [
103854
104205
  {
103855
104206
  role: 'user',
103856
- content: [
103857
- { type: 'text', text: validationPrompt },
103858
- { type: 'image_url', image_url: { url: imageUrl } }
103859
- ]
104207
+ content: visionContent
103860
104208
  }
103861
104209
  ],
103862
- max_tokens: 8000
104210
+ // Длинный пошаговый промпт валидации + reasoning у части моделей съедают бюджет; при малом лимите ответ обрывается вроде PRICE: "
104211
+ max_tokens: 16000
103863
104212
  };
103864
104213
  logMsg('log', '🔍 Отправка запроса на проверку креатива...');
103865
104214
  logMsg('log', `📊 Модель валидации: ${selectedValidationModel}`);
@@ -103975,8 +104324,13 @@ function App() {
103975
104324
  logMsg('error', `❌ Первые 500 символов ответа: ${responseText.substring(0, 500)}`);
103976
104325
  throw new Error(`Invalid JSON response: ${responseText.substring(0, 200)}`);
103977
104326
  }
103978
- const content = data.choices?.[0]?.message?.content || '';
103979
- logMsg('log', `✅ Получен ответ от модели проверки. Длина контента: ${content.length} символов`);
104327
+ const choice0 = data.choices?.[0];
104328
+ const content = extractChatCompletionText(choice0) || '';
104329
+ const truncatedByLimit = isCompletionTruncatedByTokenLimit(choice0);
104330
+ logMsg('log', `✅ Получен ответ от модели проверки. Длина контента: ${content.length} символов; finish_reason=${choice0?.finish_reason ?? '—'}; native_finish_reason=${choice0?.native_finish_reason ?? '—'}`);
104331
+ if (truncatedByLimit) {
104332
+ logMsg('error', '⚠️ Ответ обрезан по лимиту выходных токенов (часто у моделей с reasoning). В OpenRouter в Completion будет finish_reason=length или аналог. Это не «баг канала», а исчерпанный max_tokens.');
104333
+ }
103980
104334
  if (content.length > 0) {
103981
104335
  logMsg('log', `📄 Первые 300 символов ответа: ${content.substring(0, 300)}`);
103982
104336
  }
@@ -104022,13 +104376,17 @@ function App() {
104022
104376
  const snippet = status === 'needs_rebuild' && errors.length === 0
104023
104377
  ? extractValidationFinalSnippet(content, 900)
104024
104378
  : null;
104025
- const finalErrors = errors.length > 0
104379
+ let finalErrors = errors.length > 0
104026
104380
  ? errors
104027
104381
  : status === 'needs_rebuild'
104028
104382
  ? (snippet
104029
104383
  ? [`Валидатор не выписал строки «ОШИБКА:». Фрагмент ответа: ${snippet}`]
104030
104384
  : ['Валидатор указал пересборку, но не перечислил причины (нет строк «ОШИБКА:»). Откройте полный текст ответа модели в логе или повторите проверку.'])
104031
104385
  : [];
104386
+ if (truncatedByLimit) {
104387
+ const truncationMsg = 'ОШИБКА: ответ валидатора обрезан по лимиту токенов (OpenRouter/провайдер вернули неполный текст — обычно finish_reason=length). Повторите проверку или выберите модель без тяжёлого reasoning.';
104388
+ finalErrors = dedupeValidationErrors([truncationMsg, ...finalErrors]);
104389
+ }
104032
104390
  logMsg('log', `✅ === Валидация завершена ===`);
104033
104391
  logMsg('log', `⏱️ Общее время: ${Math.round(totalElapsed / 1000)}s`);
104034
104392
  logMsg('log', `📊 Статус: ${status === 'ok' ? '✅ OK' : '❌ НУЖНА ПЕРЕСБОРКА'}`);
@@ -104256,10 +104614,10 @@ function App() {
104256
104614
  logMsg('log', '=== Starting landing page HTML generation ===');
104257
104615
  logMsg('log', '📦 Product:', product);
104258
104616
  logMsg('log', '🌍 Geo:', geo);
104259
- const systemPrompt = (0,_landingPrompts__WEBPACK_IMPORTED_MODULE_3__.getLandingPageSystemPrompt)();
104260
- const userPrompt = (0,_landingPrompts__WEBPACK_IMPORTED_MODULE_3__.getLandingPageUserPrompt)(product, geo);
104617
+ const systemPrompt = (0,_landingPrompts__WEBPACK_IMPORTED_MODULE_4__.getLandingPageSystemPrompt)();
104618
+ const userPrompt = (0,_landingPrompts__WEBPACK_IMPORTED_MODULE_4__.getLandingPageUserPrompt)(product, geo);
104261
104619
  const requestBody = {
104262
- model: _models__WEBPACK_IMPORTED_MODULE_2__.MODELS.landingGeneration,
104620
+ model: selectedLandingModel,
104263
104621
  messages: [
104264
104622
  { role: 'system', content: systemPrompt },
104265
104623
  { role: 'user', content: userPrompt }
@@ -104335,7 +104693,7 @@ function App() {
104335
104693
  addLog(formatLogMessage(level, ...args));
104336
104694
  };
104337
104695
  logMsg('log', '📦 Creating ZIP archive with HTML and product image...');
104338
- const zip = new (jszip__WEBPACK_IMPORTED_MODULE_57___default())();
104696
+ const zip = new (jszip__WEBPACK_IMPORTED_MODULE_62___default())();
104339
104697
  // Replace product image path in HTML to match actual filename (png/jpg/webp)
104340
104698
  const htmlWithProductPath = htmlContent.replace(/src=["']product\.(png|jpe?g|webp)["']/gi, `src="${productImageName}"`);
104341
104699
  zip.file('index.html', htmlWithProductPath);
@@ -104676,10 +105034,10 @@ function App() {
104676
105034
  }
104677
105035
  addLog(formatLogMessage('log', '✅ Product image found'));
104678
105036
  // Generate images with different approaches (количество по каждому подходу)
104679
- const expandedTasks = (0,_prompts__WEBPACK_IMPORTED_MODULE_1__.getCreoApproachExpandedTasks)();
105037
+ const expandedTasks = (0,_prompts__WEBPACK_IMPORTED_MODULE_2__.getCreoApproachExpandedTasks)();
104680
105038
  if (expandedTasks.length === 0) {
104681
105039
  addLog(formatLogMessage('error', '❌ Укажите количество изображений (хотя бы 1) в настройках подходов'));
104682
- alert('Укажите количество изображений (1–4) хотя бы для одного подхода в настройках');
105040
+ alert(`Укажите количество изображений (1–${_promptOverrides__WEBPACK_IMPORTED_MODULE_60__.MAX_IMAGES_PER_CREO_APPROACH}) хотя бы для одного подхода в настройках`);
104683
105041
  setGeneratingImages(false);
104684
105042
  return;
104685
105043
  }
@@ -104699,12 +105057,12 @@ function App() {
104699
105057
  ? `\n📋 ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ О ПРОДУКТЕ: ${generateAdditionalInfo.trim()}`
104700
105058
  : '';
104701
105059
  const imagePrompts = tasks.map(t => {
104702
- const basePromptStructure = (0,_prompts__WEBPACK_IMPORTED_MODULE_1__.getImageGenerationBasePrompt)(generateGeo, generatePrice, currencyForPrompt, undefined, t.ratio);
105060
+ const basePromptStructure = (0,_prompts__WEBPACK_IMPORTED_MODULE_2__.getImageGenerationBasePrompt)(generateGeo, generatePrice, currencyForPrompt, undefined, t.ratio);
104703
105061
  const bulletsLine = t.approach.noBullets
104704
105062
  ? t.approach.bulletsFocus
104705
105063
  : (() => {
104706
- const [b1, b2, b3] = (0,_prompts__WEBPACK_IMPORTED_MODULE_1__.pickRandomBullets)(t.approach.name);
104707
- return `ОБЯЗАТЕЛЬНЫЕ БУЛЛЕТЫ (используй именно эти 3, в указанном порядке; переведи на язык ${generateGeo}): 1) ${b1} 2) ${b2} 3) ${b3}`;
105064
+ const [b1, b2, b3] = (0,_prompts__WEBPACK_IMPORTED_MODULE_2__.pickRandomBullets)(t.approach.name);
105065
+ return `ОБЯЗАТЕЛЬНЫЕ БУЛЛЕТЫ три опорных смысла (порядок сохранить); переведи на язык ${generateGeo} коротко (2–4 слова каждый). Тема каждого буллита должна совпасть с **назначением продукта из брифа/упаковки**. Если формулировка по смыслу относится к другой вертикали здоровья — не копируй её дословно: сохрани **тип** выгоды (действие / срок / соцдоказательство / состав / качество жизни), но **перепиши слова под этот оффер**. Запрещено подставлять клише из чужих ниш (суставы и «лёгкость движения», необоснованная «боль», желудок и т.п.). 1) ${b1} 2) ${b2} 3) ${b3}`;
104708
105066
  })();
104709
105067
  return `🏷️ ПРОДУКТ: ${generateProduct}${additionalInfoLine}\n\n${basePromptStructure}🏷️ ПРОДУКТ (повторение для ясности): ${generateProduct}${additionalInfoLine}\n🎯 ПОДХОД: ${t.approach.name}\n\n${t.approach.prompt}\n\n${t.approach.headlineAngle}\n\n${bulletsLine}\n\nТекст — строго следуй правилам этого подхода (верхний заголовок обязателен; на макете не пиши слова HOOK/CTA/CAPS; буллиты добавляй только если они разрешены для подхода).`;
104710
105068
  });
@@ -104726,7 +105084,8 @@ function App() {
104726
105084
  customRegeneratePrompt: '',
104727
105085
  failed: false,
104728
105086
  generating: false, // In sequential mode, images start queued (not generating)
104729
- remakeCount: 0
105087
+ remakeCount: 0,
105088
+ validatorTransportAutoRemakeDone: false
104730
105089
  }));
104731
105090
  setGeneratedImagesData(initialPlaceholders);
104732
105091
  addLog(formatLogMessage('log', `📝 Generated prompts for ${tasks.length} images${useBoth ? ' (1:1 + 2:3 на каждый подход)' : ''}`));
@@ -104760,7 +105119,8 @@ function App() {
104760
105119
  checkResult: undefined,
104761
105120
  checkErrors: undefined,
104762
105121
  originalPrompt: prompt,
104763
- productImageUrl: productImage.url
105122
+ productImageUrl: productImage.url,
105123
+ validatorTransportAutoRemakeDone: false
104764
105124
  }
104765
105125
  : img));
104766
105126
  try {
@@ -104787,7 +105147,7 @@ function App() {
104787
105147
  ? { status: 'ok', result: 'Проверка отключена', errors: [], checkFailed: false }
104788
105148
  : await (async () => {
104789
105149
  try {
104790
- const res = await validateCreativeImage(imageUrl, generateProduct, generateGeo, addLog, approachName);
105150
+ const res = await validateCreativeImage(imageUrl, generateProduct, generateGeo, addLog, approachName, productImage.url);
104791
105151
  if (!res)
104792
105152
  throw new Error('No validation result');
104793
105153
  return { ...res, checkFailed: false };
@@ -104803,24 +105163,45 @@ function App() {
104803
105163
  };
104804
105164
  }
104805
105165
  })();
104806
- setGeneratedImagesData(prev => {
104807
- const updated = prev.map(img => img.index === imageIndex
104808
- ? {
104809
- ...img,
104810
- checking: false,
104811
- checkFailed: validationResult.checkFailed ?? false,
104812
- checkStatus: validationResult.status,
104813
- checkResult: validationResult.result,
104814
- checkErrors: validationResult.errors,
104815
- customRegeneratePrompt: validationResult.errors.length > 0
104816
- ? validationResult.errors.map(err => err.replace(/^ОШИБКА:\s*/i, '')).join('\n')
104817
- : '',
104818
- originalPrompt: img.originalPrompt || prompt,
104819
- productImageUrl: img.productImageUrl || productImage.url
104820
- }
104821
- : img);
104822
- return updated;
105166
+ const approachLabel = approachName + (useBoth ? ` (${ratio})` : '');
105167
+ let scheduleValidatorAutoRemake = false;
105168
+ const formattedValidationErrors = formatValidationErrorsForRegeneratePrompt(validationResult.errors);
105169
+ const validatorTransportRemake = validationResult.checkFailed === true;
105170
+ (0,react_dom__WEBPACK_IMPORTED_MODULE_1__.flushSync)(() => {
105171
+ setGeneratedImagesData(prev => {
105172
+ const cur = prev.find(i => i.index === imageIndex);
105173
+ const useAuto = shouldAutoRemakeAfterValidation(autoRemakeOnValidatorError, validationDisabled, validationResult, imageUrl, cur?.validatorTransportAutoRemakeDone);
105174
+ scheduleValidatorAutoRemake = useAuto;
105175
+ return prev.map(img => img.index === imageIndex
105176
+ ? {
105177
+ ...img,
105178
+ checking: false,
105179
+ checkFailed: validationResult.checkFailed ?? false,
105180
+ checkStatus: validationResult.status,
105181
+ checkResult: validationResult.result,
105182
+ checkErrors: validationResult.errors,
105183
+ customRegeneratePrompt: useAuto && validatorTransportRemake
105184
+ ? ''
105185
+ : formattedValidationErrors,
105186
+ originalPrompt: img.originalPrompt || prompt,
105187
+ productImageUrl: img.productImageUrl || productImage.url,
105188
+ validatorTransportAutoRemakeDone: useAuto
105189
+ ? true
105190
+ : (img.validatorTransportAutoRemakeDone ?? false)
105191
+ }
105192
+ : img);
105193
+ });
104823
105194
  });
105195
+ if (scheduleValidatorAutoRemake) {
105196
+ addLog(formatLogMessage('log', `🔄 Автопеределка с нуля изображения ${imageIndex} после ошибки валидатора (один раз)...`));
105197
+ await handleRegenerateImageFresh({
105198
+ index: imageIndex,
105199
+ approach: approachLabel,
105200
+ aspectRatio: ratio,
105201
+ originalPrompt: prompt,
105202
+ productImageUrl: productImage.url,
105203
+ }, { fromAutoValidatorRemake: true });
105204
+ }
104824
105205
  return {
104825
105206
  index: imageIndex,
104826
105207
  imageUrl,
@@ -104971,7 +105352,7 @@ function App() {
104971
105352
  logToTerminal('error', '❌ Error:', err?.message || err);
104972
105353
  }
104973
105354
  };
104974
- const handleRegenerateImage = async (imageData) => {
105355
+ const handleRegenerateImage = async (imageData, opts) => {
104975
105356
  if (!imageData.originalPrompt || !imageData.productImageUrl) {
104976
105357
  alert('Не удалось найти оригинальный промпт или изображение продукта для переделки');
104977
105358
  return;
@@ -104996,9 +105377,11 @@ function App() {
104996
105377
  alert(regenPub.message);
104997
105378
  return;
104998
105379
  }
104999
- // Get current customRegeneratePrompt from state
105380
+ // Get current customRegeneratePrompt from state (явная передача из вызова — приоритет, см. автопеределку)
105000
105381
  const currentImageData = generatedImagesData.find(img => img.index === imageData.index);
105001
- const customPrompt = currentImageData?.customRegeneratePrompt?.trim() || '';
105382
+ const customPrompt = imageData.customRegeneratePrompt?.trim() ||
105383
+ currentImageData?.customRegeneratePrompt?.trim() ||
105384
+ '';
105002
105385
  // Use current imageUrl from state, not from props (to get the latest version)
105003
105386
  const currentImageUrl = currentImageData?.imageUrl || imageData.imageUrl;
105004
105387
  // Update status to regenerating - clear old image URL immediately to force re-render
@@ -105007,7 +105390,8 @@ function App() {
105007
105390
  ...img,
105008
105391
  regenerating: true,
105009
105392
  regenerateStartTime: Date.now(),
105010
- checkStatus: 'pending'
105393
+ checkStatus: 'pending',
105394
+ ...(!opts?.fromAutoValidatorRemake ? { validatorTransportAutoRemakeDone: false } : {}),
105011
105395
  }
105012
105396
  : img));
105013
105397
  addLog(formatLogMessage('log', `🔄 Переделка изображения ${imageData.index} (${imageData.approach})...`));
@@ -105149,6 +105533,8 @@ ${imageData.originalPrompt}
105149
105533
  checkResult: undefined,
105150
105534
  checkErrors: undefined,
105151
105535
  uploaded: false, // Reset uploaded status since it's a new image
105536
+ driveUploadedFileId: undefined,
105537
+ uploadedPreviewHidden: undefined,
105152
105538
  customRegeneratePrompt: '', // Reset custom prompt after regeneration
105153
105539
  failed: false, // Mark as successful
105154
105540
  remakeCount: (img.remakeCount ?? 0) + 1
@@ -105161,27 +105547,48 @@ ${imageData.originalPrompt}
105161
105547
  }
105162
105548
  else {
105163
105549
  addLog(formatLogMessage('log', `🔍 Проверка переделанного изображения ${imageData.index}...`));
105164
- validationResult = await validateCreativeImage(newImageUrl, generateProduct, generateGeo, addLog, imageData.approach);
105550
+ validationResult = await validateCreativeImage(newImageUrl, generateProduct, generateGeo, addLog, imageData.approach, imageData.productImageUrl);
105165
105551
  }
105166
- // Update with validation result
105167
- setGeneratedImagesData(prev => {
105168
- const updated = prev.map(img => img.index === imageData.index
105169
- ? {
105170
- ...img,
105171
- checking: false,
105172
- checkStatus: validationResult.status,
105173
- checkResult: validationResult.result,
105174
- checkErrors: validationResult.errors,
105175
- customRegeneratePrompt: validationResult.errors.length > 0
105176
- ? validationResult.errors.map(err => err.replace(/^ОШИБКА:\s*/i, '')).join('\n')
105177
- : '',
105178
- // Ensure originalPrompt and productImageUrl are preserved
105179
- originalPrompt: img.originalPrompt || imageData.originalPrompt,
105180
- productImageUrl: img.productImageUrl || imageData.productImageUrl
105181
- }
105182
- : img);
105183
- return updated;
105552
+ // Update with validation result (+ одна автопеределка при needs_rebuild с замечаниями, если включено)
105553
+ const outcomeForAuto = {
105554
+ status: validationResult.status,
105555
+ errors: validationResult.errors,
105556
+ };
105557
+ const formattedRegenErrors = formatValidationErrorsForRegeneratePrompt(validationResult.errors);
105558
+ const regenTransportRemake = outcomeForAuto.checkFailed === true;
105559
+ let scheduleAfterRegenValidation = false;
105560
+ (0,react_dom__WEBPACK_IMPORTED_MODULE_1__.flushSync)(() => {
105561
+ setGeneratedImagesData(prev => {
105562
+ const cur = prev.find(i => i.index === imageData.index);
105563
+ const useAuto = shouldAutoRemakeAfterValidation(autoRemakeOnValidatorError, validationDisabled, outcomeForAuto, updatedImageUrl, cur?.validatorTransportAutoRemakeDone);
105564
+ scheduleAfterRegenValidation = useAuto;
105565
+ return prev.map(img => img.index === imageData.index
105566
+ ? {
105567
+ ...img,
105568
+ checking: false,
105569
+ checkStatus: validationResult.status,
105570
+ checkResult: validationResult.result,
105571
+ checkErrors: validationResult.errors,
105572
+ customRegeneratePrompt: useAuto && regenTransportRemake ? '' : formattedRegenErrors,
105573
+ originalPrompt: img.originalPrompt || imageData.originalPrompt,
105574
+ productImageUrl: img.productImageUrl || imageData.productImageUrl,
105575
+ validatorTransportAutoRemakeDone: useAuto
105576
+ ? true
105577
+ : (img.validatorTransportAutoRemakeDone ?? false),
105578
+ }
105579
+ : img);
105580
+ });
105184
105581
  });
105582
+ if (scheduleAfterRegenValidation && imageData.originalPrompt && imageData.productImageUrl) {
105583
+ addLog(formatLogMessage('log', `🔄 Автопеределка с нуля изображения ${imageData.index} после ошибки валидатора (один раз)...`));
105584
+ await handleRegenerateImageFresh({
105585
+ index: imageData.index,
105586
+ approach: imageData.approach,
105587
+ aspectRatio: imageData.aspectRatio,
105588
+ originalPrompt: imageData.originalPrompt,
105589
+ productImageUrl: imageData.productImageUrl,
105590
+ }, { fromAutoValidatorRemake: true });
105591
+ }
105185
105592
  const statusEmoji = validationResult.status === 'ok' ? '✅' : '❌';
105186
105593
  addLog(formatLogMessage('log', `${statusEmoji} Переделанное изображение ${imageData.index}: ${validationResult.status === 'ok' ? 'OK' : 'НУЖНА ПЕРЕСБОРКА'}`));
105187
105594
  if (validationResult.errors.length > 0) {
@@ -105211,7 +105618,7 @@ ${imageData.originalPrompt}
105211
105618
  }
105212
105619
  };
105213
105620
  // Regenerate from scratch: no current creative reference, no custom comments
105214
- const handleRegenerateImageFresh = async (imageData) => {
105621
+ const handleRegenerateImageFresh = async (imageData, opts) => {
105215
105622
  if (!imageData.originalPrompt || !imageData.productImageUrl) {
105216
105623
  alert('Не удалось найти оригинальный промпт или изображение продукта для переделки');
105217
105624
  return;
@@ -105249,7 +105656,9 @@ ${imageData.originalPrompt}
105249
105656
  checkResult: undefined,
105250
105657
  checkErrors: undefined,
105251
105658
  // Explicitly ignore any previous comments
105252
- customRegeneratePrompt: ''
105659
+ customRegeneratePrompt: '',
105660
+ // Автовызов после валидатора: не сбрасывать guard — иначе внутренняя проверка снова запустит автопеределку
105661
+ validatorTransportAutoRemakeDone: opts?.fromAutoValidatorRemake === true ? true : false,
105253
105662
  }
105254
105663
  : img));
105255
105664
  addLog(formatLogMessage('log', `🔁 Переделка заново (с нуля) изображения ${imageData.index} (${imageData.approach})...`));
@@ -105280,6 +105689,8 @@ ${imageData.originalPrompt}
105280
105689
  checkResult: undefined,
105281
105690
  checkErrors: undefined,
105282
105691
  uploaded: false,
105692
+ driveUploadedFileId: undefined,
105693
+ uploadedPreviewHidden: undefined,
105283
105694
  customRegeneratePrompt: '',
105284
105695
  failed: false,
105285
105696
  remakeCount: (img.remakeCount ?? 0) + 1
@@ -105292,25 +105703,47 @@ ${imageData.originalPrompt}
105292
105703
  }
105293
105704
  else {
105294
105705
  addLog(formatLogMessage('log', `🔍 Проверка изображения ${imageData.index} (с нуля)...`));
105295
- validationResult = await validateCreativeImage(newImageUrl, generateProduct, generateGeo, addLog, imageData.approach);
105706
+ validationResult = await validateCreativeImage(newImageUrl, generateProduct, generateGeo, addLog, imageData.approach, imageData.productImageUrl);
105296
105707
  }
105297
- setGeneratedImagesData(prev => {
105298
- const updated = prev.map(img => img.index === imageData.index
105299
- ? {
105300
- ...img,
105301
- checking: false,
105302
- checkStatus: validationResult.status,
105303
- checkResult: validationResult.result,
105304
- checkErrors: validationResult.errors,
105305
- customRegeneratePrompt: validationResult.errors.length > 0
105306
- ? validationResult.errors.map(err => err.replace(/^ОШИБКА:\s*/i, '')).join('\n')
105307
- : '',
105308
- originalPrompt: img.originalPrompt || imageData.originalPrompt,
105309
- productImageUrl: img.productImageUrl || imageData.productImageUrl
105310
- }
105311
- : img);
105312
- return updated;
105708
+ const outcomeFresh = {
105709
+ status: validationResult.status,
105710
+ errors: validationResult.errors,
105711
+ };
105712
+ const formattedFreshErrors = formatValidationErrorsForRegeneratePrompt(validationResult.errors);
105713
+ const freshTransportRemake = outcomeFresh.checkFailed === true;
105714
+ let scheduleAfterFreshValidation = false;
105715
+ (0,react_dom__WEBPACK_IMPORTED_MODULE_1__.flushSync)(() => {
105716
+ setGeneratedImagesData(prev => {
105717
+ const cur = prev.find(i => i.index === imageData.index);
105718
+ const useAuto = shouldAutoRemakeAfterValidation(autoRemakeOnValidatorError, validationDisabled, outcomeFresh, updatedImageUrl, cur?.validatorTransportAutoRemakeDone);
105719
+ scheduleAfterFreshValidation = useAuto;
105720
+ return prev.map(img => img.index === imageData.index
105721
+ ? {
105722
+ ...img,
105723
+ checking: false,
105724
+ checkStatus: validationResult.status,
105725
+ checkResult: validationResult.result,
105726
+ checkErrors: validationResult.errors,
105727
+ customRegeneratePrompt: useAuto && freshTransportRemake ? '' : formattedFreshErrors,
105728
+ originalPrompt: img.originalPrompt || imageData.originalPrompt,
105729
+ productImageUrl: img.productImageUrl || imageData.productImageUrl,
105730
+ validatorTransportAutoRemakeDone: useAuto
105731
+ ? true
105732
+ : (img.validatorTransportAutoRemakeDone ?? false),
105733
+ }
105734
+ : img);
105735
+ });
105313
105736
  });
105737
+ if (scheduleAfterFreshValidation && imageData.originalPrompt && imageData.productImageUrl) {
105738
+ addLog(formatLogMessage('log', `🔄 Автопеределка с нуля изображения ${imageData.index} после ошибки валидатора (один раз)...`));
105739
+ await handleRegenerateImageFresh({
105740
+ index: imageData.index,
105741
+ approach: imageData.approach,
105742
+ aspectRatio: imageData.aspectRatio,
105743
+ originalPrompt: imageData.originalPrompt,
105744
+ productImageUrl: imageData.productImageUrl,
105745
+ }, { fromAutoValidatorRemake: true });
105746
+ }
105314
105747
  }
105315
105748
  catch (err) {
105316
105749
  addLog(formatLogMessage('error', `❌ Ошибка при переделке заново изображения ${imageData.index}: ${err.message}`));
@@ -105356,38 +105789,88 @@ ${imageData.originalPrompt}
105356
105789
  else {
105357
105790
  addLog(formatLogMessage('log', `🔍 Повторная проверка изображения ${imageData.index}...`));
105358
105791
  try {
105359
- validationResult = await validateCreativeImage(imageData.imageUrl, generateProduct, generateGeo, addLog, imageData.approach);
105792
+ validationResult = await validateCreativeImage(imageData.imageUrl, generateProduct, generateGeo, addLog, imageData.approach, imageData.productImageUrl);
105360
105793
  }
105361
105794
  catch (err) {
105362
105795
  const msg = err?.message || String(err);
105363
105796
  addLog(formatLogMessage('error', `❌ Ошибка повторной проверки изображения ${imageData.index}: ${msg}`));
105364
- setGeneratedImagesData(prev => prev.map(img => img.index === imageData.index
105797
+ const transportOutcome = {
105798
+ status: 'needs_rebuild',
105799
+ errors: [msg],
105800
+ checkFailed: true,
105801
+ };
105802
+ let scheduleValidatorAutoRemake = false;
105803
+ (0,react_dom__WEBPACK_IMPORTED_MODULE_1__.flushSync)(() => {
105804
+ setGeneratedImagesData(prev => {
105805
+ const cur = prev.find(i => i.index === imageData.index);
105806
+ const useAuto = shouldAutoRemakeAfterValidation(autoRemakeOnValidatorError, validationDisabled, transportOutcome, imageData.imageUrl, cur?.validatorTransportAutoRemakeDone);
105807
+ scheduleValidatorAutoRemake = useAuto;
105808
+ return prev.map(img => img.index === imageData.index
105809
+ ? {
105810
+ ...img,
105811
+ checking: false,
105812
+ checkFailed: true,
105813
+ checkStatus: 'needs_rebuild',
105814
+ checkResult: `Ошибка проверки: ${msg}`,
105815
+ checkErrors: [msg],
105816
+ customRegeneratePrompt: '',
105817
+ validatorTransportAutoRemakeDone: useAuto
105818
+ ? true
105819
+ : (img.validatorTransportAutoRemakeDone ?? false),
105820
+ }
105821
+ : img);
105822
+ });
105823
+ });
105824
+ if (scheduleValidatorAutoRemake && imageData.originalPrompt && imageData.productImageUrl) {
105825
+ addLog(formatLogMessage('log', `🔄 Автопеределка с нуля изображения ${imageData.index} после ошибки валидатора (один раз)...`));
105826
+ await handleRegenerateImageFresh({
105827
+ index: imageData.index,
105828
+ approach: imageData.approach,
105829
+ aspectRatio: imageData.aspectRatio,
105830
+ originalPrompt: imageData.originalPrompt,
105831
+ productImageUrl: imageData.productImageUrl,
105832
+ }, { fromAutoValidatorRemake: true });
105833
+ }
105834
+ return;
105835
+ }
105836
+ }
105837
+ const formattedRetryErrors = formatValidationErrorsForRegeneratePrompt(validationResult.errors);
105838
+ const retryOutcome = {
105839
+ status: validationResult.status,
105840
+ errors: validationResult.errors,
105841
+ };
105842
+ let scheduleValidatorAutoRemakeRetry = false;
105843
+ (0,react_dom__WEBPACK_IMPORTED_MODULE_1__.flushSync)(() => {
105844
+ setGeneratedImagesData(prev => {
105845
+ const cur = prev.find(i => i.index === imageData.index);
105846
+ const useAuto = shouldAutoRemakeAfterValidation(autoRemakeOnValidatorError, validationDisabled, retryOutcome, imageData.imageUrl, cur?.validatorTransportAutoRemakeDone);
105847
+ scheduleValidatorAutoRemakeRetry = useAuto;
105848
+ return prev.map(img => img.index === imageData.index
105365
105849
  ? {
105366
105850
  ...img,
105367
105851
  checking: false,
105368
- checkFailed: true,
105369
- checkStatus: 'needs_rebuild',
105370
- checkResult: `Ошибка проверки: ${msg}`,
105371
- checkErrors: [msg],
105372
- customRegeneratePrompt: '',
105852
+ checkFailed: false,
105853
+ checkStatus: validationResult.status,
105854
+ checkResult: validationResult.result,
105855
+ checkErrors: validationResult.errors,
105856
+ customRegeneratePrompt: formattedRetryErrors,
105857
+ validatorTransportAutoRemakeDone: useAuto
105858
+ ? true
105859
+ : (img.validatorTransportAutoRemakeDone ?? false),
105373
105860
  }
105374
- : img));
105375
- return;
105376
- }
105861
+ : img);
105862
+ });
105863
+ });
105864
+ if (scheduleValidatorAutoRemakeRetry && imageData.originalPrompt && imageData.productImageUrl) {
105865
+ addLog(formatLogMessage('log', `🔄 Автопеределка с нуля изображения ${imageData.index} после ошибки валидатора (один раз)...`));
105866
+ await handleRegenerateImageFresh({
105867
+ index: imageData.index,
105868
+ approach: imageData.approach,
105869
+ aspectRatio: imageData.aspectRatio,
105870
+ originalPrompt: imageData.originalPrompt,
105871
+ productImageUrl: imageData.productImageUrl,
105872
+ }, { fromAutoValidatorRemake: true });
105377
105873
  }
105378
- setGeneratedImagesData(prev => prev.map(img => img.index === imageData.index
105379
- ? {
105380
- ...img,
105381
- checking: false,
105382
- checkFailed: false,
105383
- checkStatus: validationResult.status,
105384
- checkResult: validationResult.result,
105385
- checkErrors: validationResult.errors,
105386
- customRegeneratePrompt: validationResult.errors.length > 0
105387
- ? validationResult.errors.map(err => err.replace(/^ОШИБКА:\s*/i, '')).join('\n')
105388
- : '',
105389
- }
105390
- : img));
105391
105874
  const statusEmoji = validationResult.status === 'ok' ? '✅' : '❌';
105392
105875
  addLog(formatLogMessage('log', `${statusEmoji} Повторная проверка изображения ${imageData.index}: ${validationResult.status === 'ok' ? 'OK' : 'НУЖНА ПЕРЕСБОРКА'}`));
105393
105876
  };
@@ -105404,11 +105887,20 @@ ${imageData.originalPrompt}
105404
105887
  logToTerminal('log', msg.replace(/\[.*?\]\s*/, ''));
105405
105888
  };
105406
105889
  try {
105407
- const filename = creativeImageUploadFilename(imageData.creoApproachUiNumber);
105890
+ const uploadAspect = imageData.aspectRatio ?? (imageAspectRatio === '2:3' ? '2:3' : '1:1');
105891
+ const filename = creativeImageUploadFilename(imageData.creoApproachUiNumber, uploadAspect);
105408
105892
  addLog(formatLogMessage('log', `📤 Uploading image ${imageData.index} to Drive: ${filename}`));
105409
105893
  const driveUrl = await uploadImageToDrive(imageData.imageUrl, filename, folderId, addLog);
105410
105894
  addLog(formatLogMessage('log', `✅ Image ${imageData.index} uploaded: ${driveUrl}`));
105411
- setGeneratedImagesData(prev => prev.map(img => img.index === imageData.index ? { ...img, uploaded: true } : img));
105895
+ const fid = extractDriveFileIdFromUrl(driveUrl);
105896
+ setGeneratedImagesData(prev => prev.map(img => img.index === imageData.index
105897
+ ? {
105898
+ ...img,
105899
+ uploaded: true,
105900
+ driveUploadedFileId: fid ?? undefined,
105901
+ uploadedPreviewHidden: true
105902
+ }
105903
+ : img));
105412
105904
  // (Removed legacy Generated Images links list)
105413
105905
  }
105414
105906
  catch (err) {
@@ -105448,7 +105940,8 @@ ${imageData.originalPrompt}
105448
105940
  try {
105449
105941
  addLog(formatLogMessage('log', `📤 Uploading ${notUploaded.length} image(s) to Drive (parallel)...`));
105450
105942
  const results = await Promise.allSettled(notUploaded.map((imageData) => {
105451
- const filename = creativeImageUploadFilename(imageData.creoApproachUiNumber);
105943
+ const uploadAspect = imageData.aspectRatio ?? (imageAspectRatio === '2:3' ? '2:3' : '1:1');
105944
+ const filename = creativeImageUploadFilename(imageData.creoApproachUiNumber, uploadAspect);
105452
105945
  addLog(formatLogMessage('log', `📤 Starting upload image ${imageData.index}: ${filename}`));
105453
105946
  return uploadImageToDrive(imageData.imageUrl, filename, folderId, addLog).then(driveUrl => ({
105454
105947
  index: imageData.index,
@@ -105457,11 +105950,13 @@ ${imageData.originalPrompt}
105457
105950
  }));
105458
105951
  }));
105459
105952
  const successfulIndices = new Set();
105953
+ const driveFileIdByIndex = new Map();
105460
105954
  results.forEach((result, i) => {
105461
105955
  const imageData = notUploaded[i];
105462
105956
  if (result.status === 'fulfilled' && result.value.success) {
105463
105957
  addLog(formatLogMessage('log', `✅ Image ${imageData.index} uploaded: ${result.value.driveUrl}`));
105464
105958
  successfulIndices.add(imageData.index);
105959
+ driveFileIdByIndex.set(imageData.index, extractDriveFileIdFromUrl(result.value.driveUrl) ?? undefined);
105465
105960
  }
105466
105961
  else {
105467
105962
  const errMsg = result.status === 'rejected' ? result.reason?.message : 'Unknown error';
@@ -105475,7 +105970,16 @@ ${imageData.originalPrompt}
105475
105970
  ...img,
105476
105971
  uploaded: successfulIndices.has(img.index),
105477
105972
  uploading: false,
105478
- uploadStartTime: undefined
105973
+ uploadStartTime: undefined,
105974
+ ...(successfulIndices.has(img.index)
105975
+ ? {
105976
+ driveUploadedFileId: driveFileIdByIndex.get(img.index),
105977
+ uploadedPreviewHidden: true
105978
+ }
105979
+ : {
105980
+ driveUploadedFileId: undefined,
105981
+ uploadedPreviewHidden: undefined
105982
+ })
105479
105983
  }
105480
105984
  : img));
105481
105985
  addLog(formatLogMessage('log', `✅ Uploaded ${uploadedCount}/${notUploaded.length} image(s) successfully`));
@@ -105492,6 +105996,46 @@ ${imageData.originalPrompt}
105492
105996
  setUploadingImages(false);
105493
105997
  }
105494
105998
  };
105999
+ /** Удалить файл крео с Google Drive (только если есть driveUploadedFileId в сессии). Локальное превью и слот остаются. */
106000
+ const handleDeleteCreativeFromDrive = async (imageData) => {
106001
+ const fileId = imageData.driveUploadedFileId?.trim();
106002
+ if (!fileId) {
106003
+ alert('Нет сохранённого ID файла на Диске для этого крео (перезагрузите страницу или загрузите снова).');
106004
+ return;
106005
+ }
106006
+ if (!window.confirm('Удалить этот файл с Google Диска? Локальное изображение в приложении останется.'))
106007
+ return;
106008
+ setDeletingDriveImageIndex(imageData.index);
106009
+ try {
106010
+ const token = await getValidAccessToken();
106011
+ if (!token) {
106012
+ alert('Войдите в Google Drive');
106013
+ return;
106014
+ }
106015
+ const res = await fetch(`https://www.googleapis.com/drive/v3/files/${encodeURIComponent(fileId)}?supportsAllDrives=true`, { method: 'DELETE', headers: { Authorization: `Bearer ${token}` } });
106016
+ if (!res.ok) {
106017
+ const t = await res.text();
106018
+ throw new Error(t || `${res.status} ${res.statusText}`);
106019
+ }
106020
+ setGeneratedImagesData(prev => prev.map(img => img.index === imageData.index
106021
+ ? {
106022
+ ...img,
106023
+ uploaded: false,
106024
+ driveUploadedFileId: undefined,
106025
+ uploadedPreviewHidden: undefined
106026
+ }
106027
+ : img));
106028
+ logToTerminal('log', `[Drive] Deleted creative file from Drive: ${fileId} (slot ${imageData.index})`);
106029
+ }
106030
+ catch (err) {
106031
+ const msg = err?.message || String(err);
106032
+ logToTerminal('error', '[Drive] Delete creative file failed:', msg);
106033
+ alert('Не удалось удалить файл с Диска: ' + msg);
106034
+ }
106035
+ finally {
106036
+ setDeletingDriveImageIndex(null);
106037
+ }
106038
+ };
105495
106039
  const handleLinkChange = (val) => {
105496
106040
  setLink(val);
105497
106041
  try {
@@ -105503,29 +106047,63 @@ ${imageData.originalPrompt}
105503
106047
  setLinkError(val.trim() ? 'Invalid URL format' : '');
105504
106048
  }
105505
106049
  };
105506
- const handleLinkBlur = () => {
105507
- const trimmed = link.trim();
105508
- if (!trimmed)
106050
+ /** Нормализация URL товара (как при blur поля ссылки). */
106051
+ const applyNormalizedProductLink = (trimmed) => {
106052
+ const t = trimmed.trim();
106053
+ if (!t) {
106054
+ handleLinkChange('');
105509
106055
  return;
105510
- let toParse = trimmed;
105511
- if (!/^https?:\/\//i.test(trimmed)) {
105512
- toParse = 'https://' + trimmed;
106056
+ }
106057
+ let toParse = t;
106058
+ if (!/^https?:\/\//i.test(t)) {
106059
+ toParse = 'https://' + t;
105513
106060
  }
105514
106061
  try {
105515
106062
  const parsed = new URL(toParse);
105516
106063
  let path = parsed.pathname || '/';
105517
- // Корень сайта — оставляем как `/`. Если есть «тело» пути (не один домен), завершающий `/` убираем, не добавляем.
105518
106064
  if (path !== '/' && path.length > 1) {
105519
106065
  while (path.length > 1 && path.endsWith('/')) {
105520
106066
  path = path.slice(0, -1);
105521
106067
  }
105522
106068
  }
105523
106069
  const result = `https://${parsed.host}${path}${parsed.search}${parsed.hash}`;
105524
- setLink(result);
105525
- setLinkError('');
106070
+ handleLinkChange(result);
106071
+ }
106072
+ catch {
106073
+ handleLinkChange(t);
106074
+ }
106075
+ };
106076
+ const handleLinkBlur = () => {
106077
+ const trimmed = link.trim();
106078
+ if (!trimmed)
106079
+ return;
106080
+ applyNormalizedProductLink(trimmed);
106081
+ };
106082
+ const handlePasteDriveFolderUrl = async () => {
106083
+ try {
106084
+ const text = (await navigator.clipboard.readText()).trim();
106085
+ if (!text) {
106086
+ alert('Буфер обмена пуст.');
106087
+ return;
106088
+ }
106089
+ setDriveFolderUrl(text);
105526
106090
  }
105527
106091
  catch {
105528
- // invalid leave as is
106092
+ alert('Не удалось прочитать буфер обмена. Разрешите доступ к буферу в браузере или вставьте ссылку вручную (Ctrl+V / Cmd+V).');
106093
+ }
106094
+ };
106095
+ const handlePasteProductLink = async () => {
106096
+ try {
106097
+ const raw = await navigator.clipboard.readText();
106098
+ const cleaned = (raw.split('?')[0] ?? raw).trim();
106099
+ if (!cleaned) {
106100
+ alert('Буфер обмена пуст.');
106101
+ return;
106102
+ }
106103
+ applyNormalizedProductLink(cleaned);
106104
+ }
106105
+ catch {
106106
+ alert('Не удалось прочитать буфер обмена. Разрешите доступ к буферу в браузере или вставьте ссылку вручную (Ctrl+V / Cmd+V).');
105529
106107
  }
105530
106108
  };
105531
106109
  const handleLinkPaste = (e) => {
@@ -105557,6 +106135,17 @@ ${imageData.originalPrompt}
105557
106135
  }
105558
106136
  return null;
105559
106137
  };
106138
+ /** ID файла на Google Drive из ссылки (webView / file/d / id=). */
106139
+ const extractDriveFileIdFromUrl = (url) => {
106140
+ const trimmed = url.trim();
106141
+ const filePath = trimmed.match(/\/file\/d\/([a-zA-Z0-9_-]+)/);
106142
+ if (filePath)
106143
+ return filePath[1];
106144
+ const idParam = trimmed.match(/[?&]id=([a-zA-Z0-9_-]+)/);
106145
+ if (idParam)
106146
+ return idParam[1];
106147
+ return null;
106148
+ };
105560
106149
  // Convert Google Drive view URL to direct image URL for display
105561
106150
  const convertDriveUrlToImageUrl = (url) => {
105562
106151
  // Extract file ID from Google Drive URL
@@ -105731,11 +106320,13 @@ ${imageData.originalPrompt}
105731
106320
  link: linkValue !== undefined ? linkValue : link || '',
105732
106321
  catalogUrlIncludeTextApproach,
105733
106322
  catalogUrlIncludeCreoApproach,
106323
+ catalogUrlIncludeImageAspect,
105734
106324
  catalogUrlTextApproachParam,
105735
106325
  catalogUrlCreoApproachParam,
106326
+ catalogUrlImageAspectParam,
105736
106327
  catalogLinkExtraMacros,
105737
- selectedPairApproaches: (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_55__.getSelectedPairApproaches)(),
105738
- imageApproachCounts: (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_55__.getImageApproachCounts)(),
106328
+ selectedPairApproaches: (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_60__.getSelectedPairApproaches)(),
106329
+ imageApproachCounts: (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_60__.getImageApproachCounts)(),
105739
106330
  savedAt: new Date().toISOString()
105740
106331
  };
105741
106332
  const jsonContent = JSON.stringify(dataToSave, null, 2);
@@ -105896,15 +106487,21 @@ ${imageData.originalPrompt}
105896
106487
  if (typeof loadedData.catalogUrlCreoApproachParam === 'string' && loadedData.catalogUrlCreoApproachParam.trim()) {
105897
106488
  setCatalogUrlCreoApproachParam(loadedData.catalogUrlCreoApproachParam.trim());
105898
106489
  }
106490
+ if (typeof loadedData.catalogUrlIncludeImageAspect === 'boolean') {
106491
+ setCatalogUrlIncludeImageAspect(loadedData.catalogUrlIncludeImageAspect);
106492
+ }
106493
+ if (typeof loadedData.catalogUrlImageAspectParam === 'string' && loadedData.catalogUrlImageAspectParam.trim()) {
106494
+ setCatalogUrlImageAspectParam(loadedData.catalogUrlImageAspectParam.trim());
106495
+ }
105899
106496
  if (typeof loadedData.catalogLinkExtraMacros === 'string') {
105900
106497
  setCatalogLinkExtraMacros(loadedData.catalogLinkExtraMacros);
105901
106498
  }
105902
106499
  else {
105903
106500
  setCatalogLinkExtraMacros(DEFAULT_CATALOG_LINK_EXTRA_MACROS);
105904
106501
  }
105905
- const parsedApproaches = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_55__.parseApproachesFromDriveData)(loadedData);
105906
- const currentPairs = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_55__.getSelectedPairApproaches)();
105907
- const currentCounts = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_55__.getImageApproachCounts)();
106502
+ const parsedApproaches = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_60__.parseApproachesFromDriveData)(loadedData);
106503
+ const currentPairs = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_60__.getSelectedPairApproaches)();
106504
+ const currentCounts = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_60__.getImageApproachCounts)();
105908
106505
  const hasDrivePairs = parsedApproaches.pairs !== null;
105909
106506
  const hasDriveCounts = parsedApproaches.counts !== null;
105910
106507
  if (hasDrivePairs || hasDriveCounts) {
@@ -105933,7 +106530,7 @@ ${imageData.originalPrompt}
105933
106530
  const handleApproachLoadApplyFromFile = () => {
105934
106531
  if (!approachLoadChoice)
105935
106532
  return;
105936
- const applied = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_55__.applyParsedApproachesFromDrive)(approachLoadChoice.savedPairs, approachLoadChoice.savedCounts);
106533
+ const applied = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_60__.applyParsedApproachesFromDrive)(approachLoadChoice.savedPairs, approachLoadChoice.savedCounts);
105937
106534
  if (applied) {
105938
106535
  logToTerminal('log', '[Load Content] Applied approach settings from offer file (user choice)');
105939
106536
  }
@@ -105961,8 +106558,8 @@ ${imageData.originalPrompt}
105961
106558
  };
105962
106559
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
105963
106560
  const listener = () => syncDriveAfterPromptSaveRef.current();
105964
- window.addEventListener(_promptOverrides__WEBPACK_IMPORTED_MODULE_55__.PROMPT_OVERRIDES_SAVED_EVENT, listener);
105965
- return () => window.removeEventListener(_promptOverrides__WEBPACK_IMPORTED_MODULE_55__.PROMPT_OVERRIDES_SAVED_EVENT, listener);
106561
+ window.addEventListener(_promptOverrides__WEBPACK_IMPORTED_MODULE_60__.PROMPT_OVERRIDES_SAVED_EVENT, listener);
106562
+ return () => window.removeEventListener(_promptOverrides__WEBPACK_IMPORTED_MODULE_60__.PROMPT_OVERRIDES_SAVED_EVENT, listener);
105966
106563
  }, []);
105967
106564
  const handleGenerate = async () => {
105968
106565
  if (!driveFolderUrl || !brand || !link) {
@@ -105990,6 +106587,14 @@ ${imageData.originalPrompt}
105990
106587
  alert('Please add at least one text variant');
105991
106588
  return;
105992
106589
  }
106590
+ const { cell: catalogPriceCell, usedUsdFallback } = resolveCatalogPriceForExport(generatePriceWithCurrency);
106591
+ if (!catalogPriceCell) {
106592
+ alert('Please set price with currency in the generate form (e.g. 29 EUR, 29.99 USD, €29) — it is used for the catalog price column.');
106593
+ return;
106594
+ }
106595
+ if (usedUsdFallback) {
106596
+ alert('Цена для каталога не распознана — в колонку подставлены доллары (USD). Проверьте поле «Цена и валюта».');
106597
+ }
105993
106598
  setLoading(true);
105994
106599
  try {
105995
106600
  const validToken = await getValidAccessToken();
@@ -106012,6 +106617,16 @@ ${imageData.originalPrompt}
106012
106617
  // Add header row
106013
106618
  rows.push(['id', 'title', 'description', 'availability', 'condition', 'price', 'link', 'image_link', 'brand']);
106014
106619
  let idCounter = 1;
106620
+ const catalogCreativeSalt = randomCatalogCreativeSalt4();
106621
+ logToTerminal('log', `[Catalog] creative_id batch salt: ${catalogCreativeSalt} (one per export)`);
106622
+ const catalogAspectValueForFile = (fileName) => {
106623
+ const fromName = catalogImageAspectUrlValueFromFileName(fileName);
106624
+ if (fromName)
106625
+ return fromName;
106626
+ if (imageAspectRatio === '2:3')
106627
+ return '2_3';
106628
+ return '1_1';
106629
+ };
106015
106630
  // Парная склейка: пара i = (titleList[i], textList[i])
106016
106631
  const pairCount = Math.min(titleList.length, textList.length);
106017
106632
  for (let i = 0; i < pairCount; i++) {
@@ -106020,13 +106635,15 @@ ${imageData.originalPrompt}
106020
106635
  const pairApproachIdx = lastUsedApproachIndices[i] ?? i;
106021
106636
  const textApproachUiNumber = String(pairApproachIdx + 1);
106022
106637
  for (const image of images) {
106023
- const id = `${brand}${idCounter++}`;
106638
+ const id = `${brand}-${catalogCreativeSalt}-${idCounter++}`;
106024
106639
  const creoFromFileName = parseCreoApproachLabelFromImageFileName(image.name);
106025
106640
  const rowLink = appendCreativeIdToCatalogLink(link, id, {
106026
106641
  textApproach: catalogUrlIncludeTextApproach ? textApproachUiNumber : undefined,
106027
106642
  creoApproach: catalogUrlIncludeCreoApproach && creoFromFileName ? creoFromFileName : undefined,
106643
+ imageAspectTag: catalogUrlIncludeImageAspect ? catalogAspectValueForFile(image.name) : undefined,
106028
106644
  textParamKey: catalogUrlTextApproachParam,
106029
- creoParamKey: catalogUrlCreoApproachParam
106645
+ creoParamKey: catalogUrlCreoApproachParam,
106646
+ imageAspectParamKey: catalogUrlImageAspectParam
106030
106647
  }, catalogLinkExtraMacros);
106031
106648
  rows.push([
106032
106649
  id,
@@ -106034,7 +106651,7 @@ ${imageData.originalPrompt}
106034
106651
  text,
106035
106652
  'in stock',
106036
106653
  'new',
106037
- '10,00 USD',
106654
+ catalogPriceCell,
106038
106655
  rowLink,
106039
106656
  image.viewUrl,
106040
106657
  brand
@@ -106043,8 +106660,8 @@ ${imageData.originalPrompt}
106043
106660
  }
106044
106661
  setGeneratedData(rows);
106045
106662
  // Create workbook
106046
- const wb = xlsx__WEBPACK_IMPORTED_MODULE_56__.utils.book_new();
106047
- const ws = xlsx__WEBPACK_IMPORTED_MODULE_56__.utils.aoa_to_sheet(rows);
106663
+ const wb = xlsx__WEBPACK_IMPORTED_MODULE_61__.utils.book_new();
106664
+ const ws = xlsx__WEBPACK_IMPORTED_MODULE_61__.utils.aoa_to_sheet(rows);
106048
106665
  // Set column widths (approximate pixel width / 7)
106049
106666
  ws['!cols'] = [
106050
106667
  { wch: 20 }, // id
@@ -106057,9 +106674,9 @@ ${imageData.originalPrompt}
106057
106674
  { wch: 40 }, // image_link
106058
106675
  { wch: 20 } // brand
106059
106676
  ];
106060
- xlsx__WEBPACK_IMPORTED_MODULE_56__.utils.book_append_sheet(wb, ws, "Products");
106677
+ xlsx__WEBPACK_IMPORTED_MODULE_61__.utils.book_append_sheet(wb, ws, "Products");
106061
106678
  // Generate buffer
106062
- const wbout = xlsx__WEBPACK_IMPORTED_MODULE_56__.write(wb, { bookType: 'xlsx', type: 'array' });
106679
+ const wbout = xlsx__WEBPACK_IMPORTED_MODULE_61__.write(wb, { bookType: 'xlsx', type: 'array' });
106063
106680
  // Upload to Drive (имя файла по бренду)
106064
106681
  const dateStr = new Date().toISOString().split('T')[0];
106065
106682
  const fileName = `${brand}-${dateStr}.xlsx`;
@@ -106178,16 +106795,24 @@ ${imageData.originalPrompt}
106178
106795
  alert('Please log in with Google first');
106179
106796
  return;
106180
106797
  }
106798
+ const { cell: testPrice, usedUsdFallback } = resolveCatalogPriceForExport(generatePriceWithCurrency);
106799
+ if (!testPrice) {
106800
+ alert('Please set price with currency in the generate form (e.g. 29 EUR, 29.99 USD, €29) — it is used for the catalog price column.');
106801
+ return;
106802
+ }
106803
+ if (usedUsdFallback) {
106804
+ alert('Цена для каталога не распознана — в колонку подставлены доллары (USD). Проверьте поле «Цена и валюта».');
106805
+ }
106181
106806
  setTestLoading(true);
106182
106807
  try {
106183
106808
  // Create simple test workbook with structure
106184
- const wb = xlsx__WEBPACK_IMPORTED_MODULE_56__.utils.book_new();
106809
+ const wb = xlsx__WEBPACK_IMPORTED_MODULE_61__.utils.book_new();
106185
106810
  const rows = [
106186
106811
  INSTRUCTION_ROW,
106187
106812
  ['id', 'title', 'description', 'availability', 'condition', 'price', 'link', 'image_link', 'brand'],
106188
- ['test1', 'Test Title', 'Test Description', 'in stock', 'new', '10.00 USD', 'http://test.com', 'http://test.com/img.jpg', 'TestBrand']
106813
+ ['test1', 'Test Title', 'Test Description', 'in stock', 'new', testPrice, 'http://test.com', 'http://test.com/img.jpg', 'TestBrand']
106189
106814
  ];
106190
- const ws = xlsx__WEBPACK_IMPORTED_MODULE_56__.utils.aoa_to_sheet(rows);
106815
+ const ws = xlsx__WEBPACK_IMPORTED_MODULE_61__.utils.aoa_to_sheet(rows);
106191
106816
  // Set column widths
106192
106817
  ws['!cols'] = [
106193
106818
  { wch: 20 }, // id
@@ -106200,8 +106825,8 @@ ${imageData.originalPrompt}
106200
106825
  { wch: 40 }, // image_link
106201
106826
  { wch: 20 } // brand
106202
106827
  ];
106203
- xlsx__WEBPACK_IMPORTED_MODULE_56__.utils.book_append_sheet(wb, ws, "Test");
106204
- const wbout = xlsx__WEBPACK_IMPORTED_MODULE_56__.write(wb, { bookType: 'xlsx', type: 'array' });
106828
+ xlsx__WEBPACK_IMPORTED_MODULE_61__.utils.book_append_sheet(wb, ws, "Test");
106829
+ const wbout = xlsx__WEBPACK_IMPORTED_MODULE_61__.write(wb, { bookType: 'xlsx', type: 'array' });
106205
106830
  // Try to extract folder ID if available, otherwise upload to root
106206
106831
  const folderId = driveFolderUrl ? extractFolderId(driveFolderUrl) : undefined;
106207
106832
  const result = await uploadFileToDrive(wbout, 'test_table.xlsx', folderId || undefined);
@@ -106253,9 +106878,9 @@ ${imageData.originalPrompt}
106253
106878
  };
106254
106879
  // Show lock screen if not unlocked
106255
106880
  if (!unlocked) {
106256
- return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_37__["default"], { theme: theme },
106257
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_4__["default"], null),
106258
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { onClick: handleSecretClick, sx: {
106881
+ return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_38__["default"], { theme: theme },
106882
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null),
106883
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { onClick: handleSecretClick, sx: {
106259
106884
  width: '100vw',
106260
106885
  height: '100vh',
106261
106886
  backgroundColor: darkMode ? '#121212' : '#ffffff',
@@ -106270,17 +106895,17 @@ ${imageData.originalPrompt}
106270
106895
  gap: 2,
106271
106896
  p: 4
106272
106897
  } },
106273
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h4", sx: {
106898
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "h4", sx: {
106274
106899
  color: darkMode ? '#ff4444' : '#d32f2f',
106275
106900
  fontWeight: 'bold',
106276
106901
  textAlign: 'center'
106277
106902
  } }, "\u26A0\uFE0F Application Error"),
106278
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h6", sx: {
106903
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "h6", sx: {
106279
106904
  color: darkMode ? '#cccccc' : '#666666',
106280
106905
  textAlign: 'center',
106281
106906
  maxWidth: '600px'
106282
106907
  } }, "Failed to initialize application context."),
106283
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body1", sx: {
106908
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body1", sx: {
106284
106909
  color: darkMode ? '#aaaaaa' : '#888888',
106285
106910
  textAlign: 'center',
106286
106911
  fontFamily: 'monospace',
@@ -106293,83 +106918,83 @@ ${imageData.originalPrompt}
106293
106918
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("br", null),
106294
106919
  "Please contact system administrator"))));
106295
106920
  }
106296
- return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_37__["default"], { theme: theme },
106297
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_4__["default"], null),
106298
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { maxWidth: "lg", sx: { py: 4 } },
106299
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 } },
106300
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h4", component: "h1", sx: { fontWeight: 'bold', color: 'primary.main' } }, "Docs Combiner"),
106301
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
106302
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_8__["default"], { onClick: () => setPromptManagerOpen(true), color: "inherit", "aria-label": "manage prompts", sx: { mr: 1 } },
106303
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_52__["default"], null)),
106304
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_8__["default"], { onClick: toggleTheme, color: "inherit", "aria-label": "toggle theme" }, darkMode ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_42__["default"], null) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_41__["default"], null)))),
106305
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_9__["default"], { variant: "outlined", sx: { mb: 4 } },
106306
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_10__["default"], null,
106307
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { spacing: 3 },
106308
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
106309
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h6", gutterBottom: true }, "Google Drive Authentication"),
106310
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { mb: 2 } },
106311
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "\u041A\u043E\u0440\u043D\u0435\u0432\u0430\u044F \u043F\u0430\u043F\u043A\u0430 \u043F\u0440\u043E\u0435\u043A\u0442\u0430"),
106312
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", color: "text.secondary", display: "block", sx: { mb: 1 } }, "\u0421\u0441\u044B\u043B\u043A\u0430 \u043D\u0430 \u043A\u043E\u0440\u043D\u0435\u0432\u0443\u044E \u043F\u0430\u043F\u043A\u0443 \u043D\u0430 Google \u0414\u0438\u0441\u043A\u0435 (\u0432\u043D\u0443\u0442\u0440\u0438 \u043D\u0435\u0451 \u0434\u043E\u043B\u0436\u043D\u0430 \u0431\u044B\u0442\u044C \u043F\u0430\u043F\u043A\u0430 OFFERS)."),
106313
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "URL \u043A\u043E\u0440\u043D\u0435\u0432\u043E\u0439 \u043F\u0430\u043F\u043A\u0438 Google Drive", variant: "outlined", fullWidth: true, value: rootDriveFolderUrl, onChange: (e) => setRootDriveFolderUrl(e.target.value), placeholder: "https://drive.google.com/drive/folders/...", sx: { mb: 1 } }),
106314
- checkingRootFolder && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], null,
106315
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { component: "span", sx: { display: 'inline-flex', alignItems: 'center', gap: 1 } },
106316
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 14 }),
106317
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043A\u043E\u0440\u043D\u0435\u0432\u043E\u0439 \u043F\u0430\u043F\u043A\u0438 (OFFERS, \u0434\u043E\u0441\u0442\u0443\u043F)...")))),
106318
- drivePublicAccessWarning && extractFolderId(rootDriveFolderUrl) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { severity: "warning", sx: { mt: 1 } }, "\u041E\u0431\u0449\u0438\u0439 \u0434\u043E\u0441\u0442\u0443\u043F \u043F\u043E \u0441\u0441\u044B\u043B\u043A\u0435 \u0432\u0441\u0451 \u0435\u0449\u0451 \u0432\u043A\u043B\u044E\u0447\u0451\u043D. \u0417\u0430\u043A\u0440\u043E\u0439\u0442\u0435 \u0435\u0433\u043E \u0432\u0440\u0443\u0447\u043D\u0443\u044E \u0432 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430\u0445 \u0434\u043E\u0441\u0442\u0443\u043F\u0430 Google \u0414\u0438\u0441\u043A\u0430 \u0438\u043B\u0438 \u0441\u043C\u0435\u043D\u0438\u0442\u0435 \u043F\u0430\u043F\u043A\u0443 \u0438 \u0441\u043E\u0433\u043B\u0430\u0441\u0438\u0442\u0435\u0441\u044C \u0443\u0431\u0440\u0430\u0442\u044C \u0434\u043E\u0441\u0442\u0443\u043F \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438.")),
106319
- !checkingRootFolder && rootFolderInfo && !rootFolderInfo.hasOffersFolder && extractFolderId(rootDriveFolderUrl) && (accessToken || refreshToken) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { severity: "error", sx: { mt: 1 } }, "\u0412 \u043A\u043E\u0440\u043D\u0435\u0432\u043E\u0439 \u043F\u0430\u043F\u043A\u0435 \u043D\u0435\u0442 \u043F\u043E\u0434\u043F\u0430\u043F\u043A\u0438 OFFERS. \u0421\u043E\u0437\u0434\u0430\u0439\u0442\u0435 \u043F\u0430\u043F\u043A\u0443 \u0441 \u0438\u043C\u0435\u043D\u0435\u043C OFFERS (\u0440\u0435\u0433\u0438\u0441\u0442\u0440 \u043D\u0435 \u0432\u0430\u0436\u0435\u043D) \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443.")),
106320
- !checkingRootFolder && !rootFolderInfo && rootDriveFolderUrl.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], null, (accessToken || refreshToken)
106321
- ? 'Введите корректную ссылку на корневую папку Google Drive'
106322
- : 'Войдите в Google, чтобы проверить корневую папку и подпапку OFFERS'))),
106323
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { sx: { my: 2 } }),
106324
- accessToken ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], null,
106325
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { expandIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_47__["default"], null) },
106326
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', color: 'success.main' } },
106327
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_43__["default"], { sx: { mr: 1 } }),
106328
- " Logged In (Credentials Hidden)")),
106329
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], null,
106330
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { spacing: 2 },
106331
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "Client ID", variant: "outlined", fullWidth: true, value: clientId, onChange: (e) => handleClientIdChange(e.target.value), helperText: "From Google Cloud Console (OAuth 2.0 Client ID)" }),
106332
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "Client Secret", variant: "outlined", fullWidth: true, value: clientSecret, onChange: (e) => handleClientSecretChange(e.target.value) }),
106333
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], null),
106334
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "OpenRouter API Key", variant: "outlined", fullWidth: true, value: openaiApiKey, onChange: (e) => handleOpenaiApiKeyChange(e.target.value), helperText: "Required for AI generation (images and text). Your key is stored locally. Get your key at openrouter.ai" }))))) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { spacing: 2 },
106335
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "Client ID", variant: "outlined", fullWidth: true, value: clientId, onChange: (e) => handleClientIdChange(e.target.value), helperText: "From Google Cloud Console (OAuth 2.0 Client ID)" }),
106336
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "Client Secret", variant: "outlined", fullWidth: true, value: clientSecret, onChange: (e) => handleClientSecretChange(e.target.value) }))),
106337
- openaiApiKey && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 2, mt: 2 } },
106338
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_38__["default"], { color: openRouterAccountBalance !== null ? 'primary' : 'disabled' }),
106339
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", color: openRouterAccountBalance !== null ? 'text.primary' : 'text.secondary' },
106921
+ return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_38__["default"], { theme: theme },
106922
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null),
106923
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_8__["default"], { maxWidth: "lg", sx: { py: 4 } },
106924
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 } },
106925
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "h4", component: "h1", sx: { fontWeight: 'bold', color: 'primary.main' } }, "Docs Combiner"),
106926
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], null,
106927
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_9__["default"], { onClick: () => setPromptManagerOpen(true), color: "inherit", "aria-label": "manage prompts", sx: { mr: 1 } },
106928
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_57__["default"], null)),
106929
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_9__["default"], { onClick: toggleTheme, color: "inherit", "aria-label": "toggle theme" }, darkMode ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_43__["default"], null) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_42__["default"], null)))),
106930
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_10__["default"], { variant: "outlined", sx: { mb: 4 } },
106931
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], null,
106932
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { spacing: 3 },
106933
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], null,
106934
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "h6", gutterBottom: true }, "Google Drive Authentication"),
106935
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], { defaultExpanded: false },
106936
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { expandIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_50__["default"], null) }, accessToken ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { sx: { display: 'flex', alignItems: 'center', color: 'success.main' } },
106937
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_44__["default"], { sx: { mr: 1 } }),
106938
+ " Logged In (Credentials Hidden)")) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { color: "text.secondary" }, "Credentials"))),
106939
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], null,
106940
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { spacing: 2 },
106941
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], null,
106942
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "\u041A\u043E\u0440\u043D\u0435\u0432\u0430\u044F \u043F\u0430\u043F\u043A\u0430 \u043F\u0440\u043E\u0435\u043A\u0442\u0430"),
106943
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", color: "text.secondary", display: "block", sx: { mb: 1 } }, "\u0421\u0441\u044B\u043B\u043A\u0430 \u043D\u0430 \u043A\u043E\u0440\u043D\u0435\u0432\u0443\u044E \u043F\u0430\u043F\u043A\u0443 \u043D\u0430 Google \u0414\u0438\u0441\u043A\u0435 (\u0432\u043D\u0443\u0442\u0440\u0438 \u043D\u0435\u0451 \u0434\u043E\u043B\u0436\u043D\u0430 \u0431\u044B\u0442\u044C \u043F\u0430\u043F\u043A\u0430 OFFERS)."),
106944
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { label: "URL \u043A\u043E\u0440\u043D\u0435\u0432\u043E\u0439 \u043F\u0430\u043F\u043A\u0438 Google Drive", variant: "outlined", fullWidth: true, value: rootDriveFolderUrl, onChange: (e) => setRootDriveFolderUrl(e.target.value), placeholder: "https://drive.google.com/drive/folders/...", sx: { mb: 1 } }),
106945
+ checkingRootFolder && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], null,
106946
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { component: "span", sx: { display: 'inline-flex', alignItems: 'center', gap: 1 } },
106947
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 14 }),
106948
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043A\u043E\u0440\u043D\u0435\u0432\u043E\u0439 \u043F\u0430\u043F\u043A\u0438 (OFFERS, \u0434\u043E\u0441\u0442\u0443\u043F)...")))),
106949
+ drivePublicAccessWarning && extractFolderId(rootDriveFolderUrl) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { severity: "warning", sx: { mt: 1 } }, "\u041E\u0431\u0449\u0438\u0439 \u0434\u043E\u0441\u0442\u0443\u043F \u043F\u043E \u0441\u0441\u044B\u043B\u043A\u0435 \u0432\u0441\u0451 \u0435\u0449\u0451 \u0432\u043A\u043B\u044E\u0447\u0451\u043D. \u0417\u0430\u043A\u0440\u043E\u0439\u0442\u0435 \u0435\u0433\u043E \u0432\u0440\u0443\u0447\u043D\u0443\u044E \u0432 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430\u0445 \u0434\u043E\u0441\u0442\u0443\u043F\u0430 Google \u0414\u0438\u0441\u043A\u0430 \u0438\u043B\u0438 \u0441\u043C\u0435\u043D\u0438\u0442\u0435 \u043F\u0430\u043F\u043A\u0443 \u0438 \u0441\u043E\u0433\u043B\u0430\u0441\u0438\u0442\u0435\u0441\u044C \u0443\u0431\u0440\u0430\u0442\u044C \u0434\u043E\u0441\u0442\u0443\u043F \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438.")),
106950
+ !checkingRootFolder && rootFolderInfo && !rootFolderInfo.hasOffersFolder && extractFolderId(rootDriveFolderUrl) && (accessToken || refreshToken) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { severity: "error", sx: { mt: 1 } }, "\u0412 \u043A\u043E\u0440\u043D\u0435\u0432\u043E\u0439 \u043F\u0430\u043F\u043A\u0435 \u043D\u0435\u0442 \u043F\u043E\u0434\u043F\u0430\u043F\u043A\u0438 OFFERS. \u0421\u043E\u0437\u0434\u0430\u0439\u0442\u0435 \u043F\u0430\u043F\u043A\u0443 \u0441 \u0438\u043C\u0435\u043D\u0435\u043C OFFERS (\u0440\u0435\u0433\u0438\u0441\u0442\u0440 \u043D\u0435 \u0432\u0430\u0436\u0435\u043D) \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443.")),
106951
+ !checkingRootFolder && !rootFolderInfo && rootDriveFolderUrl.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], null, (accessToken || refreshToken)
106952
+ ? 'Введите корректную ссылку на корневую папку Google Drive'
106953
+ : 'Войдите в Google, чтобы проверить корневую папку и подпапку OFFERS'))),
106954
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { label: "Client ID", variant: "outlined", fullWidth: true, value: clientId, onChange: (e) => handleClientIdChange(e.target.value), helperText: "From Google Cloud Console (OAuth 2.0 Client ID)" }),
106955
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { label: "Client Secret", variant: "outlined", fullWidth: true, value: clientSecret, onChange: (e) => handleClientSecretChange(e.target.value) }),
106956
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], null),
106957
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { label: "OpenRouter API Key", variant: "outlined", fullWidth: true, value: openaiApiKey, onChange: (e) => handleOpenaiApiKeyChange(e.target.value), helperText: "Required for AI generation (images and text). Your key is stored locally. Get your key at openrouter.ai" })))),
106958
+ openaiApiKey && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 2, mt: 2 } },
106959
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_39__["default"], { color: openRouterAccountBalance !== null ? 'primary' : 'disabled' }),
106960
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", color: openRouterAccountBalance !== null ? 'text.primary' : 'text.secondary' },
106340
106961
  "\u0411\u0430\u043B\u0430\u043D\u0441: ",
106341
106962
  openRouterBalanceLoading ? 'Loading...' : (openRouterAccountBalance !== null ? `$${openRouterAccountBalance.toFixed(2)}` : 'N/A')),
106342
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", color: "text.secondary" }, "\u2022"),
106343
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", color: openRouterBalance !== null ? 'text.primary' : 'text.secondary' },
106963
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", color: "text.secondary" }, "\u2022"),
106964
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", color: openRouterBalance !== null ? 'text.primary' : 'text.secondary' },
106344
106965
  "\u041B\u0438\u043C\u0438\u0442 \u043A\u043B\u044E\u0447\u0430: ",
106345
106966
  openRouterBalanceLoading ? 'Loading...' : (openRouterBalance === -1 ? 'Без лимита' : (openRouterBalance !== null ? `${openRouterBalance.toFixed(4)}` : 'N/A'))))),
106346
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: "row", spacing: 2, alignItems: "center", sx: { mt: 2 } },
106347
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { variant: "contained", color: accessToken ? "success" : "primary", onClick: handleLogin, disabled: authLoading || !clientId || !clientSecret, startIcon: authLoading ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 20, color: "inherit" }) : (accessToken ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_43__["default"], null) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_48__["default"], null)), sx: { flexGrow: 1 } }, authLoading ? 'Logging in...' : (accessToken ? 'Logged In' : 'Login with Google')),
106348
- accessToken && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { variant: "outlined", color: "error", onClick: handleLogout, startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_49__["default"], null) }, "Logout")))),
106349
- !accessToken && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
106350
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], null),
106351
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
106352
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "OpenRouter API Key", variant: "outlined", fullWidth: true, value: openaiApiKey, onChange: (e) => handleOpenaiApiKeyChange(e.target.value), helperText: "Required for AI generation (images and text). Your key is stored locally. Get your key at openrouter.ai", sx: { mb: 2 } })))),
106353
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { sx: { my: 2 } }),
106354
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
106355
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h6", gutterBottom: true }, "Google Drive Folder"),
106356
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: { xs: 'column', sm: 'row' }, spacing: 2 },
106357
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { flexGrow: 1 } },
106358
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "Google Drive Folder URL", variant: "outlined", fullWidth: true, value: driveFolderUrl, onChange: (e) => setDriveFolderUrl(e.target.value), placeholder: "https://drive.google.com/drive/folders/..." }),
106359
- checkingFolderFiles && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], null,
106360
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { component: "span", sx: { display: 'inline-flex', alignItems: 'center', gap: 1 } },
106361
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 14 }),
106967
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { direction: "row", spacing: 2, alignItems: "center", sx: { mt: 2 }, flexWrap: "wrap", useFlexGap: true },
106968
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { variant: "contained", color: accessToken ? "success" : "primary", onClick: handleLogin, disabled: authLoading || !clientId || !clientSecret, startIcon: authLoading ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 20, color: "inherit" }) : (accessToken ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_44__["default"], null) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_52__["default"], null)), sx: { flexGrow: 1, minWidth: 200 } }, authLoading ? 'Logging in...' : (accessToken ? 'Logged In' : 'Login with Google')),
106969
+ authLoading && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
106970
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { variant: "outlined", color: "primary", onClick: () => void handleReopenAuthBrowser(), startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_55__["default"], null) }, "\u041E\u0442\u043A\u0440\u044B\u0442\u044C \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0443 \u0432\u0445\u043E\u0434\u0430 \u0441\u043D\u043E\u0432\u0430"),
106971
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { variant: "outlined", color: "inherit", onClick: () => void handleCancelPendingAuth(), startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_45__["default"], null) }, "\u041E\u0442\u043C\u0435\u043D\u0438\u0442\u044C \u0432\u0445\u043E\u0434"))),
106972
+ accessToken && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { variant: "outlined", color: "error", onClick: handleLogout, startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_53__["default"], null) }, "Logout")))),
106973
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { sx: { my: 2 } }),
106974
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], null,
106975
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "h6", gutterBottom: true }, "Google Drive Folder"),
106976
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { direction: { xs: 'column', sm: 'row' }, spacing: 2 },
106977
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { flexGrow: 1 } },
106978
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { label: "Google Drive Folder URL", variant: "outlined", fullWidth: true, value: driveFolderUrl, onChange: (e) => setDriveFolderUrl(e.target.value), placeholder: "https://drive.google.com/drive/folders/...", InputProps: {
106979
+ endAdornment: (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { position: "end" },
106980
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { title: "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0438\u0437 \u0431\u0443\u0444\u0435\u0440\u0430 \u043E\u0431\u043C\u0435\u043D\u0430" },
106981
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_9__["default"], { edge: "end", "aria-label": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0441\u0441\u044B\u043B\u043A\u0443 \u043D\u0430 \u043F\u0430\u043F\u043A\u0443 \u0438\u0437 \u0431\u0443\u0444\u0435\u0440\u0430", onClick: () => void handlePasteDriveFolderUrl() },
106982
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_48__["default"], null)))))
106983
+ } }),
106984
+ checkingFolderFiles && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], null,
106985
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { component: "span", sx: { display: 'inline-flex', alignItems: 'center', gap: 1 } },
106986
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 14 }),
106362
106987
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u0444\u0430\u0439\u043B\u043E\u0432 \u0432 \u043F\u0430\u043F\u043A\u0435...")))),
106363
- !checkingFolderFiles && folderFilesInfo && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], null,
106364
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { component: "span", sx: { display: 'block' } }, folderFilesInfo.hasProduct ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { component: "span", sx: { color: 'success.main' } }, "\u2713 product.png/jpg/webp \u043D\u0430\u0439\u0434\u0435\u043D")) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { component: "span", sx: { color: 'info.main' } }, "\u2139 product.png/jpg/webp \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D \u2014 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u0435 \u0447\u0435\u0440\u0435\u0437 \u043A\u043D\u043E\u043F\u043A\u0443 \u043D\u0438\u0436\u0435"))))),
106365
- !checkingFolderFiles && !folderFilesInfo && driveFolderUrl.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], null, (accessToken || refreshToken)
106988
+ !checkingFolderFiles && folderFilesInfo && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], null,
106989
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { component: "span", sx: { display: 'block' } }, folderFilesInfo.hasProduct ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { component: "span", sx: { color: 'success.main' } }, "\u2713 product.png/jpg/webp \u043D\u0430\u0439\u0434\u0435\u043D")) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { component: "span", sx: { color: 'info.main' } }, "\u2139 product.png/jpg/webp \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D \u2014 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u0435 \u0447\u0435\u0440\u0435\u0437 \u043A\u043D\u043E\u043F\u043A\u0443 \u043D\u0438\u0436\u0435"))))),
106990
+ !checkingFolderFiles && !folderFilesInfo && driveFolderUrl.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], null, (accessToken || refreshToken)
106366
106991
  ? 'Введите корректную ссылку на папку Google Drive'
106367
106992
  : 'Войдите в Google, чтобы проверить файлы в папке'))))),
106368
- !driveFolderUrl.trim() ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { severity: "info", sx: { mt: 2 } },
106369
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body1", sx: { fontWeight: 'bold', mb: 1 } }, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u043F\u0430\u043F\u043A\u0443 Google Drive \u0434\u043B\u044F \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0435\u043D\u0438\u044F"),
106370
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2" }, "\u0414\u043B\u044F \u0440\u0430\u0431\u043E\u0442\u044B \u0441 \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0435\u0439 \u043A\u043E\u043D\u0442\u0435\u043D\u0442\u0430 \u0438 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0439 \u043D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E \u0443\u043A\u0430\u0437\u0430\u0442\u044C \u043F\u0430\u043F\u043A\u0443 Google Drive."))) : !extractFolderId(driveFolderUrl) ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { severity: "warning", sx: { mt: 2 } },
106371
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body1", sx: { fontWeight: 'bold', mb: 1 } }, "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u0443\u044E \u0441\u0441\u044B\u043B\u043A\u0443 \u043D\u0430 \u043F\u0430\u043F\u043A\u0443"),
106372
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2" }, "\u0421\u0441\u044B\u043B\u043A\u0430 \u0434\u043E\u043B\u0436\u043D\u0430 \u0431\u044B\u0442\u044C \u0432 \u0444\u043E\u0440\u043C\u0430\u0442\u0435: https://drive.google.com/drive/folders/..."))) : loadingContentFromDrive ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: {
106993
+ !driveFolderUrl.trim() ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { severity: "info", sx: { mt: 2 } },
106994
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body1", sx: { fontWeight: 'bold', mb: 1 } }, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u043F\u0430\u043F\u043A\u0443 Google Drive \u0434\u043B\u044F \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0435\u043D\u0438\u044F"),
106995
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2" }, "\u0414\u043B\u044F \u0440\u0430\u0431\u043E\u0442\u044B \u0441 \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0435\u0439 \u043A\u043E\u043D\u0442\u0435\u043D\u0442\u0430 \u0438 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0439 \u043D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E \u0443\u043A\u0430\u0437\u0430\u0442\u044C \u043F\u0430\u043F\u043A\u0443 Google Drive."))) : !extractFolderId(driveFolderUrl) ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { severity: "warning", sx: { mt: 2 } },
106996
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body1", sx: { fontWeight: 'bold', mb: 1 } }, "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u0443\u044E \u0441\u0441\u044B\u043B\u043A\u0443 \u043D\u0430 \u043F\u0430\u043F\u043A\u0443"),
106997
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2" }, "\u0421\u0441\u044B\u043B\u043A\u0430 \u0434\u043E\u043B\u0436\u043D\u0430 \u0431\u044B\u0442\u044C \u0432 \u0444\u043E\u0440\u043C\u0430\u0442\u0435: https://drive.google.com/drive/folders/..."))) : loadingContentFromDrive ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: {
106373
106998
  display: 'flex',
106374
106999
  flexDirection: 'column',
106375
107000
  alignItems: 'center',
@@ -106383,86 +107008,138 @@ ${imageData.originalPrompt}
106383
107008
  ? 'rgba(25, 118, 210, 0.1)'
106384
107009
  : 'rgba(25, 118, 210, 0.05)'
106385
107010
  } },
106386
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 40, sx: { color: 'primary.main' } }),
106387
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body1", sx: { color: 'text.secondary', fontWeight: 'bold' } }, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u0434\u0430\u043D\u043D\u044B\u0445 \u0438\u0437 \u043F\u0430\u043F\u043A\u0438..."))) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
106388
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: { xs: 'column', sm: 'row' }, spacing: 2 },
106389
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "Brand (Short ID)", variant: "outlined", sx: { flex: '0 0 320px', minWidth: 280 }, value: brand, InputProps: { readOnly: true }, placeholder: "\u0410\u0432\u0442\u043E: \u0442\u043E\u0432\u0430\u0440-\u0433\u0435\u043E-\u0446\u0435\u043D\u0430", helperText: "\u0410\u0432\u0442\u043E\u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u044F \u0438\u0437 \u0442\u043E\u0432\u0430\u0440\u0430, \u0433\u0435\u043E \u0438 \u0446\u0435\u043D\u044B" }),
106390
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { flex: 1, minWidth: 0 } },
106391
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "Link", variant: "outlined", fullWidth: true, value: link, onChange: (e) => handleLinkChange(e.target.value), onBlur: handleLinkBlur, error: !!linkError, helperText: linkError, placeholder: "https://example.com/product/", inputRef: linkInputRef, InputProps: { onPaste: handleLinkPaste } }))),
106392
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', gap: 1, alignItems: 'flex-start', mt: 1.5 } },
106393
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "\u0414\u043E\u043F. \u043C\u0430\u043A\u0440\u043E\u0441\u044B (\u043A\u0430\u0442\u0430\u043B\u043E\u0433)", variant: "outlined", size: "small", fullWidth: true, multiline: true, minRows: 2, value: catalogLinkExtraMacros, onChange: (e) => setCatalogLinkExtraMacros(e.target.value), placeholder: DEFAULT_CATALOG_LINK_EXTRA_MACROS, helperText: "\u0414\u043E\u0431\u0430\u0432\u043B\u044F\u0435\u0442\u0441\u044F \u043F\u043E\u0441\u043B\u0435 creative_id \u0438 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u043E\u0432 \u043F\u043E\u0434\u0445\u043E\u0434\u043E\u0432. \u0421\u043E\u0445\u0440\u0430\u043D\u044F\u0435\u0442\u0441\u044F \u0432 JSON \u043D\u0430\u0441\u0442\u0440\u043E\u0435\u043A \u043F\u0430\u043F\u043A\u0438.", sx: { '& .MuiInputBase-input': { fontFamily: 'monospace', fontSize: 12 } } }),
106394
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { spacing: 0.5, sx: { flexShrink: 0, mt: 0.5 } },
106395
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { size: "small", variant: "outlined", onClick: () => setCatalogLinkExtraMacros(DEFAULT_CATALOG_LINK_EXTRA_MACROS), sx: { textTransform: 'none', whiteSpace: 'nowrap' } }, "redtrack"),
106396
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { size: "small", variant: "outlined", onClick: () => setCatalogLinkExtraMacros(DEFAULT_CATALOG_LINK_KEITARO_MACROS), sx: { textTransform: 'none', whiteSpace: 'nowrap' } }, "keitaro"))),
106397
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { spacing: 1.5, sx: { mt: 1 } },
106398
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: { xs: 'column', sm: 'row' }, spacing: 2, alignItems: { xs: 'stretch', sm: 'flex-start' } },
106399
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { control: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { checked: catalogUrlIncludeTextApproach, onChange: e => setCatalogUrlIncludeTextApproach(e.target.checked), size: "small" }), label: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
106400
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2" }, "\u041F\u043E\u0434\u0445\u043E\u0434 \u043A \u0442\u0435\u043A\u0441\u0442\u0443 \u0432 URL \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430"),
106401
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", color: "text.secondary" }, "\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u2014 \u043D\u043E\u043C\u0435\u0440 \u0441\u0442\u0440\u043E\u043A\u0438 \u043F\u043E\u0434\u0445\u043E\u0434\u0430 \u0434\u043B\u044F \u043F\u0430\u0440 \u0432 \u0442\u0430\u0431\u043B\u0438\u0446\u0435 \u043D\u0430\u0441\u0442\u0440\u043E\u0435\u043A (1\u201310)")), sx: { alignItems: 'flex-start', mr: 0, flex: { sm: '1 1 200px' }, minWidth: 0 } }),
106402
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "\u041F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 \u0434\u043B\u044F \u0442\u0435\u043A\u0441\u0442\u0430", size: "small", value: catalogUrlTextApproachParam, onChange: e => setCatalogUrlTextApproachParam(e.target.value), placeholder: DEFAULT_CATALOG_TEXT_APPROACH_PARAM, sx: { width: { xs: '100%', sm: 200 }, flexShrink: 0 }, helperText: `По умолчанию: ${DEFAULT_CATALOG_TEXT_APPROACH_PARAM}` })),
106403
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: { xs: 'column', sm: 'row' }, spacing: 2, alignItems: { xs: 'stretch', sm: 'flex-start' } },
106404
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { control: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { checked: catalogUrlIncludeCreoApproach, onChange: e => setCatalogUrlIncludeCreoApproach(e.target.checked), size: "small" }), label: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
106405
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2" }, "\u041F\u043E\u0434\u0445\u043E\u0434 \u043A \u043A\u0440\u0435\u043E \u0432 URL \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430"),
106406
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", color: "text.secondary" }, "\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u2014 \u043D\u043E\u043C\u0435\u0440 N \u0438\u0437 \u0438\u043C\u0435\u043D\u0438 \u0444\u0430\u0439\u043B\u0430 N_xxxxxxxx.png \u043D\u0430 Drive (N = \u0441\u0442\u0440\u043E\u043A\u0430 \u043F\u043E\u0434\u0445\u043E\u0434\u0430 \u043A \u043A\u0440\u0435\u043E, 1\u201310); \u00AB (1)\u00BB \u043E\u0442 \u0434\u0443\u0431\u043B\u0438\u043A\u0430\u0442\u0430 Google \u0441\u043D\u0438\u043C\u0430\u0435\u0442\u0441\u044F")), sx: { alignItems: 'flex-start', mr: 0, flex: { sm: '1 1 200px' }, minWidth: 0 } }),
106407
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "\u041F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 \u0434\u043B\u044F \u043A\u0440\u0435\u043E", size: "small", value: catalogUrlCreoApproachParam, onChange: e => setCatalogUrlCreoApproachParam(e.target.value), placeholder: DEFAULT_CATALOG_CREO_APPROACH_PARAM, sx: { width: { xs: '100%', sm: 200 }, flexShrink: 0 }, helperText: `По умолчанию: ${DEFAULT_CATALOG_CREO_APPROACH_PARAM}` }))),
106408
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { sx: { my: 2 } }),
106409
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h6", gutterBottom: true }, "AI Generation Settings"),
106410
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
106411
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "\u0422\u043E\u0432\u0430\u0440", variant: "outlined", fullWidth: true, value: generateProduct, onChange: (e) => setGenerateProduct(e.target.value), placeholder: "\u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440: \u043A\u0430\u043F\u043B\u0438 \u043E\u0442 \u043F\u0430\u0440\u0430\u0437\u0438\u0442\u043E\u0432 Detoxil Water", sx: { mb: 2 } })),
106412
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: "row", spacing: 2, sx: { mb: 2 } },
106413
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "\u0413\u0435\u043E", variant: "outlined", fullWidth: true, value: generateGeo, onChange: (e) => setGenerateGeo(e.target.value), placeholder: "\u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440: \u0420\u0443\u043C\u044B\u043D\u0438\u044F" }),
106414
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "\u0426\u0435\u043D\u0430 \u0438 \u0432\u0430\u043B\u044E\u0442\u0430", variant: "outlined", fullWidth: true, value: generatePriceWithCurrency, onChange: (e) => setGeneratePriceWithCurrency(e.target.value), placeholder: "\u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440: 29 euro, 11400 HUF, 149 RON \u0438\u043B\u0438 \u043B\u044E\u0431\u043E\u0439 \u0434\u0440\u0443\u0433\u043E\u0439 \u0444\u043E\u0440\u043C\u0430\u0442", helperText: "\u041B\u044E\u0431\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0434\u043B\u044F \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u044F \u0432 \u043F\u0440\u043E\u043C\u043F\u0442\u0435 \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0439. \u041A\u043D\u043E\u043F\u043A\u0438 \u0441\u043F\u0440\u0430\u0432\u0430 \u043F\u043E\u0434\u0441\u0442\u0430\u0432\u043B\u044F\u044E\u0442 \u0441\u0438\u043C\u0432\u043E\u043B \u0432\u0430\u043B\u044E\u0442\u044B, \u0446\u0438\u0444\u0440\u0443 \u0431\u0435\u0440\u0443\u0442 \u0438\u0437 \u043F\u043E\u043B\u044F (\u0438\u043B\u0438 99).", InputProps: {
106415
- endAdornment: (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { position: "end" },
106416
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 0.25, mr: -0.5 } },
106417
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { title: "\u041F\u043E\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044C $ (\u0446\u0438\u0444\u0440\u0430 \u0438\u0437 \u043F\u043E\u043B\u044F \u0438\u043B\u0438 99)" },
106418
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_8__["default"], { size: "small", "aria-label": "\u0411\u044B\u0441\u0442\u0440\u043E \u0434\u043E\u043B\u043B\u0430\u0440", onClick: () => {
107011
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 40, sx: { color: 'primary.main' } }),
107012
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body1", sx: { color: 'text.secondary', fontWeight: 'bold' } }, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u0434\u0430\u043D\u043D\u044B\u0445 \u0438\u0437 \u043F\u0430\u043F\u043A\u0438..."))) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
107013
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { direction: { xs: 'column', sm: 'row' }, spacing: 2 },
107014
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { label: "Brand (Short ID)", variant: "outlined", sx: { flex: '0 0 320px', minWidth: 280 }, value: brand, InputProps: { readOnly: true }, placeholder: "\u0410\u0432\u0442\u043E: \u0442\u043E\u0432\u0430\u0440-\u0433\u0435\u043E-\u0446\u0435\u043D\u0430", helperText: "\u0410\u0432\u0442\u043E\u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u044F \u0438\u0437 \u0442\u043E\u0432\u0430\u0440\u0430, \u0433\u0435\u043E \u0438 \u0446\u0435\u043D\u044B" }),
107015
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { flex: 1, minWidth: 0 } },
107016
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { label: "Link", variant: "outlined", fullWidth: true, value: link, onChange: (e) => handleLinkChange(e.target.value), onBlur: handleLinkBlur, error: !!linkError, helperText: linkError, placeholder: "https://example.com/product/", inputRef: linkInputRef, InputProps: {
107017
+ onPaste: handleLinkPaste,
107018
+ endAdornment: (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { position: "end" },
107019
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { title: "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0438\u0437 \u0431\u0443\u0444\u0435\u0440\u0430 \u043E\u0431\u043C\u0435\u043D\u0430" },
107020
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_9__["default"], { edge: "end", "aria-label": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0441\u0441\u044B\u043B\u043A\u0443 \u043D\u0430 \u0442\u043E\u0432\u0430\u0440 \u0438\u0437 \u0431\u0443\u0444\u0435\u0440\u0430", onClick: () => void handlePasteProductLink() },
107021
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_48__["default"], null)))))
107022
+ } }))),
107023
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', gap: 1, alignItems: 'flex-start', mt: 1.5 } },
107024
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { label: "\u0414\u043E\u043F. \u043C\u0430\u043A\u0440\u043E\u0441\u044B (\u043A\u0430\u0442\u0430\u043B\u043E\u0433)", variant: "outlined", size: "small", fullWidth: true, multiline: true, minRows: 2, value: catalogLinkExtraMacros, onChange: (e) => setCatalogLinkExtraMacros(e.target.value), placeholder: DEFAULT_CATALOG_LINK_EXTRA_MACROS, helperText: "\u0414\u043E\u0431\u0430\u0432\u043B\u044F\u0435\u0442\u0441\u044F \u043F\u043E\u0441\u043B\u0435 creative_id \u0438 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u043E\u0432 \u043F\u043E\u0434\u0445\u043E\u0434\u043E\u0432 (sub18, sub19, sub20 \u043F\u0440\u0438 \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u0438). \u0421\u043E\u0445\u0440\u0430\u043D\u044F\u0435\u0442\u0441\u044F \u0432 JSON \u043D\u0430\u0441\u0442\u0440\u043E\u0435\u043A \u043F\u0430\u043F\u043A\u0438.", sx: { '& .MuiInputBase-input': { fontFamily: 'monospace', fontSize: 12 } } }),
107025
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { spacing: 0.5, sx: { flexShrink: 0, mt: 0.5 } },
107026
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { size: "small", variant: "outlined", onClick: () => setCatalogLinkExtraMacros(DEFAULT_CATALOG_LINK_EXTRA_MACROS), sx: { textTransform: 'none', whiteSpace: 'nowrap' } }, "redtrack"),
107027
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { size: "small", variant: "outlined", onClick: () => setCatalogLinkExtraMacros(DEFAULT_CATALOG_LINK_KEITARO_MACROS), sx: { textTransform: 'none', whiteSpace: 'nowrap' } }, "keitaro"))),
107028
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { spacing: 1.5, sx: { mt: 1 } },
107029
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { direction: { xs: 'column', sm: 'row' }, spacing: 2, alignItems: { xs: 'stretch', sm: 'flex-start' } },
107030
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { control: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_25__["default"], { checked: catalogUrlIncludeTextApproach, onChange: e => setCatalogUrlIncludeTextApproach(e.target.checked), size: "small" }), label: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], null,
107031
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2" }, "\u041F\u043E\u0434\u0445\u043E\u0434 \u043A \u0442\u0435\u043A\u0441\u0442\u0443 \u0432 URL \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430"),
107032
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", color: "text.secondary" }, "\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u2014 \u043D\u043E\u043C\u0435\u0440 \u0441\u0442\u0440\u043E\u043A\u0438 \u043F\u043E\u0434\u0445\u043E\u0434\u0430 \u0434\u043B\u044F \u043F\u0430\u0440 \u0432 \u0442\u0430\u0431\u043B\u0438\u0446\u0435 \u043D\u0430\u0441\u0442\u0440\u043E\u0435\u043A (1\u201310)")), sx: { alignItems: 'flex-start', mr: 0, flex: { sm: '1 1 200px' }, minWidth: 0 } }),
107033
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { label: "\u041F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 \u0434\u043B\u044F \u0442\u0435\u043A\u0441\u0442\u0430", size: "small", value: catalogUrlTextApproachParam, onChange: e => setCatalogUrlTextApproachParam(e.target.value), placeholder: DEFAULT_CATALOG_TEXT_APPROACH_PARAM, sx: { width: { xs: '100%', sm: 200 }, flexShrink: 0 }, helperText: `По умолчанию: ${DEFAULT_CATALOG_TEXT_APPROACH_PARAM}` })),
107034
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { direction: { xs: 'column', sm: 'row' }, spacing: 2, alignItems: { xs: 'stretch', sm: 'flex-start' } },
107035
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { control: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_25__["default"], { checked: catalogUrlIncludeCreoApproach, onChange: e => setCatalogUrlIncludeCreoApproach(e.target.checked), size: "small" }), label: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], null,
107036
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2" }, "\u041F\u043E\u0434\u0445\u043E\u0434 \u043A \u043A\u0440\u0435\u043E \u0432 URL \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430"),
107037
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", color: "text.secondary" }, "\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u2014 \u043D\u043E\u043C\u0435\u0440 N \u0438\u0437 \u0438\u043C\u0435\u043D\u0438 N_xxxxxxxx_11|23.png \u043D\u0430 Drive (N = \u0441\u0442\u0440\u043E\u043A\u0430 \u043F\u043E\u0434\u0445\u043E\u0434\u0430 \u043A \u043A\u0440\u0435\u043E, 1\u201310); \u00AB (1)\u00BB \u043E\u0442 \u0434\u0443\u0431\u043B\u0438\u043A\u0430\u0442\u0430 Google \u0441\u043D\u0438\u043C\u0430\u0435\u0442\u0441\u044F")), sx: { alignItems: 'flex-start', mr: 0, flex: { sm: '1 1 200px' }, minWidth: 0 } }),
107038
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { label: "\u041F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 \u0434\u043B\u044F \u043A\u0440\u0435\u043E", size: "small", value: catalogUrlCreoApproachParam, onChange: e => setCatalogUrlCreoApproachParam(e.target.value), placeholder: DEFAULT_CATALOG_CREO_APPROACH_PARAM, sx: { width: { xs: '100%', sm: 200 }, flexShrink: 0 }, helperText: `По умолчанию: ${DEFAULT_CATALOG_CREO_APPROACH_PARAM}` })),
107039
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { direction: { xs: 'column', sm: 'row' }, spacing: 2, alignItems: { xs: 'stretch', sm: 'flex-start' } },
107040
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { control: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_25__["default"], { checked: catalogUrlIncludeImageAspect, onChange: e => setCatalogUrlIncludeImageAspect(e.target.checked), size: "small" }), label: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], null,
107041
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2" }, "\u0422\u0438\u043F \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u044F \u0432 URL \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430"),
107042
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", color: "text.secondary" }, "\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u044F 1_1 (1:1) \u0438 2_3 (2:3) \u2014 \u0438\u0437 \u0441\u0443\u0444\u0444\u0438\u043A\u0441\u0430 _11 / _23 \u0432 \u0438\u043C\u0435\u043D\u0438 \u0444\u0430\u0439\u043B\u0430 \u043D\u0430 Drive \u0438\u043B\u0438 \u0438\u0437 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u0441\u043E\u043E\u0442\u043D\u043E\u0448\u0435\u043D\u0438\u044F, \u0435\u0441\u043B\u0438 \u0441\u0443\u0444\u0444\u0438\u043A\u0441\u0430 \u043D\u0435\u0442")), sx: { alignItems: 'flex-start', mr: 0, flex: { sm: '1 1 200px' }, minWidth: 0 } }),
107043
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { label: "\u041F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 \u0434\u043B\u044F \u0442\u0438\u043F\u0430 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u044F", size: "small", value: catalogUrlImageAspectParam, onChange: e => setCatalogUrlImageAspectParam(e.target.value), placeholder: DEFAULT_CATALOG_IMAGE_ASPECT_PARAM, sx: { width: { xs: '100%', sm: 200 }, flexShrink: 0 }, helperText: `По умолчанию: ${DEFAULT_CATALOG_IMAGE_ASPECT_PARAM}` }))),
107044
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { sx: { my: 2 } }),
107045
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "h6", gutterBottom: true }, "AI Generation Settings"),
107046
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], null,
107047
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { label: "\u0422\u043E\u0432\u0430\u0440", variant: "outlined", fullWidth: true, value: generateProduct, onChange: (e) => setGenerateProduct(e.target.value), placeholder: "\u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440: \u043A\u0430\u043F\u043B\u0438 \u043E\u0442 \u043F\u0430\u0440\u0430\u0437\u0438\u0442\u043E\u0432 Detoxil Water", sx: { mb: 2 } })),
107048
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { direction: "row", spacing: 2, sx: { mb: 2 } },
107049
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { label: "\u0413\u0435\u043E", variant: "outlined", fullWidth: true, value: generateGeo, onChange: (e) => setGenerateGeo(e.target.value), placeholder: "\u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440: \u0420\u0443\u043C\u044B\u043D\u0438\u044F" }),
107050
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { label: "\u0426\u0435\u043D\u0430 \u0438 \u0432\u0430\u043B\u044E\u0442\u0430", variant: "outlined", fullWidth: true, value: generatePriceWithCurrency, onChange: (e) => setGeneratePriceWithCurrency(e.target.value), placeholder: "\u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440: 29 euro, 11400 HUF, 149 RON \u0438\u043B\u0438 \u043B\u044E\u0431\u043E\u0439 \u0434\u0440\u0443\u0433\u043E\u0439 \u0444\u043E\u0440\u043C\u0430\u0442", helperText: "\u041B\u044E\u0431\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0434\u043B\u044F \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u044F \u0432 \u043F\u0440\u043E\u043C\u043F\u0442\u0435 \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0439. \u041A\u043D\u043E\u043F\u043A\u0438 \u0441\u043F\u0440\u0430\u0432\u0430 \u043F\u043E\u0434\u0441\u0442\u0430\u0432\u043B\u044F\u044E\u0442 \u0441\u0438\u043C\u0432\u043E\u043B \u0432\u0430\u043B\u044E\u0442\u044B, \u0446\u0438\u0444\u0440\u0443 \u0431\u0435\u0440\u0443\u0442 \u0438\u0437 \u043F\u043E\u043B\u044F (\u0438\u043B\u0438 99).", InputProps: {
107051
+ endAdornment: (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { position: "end" },
107052
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 0.25, mr: -0.5 } },
107053
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { title: "\u041F\u043E\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044C $ (\u0446\u0438\u0444\u0440\u0430 \u0438\u0437 \u043F\u043E\u043B\u044F \u0438\u043B\u0438 99)" },
107054
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_9__["default"], { size: "small", "aria-label": "\u0411\u044B\u0441\u0442\u0440\u043E \u0434\u043E\u043B\u043B\u0430\u0440", onClick: () => {
106419
107055
  const n = extractLeadingPriceNumber(generatePriceWithCurrency);
106420
107056
  setGeneratePriceWithCurrency(`$${n}`);
106421
107057
  }, edge: "end" },
106422
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_39__["default"], { sx: { fontSize: 20 } }))),
106423
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { title: "\u041F\u043E\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u20AC (\u0446\u0438\u0444\u0440\u0430 \u0438\u0437 \u043F\u043E\u043B\u044F \u0438\u043B\u0438 99)" },
106424
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_8__["default"], { size: "small", "aria-label": "\u0411\u044B\u0441\u0442\u0440\u043E \u0435\u0432\u0440\u043E", onClick: () => {
107058
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_40__["default"], { sx: { fontSize: 20 } }))),
107059
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { title: "\u041F\u043E\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u20AC (\u0446\u0438\u0444\u0440\u0430 \u0438\u0437 \u043F\u043E\u043B\u044F \u0438\u043B\u0438 99)" },
107060
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_9__["default"], { size: "small", "aria-label": "\u0411\u044B\u0441\u0442\u0440\u043E \u0435\u0432\u0440\u043E", onClick: () => {
106425
107061
  const n = extractLeadingPriceNumber(generatePriceWithCurrency);
106426
107062
  setGeneratePriceWithCurrency(`€${n}`);
106427
107063
  }, edge: "end", sx: { minWidth: 34, fontSize: '1rem', fontWeight: 700 } }, "\u20AC")),
106428
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { title: "\u041F\u043E\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044C z\u0142 (\u0446\u0438\u0444\u0440\u0430 \u0438\u0437 \u043F\u043E\u043B\u044F \u0438\u043B\u0438 99)" },
106429
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_8__["default"], { size: "small", "aria-label": "\u0411\u044B\u0441\u0442\u0440\u043E \u0437\u043B\u043E\u0442\u044B\u0435", onClick: () => {
107064
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { title: "\u041F\u043E\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044C z\u0142 (\u0446\u0438\u0444\u0440\u0430 \u0438\u0437 \u043F\u043E\u043B\u044F \u0438\u043B\u0438 99)" },
107065
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_9__["default"], { size: "small", "aria-label": "\u0411\u044B\u0441\u0442\u0440\u043E \u0437\u043B\u043E\u0442\u044B\u0435", onClick: () => {
106430
107066
  const n = extractLeadingPriceNumber(generatePriceWithCurrency);
106431
107067
  setGeneratePriceWithCurrency(`${n} zł`);
106432
107068
  }, edge: "end", sx: { minWidth: 36, fontSize: '0.8rem', fontWeight: 700, letterSpacing: -0.3 } }, "z\u0142"))))),
106433
107069
  } })),
106434
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
106435
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "\u0414\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u0430\u044F \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044F (\u043D\u0435 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E)", variant: "outlined", fullWidth: true, multiline: true, minRows: 3, value: generateAdditionalInfo, onChange: (e) => setGenerateAdditionalInfo(e.target.value), placeholder: "\u0418\u043D\u0433\u0440\u0435\u0434\u0438\u0435\u043D\u0442\u044B, \u0443\u0442\u043E\u0447\u043D\u0435\u043D\u0438\u044F \u043A \u043F\u0440\u043E\u043C\u043F\u0442\u0443 \u0438 \u0442.\u0434.", sx: { mb: 2 } })),
106436
- openaiApiKey && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: "row", spacing: 2, sx: { mb: 2 } },
106437
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_25__["default"], { fullWidth: true, variant: "outlined" },
106438
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_26__["default"], null, "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0439"),
106439
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_27__["default"], { value: selectedImageModel, onChange: (e) => handleImageModelChange(e.target.value), label: "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0439", disabled: loadingImageModels || imageModels.length === 0 }, loadingImageModels ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { disabled: true },
106440
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
106441
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 16 }),
106442
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u043C\u043E\u0434\u0435\u043B\u0435\u0439...")))) : imageModels.length === 0 ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { disabled: true }, "\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0445 \u043C\u043E\u0434\u0435\u043B\u0435\u0439")) : (imageModels.map((model) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { key: model.id, value: model.id }, model.name))))),
106443
- !loadingImageModels && imageModels.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], null, selectedImageModel === _models__WEBPACK_IMPORTED_MODULE_2__.MODELS.imageGeneration
106444
- ? 'Используется модель по умолчанию'
106445
- : 'Выбрана модель: ' + (imageModels.find(m => m.id === selectedImageModel)?.name || selectedImageModel)))),
106446
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_25__["default"], { fullWidth: true, variant: "outlined" },
106447
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_26__["default"], null, "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u043A\u0440\u0435\u0430\u0442\u0438\u0432\u043E\u0432"),
106448
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_27__["default"], { value: selectedValidationModel, onChange: (e) => handleValidationModelChange(e.target.value), label: "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u043A\u0440\u0435\u0430\u0442\u0438\u0432\u043E\u0432", disabled: loadingValidationModels || validationModels.length === 0 }, loadingValidationModels ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { disabled: true },
106449
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
106450
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 16 }),
106451
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u043C\u043E\u0434\u0435\u043B\u0435\u0439...")))) : validationModels.length === 0 ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { disabled: true }, "\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0445 \u043C\u043E\u0434\u0435\u043B\u0435\u0439")) : (validationModels.map((model) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { key: model.id, value: model.id }, model.name))))),
106452
- !loadingValidationModels && validationModels.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], null, selectedValidationModel === _models__WEBPACK_IMPORTED_MODULE_2__.MODELS.creativeValidation
106453
- ? 'Используется модель по умолчанию'
106454
- : 'Выбрана модель: ' + (validationModels.find(m => m.id === selectedValidationModel)?.name || selectedValidationModel))),
106455
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { control: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { checked: validationDisabled, onChange: (e) => handleValidationDisabledChange(e.target.checked), color: "primary" }), label: "\u041E\u0442\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443", sx: { mt: 1 } })))),
106456
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: "row", spacing: 2, sx: { mb: 2 } },
106457
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { variant: "contained", color: "primary", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_40__["default"], null), onClick: handleGenerateContent, disabled: generating || loadingContentFromDrive || !openaiApiKey || !generateProduct.trim() || !generateGeo.trim(), sx: { flexGrow: 1 }, size: "large" }, generating ? 'Generating...' : 'Generate Titles & Descriptions'),
106458
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: "row", spacing: 1, alignItems: "center", sx: { flexGrow: 1 } },
106459
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { title: imageAspectRatio === '1:1'
107070
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], null,
107071
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { label: "\u0414\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u0430\u044F \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044F (\u043D\u0435 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E)", variant: "outlined", fullWidth: true, multiline: true, minRows: 3, value: generateAdditionalInfo, onChange: (e) => setGenerateAdditionalInfo(e.target.value), placeholder: "\u0418\u043D\u0433\u0440\u0435\u0434\u0438\u0435\u043D\u0442\u044B, \u0443\u0442\u043E\u0447\u043D\u0435\u043D\u0438\u044F \u043A \u043F\u0440\u043E\u043C\u043F\u0442\u0443 \u0438 \u0442.\u0434.", sx: { mb: 2 } })),
107072
+ openaiApiKey && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
107073
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { direction: { xs: 'column', md: 'row' }, spacing: 2, sx: { mb: 2, alignItems: 'stretch' } },
107074
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_26__["default"], { fullWidth: true, variant: "outlined", sx: { flex: { md: '1 1 0' }, minWidth: { md: 0 } } },
107075
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_27__["default"], null, "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u043A\u0432\u0430\u0434\u0440\u0430\u0442\u043E\u0432 (1:1)"),
107076
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { value: selectedImageModelSquare, onChange: (e) => handleImageModelSquareChange(e.target.value), label: "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u043A\u0432\u0430\u0434\u0440\u0430\u0442\u043E\u0432 (1:1)", disabled: loadingImageModels || imageModels.length === 0 }, loadingImageModels ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { disabled: true },
107077
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
107078
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 16 }),
107079
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u043C\u043E\u0434\u0435\u043B\u0435\u0439...")))) : imageModels.length === 0 ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { disabled: true }, "\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0445 \u043C\u043E\u0434\u0435\u043B\u0435\u0439")) : (imageModels.map((model) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { key: model.id, value: model.id }, model.name))))),
107080
+ !loadingImageModels && imageModels.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], null, selectedImageModelSquare === _models__WEBPACK_IMPORTED_MODULE_3__.MODELS.imageGeneration
107081
+ ? 'По умолчанию для квадрата'
107082
+ : '1:1: ' +
107083
+ (imageModels.find(m => m.id === selectedImageModelSquare)?.name ||
107084
+ selectedImageModelSquare)))),
107085
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_26__["default"], { fullWidth: true, variant: "outlined", sx: { flex: { md: '1 1 0' }, minWidth: { md: 0 } } },
107086
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_27__["default"], null, "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u043F\u0440\u044F\u043C\u043E\u0443\u0433\u043E\u043B\u044C\u043D\u0438\u043A\u043E\u0432 (2:3)"),
107087
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { value: selectedImageModelRect, onChange: (e) => handleImageModelRectChange(e.target.value), label: "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u043F\u0440\u044F\u043C\u043E\u0443\u0433\u043E\u043B\u044C\u043D\u0438\u043A\u043E\u0432 (2:3)", disabled: loadingImageModels || imageModels.length === 0 }, loadingImageModels ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { disabled: true },
107088
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
107089
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 16 }),
107090
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u043C\u043E\u0434\u0435\u043B\u0435\u0439...")))) : imageModels.length === 0 ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { disabled: true }, "\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0445 \u043C\u043E\u0434\u0435\u043B\u0435\u0439")) : (imageModels.map((model) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { key: model.id, value: model.id }, model.name))))),
107091
+ !loadingImageModels && imageModels.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], null, selectedImageModelRect === _models__WEBPACK_IMPORTED_MODULE_3__.MODELS.imageGeneration
107092
+ ? 'По умолчанию для 2:3'
107093
+ : '2:3: ' +
107094
+ (imageModels.find(m => m.id === selectedImageModelRect)?.name ||
107095
+ selectedImageModelRect)))),
107096
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_26__["default"], { fullWidth: true, variant: "outlined", sx: { flex: { md: '1 1 0' }, minWidth: { md: 0 } } },
107097
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_27__["default"], null, "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u043A\u0440\u0435\u0430\u0442\u0438\u0432\u043E\u0432"),
107098
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { value: selectedValidationModel, onChange: (e) => handleValidationModelChange(e.target.value), label: "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u043A\u0440\u0435\u0430\u0442\u0438\u0432\u043E\u0432", disabled: loadingValidationModels || validationModels.length === 0 }, loadingValidationModels ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { disabled: true },
107099
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
107100
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 16 }),
107101
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u043C\u043E\u0434\u0435\u043B\u0435\u0439...")))) : validationModels.length === 0 ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { disabled: true }, "\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0445 \u043C\u043E\u0434\u0435\u043B\u0435\u0439")) : (validationModels.map((model) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { key: model.id, value: model.id }, model.name))))),
107102
+ !loadingValidationModels && validationModels.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], null, selectedValidationModel === _models__WEBPACK_IMPORTED_MODULE_3__.MODELS.creativeValidation
107103
+ ? 'Используется модель по умолчанию'
107104
+ : 'Выбрана модель: ' + (validationModels.find(m => m.id === selectedValidationModel)?.name || selectedValidationModel))),
107105
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { control: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_25__["default"], { checked: validationDisabled, onChange: (e) => handleValidationDisabledChange(e.target.checked), color: "primary" }), label: "\u041E\u0442\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443", sx: { mt: 1 } }),
107106
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { control: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_25__["default"], { checked: autoRemakeOnValidatorError, onChange: (e) => handleAutoRemakeOnValidatorErrorChange(e.target.checked), color: "primary", disabled: validationDisabled }), label: "\u0430\u0432\u0442\u043E\u043F\u0435\u0440\u0435\u0434\u0435\u043B\u043A\u0430", sx: { mt: 0.5, display: 'block' } }),
107107
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { sx: { mt: 0, mx: 0 } }, validationDisabled
107108
+ ? 'Недоступно, пока проверка отключена.'
107109
+ : 'Один раз на слот креатива: при сбое проверки (сеть, таймаут, ошибка API) или когда валидатор вернул «нужна пересборка» с замечаниями — автоматически одна полная переделка с нуля (исходный промпт + product, без правок по списку ошибок).'))),
107110
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { direction: { xs: 'column', md: 'row' }, spacing: 2, sx: { mb: 2, alignItems: 'stretch' } },
107111
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_26__["default"], { fullWidth: true, variant: "outlined", sx: { flex: { md: '1 1 0' }, minWidth: { md: 0 } } },
107112
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_27__["default"], null, "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u043E\u0432 \u0438 \u0442\u0435\u043A\u0441\u0442\u043E\u0432"),
107113
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { value: selectedContentModel, onChange: (e) => handleContentModelChange(e.target.value), label: "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u043E\u0432 \u0438 \u0442\u0435\u043A\u0441\u0442\u043E\u0432", disabled: loadingValidationModels || chatModels.length === 0 }, loadingValidationModels ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { disabled: true },
107114
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
107115
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 16 }),
107116
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u043C\u043E\u0434\u0435\u043B\u0435\u0439...")))) : chatModels.length === 0 ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { disabled: true }, "\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0445 \u043C\u043E\u0434\u0435\u043B\u0435\u0439")) : (chatModels.map((model) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { key: model.id, value: model.id }, model.name))))),
107117
+ !loadingValidationModels && chatModels.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], null, selectedContentModel === _models__WEBPACK_IMPORTED_MODULE_3__.MODELS.contentGeneration
107118
+ ? 'Заголовки, описания, пары и перевод — модель по умолчанию из кода'
107119
+ : 'Заголовки и тексты: ' +
107120
+ (chatModels.find(m => m.id === selectedContentModel)?.name ||
107121
+ selectedContentModel)))),
107122
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_26__["default"], { fullWidth: true, variant: "outlined", sx: { flex: { md: '1 1 0' }, minWidth: { md: 0 } } },
107123
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_27__["default"], null, "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u043B\u0435\u043D\u0434\u0438\u043D\u0433\u0430"),
107124
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { value: selectedLandingModel, onChange: (e) => handleLandingModelChange(e.target.value), label: "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u043B\u0435\u043D\u0434\u0438\u043D\u0433\u0430", disabled: loadingValidationModels || chatModels.length === 0 }, loadingValidationModels ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { disabled: true },
107125
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
107126
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 16 }),
107127
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u043C\u043E\u0434\u0435\u043B\u0435\u0439...")))) : chatModels.length === 0 ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { disabled: true }, "\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0445 \u043C\u043E\u0434\u0435\u043B\u0435\u0439")) : (chatModels.map((model) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { key: model.id, value: model.id }, model.name))))),
107128
+ !loadingValidationModels && chatModels.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], null, selectedLandingModel === _models__WEBPACK_IMPORTED_MODULE_3__.MODELS.landingGeneration
107129
+ ? 'HTML лендинга — модель по умолчанию из кода'
107130
+ : 'Лендинг: ' +
107131
+ (chatModels.find(m => m.id === selectedLandingModel)?.name ||
107132
+ selectedLandingModel))))))),
107133
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { direction: "row", spacing: 2, sx: { mb: 2 } },
107134
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { variant: "contained", color: "primary", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_41__["default"], null), onClick: handleGenerateContent, disabled: generating || loadingContentFromDrive || !openaiApiKey || !generateProduct.trim() || !generateGeo.trim(), sx: { flexGrow: 1 }, size: "large" }, generating ? 'Generating...' : 'Generate Titles & Descriptions'),
107135
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { direction: "row", spacing: 1, alignItems: "center", sx: { flexGrow: 1 } },
107136
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { title: imageAspectRatio === '1:1'
106460
107137
  ? '1:1 — квадрат (1024×1024 px)'
106461
107138
  : imageAspectRatio === '2:3'
106462
107139
  ? '2:3 — портрет (1024×1536 px)'
106463
107140
  : 'Оба — квадрат и портрет на каждый подход', placement: "top" },
106464
107141
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null,
106465
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { value: imageAspectRatio, exclusive: true, onChange: (_e, val) => {
107142
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_30__["default"], { value: imageAspectRatio, exclusive: true, onChange: (_e, val) => {
106466
107143
  if (val && val !== imageAspectRatio) {
106467
107144
  if (generatedImagesData.length > 0) {
106468
107145
  const label = val === 'both' ? 'оба (1:1 + 2:3)' : val;
@@ -106478,10 +107155,10 @@ ${imageData.originalPrompt}
106478
107155
  }
106479
107156
  }
106480
107157
  }, size: "small", disabled: generatingImages, sx: { height: 42 } },
106481
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_30__["default"], { value: "1:1", sx: { px: 1.5, fontWeight: 600, fontSize: '0.8rem' } }, "1:1"),
106482
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_30__["default"], { value: "2:3", sx: { px: 1.5, fontWeight: 600, fontSize: '0.8rem' } }, "2:3"),
106483
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_30__["default"], { value: "both", sx: { px: 1.5, fontWeight: 600, fontSize: '0.8rem' } }, "\u041E\u0431\u0430")))),
106484
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { variant: "contained", color: "secondary", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_40__["default"], null), onClick: handleGenerateImages, disabled: generatingImages ||
107158
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_31__["default"], { value: "1:1", sx: { px: 1.5, fontWeight: 600, fontSize: '0.8rem' } }, "1:1"),
107159
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_31__["default"], { value: "2:3", sx: { px: 1.5, fontWeight: 600, fontSize: '0.8rem' } }, "2:3"),
107160
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_31__["default"], { value: "both", sx: { px: 1.5, fontWeight: 600, fontSize: '0.8rem' } }, "\u041E\u0431\u0430")))),
107161
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { variant: "contained", color: "secondary", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_41__["default"], null), onClick: handleGenerateImages, disabled: generatingImages ||
106485
107162
  !openaiApiKey ||
106486
107163
  (!accessToken && !refreshToken) ||
106487
107164
  !generateProduct.trim() ||
@@ -106499,10 +107176,10 @@ ${imageData.originalPrompt}
106499
107176
  : !driveFolderUrl.trim()
106500
107177
  ? 'Заполните URL папки Google Drive'
106501
107178
  : undefined }, generatingImages ? 'Generating Images...' : 'Generate Images')),
106502
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { variant: "contained", color: "success", size: "large", onClick: handleGenerate, disabled: loading, startIcon: loading ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 20, color: "inherit" }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_44__["default"], null), sx: { py: 1.5, fontSize: '1.1rem', flexGrow: 1 } }, loading ? 'Generating...' : 'Generate Catalog'),
106503
- uploadedLink && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { severity: "success", sx: { flexGrow: 1, minWidth: 0 }, action: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_8__["default"], { "aria-label": "copy link", color: "inherit", size: "small", onClick: handleCopyLink, sx: { ml: 1 } },
106504
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_45__["default"], { fontSize: "small" })) },
106505
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1, flexWrap: 'wrap' } },
107179
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { variant: "contained", color: "success", size: "large", onClick: handleGenerate, disabled: loading, startIcon: loading ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 20, color: "inherit" }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_46__["default"], null), sx: { py: 1.5, fontSize: '1.1rem', flexGrow: 1 } }, loading ? 'Generating...' : 'Generate Catalog'),
107180
+ uploadedLink && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { severity: "success", sx: { flexGrow: 1, minWidth: 0 }, action: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_9__["default"], { "aria-label": "copy link", color: "inherit", size: "small", onClick: handleCopyLink, sx: { ml: 1 } },
107181
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_47__["default"], { fontSize: "small" })) },
107182
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1, flexWrap: 'wrap' } },
106506
107183
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null,
106507
107184
  "\u041A\u0430\u0442\u0430\u043B\u043E\u0433 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D!",
106508
107185
  ' ',
@@ -106510,37 +107187,37 @@ ${imageData.originalPrompt}
106510
107187
  e.preventDefault();
106511
107188
  getElectronAPI().openExternal(uploadedLink);
106512
107189
  }, style: { cursor: 'pointer', textDecoration: 'underline', color: 'inherit' } }, "\u041E\u0442\u043A\u0440\u044B\u0442\u044C \u0432 Google Drive")),
106513
- linkCopied && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'success.dark', fontWeight: 'bold' } }, "\u0421\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u043D\u043E!"))))),
106514
- folderFilesInfo !== null && !folderFilesInfo.hasProduct && driveFolderUrl.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { severity: "warning", sx: { mt: 1 } }, "\u0414\u043B\u044F \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0439 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F product.png/jpg/webp. \u0421\u043D\u0430\u0447\u0430\u043B\u0430 \u0441\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0439\u0442\u0435 \u0435\u0433\u043E \u0441 \u043F\u043E\u043C\u043E\u0449\u044C\u044E \u043A\u043D\u043E\u043F\u043A\u0438 \"Generate Product from Banka\".")),
107190
+ linkCopied && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", sx: { color: 'success.dark', fontWeight: 'bold' } }, "\u0421\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u043D\u043E!"))))),
107191
+ folderFilesInfo !== null && !folderFilesInfo.hasProduct && driveFolderUrl.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { severity: "warning", sx: { mt: 1 } }, "\u0414\u043B\u044F \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0439 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F product.png/jpg/webp. \u0421\u043D\u0430\u0447\u0430\u043B\u0430 \u0441\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0439\u0442\u0435 \u0435\u0433\u043E \u0441 \u043F\u043E\u043C\u043E\u0449\u044C\u044E \u043A\u043D\u043E\u043F\u043A\u0438 \"Generate Product from Banka\".")),
106515
107192
  !generatingImages && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
106516
- !openaiApiKey && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { severity: "error", sx: { mt: 1 } }, "\u0417\u0430\u043F\u043E\u043B\u043D\u0438\u0442\u0435 OpenRouter API Key")),
106517
- openaiApiKey && (!accessToken && !refreshToken) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { severity: "error", sx: { mt: 1 } }, "\u0412\u043E\u0439\u0434\u0438\u0442\u0435 \u0432 Google Drive")),
106518
- openaiApiKey && (accessToken || refreshToken) && !generateProduct.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { severity: "error", sx: { mt: 1 } }, "\u0417\u0430\u043F\u043E\u043B\u043D\u0438\u0442\u0435 \u043F\u043E\u043B\u0435 \u0422\u043E\u0432\u0430\u0440")),
106519
- openaiApiKey && (accessToken || refreshToken) && generateProduct.trim() && !generateGeo.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { severity: "error", sx: { mt: 1 } }, "\u0417\u0430\u043F\u043E\u043B\u043D\u0438\u0442\u0435 \u043F\u043E\u043B\u0435 \u0413\u0435\u043E")),
106520
- openaiApiKey && (accessToken || refreshToken) && generateProduct.trim() && generateGeo.trim() && !driveFolderUrl.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { severity: "error", sx: { mt: 1 } }, "\u0417\u0430\u043F\u043E\u043B\u043D\u0438\u0442\u0435 URL \u043F\u0430\u043F\u043A\u0438 Google Drive"))))),
107193
+ !openaiApiKey && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { severity: "error", sx: { mt: 1 } }, "\u0417\u0430\u043F\u043E\u043B\u043D\u0438\u0442\u0435 OpenRouter API Key")),
107194
+ openaiApiKey && (!accessToken && !refreshToken) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { severity: "error", sx: { mt: 1 } }, "\u0412\u043E\u0439\u0434\u0438\u0442\u0435 \u0432 Google Drive")),
107195
+ openaiApiKey && (accessToken || refreshToken) && !generateProduct.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { severity: "error", sx: { mt: 1 } }, "\u0417\u0430\u043F\u043E\u043B\u043D\u0438\u0442\u0435 \u043F\u043E\u043B\u0435 \u0422\u043E\u0432\u0430\u0440")),
107196
+ openaiApiKey && (accessToken || refreshToken) && generateProduct.trim() && !generateGeo.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { severity: "error", sx: { mt: 1 } }, "\u0417\u0430\u043F\u043E\u043B\u043D\u0438\u0442\u0435 \u043F\u043E\u043B\u0435 \u0413\u0435\u043E")),
107197
+ openaiApiKey && (accessToken || refreshToken) && generateProduct.trim() && generateGeo.trim() && !driveFolderUrl.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { severity: "error", sx: { mt: 1 } }, "\u0417\u0430\u043F\u043E\u043B\u043D\u0438\u0442\u0435 URL \u043F\u0430\u043F\u043A\u0438 Google Drive"))))),
106521
107198
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "file", ref: productFileInputRef, accept: "image/png,image/jpeg,image/jpg,image/webp", style: { display: 'none' }, onChange: handleProductFileSelected }),
106522
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: "row", spacing: 2, sx: { mb: 2 } },
106523
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { variant: "contained", color: "success", startIcon: uploadingProduct ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 20, color: "inherit" }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_44__["default"], null), onClick: handleUploadProductFile, disabled: uploadingProduct || !accessToken || !driveFolderUrl.trim(), sx: { flexGrow: 1 }, size: "large" }, uploadingProduct ? 'Загрузка...' : 'Загрузить product.png/jpg/webp'),
106524
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { variant: "contained", color: "info", startIcon: generatingLanding ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 20, color: "inherit" }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_50__["default"], null), onClick: handleCreateLanding, disabled: generatingLanding ||
107199
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { direction: "row", spacing: 2, sx: { mb: 2 } },
107200
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { variant: "contained", color: "success", startIcon: uploadingProduct ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 20, color: "inherit" }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_46__["default"], null), onClick: handleUploadProductFile, disabled: uploadingProduct || !accessToken || !driveFolderUrl.trim(), sx: { flexGrow: 1 }, size: "large" }, uploadingProduct ? 'Загрузка...' : 'Загрузить product.png/jpg/webp'),
107201
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { variant: "contained", color: "info", startIcon: generatingLanding ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 20, color: "inherit" }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_54__["default"], null), onClick: handleCreateLanding, disabled: generatingLanding ||
106525
107202
  !openaiApiKey ||
106526
107203
  !accessToken ||
106527
107204
  !driveFolderUrl.trim() ||
106528
107205
  (folderFilesInfo !== null && !folderFilesInfo.hasProduct), sx: { flexGrow: 1 }, size: "large", title: folderFilesInfo !== null && !folderFilesInfo.hasProduct
106529
107206
  ? 'product.png/jpg/webp не найден в папке. Загрузите его с помощью кнопки «Загрузить product»'
106530
107207
  : undefined }, generatingLanding ? 'Creating Landing...' : 'Создать лендинг')),
106531
- generatedImagesData.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { mb: 2, mt: 3 } },
106532
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h6", gutterBottom: true, sx: { fontWeight: 'bold' } },
107208
+ generatedImagesData.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { mb: 2, mt: 3 } },
107209
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "h6", gutterBottom: true, sx: { fontWeight: 'bold' } },
106533
107210
  "\u0421\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u043A\u0440\u0435\u043E (",
106534
107211
  generatedImagesData.filter(img => img.imageUrl).length,
106535
107212
  "/",
106536
107213
  generatedImagesData.length,
106537
107214
  ")"),
106538
- checkingImages && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1, mb: 2 } },
106539
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 16 }),
106540
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'text.secondary' } }, "\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043A\u0430\u0447\u0435\u0441\u0442\u0432\u0430..."))),
106541
- generatedImagesData.length > 0 && generatedImagesData.some(img => !img.uploaded && img.imageUrl) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { mb: 2 } },
106542
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { variant: "contained", color: "primary", onClick: handleUploadAllImages, disabled: uploadingImages || generatingImages || !driveFolderUrl.trim(), startIcon: uploadingImages ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 20 }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_44__["default"], null), fullWidth: true }, uploadingImages ? 'Загрузка...' : `Загрузить все (${generatedImagesData.filter(img => !img.uploaded && img.imageUrl).length})`))),
106543
- generatedImagesData.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: {
107215
+ checkingImages && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1, mb: 2 } },
107216
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 16 }),
107217
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", sx: { color: 'text.secondary' } }, "\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043A\u0430\u0447\u0435\u0441\u0442\u0432\u0430..."))),
107218
+ generatedImagesData.length > 0 && generatedImagesData.some(img => !img.uploaded && img.imageUrl) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { mb: 2 } },
107219
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { variant: "contained", color: "primary", onClick: handleUploadAllImages, disabled: uploadingImages || generatingImages || !driveFolderUrl.trim(), startIcon: uploadingImages ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 20 }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_46__["default"], null), fullWidth: true }, uploadingImages ? 'Загрузка...' : `Загрузить все (${generatedImagesData.filter(img => !img.uploaded && img.imageUrl).length})`))),
107220
+ generatedImagesData.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: {
106544
107221
  display: 'grid',
106545
107222
  gridTemplateColumns: { xs: '1fr', sm: 'repeat(2, 1fr)', md: 'repeat(3, 1fr)' },
106546
107223
  gap: 2,
@@ -106550,7 +107227,22 @@ ${imageData.originalPrompt}
106550
107227
  const aspectRatioCss = imgRatio === '2:3' ? '2 / 3' : '1 / 1';
106551
107228
  const remakeN = imageData.remakeCount ?? 0;
106552
107229
  const remakeBg = getRemakeHighlightBackground(remakeN, darkMode);
106553
- return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { key: imageData.index, sx: {
107230
+ const hideUploadedPreview = !imageData.regenerating &&
107231
+ !!imageData.uploaded &&
107232
+ imageData.uploadedPreviewHidden !== false;
107233
+ const creativePreviewBorder = (theme) => {
107234
+ if (imageData.uploaded || imageData.checkStatus === 'ok') {
107235
+ return `2px solid ${theme.palette.success.main}`;
107236
+ }
107237
+ if (imageData.checkStatus === 'needs_rebuild') {
107238
+ return `2px solid ${theme.palette.error.main}`;
107239
+ }
107240
+ if (imageData.checkStatus === 'checking') {
107241
+ return `2px solid ${theme.palette.warning.main}`;
107242
+ }
107243
+ return `2px solid ${theme.palette.primary.main}`;
107244
+ };
107245
+ return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { key: imageData.index, sx: {
106554
107246
  position: 'relative',
106555
107247
  ...(remakeBg
106556
107248
  ? {
@@ -106561,9 +107253,9 @@ ${imageData.originalPrompt}
106561
107253
  }
106562
107254
  : {})
106563
107255
  } },
106564
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { title: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u043A\u0440\u0435\u0430\u0442\u0438\u0432" },
107256
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { title: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u043A\u0440\u0435\u0430\u0442\u0438\u0432" },
106565
107257
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null,
106566
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_8__["default"], { size: "small", "aria-label": "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u043A\u0440\u0435\u0430\u0442\u0438\u0432", sx: {
107258
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_9__["default"], { size: "small", "aria-label": "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u043A\u0440\u0435\u0430\u0442\u0438\u0432", sx: {
106567
107259
  position: 'absolute',
106568
107260
  top: 6,
106569
107261
  right: 6,
@@ -106587,9 +107279,38 @@ ${imageData.originalPrompt}
106587
107279
  }, onClick: () => handleDeleteGeneratedImage(imageData.index), disabled: generatingImages ||
106588
107280
  imageData.generating ||
106589
107281
  !!imageData.regenerating ||
106590
- !!imageData.uploading },
106591
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_46__["default"], { fontSize: "small" })))),
106592
- imageData.generating ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: {
107282
+ !!imageData.uploading ||
107283
+ deletingDriveImageIndex === imageData.index },
107284
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_49__["default"], { fontSize: "small" })))),
107285
+ imageData.imageUrl ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_9__["default"], { size: "small", "aria-label": "\u041D\u0430 \u0432\u0435\u0441\u044C \u044D\u043A\u0440\u0430\u043D", sx: {
107286
+ position: 'absolute',
107287
+ top: 6,
107288
+ left: 6,
107289
+ zIndex: 3,
107290
+ bgcolor: 'rgba(32, 32, 32, 0.92)',
107291
+ color: '#fff',
107292
+ border: '2px solid rgba(255, 255, 255, 0.95)',
107293
+ boxShadow: '0 2px 12px rgba(0, 0, 0, 0.45)',
107294
+ width: 36,
107295
+ height: 36,
107296
+ '&:hover': {
107297
+ bgcolor: 'primary.main',
107298
+ borderColor: 'rgba(255, 255, 255, 1)',
107299
+ color: '#fff'
107300
+ },
107301
+ '&.Mui-disabled': {
107302
+ bgcolor: 'rgba(32, 32, 32, 0.45)',
107303
+ borderColor: 'rgba(255, 255, 255, 0.4)',
107304
+ color: 'rgba(255, 255, 255, 0.5)'
107305
+ }
107306
+ }, onClick: () => setCreoFullscreen({
107307
+ url: imageData.imageUrl,
107308
+ title: `${imageData.index}. ${imageData.approach}`
107309
+ }), disabled: imageData.generating ||
107310
+ !!imageData.uploading ||
107311
+ deletingDriveImageIndex === imageData.index },
107312
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_51__["default"], { fontSize: "small" }))) : null,
107313
+ imageData.generating ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: {
106593
107314
  width: '100%',
106594
107315
  aspectRatio: aspectRatioCss,
106595
107316
  border: (theme) => `2px dashed ${theme.palette.primary.main}`,
@@ -106604,12 +107325,12 @@ ${imageData.originalPrompt}
106604
107325
  gap: 2,
106605
107326
  p: 3
106606
107327
  } },
106607
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 40, sx: { color: 'primary.main' } }),
106608
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", sx: { color: 'text.secondary', textAlign: 'center', fontWeight: 'bold' } }, "\u0413\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u044F..."),
106609
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'text.secondary', textAlign: 'center' } }, imageData.approach),
106610
- generatingImages && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'text.secondary', textAlign: 'center' } },
107328
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 40, sx: { color: 'primary.main' } }),
107329
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", sx: { color: 'text.secondary', textAlign: 'center', fontWeight: 'bold' } }, "\u0413\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u044F..."),
107330
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", sx: { color: 'text.secondary', textAlign: 'center' } }, imageData.approach),
107331
+ generatingImages && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", sx: { color: 'text.secondary', textAlign: 'center' } },
106611
107332
  "\u23F1\uFE0F \u041F\u0440\u043E\u0448\u043B\u043E: ",
106612
- formatElapsedTime(elapsedTime.images))))) : imageData.regenerating && !imageData.imageUrl ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: {
107333
+ formatElapsedTime(elapsedTime.images))))) : imageData.regenerating && !imageData.imageUrl ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: {
106613
107334
  width: '100%',
106614
107335
  aspectRatio: aspectRatioCss,
106615
107336
  border: (theme) => `2px dashed ${theme.palette.primary.main}`,
@@ -106624,32 +107345,46 @@ ${imageData.originalPrompt}
106624
107345
  gap: 2,
106625
107346
  p: 3
106626
107347
  } },
106627
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 40, sx: { color: 'primary.main' } }),
106628
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", sx: { color: 'text.secondary', textAlign: 'center', fontWeight: 'bold' } }, "\u041F\u0435\u0440\u0435\u0434\u0435\u043B\u043A\u0430..."),
106629
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'text.secondary', textAlign: 'center' } }, imageData.approach))) : imageData.imageUrl ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
106630
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { component: "img", src: imageData.imageUrl, alt: `Generated Image ${imageData.index}`, sx: {
107348
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 40, sx: { color: 'primary.main' } }),
107349
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", sx: { color: 'text.secondary', textAlign: 'center', fontWeight: 'bold' } }, "\u041F\u0435\u0440\u0435\u0434\u0435\u043B\u043A\u0430..."),
107350
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", sx: { color: 'text.secondary', textAlign: 'center' } }, imageData.approach))) : imageData.imageUrl ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, hideUploadedPreview ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: {
107351
+ width: '100%',
107352
+ maxHeight: { xs: 152, sm: 168 },
107353
+ minHeight: 112,
107354
+ border: creativePreviewBorder,
107355
+ borderRadius: 1,
107356
+ backgroundColor: (theme) => (theme.palette.mode === 'dark' ? '#2a2a2a' : '#f5f5f5'),
107357
+ display: 'flex',
107358
+ flexDirection: 'column',
107359
+ alignItems: 'center',
107360
+ justifyContent: 'center',
107361
+ gap: 1,
107362
+ py: 1.25,
107363
+ px: 2,
107364
+ boxSizing: 'border-box'
107365
+ } },
107366
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", color: "text.secondary", align: "center", sx: { px: 1 } }, "\u0417\u0430\u0433\u0440\u0443\u0436\u0435\u043D\u043E \u043D\u0430 Google \u0414\u0438\u0441\u043A \u2014 \u043F\u0440\u0435\u0432\u044C\u044E \u0441\u043A\u0440\u044B\u0442\u043E"),
107367
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { direction: "row", spacing: 1, flexWrap: "wrap", justifyContent: "center" },
107368
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { size: "small", variant: "contained", onClick: () => setGeneratedImagesData(prev => prev.map(img => img.index === imageData.index
107369
+ ? { ...img, uploadedPreviewHidden: false }
107370
+ : img)) }, "\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u043F\u0440\u0435\u0432\u044C\u044E"),
107371
+ imageData.driveUploadedFileId ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { size: "small", variant: "outlined", color: "error", disabled: deletingDriveImageIndex === imageData.index, onClick: () => void handleDeleteCreativeFromDrive(imageData) }, deletingDriveImageIndex === imageData.index
107372
+ ? 'Удаление…'
107373
+ : 'Удалить с Диска')) : null,
107374
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { size: "small", variant: "outlined", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_51__["default"], null), onClick: () => setCreoFullscreen({
107375
+ url: imageData.imageUrl,
107376
+ title: `${imageData.index}. ${imageData.approach}`
107377
+ }), disabled: !!imageData.uploading || deletingDriveImageIndex === imageData.index }, "\u041D\u0430 \u0432\u0435\u0441\u044C \u044D\u043A\u0440\u0430\u043D")))) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
107378
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { component: "img", src: imageData.imageUrl, alt: `Generated Image ${imageData.index}`, sx: {
106631
107379
  width: '100%',
106632
107380
  height: 'auto',
106633
107381
  display: 'block',
106634
107382
  objectFit: 'contain',
106635
- border: (theme) => {
106636
- if (imageData.uploaded || imageData.checkStatus === 'ok') {
106637
- return `2px solid ${theme.palette.success.main}`;
106638
- }
106639
- else if (imageData.checkStatus === 'needs_rebuild') {
106640
- return `2px solid ${theme.palette.error.main}`;
106641
- }
106642
- else if (imageData.checkStatus === 'checking') {
106643
- return `2px solid ${theme.palette.warning.main}`;
106644
- }
106645
- else {
106646
- return `2px solid ${theme.palette.primary.main}`;
106647
- }
106648
- },
107383
+ border: creativePreviewBorder,
106649
107384
  borderRadius: 1,
106650
- backgroundColor: (theme) => theme.palette.mode === 'dark' ? '#2a2a2a' : '#f5f5f5'
107385
+ backgroundColor: (theme) => (theme.palette.mode === 'dark' ? '#2a2a2a' : '#f5f5f5')
106651
107386
  } }),
106652
- imageData.regenerating && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: {
107387
+ imageData.regenerating && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: {
106653
107388
  position: 'absolute',
106654
107389
  top: '50%',
106655
107390
  left: '50%',
@@ -106659,8 +107394,15 @@ ${imageData.originalPrompt}
106659
107394
  alignItems: 'center',
106660
107395
  gap: 1
106661
107396
  } },
106662
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 40, sx: { color: 'primary.main' } }),
106663
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'white', fontWeight: 'bold', textShadow: '1px 1px 2px rgba(0,0,0,0.8)' } }, "\u041F\u0435\u0440\u0435\u0434\u0435\u043B\u043A\u0430..."))))) : imageData.failed ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: {
107397
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 40, sx: { color: 'primary.main' } }),
107398
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", sx: { color: 'white', fontWeight: 'bold', textShadow: '1px 1px 2px rgba(0,0,0,0.8)' } }, "\u041F\u0435\u0440\u0435\u0434\u0435\u043B\u043A\u0430..."))),
107399
+ imageData.uploaded && !imageData.regenerating ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', justifyContent: 'center', mt: 0.5 } },
107400
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { size: "small", variant: "text", onClick: () => setGeneratedImagesData(prev => prev.map(img => img.index === imageData.index
107401
+ ? { ...img, uploadedPreviewHidden: true }
107402
+ : img)) }, "\u0421\u043A\u0440\u044B\u0442\u044C \u043F\u0440\u0435\u0432\u044C\u044E"),
107403
+ imageData.driveUploadedFileId ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { size: "small", variant: "outlined", color: "error", sx: { ml: 1 }, disabled: deletingDriveImageIndex === imageData.index, onClick: () => void handleDeleteCreativeFromDrive(imageData) }, deletingDriveImageIndex === imageData.index
107404
+ ? 'Удаление…'
107405
+ : 'Удалить с Диска')) : null)) : null)))) : imageData.failed ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: {
106664
107406
  width: '100%',
106665
107407
  aspectRatio: aspectRatioCss,
106666
107408
  border: (theme) => `2px dashed ${theme.palette.error.main}`,
@@ -106675,11 +107417,11 @@ ${imageData.originalPrompt}
106675
107417
  gap: 2,
106676
107418
  p: 3
106677
107419
  } },
106678
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h4", sx: { color: 'error.main' } }, "\u274C"),
106679
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", sx: { color: 'error.main', textAlign: 'center', fontWeight: 'bold' } }, "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0441\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C"),
106680
- imageData.errorMessage && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'error.main', textAlign: 'center', fontSize: '0.7rem' } }, imageData.errorMessage.length > 100
107420
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "h4", sx: { color: 'error.main' } }, "\u274C"),
107421
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", sx: { color: 'error.main', textAlign: 'center', fontWeight: 'bold' } }, "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0441\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C"),
107422
+ imageData.errorMessage && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", sx: { color: 'error.main', textAlign: 'center', fontSize: '0.7rem' } }, imageData.errorMessage.length > 100
106681
107423
  ? imageData.errorMessage.substring(0, 100) + '...'
106682
- : imageData.errorMessage)))) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: {
107424
+ : imageData.errorMessage)))) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: {
106683
107425
  width: '100%',
106684
107426
  aspectRatio: aspectRatioCss,
106685
107427
  border: (theme) => `2px dashed ${theme.palette.divider}`,
@@ -106694,22 +107436,22 @@ ${imageData.originalPrompt}
106694
107436
  gap: 2,
106695
107437
  p: 3
106696
107438
  } },
106697
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h4", sx: { color: 'text.secondary' } }, "\u23F3"),
106698
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", sx: { color: 'text.secondary', textAlign: 'center', fontWeight: 'bold' } }, "\u0412 \u043E\u0447\u0435\u0440\u0435\u0434\u0438"),
106699
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'text.secondary', textAlign: 'center' } }, generatingImages ? 'Ожидает генерации...' : 'Готово к генерации'),
106700
- generatingImages && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'text.secondary', textAlign: 'center' } },
107439
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "h4", sx: { color: 'text.secondary' } }, "\u23F3"),
107440
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", sx: { color: 'text.secondary', textAlign: 'center', fontWeight: 'bold' } }, "\u0412 \u043E\u0447\u0435\u0440\u0435\u0434\u0438"),
107441
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", sx: { color: 'text.secondary', textAlign: 'center' } }, generatingImages ? 'Ожидает генерации...' : 'Готово к генерации'),
107442
+ generatingImages && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", sx: { color: 'text.secondary', textAlign: 'center' } },
106701
107443
  "\u23F1\uFE0F \u041F\u0440\u043E\u0448\u043B\u043E: ",
106702
107444
  formatElapsedTime(elapsedTime.images))))),
106703
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { mt: 1, mb: 1 } },
106704
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { display: 'block', fontWeight: 'bold' } },
107445
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { mt: 1, mb: hideUploadedPreview ? 0 : 1 } },
107446
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", sx: { display: 'block', fontWeight: 'bold' } },
106705
107447
  imageData.index,
106706
107448
  ". ",
106707
107449
  imageData.approach),
106708
- imageData.checkStatus === 'ok' && imageData.checkResult !== 'Проверка отключена' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'success.main', display: 'block', fontWeight: 'bold' } }, "\u2705 \u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u0440\u043E\u0439\u0434\u0435\u043D\u0430")),
106709
- imageData.checkStatus === 'needs_rebuild' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
106710
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'error.main', display: 'block', fontWeight: 'bold' } }, imageData.checkFailed ? '⚠️ Ошибка проверки' : '❌ Требует пересборки'),
106711
- imageData.checkErrors && imageData.checkErrors.length > 0 && !imageData.checkFailed && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { mt: 0.5 } },
106712
- imageData.checkErrors.slice(0, 2).map((error, idx) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { key: idx, variant: "caption", sx: {
107450
+ !hideUploadedPreview && imageData.checkStatus === 'ok' && imageData.checkResult !== 'Проверка отключена' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", sx: { color: 'success.main', display: 'block', fontWeight: 'bold' } }, "\u2705 \u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u0440\u043E\u0439\u0434\u0435\u043D\u0430")),
107451
+ !hideUploadedPreview && imageData.checkStatus === 'needs_rebuild' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], null,
107452
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", sx: { color: 'error.main', display: 'block', fontWeight: 'bold' } }, imageData.checkFailed ? '⚠️ Ошибка проверки' : '❌ Требует пересборки'),
107453
+ imageData.checkErrors && imageData.checkErrors.length > 0 && !imageData.checkFailed && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { mt: 0.5 } },
107454
+ imageData.checkErrors.slice(0, 2).map((error, idx) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { key: idx, variant: "caption", sx: {
106713
107455
  color: 'error.main',
106714
107456
  display: 'block',
106715
107457
  fontSize: '0.7rem',
@@ -106717,16 +107459,16 @@ ${imageData.originalPrompt}
106717
107459
  } },
106718
107460
  "\u2022 ",
106719
107461
  error.length > 50 ? error.substring(0, 50) + '...' : error))),
106720
- imageData.checkErrors.length > 2 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'text.secondary', fontSize: '0.7rem' } },
107462
+ imageData.checkErrors.length > 2 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", sx: { color: 'text.secondary', fontSize: '0.7rem' } },
106721
107463
  "+",
106722
107464
  imageData.checkErrors.length - 2,
106723
107465
  " \u0435\u0449\u0451")))))),
106724
- imageData.checkStatus === 'checking' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'warning.main', display: 'block' } }, "\uD83D\uDD0D \u041F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u0442\u0441\u044F...")),
106725
- imageData.checkStatus === 'pending' && !imageData.failed && !imageData.generating && imageData.imageUrl && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'text.secondary', display: 'block' } }, "\u23F3 \u041E\u0436\u0438\u0434\u0430\u0435\u0442 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438")),
106726
- imageData.failed && !imageData.generating && !imageData.imageUrl && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'error.main', display: 'block', fontWeight: 'bold' } }, "\u274C \u0413\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u044F \u043D\u0435 \u0443\u0434\u0430\u043B\u0430\u0441\u044C")),
106727
- imageData.uploaded && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'success.main', display: 'block', mt: 0.5 } }, "\u2713 \u0417\u0430\u0433\u0440\u0443\u0436\u0435\u043D\u043E"))),
106728
- !imageData.generating && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', gap: 1, flexDirection: 'column', mt: 1 } },
106729
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { size: "small", multiline: true, minRows: 2, maxRows: 4, fullWidth: true, placeholder: "\u0423\u0442\u043E\u0447\u043D\u0435\u043D\u0438\u044F \u0434\u043B\u044F \u043F\u0435\u0440\u0435\u0434\u0435\u043B\u043A\u0438 (\u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438 \u0437\u0430\u043F\u043E\u043B\u043D\u0435\u043D\u043E \u043F\u0440\u0438 \u043F\u0440\u043E\u0431\u043B\u0435\u043C\u0430\u0445)", value: imageData.customRegeneratePrompt || '', onChange: (e) => {
107466
+ !hideUploadedPreview && imageData.checkStatus === 'checking' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", sx: { color: 'warning.main', display: 'block' } }, "\uD83D\uDD0D \u041F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u0442\u0441\u044F...")),
107467
+ !hideUploadedPreview && imageData.checkStatus === 'pending' && !imageData.failed && !imageData.generating && imageData.imageUrl && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", sx: { color: 'text.secondary', display: 'block' } }, "\u23F3 \u041E\u0436\u0438\u0434\u0430\u0435\u0442 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438")),
107468
+ !hideUploadedPreview && imageData.failed && !imageData.generating && !imageData.imageUrl && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", sx: { color: 'error.main', display: 'block', fontWeight: 'bold' } }, "\u274C \u0413\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u044F \u043D\u0435 \u0443\u0434\u0430\u043B\u0430\u0441\u044C")),
107469
+ !hideUploadedPreview && imageData.uploaded && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", sx: { color: 'success.main', display: 'block', mt: 0.5 } }, "\u2713 \u0417\u0430\u0433\u0440\u0443\u0436\u0435\u043D\u043E"))),
107470
+ !imageData.generating && !hideUploadedPreview && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', gap: 1, flexDirection: 'column', mt: 1 } },
107471
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { size: "small", multiline: true, minRows: 2, maxRows: 4, fullWidth: true, placeholder: "\u0423\u0442\u043E\u0447\u043D\u0435\u043D\u0438\u044F \u0434\u043B\u044F \u043F\u0435\u0440\u0435\u0434\u0435\u043B\u043A\u0438 (\u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438 \u0437\u0430\u043F\u043E\u043B\u043D\u0435\u043D\u043E \u043F\u0440\u0438 \u043F\u0440\u043E\u0431\u043B\u0435\u043C\u0430\u0445)", value: imageData.customRegeneratePrompt || '', onChange: (e) => {
106730
107472
  setGeneratedImagesData(prev => prev.map(img => img.index === imageData.index
106731
107473
  ? { ...img, customRegeneratePrompt: e.target.value }
106732
107474
  : img));
@@ -106743,8 +107485,8 @@ ${imageData.originalPrompt}
106743
107485
  : 'transparent'
106744
107486
  }
106745
107487
  } }),
106746
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: "row", spacing: 1, sx: { width: '100%', alignItems: 'stretch' } },
106747
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { size: "small", variant: "contained", color: imageData.failed ? 'error' : imageData.checkStatus === 'needs_rebuild' ? 'warning' : 'primary', startIcon: imageData.regenerating ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 16 }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_40__["default"], null), onClick: () => handleRegenerateImage(imageData), disabled: imageData.regenerating ||
107488
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { direction: "row", spacing: 1, sx: { width: '100%', alignItems: 'stretch' } },
107489
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { size: "small", variant: "contained", color: imageData.failed ? 'error' : imageData.checkStatus === 'needs_rebuild' ? 'warning' : 'primary', startIcon: imageData.regenerating ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 16 }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_41__["default"], null), onClick: () => handleRegenerateImage(imageData), disabled: imageData.regenerating ||
106748
107490
  imageData.uploading ||
106749
107491
  imageData.checkStatus === 'checking' ||
106750
107492
  !imageData.originalPrompt ||
@@ -106754,42 +107496,81 @@ ${imageData.originalPrompt}
106754
107496
  : ((generatingImages && !imageData.imageUrl && !imageData.failed && !imageData.generating)
106755
107497
  ? 'В очереди'
106756
107498
  : (!imageData.imageUrl ? 'Сгенерировать' : 'Переделать'))),
106757
- imageData.imageUrl ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { size: "small", variant: "outlined", color: "secondary", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_51__["default"], null), onClick: () => handleRegenerateImageFresh(imageData), disabled: imageData.regenerating ||
107499
+ imageData.imageUrl ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { size: "small", variant: "outlined", color: "secondary", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_56__["default"], null), onClick: () => handleRegenerateImageFresh(imageData), disabled: imageData.regenerating ||
106758
107500
  imageData.uploading ||
106759
107501
  imageData.checkStatus === 'checking' ||
106760
107502
  !imageData.originalPrompt ||
106761
107503
  !imageData.productImageUrl, sx: { flex: 1, minWidth: 0, py: 1, lineHeight: 1.2, whiteSpace: 'normal' } }, "\u041F\u0435\u0440\u0435\u0434\u0435\u043B\u0430\u0442\u044C \u0437\u0430\u043D\u043E\u0432\u043E")) : null),
106762
- imageData.imageUrl && !validationDisabled && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { size: "small", variant: "outlined", color: "warning", startIcon: imageData.checkStatus === 'checking' ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 16, color: "inherit" })) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_51__["default"], null)), onClick: () => handleRetryCheck(imageData), disabled: imageData.regenerating ||
107504
+ imageData.imageUrl && !validationDisabled && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { size: "small", variant: "outlined", color: "warning", startIcon: imageData.checkStatus === 'checking' ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 16, color: "inherit" })) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_56__["default"], null)), onClick: () => handleRetryCheck(imageData), disabled: imageData.regenerating ||
106763
107505
  imageData.uploading ||
106764
107506
  imageData.generating ||
106765
107507
  imageData.checkStatus === 'checking', fullWidth: true }, imageData.checkStatus === 'checking' ? 'Проверка…' : 'Перепроверить')),
106766
- !imageData.uploaded && imageData.imageUrl && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { size: "small", variant: "outlined", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_44__["default"], null), onClick: () => {
107508
+ !imageData.uploaded && imageData.imageUrl && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { size: "small", variant: "outlined", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_46__["default"], null), onClick: () => {
106767
107509
  const folderId = extractFolderId(driveFolderUrl);
106768
107510
  if (folderId) {
106769
107511
  handleUploadImage(imageData, folderId);
106770
107512
  }
106771
107513
  }, disabled: imageData.uploading || imageData.regenerating || !driveFolderUrl.trim(), fullWidth: true }, imageData.uploading ? 'Загрузка...' : 'Загрузить'))))));
106772
107514
  }))),
106773
- generatedImagesData.length > 0 && generatedImagesData.some(img => !img.uploaded && img.imageUrl) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { mt: 2 } },
106774
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { variant: "contained", color: "primary", onClick: handleUploadAllImages, disabled: uploadingImages || generatingImages || !driveFolderUrl.trim(), startIcon: uploadingImages ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 20 }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_44__["default"], null), fullWidth: true }, uploadingImages ? 'Загрузка...' : `Загрузить все (${generatedImagesData.filter(img => !img.uploaded && img.imageUrl).length})`))))),
106775
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { sx: { my: 2 } }),
106776
- (generatedTitlesData.length > 0 || generatedTextsData.length > 0 || loadingContentFromDrive) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { mb: 2 } },
106777
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 1, mb: 1 } },
106778
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h6", sx: { fontWeight: 'bold' }, component: "div" },
107515
+ generatedImagesData.length > 0 && generatedImagesData.some(img => !img.uploaded && img.imageUrl) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { mt: 2 } },
107516
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { variant: "contained", color: "primary", onClick: handleUploadAllImages, disabled: uploadingImages || generatingImages || !driveFolderUrl.trim(), startIcon: uploadingImages ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 20 }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_46__["default"], null), fullWidth: true }, uploadingImages ? 'Загрузка...' : `Загрузить все (${generatedImagesData.filter(img => !img.uploaded && img.imageUrl).length})`))),
107517
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_32__["default"], { fullScreen: true, open: Boolean(creoFullscreen), onClose: () => setCreoFullscreen(null), PaperProps: {
107518
+ sx: {
107519
+ m: 0,
107520
+ bgcolor: '#0d0d0d',
107521
+ display: 'flex',
107522
+ flexDirection: 'column',
107523
+ backgroundImage: 'none'
107524
+ }
107525
+ } },
107526
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: {
107527
+ display: 'flex',
107528
+ alignItems: 'center',
107529
+ justifyContent: 'space-between',
107530
+ gap: 1,
107531
+ px: 1.5,
107532
+ py: 1,
107533
+ borderBottom: '1px solid rgba(255,255,255,0.12)',
107534
+ flexShrink: 0
107535
+ } },
107536
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "subtitle1", sx: { color: '#fff', flex: 1, pr: 1 }, noWrap: true }, creoFullscreen?.title),
107537
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { title: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C (Esc)" },
107538
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_9__["default"], { "aria-label": "\u0417\u0430\u043A\u0440\u044B\u0442\u044C \u043F\u043E\u043B\u043D\u043E\u044D\u043A\u0440\u0430\u043D\u043D\u044B\u0439 \u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440", onClick: () => setCreoFullscreen(null), sx: { color: '#fff' } },
107539
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_45__["default"], null)))),
107540
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: {
107541
+ flex: 1,
107542
+ minHeight: 0,
107543
+ display: 'flex',
107544
+ alignItems: 'center',
107545
+ justifyContent: 'center',
107546
+ p: { xs: 1, sm: 2 },
107547
+ overflow: 'auto'
107548
+ } }, creoFullscreen ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { component: "img", src: creoFullscreen.url, alt: "", sx: {
107549
+ maxWidth: 'min(100%, 100vw - 16px)',
107550
+ maxHeight: 'min(92dvh, calc(100dvh - 72px))',
107551
+ width: 'auto',
107552
+ height: 'auto',
107553
+ objectFit: 'contain',
107554
+ borderRadius: 1
107555
+ } })) : null)))),
107556
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { sx: { my: 2 } }),
107557
+ (generatedTitlesData.length > 0 || generatedTextsData.length > 0 || loadingContentFromDrive) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { mb: 2 } },
107558
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 1, mb: 1 } },
107559
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "h6", sx: { fontWeight: 'bold' }, component: "div" },
106779
107560
  "\u0421\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u043F\u0430\u0440\u044B (",
106780
107561
  Math.min(generatedTitlesData.filter(t => t.title).length, generatedTextsData.filter(t => t.text).length),
106781
107562
  "/",
106782
107563
  Math.max(generatedTitlesData.length, generatedTextsData.length),
106783
107564
  ")"),
106784
- (generatedTitlesData.length > 0 || generatedTextsData.length > 0) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { title: pairsJsonCopied ? 'Скопировано' : 'Скопировать все пары как JSON-массив' },
106785
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_8__["default"], { size: "small", "aria-label": "\u0421\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043F\u0430\u0440\u044B \u043A\u0430\u043A JSON", onClick: () => void copyGeneratedPairsAsJson(), disabled: generating, sx: {
107565
+ (generatedTitlesData.length > 0 || generatedTextsData.length > 0) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { title: pairsJsonCopied ? 'Скопировано' : 'Скопировать все пары как JSON-массив' },
107566
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_9__["default"], { size: "small", "aria-label": "\u0421\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043F\u0430\u0440\u044B \u043A\u0430\u043A JSON", onClick: () => void copyGeneratedPairsAsJson(), disabled: generating, sx: {
106786
107567
  opacity: 0.28,
106787
107568
  p: 0.35,
106788
107569
  color: 'text.secondary',
106789
107570
  '&:hover': { opacity: 0.75, color: 'text.primary' },
106790
107571
  } },
106791
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_45__["default"], { sx: { fontSize: 16 } }))))),
106792
- loadingContentFromDrive && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: {
107572
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_47__["default"], { sx: { fontSize: 16 } }))))),
107573
+ loadingContentFromDrive && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: {
106793
107574
  display: 'flex',
106794
107575
  flexDirection: 'column',
106795
107576
  alignItems: 'center',
@@ -106803,141 +107584,143 @@ ${imageData.originalPrompt}
106803
107584
  : 'rgba(25, 118, 210, 0.05)',
106804
107585
  mb: 2
106805
107586
  } },
106806
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 40, sx: { color: 'primary.main' } }),
106807
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body1", sx: { color: 'text.secondary', fontWeight: 'bold' } }, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u043A\u043E\u043D\u0442\u0435\u043D\u0442\u0430 \u0438\u0437 Google Drive..."))),
106808
- (generatedTitlesData.length > 0 || generatedTextsData.length > 0) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { spacing: 2 }, Array.from({
106809
- length: Math.max(generatedTitlesData.length, generatedTextsData.length)
106810
- }, (_, i) => i).map((i) => {
106811
- const titleData = generatedTitlesData[i];
106812
- const textData = generatedTextsData[i];
106813
- const approachPoolIdx = lastUsedApproachIndices[i] ?? i;
106814
- const pairApproach = _prompts__WEBPACK_IMPORTED_MODULE_1__.PAIR_APPROACH_POOL[approachPoolIdx];
106815
- const pairLabel = pairApproach ? pairApproach.name : `пара ${i + 1}`;
106816
- const pairGenerating = (titleData?.generating || textData?.generating);
106817
- const pairFailed = (!pairGenerating && titleData?.failed && textData?.failed);
106818
- return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_31__["default"], { key: i, variant: "outlined", sx: {
106819
- p: 2,
106820
- borderColor: pairFailed
106821
- ? 'error.main'
106822
- : pairGenerating
106823
- ? 'primary.main'
106824
- : 'divider',
106825
- borderStyle: pairGenerating ? 'dashed' : 'solid',
106826
- backgroundColor: (theme) => pairGenerating
106827
- ? (theme.palette.mode === 'dark' ? 'rgba(25,118,210,0.05)' : 'rgba(25,118,210,0.02)')
106828
- : 'transparent',
106829
- } },
106830
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: "row", spacing: 1, alignItems: "center", sx: { mb: 1.5, flexWrap: 'wrap' } },
106831
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: {
106832
- fontWeight: 700,
106833
- px: 1,
106834
- py: 0.25,
106835
- borderRadius: 1,
106836
- backgroundColor: (theme) => theme.palette.mode === 'dark'
106837
- ? 'rgba(255,255,255,0.08)'
106838
- : 'rgba(0,0,0,0.06)',
106839
- color: 'text.secondary',
106840
- textTransform: 'uppercase',
106841
- letterSpacing: 0.5,
106842
- } },
106843
- "\u041F\u0430\u0440\u0430 ",
106844
- i + 1),
106845
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", color: "text.secondary" }, pairLabel),
106846
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { flexGrow: 1, minWidth: 8 } }),
106847
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { title: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u043F\u0430\u0440\u0443" },
106848
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null,
106849
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_8__["default"], { size: "small", color: "error", "aria-label": "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u043F\u0430\u0440\u0443", onClick: () => handleDeleteGeneratedPair(i), disabled: pairGenerating || generating },
106850
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_46__["default"], { fontSize: "small" })))),
106851
- translatingPairs && !pairTranslations[i] ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 13, sx: { color: 'text.disabled', ml: 0.5 } })) : null),
106852
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { spacing: 1.5 },
106853
- titleData && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
106854
- titleData.generating ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
106855
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 16, sx: { color: 'primary.main' } }),
106856
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", color: "text.secondary" }, "\u0413\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u044F \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0430\u2026"))) : titleData.failed ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: `Заголовок ${i + 1}`, variant: "outlined", fullWidth: true, size: "small", value: titleData.title, placeholder: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043E\u043A \u0432\u0440\u0443\u0447\u043D\u0443\u044E", helperText: titleData.errorMessage ? `Не сгенерирован: ${titleData.errorMessage}` : 'Можно ввести или исправить заголовок вручную', error: !!titleData.errorMessage, onChange: (e) => {
106857
- const newValue = e.target.value;
106858
- setGeneratedTitlesData(prev => {
106859
- const updated = prev.map(t => t.index === titleData.index
106860
- ? { ...t, title: newValue, failed: false, errorMessage: undefined }
106861
- : t);
106862
- setTitles(updated.map(t => t.title).filter(Boolean).join('\n'));
106863
- return updated;
106864
- });
106865
- }, sx: {
106866
- '& .MuiInputBase-root': {
106867
- backgroundColor: (theme) => theme.palette.mode === 'dark'
106868
- ? 'rgba(244,67,54,0.08)'
106869
- : 'rgba(244,67,54,0.04)'
106870
- }
106871
- } })) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: `Заголовок ${i + 1}`, variant: "outlined", fullWidth: true, size: "small", value: titleData.title, onChange: (e) => {
106872
- const newValue = e.target.value;
106873
- setGeneratedTitlesData(prev => {
106874
- const updated = prev.map(t => t.index === titleData.index ? { ...t, title: newValue } : t);
106875
- setTitles(updated.map(t => t.title).filter(Boolean).join('\n'));
106876
- return updated;
106877
- });
106878
- }, sx: {
106879
- '& .MuiInputBase-root': {
106880
- backgroundColor: (theme) => theme.palette.mode === 'dark'
106881
- ? 'rgba(76,175,80,0.08)'
106882
- : 'rgba(76,175,80,0.04)'
106883
- }
106884
- } })),
106885
- !titleData.generating && pairTranslations[i]?.titleRu?.trim() ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { component: "div", variant: "caption", sx: {
106886
- mt: -0.5,
106887
- pl: 1.5,
106888
- pr: 1,
106889
- fontSize: '0.7rem',
106890
- lineHeight: 1.45,
106891
- color: 'text.secondary',
106892
- opacity: 0.92,
106893
- } }, pairTranslations[i].titleRu)) : null)),
106894
- textData && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
106895
- textData.generating ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
106896
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 16, sx: { color: 'primary.main' } }),
106897
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", color: "text.secondary" }, "\u0413\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u044F \u0442\u0435\u043A\u0441\u0442\u0430\u2026"))) : textData.failed ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: `Текст ${i + 1}`, variant: "outlined", fullWidth: true, multiline: true, minRows: 3, value: textData.text, placeholder: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0442\u0435\u043A\u0441\u0442 \u043E\u0431\u044A\u044F\u0432\u043B\u0435\u043D\u0438\u044F \u0432\u0440\u0443\u0447\u043D\u0443\u044E", helperText: textData.errorMessage ? `Не сгенерирован: ${textData.errorMessage}` : 'Можно ввести или исправить текст вручную', error: !!textData.errorMessage, onChange: (e) => {
106898
- const newValue = e.target.value;
106899
- setGeneratedTextsData(prev => {
106900
- const updated = prev.map(t => t.index === textData.index
106901
- ? { ...t, text: newValue, failed: false, errorMessage: undefined }
106902
- : t);
106903
- setTexts(updated.map(t => t.text));
106904
- return updated;
106905
- });
106906
- }, sx: {
106907
- '& .MuiInputBase-root': {
106908
- backgroundColor: (theme) => theme.palette.mode === 'dark'
106909
- ? 'rgba(244,67,54,0.08)'
106910
- : 'rgba(244,67,54,0.04)'
106911
- }
106912
- } })) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: `Текст ${i + 1}`, variant: "outlined", fullWidth: true, multiline: true, minRows: 3, value: textData.text, onChange: (e) => {
106913
- const newValue = e.target.value;
106914
- setGeneratedTextsData(prev => {
106915
- const updated = prev.map(t => t.index === textData.index ? { ...t, text: newValue } : t);
106916
- setTexts(updated.map(t => t.text).filter(Boolean));
106917
- return updated;
106918
- });
106919
- }, sx: {
106920
- '& .MuiInputBase-root': {
106921
- backgroundColor: (theme) => theme.palette.mode === 'dark'
106922
- ? 'rgba(76,175,80,0.08)'
106923
- : 'rgba(76,175,80,0.04)'
106924
- }
106925
- } })),
106926
- !textData.generating && pairTranslations[i]?.textRu?.trim() ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { component: "div", variant: "caption", sx: {
106927
- mt: -0.25,
106928
- pl: 1.5,
106929
- pr: 1,
106930
- fontSize: '0.7rem',
106931
- lineHeight: 1.45,
107587
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 40, sx: { color: 'primary.main' } }),
107588
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body1", sx: { color: 'text.secondary', fontWeight: 'bold' } }, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u043A\u043E\u043D\u0442\u0435\u043D\u0442\u0430 \u0438\u0437 Google Drive..."))),
107589
+ (generatedTitlesData.length > 0 || generatedTextsData.length > 0) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { spacing: 2 },
107590
+ pairTranslationFailed && !translatingPairs && !generating ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { severity: "warning", action: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { color: "inherit", size: "small", onClick: handleRetryPairTranslation, sx: { textTransform: 'none', whiteSpace: 'nowrap' } }, "\u0417\u0430\u043F\u0440\u043E\u0441\u0438\u0442\u044C \u043F\u0435\u0440\u0435\u0432\u043E\u0434") }, "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u043F\u0435\u0440\u0435\u0432\u043E\u0434 \u043F\u0430\u0440 \u043D\u0430 \u0440\u0443\u0441\u0441\u043A\u0438\u0439 (\u043E\u0442\u0434\u0435\u043B\u044C\u043D\u044B\u0439 \u0437\u0430\u043F\u0440\u043E\u0441 \u043A \u043C\u043E\u0434\u0435\u043B\u0438).")) : null,
107591
+ Array.from({
107592
+ length: Math.max(generatedTitlesData.length, generatedTextsData.length)
107593
+ }, (_, i) => i).map((i) => {
107594
+ const titleData = generatedTitlesData[i];
107595
+ const textData = generatedTextsData[i];
107596
+ const approachPoolIdx = lastUsedApproachIndices[i] ?? i;
107597
+ const pairApproach = _prompts__WEBPACK_IMPORTED_MODULE_2__.PAIR_APPROACH_POOL[approachPoolIdx];
107598
+ const pairLabel = pairApproach ? pairApproach.name : `пара ${i + 1}`;
107599
+ const pairGenerating = (titleData?.generating || textData?.generating);
107600
+ const pairFailed = (!pairGenerating && titleData?.failed && textData?.failed);
107601
+ return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_33__["default"], { key: i, variant: "outlined", sx: {
107602
+ p: 2,
107603
+ borderColor: pairFailed
107604
+ ? 'error.main'
107605
+ : pairGenerating
107606
+ ? 'primary.main'
107607
+ : 'divider',
107608
+ borderStyle: pairGenerating ? 'dashed' : 'solid',
107609
+ backgroundColor: (theme) => pairGenerating
107610
+ ? (theme.palette.mode === 'dark' ? 'rgba(25,118,210,0.05)' : 'rgba(25,118,210,0.02)')
107611
+ : 'transparent',
107612
+ } },
107613
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { direction: "row", spacing: 1, alignItems: "center", sx: { mb: 1.5, flexWrap: 'wrap' } },
107614
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", sx: {
107615
+ fontWeight: 700,
107616
+ px: 1,
107617
+ py: 0.25,
107618
+ borderRadius: 1,
107619
+ backgroundColor: (theme) => theme.palette.mode === 'dark'
107620
+ ? 'rgba(255,255,255,0.08)'
107621
+ : 'rgba(0,0,0,0.06)',
106932
107622
  color: 'text.secondary',
106933
- opacity: 0.92,
106934
- whiteSpace: 'pre-wrap',
106935
- wordBreak: 'break-word',
106936
- } }, pairTranslations[i].textRu)) : null)))));
106937
- }))))),
106938
- (landingGenerationLogs.length > 0 || generatingLanding) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { mb: 2 } },
106939
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h6", gutterBottom: true, sx: { fontWeight: 'bold' } }, "\u041F\u0440\u043E\u0446\u0435\u0441\u0441 \u0441\u043E\u0437\u0434\u0430\u043D\u0438\u044F \u043B\u0435\u043D\u0434\u0438\u043D\u0433\u0430"),
106940
- landingGenerationLogs.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_31__["default"], { sx: {
107623
+ textTransform: 'uppercase',
107624
+ letterSpacing: 0.5,
107625
+ } },
107626
+ "\u041F\u0430\u0440\u0430 ",
107627
+ i + 1),
107628
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", color: "text.secondary" }, pairLabel),
107629
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { flexGrow: 1, minWidth: 8 } }),
107630
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { title: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u043F\u0430\u0440\u0443" },
107631
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null,
107632
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_9__["default"], { size: "small", color: "error", "aria-label": "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u043F\u0430\u0440\u0443", onClick: () => handleDeleteGeneratedPair(i), disabled: pairGenerating || generating },
107633
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_49__["default"], { fontSize: "small" })))),
107634
+ translatingPairs && !pairTranslations[i] ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 13, sx: { color: 'text.disabled', ml: 0.5 } })) : null),
107635
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { spacing: 1.5 },
107636
+ titleData && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
107637
+ titleData.generating ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
107638
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 16, sx: { color: 'primary.main' } }),
107639
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", color: "text.secondary" }, "\u0413\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u044F \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0430\u2026"))) : titleData.failed ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { label: `Заголовок ${i + 1}`, variant: "outlined", fullWidth: true, size: "small", value: titleData.title, placeholder: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043E\u043A \u0432\u0440\u0443\u0447\u043D\u0443\u044E", helperText: titleData.errorMessage ? `Не сгенерирован: ${titleData.errorMessage}` : 'Можно ввести или исправить заголовок вручную', error: !!titleData.errorMessage, onChange: (e) => {
107640
+ const newValue = e.target.value;
107641
+ setGeneratedTitlesData(prev => {
107642
+ const updated = prev.map(t => t.index === titleData.index
107643
+ ? { ...t, title: newValue, failed: false, errorMessage: undefined }
107644
+ : t);
107645
+ setTitles(updated.map(t => t.title).filter(Boolean).join('\n'));
107646
+ return updated;
107647
+ });
107648
+ }, sx: {
107649
+ '& .MuiInputBase-root': {
107650
+ backgroundColor: (theme) => theme.palette.mode === 'dark'
107651
+ ? 'rgba(244,67,54,0.08)'
107652
+ : 'rgba(244,67,54,0.04)'
107653
+ }
107654
+ } })) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { label: `Заголовок ${i + 1}`, variant: "outlined", fullWidth: true, size: "small", value: titleData.title, onChange: (e) => {
107655
+ const newValue = e.target.value;
107656
+ setGeneratedTitlesData(prev => {
107657
+ const updated = prev.map(t => t.index === titleData.index ? { ...t, title: newValue } : t);
107658
+ setTitles(updated.map(t => t.title).filter(Boolean).join('\n'));
107659
+ return updated;
107660
+ });
107661
+ }, sx: {
107662
+ '& .MuiInputBase-root': {
107663
+ backgroundColor: (theme) => theme.palette.mode === 'dark'
107664
+ ? 'rgba(76,175,80,0.08)'
107665
+ : 'rgba(76,175,80,0.04)'
107666
+ }
107667
+ } })),
107668
+ !titleData.generating && pairTranslations[i]?.titleRu?.trim() ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { component: "div", variant: "caption", sx: {
107669
+ mt: -0.5,
107670
+ pl: 1.5,
107671
+ pr: 1,
107672
+ fontSize: '0.7rem',
107673
+ lineHeight: 1.45,
107674
+ color: 'text.secondary',
107675
+ opacity: 0.92,
107676
+ } }, pairTranslations[i].titleRu)) : null)),
107677
+ textData && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
107678
+ textData.generating ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
107679
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 16, sx: { color: 'primary.main' } }),
107680
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", color: "text.secondary" }, "\u0413\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u044F \u0442\u0435\u043A\u0441\u0442\u0430\u2026"))) : textData.failed ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { label: `Текст ${i + 1}`, variant: "outlined", fullWidth: true, multiline: true, minRows: 3, value: textData.text, placeholder: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0442\u0435\u043A\u0441\u0442 \u043E\u0431\u044A\u044F\u0432\u043B\u0435\u043D\u0438\u044F \u0432\u0440\u0443\u0447\u043D\u0443\u044E", helperText: textData.errorMessage ? `Не сгенерирован: ${textData.errorMessage}` : 'Можно ввести или исправить текст вручную', error: !!textData.errorMessage, onChange: (e) => {
107681
+ const newValue = e.target.value;
107682
+ setGeneratedTextsData(prev => {
107683
+ const updated = prev.map(t => t.index === textData.index
107684
+ ? { ...t, text: newValue, failed: false, errorMessage: undefined }
107685
+ : t);
107686
+ setTexts(updated.map(t => t.text));
107687
+ return updated;
107688
+ });
107689
+ }, sx: {
107690
+ '& .MuiInputBase-root': {
107691
+ backgroundColor: (theme) => theme.palette.mode === 'dark'
107692
+ ? 'rgba(244,67,54,0.08)'
107693
+ : 'rgba(244,67,54,0.04)'
107694
+ }
107695
+ } })) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { label: `Текст ${i + 1}`, variant: "outlined", fullWidth: true, multiline: true, minRows: 3, value: textData.text, onChange: (e) => {
107696
+ const newValue = e.target.value;
107697
+ setGeneratedTextsData(prev => {
107698
+ const updated = prev.map(t => t.index === textData.index ? { ...t, text: newValue } : t);
107699
+ setTexts(updated.map(t => t.text).filter(Boolean));
107700
+ return updated;
107701
+ });
107702
+ }, sx: {
107703
+ '& .MuiInputBase-root': {
107704
+ backgroundColor: (theme) => theme.palette.mode === 'dark'
107705
+ ? 'rgba(76,175,80,0.08)'
107706
+ : 'rgba(76,175,80,0.04)'
107707
+ }
107708
+ } })),
107709
+ !textData.generating && pairTranslations[i]?.textRu?.trim() ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { component: "div", variant: "caption", sx: {
107710
+ mt: -0.25,
107711
+ pl: 1.5,
107712
+ pr: 1,
107713
+ fontSize: '0.7rem',
107714
+ lineHeight: 1.45,
107715
+ color: 'text.secondary',
107716
+ opacity: 0.92,
107717
+ whiteSpace: 'pre-wrap',
107718
+ wordBreak: 'break-word',
107719
+ } }, pairTranslations[i].textRu)) : null)))));
107720
+ }))))),
107721
+ (landingGenerationLogs.length > 0 || generatingLanding) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { mb: 2 } },
107722
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "h6", gutterBottom: true, sx: { fontWeight: 'bold' } }, "\u041F\u0440\u043E\u0446\u0435\u0441\u0441 \u0441\u043E\u0437\u0434\u0430\u043D\u0438\u044F \u043B\u0435\u043D\u0434\u0438\u043D\u0433\u0430"),
107723
+ landingGenerationLogs.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_33__["default"], { sx: {
106941
107724
  p: 2,
106942
107725
  backgroundColor: (theme) => theme.palette.mode === 'dark'
106943
107726
  ? 'rgba(25, 118, 210, 0.1)'
@@ -106948,51 +107731,51 @@ ${imageData.originalPrompt}
106948
107731
  overflowY: 'auto',
106949
107732
  mb: 2
106950
107733
  } },
106951
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: {
107734
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: {
106952
107735
  fontFamily: 'monospace',
106953
107736
  fontSize: '0.875rem',
106954
107737
  lineHeight: 1.6,
106955
107738
  whiteSpace: 'pre-wrap',
106956
107739
  wordBreak: 'break-word'
106957
- } }, landingGenerationLogs.map((log, idx) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { key: idx, sx: {
107740
+ } }, landingGenerationLogs.map((log, idx) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { key: idx, sx: {
106958
107741
  mb: 0.5,
106959
107742
  color: log.includes('❌') || log.toLowerCase().includes('error') ? 'error.main' :
106960
107743
  log.includes('⚠️') || log.toLowerCase().includes('warn') ? 'warning.main' :
106961
107744
  'text.primary'
106962
107745
  } }, log)))))),
106963
- generatingLanding && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1, mb: 2 } },
106964
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 20 }),
106965
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", sx: { color: 'text.secondary' } }, formatElapsedTime(elapsedTime.landing)))),
106966
- !generatingLanding && generatedLandingHTML && generatedLandingImageBlob && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { variant: "outlined", color: "primary", onClick: handlePreviewLanding, startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_53__["default"], null), fullWidth: true }, "\u041F\u0440\u0435\u0434\u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440 \u043B\u0435\u043D\u0434\u0438\u043D\u0433\u0430"))))))))),
107746
+ generatingLanding && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1, mb: 2 } },
107747
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 20 }),
107748
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", sx: { color: 'text.secondary' } }, formatElapsedTime(elapsedTime.landing)))),
107749
+ !generatingLanding && generatedLandingHTML && generatedLandingImageBlob && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { variant: "outlined", color: "primary", onClick: handlePreviewLanding, startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_58__["default"], null), fullWidth: true }, "\u041F\u0440\u0435\u0434\u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440 \u043B\u0435\u043D\u0434\u0438\u043D\u0433\u0430"))))))))),
106967
107750
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_32__["default"], { open: approachLoadChoice !== null, onClose: handleApproachLoadKeepCurrent, maxWidth: "md", fullWidth: true }, approachLoadChoice ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
106968
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_33__["default"], null, "\u041F\u043E\u0434\u0445\u043E\u0434\u044B \u0438\u0437 \u0444\u0430\u0439\u043B\u0430 \u043E\u0444\u0444\u0435\u0440\u0430"),
106969
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_34__["default"], null,
106970
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", color: "text.secondary", sx: { mb: 2 } }, "\u0412 project-settings.json \u0435\u0441\u0442\u044C \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D\u043D\u044B\u0435 \u043F\u043E\u0434\u0445\u043E\u0434\u044B \u0434\u043B\u044F \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u043E\u0432/\u0442\u0435\u043A\u0441\u0442\u043E\u0432 \u0438 \u0434\u043B\u044F \u043A\u0440\u0435\u043E. \u041F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u044C \u0438\u0445 \u0432 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0438 \u0438\u043B\u0438 \u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0442\u0435, \u0447\u0442\u043E \u0443\u0436\u0435 \u0432\u044B\u0431\u0440\u0430\u043D\u044B \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E?"),
106971
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { spacing: 2 },
106972
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "subtitle2" }, "\u0417\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0438 \u0438 \u0442\u0435\u043A\u0441\u0442\u044B (\u043F\u0430\u0440\u044B \u043F\u043E\u0434\u0445\u043E\u0434\u043E\u0432)"),
106973
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: { xs: 'column', sm: 'row' }, spacing: 2 },
106974
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_31__["default"], { variant: "outlined", sx: { p: 1.5, flex: 1 } },
106975
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", color: "text.secondary", display: "block", gutterBottom: true }, "\u0421\u0435\u0439\u0447\u0430\u0441 \u0432 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0438"),
106976
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", component: "div", sx: { whiteSpace: 'pre-wrap', fontFamily: 'inherit' } }, formatPairApproachesForDisplay(approachLoadChoice.currentPairs))),
106977
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_31__["default"], { variant: "outlined", sx: { p: 1.5, flex: 1 } },
106978
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", color: "text.secondary", display: "block", gutterBottom: true }, "\u0412 \u0444\u0430\u0439\u043B\u0435 \u043E\u0444\u0444\u0435\u0440\u0430"),
106979
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", component: "div", sx: { whiteSpace: 'pre-wrap', fontFamily: 'inherit' } }, approachLoadChoice.savedPairs
107751
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_34__["default"], null, "\u041F\u043E\u0434\u0445\u043E\u0434\u044B \u0438\u0437 \u0444\u0430\u0439\u043B\u0430 \u043E\u0444\u0444\u0435\u0440\u0430"),
107752
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_35__["default"], null,
107753
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", color: "text.secondary", sx: { mb: 2 } }, "\u0412 project-settings.json \u0435\u0441\u0442\u044C \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D\u043D\u044B\u0435 \u043F\u043E\u0434\u0445\u043E\u0434\u044B \u0434\u043B\u044F \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u043E\u0432/\u0442\u0435\u043A\u0441\u0442\u043E\u0432 \u0438 \u0434\u043B\u044F \u043A\u0440\u0435\u043E. \u041F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u044C \u0438\u0445 \u0432 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0438 \u0438\u043B\u0438 \u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0442\u0435, \u0447\u0442\u043E \u0443\u0436\u0435 \u0432\u044B\u0431\u0440\u0430\u043D\u044B \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E?"),
107754
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { spacing: 2 },
107755
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "subtitle2" }, "\u0417\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0438 \u0438 \u0442\u0435\u043A\u0441\u0442\u044B (\u043F\u0430\u0440\u044B \u043F\u043E\u0434\u0445\u043E\u0434\u043E\u0432)"),
107756
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { direction: { xs: 'column', sm: 'row' }, spacing: 2 },
107757
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_33__["default"], { variant: "outlined", sx: { p: 1.5, flex: 1 } },
107758
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", color: "text.secondary", display: "block", gutterBottom: true }, "\u0421\u0435\u0439\u0447\u0430\u0441 \u0432 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0438"),
107759
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", component: "div", sx: { whiteSpace: 'pre-wrap', fontFamily: 'inherit' } }, formatPairApproachesForDisplay(approachLoadChoice.currentPairs))),
107760
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_33__["default"], { variant: "outlined", sx: { p: 1.5, flex: 1 } },
107761
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", color: "text.secondary", display: "block", gutterBottom: true }, "\u0412 \u0444\u0430\u0439\u043B\u0435 \u043E\u0444\u0444\u0435\u0440\u0430"),
107762
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", component: "div", sx: { whiteSpace: 'pre-wrap', fontFamily: 'inherit' } }, approachLoadChoice.savedPairs
106980
107763
  ? formatPairApproachesForDisplay(approachLoadChoice.savedPairs)
106981
107764
  : '— не указано (список для текстов не изменится)'))),
106982
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "subtitle2" }, "\u0418\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u044F (\u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u043F\u043E \u043F\u043E\u0434\u0445\u043E\u0434\u0443)"),
106983
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: { xs: 'column', sm: 'row' }, spacing: 2 },
106984
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_31__["default"], { variant: "outlined", sx: { p: 1.5, flex: 1 } },
106985
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", color: "text.secondary", display: "block", gutterBottom: true }, "\u0421\u0435\u0439\u0447\u0430\u0441 \u0432 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0438"),
106986
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", component: "div", sx: { whiteSpace: 'pre-wrap', fontFamily: 'inherit' } }, formatImageCountsForDisplay(approachLoadChoice.currentCounts))),
106987
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_31__["default"], { variant: "outlined", sx: { p: 1.5, flex: 1 } },
106988
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", color: "text.secondary", display: "block", gutterBottom: true }, "\u0412 \u0444\u0430\u0439\u043B\u0435 \u043E\u0444\u0444\u0435\u0440\u0430"),
106989
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", component: "div", sx: { whiteSpace: 'pre-wrap', fontFamily: 'inherit' } }, approachLoadChoice.savedCounts
107765
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "subtitle2" }, "\u0418\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u044F (\u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u043F\u043E \u043F\u043E\u0434\u0445\u043E\u0434\u0443)"),
107766
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { direction: { xs: 'column', sm: 'row' }, spacing: 2 },
107767
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_33__["default"], { variant: "outlined", sx: { p: 1.5, flex: 1 } },
107768
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", color: "text.secondary", display: "block", gutterBottom: true }, "\u0421\u0435\u0439\u0447\u0430\u0441 \u0432 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0438"),
107769
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", component: "div", sx: { whiteSpace: 'pre-wrap', fontFamily: 'inherit' } }, formatImageCountsForDisplay(approachLoadChoice.currentCounts))),
107770
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_33__["default"], { variant: "outlined", sx: { p: 1.5, flex: 1 } },
107771
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", color: "text.secondary", display: "block", gutterBottom: true }, "\u0412 \u0444\u0430\u0439\u043B\u0435 \u043E\u0444\u0444\u0435\u0440\u0430"),
107772
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", component: "div", sx: { whiteSpace: 'pre-wrap', fontFamily: 'inherit' } }, approachLoadChoice.savedCounts
106990
107773
  ? formatImageCountsForDisplay(approachLoadChoice.savedCounts)
106991
107774
  : '— не указано (количества крео не изменятся)'))))),
106992
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_35__["default"], null,
106993
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { onClick: handleApproachLoadKeepCurrent }, "\u041E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u043A\u0430\u043A \u0441\u0435\u0439\u0447\u0430\u0441"),
106994
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { variant: "contained", onClick: handleApproachLoadApplyFromFile }, "\u041F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u044C \u0438\u0437 \u0444\u0430\u0439\u043B\u0430")))) : null),
106995
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_PromptManagerDialog__WEBPACK_IMPORTED_MODULE_54__["default"], { open: promptManagerOpen, onClose: () => setPromptManagerOpen(false) }))));
107775
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_36__["default"], null,
107776
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { onClick: handleApproachLoadKeepCurrent }, "\u041E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u043A\u0430\u043A \u0441\u0435\u0439\u0447\u0430\u0441"),
107777
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { variant: "contained", onClick: handleApproachLoadApplyFromFile }, "\u041F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u044C \u0438\u0437 \u0444\u0430\u0439\u043B\u0430")))) : null),
107778
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_PromptManagerDialog__WEBPACK_IMPORTED_MODULE_59__["default"], { open: promptManagerOpen, onClose: () => setPromptManagerOpen(false) }))));
106996
107779
  }
106997
107780
  /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (App);
106998
107781
 
@@ -107459,7 +108242,7 @@ function PromptManagerDialog({ open, onClose }) {
107459
108242
  const [viewModes, setViewModes] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)({});
107460
108243
  // selectedApproaches: array of indices from PAIR_APPROACH_POOL, ordered
107461
108244
  const [selectedApproaches, setSelectedApproaches] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)([0, 1, 2]);
107462
- // imageApproachCounts: по одному на каждый подход (CREO_APPROACHES), каждый 0–4
108245
+ // imageApproachCounts: по одному на каждый подход (CREO_APPROACHES), каждый 0–MAX_IMAGES_PER_CREO_APPROACH
107463
108246
  const [imageApproachCounts, setImageApproachCounts] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(() => Array(_prompts__WEBPACK_IMPORTED_MODULE_36__.CREO_APPROACHES.length).fill(1));
107464
108247
  const [creoToastOpen, setCreoToastOpen] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
107465
108248
  const [creoToastMessage, setCreoToastMessage] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
@@ -107508,7 +108291,7 @@ function PromptManagerDialog({ open, onClose }) {
107508
108291
  });
107509
108292
  };
107510
108293
  const handleImageApproachCountChange = (idx, count) => {
107511
- const clamped = Math.max(0, Math.min(4, count));
108294
+ const clamped = Math.max(0, Math.min(_promptOverrides__WEBPACK_IMPORTED_MODULE_38__.MAX_IMAGES_PER_CREO_APPROACH, count));
107512
108295
  setImageApproachCounts(prev => {
107513
108296
  const next = [...prev];
107514
108297
  if (idx >= 0 && idx < next.length)
@@ -107617,8 +108400,6 @@ function PromptManagerDialog({ open, onClose }) {
107617
108400
  return (0,_prompts__WEBPACK_IMPORTED_MODULE_36__.getTextsSystemPrompt)('${geo}', true, selectedApproaches.length);
107618
108401
  case 'getUserPrompt':
107619
108402
  return (0,_prompts__WEBPACK_IMPORTED_MODULE_36__.getUserPrompt)('${product}', '${geo}', '${additionalInfo}', 'titles', true, selectedApproaches.length);
107620
- case 'getImageCheckPrompt':
107621
- return (0,_prompts__WEBPACK_IMPORTED_MODULE_36__.getImageCheckPrompt)('${product}', '${geo}', true);
107622
108403
  case 'getValidationPrompt':
107623
108404
  return (0,_prompts__WEBPACK_IMPORTED_MODULE_36__.getValidationPrompt)('${product}', '${geo}', ['keyword1', 'keyword2'], '${approachName}', true, 'Новая по брифу: 29 EUR; старая (2×): 58 EUR — в рантайме подставляется из поля цены; в кастомном промпте плейсхолдер ${priceBrief}');
107624
108405
  case 'getImageGenerationBasePrompt':
@@ -107785,7 +108566,10 @@ function PromptManagerDialog({ open, onClose }) {
107785
108566
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { direction: "row", spacing: 2, alignItems: "center", sx: { mb: 1.5 } },
107786
108567
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_2__["default"], { variant: "subtitle1", sx: { fontWeight: 600 } }, "\u041F\u043E\u0434\u0445\u043E\u0434\u044B \u0434\u043B\u044F \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0439"),
107787
108568
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { label: `Всего: ${imageApproachCounts.reduce((a, b) => a + b, 0)}`, color: "primary", size: "small" }),
107788
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_2__["default"], { variant: "caption", color: "text.secondary" }, "(0\u20134 \u043D\u0430 \u043A\u0430\u0436\u0434\u044B\u0439 \u043F\u043E\u0434\u0445\u043E\u0434)")),
108569
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_2__["default"], { variant: "caption", color: "text.secondary" },
108570
+ "(0\u2013",
108571
+ _promptOverrides__WEBPACK_IMPORTED_MODULE_38__.MAX_IMAGES_PER_CREO_APPROACH,
108572
+ " \u043D\u0430 \u043A\u0430\u0436\u0434\u044B\u0439 \u043F\u043E\u0434\u0445\u043E\u0434)")),
107789
108573
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_1__["default"], { sx: { overflowX: 'auto' } },
107790
108574
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("table", { style: { width: '100%', borderCollapse: 'collapse', fontSize: 12 } },
107791
108575
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("thead", null,
@@ -107802,7 +108586,7 @@ function PromptManagerDialog({ open, onClose }) {
107802
108586
  handleImageApproachCountChange(i, isNaN(v) ? 0 : v);
107803
108587
  }, onBlur: handleImageCountBlur, inputProps: {
107804
108588
  min: 0,
107805
- max: 4,
108589
+ max: _promptOverrides__WEBPACK_IMPORTED_MODULE_38__.MAX_IMAGES_PER_CREO_APPROACH,
107806
108590
  step: 1,
107807
108591
  onWheel: (e) => {
107808
108592
  e.preventDefault();
@@ -107857,9 +108641,7 @@ function PromptManagerDialog({ open, onClose }) {
107857
108641
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_2__["default"], { variant: "subtitle2", gutterBottom: true }, "\u0424\u043E\u043A\u0443\u0441 \u043D\u0430 \u0431\u0443\u043B\u043B\u0435\u0442\u0430\u0445"),
107858
108642
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(SearchableTextField, { rows: 2, value: override?.customBulletsFocus || approach.bulletsFocus, onChange: (e) => handleApproachFieldChange(approach.name, 'customBulletsFocus', e.target.value), disabled: !enabled })))))));
107859
108643
  })),
107860
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(TabPanel, { value: tabValue, index: 2 },
107861
- renderPromptEditor('getImageCheckPrompt', 'Промпт проверки изображений', 'Промпт для проверки качества изображений'),
107862
- renderPromptEditor('getValidationPrompt', 'Промпт валидации', 'Промпт для валидации рекламных креативов')),
108644
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(TabPanel, { value: tabValue, index: 2 }, renderPromptEditor('getValidationPrompt', 'Промпт валидации', 'Промпт для валидации рекламных креативов. Если в папке есть product и он привязан к креативу, перед этим текстом автоматически добавляется блок про порядок изображений: креатив, затем эталон упаковки.')),
107863
108645
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(TabPanel, { value: tabValue, index: 3 },
107864
108646
  renderPromptEditor('getLandingPageSystemPrompt', 'Промпт лендинга (System)', 'Системный промпт для генерации лендинг-страниц'),
107865
108647
  renderPromptEditor('getLandingPageUserPrompt', 'Промпт лендинга (User)', 'Пользовательский промпт для генерации лендинг-страниц'))),
@@ -108059,13 +108841,10 @@ __webpack_require__.r(__webpack_exports__);
108059
108841
  */
108060
108842
  const MODELS = {
108061
108843
  // Модель для генерации заголовков и текстов
108062
- contentGeneration: 'openai/gpt-5.2-pro',
108844
+ contentGeneration: 'openai/gpt-5.2',
108063
108845
  // Модель для генерации изображений креативов
108064
108846
  // imageGeneration: 'openai/gpt-5-image',
108065
108847
  imageGeneration: 'openai/gpt-5-image-mini',
108066
- // Модель для проверки качества изображений
108067
- // imageCheck: 'openai/gpt-4o',
108068
- imageCheck: 'openai/gpt-5.2-pro',
108069
108848
  // Модель для валидации креативов
108070
108849
  // creativeValidation: 'openai/gpt-4o',
108071
108850
  creativeValidation: 'openai/gpt-5.2-pro',
@@ -108085,6 +108864,7 @@ const MODELS = {
108085
108864
  "use strict";
108086
108865
  __webpack_require__.r(__webpack_exports__);
108087
108866
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
108867
+ /* harmony export */ MAX_IMAGES_PER_CREO_APPROACH: () => (/* binding */ MAX_IMAGES_PER_CREO_APPROACH),
108088
108868
  /* harmony export */ PRODUCT_TYPE_IMAGE_PRESETS: () => (/* binding */ PRODUCT_TYPE_IMAGE_PRESETS),
108089
108869
  /* harmony export */ PRODUCT_TYPE_TEXT_PAIR_PRESETS: () => (/* binding */ PRODUCT_TYPE_TEXT_PAIR_PRESETS),
108090
108870
  /* harmony export */ PROMPT_OVERRIDES_SAVED_EVENT: () => (/* binding */ PROMPT_OVERRIDES_SAVED_EVENT),
@@ -108190,6 +108970,8 @@ function getPairsCount() {
108190
108970
  }
108191
108971
  /** Количество подходов для изображений. Должно совпадать с CREO_APPROACHES.length в prompts.ts */
108192
108972
  const IMAGE_APPROACH_COUNT = 10;
108973
+ /** Максимум картинок на один визуальный подход (слоты в UI и clamp при загрузке). */
108974
+ const MAX_IMAGES_PER_CREO_APPROACH = 10;
108193
108975
  const PAIR_APPROACH_POOL_LEN = 10;
108194
108976
  /** Нормализация индексов пар из JSON с диска (0-based, уникальные, отсортированные, минимум 2). */
108195
108977
  function normalizeSelectedPairApproachesFromDrive(raw) {
@@ -108204,7 +108986,7 @@ function normalizeSelectedPairApproachesFromDrive(raw) {
108204
108986
  const out = [...seen].sort((a, b) => a - b);
108205
108987
  return out.length >= 2 ? out : null;
108206
108988
  }
108207
- /** Нормализация количеств крео из JSON с диска (длина 10, 0–4; хотя бы один подход > 0). */
108989
+ /** Нормализация количеств крео из JSON с диска (длина 10, 0–MAX; хотя бы один подход > 0). */
108208
108990
  function normalizeImageApproachCountsFromDrive(raw) {
108209
108991
  if (!Array.isArray(raw))
108210
108992
  return null;
@@ -108212,21 +108994,21 @@ function normalizeImageApproachCountsFromDrive(raw) {
108212
108994
  for (let i = 0; i < IMAGE_APPROACH_COUNT; i++) {
108213
108995
  const v = raw[i];
108214
108996
  const n = typeof v === 'number' && Number.isFinite(v) ? v : parseInt(String(v), 10);
108215
- out.push(Number.isFinite(n) ? Math.max(0, Math.min(4, Math.round(n))) : 0);
108997
+ out.push(Number.isFinite(n) ? Math.max(0, Math.min(MAX_IMAGES_PER_CREO_APPROACH, Math.round(n))) : 0);
108216
108998
  }
108217
108999
  if (out.every(c => c === 0))
108218
109000
  return null;
108219
109001
  return out;
108220
109002
  }
108221
109003
  /**
108222
- * Получить количество изображений по каждому подходу (N элементов, 0–4).
109004
+ * Получить количество изображений по каждому подходу (N элементов, 0–MAX_IMAGES_PER_CREO_APPROACH).
108223
109005
  * По умолчанию — по 1 на каждый подход.
108224
109006
  */
108225
109007
  function getImageApproachCounts() {
108226
109008
  const overrides = loadPromptOverrides();
108227
109009
  const defaultCounts = Array(IMAGE_APPROACH_COUNT).fill(1);
108228
109010
  if (overrides.imageApproachCounts && overrides.imageApproachCounts.length > 0) {
108229
- const stored = overrides.imageApproachCounts.map(c => Math.max(0, Math.min(4, c)));
109011
+ const stored = overrides.imageApproachCounts.map(c => Math.max(0, Math.min(MAX_IMAGES_PER_CREO_APPROACH, c)));
108230
109012
  // backward compat: если было 8, дополняем новыми подходами
108231
109013
  while (stored.length < IMAGE_APPROACH_COUNT)
108232
109014
  stored.push(1);
@@ -108437,13 +109219,13 @@ __webpack_require__.r(__webpack_exports__);
108437
109219
  /* harmony export */ TITLES_APPROACH_POOL: () => (/* binding */ TITLES_APPROACH_POOL),
108438
109220
  /* harmony export */ getCreoApproachExpandedTasks: () => (/* binding */ getCreoApproachExpandedTasks),
108439
109221
  /* harmony export */ getCreoApproaches: () => (/* binding */ getCreoApproaches),
108440
- /* harmony export */ getImageCheckPrompt: () => (/* binding */ getImageCheckPrompt),
108441
109222
  /* harmony export */ getImageGenerationBasePrompt: () => (/* binding */ getImageGenerationBasePrompt),
108442
109223
  /* harmony export */ getPairsSystemPrompt: () => (/* binding */ getPairsSystemPrompt),
108443
109224
  /* harmony export */ getPairsUserPrompt: () => (/* binding */ getPairsUserPrompt),
108444
109225
  /* harmony export */ getTextsSystemPrompt: () => (/* binding */ getTextsSystemPrompt),
108445
109226
  /* harmony export */ getTitlesSystemPrompt: () => (/* binding */ getTitlesSystemPrompt),
108446
109227
  /* harmony export */ getUserPrompt: () => (/* binding */ getUserPrompt),
109228
+ /* harmony export */ getValidationProductReferencePreamble: () => (/* binding */ getValidationProductReferencePreamble),
108447
109229
  /* harmony export */ getValidationPrompt: () => (/* binding */ getValidationPrompt),
108448
109230
  /* harmony export */ pickRandomBullets: () => (/* binding */ pickRandomBullets)
108449
109231
  /* harmony export */ });
@@ -108453,27 +109235,29 @@ __webpack_require__.r(__webpack_exports__);
108453
109235
  * Все промпты на русском языке для консистентности
108454
109236
  */
108455
109237
 
108456
- /** Пул шаблонов буллетов по категориям (на русском — модель переводит на язык GEO) */
109238
+ /** Пул шаблонов буллетов по категориям (на русском — модель переводит на язык GEO).
109239
+ * Без привязки к «чужим» вертикалям (суставы/боль/ЖКТ/кожа): иначе модель переносит их на любой оффер. */
108457
109240
  const BULLET_BY_CATEGORY = {
108458
109241
  action: [
108459
- 'Снижает дискомфорт', 'Снижает вздутие', 'Снижает боль', 'Быстрое облегчение',
108460
- 'Комфорт каждый день', 'Лёгкость движений', 'Меньше симптомов', 'Без боли',
108461
- 'Дни без дискомфорта', 'Спокойный желудок', 'Спокойные ночи', 'Видимый результат'
109242
+ 'Поддержка организма', 'Комфорт в повседневности', 'Мягкая поддержка формулы',
109243
+ 'Заметная поддержка самочувствия', 'Для регулярного приёма', 'Естественная поддержка',
109244
+ 'Видимая разница со временем', 'Больше уверенности в себе'
108462
109245
  ],
108463
109246
  timeframe: [
108464
- 'Эффект за 7–14 дней', 'Эффект за 2 недели', 'Быстрый результат', 'Облегчение с 1 дня',
108465
- 'Эффект за 7 дней', 'В 3 раза быстрее', 'С первого дня', 'В первые дни'
109247
+ 'Эффект за 7–14 дней', 'Эффект за 2 недели', 'Быстрый результат', 'Поддержка с первого дня',
109248
+ 'Эффект за 7 дней', 'Заметно уже скоро', 'С первого дня курса', 'В первые дни приёма'
108466
109249
  ],
108467
109250
  socialProof: [
108468
- '9 из 10 рекомендуют', '9 из 10 мужчин рекомендуют', '93% подтверждают',
108469
- '8 из 10 довольны', 'Тысячи довольных'
109251
+ '9 из 10 рекомендуют', '9 из 10 мужчин рекомендуют', 'Тысячи уже выбрали',
109252
+ '8 из 10 довольны результатом', 'Высокая оценка покупателей'
108470
109253
  ],
108471
109254
  formula: [
108472
- 'Натуральная формула', 'Натуральные ингредиенты', 'Без химии', 'Отборный состав',
108473
- 'Дерматологически протестировано'
109255
+ 'Натуральная формула', 'Натуральные ингредиенты', 'Без лишней химии', 'Тщательно подобранный состав',
109256
+ 'Продуманное сочетание компонентов'
108474
109257
  ],
108475
109258
  qualityOfLife: [
108476
- 'Активный комфорт', 'Дни без забот', 'Больше энергии', 'Здоровый сон', 'Лёгкая жизнь'
109259
+ 'Спокойнее каждый день', 'Больше энергии', 'Увереннее в себе', 'Ровнее самочувствие',
109260
+ 'Поддержка вашего запроса'
108477
109261
  ]
108478
109262
  };
108479
109263
  /** Какие категории для какого подхода (по 1 из каждой категории, порядок случайный) */
@@ -108527,7 +109311,7 @@ const PAIR_APPROACH_POOL = [
108527
109311
  {
108528
109312
  name: 'testimonial',
108529
109313
  isTestimonial: true,
108530
- titleApproach: 'короткая «живая» фраза с **конкретным результатом по теме продукта** и сроком (7–14 дней). Цифры −кг, весы, «минус на весах» — **только** если продукт явно про похудение / контроль веса. Для паразитов/ЖКТ/суставов/простаты/сна и т.п. — результат = облегчение симптомов, комфорт, сон, подвижность, меньше дискомфорта (без выдуманных кг). Это отличает testimonial от «трансформации». НЕ используй «Имя, возраст:» в заголовке — только в тексте',
109314
+ titleApproach: 'короткая «живая» фраза с **конкретным результатом по теме продукта** и сроком (7–14 дней). Цифры −кг, весы, «минус на весах» — **только** если продукт явно про похудение / контроль веса. Для любых офферов не про вес — результат = облегчение симптомов, комфорт, сон, подвижность, меньше дискомфорта (без выдуманных кг). Это отличает testimonial от «трансформации». НЕ используй «Имя, возраст:» в заголовке — только в тексте',
108531
109315
  textApproach: `Это единственный подход-ОТЗЫВ: читатель должен поверить в реального человека.
108532
109316
  * Обязательно начало текста с «Имя, возраст:» (формат на языке GEO, например «Анна, 49 лет:», «Олег, 41 год:»)
108533
109317
  * История только от первого лица после имени
@@ -108638,7 +109422,7 @@ function getTitlesSystemPrompt(geo, noOverride, count = 3) {
108638
109422
  - ЗАПРЕЩЕНО использовать двоеточие (:) в заголовках. Используй тире (—) или переформулируй заголовок без двоеточия. Если в заголовке есть двоеточие — это критическая ошибка, перепиши заголовок
108639
109423
  - Используй 1-2 эмодзи естественно внутри заголовка, не только в конце. Эмодзи должны заменять или визуально отмечать смысл (эмоция, действие, предупреждение), а не украшать или заполнять пространство. Максимум 1-2 на строку
108640
109424
  - Избегай эмодзи ⚠️ и 🚨 — они ассоциируются с опасностью. Для привлечения внимания используй 👉, ✅, 🌿, ⏳
108641
- - КРИТИЧНО: Каждый заголовок ОБЯЗАН содержать ключевое слово проблемы ЯВНО. Примеры по категориям: простата простатит, простата; похудение лишний вес, похудение; суставы боль в суставах, колени, скованность; пищеварение дискомфорт, вздутие, тяжесть; сон бессонница, усталость. Если ключевое слово отсутствует — перепиши заголовок
109425
+ - КРИТИЧНО: Заголовок должен быть **в одной теме с продуктом** из задания пользователя: читатель сразу понимает, какую потребность или боль вы закрываете (симптом, ситуация или ясная выгода в этой нише). Запрещено подменять другую вертикаль здоровья или «типичную» боль соседней категории. Нельзя оставаться на уровне общих слов («легче», «лучше», «помощь») без привязки к теме оффера — перепиши
108642
109426
  - Используй знания лучших практик рекламного копирайтинга
108643
109427
  - После создания проверь их правильность и разнообразие
108644
109428
  - Верни только заголовки, по одному на строку, без нумерации или буллетов
@@ -108701,7 +109485,7 @@ ${deadlineNote}
108701
109485
  Требования к содержанию:
108702
109486
  - КРИТИЧНО: Держи каждый текст кратким — цель примерно 150-280 символов на текст. Тексты в стиле отзыва могут использовать до 300 символов при необходимости для естественного повествования. Будь эффектным, но коротким
108703
109487
  - Если текст длиннее 280 символов (или 300 для стиля отзыва) — сожми и перепиши, сохраняя смысл
108704
- - КРИТИЧНО: Каждый текст должен содержать хотя бы одно ясное ключевое слово проблемы, релевантное категории продукта (например, для суставов: боль/скованность/подвижность; для пищеварения: дискомфорт/тяжесть; для сна: бессонница/усталость примеры только для формата, адаптируй под категорию). Если ключевое слово проблемы отсутствует — перепиши текст
109488
+ - КРИТИЧНО: Текст держи **в той же теме, что продукт** из задания: конкретная боль, ситуация или выгода, которые очевидно относятся к этому офферу, без ухода в соседние ниши. Если тема размыта или общая — перепиши
108705
109489
  - КРИТИЧНО: Каждый текст должен заканчиваться сильным и явным призывом к действию (кликни, попробуй сейчас, закажи сегодня, узнай больше и т.д.)
108706
109490
  - Избегай слабых окончаний или информационного тона. Слабые или нейтральные окончания не допускаются
108707
109491
  - CTA должен быть “нажимным”: допускаются формулировки «не откладывай», «последний шанс», «только сегодня», «забери -50%» (всё строго на языке GEO). Если пишешь скидку — **-50%** буквально, без выдуманной механики акции
@@ -108812,21 +109596,21 @@ ${hasTransformAndTestimonial ? '\n- РАЗВЕДЕНИЕ «трансформа
108812
109596
  - Цельность: заголовок читается как одно высказывание (или один связный вопрос целиком). ЗАПРЕЩЕНО склеивать несвязные ярлыки запятыми ради ключевых слов; эмодзи не должно разрывать грамматику так, что фраза перестаёт быть человеческой
108813
109597
  - ЗАПРЕЩЕНО двоеточие (:) — используй тире (—) или переформулируй
108814
109598
  - 1–2 эмодзи естественно (часто в конце фразы или после целого смыслового блока); избегай ⚠️ и 🚨${hasDirectOffer ? '\n- Для пары «прямой оффер/выгода» эмодзи в заголовке ОБЯЗАТЕЛЕН (минимум один) — визуально вровень с остальными парами набора' : ''}
108815
- - Хотя бы одно ключевое слово проблемы, релевантное продукту${hasUrgencyScarcity ? '\n- ИСКЛЮЧЕНИЕ: для пары с подходом «срочность/дефицит» в заголовке ЗАПРЕЩЕНЫ симптомы, проблема, тело, продукт — только лимит оффера (время, количество, дедлайн)' : ''}${hasDirectOffer ? '\n- ИСКЛЮЧЕНИЕ: для пары «прямой оффер/выгода» ключевое слово проблемы в заголовке **не обязательно**; ядро — **-50%** (+эмодзи + короткая срочность). **Не выдумывай** «2-й пакет / вторая упаковка / полцены» без данных в задании — **-50% это просто -50%.**' : ''}${hasAuthorityExpertise ? '\n- Пара «авторитет/экспертиза»: заголовок держит information gap — без ингредиентов и без прямого названия механизма; состав и «как это работает» — только в тексте пары' : ''}
109599
+ - Заголовок с явной привязкой к теме продукта из задания (симптом, ситуация или понятная выгода в этой нише), не абстрактный слоган${hasUrgencyScarcity ? '\n- ИСКЛЮЧЕНИЕ: для пары с подходом «срочность/дефицит» в заголовке ЗАПРЕЩЕНЫ симптомы, проблема, тело, продукт — только лимит оффера (время, количество, дедлайн)' : ''}${hasDirectOffer ? '\n- ИСКЛЮЧЕНИЕ: для пары «прямой оффер/выгода» болевая конкретика в заголовке **не обязательна**; ядро — **-50%** (+эмодзи + короткая срочность). **Не выдумывай** «2-й пакет / вторая упаковка / полцены» без данных в задании — **-50% это просто -50%.**' : ''}${hasAuthorityExpertise ? '\n- Пара «авторитет/экспертиза»: заголовок держит information gap — без ингредиентов и без прямого названия механизма; состав и «как это работает» — только в тексте пары' : ''}
108816
109600
  - НЕ упоминай название продукта — заголовок = боль + триггер${hasUrgencyScarcity ? ' (кроме пары «срочность/дефицит»: там заголовок = только дефицит/дедлайн, без продукта и без боли)' : ''}${hasSocialProof ? '\n- Исключение: в паре «социальное доказательство» название продукта в заголовке допустимо, если оно входит в цельную фразу про массовый выбор (например «Уже N тысяч человек выбрали [продукт]» на языке GEO)' : ''}
108817
109601
  - Звучит как живая рекламная фраза, а не строка ключевых слов
108818
109602
 
108819
109603
  ТРЕБОВАНИЯ К ТЕКСТУ:
108820
109604
  - 150–280 символов (testimonial — до 300)${hasDirectOffer ? '; для пары «прямой оффер/выгода» достаточно **120–200** символов, если текст = **-50%** + срочность + CTA без воды' : ''}
108821
- - Хотя бы одно ключевое слово проблемы (для «срочность/дефицит» в тексте допустимо слабее, если весь фокус — на лимите оффера)${hasDirectOffer ? '; для «прямой оффер/выгода» ключевое слово проблемы **не обязательно** — фокус на **-50%** и CTA' : ''}
109605
+ - Та же тема, что в заголовке пары: конкретика боли/ситуации или выгоды в нише продукта (для «срочность/дефицит» в тексте допустимо слабее, если весь фокус — на лимите оффера)${hasDirectOffer ? '; для «прямой оффер/выгода» болевая конкретика **не обязательна** — фокус на **-50%** и CTA' : ''}
108822
109606
  - Сильный явный CTA в конце («кликни», «попробуй сейчас», «закажи», «не жди»)${hasFearInaction ? '\n- ИСКЛЮЧЕНИЕ: для пары «страх бездействия» финальная фраза — не заказ, а забота о себе / начать сейчас; без «закажи», «купи», «оформи заказ» в конце' : ''}
108823
- - Короткие рубленые фразы, императивы, FOMO допустим${hasTestimonial ? '\n- Testimonial: строго от первого лица, начинается с «Имя, возраст:», конкретный результат с таймингом; тема результата = как в названии/описании продукта (антипаразитарное ЖКТ/вздутие/дискомфорт и т.д.), не минус кг если продукт не про вес' : ''}
109607
+ - Короткие рубленые фразы, императивы, FOMO допустим${hasTestimonial ? '\n- Testimonial: строго от первого лица, начинается с «Имя, возраст:», конкретный результат с таймингом; тема результата = та же, что в продукте из задания; не минус кг, если продукт не про вес' : ''}
108824
109608
  - Ингредиенты и состав из доп. информации — только в углах «авторитет/экспертиза» (и сходных); в «болевой точке» и чисто эмоциональных углах состав не перечислять
108825
109609
  ${deadlineNote}
108826
109610
 
108827
109611
  ОБЩИЕ ТРЕБОВАНИЯ:
108828
109612
  - Язык: ${geo} — все тексты строго на этом языке
108829
- - Релевантность продукту из пользовательского сообщения: не подменяй категорию. Капли/средства от паразитов, детокс ЖКТ симптомы и облегчение в этой зоне; **не делай главным мотивом −кг и весы**, если в задании нет явного похудения. Аналогично не смешивай суставы с простатой и т.д.
109613
+ - Релевантность продукту из пользовательского сообщения: не подменяй категорию и не смешивай симптоматику разных вертикалей. **Не делай главным мотивом −кг и весы**, если в задании нет явного похудения.
108830
109614
  - Естественный человеческий тон, без академизма и AI-звучания
108831
109615
  - Разные психологические триггеры в каждой паре, минимальное пересечение
108832
109616
  - Верни ТОЛЬКО пары в указанном формате, без объяснений и комментариев`;
@@ -108856,30 +109640,27 @@ function getPairsUserPrompt(product, geo, additionalInfo, noOverride, count = 3,
108856
109640
  Создай ${n} пар «заголовок + текст» для рекламы в Facebook.`;
108857
109641
  }
108858
109642
  /**
108859
- * Промпт для проверки качества изображения
109643
+ * Блок, который приложение добавляет **перед** текстом `getValidationPrompt`, когда в запрос к модели
109644
+ * уходят два изображения: сначала креатив, затем эталон `product.png`/`product.jpg`/`product.webp`.
109645
+ * В кастомном override этого текста нет — он всё равно префиксуется в рантайме при наличии референса.
108860
109646
  */
108861
- function getImageCheckPrompt(product, geo, noOverride) {
108862
- if (!noOverride) {
108863
- const override = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_0__.getPromptOverride)('getImageCheckPrompt');
108864
- if (override?.enabled && override.customPrompt) {
108865
- return override.customPrompt
108866
- .replace(/\$\{product\}/g, product)
108867
- .replace(/\$\{geo\}/g, geo);
108868
- }
108869
- }
108870
- return `Ты проверщик качества изображений для рекламы продуктов. Проверь, подходит ли это изображение для рекламы "${product}" на рынке ${geo}.
108871
-
108872
- Требования:
108873
- - Изображение должно быть подходящим для e-commerce/рекламы в Facebook
108874
- - Изображение должно соответствовать концепции продукта
108875
- - Изображение должно быть профессиональным и высокого качества
108876
- - Изображение не должно содержать неподходящий контент
108877
- - Изображение должно быть релевантным продукту
108878
-
108879
- Ответь JSON: {"approved": true/false, "reason": "краткое объяснение"}`;
109647
+ function getValidationProductReferencePreamble() {
109648
+ return `ИЗОБРАЖЕНИЯ (строгий порядок после этого текста):
109649
+ 1) Первое изображение — готовый рекламный креатив для проверки.
109650
+ 2) Второе изображение — эталонная фотография упаковки/товара (тот же файл, что использовался при генерации).
109651
+
109652
+ СВЕРКА С ЭТАЛОНОМ:
109653
+ - Убедись, что товар на креативе — тот же продукт/упаковка, что на эталоне: бренд, форма, характерные цвета и узнаваемые элементы дизайна.
109654
+ - Допустимы другой ракурс, крупность, свет, фон, отражения и рекламная обработка; не требуй пиксель-в-пиксель.
109655
+ - Ошибка (отдельным пунктом в списке ОШИБОК), только если на креативе визуально другой товар или настолько сильное расхождение, что покупатель спутает продукт.
109656
+ - Текст на этикетке упаковки по общим правилам ниже не валидируй на язык; эталон не отменяет остальные шаги проверки макета.
109657
+
109658
+ ---
109659
+ `;
108880
109660
  }
108881
109661
  /**
108882
- * Промпт для валидации рекламных креативов
109662
+ * Промпт для валидации рекламных креативов.
109663
+ * `keywords` — только для подстановки в кастомный override (${keywords}); в дефолтном тексте не используется.
108883
109664
  */
108884
109665
  function getValidationPrompt(product, geo, keywords, approachName, noOverride,
108885
109666
  /** Краткий эталон цен из приложения (новая + ожидаемая старая при -50%); пусто — без сверки чисел */
@@ -108910,7 +109691,7 @@ priceBrief) {
108910
109691
  ${priceBriefTrim}
108911
109692
 
108912
109693
  1) ВНУТРЕННЯЯ ЛОГИКА НА МАКЕТЕ (главное):
108913
- - Должны быть видны две суммы: новая (акционная) и старая (зачёркнутая). Старая новой при скидке -50% (допуск до ~2% из‑за округления; «1 180» и «1180» — одно и то же).
109694
+ - Должны быть видны две суммы: новая (акционная) и старая (зачёркнутая). Числовое отношение: старая вдвое новой при скидке -50% (допуск до ~2% из‑за округления; «1 180» и «1180» — одно и то же). На макете **не должно** быть текста «2×»/«2x» перед суммой как множитель — только две суммы (см. ШАГ 4).
108914
109695
  - Если отношение старая/новая ≈ 2 (в пределах допуска) — это уже доказательство корректной пары -50% на макете. В этом случае ЗАПРЕЩЕНО выводить ОШИБКУ формулировками вроде «выдуманные цены», «неверные суммы», «не соответствуют брифу» — даже если цифры в брифе другие или бриф кажется не тем.
108915
109696
  - Если пара математически согласована с -50% и процент «-50%» где‑либо читаем в зоне оффера (см. ШАГ 4) — не придумывай расхождение с брифом из‑за символа валюты (€ / EUR / MXN и т.д.) или пробелов.
108916
109697
 
@@ -108921,10 +109702,11 @@ ${priceBriefTrim}
108921
109702
 
108922
109703
  3) Цена не должна быть оформлена как строка буллета с галочкой — отдельный блок.`
108923
109704
  : `ШАГ P — ЦЕНЫ (бриф числом не задан):
108924
- - На макете должны быть ДВЕ цены, старая зачёркнута толсто, новая выразительна; пара должна визуально соответствовать -50% (старая новой). Конкретные суммы с приложением не сверяй.`;
109705
+ - На макете должны быть ДВЕ цены, старая зачёркнута толсто, новая выразительна; пара должна визуально соответствовать -50% (старое число примерно вдвое больше нового). Надписей «2×» перед суммой быть не должно — см. ШАГ 4. Конкретные суммы с приложением не сверяй.`;
108925
109706
  const step3OtherText = isScreenshotReviews
108926
109707
  ? `ШАГ 3 — Подход «Скрин отзывов»:
108927
- - Блок отзывов (аватары, имена, звёзды, текст) — это нормальный контент креатива, не считай его лишним OTHER_TEXT.`
109708
+ - Блок отзывов (аватары, имена, звёзды, текст) — это нормальный контент креатива, не считай его лишним OTHER_TEXT.
109709
+ - Английские подписи только в имитации шапки приложения («Reviews» и т.п.) — см. исключение в ШАГ 5 и блок ВАЖНО про UI телефона; не смешивай с языком тел отзыва.`
108928
109710
  : `ШАГ 3 — Доп. текст, бейджи, печати:
108929
109711
  - Не валидируй объём текста и не отклоняй креатив за «слишком много элементов», punch vs не punch, количество плашек или пустоту OTHER_TEXT.
108930
109712
  - Не требуй отсутствия trust/urgency/подписей — для любого подхода это не ошибка.
@@ -108934,6 +109716,8 @@ ${priceBriefTrim}
108934
109716
 
108935
109717
  ВАЖНО:
108936
109718
  - Проверяй язык ТОЛЬКО для рекламного текста на макете (заголовок/CTA/цена/скидка/разрешённые бейджи из OTHER_TEXT; если есть буллеты — и их текст). Текст на самой упаковке/этикетке игнорируй (включая название/бренд вроде "${product}").
109719
+ - **Имитация UI телефона / приложения** (рамка смартфона, скрин отзывов): короткие англоязычные подписи «как в интерфейсе» — заголовок окна («Reviews», «Ratings»), служебные метки шапки магазина приложений — **не** считай нарушением языка GEO: это декор интерфейса, не рекламный оффер. Тексты самих отзывов внутри карточек по-прежнему должны быть на языке GEO (если видны как контент отзыва). HOOK креатива, CTA, цены, буллеты **вне** рамки телефона — строго язык GEO.
109720
+ - **Круглые trust‑печати (FDA)**: мелкий текст по ободу — не валидируй на читабельность и не требуй «крупнее»; отдельные подписи в футере рядом с печатью — по ШАГ 5–6.
108937
109721
  - Если сомневаешься, мелкий текст на банке — этикетка или нет: для языка считай этикеткой и не валидируй. Исключение: **нижний оффер-блок** макета (две цены, зачёркивание, «-50%», кнопка CTA в одной полосе/ряду под картинкой продукта) — это всегда рекламный слой **вне упаковки**, даже рядом с фото банки; цены и скидку оттуда учитывай в PRICE/DISCOUNT и не считай «на упаковке».
108938
109722
  - Если текст плохо читается/микрошрифт — это ошибка.
108939
109723
 
@@ -108950,9 +109734,10 @@ OTHER_TEXT: ["..."] (любой другой рекламный текст на
108950
109734
  - Должен быть ВЫШЕ всех элементов и читабелен (хорошо читается на телефоне)
108951
109735
  - Допускается 1–4 строки (это НЕ ошибка). Ошибка только если текст нечитабелен или есть разрыв/перенос внутри слова.
108952
109736
  - Смысл: релевантно проблеме/выгоде продукта. Допускаются и боль/дискомфорт, и обещание/выгода (например «станет легче день ото дня» — смысл на языке GEO). НЕ считай ошибкой формулировки обещаний результата или клеймы.
108953
- - ОБЯЗАТЕЛЬНО: ключевое слово проблемы явно в формулировке. Примеры по категориям: простата простатит, простата; похудение лишний вес, похудение; суставы боль, колени, скованность; пищеварение дискомфорт, вздутие; сон бессонница, усталость. Отсутствие ключевого слова ОШИБКА. Дополнительно для ориентира: ${keywords.join(', ')} (можно синонимы).
108954
- - Проверь вторую часть заголовка. Если она состоит только из абстрактных слов («легче», «лучше», «решение», «помощь» без конкретики на языке GEO) без конкретного бенефитаотметь: РЕКОМЕНДАЦИЯ: заголовок можно усилить конкретикой
108955
- - Если заголовок слишком общий/абстрактный («легче», «лучше» без конкретики) — отметь как РЕКОМЕНДАЦИЯ к улучшению (не ошибка, но слабо)
109737
+ - Заголовок должен **совпадать по теме с продуктом** из брифа (та же потребность/проблемная зона/обещание, что в описании продукта). ОШИБКА, если заголовок явно про другую вертикаль здоровья или другую боль, чем следует из брифа. ОШИБКА, если заголовок настолько общий, что его нельзя однозначно отнести к заявленному продукту (одни абстрактные «легче/лучше/решение» без привязки к нише).${keywords.length ? ` Ориентир по словам (не выносить в другую нишу): ${keywords.join(', ')}.` : ''}
109738
+ - ОШИБКА (соцдоказательство без нише): если текст HOOK по сути **только** массовое доверие/рейтинг («тысячи доверяют», «98% pozytywnych opinii», «10 000 confían» как главный посыл) и **нет** ни одного узнаваемого маркера **темы продукта из брифа** (не абстрактное «организм/здоровье» — нужны слова уровня назначения: слим → тело/фигура/вес; детокс/очищение/паразиты → соответствующая лексика на языке GEO и т.д.), так что заголовок подошёл бы любому БАДу **НУЖНА ПЕРЕСБОРКА**. Отдельная строка скидки не считается привязкой к нише.
109739
+ - Проверь вторую часть заголовка. Если она состоит только из абстрактных слов («легче», «лучше», «решение», «помощь» без конкретики на языке GEO) без конкретного бенефита отметь: РЕКОМЕНДАЦИЯ: заголовок можно усилить конкретикой (но если одновременно срабатывает пункт про «только соцдоказательство» выше — ставь ОШИБКА, не рекомендацию)
109740
+ - Если заголовок слишком общий/абстрактный («легче», «лучше» без конкретики), но не попадает под «только соцдоказательство» — отметь как РЕКОМЕНДАЦИЯ к улучшению (не ошибка, но слабо)
108956
109741
  - РЕКОМЕНДАЦИЯ (CTR): верхний текст лучше делать как HOOK — ОЧЕНЬ крупный, жирный, ВСЕ БУКВЫ ПРОПИСНЫЕ, на яркой контрастной плашке. Если это не так — не ошибка, но отметь рекомендацией
108957
109742
 
108958
109743
  ${step2Bullets}
@@ -108966,6 +109751,7 @@ ${step3OtherText}
108966
109751
 
108967
109752
  ШАГ 4 — CTA и визуал цены/скидки (критично):
108968
109753
  - CTA: на языке ${geo}, хорошо заметная кнопка (позиция НЕ важна: можно вправо/влево/по центру). Главное — CTA читабельна и выглядит как кнопка.
109754
+ - Оформление сумм: у старой и новой цены должны быть **только число и валюта** (и зачёркивание у старой). ОШИБКА, если перед суммой напечатано «2×» / «2x» / «x2» как пояснение множителя для стандартной пары -50% (типичная ошибка генератора): читатель должен видеть, например, «58 €» и «29 €», а не «2× 58 €». Исключение — только если в брифе явная акция «2 шт.» / «pack x2» и это не наш кейс простого -50%.
108969
109755
  - Блок цен: две суммы, старая зачёркнута ТОЛСТОЙ линией, новая выразительно (детали и сверка чисел с брифом — в ШАГ P). Если только одна цена — ОШИБКА.
108970
109756
  - Скидка ОБЯЗАТЕЛЬНА: читаемое «-50%». **Отдельный видимый бейдж** = любая заметная плашка/круг/прямоугольник с текстом «-50%» в **той же зоне оффера**, что и цены (сбоку от сумм, над/под ценовой строкой, между ценой и CTA) — это ОК, не требуй отдельного «далёкого» элемента. «НЕ на упаковке» означает только: не напечатано на этикетке/банке товара; плашка в нижней рекламной полосе макета **не** считается упаковкой.
108971
109757
  - Если в ШАГ 0 в DISCOUNT ты указал «-50%» (или эквивалент) — ЗАПРЕЩЕНО ставить ошибку «скидка не отображается» / «нет отдельного бейджа».
@@ -108974,14 +109760,16 @@ ${step3OtherText}
108974
109760
 
108975
109761
  ШАГ 5 — ЯЗЫК (критично):
108976
109762
  - В рекламном тексте (HOOK/HEADLINE, текст на буллетах если есть, CTA/PRICE/DISCOUNT/OTHER_TEXT) НЕТ английских слов. Если есть — ошибка.
109763
+ - ИСКЛЮЧЕНИЕ: внутри **графики интерфейса телефона** (шапка окна приложения, подпись вкладки вроде «Reviews», «App Store»-стиль бейджа в углу рамки) английские слова **не** считать ошибкой — это имитация ОС/магазина, не рекламный текст баннера. Ошибкой остаётся английский в HOOK над макетом, в CTA, в буллетах, на плашках цены/скидки, в теле отзыва как пользовательском тексте (не в подписи UI).
108977
109764
  - ЗАПРЕЩЁННЫЕ служебные подписи на макете (частая ошибка модели): слова HOOK, CTA, CAPS, BULLET, HEADLINE, PRICE, DISCOUNT как видимый текст на плашках или кнопке — ОШИБКА (это метки из брифа, не для читателя).
108978
109765
  - Все слова соответствуют GEO/языку ${geo}. Если смешение языков — ошибка.
109766
+ - ПСЕВДОСЛОВА И ОПЕЧАТКИ: если на плашке скидки или в буллетах видно **несуществующее** или явно ошибочное слово для языка GEO (не словарное), либо «калька» под английский без нормы (пример для испанского: «DISCONTA» вместо «DESCUENTO»; на буллете англ. «COMFORT» вместо исп. «CONFORT»/«COMODIDAD») — ОШИБКА языка (приземлённая формулировка в строке ОШИБКА).
108979
109767
 
108980
109768
  ШАГ 6 — КОМПОЗИЦИЯ:
108981
109769
  - Если креатив без человека (lifestyle/clean), проверь наличие контекста использования (кухня, стол, тумбочка, и т.д.). Продукт на полностью пустом/белом фоне без контекста — РЕКОМЕНДАЦИЯ к улучшению (не критичная ошибка, но слабый визуал)
108982
109770
  - КОНТРАСТ ПЛАШЕК: если HOOK, CTA или блок цен визуально сливается с фоном и текст плохо читается — это ОШИБКА. (Контраст буллетов не проверяй.)
108983
109771
  - Две цены и зачёркивание старой — см. ШАГ 4 и ШАГ P. Цена не должна быть оформлена как элемент списка с галочкой в стиле бенефитов.
108984
- - TRUST‑печати: если печати мелкие или текст нечитабелен на телефонеОШИБКА (должны быть крупными).
109772
+ - TRUST‑печати (круги FDA и т.п.): **не** считай ОШИБКОЙ мелкий/неразборчивый текст **на самой печати** и по кругу ободка декоративный слой, на мобильном часто нечитаем. Короткая **отдельная подпись** рядом с печатью в футере («FÓRMULA NATURAL», «CALIDAD PREMIUM»), если видна как строка — проверь язык GEO и явные опечатки; если разобрать нельзя — не выдумывай нарушение.
108985
109773
  - СКИДКА «-50%»: как в ШАГ 4 — плашка рядом с ценами в оффер-блоке засчитывается; если DISCOUNT в ШАГ 0 заполнен — не дублируй ошибку про бейдж.
108986
109774
  - Бейджи срочности: «24h» и подобные неясные формулировки — ОШИБКА (непонятно что означает). Ясные («только сегодня», «последние штуки») — ок.
108987
109775
 
@@ -109030,10 +109818,10 @@ function getImageGenerationBasePrompt(generateGeo, generatePrice, generateCurren
109030
109818
  Слова HOOK, CTA, CAPS, BULLET, HEADLINE, PRICE, DISCOUNT, BULLETS, LAYER и любые похожие английские метки из этой инструкции (в т.ч. thumb‑stop) — только для тебя; на макете их НЕТ ни на кнопке, ни на плашках, ни мелким текстом. Кнопка: только реальный призыв на языке ${generateGeo} (1–2 слова), без префикса «CTA». Заголовок: только живой текст оффера, без слова HOOK. Заголовок делай прописными буквами на языке ${generateGeo}, но не пиши на изображении слово CAPS и не подписывай блоки ярлыками.
109031
109819
 
109032
109820
  🚨 КРИТИЧНО — РЕЛЕВАНТНОСТЬ ПРОДУКТУ (читай ЭТО ПЕРВЫМ):
109033
- - Название продукта передано в начале промпта (строка «🏷️ ПРОДУКТ: ...»). Прочитай его ПРЯМО СЕЙЧАС и определи категорию.
109034
- - Категории и их проблемы: суставы/колени боль в суставах, скованность, тугоподвижность; пищеварение дискомфорт после еды, тяжесть; сон → бессонница, усталость; похудение → лишний вес; простата → частые позывы, дискомфорт.
109035
- - HOOK и буллиты ОБЯЗАНЫ отражать ТОЛЬКО проблему категории этого продукта. Если продукт для суставовНИКАКОГО желудка, вздутия, газов, пищеварения.
109036
- - Иконки и схемы тела — ТОЛЬКО зона продукта: для суставов рисуй колено/сустав/позвоночник; для пищеварения желудок; для простаты силуэт мужчины. НЕ рисуй желудок для суставного продукта.
109821
+ - Название и назначение продукта в начале запроса (строка «🏷️ ПРОДУКТ: ...»). Прочитай и держи **одну** тему: HOOK, буллиты и любая схема тела должны относиться к **этой** нише, без подмены «типичной» болью из другой категории.
109822
+ - HOOK и буллиты отражают только проблему/выгоду, которые читатель свяжет с **данным** продуктом. Не переносай симптоматику и органы соседних вертикалей.
109823
+ - ЗАПРЕТ на «склейку» вертикалей: без явного повода в брифе/упаковке не используй для буллитов и HOOK клише **других** ниш — суставы и подвижность, «лёгкость движения», общую «боль», желудок/вздутие, зрение, слух, вес/кг, кожу/дерматологию если оффер про другое (например мужское здоровье/либидо/фертильность формулируй в **той** лексике: энергия, уверенность, ритм, пара, интимное самочувствие мягко и по месту и т.д., без переноса из примеров про суставы или ЖКТ).
109824
+ - Иконки и схемы тела — только то, что логично для этого оффера; если сомневаешьсяlifestyle без анатомической схемы предпочтительнее, чем неверная зона тела.
109037
109825
  - НЕ копируй примеры из промпта — они только показывают формат. Все слова HOOK/буллитов придумывай под конкретный продукт.
109038
109826
  - Это правило ВАЖНЕЕ ВСЕХ остальных правил промпта. Нарушение = полностью нерелевантный креатив.
109039
109827
 
@@ -109041,40 +109829,47 @@ function getImageGenerationBasePrompt(generateGeo, generatePrice, generateCurren
109041
109829
 
109042
109830
  CRITICAL RULES (это ВАЛИДАЦИЯ, не рекомендации; если нарушено — УДАЛИ ЛИШНЕЕ и ПЕРЕСОБЕРИ МАКЕТ):
109043
109831
  РОЛЬ ЭЛЕМЕНТОВ (маркетинговая логика):
109044
- - HOOK (верхний текст / HEADLINE) = зацепка боли (реальный дискомфорт/состояние) + направление облегчения
109045
- - Буллеты = свойства ИЛИ ощущаемые преимущества (комфорт, лёгкость, спокойствие)
109832
+ - HOOK (верхний текст / HEADLINE) = зацепка боли (реальный дискомфорт/состояние) + направление облегчения **в нише этого продукта**
109833
+ - Буллеты = свойства ИЛИ ощущаемые преимущества **в теме оффера** (комфорт, уверенность, срок, состав — что уместно для брифа, не «универсальная боль» соседней категории)
109046
109834
  - Цена/скидка = триггер "сейчас"
109047
109835
  - CTA = финальный шаг к действию
109048
109836
 
109049
109837
  ❗ ЦЕНА (КРИТИЧНО — НЕ ПРОПУСКАЙ):
109050
109838
  - ОБЯЗАТЕЛЬНО ДВЕ цены: старая (до скидки) + новая (${generatePrice} ${generateCurrency}). Одна цена = ОШИБКА.
109051
- - Старая = новой (при -50%). Новая выразительно, крупно. Стараязачёркнута.
109839
+ - Математика для тебя: при -50% сумма «старой» цены должна быть **вдвое** больше новой но на макете печатай **только цифры и символ валюты**, например новая **${generatePrice} ${generateCurrency}**, старая **удвоенная сумма + ${generateCurrency}** (без каких‑либо слов-множителей).
109840
+ - ЗАПРЕЩЕНО писать на ценниках префиксы **«2×», «2x», «x2», «double»** перед суммой — это служебная логика промпта, не надпись для покупателя. Неправильно: «2× 58 €», «2x 29 €». Правильно: старая строка **«58 €»** (или другая удвоенная сумма к новой), новая **«${generatePrice} ${generateCurrency}»**.
109841
+ - Новая — выразительно, крупно. Старая — зачёркнута.
109052
109842
  - Зачёркивание: ТОЛСТАЯ линия (не тонкая!), контрастная, хорошо видимая. Тонкая/незаметная линия = ОШИБКА.
109053
109843
 
109054
109844
  ЯЗЫК + ТОН:
109055
- - ВСЕ слова строго на языке ${generateGeo} (HOOK/буллеты/CTA/скидка/опциональный бейдж срочности/опциональные trust‑печати). Английские слова запрещены. Исключений нет.
109845
+ - ВСЕ слова строго на языке ${generateGeo} (HOOK/буллеты/CTA/скидка/опциональный бейдж срочности; отдельные читаемые подписи рядом с trust‑печатью если есть). Микротекст по кругу декоративной печати можно не разбирать — не смешивай с рекламным текстом. Английские слова в основном слое запрещены. Исключений нет.
109056
109846
  - Тон: убедительный, но честный. Лёгкая эмоция и срочность — ок.
109057
109847
 
109848
+ ЛЕКСИКА И ОРФОГРАФИЯ ЦЕЛЕВОГО ЯЗЫКА (КРИТИЧНО — частая ошибка генераторов):
109849
+ - На макете допустимы только **обычные словарные слова** языка ${generateGeo}. Запрещены выдуманные формы и «склейки» под слово («DISCONTA», «DESCOUNTO» и любые несуществующие варианты вместо нормального слова «скидка» — ОШИБКА).
109850
+ - Если нужна подпись рядом со скидкой кроме «-50%»: используй **одно короткое проверенное слово** языка рынка (например для испанского — «DESCUENTO» / «OFERTA»). Если не уверен в написании — не пиши слово на бейдж: оставь на красной/яркой плашке только крупное «-50%» и процент без длинного текста.
109851
+ - Не подставляй **английские слова вместо перевода** и не оставляй «ложных друзей»: на испаноязычном макете не «COMFORT», «CONTROL» в значении «контроль» под английским влиянием и т.п. — только нормальная лексика целевого языка (для комфорта на испанском чаще «CONFORT», «COMODIDAD», «BIENESTAR» и т.д., по контексту).
109852
+ - Идеи из англоязычного «пула смыслов» ниже берёшь только как смысл; **на картинке печатаешь полностью переведённые фразы на ${generateGeo}**, без английских корней в видимом тексте.
109853
+
109058
109854
  HOOK / HEADLINE (строгое правило):
109059
109855
  - 1–4 строки; до 12 слов. Без переноса внутри слова
109060
- - ОБЯЗАТЕЛЬНО содержит ключевое слово проблемы ЯВНО. Примеры по категориям: простата простатит, простата; похудение лишний вес, похудение; суставы боль в суставах, колени, скованность; пищеварение → дискомфорт, вздутие, тяжесть; сон → бессонница, усталость. Без ключевого слова — ОШИБКА
109856
+ - Явная привязка к теме продукта из брифа: узнаваемая боль, ситуация или конкретная выгода **в той же нише**, не общий слоган и не тема другого оффера. Иначе — ОШИБКА
109857
+ - ЗАПРЕТ НА «ПУСТОЕ» СОЦДОКАЗАТЕЛЬСТВО В HOOK: нельзя делать заголовок **только** из массового доверия/рейтинга и срочности **без** явных маркеров **назначения продукта из брифа** (возьми короткие слова из темы задания: что лечим/что улучшаем — не общие «организм», «поддержка», «лучше»). Читатель по первой строке HOOK должен понять категорию оффера; для другой ниши — свои маркеры на языке ${generateGeo} (слим: figura/peso/…; детокс/очищение/паразиты: очищение/детокс/кишечник/… по смыслу брифа). Соцдоказательство — **вторая строка** или буллеты; верхний HOOK — сначала нишевая боль/результат.
109858
+ - НЕ ТАК: только «98% POZYTYWNYCH OPINII — TYLKO DZIŚ!» без слов про очищение/детокс/паразитов из брифа. НЕ ТАК: только «¡MÁS DE 10.000 CONFÍAN…!» + «-50%» без привязки к теме. ТАК: первая строка HOOK с маркером нише из брифа; проценты/«N tys.» при желании второй строкой или в буллете.
109061
109859
  - HOOK ВЫШЕ всех элементов (продукт, буллеты, CTA, цена). Самый крупный текстовый элемент на креативе
109062
109860
  - HOOK должен быть ВИЗУАЛЬНО “тяжёлым”: ОЧЕНЬ крупный, ТОЛСТЫЙ/жирный шрифт, весь текст заголовка ПРОПИСНЫМИ буквами на языке ${generateGeo}, на яркой контрастной плашке/подложке. Это верхний главный элемент, цепляющий внимание в ленте
109063
109861
  - Можно (не обязательно) включить СРОК прямо в HOOK (например «7 дней», «14 дней» — перенеси число и слово «дней» на язык ${generateGeo}) как триггер
109064
109862
  - Заголовок должен быть РЕЗКИМ и призывным: короткие рубленые фразы, вопросы и предупреждения допустимы. Можно использовать обращение на «ты» (естественное для языка ${generateGeo}) и вопросительный знак
109065
- - заголовок НЕ должен быть абстрактным слоганом/лозунгом или метафорой без конкретной боли. Допускается «thumb‑stop» стиль (вызов/вопрос/предупреждение), если это конкретно и релевантно категории
109863
+ - заголовок НЕ должен быть абстрактным слоганом/лозунгом или метафорой без конкретной боли. Допускается «thumb‑stop» стиль (вызов/вопрос/предупреждение), если это конкретно и в теме продукта из брифа
109066
109864
  - если не влезает: сначала измени композицию (расширь блок/переставь элементы), затем перепиши короче. НЕЛЬЗЯ уменьшать шрифт. HOOK всегда остаётся выше всех остальных элементов
109067
109865
  - Не повторяй один и тот же заголовок в разных подходах: для текущего подхода придумай новый, уникальный заголовок. Варьируй: угол боли, формулировку, акцент (проблема vs облегчение)
109068
109866
  - Вторая часть заголовка = конкретное улучшение, не абстрактное слово. ПРАВИЛЬНО (структура; на макете — только язык ${generateGeo}): боль/вопрос + конкретное облегчение. НЕ ТАК: размытые «легче», «лучше», «решение» без конкретики
109069
- - Специфические медицинские термины или диагнозы разрешены в заголовке, но не обязательны. Варьируй между прямыми формулировками (если релевантно категории) и мягкими формулировками, фокусирующимися на симптомах и дискомфорте, для разных креативов. Не используй один и тот же термин во всех креативах
109070
- ПРИМЕР (русский — только формат и ритм; на макете пиши строго на языке ${generateGeo}, не копируй русский текст, если GEO не русскоязычный):
109071
- - НЕ ТАК: «Борьба с токсинами» (лозунг, абстрактно)
109072
- - ТАК (пищеварение): «Вздутие мучает? Легче к вечеру»
109073
- - ТАК (суставы): «Боль в суставах? Двигайся свободнее»
109074
- - ТАК (сон): «Бессонные ночи? Выспишься наконец»
109075
- - ТАК (простата): «Простата беспокоит? Быстрое облегчение»
109076
- - ТАК (похудение): «Лишний вес? Без жёстких диет»
109077
- ❗ Если заголовок > 12 слов, похож на длинное предложение или звучит как лозунг без ключевого слова проблемы — ОШИБКА → пересоздай вариант.
109867
+ - Специфические медицинские термины или диагнозы разрешены в заголовке, но не обязательны. Варьируй между прямыми формулировками (если уместно для продукта) и мягкими формулировками, фокусирующимися на симптомах и дискомфорте, для разных креативов. Не используй один и тот же термин во всех креативах
109868
+ ПРИМЕР структуры (русский — только ритм; на макете строго язык ${generateGeo}, формулировки подбирай по смыслу продукта, не копируй чужие ниши из инструкции):
109869
+ - НЕ ТАК: общий лозунг без узнаваемой боли/ситуации из темы продукта
109870
+ - ТАК: короткая цепочка «узнаваемая боль или ситуация → надежда/облегчение» в **той же теме**, что и продукт
109871
+ Если заголовок > 12 слов, похож на длинное предложение или остаётся абстрактным без привязки к нише продукта — ОШИБКА → пересоздай вариант.
109872
+ Если HOOK — только доверие/рейтинг без нишеевого крючка (см. «ЗАПРЕТ НА ПУСТОЕ СОЦДОКАЗАТЕЛЬСТВО») ОШИБКА пересоздай.
109078
109873
 
109079
109874
  BULLETS (жёсткое правило, ровно 3):
109080
109875
  - По умолчанию: ровно 3 буллета
@@ -109083,14 +109878,14 @@ BULLETS (жёсткое правило, ровно 3):
109083
109878
  - ВИЗУАЛ БУЛЛЕТОВ: крупные, с иконками/галочками (✓), на контрастных подложках. Буллеты ВНЕ упаковки/банки — не на продукте.
109084
109879
  - Буллиты должны быть расположены ВЕРТИКАЛЬНО (столбиком), не горизонтально в одну строку. Минимальный размер шрифта буллитов — чтобы читались на экране телефона без зума
109085
109880
 
109086
- ❗ ВЫБОР БУЛЛЕТОВ (КРИТИЧНО): Если выше по промпту задан блок «ОБЯЗАТЕЛЬНЫЕ БУЛЛЕТЫ» — используй ТОЛЬКО его (перевод на язык ${generateGeo}). Если такого блока нет — выбери 3 случайных смысла из пула ниже; пул на английском только для смысла, на макете **ноль английского** — короткие фразы строго на языке ${generateGeo}. Не копируй на макет язык примеров из этого промпта, кроме целевого ${generateGeo}.
109881
+ ❗ ВЫБОР БУЛЛЕТОВ (КРИТИЧНО): Если выше по промпту задан блок «ОБЯЗАТЕЛЬНЫЕ БУЛЛЕТЫ» — используй его как **три опорных смысла**: переведи на язык ${generateGeo} коротко (2–4 слова), но **перепиши формулировки под тему продукта из брифа**, если исходный смысл не совпадает с нишей (тип выгоды сохрани — «срок / соцдоказательство / состав / комфорт» — слова подставь свои под оффер). Если такого блока нет — выбери 3 смысла из нейтрального пула ниже и тоже адаптируй под продукт; пул только для категории смысла, на макете **ноль английского**. Не копируй дословно строки пула, если они тянут в другую медицинскую вертикаль.
109087
109882
 
109088
- ПУЛ СМЫСЛОВ (английский не печатать; переписать на ${generateGeo}, 2–4 слова каждый):
109089
- • Action/benefit: less discomfort, less bloating, less pain, fast relief, daily comfort, easier movement, fewer symptoms, pain-free, calm stomach, calm nights, visible result
109090
- • Time/speed: effect in 7–14 days, effect in 2 weeks, fast result, relief from day one, faster, from first day, in the first days
109091
- • Social proof: 9 of 10 recommend, 9 of 10 men recommend, 93% confirm, 8 of 10 satisfied, thousands happy customers
109092
- • Formula: natural formula, natural ingredients, no harsh chemicals, selected composition, dermatologically tested
109093
- • Quality of life: active comfort, worry-free days, more energy, restful sleep, easier life
109883
+ ПУЛ СМЫСЛОВ — нейтральный, без нишевых якорей (английский не печатать; переписать на ${generateGeo}, 2–4 слова каждый; смысл должен совпасть с продуктом из «🏷️ ПРОДУКТ»):
109884
+ • Action/benefit: daily body support, everyday comfort, gentle formula support, noticeable wellness support, for regular use, natural support, visible difference over time, more self-confidence
109885
+ • Time/speed: effect in 7–14 days, effect in 2 weeks, fast perceived result, support from day one, effect in 7 days, noticeable soon, from first days of use
109886
+ • Social proof: 9 of 10 recommend, 9 of 10 men recommend, thousands already chose, 8 of 10 satisfied with results, high buyer ratings
109887
+ • Formula: natural formula, natural ingredients, no unnecessary harsh additives, carefully selected composition, thoughtful ingredient blend
109888
+ • Quality of life: calmer every day, more energy, more confidence, steadier well-being, supports what matters to you
109094
109889
 
109095
109890
  ❗ Если хоть один буллет > 4 слов или похож на предложение/обещание — ОШИБКА → пересоздай.
109096
109891
 
@@ -109098,7 +109893,7 @@ TEXT LIMIT:
109098
109893
  - кроме: HOOK, 3 буллетов, цены, скидки, кнопки — ЛЮБОЙ другой текст запрещён (ярлыки/подписи/дисклеймеры/пояснения)
109099
109894
  - ИСКЛЮЧЕНИЕ: если 🎯 ПОДХОД = ${noBulletsCond} → кроме HOOK, цены, скидки, CTA — НИКАКОГО другого текста. Буллиты запрещены. Other_text/urgency/trust‑печати запрещены
109100
109895
  - ДОПУСКАЕТСЯ (не обязательно) ОДИН бейдж срочности: только ясные формулировки («только сегодня», «последние штуки», «акция до конца дня»). ЗАПРЕЩЕНО: «24h» — непонятно что означает.
109101
- - ДОПУСКАЕТСЯ (не обязательно) 1–3 trust‑печати: печати цветные, яркие, в стиле FDA. Текст СТРОГО на языке ${generateGeo} запрещены английские слова («NATURAL», «QUALITY» и т.д.). По смыслу: «натуральный», «натуральный состав» оформи короткой фразой на языке ${generateGeo}, не латиницей. Размер как минимум как у буллитов. Мелкие печати — ОШИБКА.
109896
+ - ДОПУСКАЕТСЯ (не обязательно) 1–3 trust‑печати: цветные, яркие, в стиле FDA; микротекст по ободу круга может быть декоративным и нечитаемым. Если делаешь **отдельную** короткую подпись рядом с печатью (не на круге) только на языке ${generateGeo}, без английского («NATURAL», «QUALITY» и т.д.).
109102
109897
  - HOOK + буллеты <= 24 слова суммарно (HOOK до 12, буллеты до 12); если больше — ОШИБКА → пересоздай
109103
109898
  - Если хочешь добавить ощущение срочности — делай это через формулировки в HOOK/BULLETS, через реквизит/иконки, ИЛИ (не обязательно) через один короткий бейдж срочности. Только ясные формулировки на языке ${generateGeo} (уровень смысла: «последние штуки», «только сегодня», «акция до конца дня»). ЗАПРЕЩЕНО «24h» — непонятно что означает.
109104
109899
 
@@ -109110,13 +109905,13 @@ CTA > PRICE:
109110
109905
  ❗ ИСКЛЮЧЕНИЕ: если 🎯 ПОДХОД = ${noBulletsCond} → цена и «-50%» могут быть более крупными и заметными, но CTA всё равно должен оставаться очень заметным и выглядеть как кнопка (не теряется на фоне)
109111
109906
 
109112
109907
  ПОКАЗЫВАЙ ТОЛЬКО ЭТИ ТЕКСТОВЫЕ ЭЛЕМЕНТЫ:
109113
- - HOOK (1–4 строки; выше всех элементов; самый крупный; прописные буквы; жирный; на яркой контрастной подложке; ключевое слово проблемы явно)
109908
+ - HOOK (1–4 строки; выше всех элементов; самый крупный; прописные буквы; жирный; на яркой контрастной подложке; привязка к теме продукта (боль/ситуация), не абстракция)
109114
109909
  - 3 буллета (крупные, с иконками/галочками ✓, на контрастных подложках, НЕ на банке/упаковке)
109115
- - Цена: ОБЯЗАТЕЛЬНО ДВЕ — старая (2×${generatePrice} ${generateCurrency}) зачёркнута ТОЛСТОЙ контрастной линией + новая ${generatePrice} ${generateCurrency} выразительно. Одна цена = ОШИБКА. Тонкая линия зачёркивания = ОШИБКА. Без слов. Не на упаковке.
109116
- - Скидка: «-50%» ОБЯЗАТЕЛЬНО отдельным видимым бейджем (не только в цене!). Процент скидки должен быть явно читаем. Ярко, заметно, строго НЕ на банке/упаковке, визуально слабее CTA
109910
+ - Цена: ОБЯЗАТЕЛЬНО ДВЕ — старая **только как число + валюта** (сумма вдвое больше новой; при новой ${generatePrice} ${generateCurrency} на макете старая — удвоенное число и тот же символ валюты) зачёркнута ТОЛСТОЙ контрастной линией + новая ${generatePrice} ${generateCurrency} выразительно. **Без** «2×»/«2x» перед суммой. Одна цена = ОШИБКА. Тонкая линия зачёркивания = ОШИБКА. Без слов. Не на упаковке.
109911
+ - Скидка: «-50%» ОБЯЗАТЕЛЬНО отдельным видимым бейджем (не только в цене!). Процент скидки должен быть явно читаем. Ярко, заметно, строго НЕ на банке/упаковке, визуально слабее CTA. На этом бейдже допустимо только «-50%» и при необходимости одно короткое **словарное** слово языка ${generateGeo} для «скидки» — без выдуманных написаний; если сомневаешься — только «-50%»
109117
109912
  - Кнопка CTA (1-2 слова)
109118
109913
  - Опционально: 1 короткий бейдж срочности (ясные формулировки: «только сегодня», «последние штуки»; ЗАПРЕЩЕНО «24h»). Бейдж в углу кадра, контрастный, но меньше CTA. НЕ обязателен
109119
- - Опционально: 1–3 trust‑печати (цветные, яркие, в стиле FDA; натуральность, премиальность, безопасность). Текст СТРОГО на языке ${generateGeo} никакого английского. По смыслу: «натуральный», «натуральный состав», «качество» — коротко на ${generateGeo}; НЕ «NATURAL», «QUALITY». Размер как минимум как у буллитов. НЕ обязательны
109914
+ - Опционально: 1–3 trust‑печати (цветные, яркие, в стиле FDA). Можно только «медаль» с мелким неразборчивым ободом; опционально одна-две короткие подписи рядом строкой футера на ${generateGeo} («натуральный», «качество»); не «NATURAL», «QUALITY». НЕ обязательны
109120
109915
  - ИСКЛЮЧЕНИЕ: если 🎯 ПОДХОД = ${noBulletsCond} → показывай только: HOOK + PRICE + DISCOUNT + CTA. Буллиты/urgency/trust‑печати запрещены
109121
109916
 
109122
109917
  ВИЗУАЛ:
@@ -109128,33 +109923,34 @@ CTA > PRICE:
109128
109923
  - КОНТРАСТ ПЛАШЕК: каждая текстовая плашка (HOOK, буллеты, CTA, цена) должна иметь высокий контраст с фоном — светлый текст на тёмной подложке ИЛИ тёмный текст на светлой. Плашка, сливающаяся с фоном = ОШИБКА → добавь подложку или измени цвет фона/плашки.
109129
109924
  - АНГЛИЙСКИЙ НА УПАКОВКЕ: если на этикетке продукта есть английский текст (например название/описание на упаковке) — не приближай её настолько, чтобы эти надписи были читабельны как отдельный контент. Упаковка = визуальный объект; мелкий этикеточный текст должен быть нечитабелен или едва различим.
109130
109925
  - Без ссылок/доменов и мелкого текста
109131
- - LIFESTYLE/CLEAN: продукт в контексте использования. Для пищеварения кухня, обеденный стол, рядом с едой/чашкой. Для сна/простаты спальня, тумбочка, стакан воды. Для суставов стол/тумбочка рядом с эластичным бинтом, ортезом или активным фоном (кроссовки, лестница). Контекст = момент приёма. Просто продукт на белом фоне — НЕ допускается
109926
+ - LIFESTYLE/CLEAN: продукт в контексте использования, который читатель свяжет с **этим** продуктом по брифу (момент приёма, быт, активностьпо смыслу оффера). Просто продукт на белом фоне — НЕ допускается
109132
109927
  - Цвета и композиция должны быть «thumb‑stop»: высокий контраст, яркий акцент на продукте (луч света/подсветка/обводка/глоу), избегай бледных пастельных сцен
109133
109928
  - ЛЮДИ: человек допускается ТОЛЬКО для подходов «Эмоция / Портрет» и «Врач / Эксперт». Для всех остальных — БЕЗ человека.
109134
- - Если «Эмоция / Портрет»: прямой взгляд в камеру, эмоция облегчения/надежды. Возраст 50–60 (похудение/фитнес — 35–55). Этнически соответствует GEO/рынку.
109929
+ - Если «Эмоция / Портрет»: прямой взгляд в камеру, эмоция облегчения/надежды. Возраст подбери под ЦА из брифа (часто 50–60; если продукт явно для более молодой аудитории — 35–55). Этнически соответствует GEO/рынку.
109135
109930
  - Если «Врач / Эксперт»: женщина-врач 40–55 лет. Внешность и этничность СТРОГО соответствуют GEO/рынку (локал — врач выглядит как местный специалист).
109136
109931
 
109137
109932
  ANTI-TEMPLATE DIVERSITY (КРИТИЧНО):
109138
109933
  - Ты сейчас создаёшь ОДИН креатив для текущего подхода. В проекте будет серия из ${totalApproaches} креативов, поэтому у каждого подхода должна быть своя узнаваемая композиция.
109139
109934
  - Для ЭТОГО креатива НЕ используй универсальный шаблон. Обязательно вариируй: (1) крупность/кроп (close-up vs medium vs flat-lay), (2) угол камеры (прямо vs сверху vs диагональ), (3) расположение блоков HOOK/BULLETS/CTA/PRICE/DISCOUNT, (4) свет/фон (но без лишнего текста).
109140
109935
  - Ориентируйся на строку «🎯 ПОДХОД: ...» и примени соответствующую раскладку ниже (только одну, соответствующую текущему подходу):
109141
- * 🎯 ПОДХОД: Эксперт / Авторитет → БЕЗ человека. Профессиональный контекст с реквизитом экспертизы вокруг продукта. Продукт в центре. HOOK сверху справа, BULLETS справа ниже, CTA снизу справа, PRICE/DISCOUNT рядом с CTA (слабее CTA). Trust‑печати (если есть) — компактно по низу, размер как минимум как у буллитов (крупные, читабельны на телефоне).
109142
- * 🎯 ПОДХОД: Lifestyle / Момент приёма → FLAT-LAY/СВЕРХУ или 3/4 сверху на столе/тумбочке. Продукт в центре, реквизит "момент приёма" вокруг. HOOK сверху по центру, BULLETS сбоку (вертикально, шрифт достаточно крупный для чтения на телефоне без зума), CTA снизу по центру, PRICE/DISCOUNT рядом (слабее CTA). Urgency‑бейдж (если есть) — в углу, только ясные формулировки, НЕ «24h». Trust‑печати (если есть) — компактно по низу, размер как минимум как у буллитов (крупные, читабельны на телефоне).
109936
+ * 🎯 ПОДХОД: Эксперт / Авторитет → БЕЗ человека. Профессиональный контекст с реквизитом экспертизы вокруг продукта. Продукт в центре. HOOK сверху справа, BULLETS справа ниже, CTA снизу справа, PRICE/DISCOUNT рядом с CTA (слабее CTA). Trust‑печати (если есть) — компактно по низу; круг может быть декоративным с мелким текстом.
109937
+ * 🎯 ПОДХОД: Lifestyle / Момент приёма → FLAT-LAY/СВЕРХУ или 3/4 сверху на столе/тумбочке. Продукт в центре, реквизит "момент приёма" вокруг. HOOK сверху по центру, BULLETS сбоку (вертикально, шрифт достаточно крупный для чтения на телефоне без зума), CTA снизу по центру, PRICE/DISCOUNT рядом (слабее CTA). Urgency‑бейдж (если есть) — в углу, только ясные формулировки, НЕ «24h». Trust‑печати (если есть) — компактно по низу; декоративный круг с мелким текстом ок.
109143
109938
  * 🎯 ПОДХОД: Эмоция / Портрет → Один из двух подходов с человеком. ОЧЕНЬ крупный портрет лица (thumb‑stop), взгляд в камеру — лицо занимает верхние 2/3 кадра. Продукт в руке или у подбородка. HOOK сверху по центру. BULLETS, CTA, PRICE/DISCOUNT, trust‑печати — в нижней трети.
109144
109939
  * 🎯 ПОДХОД: Врач / Эксперт → Женщина-врач (локал по GEO), продукт в руках или рядом. Профессиональный фон. HOOK сверху, BULLETS сбоку/снизу, CTA и PRICE/DISCOUNT внизу. Врач и продукт — главные элементы.
109145
109940
  * 🎯 ПОДХОД: Скрин отзывов → Имитация скриншота: блок отзывов (аватарки, имена, возраст, 5 звёзд, текст на языке GEO). HOOK ОБЯЗАТЕЛЕН — выше отзывов или поверх, крупно. Продукт виден. CTA, PRICE, DISCOUNT внизу.
109146
- * 🎯 ПОДХОД: Визуализация проблемы → Инфографика/схема: слева проблема (иконка/схема зоны тела, релевантной продукту), справа решение + продукт. HOOK сверху по центру, BULLETS справа или снизу (вертикально), CTA снизу справа, PRICE/DISCOUNT возле CTA. Trust‑печати (если есть) — компактно по низу, размер как минимум как у буллитов (крупные, читабельны на телефоне). Красный акцент только на проблеме.
109147
- * 🎯 ПОДХОД: Power / Сила решения → БЕЗ человека. ДИНАМИЧНЫЙ комикс‑кадр, диагональная композиция. Продукт как “герой” + power‑иконки (щит/молния/бёрст). HOOK сверху слева на яркой плашке, BULLETS слева ниже, CTA снизу справа, PRICE/DISCOUNT возле CTA. Trust‑печати (если есть) — компактно по низу, размер как минимум как у буллитов (крупные, читабельны на телефоне).
109148
- * 🎯 ПОДХОД: Минимализм / Clean Big Text → Белый/градиентный фон, минимум элементов. ОГРОМНЫЙ HOOK занимает верх/центр, продукт крупно (центр/право), CTA снизу по центру, PRICE/DISCOUNT рядом (слабее CTA). Trust‑печати (если есть) — размер как минимум как у буллитов, крупные и читабельные.
109149
- * 🎯 ПОДХОД: Problem Visual Punch → БЕЗ человека. Яркий сплошной фон (красный/оранжевый) или агрессивный градиент. В центре КРУПНАЯ иконка/схема зоны тела, релевантной продукту (для суставов колено; для пищеварения — желудок), с красным свечением. Продукт крупно рядом. HOOK 1–4 строки, до 12 слов, огромный прописной текст, выше всех. БЕЗ буллитов. Две цены + «-50%» крупно и заметно. Кнопка призыва контрастная (только текст на языке ${generateGeo}, без слова CTA)
109941
+ * 🎯 ПОДХОД: Визуализация проблемы → Инфографика/схема: слева проблема (иконка или схема, уместная для продукта по брифу), справа решение + продукт. HOOK сверху по центру, BULLETS справа или снизу (вертикально), CTA снизу справа, PRICE/DISCOUNT возле CTA. Trust‑печати (если есть) — компактно по низу; декоративный круг с мелким текстом ок. Красный акцент только на проблеме.
109942
+ * 🎯 ПОДХОД: Power / Сила решения → БЕЗ человека. ДИНАМИЧНЫЙ комикс‑кадр, диагональная композиция. Продукт как “герой” + power‑иконки (щит/молния/бёрст). HOOK сверху слева на яркой плашке, BULLETS слева ниже, CTA снизу справа, PRICE/DISCOUNT возле CTA. Trust‑печати (если есть) — компактно по низу; декоративный круг с мелким текстом ок.
109943
+ * 🎯 ПОДХОД: Минимализм / Clean Big Text → Белый/градиентный фон, минимум элементов. ОГРОМНЫЙ HOOK занимает верх/центр, продукт крупно (центр/право), CTA снизу по центру, PRICE/DISCOUNT рядом (слабее CTA). Trust‑печати (если есть) — по желанию, без требования читать микротекст на круге.
109944
+ * 🎯 ПОДХОД: Problem Visual Punch → БЕЗ человека. Яркий сплошной фон (красный/оранжевый) или агрессивный градиент. В центре КРУПНАЯ иконка/схема проблемы, уместная для продукта по брифу, с красным свечением. Продукт крупно рядом. HOOK 1–4 строки, до 12 слов, огромный прописной текст, выше всех. БЕЗ буллитов. Две цены + «-50%» крупно и заметно. Кнопка призыва контрастная (только текст на языке ${generateGeo}, без слова CTA)
109150
109945
  * 🎯 ПОДХОД: Любительский Примитивизм → БЕЗ человека. Чёрный или кислотный сплошной фон. Продукт по центру или справа без декора. HOOK сверху — 1–4 строки, до 12 слов, крупный, грубый bold/рукописный, прописные буквы, выше всех. Крупные надписи цены/скидки вокруг продукта. Кнопка призыва — плоская, без градиентов (только короткая фраза на языке ${generateGeo}). БЕЗ буллитов, бейджей, теней, иконок.
109151
109946
 
109152
109947
  AUTO-CHECK (перед финалом):
109153
- - HOOK: 1–4 строки, до 12 слов, прописные буквы, жирный на контрастной плашке, выше всех элементов, ключевое слово проблемы явно, язык ${generateGeo} без английских слов (включая метки HOOK/CTA/CAPS)
109154
- - Буллеты: по умолчанию ровно 3, каждый <= 4 слов, без запятых, не предложения. ИСКЛЮЧЕНИЕ: если 🎯 ПОДХОД = ${noBulletsCond} буллетов 0 (запрещены)
109948
+ - HOOK: 1–4 строки, до 12 слов, прописные буквы, жирный на контрастной плашке, выше всех элементов, привязка к теме продукта (боль/ситуация), не абстракция, язык ${generateGeo} без английских слов (включая метки HOOK/CTA/CAPS). Не только «тысячи доверяют / N из 10» без маркеров нише; не «абстрактный процент подтверждений» без связи с темой продукта
109949
+ - Скидка/буллеты: нет псевдослов и «английского под видом локали»; на бейдже скидки либо только «-50%», либо словарное слово; буллеты только реальная лексика ${generateGeo}
109950
+ - Буллеты: по умолчанию ровно 3, каждый <= 4 слов, без запятых, не предложения; смысл **строго в нише продукта из брифа**, без перекрёста с другими вертикалями (суставы/ЖКТ/«боль» без повода и т.п.). ИСКЛЮЧЕНИЕ: если 🎯 ПОДХОД = ${noBulletsCond} → буллетов 0 (запрещены)
109155
109951
  - Нет лишнего текста кроме: HOOK, буллетов, цены, скидки, кнопки (+ опционально 1 бейдж срочности + опционально 1–3 trust‑печати)
109156
109952
  - Есть скидка «-50%» (вне упаковки) и она слабее CTA
109157
- - Цена: ОБЯЗАТЕЛЬНО ДВЕ (старая + новая). Старая зачёркнута ТОЛСТОЙ контрастной линией. Одна цена = ОШИБКА. Тонкая/незаметная линия зачёркивания = ОШИБКА
109953
+ - Цена: ОБЯЗАТЕЛЬНО ДВЕ (старая + новая). Старая зачёркнута ТОЛСТОЙ контрастной линией. Одна цена = ОШИБКА. Тонкая/незаметная линия зачёркивания = ОШИБКА. На ценниках **нет** префикса «2×»/«2x» у суммы
109158
109954
  - CTA визуально сильнее цены/скидки
109159
109955
  - Контраст: каждая текстовая плашка читабельна на телефоне — ни одна не сливается с фоном
109160
109956
  Если хоть один пункт нарушен — пересоздай креатив.
@@ -109162,7 +109958,7 @@ AUTO-CHECK (перед финалом):
109162
109958
  ВАЖНО: Твой ответ — это ИЗОБРАЖЕНИЕ, не текст. Не пиши план, не перечисляй элементы текстом, не рассуждай, не вызывай tools. Сразу генерируй визуальный креатив как картинку.`;
109163
109959
  }
109164
109960
  /**
109165
- * Развёрнутый список задач крео: каждый подход повторяется N раз (0–4), N = imageApproachCounts[i].
109961
+ * Развёрнутый список задач крео: каждый подход повторяется N раз (0–10), N = imageApproachCounts[i].
109166
109962
  * poolIndex — индекс строки в CREO_APPROACHES (0-based); в UI таблице это номер строки poolIndex + 1 (1–10).
109167
109963
  */
109168
109964
  function getCreoApproachExpandedTasks() {
@@ -109197,38 +109993,38 @@ function getCreoApproaches() {
109197
109993
  const CREO_APPROACHES = [
109198
109994
  {
109199
109995
  name: 'Эксперт / Авторитет',
109200
- prompt: `ЭКСПЕРТ / АВТОРИТЕТ (без человека): стиль “рекомендация эксперта”, но БЕЗ человека. Профессиональный контекст — аптечные полки на фоне / медицинский планшет / стетоскоп / рецептурный блокнот как реквизит рядом с продуктом. Продукт в центре как “рекомендованное решение”. Чистый профессиональный фон, высокий контраст. Trust‑печати (1–3 шт): цветные, яркие, очень похожие на печать FDA; подчёркивают натуральность состава, премиальность, безопасность. Размер как минимум как у буллитов крупные, читабельны на телефоне. Даже одна печать — крупная.`,
109201
- headlineAngle: `HOOK: угол «предупреждение / интрига / специалисты знают». Формат: вопрос‑предупреждение ИЛИ “специалисты знают/используют” (адаптируй под GEO). 1–4 строки, до 12 слов, прописные буквы, ключевое слово проблемы явно.`,
109996
+ prompt: `ЭКСПЕРТ / АВТОРИТЕТ (без человека): стиль “рекомендация эксперта”, но БЕЗ человека. Профессиональный контекст — аптечные полки на фоне / медицинский планшет / стетоскоп / рецептурный блокнот как реквизит рядом с продуктом. Продукт в центре как “рекомендованное решение”. Чистый профессиональный фон, высокий контраст. Trust‑печати (1–3 шт): цветные, яркие, в стиле FDA; допускается декоративный круг с мелким нечитаемым ободом; опционально короткие подписи рядом строкой на языке GEO.`,
109997
+ headlineAngle: `HOOK: угол «предупреждение / интрига / специалисты знают». Формат: вопрос‑предупреждение ИЛИ “специалисты знают/используют” (адаптируй под GEO). 1–4 строки, до 12 слов, прописные буквы, привязка к теме продукта (боль/ситуация), не абстракция.`,
109202
109998
  bulletsFocus: `БУЛЛИТЫ: выбери 3 СЛУЧАЙНЫХ из пула (действие + срок + соц.доказательство/формула) в ПРОИЗВОЛЬНОМ порядке. Уникальный набор для этого подхода — не повторяй комбинации из других креативов.`
109203
109999
  },
109204
110000
  {
109205
110001
  name: 'Lifestyle / Момент приёма',
109206
- prompt: `LIFESTYLE / МОМЕНТ ПРИЁМА: продукт в контексте использования (кухня/стол/спальня/тумбочка). Без человека, но с "историей" (стакан воды, чай, тарелка, будильник/часы/календарь). Срочность: реквизит (часы/таймер‑иконка) и/или опциональный urgency‑бейдж (1–3 слова) в углу кадра — только ясные формулировки («только сегодня», «последние штуки»), ЗАПРЕЩЕНО «24h». Высокий контраст, яркий акцент на продукте. Опционально: 1–3 trust‑печати по низу (цветные, яркие, очень похожие на печать FDA; натуральность/премиальность/безопасность). Размер как минимум как у буллитов — крупные, читабельны на телефоне. Даже одна печать — крупная. ВАЖНО: буллиты располагаются вертикально и всегда читабельны на экране телефона без зума — даже если уходят вбок, минимальный размер шрифта буллитов не уменьшается. Если буллиты не вмещаются сбоку с нужным шрифтом — переставь их вниз.`,
109207
- headlineAngle: `HOOK: угол «цифры + срочность». Формат: число/процент + ограничение времени/“сейчас/сегодня” (адаптируй под GEO), 1–4 строки, до 12 слов, прописные буквы, ключевое слово проблемы явно.`,
110002
+ prompt: `LIFESTYLE / МОМЕНТ ПРИЁМА: продукт в контексте использования (кухня/стол/спальня/тумбочка). Без человека, но с "историей" (стакан воды, чай, тарелка, будильник/часы/календарь). Срочность: реквизит (часы/таймер‑иконка) и/или опциональный urgency‑бейдж (1–3 слова) в углу кадра — только ясные формулировки («только сегодня», «последние штуки»), ЗАПРЕЩЕНО «24h». Высокий контраст, яркий акцент на продукте. Опционально: 1–3 trust‑печати по низу (цветные, яркие, FDA‑стиль; декоративный микротекст по кругу допустим). ВАЖНО: буллиты располагаются вертикально и всегда читабельны на экране телефона без зума — даже если уходят вбок, минимальный размер шрифта буллитов не уменьшается. Если буллиты не вмещаются сбоку с нужным шрифтом — переставь их вниз.`,
110003
+ headlineAngle: `HOOK: угол «цифры + срочность». Формат: число/процент + ограничение времени/“сейчас/сегодня” (адаптируй под GEO), 1–4 строки, до 12 слов, прописные буквы, привязка к теме продукта (боль/ситуация), не абстракция.`,
109208
110004
  bulletsFocus: `БУЛЛИТЫ: выбери 3 СЛУЧАЙНЫХ из пула (срок + цифра + действие/формула) в ПРОИЗВОЛЬНОМ порядке. Уникальный набор — не повторяй комбинации из других креативов.`
109209
110005
  },
109210
110006
  {
109211
110007
  name: 'Эмоция / Портрет',
109212
- prompt: `ЭМОЦИЯ / ПОРТРЕТ: один из двух подходов с человеком в серии. Используй максимально: ОЧЕНЬ крупный план лица (thumb‑stop), прямой взгляд в камеру, сильная эмоция облегчения/надежды (без широкой стоковой улыбки). Возраст человека подбери под категорию и ЦА продукта: по умолчанию 50–60, но для категорий с более молодой аудиторией (например похудение/фитнес) допускается 35–55. Продукт в руке на уровне лица или рядом, хорошо виден. Свет "тень → свет" на лице допустим. Минимум отвлекающих деталей, высокий контраст. Опционально: 1–3 trust‑печати по низу (цветные, яркие, очень похожие на печать FDA; натуральность/премиальность/безопасность). Размер как минимум как у буллитов — крупные, читабельны на телефоне. Даже одна печать — крупная. ИЕРАРХИЯ: лицо — главный и доминирующий визуальный элемент (верхние 2/3 кадра). Все текстовые блоки (HOOK исключение — сверху) уходят в нижнюю треть. Буллиты, цена, CTA НЕ перекрывают лицо и НЕ конкурируют с ним по визуальному весу — они заметно меньше и ниже.`,
109213
- headlineAngle: `HOOK: угол «персонально на “ты” + результат/облегчение». 1–4 строки, до 12 слов, прописные буквы, ключевое слово проблемы явно. Тон максимально личный и прямой.`,
110008
+ prompt: `ЭМОЦИЯ / ПОРТРЕТ: один из двух подходов с человеком в серии. Используй максимально: ОЧЕНЬ крупный план лица (thumb‑stop), прямой взгляд в камеру, сильная эмоция облегчения/надежды (без широкой стоковой улыбки). Возраст человека подбери под ЦА из брифа: по умолчанию 50–60; если продукт явно для более молодой аудитории допускается 35–55. Продукт в руке на уровне лица или рядом, хорошо виден. Свет "тень → свет" на лице допустим. Минимум отвлекающих деталей, высокий контраст. Опционально: 1–3 trust‑печати по низу (FDA‑стиль; мелкий текст на круге может быть декоративным). ИЕРАРХИЯ: лицо — главный и доминирующий визуальный элемент (верхние 2/3 кадра). Все текстовые блоки (HOOK исключение — сверху) уходят в нижнюю треть. Буллиты, цена, CTA НЕ перекрывают лицо и НЕ конкурируют с ним по визуальному весу — они заметно меньше и ниже.`,
110009
+ headlineAngle: `HOOK: угол «персонально на “ты” + результат/облегчение». 1–4 строки, до 12 слов, прописные буквы, привязка к теме продукта (боль/ситуация), не абстракция. Тон максимально личный и прямой.`,
109214
110010
  bulletsFocus: `БУЛЛИТЫ: выбери 3 СЛУЧАЙНЫХ из пула (качество жизни + действие + срок/цифра) в ПРОИЗВОЛЬНОМ порядке. Уникальный набор — не повторяй комбинации из других креативов.`
109215
110011
  },
109216
110012
  {
109217
110013
  name: 'Визуализация проблемы',
109218
- prompt: `ВИЗУАЛИЗАЦИЯ ПРОБЛЕМЫ (без человека): схема/иконка проблемной зоны тела, строго соответствующей категории продукта (для суставов колено/сустав/позвоночник; для пищеварения — желудок; для простаты — силуэт мужчины) + мягкий красный акцент на этой зоне (не шок‑контент, без графики). Рядом продукт как решение. Можно добавить простую стрелку/переход “проблема → облегчение” как графику (без лишнего текста). Чистый фон, высокая читабельность. Опционально: 1–3 trust‑печати по низу (цветные, яркие, очень похожие на печать FDA; натуральность/премиальность/безопасность). Размер как минимум как у буллитов — крупные, читабельны на телефоне. Даже одна печать — крупная. БЕЗ человека.`,
109219
- headlineAngle: `HOOK: прямой вопрос о боли/дискомфорте (релевантно категории). 1–4 строки, до 12 слов, прописные буквы, ключевое слово проблемы явно, вопросительный знак допустим.`,
110014
+ prompt: `ВИЗУАЛИЗАЦИЯ ПРОБЛЕМЫ (без человека): схема или иконка проблемы, которую читатель свяжет с **этим** продуктом по брифу, + мягкий красный акцент на зоне дискомфорта (не шок‑контент, без графики). Рядом продукт как решение. Можно добавить простую стрелку/переход “проблема → облегчение” как графику (без лишнего текста). Чистый фон, высокая читабельность. Опционально: 1–3 trust‑печати по низу (FDA‑стиль; декоративный круг с мелким ободом ок). БЕЗ человека.`,
110015
+ headlineAngle: `HOOK: прямой вопрос о боли/дискомфорте в теме продукта. 1–4 строки, до 12 слов, прописные буквы, без абстрактного слогана, вопросительный знак допустим.`,
109220
110016
  bulletsFocus: `БУЛЛИТЫ: выбери 3 СЛУЧАЙНЫХ из пула (действие + срок + соц.доказательство) в ПРОИЗВОЛЬНОМ порядке. Уникальный набор — не повторяй комбинации из других креативов.`
109221
110017
  },
109222
110018
  {
109223
110019
  name: 'Power / Сила решения',
109224
- prompt: `POWER / СИЛА РЕШЕНИЯ (без человека): метафоры силы и победы над проблемой: щит, молния, энергия, взрыв‑бёрст, мощные стикеры/иконки. Продукт как “герой” в центре, высокая энергия, контрастные агрессивные цвета. Можно комикс/anti‑design подачу, но всё должно быть читабельно. Без лишнего текста. Опционально: 1–3 trust‑печати по низу (цветные, яркие, очень похожие на печать FDA; натуральность/премиальность/безопасность). Размер как минимум как у буллитов — крупные, читабельны на телефоне. Даже одна печать — крупная. БЕЗ человека. Только продукт + power‑иконки.`,
109225
- headlineAngle: `HOOK: угол «было → стало / победа над проблемой». Можно использовать стрелку “→” как часть перехода. 1–4 строки, до 12 слов, прописные буквы, ключевое слово проблемы явно.`,
110020
+ prompt: `POWER / СИЛА РЕШЕНИЯ (без человека): метафоры силы и победы над проблемой: щит, молния, энергия, взрыв‑бёрст, мощные стикеры/иконки. Продукт как “герой” в центре, высокая энергия, контрастные агрессивные цвета. Можно комикс/anti‑design подачу, но всё должно быть читабельно. Без лишнего текста. Опционально: 1–3 trust‑печати по низу (FDA‑стиль; декоративный круг с мелким ободом ок). БЕЗ человека. Только продукт + power‑иконки.`,
110021
+ headlineAngle: `HOOK: угол «было → стало / победа над проблемой». Можно использовать стрелку “→” как часть перехода. 1–4 строки, до 12 слов, прописные буквы, привязка к теме продукта (боль/ситуация), не абстракция.`,
109226
110022
  bulletsFocus: `БУЛЛИТЫ: выбери 3 СЛУЧАЙНЫХ из пула (действие + скорость + цифра) в ПРОИЗВОЛЬНОМ порядке. Уникальный набор — не повторяй комбинации из других креативов.`
109227
110023
  },
109228
110024
  {
109229
110025
  name: 'Минимализм / Clean Big Text',
109230
- prompt: `МИНИМАЛИЗМ / CLEAN BIG TEXT (без человека): белый или мягкий градиентный фон, премиальное ощущение. ОГРОМНЫЙ HOOK как главный элемент (прописные буквы, жирный, на контрастной плашке). Продукт крупно, минимум реквизита, минимум шума. Тени/премиальные материалы допустимы, но без лишнего текста. БЕЗ человека. Urgency и trust‑печати в этом подходе НЕ рекомендуются — они нарушают чистоту и ощущение премиальности. Добавляй печати только если это критически необходимо, не более 1, но крупную размер как у буллитов.`,
109231
- headlineAngle: `HOOK: одна максимально простая сильная фраза (коротко и ясно), 1–4 строки, до 12 слов, прописные буквы, ключевое слово проблемы явно.`,
110026
+ prompt: `МИНИМАЛИЗМ / CLEAN BIG TEXT (без человека): белый или мягкий градиентный фон, премиальное ощущение. ОГРОМНЫЙ HOOK как главный элемент (прописные буквы, жирный, на контрастной плашке). Продукт крупно, минимум реквизита, минимум шума. Тени/премиальные материалы допустимы, но без лишнего текста. БЕЗ человека. Urgency и trust‑печати в этом подходе НЕ рекомендуются — они нарушают чистоту и ощущение премиальности. Если всё же добавляешь одну печать допускается декоративная медаль с мелким нечитаемым ободом.`,
110027
+ headlineAngle: `HOOK: одна максимально простая сильная фраза (коротко и ясно), 1–4 строки, до 12 слов, прописные буквы, привязка к теме продукта (боль/ситуация), не абстракция.`,
109232
110028
  bulletsFocus: `БУЛЛИТЫ: выбери 3 СЛУЧАЙНЫХ из пула (действие + срок + формула/цифра) в ПРОИЗВОЛЬНОМ порядке. Уникальный набор — не повторяй комбинации из других креативов.`
109233
110029
  },
109234
110030
  {
@@ -109236,16 +110032,16 @@ const CREO_APPROACHES = [
109236
110032
  noBullets: true,
109237
110033
  prompt: `PROBLEM VISUAL PUNCH (без человека): максимально агрессивный и простой креатив.
109238
110034
  - Фон: яркий сплошной цвет (красный/оранжевый) или агрессивный градиент
109239
- - Центр: КРУПНАЯ иконка/схема проблемной зоны тела, строго соответствующей категории продукта (для суставов — колено/сустав; для простаты — силуэт мужчины; для пищеварения — желудок), с красным свечением
110035
+ - Центр: КРУПНАЯ иконка/схема проблемы, уместная для продукта по брифу, с красным свечением
109240
110036
  - Продукт: крупно рядом
109241
- - HOOK: 1–4 строки, до 12 слов, огромный, прописные буквы, ключевое слово проблемы явно
110037
+ - HOOK: 1–4 строки, до 12 слов, огромный, прописные буквы, привязка к теме продукта (боль/ситуация), не абстракция
109242
110038
  - БЕЗ буллитов
109243
110039
  - Цена + скидка: крупно, заметно
109244
110040
  - CTA: контрастная яркая кнопка
109245
110041
 
109246
110042
  Цель: считывание за 1 секунду. Минимум текста, максимум визуального удара.
109247
110043
  БЕЗ человека. Никаких trust‑печатей/urgency бейджей — только HOOK + PRICE + -50% + CTA.`,
109248
- headlineAngle: `HOOK: «ПРОЩАЙ/КОНЕЦ/СТОП + [проблема]!». Эмоция победы/избавления. 1–4 строки, до 12 слов, прописные буквы, ключевое слово проблемы явно.`,
110044
+ headlineAngle: `HOOK: «ПРОЩАЙ/КОНЕЦ/СТОП + [проблема]!». Эмоция победы/избавления. 1–4 строки, до 12 слов, прописные буквы, привязка к теме продукта (боль/ситуация), не абстракция.`,
109249
110045
  bulletsFocus: `БУЛЛИТЫ: ЗАПРЕЩЕНЫ. Вся информация — в HOOK и крупных надписях.`
109250
110046
  },
109251
110047
  {
@@ -109260,7 +110056,7 @@ const CREO_APPROACHES = [
109260
110056
  - Крупные текстовые надписи рядом с продуктом: цена, скидка; допустимо одно короткое слово-восклицание («РАБОТАЕТ!», «ПРОВЕРЕНО!») на языке GEO
109261
110057
  - Кнопка CTA: плоская, без градиентов, контрастный сплошной цвет
109262
110058
  - НИКАКИХ буллитов, trust‑печатей, urgency‑бейджей, иконок — только продукт и текст`,
109263
- headlineAngle: `HOOK: максимально прямолинейный — короткий, грубый, без украшений. 1–4 строки, до 12 слов, прописные буквы, ключевое слово проблемы явно, как объявление на столбе. Никакого маркетингового лоска, никаких абстрактных слоганов.`,
110059
+ headlineAngle: `HOOK: максимально прямолинейный — короткий, грубый, без украшений. 1–4 строки, до 12 слов, прописные буквы, привязка к теме продукта (боль/ситуация), не абстракция, как объявление на столбе. Никакого маркетингового лоска, никаких абстрактных слоганов.`,
109264
110060
  bulletsFocus: `БУЛЛИТЫ: ЗАПРЕЩЕНЫ. Всё — в HOOK и крупных надписях вокруг продукта (цена, скидка, одно слово-восклицание).`
109265
110061
  },
109266
110062
  {
@@ -109270,9 +110066,9 @@ const CREO_APPROACHES = [
109270
110066
  - ЛОКАЛИЗАЦИЯ (КРИТИЧНО): врач НЕ generic — явно угадывается регион/GEO. Черты лица, тип кожи, причёска — характерные для целевого рынка. RO → румынский тип; PL → польский; HU → венгерский; ES → испанский/латино; IT → итальянский. Врач = местный специалист, не стоковое универсальное лицо.
109271
110067
  - Продукт: в руках врача или на столе/планшете рядом, хорошо виден.
109272
110068
  - Фон: профессиональный (кабинет, аптечные полки, медицинский контекст). Высокий контраст, читабельность.
109273
- - Опционально: 1–3 trust‑печати по низу (цветные, яркие, в стиле FDA). Размер как минимум как у буллитов.
110069
+ - Опционально: 1–3 trust‑печати по низу (FDA‑стиль; микротекст на круге может быть декоративным).
109274
110070
  - ИЕРАРХИЯ: врач + продукт — главные элементы. HOOK сверху, BULLETS сбоку или снизу, CTA и PRICE/DISCOUNT в нижнем блоке.`,
109275
- headlineAngle: `HOOK: угол «врач/специалист рекомендует» или «эксперты знают». 1–4 строки, до 12 слов, прописные буквы, ключевое слово проблемы явно.`,
110071
+ headlineAngle: `HOOK: угол «врач/специалист рекомендует» или «эксперты знают». 1–4 строки, до 12 слов, прописные буквы, привязка к теме продукта (боль/ситуация), не абстракция.`,
109276
110072
  bulletsFocus: `БУЛЛИТЫ: выбери 3 СЛУЧАЙНЫХ из пула (действие + срок + соц.доказательство) в ПРОИЗВОЛЬНОМ порядке. Уникальный набор — не повторяй комбинации из других креативов.`
109277
110073
  },
109278
110074
  {
@@ -109280,12 +110076,13 @@ const CREO_APPROACHES = [
109280
110076
  noBullets: true,
109281
110077
  prompt: `СКРИН ОТЗЫВОВ: имитация реалистичного скриншота с положительными благодарными отзывами и оценками 5 звёзд.
109282
110078
  - Визуал: реалистичный стиль — как скриншот приложения/сайта отзывов (App Store, Google Play, или страница отзывов). Высокое качество, читабельный текст.
109283
- - Отзывы: 2–4 отзыва с аватарками, именами и возрастом (формат «Имя, 45 лет»), ОБЯЗАТЕЛЬНО 5 звёзд (★★★★★) в каждом, короткий текст благодарности. Текст отзывов СТРОГО на языке GEO никакого английского. Релевантен категории продукта.
110079
+ - Шапка окна / подписи интерфейса внутри рамки телефона: допускаются короткие англоязычные метки как у реального UI («Reviews», бейдж стора)это не рекламный текст баннера.
110080
+ - Отзывы: 2–4 отзыва с аватарками, именами и возрастом (формат «Имя, 45 лет»), ОБЯЗАТЕЛЬНО 5 звёзд (★★★★★) в каждом, короткий текст благодарности. Текст отзывов СТРОГО на языке GEO — без английского в теле отзыва. Релевантен категории продукта.
109284
110081
  - HOOK ОБЯЗАТЕЛЕН: крупно, выше блока отзывов или поверх, прописные буквы, на яркой подложке. Ключевое слово проблемы явно.
109285
110082
  - Продукт: виден в кадре (рядом со скрином или интегрирован в композицию).
109286
110083
  - Цена, скидка «-50%», CTA — в нижней части. БЕЗ буллитов — отзывы заменяют их.
109287
110084
  - Стиль: не мультяшный, не комикс — максимально реалистичный скрин.`,
109288
- headlineAngle: `HOOK: угол «тысячи довольны» / «9 из 10 рекомендуют» / «реальный результат». 1–4 строки, до 12 слов, прописные буквы, ключевое слово проблемы явно.`,
110085
+ headlineAngle: `HOOK: угол «тысячи довольны» / «9 из 10 рекомендуют» / «реальный результат». 1–4 строки, до 12 слов, прописные буквы, привязка к теме продукта (боль/ситуация), не абстракция.`,
109289
110086
  bulletsFocus: `БУЛЛИТЫ: ЗАПРЕЩЕНЫ. Вся информация — в HOOK, отзывах (с 5 звёздами), цене и CTA.`
109290
110087
  }
109291
110088
  ];