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/main.js +64 -5
- package/dist/preload.js +2 -0
- package/dist/renderer.js +1647 -850
- package/dist/renderer.js.map +1 -1
- package/package.json +1 -1
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
|
|
100849
|
-
/* harmony import */ var
|
|
100850
|
-
/* harmony import */ var
|
|
100851
|
-
/* harmony import */ var
|
|
100852
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100853
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100854
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100855
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100856
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100857
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100858
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100859
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100860
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100861
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100862
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100863
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100864
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100865
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100866
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100867
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100868
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100869
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100870
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100871
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100872
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100873
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100874
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100875
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100876
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100877
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100878
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
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/
|
|
100881
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100882
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100883
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/
|
|
100884
|
-
/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/styles/
|
|
100885
|
-
/* harmony import */ var
|
|
100886
|
-
/* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_39__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/
|
|
100887
|
-
/* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/
|
|
100888
|
-
/* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/
|
|
100889
|
-
/* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/
|
|
100890
|
-
/* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/
|
|
100891
|
-
/* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_44__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/
|
|
100892
|
-
/* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_45__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/
|
|
100893
|
-
/* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/
|
|
100894
|
-
/* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/
|
|
100895
|
-
/* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/
|
|
100896
|
-
/* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/
|
|
100897
|
-
/* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_50__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/
|
|
100898
|
-
/* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/
|
|
100899
|
-
/* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/
|
|
100900
|
-
/* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/
|
|
100901
|
-
/* harmony import */ var
|
|
100902
|
-
/* harmony import */ var
|
|
100903
|
-
/* harmony import */ var
|
|
100904
|
-
/* harmony import */ var
|
|
100905
|
-
/* harmony import */ var
|
|
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
|
-
/**
|
|
100976
|
-
function
|
|
100977
|
-
|
|
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})
|
|
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}
|
|
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 =
|
|
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 ? `${
|
|
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 [
|
|
101300
|
-
|
|
101301
|
-
|
|
101302
|
-
|
|
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 ||
|
|
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
|
-
/**
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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
|
|
102455
|
-
|
|
102456
|
-
localStorage.setItem('
|
|
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
|
-
//
|
|
102992
|
+
// Если выбранной модели нет в списке API — сброс на дефолт (отдельно для 1:1 и 2:3)
|
|
102720
102993
|
if (imageGenModels.length > 0) {
|
|
102721
|
-
const
|
|
102722
|
-
|
|
102723
|
-
|
|
102724
|
-
|
|
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
|
-
|
|
102727
|
-
localStorage.setItem(
|
|
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
|
-
|
|
102786
|
-
|
|
102787
|
-
|
|
102788
|
-
if (
|
|
102789
|
-
|
|
102790
|
-
|
|
102791
|
-
|
|
102792
|
-
|
|
102793
|
-
|
|
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,
|
|
102829
|
-
const userPrompt = (0,
|
|
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:
|
|
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,
|
|
103141
|
-
const userPrompt = (0,
|
|
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:
|
|
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
|
-
*
|
|
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:
|
|
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
|
|
103303
|
-
logToTerminal('warn', '[Translate RU]
|
|
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,
|
|
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
|
-
|
|
103400
|
-
|
|
103401
|
-
|
|
103402
|
-
|
|
103403
|
-
|
|
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 =
|
|
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:
|
|
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
|
-
|
|
103826
|
-
const
|
|
103827
|
-
|
|
103828
|
-
|
|
103829
|
-
'
|
|
103830
|
-
|
|
103831
|
-
|
|
103832
|
-
'
|
|
103833
|
-
}
|
|
103834
|
-
|
|
103835
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
|
103979
|
-
|
|
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
|
-
|
|
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,
|
|
104260
|
-
const userPrompt = (0,
|
|
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:
|
|
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 (
|
|
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,
|
|
105037
|
+
const expandedTasks = (0,_prompts__WEBPACK_IMPORTED_MODULE_2__.getCreoApproachExpandedTasks)();
|
|
104680
105038
|
if (expandedTasks.length === 0) {
|
|
104681
105039
|
addLog(formatLogMessage('error', '❌ Укажите количество изображений (хотя бы 1) в настройках подходов'));
|
|
104682
|
-
alert(
|
|
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,
|
|
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,
|
|
104707
|
-
return `ОБЯЗАТЕЛЬНЫЕ БУЛЛЕТЫ
|
|
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
|
-
|
|
104807
|
-
|
|
104808
|
-
|
|
104809
|
-
|
|
104810
|
-
|
|
104811
|
-
|
|
104812
|
-
|
|
104813
|
-
|
|
104814
|
-
|
|
104815
|
-
|
|
104816
|
-
|
|
104817
|
-
|
|
104818
|
-
|
|
104819
|
-
|
|
104820
|
-
|
|
104821
|
-
|
|
104822
|
-
|
|
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 =
|
|
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
|
-
|
|
105168
|
-
|
|
105169
|
-
|
|
105170
|
-
|
|
105171
|
-
|
|
105172
|
-
|
|
105173
|
-
|
|
105174
|
-
|
|
105175
|
-
|
|
105176
|
-
|
|
105177
|
-
|
|
105178
|
-
|
|
105179
|
-
|
|
105180
|
-
|
|
105181
|
-
|
|
105182
|
-
|
|
105183
|
-
|
|
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
|
-
|
|
105298
|
-
|
|
105299
|
-
|
|
105300
|
-
|
|
105301
|
-
|
|
105302
|
-
|
|
105303
|
-
|
|
105304
|
-
|
|
105305
|
-
|
|
105306
|
-
|
|
105307
|
-
|
|
105308
|
-
|
|
105309
|
-
|
|
105310
|
-
|
|
105311
|
-
|
|
105312
|
-
|
|
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
|
-
|
|
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:
|
|
105369
|
-
checkStatus:
|
|
105370
|
-
checkResult:
|
|
105371
|
-
checkErrors:
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
105507
|
-
|
|
105508
|
-
|
|
106050
|
+
/** Нормализация URL товара (как при blur поля ссылки). */
|
|
106051
|
+
const applyNormalizedProductLink = (trimmed) => {
|
|
106052
|
+
const t = trimmed.trim();
|
|
106053
|
+
if (!t) {
|
|
106054
|
+
handleLinkChange('');
|
|
105509
106055
|
return;
|
|
105510
|
-
|
|
105511
|
-
|
|
105512
|
-
|
|
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
|
-
|
|
105525
|
-
|
|
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
|
-
|
|
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,
|
|
105738
|
-
imageApproachCounts: (0,
|
|
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,
|
|
105906
|
-
const currentPairs = (0,
|
|
105907
|
-
const currentCounts = (0,
|
|
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,
|
|
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(
|
|
105965
|
-
return () => window.removeEventListener(
|
|
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}
|
|
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
|
-
|
|
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 =
|
|
106047
|
-
const ws =
|
|
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
|
-
|
|
106677
|
+
xlsx__WEBPACK_IMPORTED_MODULE_61__.utils.book_append_sheet(wb, ws, "Products");
|
|
106061
106678
|
// Generate buffer
|
|
106062
|
-
const wbout =
|
|
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 =
|
|
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',
|
|
106813
|
+
['test1', 'Test Title', 'Test Description', 'in stock', 'new', testPrice, 'http://test.com', 'http://test.com/img.jpg', 'TestBrand']
|
|
106189
106814
|
];
|
|
106190
|
-
const ws =
|
|
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
|
-
|
|
106204
|
-
const wbout =
|
|
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(
|
|
106257
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106258
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
106297
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106298
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106299
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106300
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106301
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106302
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106303
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106304
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106305
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106306
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106307
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106308
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106309
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106310
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106311
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106312
|
-
|
|
106313
|
-
|
|
106314
|
-
|
|
106315
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106316
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106317
|
-
|
|
106318
|
-
|
|
106319
|
-
|
|
106320
|
-
|
|
106321
|
-
|
|
106322
|
-
|
|
106323
|
-
|
|
106324
|
-
|
|
106325
|
-
|
|
106326
|
-
|
|
106327
|
-
|
|
106328
|
-
|
|
106329
|
-
|
|
106330
|
-
|
|
106331
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106332
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106333
|
-
|
|
106334
|
-
|
|
106335
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
106343
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
106347
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106348
|
-
|
|
106349
|
-
|
|
106350
|
-
|
|
106351
|
-
|
|
106352
|
-
|
|
106353
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106354
|
-
|
|
106355
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106356
|
-
|
|
106357
|
-
|
|
106358
|
-
|
|
106359
|
-
|
|
106360
|
-
|
|
106361
|
-
|
|
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(
|
|
106364
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106365
|
-
!checkingFolderFiles && !folderFilesInfo && driveFolderUrl.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
106369
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106370
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106371
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106372
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
106387
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106388
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106389
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106390
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106391
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106392
|
-
|
|
106393
|
-
|
|
106394
|
-
|
|
106395
|
-
|
|
106396
|
-
|
|
106397
|
-
|
|
106398
|
-
|
|
106399
|
-
|
|
106400
|
-
|
|
106401
|
-
|
|
106402
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106403
|
-
|
|
106404
|
-
|
|
106405
|
-
|
|
106406
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106407
|
-
|
|
106408
|
-
|
|
106409
|
-
|
|
106410
|
-
|
|
106411
|
-
|
|
106412
|
-
|
|
106413
|
-
|
|
106414
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], {
|
|
106415
|
-
|
|
106416
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106417
|
-
|
|
106418
|
-
|
|
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(
|
|
106423
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106424
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
106429
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
106435
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106436
|
-
openaiApiKey && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106437
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106438
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_26__["default"],
|
|
106439
|
-
|
|
106440
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106441
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106442
|
-
|
|
106443
|
-
|
|
106444
|
-
|
|
106445
|
-
|
|
106446
|
-
|
|
106447
|
-
|
|
106448
|
-
|
|
106449
|
-
|
|
106450
|
-
|
|
106451
|
-
|
|
106452
|
-
|
|
106453
|
-
|
|
106454
|
-
|
|
106455
|
-
|
|
106456
|
-
|
|
106457
|
-
|
|
106458
|
-
|
|
106459
|
-
|
|
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(
|
|
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(
|
|
106482
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106483
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106484
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
106503
|
-
uploadedLink && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106504
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106505
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
106514
|
-
folderFilesInfo !== null && !folderFilesInfo.hasProduct && driveFolderUrl.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
106517
|
-
openaiApiKey && (!accessToken && !refreshToken) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106518
|
-
openaiApiKey && (accessToken || refreshToken) && !generateProduct.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106519
|
-
openaiApiKey && (accessToken || refreshToken) && generateProduct.trim() && !generateGeo.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106520
|
-
openaiApiKey && (accessToken || refreshToken) && generateProduct.trim() && generateGeo.trim() && !driveFolderUrl.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
106523
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106524
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
106532
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
106539
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106540
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106541
|
-
generatedImagesData.length > 0 && generatedImagesData.some(img => !img.uploaded && img.imageUrl) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106542
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106543
|
-
generatedImagesData.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
106592
|
-
|
|
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(
|
|
106608
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106609
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106610
|
-
generatingImages && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
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(
|
|
106628
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106629
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106630
|
-
|
|
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:
|
|
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(
|
|
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(
|
|
106663
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
106679
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106680
|
-
imageData.errorMessage && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
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(
|
|
106698
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106699
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106700
|
-
generatingImages && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
106704
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
106709
|
-
imageData.checkStatus === 'needs_rebuild' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106710
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106711
|
-
imageData.checkErrors && imageData.checkErrors.length > 0 && !imageData.checkFailed && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106712
|
-
imageData.checkErrors.slice(0, 2).map((error, idx) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
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(
|
|
106725
|
-
imageData.checkStatus === 'pending' && !imageData.failed && !imageData.generating && imageData.imageUrl && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106726
|
-
imageData.failed && !imageData.generating && !imageData.imageUrl && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106727
|
-
imageData.uploaded && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106728
|
-
!imageData.generating && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106729
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
106747
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
106774
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106775
|
-
|
|
106776
|
-
|
|
106777
|
-
|
|
106778
|
-
|
|
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(
|
|
106785
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
106792
|
-
loadingContentFromDrive && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
106807
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106808
|
-
(generatedTitlesData.length > 0 || generatedTextsData.length > 0) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106809
|
-
|
|
106810
|
-
|
|
106811
|
-
|
|
106812
|
-
|
|
106813
|
-
|
|
106814
|
-
|
|
106815
|
-
|
|
106816
|
-
|
|
106817
|
-
|
|
106818
|
-
|
|
106819
|
-
|
|
106820
|
-
|
|
106821
|
-
|
|
106822
|
-
:
|
|
106823
|
-
? '
|
|
106824
|
-
:
|
|
106825
|
-
|
|
106826
|
-
|
|
106827
|
-
|
|
106828
|
-
:
|
|
106829
|
-
|
|
106830
|
-
|
|
106831
|
-
|
|
106832
|
-
|
|
106833
|
-
|
|
106834
|
-
|
|
106835
|
-
|
|
106836
|
-
|
|
106837
|
-
|
|
106838
|
-
:
|
|
106839
|
-
|
|
106840
|
-
|
|
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
|
-
|
|
106934
|
-
|
|
106935
|
-
|
|
106936
|
-
|
|
106937
|
-
|
|
106938
|
-
|
|
106939
|
-
|
|
106940
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
106964
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106965
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106966
|
-
!generatingLanding && generatedLandingHTML && generatedLandingImageBlob && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
106969
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106970
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106971
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106972
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106973
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106974
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106975
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106976
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106977
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106978
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106979
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
106983
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106984
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106985
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106986
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106987
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106988
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106989
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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(
|
|
106993
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106994
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
106995
|
-
react__WEBPACK_IMPORTED_MODULE_0___default().createElement(
|
|
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–
|
|
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(
|
|
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" },
|
|
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:
|
|
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
|
|
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–
|
|
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(
|
|
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–
|
|
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(
|
|
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 недели', 'Быстрый результат', '
|
|
108465
|
-
'Эффект за 7 дней', '
|
|
109247
|
+
'Эффект за 7–14 дней', 'Эффект за 2 недели', 'Быстрый результат', 'Поддержка с первого дня',
|
|
109248
|
+
'Эффект за 7 дней', 'Заметно уже скоро', 'С первого дня курса', 'В первые дни приёма'
|
|
108466
109249
|
],
|
|
108467
109250
|
socialProof: [
|
|
108468
|
-
'9 из 10 рекомендуют', '9 из 10 мужчин рекомендуют', '
|
|
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 дней). Цифры −кг, весы, «минус на весах» — **только** если продукт явно про похудение / контроль веса. Для
|
|
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
|
-
-
|
|
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
|
-
-
|
|
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
|
|
108862
|
-
|
|
108863
|
-
|
|
108864
|
-
|
|
108865
|
-
|
|
108866
|
-
|
|
108867
|
-
|
|
108868
|
-
|
|
108869
|
-
|
|
108870
|
-
|
|
108871
|
-
|
|
108872
|
-
|
|
108873
|
-
|
|
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
|
-
- Должны быть видны две суммы: новая (акционная) и старая (зачёркнутая).
|
|
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
|
-
-
|
|
108954
|
-
-
|
|
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
|
-
-
|
|
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/скидка/опциональный бейдж
|
|
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
|
-
ПРИМЕР (русский — только
|
|
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
|
-
❗ ВЫБОР БУЛЛЕТОВ (КРИТИЧНО): Если выше по промпту задан блок «ОБЯЗАТЕЛЬНЫЕ БУЛЛЕТЫ» — используй
|
|
109881
|
+
❗ ВЫБОР БУЛЛЕТОВ (КРИТИЧНО): Если выше по промпту задан блок «ОБЯЗАТЕЛЬНЫЕ БУЛЛЕТЫ» — используй его как **три опорных смысла**: переведи на язык ${generateGeo} коротко (2–4 слова), но **перепиши формулировки под тему продукта из брифа**, если исходный смысл не совпадает с нишей (тип выгоды сохрани — «срок / соцдоказательство / состав / комфорт» — слова подставь свои под оффер). Если такого блока нет — выбери 3 смысла из нейтрального пула ниже и тоже адаптируй под продукт; пул только для категории смысла, на макете **ноль английского**. Не копируй дословно строки пула, если они тянут в другую медицинскую вертикаль.
|
|
109087
109882
|
|
|
109088
|
-
ПУЛ СМЫСЛОВ (английский не печатать; переписать на ${generateGeo}, 2–4 слова
|
|
109089
|
-
• Action/benefit:
|
|
109090
|
-
• Time/speed: effect in 7–14 days, effect in 2 weeks, fast result,
|
|
109091
|
-
• Social proof: 9 of 10 recommend, 9 of 10 men recommend,
|
|
109092
|
-
• Formula: natural formula, natural ingredients, no harsh
|
|
109093
|
-
• Quality of 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‑печати:
|
|
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
|
-
- Цена: ОБЯЗАТЕЛЬНО ДВЕ — старая (
|
|
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
|
|
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
|
|
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
|
-
* 🎯 ПОДХОД: Визуализация проблемы → Инфографика/схема: слева проблема (
|
|
109147
|
-
* 🎯 ПОДХОД: Power / Сила решения → БЕЗ человека. ДИНАМИЧНЫЙ комикс‑кадр, диагональная композиция. Продукт как “герой” + power‑иконки (щит/молния/бёрст). HOOK сверху слева на яркой плашке, BULLETS слева ниже, CTA снизу справа, PRICE/DISCOUNT возле CTA. Trust‑печати (если есть) — компактно по
|
|
109148
|
-
* 🎯 ПОДХОД: Минимализм / Clean Big Text → Белый/градиентный фон, минимум элементов. ОГРОМНЫЙ HOOK занимает верх/центр, продукт крупно (центр/право), CTA снизу по центру, PRICE/DISCOUNT рядом (слабее CTA). Trust‑печати (если есть) —
|
|
109149
|
-
* 🎯 ПОДХОД: Problem Visual Punch → БЕЗ человека. Яркий сплошной фон (красный/оранжевый) или агрессивный градиент. В центре КРУПНАЯ иконка/схема
|
|
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 слов, прописные буквы, жирный на контрастной плашке, выше всех элементов,
|
|
109154
|
-
-
|
|
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–
|
|
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 шт): цветные, яркие,
|
|
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‑печати по низу (цветные, яркие,
|
|
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), прямой взгляд в камеру, сильная эмоция облегчения/надежды (без широкой стоковой улыбки). Возраст человека подбери под
|
|
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: `ВИЗУАЛИЗАЦИЯ ПРОБЛЕМЫ (без человека):
|
|
109219
|
-
headlineAngle: `HOOK: прямой вопрос о боли/дискомфорте
|
|
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‑печати по низу (
|
|
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‑печати в этом подходе НЕ рекомендуются — они нарушают чистоту и ощущение премиальности.
|
|
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‑печати по низу (
|
|
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
|
-
-
|
|
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
|
];
|