docs-combiner 0.1.15 → 0.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/renderer.js CHANGED
@@ -3056,29 +3056,6 @@ __webpack_require__.r(__webpack_exports__);
3056
3056
 
3057
3057
  /***/ }),
3058
3058
 
3059
- /***/ "./node_modules/@mui/icons-material/esm/InfoOutlined.js":
3060
- /*!**************************************************************!*\
3061
- !*** ./node_modules/@mui/icons-material/esm/InfoOutlined.js ***!
3062
- \**************************************************************/
3063
- /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
3064
-
3065
- "use strict";
3066
- __webpack_require__.r(__webpack_exports__);
3067
- /* harmony export */ __webpack_require__.d(__webpack_exports__, {
3068
- /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
3069
- /* harmony export */ });
3070
- /* harmony import */ var _utils_createSvgIcon_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utils/createSvgIcon.js */ "./node_modules/@mui/material/esm/utils/createSvgIcon.js");
3071
- /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react/jsx-runtime */ "./node_modules/react/jsx-runtime.js");
3072
- "use client";
3073
-
3074
-
3075
-
3076
- /* 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", {
3077
- d: "M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8"
3078
- }), 'InfoOutlined'));
3079
-
3080
- /***/ }),
3081
-
3082
3059
  /***/ "./node_modules/@mui/icons-material/esm/KeyboardArrowDown.js":
3083
3060
  /*!*******************************************************************!*\
3084
3061
  !*** ./node_modules/@mui/icons-material/esm/KeyboardArrowDown.js ***!
@@ -100879,15 +100856,15 @@ __webpack_require__.r(__webpack_exports__);
100879
100856
  /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Card/Card.js");
100880
100857
  /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/CardContent/CardContent.js");
100881
100858
  /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Stack/Stack.js");
100882
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Accordion/Accordion.js");
100883
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/AccordionSummary/AccordionSummary.js");
100884
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/AccordionDetails/AccordionDetails.js");
100885
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/TextField/TextField.js");
100859
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/TextField/TextField.js");
100860
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/FormHelperText/FormHelperText.js");
100861
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/CircularProgress/CircularProgress.js");
100862
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Alert/Alert.js");
100886
100863
  /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Divider/Divider.js");
100887
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Button/Button.js");
100888
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/CircularProgress/CircularProgress.js");
100889
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/FormHelperText/FormHelperText.js");
100890
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Alert/Alert.js");
100864
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Accordion/Accordion.js");
100865
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/AccordionSummary/AccordionSummary.js");
100866
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/AccordionDetails/AccordionDetails.js");
100867
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Button/Button.js");
100891
100868
  /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/FormControlLabel/FormControlLabel.js");
100892
100869
  /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Checkbox/Checkbox.js");
100893
100870
  /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/InputAdornment/InputAdornment.js");
@@ -100899,30 +100876,33 @@ __webpack_require__.r(__webpack_exports__);
100899
100876
  /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/ToggleButtonGroup/ToggleButtonGroup.js");
100900
100877
  /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/ToggleButton/ToggleButton.js");
100901
100878
  /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Paper/Paper.js");
100902
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/styles/createTheme.js");
100903
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/styles/ThemeProvider.js");
100904
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/AccountBalance.js");
100905
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/AttachMoney.js");
100906
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/AutoAwesome.js");
100907
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Brightness4.js");
100908
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Brightness7.js");
100909
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_39__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/CheckCircle.js");
100910
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/CloudDownload.js");
100911
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/ContentCopy.js");
100912
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/DeleteOutline.js");
100913
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/ExpandMore.js");
100914
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_44__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/InfoOutlined.js");
100915
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_45__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Login.js");
100916
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Logout.js");
100917
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/NoteAdd.js");
100918
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Replay.js");
100919
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Settings.js");
100920
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_50__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Visibility.js");
100921
- /* harmony import */ var _PromptManagerDialog__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(/*! ./PromptManagerDialog */ "./src/PromptManagerDialog.tsx");
100922
- /* harmony import */ var _promptOverrides__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(/*! ./promptOverrides */ "./src/promptOverrides.ts");
100923
- /* harmony import */ var xlsx__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(/*! xlsx */ "./node_modules/xlsx/xlsx.mjs");
100924
- /* harmony import */ var jszip__WEBPACK_IMPORTED_MODULE_54__ = __webpack_require__(/*! jszip */ "./node_modules/jszip/dist/jszip.min.js");
100925
- /* harmony import */ var jszip__WEBPACK_IMPORTED_MODULE_54___default = /*#__PURE__*/__webpack_require__.n(jszip__WEBPACK_IMPORTED_MODULE_54__);
100879
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Dialog/Dialog.js");
100880
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/DialogTitle/DialogTitle.js");
100881
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/DialogContent/DialogContent.js");
100882
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/DialogActions/DialogActions.js");
100883
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/styles/createTheme.js");
100884
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/styles/ThemeProvider.js");
100885
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/AccountBalance.js");
100886
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_39__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/AttachMoney.js");
100887
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/AutoAwesome.js");
100888
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Brightness4.js");
100889
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Brightness7.js");
100890
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/CheckCircle.js");
100891
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_44__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/CloudDownload.js");
100892
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_45__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/ContentCopy.js");
100893
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/DeleteOutline.js");
100894
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/ExpandMore.js");
100895
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Login.js");
100896
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Logout.js");
100897
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_50__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/NoteAdd.js");
100898
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Replay.js");
100899
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Settings.js");
100900
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Visibility.js");
100901
+ /* harmony import */ var _PromptManagerDialog__WEBPACK_IMPORTED_MODULE_54__ = __webpack_require__(/*! ./PromptManagerDialog */ "./src/PromptManagerDialog.tsx");
100902
+ /* harmony import */ var _promptOverrides__WEBPACK_IMPORTED_MODULE_55__ = __webpack_require__(/*! ./promptOverrides */ "./src/promptOverrides.ts");
100903
+ /* harmony import */ var xlsx__WEBPACK_IMPORTED_MODULE_56__ = __webpack_require__(/*! xlsx */ "./node_modules/xlsx/xlsx.mjs");
100904
+ /* harmony import */ var jszip__WEBPACK_IMPORTED_MODULE_57__ = __webpack_require__(/*! jszip */ "./node_modules/jszip/dist/jszip.min.js");
100905
+ /* harmony import */ var jszip__WEBPACK_IMPORTED_MODULE_57___default = /*#__PURE__*/__webpack_require__.n(jszip__WEBPACK_IMPORTED_MODULE_57__);
100926
100906
 
100927
100907
 
100928
100908
 
@@ -101222,6 +101202,70 @@ function getRemakeHighlightBackground(remakeCount, dark) {
101222
101202
  const light = ['#fffde7', '#fff9c4', '#fff59d', '#ffee58', '#ffeb3b', '#fdd835', '#fbc02d', '#f9a825'];
101223
101203
  return light[step];
101224
101204
  }
101205
+ function sortedPairIndicesEqual(a, b) {
101206
+ const sa = [...a].sort((x, y) => x - y);
101207
+ const sb = [...b].sort((x, y) => x - y);
101208
+ if (sa.length !== sb.length)
101209
+ return false;
101210
+ return sa.every((v, i) => v === sb[i]);
101211
+ }
101212
+ function imageApproachCountsEqual(a, b) {
101213
+ if (a.length !== b.length)
101214
+ return false;
101215
+ return a.every((v, i) => v === b[i]);
101216
+ }
101217
+ function formatPairApproachesForDisplay(indices) {
101218
+ return [...indices]
101219
+ .sort((x, y) => x - y)
101220
+ .map(i => {
101221
+ const p = _prompts__WEBPACK_IMPORTED_MODULE_1__.PAIR_APPROACH_POOL[i];
101222
+ const label = p?.name ?? '?';
101223
+ return `${i + 1}. ${label}`;
101224
+ })
101225
+ .join('\n');
101226
+ }
101227
+ function formatImageCountsForDisplay(counts) {
101228
+ const lines = counts
101229
+ .map((c, i) => (c > 0 ? `${_prompts__WEBPACK_IMPORTED_MODULE_1__.CREO_APPROACHES[i]?.name ?? `№${i + 1}`}: ×${c}` : null))
101230
+ .filter((x) => x != null);
101231
+ return lines.length > 0 ? lines.join('\n') : 'ни один подход (все 0)';
101232
+ }
101233
+ /** Один раз прошли экран «Application Error» — больше не показывать (localStorage + Electron config). */
101234
+ const SECRET_UNLOCK_STORAGE_KEY = 'docs_combiner_secret_unlocked';
101235
+ async function fetchDriveFilePermissions(token, fileId) {
101236
+ const permissionsUrl = `https://www.googleapis.com/drive/v3/files/${fileId}?fields=permissions(id,type,role)&supportsAllDrives=true`;
101237
+ const permissionsResponse = await fetch(permissionsUrl, {
101238
+ headers: { Authorization: `Bearer ${token}` }
101239
+ });
101240
+ if (!permissionsResponse.ok) {
101241
+ return { ok: false, errorText: await permissionsResponse.text() };
101242
+ }
101243
+ const permissionsData = await permissionsResponse.json();
101244
+ return { ok: true, permissions: permissionsData.permissions || [] };
101245
+ }
101246
+ /** Доступ «любой по ссылке» / публичный читатель — нужен для скачивания референса по URL без OAuth. */
101247
+ function permissionsIncludeAnyoneLink(permissions) {
101248
+ return permissions.some(p => p.type === 'anyone');
101249
+ }
101250
+ async function verifyOfferFolderHasAnyoneLinkForImageGen(token, folderId) {
101251
+ const offerPermResult = await fetchDriveFilePermissions(token, folderId);
101252
+ if (!offerPermResult.ok) {
101253
+ return {
101254
+ ok: false,
101255
+ message: 'Не удалось проверить доступ к папке оффера в Google Диске.\n\n' +
101256
+ 'Генерация изображений с референсом может не работать без доступа по ссылке.\n\n' +
101257
+ (offerPermResult.errorText.trim().slice(0, 280) || 'Ошибка API')
101258
+ };
101259
+ }
101260
+ if (!permissionsIncludeAnyoneLink(offerPermResult.permissions)) {
101261
+ return {
101262
+ ok: false,
101263
+ message: 'Папка оффера закрыта для доступа по ссылке: нет права «Все, у кого есть ссылка» (тип anyone).\n\n' +
101264
+ 'Сервисы генерации скачивают product.png по публичному URL. Включите в Google Диске общий доступ с ролью «Читатель» для этой папки (или согласитесь, когда приложение предложит сделать это автоматически).'
101265
+ };
101266
+ }
101267
+ return { ok: true };
101268
+ }
101225
101269
  function App() {
101226
101270
  const [clientId, setClientId] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
101227
101271
  const [clientSecret, setClientSecret] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
@@ -101236,7 +101280,10 @@ function App() {
101236
101280
  // Переводы сгенерированных пар на русский: ключ = 0-based индекс пары
101237
101281
  const [pairTranslations, setPairTranslations] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)({});
101238
101282
  const [translatingPairs, setTranslatingPairs] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
101283
+ /** Папка оффера: загрузка/сохранение контента и крео (как раньше) */
101239
101284
  const [driveFolderUrl, setDriveFolderUrl] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
101285
+ /** Корневая папка: только поле + проверка общего доступа и подпапки OFFERS (отдельно от папки оффера) */
101286
+ const [rootDriveFolderUrl, setRootDriveFolderUrl] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
101240
101287
  const [link, setLink] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
101241
101288
  const linkInputRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
101242
101289
  const [openaiApiKey, setOpenaiApiKey] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
@@ -101441,10 +101488,17 @@ function App() {
101441
101488
  const [uploadingImages, setUploadingImages] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
101442
101489
  const [uploadingProduct, setUploadingProduct] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
101443
101490
  const [folderFilesInfo, setFolderFilesInfo] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
101491
+ const [rootFolderInfo, setRootFolderInfo] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
101444
101492
  const productFileInputRef = react__WEBPACK_IMPORTED_MODULE_0___default().useRef(null);
101445
101493
  const [checkingFolderFiles, setCheckingFolderFiles] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
101494
+ const [checkingRootFolder, setCheckingRootFolder] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
101495
+ const [drivePublicAccessWarning, setDrivePublicAccessWarning] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
101496
+ /** Уже обработали запрос доступа по ссылке для корневой папки (OFFERS) */
101446
101497
  const permissionCheckedFoldersRef = react__WEBPACK_IMPORTED_MODULE_0___default().useRef(new Set());
101498
+ /** Уже спрашивали про доступ по ссылке для папки оффера (нужен OpenRouter по uc?export=view) */
101499
+ const driveFolderPublicLinkPromptedRef = react__WEBPACK_IMPORTED_MODULE_0___default().useRef(new Set());
101447
101500
  const folderCheckRunningRef = react__WEBPACK_IMPORTED_MODULE_0___default().useRef(false);
101501
+ const rootFolderCheckRunningRef = react__WEBPACK_IMPORTED_MODULE_0___default().useRef(false);
101448
101502
  const [imagesProcessStartTime, setImagesProcessStartTime] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
101449
101503
  const [contentProcessStartTime, setContentProcessStartTime] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
101450
101504
  const [contentGenerationLogs, setContentGenerationLogs] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)([]);
@@ -101485,9 +101539,36 @@ function App() {
101485
101539
  const saved = localStorage.getItem('themeMode');
101486
101540
  return saved === 'dark';
101487
101541
  });
101488
- // Secret unlock state - always start locked
101489
- const [unlocked, setUnlocked] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
101542
+ // Secret unlock state восстанавливаем из localStorage; при старте Electron подмешивает config.secretUnlockPassed
101543
+ const [unlocked, setUnlocked] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(() => {
101544
+ try {
101545
+ return localStorage.getItem(SECRET_UNLOCK_STORAGE_KEY) === '1';
101546
+ }
101547
+ catch {
101548
+ return false;
101549
+ }
101550
+ });
101490
101551
  const [clickSequence, setClickSequence] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)([]);
101552
+ const persistSecretUnlockPassed = () => {
101553
+ try {
101554
+ localStorage.setItem(SECRET_UNLOCK_STORAGE_KEY, '1');
101555
+ }
101556
+ catch {
101557
+ // ignore
101558
+ }
101559
+ void (async () => {
101560
+ try {
101561
+ const api = getElectronAPI();
101562
+ if (api) {
101563
+ const current = await api.loadConfig();
101564
+ await api.saveConfig({ ...current, secretUnlockPassed: true });
101565
+ }
101566
+ }
101567
+ catch {
101568
+ // ignore
101569
+ }
101570
+ })();
101571
+ };
101491
101572
  // Handle secret unlock clicks - simplified to left then right
101492
101573
  const handleSecretClick = (e) => {
101493
101574
  if (unlocked)
@@ -101512,6 +101593,7 @@ function App() {
101512
101593
  // Second click: right side (after left)
101513
101594
  setUnlocked(true);
101514
101595
  setClickSequence([]);
101596
+ persistSecretUnlockPassed();
101515
101597
  }
101516
101598
  else {
101517
101599
  // Wrong click or wrong sequence, reset
@@ -101526,7 +101608,7 @@ function App() {
101526
101608
  }
101527
101609
  };
101528
101610
  // Create theme based on mode
101529
- const theme = react__WEBPACK_IMPORTED_MODULE_0___default().useMemo(() => (0,_mui_material__WEBPACK_IMPORTED_MODULE_32__["default"])({
101611
+ const theme = react__WEBPACK_IMPORTED_MODULE_0___default().useMemo(() => (0,_mui_material__WEBPACK_IMPORTED_MODULE_36__["default"])({
101530
101612
  palette: {
101531
101613
  mode: darkMode ? 'dark' : 'light',
101532
101614
  ...(darkMode
@@ -101581,6 +101663,8 @@ function App() {
101581
101663
  applyThemeStyles(darkMode);
101582
101664
  }, [darkMode]);
101583
101665
  const [promptManagerOpen, setPromptManagerOpen] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
101666
+ /** После загрузки project-settings.json: спросить, подставить подходы из файла или оставить локальные. */
101667
+ const [approachLoadChoice, setApproachLoadChoice] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
101584
101668
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
101585
101669
  // Load config on mount
101586
101670
  const loadKey = async () => {
@@ -101601,6 +101685,15 @@ function App() {
101601
101685
  setOpenaiApiKey(config.openaiApiKey);
101602
101686
  // Balance will be fetched automatically by useEffect when openaiApiKey changes
101603
101687
  }
101688
+ if (config.secretUnlockPassed === true) {
101689
+ setUnlocked(true);
101690
+ try {
101691
+ localStorage.setItem(SECRET_UNLOCK_STORAGE_KEY, '1');
101692
+ }
101693
+ catch {
101694
+ // ignore
101695
+ }
101696
+ }
101604
101697
  }
101605
101698
  catch (err) {
101606
101699
  // Silent fail
@@ -101609,8 +101702,11 @@ function App() {
101609
101702
  // effect (which depends on [driveFolderUrl]) will have valid tokens available
101610
101703
  try {
101611
101704
  const cachedDriveFolderUrl = localStorage.getItem('cached_driveFolderUrl');
101705
+ const cachedRootDriveFolderUrl = localStorage.getItem('cached_rootDriveFolderUrl');
101612
101706
  if (cachedDriveFolderUrl)
101613
101707
  setDriveFolderUrl(cachedDriveFolderUrl);
101708
+ if (cachedRootDriveFolderUrl)
101709
+ setRootDriveFolderUrl(cachedRootDriveFolderUrl);
101614
101710
  }
101615
101711
  catch (err) {
101616
101712
  // Silent fail
@@ -101618,7 +101714,7 @@ function App() {
101618
101714
  };
101619
101715
  loadKey();
101620
101716
  // Load prompt overrides from Electron config
101621
- (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_52__.loadOverridesFromElectron)();
101717
+ (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_55__.loadOverridesFromElectron)();
101622
101718
  }, []);
101623
101719
  // Save form fields to localStorage whenever they change
101624
101720
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
@@ -101631,6 +101727,22 @@ function App() {
101631
101727
  // Silent fail
101632
101728
  }
101633
101729
  }, [driveFolderUrl]);
101730
+ (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
101731
+ try {
101732
+ if (rootDriveFolderUrl) {
101733
+ localStorage.setItem('cached_rootDriveFolderUrl', rootDriveFolderUrl);
101734
+ }
101735
+ else {
101736
+ localStorage.removeItem('cached_rootDriveFolderUrl');
101737
+ }
101738
+ }
101739
+ catch (err) {
101740
+ // Silent fail
101741
+ }
101742
+ }, [rootDriveFolderUrl]);
101743
+ (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
101744
+ setDrivePublicAccessWarning(false);
101745
+ }, [rootDriveFolderUrl]);
101634
101746
  // Auto-save AI Generation Settings, brand and link to Google Drive when they change
101635
101747
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
101636
101748
  if (!driveFolderUrl)
@@ -101691,6 +101803,7 @@ function App() {
101691
101803
  setUploadedLink('');
101692
101804
  setCatalogLinkExtraMacros(DEFAULT_CATALOG_LINK_EXTRA_MACROS);
101693
101805
  setPairTranslations({});
101806
+ setApproachLoadChoice(null);
101694
101807
  return;
101695
101808
  }
101696
101809
  const folderId = extractFolderId(driveFolderUrl);
@@ -101708,6 +101821,7 @@ function App() {
101708
101821
  setUploadedLink('');
101709
101822
  setCatalogLinkExtraMacros(DEFAULT_CATALOG_LINK_EXTRA_MACROS);
101710
101823
  setPairTranslations({});
101824
+ setApproachLoadChoice(null);
101711
101825
  return;
101712
101826
  }
101713
101827
  logToTerminal('log', '[Load] driveFolderUrl changed, clearing old data and loading from folderId:', folderId);
@@ -101721,6 +101835,7 @@ function App() {
101721
101835
  setUploadedLink('');
101722
101836
  setCatalogLinkExtraMacros(DEFAULT_CATALOG_LINK_EXTRA_MACROS);
101723
101837
  setPairTranslations({});
101838
+ setApproachLoadChoice(null);
101724
101839
  setLoadingContentFromDrive(true);
101725
101840
  setDriveFilesFound({ content: false });
101726
101841
  // Load content from Google Drive
@@ -101877,11 +101992,132 @@ function App() {
101877
101992
  fetchValidationModels();
101878
101993
  // eslint-disable-next-line react-hooks/exhaustive-deps
101879
101994
  }, [openaiApiKey]);
101880
- // Check folder files when driveFolderUrl changes
101995
+ // Корневая папка: общий доступ и подпапка OFFERS
101996
+ (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
101997
+ let cancelled = false;
101998
+ const checkRootFolder = async () => {
101999
+ if (rootFolderCheckRunningRef.current)
102000
+ return;
102001
+ if (!rootDriveFolderUrl.trim()) {
102002
+ setRootFolderInfo(null);
102003
+ setDrivePublicAccessWarning(false);
102004
+ return;
102005
+ }
102006
+ const validToken = await getValidAccessToken();
102007
+ if (!validToken || cancelled) {
102008
+ if (!cancelled)
102009
+ setRootFolderInfo(null);
102010
+ return;
102011
+ }
102012
+ const rootFolderId = extractFolderId(rootDriveFolderUrl);
102013
+ if (!rootFolderId || cancelled) {
102014
+ if (!cancelled)
102015
+ setRootFolderInfo(null);
102016
+ return;
102017
+ }
102018
+ rootFolderCheckRunningRef.current = true;
102019
+ setCheckingRootFolder(true);
102020
+ try {
102021
+ if (!permissionCheckedFoldersRef.current.has(rootFolderId)) {
102022
+ const permissionsUrl = `https://www.googleapis.com/drive/v3/files/${rootFolderId}?fields=permissions(id,type,role)&supportsAllDrives=true`;
102023
+ const permissionsResponse = await fetch(permissionsUrl, {
102024
+ headers: {
102025
+ 'Authorization': `Bearer ${validToken}`
102026
+ }
102027
+ });
102028
+ if (cancelled)
102029
+ return;
102030
+ permissionCheckedFoldersRef.current.add(rootFolderId);
102031
+ if (permissionsResponse.ok) {
102032
+ const permissionsData = await permissionsResponse.json();
102033
+ const anyonePerms = (permissionsData.permissions || []).filter((p) => p.type === 'anyone' && p.id);
102034
+ if (anyonePerms.length > 0) {
102035
+ if (cancelled)
102036
+ return;
102037
+ const revoke = window.confirm('У этой папки включён общий доступ по ссылке («Все, у кого есть ссылка» или публично в интернете).\n\n' +
102038
+ 'Приложение использует ваш аккаунт Google — открывать папку всем не обязательно.\n\n' +
102039
+ 'Убрать общий доступ для этой папки автоматически?');
102040
+ if (cancelled)
102041
+ return;
102042
+ if (revoke) {
102043
+ const tok = await getValidAccessToken();
102044
+ if (!tok || cancelled) {
102045
+ if (!cancelled)
102046
+ setRootFolderInfo(null);
102047
+ return;
102048
+ }
102049
+ for (const p of anyonePerms) {
102050
+ if (cancelled)
102051
+ break;
102052
+ const delUrl = `https://www.googleapis.com/drive/v3/files/${rootFolderId}/permissions/${p.id}?supportsAllDrives=true`;
102053
+ const delRes = await fetch(delUrl, {
102054
+ method: 'DELETE',
102055
+ headers: { 'Authorization': `Bearer ${tok}` }
102056
+ });
102057
+ if (!delRes.ok) {
102058
+ const errText = await delRes.text();
102059
+ logToTerminal('error', 'Failed to remove Drive permission:', errText);
102060
+ }
102061
+ }
102062
+ if (!cancelled)
102063
+ setDrivePublicAccessWarning(false);
102064
+ }
102065
+ else if (!cancelled) {
102066
+ setDrivePublicAccessWarning(true);
102067
+ }
102068
+ }
102069
+ else if (!cancelled) {
102070
+ setDrivePublicAccessWarning(false);
102071
+ }
102072
+ }
102073
+ }
102074
+ if (cancelled)
102075
+ return;
102076
+ const currentToken = await getValidAccessToken();
102077
+ if (!currentToken || cancelled) {
102078
+ if (!cancelled)
102079
+ setRootFolderInfo(null);
102080
+ return;
102081
+ }
102082
+ const fields = 'files(id, name)';
102083
+ const qOffers = `'${rootFolderId}' in parents and trashed = false and mimeType = 'application/vnd.google-apps.folder'`;
102084
+ const offersUrl = `https://www.googleapis.com/drive/v3/files?q=${encodeURIComponent(qOffers)}&fields=${encodeURIComponent(fields)}&supportsAllDrives=true`;
102085
+ const offersResponse = await fetch(offersUrl, {
102086
+ headers: { 'Authorization': `Bearer ${currentToken}` }
102087
+ });
102088
+ if (cancelled)
102089
+ return;
102090
+ let hasOffersFolder = false;
102091
+ if (offersResponse.ok) {
102092
+ const offersData = await offersResponse.json();
102093
+ hasOffersFolder = (offersData.files || []).some((f) => (f.name || '').toUpperCase() === 'OFFERS');
102094
+ }
102095
+ if (!cancelled) {
102096
+ setRootFolderInfo({ hasOffersFolder });
102097
+ }
102098
+ }
102099
+ catch (err) {
102100
+ if (!cancelled)
102101
+ setRootFolderInfo(null);
102102
+ }
102103
+ finally {
102104
+ rootFolderCheckRunningRef.current = false;
102105
+ if (!cancelled)
102106
+ setCheckingRootFolder(false);
102107
+ }
102108
+ };
102109
+ const timeoutId = setTimeout(() => {
102110
+ checkRootFolder();
102111
+ }, 500);
102112
+ return () => {
102113
+ cancelled = true;
102114
+ clearTimeout(timeoutId);
102115
+ };
102116
+ }, [rootDriveFolderUrl, accessToken, refreshToken]);
102117
+ // Рабочая папка Google Drive: product и крео-изображения
101881
102118
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
101882
102119
  let cancelled = false;
101883
102120
  const checkFolderFiles = async () => {
101884
- // Prevent concurrent runs (e.g. if token refresh re-triggers the effect while a check is in progress)
101885
102121
  if (folderCheckRunningRef.current)
101886
102122
  return;
101887
102123
  if (!driveFolderUrl.trim()) {
@@ -101903,47 +102139,38 @@ function App() {
101903
102139
  folderCheckRunningRef.current = true;
101904
102140
  setCheckingFolderFiles(true);
101905
102141
  try {
101906
- // Only check/ask about permissions if we haven't already handled this folder
101907
- if (!permissionCheckedFoldersRef.current.has(folderId)) {
101908
- const permissionsUrl = `https://www.googleapis.com/drive/v3/files/${folderId}?fields=permissions(id,type,role)`;
101909
- const permissionsResponse = await fetch(permissionsUrl, {
101910
- headers: {
101911
- 'Authorization': `Bearer ${validToken}`
101912
- }
101913
- });
102142
+ if (!driveFolderPublicLinkPromptedRef.current.has(folderId)) {
102143
+ const permResult = await fetchDriveFilePermissions(validToken, folderId);
101914
102144
  if (cancelled)
101915
102145
  return;
101916
- if (permissionsResponse.ok) {
101917
- const permissionsData = await permissionsResponse.json();
101918
- const hasPublicLinkAccess = permissionsData.permissions?.some((p) => p.type === 'anyone' && p.role === 'reader');
102146
+ if (!permResult.ok) {
102147
+ logToTerminal('warn', 'Could not read offer folder permissions (will retry on next check):', permResult.errorText);
102148
+ }
102149
+ else {
102150
+ const hasPublicLinkAccess = permissionsIncludeAnyoneLink(permResult.permissions);
101919
102151
  if (hasPublicLinkAccess) {
101920
- // Already accessible — remember so we don't ask again
101921
- permissionCheckedFoldersRef.current.add(folderId);
102152
+ driveFolderPublicLinkPromptedRef.current.add(folderId);
101922
102153
  }
101923
102154
  else {
101924
- // Mark as checked BEFORE showing confirm to prevent duplicate prompts
101925
- permissionCheckedFoldersRef.current.add(folderId);
101926
102155
  if (cancelled)
101927
102156
  return;
101928
- // Ask user for consent to enable public link access
101929
- const userConsent = confirm('⚠️ Папка Google Drive не доступна всем по ссылке.\n\n' +
101930
- 'Для работы приложения необходимо сделать папку доступной по ссылке.\n\n' +
101931
- 'Разрешить автоматически включить доступ по ссылке для этой папки?');
102157
+ const userConsent = window.confirm('⚠️ Папка оффера на Google Диске не открыта для всех по ссылке.\n\n' +
102158
+ 'Сервисы генерации изображений (OpenRouter) скачивают картинки по публичному URL без доступа по ссылке запросы могут падать с ошибкой.\n\n' +
102159
+ 'Включить доступ по ссылке для этой папки (читатель) автоматически?');
101932
102160
  if (cancelled)
101933
102161
  return;
101934
102162
  if (userConsent) {
101935
- // Automatically enable public link access via API
101936
- const currentToken = await getValidAccessToken();
101937
- if (!currentToken || cancelled) {
102163
+ const tok = await getValidAccessToken();
102164
+ if (!tok || cancelled) {
101938
102165
  if (!cancelled)
101939
102166
  setFolderFilesInfo(null);
101940
102167
  return;
101941
102168
  }
101942
- const createPermissionUrl = `https://www.googleapis.com/drive/v3/files/${folderId}/permissions`;
102169
+ const createPermissionUrl = `https://www.googleapis.com/drive/v3/files/${folderId}/permissions?supportsAllDrives=true`;
101943
102170
  const createPermissionResponse = await fetch(createPermissionUrl, {
101944
102171
  method: 'POST',
101945
102172
  headers: {
101946
- 'Authorization': `Bearer ${currentToken}`,
102173
+ 'Authorization': `Bearer ${tok}`,
101947
102174
  'Content-Type': 'application/json'
101948
102175
  },
101949
102176
  body: JSON.stringify({
@@ -101955,22 +102182,34 @@ function App() {
101955
102182
  return;
101956
102183
  if (!createPermissionResponse.ok) {
101957
102184
  const errorText = await createPermissionResponse.text();
101958
- logToTerminal('error', 'Failed to enable public link access:', errorText);
101959
- alert('⚠️ Не удалось автоматически включить доступ по ссылке для папки Google Drive.\n\nПожалуйста, сделайте папку доступной по ссылке вручную:\n1. Откройте папку в Google Drive\n2. Нажмите "Настройки доступа"\n3. Выберите "Все, у кого есть ссылка"\n4. Убедитесь, что роль установлена как "Читатель"');
102185
+ logToTerminal('error', 'Failed to enable public link access for offer folder:', errorText);
102186
+ window.alert('⚠️ Не удалось автоматически включить доступ по ссылке.\n\n' +
102187
+ 'Сделайте папку доступной по ссылке вручную в Google Диске (роль «Читатель»).');
102188
+ // Не помечаем folderId — при следующей проверке снова предложим / повторим
101960
102189
  }
101961
102190
  else {
101962
- logToTerminal('log', '✅ Public link access enabled for folder');
102191
+ const verify = await fetchDriveFilePermissions(tok, folderId);
102192
+ if (verify.ok && permissionsIncludeAnyoneLink(verify.permissions)) {
102193
+ driveFolderPublicLinkPromptedRef.current.add(folderId);
102194
+ logToTerminal('log', '✅ Public link access enabled for offer folder (verified)');
102195
+ }
102196
+ else {
102197
+ logToTerminal('warn', 'Permission API succeeded but anyone-link not visible on re-fetch; user should verify in Drive UI');
102198
+ window.alert('⚠️ Запрос на доступ отправлен, но повторная проверка не увидела права «для всех по ссылке».\n\n' +
102199
+ 'Проверьте папку в Google Диске вручную: общий доступ → «Все, у кого есть ссылка» → Читатель.');
102200
+ }
101963
102201
  }
101964
102202
  }
101965
102203
  else {
101966
- alert('⚠️ Папка должна быть доступна по ссылке для работы приложения.\n\nПожалуйста, сделайте папку доступной по ссылке вручную:\n1. Откройте папку в Google Drive\n2. Нажмите "Настройки доступа"\n3. Выберите "Все, у кого есть ссылка"\n4. Убедитесь, что роль установлена как "Читатель"');
102204
+ driveFolderPublicLinkPromptedRef.current.add(folderId);
102205
+ window.alert('⚠️ Без доступа по ссылке генерация с референсами из этой папки может не работать.\n\n' +
102206
+ 'Включите вручную: Настройки доступа → «Все, у кого есть ссылка» → Читатель.');
101967
102207
  }
101968
102208
  }
101969
102209
  }
101970
102210
  }
101971
102211
  if (cancelled)
101972
102212
  return;
101973
- // Then check folder files
101974
102213
  const currentToken = await getValidAccessToken();
101975
102214
  if (!currentToken || cancelled) {
101976
102215
  if (!cancelled)
@@ -101979,7 +102218,7 @@ function App() {
101979
102218
  }
101980
102219
  const q = `'${folderId}' in parents and trashed = false and mimeType contains 'image/'`;
101981
102220
  const fields = 'files(id, name)';
101982
- const url = `https://www.googleapis.com/drive/v3/files?q=${encodeURIComponent(q)}&fields=${encodeURIComponent(fields)}`;
102221
+ const url = `https://www.googleapis.com/drive/v3/files?q=${encodeURIComponent(q)}&fields=${encodeURIComponent(fields)}&supportsAllDrives=true`;
101983
102222
  const response = await fetch(url, {
101984
102223
  headers: {
101985
102224
  'Authorization': `Bearer ${currentToken}`
@@ -101987,30 +102226,28 @@ function App() {
101987
102226
  });
101988
102227
  if (cancelled)
101989
102228
  return;
101990
- if (response.ok) {
101991
- const data = await response.json();
101992
- const hasProduct = data.files.some((f) => {
101993
- const name = f.name?.toLowerCase() || '';
101994
- return name === 'product.png' || name === 'product.jpg' || name === 'product.webp';
101995
- });
101996
- const hasCreativeImages = data.files.some((f) => {
101997
- const name = f.name?.toLowerCase() || '';
101998
- return name !== 'product.png' && name !== 'product.jpg' && name !== 'product.webp';
101999
- });
102000
- if (!cancelled) {
102001
- setFolderFilesInfo({ hasProduct, hasCreativeImages });
102002
- // If product not found, poll every 10 seconds until found
102003
- if (!hasProduct) {
102004
- pollTimeoutId = setTimeout(() => {
102005
- if (!cancelled)
102006
- checkFolderFiles();
102007
- }, 10_000);
102008
- }
102009
- }
102010
- }
102011
- else {
102229
+ if (!response.ok) {
102012
102230
  if (!cancelled)
102013
102231
  setFolderFilesInfo(null);
102232
+ return;
102233
+ }
102234
+ const data = await response.json();
102235
+ const hasProduct = data.files.some((f) => {
102236
+ const name = f.name?.toLowerCase() || '';
102237
+ return name === 'product.png' || name === 'product.jpg' || name === 'product.webp';
102238
+ });
102239
+ const hasCreativeImages = data.files.some((f) => {
102240
+ const name = f.name?.toLowerCase() || '';
102241
+ return name !== 'product.png' && name !== 'product.jpg' && name !== 'product.webp';
102242
+ });
102243
+ if (!cancelled) {
102244
+ setFolderFilesInfo({ hasProduct, hasCreativeImages });
102245
+ if (!hasProduct) {
102246
+ pollTimeoutId = setTimeout(() => {
102247
+ if (!cancelled)
102248
+ checkFolderFiles();
102249
+ }, 10_000);
102250
+ }
102014
102251
  }
102015
102252
  }
102016
102253
  catch (err) {
@@ -102024,7 +102261,6 @@ function App() {
102024
102261
  }
102025
102262
  };
102026
102263
  let pollTimeoutId = null;
102027
- // Debounce check
102028
102264
  const timeoutId = setTimeout(() => {
102029
102265
  checkFolderFiles();
102030
102266
  }, 500);
@@ -103131,7 +103367,7 @@ function App() {
103131
103367
  setTexts(['']);
103132
103368
  setPairTranslations({});
103133
103369
  // Read pairs count from settings (3–10, default 3)
103134
- const pairsCountInit = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_52__.getPairsCount)();
103370
+ const pairsCountInit = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_55__.getPairsCount)();
103135
103371
  // Initialize placeholders
103136
103372
  const initialTitles = Array.from({ length: pairsCountInit }, (_, index) => ({
103137
103373
  index: index + 1,
@@ -103160,7 +103396,7 @@ function App() {
103160
103396
  }
103161
103397
  // Generate all pairs (title + text) in a single request
103162
103398
  addLog(formatLogMessage('log', '📋 Generating title+text pairs in a single request...'));
103163
- const selectedIndices = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_52__.getSelectedPairApproaches)();
103399
+ const selectedIndices = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_55__.getSelectedPairApproaches)();
103164
103400
  setLastUsedApproachIndices(selectedIndices);
103165
103401
  const pairsCount = selectedIndices.length;
103166
103402
  addLog(formatLogMessage('log', `⚙️ Generating ${pairsCount} pairs (approaches: ${selectedIndices.join(', ')})`));
@@ -104099,7 +104335,7 @@ function App() {
104099
104335
  addLog(formatLogMessage(level, ...args));
104100
104336
  };
104101
104337
  logMsg('log', '📦 Creating ZIP archive with HTML and product image...');
104102
- const zip = new (jszip__WEBPACK_IMPORTED_MODULE_54___default())();
104338
+ const zip = new (jszip__WEBPACK_IMPORTED_MODULE_57___default())();
104103
104339
  // Replace product image path in HTML to match actual filename (png/jpg/webp)
104104
104340
  const htmlWithProductPath = htmlContent.replace(/src=["']product\.(png|jpe?g|webp)["']/gi, `src="${productImageName}"`);
104105
104341
  zip.file('index.html', htmlWithProductPath);
@@ -104421,6 +104657,15 @@ function App() {
104421
104657
  throw new Error(errorMsg);
104422
104658
  }
104423
104659
  addLog(formatLogMessage('log', '✅ Folder ID extracted:', folderId));
104660
+ addLog(formatLogMessage('log', '🔒 Проверка доступа по ссылке к папке оффера (нужен для референса)...'));
104661
+ const pubGate = await verifyOfferFolderHasAnyoneLinkForImageGen(validToken, folderId);
104662
+ if (!pubGate.ok) {
104663
+ addLog(formatLogMessage('error', '❌ ' + pubGate.message));
104664
+ alert(pubGate.message);
104665
+ setGeneratingImages(false);
104666
+ return;
104667
+ }
104668
+ addLog(formatLogMessage('log', '✅ Доступ по ссылке к папке оффера подтверждён'));
104424
104669
  // Check for product image (png/jpg/webp)
104425
104670
  addLog(formatLogMessage('log', '🔍 Checking for product image...'));
104426
104671
  const productImage = await fetchProductImage(folderId);
@@ -104731,11 +104976,26 @@ function App() {
104731
104976
  alert('Не удалось найти оригинальный промпт или изображение продукта для переделки');
104732
104977
  return;
104733
104978
  }
104979
+ const regenFolderId = extractFolderId(driveFolderUrl);
104980
+ if (!regenFolderId) {
104981
+ alert('Укажите корректный URL папки оффера на Google Drive');
104982
+ return;
104983
+ }
104734
104984
  const addLog = (msg) => {
104735
104985
  setImagesGenerationLogs(prev => [...prev, msg]);
104736
104986
  logToTerminal('log', msg.replace(/\[.*?\]\s*/, ''));
104737
104987
  };
104738
104988
  try {
104989
+ const regenTok = await getValidAccessToken();
104990
+ if (!regenTok) {
104991
+ alert('Please log in with Google first');
104992
+ return;
104993
+ }
104994
+ const regenPub = await verifyOfferFolderHasAnyoneLinkForImageGen(regenTok, regenFolderId);
104995
+ if (!regenPub.ok) {
104996
+ alert(regenPub.message);
104997
+ return;
104998
+ }
104739
104999
  // Get current customRegeneratePrompt from state
104740
105000
  const currentImageData = generatedImagesData.find(img => img.index === imageData.index);
104741
105001
  const customPrompt = currentImageData?.customRegeneratePrompt?.trim() || '';
@@ -104956,11 +105216,26 @@ ${imageData.originalPrompt}
104956
105216
  alert('Не удалось найти оригинальный промпт или изображение продукта для переделки');
104957
105217
  return;
104958
105218
  }
105219
+ const freshFolderId = extractFolderId(driveFolderUrl);
105220
+ if (!freshFolderId) {
105221
+ alert('Укажите корректный URL папки оффера на Google Drive');
105222
+ return;
105223
+ }
104959
105224
  const addLog = (msg) => {
104960
105225
  setImagesGenerationLogs(prev => [...prev, msg]);
104961
105226
  logToTerminal('log', msg.replace(/\[.*?\]\s*/, ''));
104962
105227
  };
104963
105228
  try {
105229
+ const freshTok = await getValidAccessToken();
105230
+ if (!freshTok) {
105231
+ alert('Please log in with Google first');
105232
+ return;
105233
+ }
105234
+ const freshPub = await verifyOfferFolderHasAnyoneLinkForImageGen(freshTok, freshFolderId);
105235
+ if (!freshPub.ok) {
105236
+ alert(freshPub.message);
105237
+ return;
105238
+ }
104964
105239
  // Mark as regenerating (show overlay timer) - clear old image URL immediately to force re-render
104965
105240
  setGeneratedImagesData(prev => prev.map(img => img.index === imageData.index
104966
105241
  ? {
@@ -105459,8 +105734,8 @@ ${imageData.originalPrompt}
105459
105734
  catalogUrlTextApproachParam,
105460
105735
  catalogUrlCreoApproachParam,
105461
105736
  catalogLinkExtraMacros,
105462
- selectedPairApproaches: (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_52__.getSelectedPairApproaches)(),
105463
- imageApproachCounts: (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_52__.getImageApproachCounts)(),
105737
+ selectedPairApproaches: (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_55__.getSelectedPairApproaches)(),
105738
+ imageApproachCounts: (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_55__.getImageApproachCounts)(),
105464
105739
  savedAt: new Date().toISOString()
105465
105740
  };
105466
105741
  const jsonContent = JSON.stringify(dataToSave, null, 2);
@@ -105627,9 +105902,23 @@ ${imageData.originalPrompt}
105627
105902
  else {
105628
105903
  setCatalogLinkExtraMacros(DEFAULT_CATALOG_LINK_EXTRA_MACROS);
105629
105904
  }
105630
- const mergedApproaches = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_52__.mergePromptApproachesFromDriveFile)(loadedData);
105631
- if (mergedApproaches) {
105632
- logToTerminal('log', '[Load Content] Applied text + image approach settings from Drive JSON');
105905
+ const parsedApproaches = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_55__.parseApproachesFromDriveData)(loadedData);
105906
+ const currentPairs = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_55__.getSelectedPairApproaches)();
105907
+ const currentCounts = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_55__.getImageApproachCounts)();
105908
+ const hasDrivePairs = parsedApproaches.pairs !== null;
105909
+ const hasDriveCounts = parsedApproaches.counts !== null;
105910
+ if (hasDrivePairs || hasDriveCounts) {
105911
+ const pairsDiffer = hasDrivePairs && !sortedPairIndicesEqual(parsedApproaches.pairs, currentPairs);
105912
+ const countsDiffer = hasDriveCounts && !imageApproachCountsEqual(parsedApproaches.counts, currentCounts);
105913
+ if (pairsDiffer || countsDiffer) {
105914
+ setApproachLoadChoice({
105915
+ savedPairs: parsedApproaches.pairs,
105916
+ savedCounts: parsedApproaches.counts,
105917
+ currentPairs,
105918
+ currentCounts,
105919
+ });
105920
+ logToTerminal('log', '[Load Content] Offer file contains approach settings — asking user to apply or keep current');
105921
+ }
105633
105922
  }
105634
105923
  logToTerminal('log', '[Load Content] Successfully loaded content');
105635
105924
  setDriveFilesFound(prev => ({ ...prev, content: false }));
@@ -105641,6 +105930,19 @@ ${imageData.originalPrompt}
105641
105930
  return { found: false };
105642
105931
  }
105643
105932
  };
105933
+ const handleApproachLoadApplyFromFile = () => {
105934
+ if (!approachLoadChoice)
105935
+ return;
105936
+ const applied = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_55__.applyParsedApproachesFromDrive)(approachLoadChoice.savedPairs, approachLoadChoice.savedCounts);
105937
+ if (applied) {
105938
+ logToTerminal('log', '[Load Content] Applied approach settings from offer file (user choice)');
105939
+ }
105940
+ setApproachLoadChoice(null);
105941
+ };
105942
+ const handleApproachLoadKeepCurrent = () => {
105943
+ logToTerminal('log', '[Load Content] Kept local approach settings (user choice)');
105944
+ setApproachLoadChoice(null);
105945
+ };
105644
105946
  syncDriveAfterPromptSaveRef.current = () => {
105645
105947
  if (!driveFolderUrl || loadingContentFromDrive)
105646
105948
  return;
@@ -105659,8 +105961,8 @@ ${imageData.originalPrompt}
105659
105961
  };
105660
105962
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
105661
105963
  const listener = () => syncDriveAfterPromptSaveRef.current();
105662
- window.addEventListener(_promptOverrides__WEBPACK_IMPORTED_MODULE_52__.PROMPT_OVERRIDES_SAVED_EVENT, listener);
105663
- return () => window.removeEventListener(_promptOverrides__WEBPACK_IMPORTED_MODULE_52__.PROMPT_OVERRIDES_SAVED_EVENT, listener);
105964
+ window.addEventListener(_promptOverrides__WEBPACK_IMPORTED_MODULE_55__.PROMPT_OVERRIDES_SAVED_EVENT, listener);
105965
+ return () => window.removeEventListener(_promptOverrides__WEBPACK_IMPORTED_MODULE_55__.PROMPT_OVERRIDES_SAVED_EVENT, listener);
105664
105966
  }, []);
105665
105967
  const handleGenerate = async () => {
105666
105968
  if (!driveFolderUrl || !brand || !link) {
@@ -105741,8 +106043,8 @@ ${imageData.originalPrompt}
105741
106043
  }
105742
106044
  setGeneratedData(rows);
105743
106045
  // Create workbook
105744
- const wb = xlsx__WEBPACK_IMPORTED_MODULE_53__.utils.book_new();
105745
- const ws = xlsx__WEBPACK_IMPORTED_MODULE_53__.utils.aoa_to_sheet(rows);
106046
+ const wb = xlsx__WEBPACK_IMPORTED_MODULE_56__.utils.book_new();
106047
+ const ws = xlsx__WEBPACK_IMPORTED_MODULE_56__.utils.aoa_to_sheet(rows);
105746
106048
  // Set column widths (approximate pixel width / 7)
105747
106049
  ws['!cols'] = [
105748
106050
  { wch: 20 }, // id
@@ -105755,9 +106057,9 @@ ${imageData.originalPrompt}
105755
106057
  { wch: 40 }, // image_link
105756
106058
  { wch: 20 } // brand
105757
106059
  ];
105758
- xlsx__WEBPACK_IMPORTED_MODULE_53__.utils.book_append_sheet(wb, ws, "Products");
106060
+ xlsx__WEBPACK_IMPORTED_MODULE_56__.utils.book_append_sheet(wb, ws, "Products");
105759
106061
  // Generate buffer
105760
- const wbout = xlsx__WEBPACK_IMPORTED_MODULE_53__.write(wb, { bookType: 'xlsx', type: 'array' });
106062
+ const wbout = xlsx__WEBPACK_IMPORTED_MODULE_56__.write(wb, { bookType: 'xlsx', type: 'array' });
105761
106063
  // Upload to Drive (имя файла по бренду)
105762
106064
  const dateStr = new Date().toISOString().split('T')[0];
105763
106065
  const fileName = `${brand}-${dateStr}.xlsx`;
@@ -105879,13 +106181,13 @@ ${imageData.originalPrompt}
105879
106181
  setTestLoading(true);
105880
106182
  try {
105881
106183
  // Create simple test workbook with structure
105882
- const wb = xlsx__WEBPACK_IMPORTED_MODULE_53__.utils.book_new();
106184
+ const wb = xlsx__WEBPACK_IMPORTED_MODULE_56__.utils.book_new();
105883
106185
  const rows = [
105884
106186
  INSTRUCTION_ROW,
105885
106187
  ['id', 'title', 'description', 'availability', 'condition', 'price', 'link', 'image_link', 'brand'],
105886
106188
  ['test1', 'Test Title', 'Test Description', 'in stock', 'new', '10.00 USD', 'http://test.com', 'http://test.com/img.jpg', 'TestBrand']
105887
106189
  ];
105888
- const ws = xlsx__WEBPACK_IMPORTED_MODULE_53__.utils.aoa_to_sheet(rows);
106190
+ const ws = xlsx__WEBPACK_IMPORTED_MODULE_56__.utils.aoa_to_sheet(rows);
105889
106191
  // Set column widths
105890
106192
  ws['!cols'] = [
105891
106193
  { wch: 20 }, // id
@@ -105898,8 +106200,8 @@ ${imageData.originalPrompt}
105898
106200
  { wch: 40 }, // image_link
105899
106201
  { wch: 20 } // brand
105900
106202
  ];
105901
- xlsx__WEBPACK_IMPORTED_MODULE_53__.utils.book_append_sheet(wb, ws, "Test");
105902
- const wbout = xlsx__WEBPACK_IMPORTED_MODULE_53__.write(wb, { bookType: 'xlsx', type: 'array' });
106203
+ xlsx__WEBPACK_IMPORTED_MODULE_56__.utils.book_append_sheet(wb, ws, "Test");
106204
+ const wbout = xlsx__WEBPACK_IMPORTED_MODULE_56__.write(wb, { bookType: 'xlsx', type: 'array' });
105903
106205
  // Try to extract folder ID if available, otherwise upload to root
105904
106206
  const folderId = driveFolderUrl ? extractFolderId(driveFolderUrl) : undefined;
105905
106207
  const result = await uploadFileToDrive(wbout, 'test_table.xlsx', folderId || undefined);
@@ -105951,7 +106253,7 @@ ${imageData.originalPrompt}
105951
106253
  };
105952
106254
  // Show lock screen if not unlocked
105953
106255
  if (!unlocked) {
105954
- return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_33__["default"], { theme: theme },
106256
+ return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_37__["default"], { theme: theme },
105955
106257
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_4__["default"], null),
105956
106258
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { onClick: handleSecretClick, sx: {
105957
106259
  width: '100vw',
@@ -105991,35 +106293,49 @@ ${imageData.originalPrompt}
105991
106293
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("br", null),
105992
106294
  "Please contact system administrator"))));
105993
106295
  }
105994
- return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_33__["default"], { theme: theme },
106296
+ return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_37__["default"], { theme: theme },
105995
106297
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_4__["default"], null),
105996
106298
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { maxWidth: "lg", sx: { py: 4 } },
105997
106299
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 } },
105998
106300
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h4", component: "h1", sx: { fontWeight: 'bold', color: 'primary.main' } }, "Docs Combiner"),
105999
106301
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
106000
106302
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_8__["default"], { onClick: () => setPromptManagerOpen(true), color: "inherit", "aria-label": "manage prompts", sx: { mr: 1 } },
106001
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_49__["default"], null)),
106002
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_8__["default"], { onClick: toggleTheme, color: "inherit", "aria-label": "toggle theme" }, darkMode ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_38__["default"], null) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_37__["default"], null)))),
106303
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_52__["default"], null)),
106304
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_8__["default"], { onClick: toggleTheme, color: "inherit", "aria-label": "toggle theme" }, darkMode ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_42__["default"], null) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_41__["default"], null)))),
106003
106305
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_9__["default"], { variant: "outlined", sx: { mb: 4 } },
106004
106306
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_10__["default"], null,
106005
106307
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { spacing: 3 },
106006
106308
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
106007
106309
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h6", gutterBottom: true }, "Google Drive Authentication"),
106008
- accessToken ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], null,
106009
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], { expandIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_43__["default"], null) },
106310
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { mb: 2 } },
106311
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "\u041A\u043E\u0440\u043D\u0435\u0432\u0430\u044F \u043F\u0430\u043F\u043A\u0430 \u043F\u0440\u043E\u0435\u043A\u0442\u0430"),
106312
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", color: "text.secondary", display: "block", sx: { mb: 1 } }, "\u0421\u0441\u044B\u043B\u043A\u0430 \u043D\u0430 \u043A\u043E\u0440\u043D\u0435\u0432\u0443\u044E \u043F\u0430\u043F\u043A\u0443 \u043D\u0430 Google \u0414\u0438\u0441\u043A\u0435 (\u0432\u043D\u0443\u0442\u0440\u0438 \u043D\u0435\u0451 \u0434\u043E\u043B\u0436\u043D\u0430 \u0431\u044B\u0442\u044C \u043F\u0430\u043F\u043A\u0430 OFFERS)."),
106313
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "URL \u043A\u043E\u0440\u043D\u0435\u0432\u043E\u0439 \u043F\u0430\u043F\u043A\u0438 Google Drive", variant: "outlined", fullWidth: true, value: rootDriveFolderUrl, onChange: (e) => setRootDriveFolderUrl(e.target.value), placeholder: "https://drive.google.com/drive/folders/...", sx: { mb: 1 } }),
106314
+ checkingRootFolder && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], null,
106315
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { component: "span", sx: { display: 'inline-flex', alignItems: 'center', gap: 1 } },
106316
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 14 }),
106317
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043A\u043E\u0440\u043D\u0435\u0432\u043E\u0439 \u043F\u0430\u043F\u043A\u0438 (OFFERS, \u0434\u043E\u0441\u0442\u0443\u043F)...")))),
106318
+ drivePublicAccessWarning && extractFolderId(rootDriveFolderUrl) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { severity: "warning", sx: { mt: 1 } }, "\u041E\u0431\u0449\u0438\u0439 \u0434\u043E\u0441\u0442\u0443\u043F \u043F\u043E \u0441\u0441\u044B\u043B\u043A\u0435 \u0432\u0441\u0451 \u0435\u0449\u0451 \u0432\u043A\u043B\u044E\u0447\u0451\u043D. \u0417\u0430\u043A\u0440\u043E\u0439\u0442\u0435 \u0435\u0433\u043E \u0432\u0440\u0443\u0447\u043D\u0443\u044E \u0432 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430\u0445 \u0434\u043E\u0441\u0442\u0443\u043F\u0430 Google \u0414\u0438\u0441\u043A\u0430 \u0438\u043B\u0438 \u0441\u043C\u0435\u043D\u0438\u0442\u0435 \u043F\u0430\u043F\u043A\u0443 \u0438 \u0441\u043E\u0433\u043B\u0430\u0441\u0438\u0442\u0435\u0441\u044C \u0443\u0431\u0440\u0430\u0442\u044C \u0434\u043E\u0441\u0442\u0443\u043F \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438.")),
106319
+ !checkingRootFolder && rootFolderInfo && !rootFolderInfo.hasOffersFolder && extractFolderId(rootDriveFolderUrl) && (accessToken || refreshToken) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { severity: "error", sx: { mt: 1 } }, "\u0412 \u043A\u043E\u0440\u043D\u0435\u0432\u043E\u0439 \u043F\u0430\u043F\u043A\u0435 \u043D\u0435\u0442 \u043F\u043E\u0434\u043F\u0430\u043F\u043A\u0438 OFFERS. \u0421\u043E\u0437\u0434\u0430\u0439\u0442\u0435 \u043F\u0430\u043F\u043A\u0443 \u0441 \u0438\u043C\u0435\u043D\u0435\u043C OFFERS (\u0440\u0435\u0433\u0438\u0441\u0442\u0440 \u043D\u0435 \u0432\u0430\u0436\u0435\u043D) \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443.")),
106320
+ !checkingRootFolder && !rootFolderInfo && rootDriveFolderUrl.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], null, (accessToken || refreshToken)
106321
+ ? 'Введите корректную ссылку на корневую папку Google Drive'
106322
+ : 'Войдите в Google, чтобы проверить корневую папку и подпапку OFFERS'))),
106323
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { sx: { my: 2 } }),
106324
+ accessToken ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], null,
106325
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { expandIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_47__["default"], null) },
106010
106326
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', color: 'success.main' } },
106011
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_39__["default"], { sx: { mr: 1 } }),
106327
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_43__["default"], { sx: { mr: 1 } }),
106012
106328
  " Logged In (Credentials Hidden)")),
106013
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], null,
106329
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], null,
106014
106330
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { spacing: 2 },
106015
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["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)" }),
106016
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { label: "Client Secret", variant: "outlined", fullWidth: true, value: clientSecret, onChange: (e) => handleClientSecretChange(e.target.value) }),
106331
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "Client ID", variant: "outlined", fullWidth: true, value: clientId, onChange: (e) => handleClientIdChange(e.target.value), helperText: "From Google Cloud Console (OAuth 2.0 Client ID)" }),
106332
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "Client Secret", variant: "outlined", fullWidth: true, value: clientSecret, onChange: (e) => handleClientSecretChange(e.target.value) }),
106017
106333
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], null),
106018
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { label: "OpenRouter API Key", variant: "outlined", fullWidth: true, value: openaiApiKey, onChange: (e) => handleOpenaiApiKeyChange(e.target.value), helperText: "Required for AI generation (images and text). Your key is stored locally. Get your key at openrouter.ai" }))))) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { spacing: 2 },
106019
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["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)" }),
106020
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { label: "Client Secret", variant: "outlined", fullWidth: true, value: clientSecret, onChange: (e) => handleClientSecretChange(e.target.value) }))),
106334
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "OpenRouter API Key", variant: "outlined", fullWidth: true, value: openaiApiKey, onChange: (e) => handleOpenaiApiKeyChange(e.target.value), helperText: "Required for AI generation (images and text). Your key is stored locally. Get your key at openrouter.ai" }))))) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { spacing: 2 },
106335
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "Client ID", variant: "outlined", fullWidth: true, value: clientId, onChange: (e) => handleClientIdChange(e.target.value), helperText: "From Google Cloud Console (OAuth 2.0 Client ID)" }),
106336
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "Client Secret", variant: "outlined", fullWidth: true, value: clientSecret, onChange: (e) => handleClientSecretChange(e.target.value) }))),
106021
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 } },
106022
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_34__["default"], { color: openRouterAccountBalance !== null ? 'primary' : 'disabled' }),
106338
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_38__["default"], { color: openRouterAccountBalance !== null ? 'primary' : 'disabled' }),
106023
106339
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", color: openRouterAccountBalance !== null ? 'text.primary' : 'text.secondary' },
106024
106340
  "\u0411\u0430\u043B\u0430\u043D\u0441: ",
106025
106341
  openRouterBalanceLoading ? 'Loading...' : (openRouterAccountBalance !== null ? `$${openRouterAccountBalance.toFixed(2)}` : 'N/A')),
@@ -106028,28 +106344,30 @@ ${imageData.originalPrompt}
106028
106344
  "\u041B\u0438\u043C\u0438\u0442 \u043A\u043B\u044E\u0447\u0430: ",
106029
106345
  openRouterBalanceLoading ? 'Loading...' : (openRouterBalance === -1 ? 'Без лимита' : (openRouterBalance !== null ? `${openRouterBalance.toFixed(4)}` : 'N/A'))))),
106030
106346
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: "row", spacing: 2, alignItems: "center", sx: { mt: 2 } },
106031
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["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_39__["default"], null) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_45__["default"], null)), sx: { flexGrow: 1 } }, authLoading ? 'Logging in...' : (accessToken ? 'Logged In' : 'Login with Google')),
106032
- accessToken && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { variant: "outlined", color: "error", onClick: handleLogout, startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_46__["default"], null) }, "Logout")))),
106347
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { variant: "contained", color: accessToken ? "success" : "primary", onClick: handleLogin, disabled: authLoading || !clientId || !clientSecret, startIcon: authLoading ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 20, color: "inherit" }) : (accessToken ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_43__["default"], null) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_48__["default"], null)), sx: { flexGrow: 1 } }, authLoading ? 'Logging in...' : (accessToken ? 'Logged In' : 'Login with Google')),
106348
+ accessToken && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { variant: "outlined", color: "error", onClick: handleLogout, startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_49__["default"], null) }, "Logout")))),
106033
106349
  !accessToken && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
106034
106350
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], null),
106035
106351
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
106036
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { label: "OpenRouter API Key", variant: "outlined", fullWidth: true, value: openaiApiKey, onChange: (e) => handleOpenaiApiKeyChange(e.target.value), helperText: "Required for AI generation (images and text). Your key is stored locally. Get your key at openrouter.ai", sx: { mb: 2 } })))),
106352
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "OpenRouter API Key", variant: "outlined", fullWidth: true, value: openaiApiKey, onChange: (e) => handleOpenaiApiKeyChange(e.target.value), helperText: "Required for AI generation (images and text). Your key is stored locally. Get your key at openrouter.ai", sx: { mb: 2 } })))),
106037
106353
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { sx: { my: 2 } }),
106038
106354
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
106039
106355
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h6", gutterBottom: true }, "Google Drive Folder"),
106040
106356
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: { xs: 'column', sm: 'row' }, spacing: 2 },
106041
106357
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { flexGrow: 1 } },
106042
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["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/...", error: false }),
106043
- checkingFolderFiles && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], null,
106358
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "Google Drive Folder URL", variant: "outlined", fullWidth: true, value: driveFolderUrl, onChange: (e) => setDriveFolderUrl(e.target.value), placeholder: "https://drive.google.com/drive/folders/..." }),
106359
+ checkingFolderFiles && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], null,
106044
106360
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { component: "span", sx: { display: 'inline-flex', alignItems: 'center', gap: 1 } },
106045
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 14 }),
106046
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u0444\u0430\u0439\u043B\u043E\u0432...")))),
106047
- !checkingFolderFiles && folderFilesInfo && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], null,
106361
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 14 }),
106362
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u0444\u0430\u0439\u043B\u043E\u0432 \u0432 \u043F\u0430\u043F\u043A\u0435...")))),
106363
+ !checkingFolderFiles && folderFilesInfo && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], null,
106048
106364
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { component: "span", sx: { display: 'block' } }, folderFilesInfo.hasProduct ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { component: "span", sx: { color: 'success.main' } }, "\u2713 product.png/jpg/webp \u043D\u0430\u0439\u0434\u0435\u043D")) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { component: "span", sx: { color: 'info.main' } }, "\u2139 product.png/jpg/webp \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D \u2014 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u0435 \u0447\u0435\u0440\u0435\u0437 \u043A\u043D\u043E\u043F\u043A\u0443 \u043D\u0438\u0436\u0435"))))),
106049
- !checkingFolderFiles && !folderFilesInfo && driveFolderUrl.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], null, "\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 Google Drive"))))),
106050
- !driveFolderUrl.trim() ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { severity: "info", sx: { mt: 2 } },
106365
+ !checkingFolderFiles && !folderFilesInfo && driveFolderUrl.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], null, (accessToken || refreshToken)
106366
+ ? 'Введите корректную ссылку на папку Google Drive'
106367
+ : 'Войдите в Google, чтобы проверить файлы в папке'))))),
106368
+ !driveFolderUrl.trim() ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { severity: "info", sx: { mt: 2 } },
106051
106369
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body1", sx: { fontWeight: 'bold', mb: 1 } }, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u043F\u0430\u043F\u043A\u0443 Google Drive \u0434\u043B\u044F \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0435\u043D\u0438\u044F"),
106052
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2" }, "\u0414\u043B\u044F \u0440\u0430\u0431\u043E\u0442\u044B \u0441 \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0435\u0439 \u043A\u043E\u043D\u0442\u0435\u043D\u0442\u0430 \u0438 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0439 \u043D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E \u0443\u043A\u0430\u0437\u0430\u0442\u044C \u043F\u0430\u043F\u043A\u0443 Google Drive. \u0412\u0441\u0435 \u0434\u0430\u043D\u043D\u044B\u0435 \u0431\u0443\u0434\u0443\u0442 \u0441\u043E\u0445\u0440\u0430\u043D\u044F\u0442\u044C\u0441\u044F \u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044C\u0441\u044F \u0438\u0437 \u044D\u0442\u043E\u0439 \u043F\u0430\u043F\u043A\u0438."))) : !extractFolderId(driveFolderUrl) ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { severity: "warning", sx: { mt: 2 } },
106370
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2" }, "\u0414\u043B\u044F \u0440\u0430\u0431\u043E\u0442\u044B \u0441 \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0435\u0439 \u043A\u043E\u043D\u0442\u0435\u043D\u0442\u0430 \u0438 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0439 \u043D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E \u0443\u043A\u0430\u0437\u0430\u0442\u044C \u043F\u0430\u043F\u043A\u0443 Google Drive."))) : !extractFolderId(driveFolderUrl) ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { severity: "warning", sx: { mt: 2 } },
106053
106371
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body1", sx: { fontWeight: 'bold', mb: 1 } }, "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u0443\u044E \u0441\u0441\u044B\u043B\u043A\u0443 \u043D\u0430 \u043F\u0430\u043F\u043A\u0443"),
106054
106372
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2" }, "\u0421\u0441\u044B\u043B\u043A\u0430 \u0434\u043E\u043B\u0436\u043D\u0430 \u0431\u044B\u0442\u044C \u0432 \u0444\u043E\u0440\u043C\u0430\u0442\u0435: https://drive.google.com/drive/folders/..."))) : loadingContentFromDrive ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: {
106055
106373
  display: 'flex',
@@ -106065,35 +106383,35 @@ ${imageData.originalPrompt}
106065
106383
  ? 'rgba(25, 118, 210, 0.1)'
106066
106384
  : 'rgba(25, 118, 210, 0.05)'
106067
106385
  } },
106068
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 40, sx: { color: 'primary.main' } }),
106386
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 40, sx: { color: 'primary.main' } }),
106069
106387
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body1", sx: { color: 'text.secondary', fontWeight: 'bold' } }, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u0434\u0430\u043D\u043D\u044B\u0445 \u0438\u0437 \u043F\u0430\u043F\u043A\u0438..."))) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
106070
106388
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: { xs: 'column', sm: 'row' }, spacing: 2 },
106071
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["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" }),
106389
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "Brand (Short ID)", variant: "outlined", sx: { flex: '0 0 320px', minWidth: 280 }, value: brand, InputProps: { readOnly: true }, placeholder: "\u0410\u0432\u0442\u043E: \u0442\u043E\u0432\u0430\u0440-\u0433\u0435\u043E-\u0446\u0435\u043D\u0430", helperText: "\u0410\u0432\u0442\u043E\u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u044F \u0438\u0437 \u0442\u043E\u0432\u0430\u0440\u0430, \u0433\u0435\u043E \u0438 \u0446\u0435\u043D\u044B" }),
106072
106390
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { flex: 1, minWidth: 0 } },
106073
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { label: "Link", variant: "outlined", fullWidth: true, value: link, onChange: (e) => handleLinkChange(e.target.value), onBlur: handleLinkBlur, error: !!linkError, helperText: linkError, placeholder: "https://example.com/product/", inputRef: linkInputRef, InputProps: { onPaste: handleLinkPaste } }))),
106391
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "Link", variant: "outlined", fullWidth: true, value: link, onChange: (e) => handleLinkChange(e.target.value), onBlur: handleLinkBlur, error: !!linkError, helperText: linkError, placeholder: "https://example.com/product/", inputRef: linkInputRef, InputProps: { onPaste: handleLinkPaste } }))),
106074
106392
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', gap: 1, alignItems: 'flex-start', mt: 1.5 } },
106075
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { label: "\u0414\u043E\u043F. \u043C\u0430\u043A\u0440\u043E\u0441\u044B (\u043A\u0430\u0442\u0430\u043B\u043E\u0433)", variant: "outlined", size: "small", fullWidth: true, multiline: true, minRows: 2, value: catalogLinkExtraMacros, onChange: (e) => setCatalogLinkExtraMacros(e.target.value), placeholder: DEFAULT_CATALOG_LINK_EXTRA_MACROS, helperText: "\u0414\u043E\u0431\u0430\u0432\u043B\u044F\u0435\u0442\u0441\u044F \u043F\u043E\u0441\u043B\u0435 creative_id \u0438 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u043E\u0432 \u043F\u043E\u0434\u0445\u043E\u0434\u043E\u0432. \u0421\u043E\u0445\u0440\u0430\u043D\u044F\u0435\u0442\u0441\u044F \u0432 JSON \u043D\u0430\u0441\u0442\u0440\u043E\u0435\u043A \u043F\u0430\u043F\u043A\u0438.", sx: { '& .MuiInputBase-input': { fontFamily: 'monospace', fontSize: 12 } } }),
106393
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "\u0414\u043E\u043F. \u043C\u0430\u043A\u0440\u043E\u0441\u044B (\u043A\u0430\u0442\u0430\u043B\u043E\u0433)", variant: "outlined", size: "small", fullWidth: true, multiline: true, minRows: 2, value: catalogLinkExtraMacros, onChange: (e) => setCatalogLinkExtraMacros(e.target.value), placeholder: DEFAULT_CATALOG_LINK_EXTRA_MACROS, helperText: "\u0414\u043E\u0431\u0430\u0432\u043B\u044F\u0435\u0442\u0441\u044F \u043F\u043E\u0441\u043B\u0435 creative_id \u0438 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u043E\u0432 \u043F\u043E\u0434\u0445\u043E\u0434\u043E\u0432. \u0421\u043E\u0445\u0440\u0430\u043D\u044F\u0435\u0442\u0441\u044F \u0432 JSON \u043D\u0430\u0441\u0442\u0440\u043E\u0435\u043A \u043F\u0430\u043F\u043A\u0438.", sx: { '& .MuiInputBase-input': { fontFamily: 'monospace', fontSize: 12 } } }),
106076
106394
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { spacing: 0.5, sx: { flexShrink: 0, mt: 0.5 } },
106077
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { size: "small", variant: "outlined", onClick: () => setCatalogLinkExtraMacros(DEFAULT_CATALOG_LINK_EXTRA_MACROS), sx: { textTransform: 'none', whiteSpace: 'nowrap' } }, "redtrack"),
106078
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { size: "small", variant: "outlined", onClick: () => setCatalogLinkExtraMacros(DEFAULT_CATALOG_LINK_KEITARO_MACROS), sx: { textTransform: 'none', whiteSpace: 'nowrap' } }, "keitaro"))),
106395
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { size: "small", variant: "outlined", onClick: () => setCatalogLinkExtraMacros(DEFAULT_CATALOG_LINK_EXTRA_MACROS), sx: { textTransform: 'none', whiteSpace: 'nowrap' } }, "redtrack"),
106396
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { size: "small", variant: "outlined", onClick: () => setCatalogLinkExtraMacros(DEFAULT_CATALOG_LINK_KEITARO_MACROS), sx: { textTransform: 'none', whiteSpace: 'nowrap' } }, "keitaro"))),
106079
106397
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { spacing: 1.5, sx: { mt: 1 } },
106080
106398
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: { xs: 'column', sm: 'row' }, spacing: 2, alignItems: { xs: 'stretch', sm: 'flex-start' } },
106081
106399
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { control: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { checked: catalogUrlIncludeTextApproach, onChange: e => setCatalogUrlIncludeTextApproach(e.target.checked), size: "small" }), label: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
106082
106400
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2" }, "\u041F\u043E\u0434\u0445\u043E\u0434 \u043A \u0442\u0435\u043A\u0441\u0442\u0443 \u0432 URL \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430"),
106083
106401
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", color: "text.secondary" }, "\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u2014 \u043D\u043E\u043C\u0435\u0440 \u0441\u0442\u0440\u043E\u043A\u0438 \u043F\u043E\u0434\u0445\u043E\u0434\u0430 \u0434\u043B\u044F \u043F\u0430\u0440 \u0432 \u0442\u0430\u0431\u043B\u0438\u0446\u0435 \u043D\u0430\u0441\u0442\u0440\u043E\u0435\u043A (1\u201310)")), sx: { alignItems: 'flex-start', mr: 0, flex: { sm: '1 1 200px' }, minWidth: 0 } }),
106084
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["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}` })),
106402
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "\u041F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 \u0434\u043B\u044F \u0442\u0435\u043A\u0441\u0442\u0430", size: "small", value: catalogUrlTextApproachParam, onChange: e => setCatalogUrlTextApproachParam(e.target.value), placeholder: DEFAULT_CATALOG_TEXT_APPROACH_PARAM, sx: { width: { xs: '100%', sm: 200 }, flexShrink: 0 }, helperText: `По умолчанию: ${DEFAULT_CATALOG_TEXT_APPROACH_PARAM}` })),
106085
106403
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: { xs: 'column', sm: 'row' }, spacing: 2, alignItems: { xs: 'stretch', sm: 'flex-start' } },
106086
106404
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { control: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { checked: catalogUrlIncludeCreoApproach, onChange: e => setCatalogUrlIncludeCreoApproach(e.target.checked), size: "small" }), label: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
106087
106405
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2" }, "\u041F\u043E\u0434\u0445\u043E\u0434 \u043A \u043A\u0440\u0435\u043E \u0432 URL \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430"),
106088
106406
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", color: "text.secondary" }, "\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u2014 \u043D\u043E\u043C\u0435\u0440 N \u0438\u0437 \u0438\u043C\u0435\u043D\u0438 \u0444\u0430\u0439\u043B\u0430 N_xxxxxxxx.png \u043D\u0430 Drive (N = \u0441\u0442\u0440\u043E\u043A\u0430 \u043F\u043E\u0434\u0445\u043E\u0434\u0430 \u043A \u043A\u0440\u0435\u043E, 1\u201310); \u00AB (1)\u00BB \u043E\u0442 \u0434\u0443\u0431\u043B\u0438\u043A\u0430\u0442\u0430 Google \u0441\u043D\u0438\u043C\u0430\u0435\u0442\u0441\u044F")), sx: { alignItems: 'flex-start', mr: 0, flex: { sm: '1 1 200px' }, minWidth: 0 } }),
106089
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["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}` }))),
106407
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "\u041F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 \u0434\u043B\u044F \u043A\u0440\u0435\u043E", size: "small", value: catalogUrlCreoApproachParam, onChange: e => setCatalogUrlCreoApproachParam(e.target.value), placeholder: DEFAULT_CATALOG_CREO_APPROACH_PARAM, sx: { width: { xs: '100%', sm: 200 }, flexShrink: 0 }, helperText: `По умолчанию: ${DEFAULT_CATALOG_CREO_APPROACH_PARAM}` }))),
106090
106408
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { sx: { my: 2 } }),
106091
106409
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h6", gutterBottom: true }, "AI Generation Settings"),
106092
106410
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
106093
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["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 } })),
106411
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "\u0422\u043E\u0432\u0430\u0440", variant: "outlined", fullWidth: true, value: generateProduct, onChange: (e) => setGenerateProduct(e.target.value), placeholder: "\u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440: \u043A\u0430\u043F\u043B\u0438 \u043E\u0442 \u043F\u0430\u0440\u0430\u0437\u0438\u0442\u043E\u0432 Detoxil Water", sx: { mb: 2 } })),
106094
106412
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: "row", spacing: 2, sx: { mb: 2 } },
106095
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["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" }),
106096
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["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: {
106413
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "\u0413\u0435\u043E", variant: "outlined", fullWidth: true, value: generateGeo, onChange: (e) => setGenerateGeo(e.target.value), placeholder: "\u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440: \u0420\u0443\u043C\u044B\u043D\u0438\u044F" }),
106414
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "\u0426\u0435\u043D\u0430 \u0438 \u0432\u0430\u043B\u044E\u0442\u0430", variant: "outlined", fullWidth: true, value: generatePriceWithCurrency, onChange: (e) => setGeneratePriceWithCurrency(e.target.value), placeholder: "\u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440: 29 euro, 11400 HUF, 149 RON \u0438\u043B\u0438 \u043B\u044E\u0431\u043E\u0439 \u0434\u0440\u0443\u0433\u043E\u0439 \u0444\u043E\u0440\u043C\u0430\u0442", helperText: "\u041B\u044E\u0431\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0434\u043B\u044F \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u044F \u0432 \u043F\u0440\u043E\u043C\u043F\u0442\u0435 \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0439. \u041A\u043D\u043E\u043F\u043A\u0438 \u0441\u043F\u0440\u0430\u0432\u0430 \u043F\u043E\u0434\u0441\u0442\u0430\u0432\u043B\u044F\u044E\u0442 \u0441\u0438\u043C\u0432\u043E\u043B \u0432\u0430\u043B\u044E\u0442\u044B, \u0446\u0438\u0444\u0440\u0443 \u0431\u0435\u0440\u0443\u0442 \u0438\u0437 \u043F\u043E\u043B\u044F (\u0438\u043B\u0438 99).", InputProps: {
106097
106415
  endAdornment: (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { position: "end" },
106098
106416
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 0.25, mr: -0.5 } },
106099
106417
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { title: "\u041F\u043E\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044C $ (\u0446\u0438\u0444\u0440\u0430 \u0438\u0437 \u043F\u043E\u043B\u044F \u0438\u043B\u0438 99)" },
@@ -106101,7 +106419,7 @@ ${imageData.originalPrompt}
106101
106419
  const n = extractLeadingPriceNumber(generatePriceWithCurrency);
106102
106420
  setGeneratePriceWithCurrency(`$${n}`);
106103
106421
  }, edge: "end" },
106104
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_35__["default"], { sx: { fontSize: 20 } }))),
106422
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_39__["default"], { sx: { fontSize: 20 } }))),
106105
106423
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { title: "\u041F\u043E\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u20AC (\u0446\u0438\u0444\u0440\u0430 \u0438\u0437 \u043F\u043E\u043B\u044F \u0438\u043B\u0438 99)" },
106106
106424
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_8__["default"], { size: "small", "aria-label": "\u0411\u044B\u0441\u0442\u0440\u043E \u0435\u0432\u0440\u043E", onClick: () => {
106107
106425
  const n = extractLeadingPriceNumber(generatePriceWithCurrency);
@@ -106114,29 +106432,29 @@ ${imageData.originalPrompt}
106114
106432
  }, edge: "end", sx: { minWidth: 36, fontSize: '0.8rem', fontWeight: 700, letterSpacing: -0.3 } }, "z\u0142"))))),
106115
106433
  } })),
106116
106434
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
106117
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["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 } })),
106435
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { label: "\u0414\u043E\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C\u043D\u0430\u044F \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044F (\u043D\u0435 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E)", variant: "outlined", fullWidth: true, multiline: true, minRows: 3, value: generateAdditionalInfo, onChange: (e) => setGenerateAdditionalInfo(e.target.value), placeholder: "\u0418\u043D\u0433\u0440\u0435\u0434\u0438\u0435\u043D\u0442\u044B, \u0443\u0442\u043E\u0447\u043D\u0435\u043D\u0438\u044F \u043A \u043F\u0440\u043E\u043C\u043F\u0442\u0443 \u0438 \u0442.\u0434.", sx: { mb: 2 } })),
106118
106436
  openaiApiKey && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: "row", spacing: 2, sx: { mb: 2 } },
106119
106437
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_25__["default"], { fullWidth: true, variant: "outlined" },
106120
106438
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_26__["default"], null, "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0439"),
106121
106439
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_27__["default"], { value: selectedImageModel, onChange: (e) => handleImageModelChange(e.target.value), label: "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0439", disabled: loadingImageModels || imageModels.length === 0 }, loadingImageModels ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { disabled: true },
106122
106440
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
106123
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 16 }),
106441
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 16 }),
106124
106442
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u043C\u043E\u0434\u0435\u043B\u0435\u0439...")))) : imageModels.length === 0 ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { disabled: true }, "\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0445 \u043C\u043E\u0434\u0435\u043B\u0435\u0439")) : (imageModels.map((model) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { key: model.id, value: model.id }, model.name))))),
106125
- !loadingImageModels && imageModels.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], null, selectedImageModel === _models__WEBPACK_IMPORTED_MODULE_2__.MODELS.imageGeneration
106443
+ !loadingImageModels && imageModels.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], null, selectedImageModel === _models__WEBPACK_IMPORTED_MODULE_2__.MODELS.imageGeneration
106126
106444
  ? 'Используется модель по умолчанию'
106127
106445
  : 'Выбрана модель: ' + (imageModels.find(m => m.id === selectedImageModel)?.name || selectedImageModel)))),
106128
106446
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_25__["default"], { fullWidth: true, variant: "outlined" },
106129
106447
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_26__["default"], null, "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u043A\u0440\u0435\u0430\u0442\u0438\u0432\u043E\u0432"),
106130
106448
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_27__["default"], { value: selectedValidationModel, onChange: (e) => handleValidationModelChange(e.target.value), label: "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u043A\u0440\u0435\u0430\u0442\u0438\u0432\u043E\u0432", disabled: loadingValidationModels || validationModels.length === 0 }, loadingValidationModels ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { disabled: true },
106131
106449
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
106132
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 16 }),
106450
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 16 }),
106133
106451
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u043C\u043E\u0434\u0435\u043B\u0435\u0439...")))) : validationModels.length === 0 ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { disabled: true }, "\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0445 \u043C\u043E\u0434\u0435\u043B\u0435\u0439")) : (validationModels.map((model) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { key: model.id, value: model.id }, model.name))))),
106134
- !loadingValidationModels && validationModels.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], null, selectedValidationModel === _models__WEBPACK_IMPORTED_MODULE_2__.MODELS.creativeValidation
106452
+ !loadingValidationModels && validationModels.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], null, selectedValidationModel === _models__WEBPACK_IMPORTED_MODULE_2__.MODELS.creativeValidation
106135
106453
  ? 'Используется модель по умолчанию'
106136
106454
  : 'Выбрана модель: ' + (validationModels.find(m => m.id === selectedValidationModel)?.name || selectedValidationModel))),
106137
106455
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { control: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { checked: validationDisabled, onChange: (e) => handleValidationDisabledChange(e.target.checked), color: "primary" }), label: "\u041E\u0442\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443", sx: { mt: 1 } })))),
106138
106456
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: "row", spacing: 2, sx: { mb: 2 } },
106139
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { variant: "contained", color: "primary", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_36__["default"], null), onClick: handleGenerateContent, disabled: generating || loadingContentFromDrive || !openaiApiKey || !generateProduct.trim() || !generateGeo.trim(), sx: { flexGrow: 1 }, size: "large" }, generating ? 'Generating...' : 'Generate Titles & Descriptions'),
106457
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { variant: "contained", color: "primary", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_40__["default"], null), onClick: handleGenerateContent, disabled: generating || loadingContentFromDrive || !openaiApiKey || !generateProduct.trim() || !generateGeo.trim(), sx: { flexGrow: 1 }, size: "large" }, generating ? 'Generating...' : 'Generate Titles & Descriptions'),
106140
106458
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: "row", spacing: 1, alignItems: "center", sx: { flexGrow: 1 } },
106141
106459
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { title: imageAspectRatio === '1:1'
106142
106460
  ? '1:1 — квадрат (1024×1024 px)'
@@ -106163,7 +106481,7 @@ ${imageData.originalPrompt}
106163
106481
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_30__["default"], { value: "1:1", sx: { px: 1.5, fontWeight: 600, fontSize: '0.8rem' } }, "1:1"),
106164
106482
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_30__["default"], { value: "2:3", sx: { px: 1.5, fontWeight: 600, fontSize: '0.8rem' } }, "2:3"),
106165
106483
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_30__["default"], { value: "both", sx: { px: 1.5, fontWeight: 600, fontSize: '0.8rem' } }, "\u041E\u0431\u0430")))),
106166
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { variant: "contained", color: "secondary", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_36__["default"], null), onClick: handleGenerateImages, disabled: generatingImages ||
106484
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { variant: "contained", color: "secondary", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_40__["default"], null), onClick: handleGenerateImages, disabled: generatingImages ||
106167
106485
  !openaiApiKey ||
106168
106486
  (!accessToken && !refreshToken) ||
106169
106487
  !generateProduct.trim() ||
@@ -106181,9 +106499,9 @@ ${imageData.originalPrompt}
106181
106499
  : !driveFolderUrl.trim()
106182
106500
  ? 'Заполните URL папки Google Drive'
106183
106501
  : undefined }, generatingImages ? 'Generating Images...' : 'Generate Images')),
106184
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["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_40__["default"], null), sx: { py: 1.5, fontSize: '1.1rem', flexGrow: 1 } }, loading ? 'Generating...' : 'Generate Catalog'),
106185
- uploadedLink && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { severity: "success", sx: { flexGrow: 1, minWidth: 0 }, action: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_8__["default"], { "aria-label": "copy link", color: "inherit", size: "small", onClick: handleCopyLink, sx: { ml: 1 } },
106186
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_41__["default"], { fontSize: "small" })) },
106502
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { variant: "contained", color: "success", size: "large", onClick: handleGenerate, disabled: loading, startIcon: loading ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 20, color: "inherit" }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_44__["default"], null), sx: { py: 1.5, fontSize: '1.1rem', flexGrow: 1 } }, loading ? 'Generating...' : 'Generate Catalog'),
106503
+ uploadedLink && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { severity: "success", sx: { flexGrow: 1, minWidth: 0 }, action: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_8__["default"], { "aria-label": "copy link", color: "inherit", size: "small", onClick: handleCopyLink, sx: { ml: 1 } },
106504
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_45__["default"], { fontSize: "small" })) },
106187
106505
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1, flexWrap: 'wrap' } },
106188
106506
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null,
106189
106507
  "\u041A\u0430\u0442\u0430\u043B\u043E\u0433 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D!",
@@ -106193,17 +106511,17 @@ ${imageData.originalPrompt}
106193
106511
  getElectronAPI().openExternal(uploadedLink);
106194
106512
  }, style: { cursor: 'pointer', textDecoration: 'underline', color: 'inherit' } }, "\u041E\u0442\u043A\u0440\u044B\u0442\u044C \u0432 Google Drive")),
106195
106513
  linkCopied && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'success.dark', fontWeight: 'bold' } }, "\u0421\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u043D\u043E!"))))),
106196
- folderFilesInfo !== null && !folderFilesInfo.hasProduct && driveFolderUrl.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["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\".")),
106514
+ folderFilesInfo !== null && !folderFilesInfo.hasProduct && driveFolderUrl.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { severity: "warning", sx: { mt: 1 } }, "\u0414\u043B\u044F \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0439 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F product.png/jpg/webp. \u0421\u043D\u0430\u0447\u0430\u043B\u0430 \u0441\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0439\u0442\u0435 \u0435\u0433\u043E \u0441 \u043F\u043E\u043C\u043E\u0449\u044C\u044E \u043A\u043D\u043E\u043F\u043A\u0438 \"Generate Product from Banka\".")),
106197
106515
  !generatingImages && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
106198
- !openaiApiKey && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { severity: "error", sx: { mt: 1 } }, "\u0417\u0430\u043F\u043E\u043B\u043D\u0438\u0442\u0435 OpenRouter API Key")),
106199
- openaiApiKey && (!accessToken && !refreshToken) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { severity: "error", sx: { mt: 1 } }, "\u0412\u043E\u0439\u0434\u0438\u0442\u0435 \u0432 Google Drive")),
106200
- openaiApiKey && (accessToken || refreshToken) && !generateProduct.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { severity: "error", sx: { mt: 1 } }, "\u0417\u0430\u043F\u043E\u043B\u043D\u0438\u0442\u0435 \u043F\u043E\u043B\u0435 \u0422\u043E\u0432\u0430\u0440")),
106201
- openaiApiKey && (accessToken || refreshToken) && generateProduct.trim() && !generateGeo.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { severity: "error", sx: { mt: 1 } }, "\u0417\u0430\u043F\u043E\u043B\u043D\u0438\u0442\u0435 \u043F\u043E\u043B\u0435 \u0413\u0435\u043E")),
106202
- openaiApiKey && (accessToken || refreshToken) && generateProduct.trim() && generateGeo.trim() && !driveFolderUrl.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { severity: "error", sx: { mt: 1 } }, "\u0417\u0430\u043F\u043E\u043B\u043D\u0438\u0442\u0435 URL \u043F\u0430\u043F\u043A\u0438 Google Drive"))))),
106516
+ !openaiApiKey && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { severity: "error", sx: { mt: 1 } }, "\u0417\u0430\u043F\u043E\u043B\u043D\u0438\u0442\u0435 OpenRouter API Key")),
106517
+ openaiApiKey && (!accessToken && !refreshToken) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { severity: "error", sx: { mt: 1 } }, "\u0412\u043E\u0439\u0434\u0438\u0442\u0435 \u0432 Google Drive")),
106518
+ openaiApiKey && (accessToken || refreshToken) && !generateProduct.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { severity: "error", sx: { mt: 1 } }, "\u0417\u0430\u043F\u043E\u043B\u043D\u0438\u0442\u0435 \u043F\u043E\u043B\u0435 \u0422\u043E\u0432\u0430\u0440")),
106519
+ openaiApiKey && (accessToken || refreshToken) && generateProduct.trim() && !generateGeo.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { severity: "error", sx: { mt: 1 } }, "\u0417\u0430\u043F\u043E\u043B\u043D\u0438\u0442\u0435 \u043F\u043E\u043B\u0435 \u0413\u0435\u043E")),
106520
+ openaiApiKey && (accessToken || refreshToken) && generateProduct.trim() && generateGeo.trim() && !driveFolderUrl.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { severity: "error", sx: { mt: 1 } }, "\u0417\u0430\u043F\u043E\u043B\u043D\u0438\u0442\u0435 URL \u043F\u0430\u043F\u043A\u0438 Google Drive"))))),
106203
106521
  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 }),
106204
106522
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: "row", spacing: 2, sx: { mb: 2 } },
106205
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["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_40__["default"], null), onClick: handleUploadProductFile, disabled: uploadingProduct || !accessToken || !driveFolderUrl.trim(), sx: { flexGrow: 1 }, size: "large" }, uploadingProduct ? 'Загрузка...' : 'Загрузить product.png/jpg/webp'),
106206
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["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_47__["default"], null), onClick: handleCreateLanding, disabled: generatingLanding ||
106523
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { variant: "contained", color: "success", startIcon: uploadingProduct ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 20, color: "inherit" }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_44__["default"], null), onClick: handleUploadProductFile, disabled: uploadingProduct || !accessToken || !driveFolderUrl.trim(), sx: { flexGrow: 1 }, size: "large" }, uploadingProduct ? 'Загрузка...' : 'Загрузить product.png/jpg/webp'),
106524
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { variant: "contained", color: "info", startIcon: generatingLanding ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 20, color: "inherit" }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_50__["default"], null), onClick: handleCreateLanding, disabled: generatingLanding ||
106207
106525
  !openaiApiKey ||
106208
106526
  !accessToken ||
106209
106527
  !driveFolderUrl.trim() ||
@@ -106218,10 +106536,10 @@ ${imageData.originalPrompt}
106218
106536
  generatedImagesData.length,
106219
106537
  ")"),
106220
106538
  checkingImages && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1, mb: 2 } },
106221
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 16 }),
106539
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 16 }),
106222
106540
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'text.secondary' } }, "\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043A\u0430\u0447\u0435\u0441\u0442\u0432\u0430..."))),
106223
106541
  generatedImagesData.length > 0 && generatedImagesData.some(img => !img.uploaded && img.imageUrl) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { mb: 2 } },
106224
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["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_40__["default"], null), fullWidth: true }, uploadingImages ? 'Загрузка...' : `Загрузить все (${generatedImagesData.filter(img => !img.uploaded && img.imageUrl).length})`))),
106542
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { variant: "contained", color: "primary", onClick: handleUploadAllImages, disabled: uploadingImages || generatingImages || !driveFolderUrl.trim(), startIcon: uploadingImages ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 20 }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_44__["default"], null), fullWidth: true }, uploadingImages ? 'Загрузка...' : `Загрузить все (${generatedImagesData.filter(img => !img.uploaded && img.imageUrl).length})`))),
106225
106543
  generatedImagesData.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: {
106226
106544
  display: 'grid',
106227
106545
  gridTemplateColumns: { xs: '1fr', sm: 'repeat(2, 1fr)', md: 'repeat(3, 1fr)' },
@@ -106270,7 +106588,7 @@ ${imageData.originalPrompt}
106270
106588
  imageData.generating ||
106271
106589
  !!imageData.regenerating ||
106272
106590
  !!imageData.uploading },
106273
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_42__["default"], { fontSize: "small" })))),
106591
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_46__["default"], { fontSize: "small" })))),
106274
106592
  imageData.generating ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: {
106275
106593
  width: '100%',
106276
106594
  aspectRatio: aspectRatioCss,
@@ -106286,7 +106604,7 @@ ${imageData.originalPrompt}
106286
106604
  gap: 2,
106287
106605
  p: 3
106288
106606
  } },
106289
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 40, sx: { color: 'primary.main' } }),
106607
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 40, sx: { color: 'primary.main' } }),
106290
106608
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", sx: { color: 'text.secondary', textAlign: 'center', fontWeight: 'bold' } }, "\u0413\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u044F..."),
106291
106609
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'text.secondary', textAlign: 'center' } }, imageData.approach),
106292
106610
  generatingImages && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'text.secondary', textAlign: 'center' } },
@@ -106306,7 +106624,7 @@ ${imageData.originalPrompt}
106306
106624
  gap: 2,
106307
106625
  p: 3
106308
106626
  } },
106309
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 40, sx: { color: 'primary.main' } }),
106627
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 40, sx: { color: 'primary.main' } }),
106310
106628
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", sx: { color: 'text.secondary', textAlign: 'center', fontWeight: 'bold' } }, "\u041F\u0435\u0440\u0435\u0434\u0435\u043B\u043A\u0430..."),
106311
106629
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'text.secondary', textAlign: 'center' } }, imageData.approach))) : imageData.imageUrl ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
106312
106630
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { component: "img", src: imageData.imageUrl, alt: `Generated Image ${imageData.index}`, sx: {
@@ -106341,7 +106659,7 @@ ${imageData.originalPrompt}
106341
106659
  alignItems: 'center',
106342
106660
  gap: 1
106343
106661
  } },
106344
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 40, sx: { color: 'primary.main' } }),
106662
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 40, sx: { color: 'primary.main' } }),
106345
106663
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'white', fontWeight: 'bold', textShadow: '1px 1px 2px rgba(0,0,0,0.8)' } }, "\u041F\u0435\u0440\u0435\u0434\u0435\u043B\u043A\u0430..."))))) : imageData.failed ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: {
106346
106664
  width: '100%',
106347
106665
  aspectRatio: aspectRatioCss,
@@ -106402,14 +106720,13 @@ ${imageData.originalPrompt}
106402
106720
  imageData.checkErrors.length > 2 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'text.secondary', fontSize: '0.7rem' } },
106403
106721
  "+",
106404
106722
  imageData.checkErrors.length - 2,
106405
- " \u0435\u0449\u0451")))),
106406
- imageData.checkFailed && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { size: "small", variant: "outlined", color: "warning", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_48__["default"], null), onClick: () => handleRetryCheck(imageData), disabled: imageData.checking, sx: { mt: 0.5, fontSize: '0.7rem', py: 0.25, px: 1 } }, "\u041F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443")))),
106723
+ " \u0435\u0449\u0451")))))),
106407
106724
  imageData.checkStatus === 'checking' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'warning.main', display: 'block' } }, "\uD83D\uDD0D \u041F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u0442\u0441\u044F...")),
106408
106725
  imageData.checkStatus === 'pending' && !imageData.failed && !imageData.generating && imageData.imageUrl && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'text.secondary', display: 'block' } }, "\u23F3 \u041E\u0436\u0438\u0434\u0430\u0435\u0442 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438")),
106409
106726
  imageData.failed && !imageData.generating && !imageData.imageUrl && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'error.main', display: 'block', fontWeight: 'bold' } }, "\u274C \u0413\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u044F \u043D\u0435 \u0443\u0434\u0430\u043B\u0430\u0441\u044C")),
106410
106727
  imageData.uploaded && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'success.main', display: 'block', mt: 0.5 } }, "\u2713 \u0417\u0430\u0433\u0440\u0443\u0436\u0435\u043D\u043E"))),
106411
106728
  !imageData.generating && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', gap: 1, flexDirection: 'column', mt: 1 } },
106412
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["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) => {
106729
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], { size: "small", multiline: true, minRows: 2, maxRows: 4, fullWidth: true, placeholder: "\u0423\u0442\u043E\u0447\u043D\u0435\u043D\u0438\u044F \u0434\u043B\u044F \u043F\u0435\u0440\u0435\u0434\u0435\u043B\u043A\u0438 (\u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438 \u0437\u0430\u043F\u043E\u043B\u043D\u0435\u043D\u043E \u043F\u0440\u0438 \u043F\u0440\u043E\u0431\u043B\u0435\u043C\u0430\u0445)", value: imageData.customRegeneratePrompt || '', onChange: (e) => {
106413
106730
  setGeneratedImagesData(prev => prev.map(img => img.index === imageData.index
106414
106731
  ? { ...img, customRegeneratePrompt: e.target.value }
106415
106732
  : img));
@@ -106426,22 +106743,27 @@ ${imageData.originalPrompt}
106426
106743
  : 'transparent'
106427
106744
  }
106428
106745
  } }),
106429
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["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_36__["default"], null), onClick: () => handleRegenerateImage(imageData), disabled: imageData.regenerating ||
106746
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: "row", spacing: 1, sx: { width: '100%', alignItems: 'stretch' } },
106747
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { size: "small", variant: "contained", color: imageData.failed ? 'error' : imageData.checkStatus === 'needs_rebuild' ? 'warning' : 'primary', startIcon: imageData.regenerating ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 16 }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_40__["default"], null), onClick: () => handleRegenerateImage(imageData), disabled: imageData.regenerating ||
106748
+ imageData.uploading ||
106749
+ imageData.checkStatus === 'checking' ||
106750
+ !imageData.originalPrompt ||
106751
+ !imageData.productImageUrl ||
106752
+ (generatingImages && !imageData.imageUrl && !imageData.failed && !imageData.generating), sx: { flex: 1, minWidth: 0, py: 1, lineHeight: 1.2, whiteSpace: 'normal' } }, imageData.regenerating
106753
+ ? (imageData.failed ? 'Генерация...' : 'Переделка...')
106754
+ : ((generatingImages && !imageData.imageUrl && !imageData.failed && !imageData.generating)
106755
+ ? 'В очереди'
106756
+ : (!imageData.imageUrl ? 'Сгенерировать' : 'Переделать'))),
106757
+ imageData.imageUrl ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { size: "small", variant: "outlined", color: "secondary", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_51__["default"], null), onClick: () => handleRegenerateImageFresh(imageData), disabled: imageData.regenerating ||
106758
+ imageData.uploading ||
106759
+ imageData.checkStatus === 'checking' ||
106760
+ !imageData.originalPrompt ||
106761
+ !imageData.productImageUrl, sx: { flex: 1, minWidth: 0, py: 1, lineHeight: 1.2, whiteSpace: 'normal' } }, "\u041F\u0435\u0440\u0435\u0434\u0435\u043B\u0430\u0442\u044C \u0437\u0430\u043D\u043E\u0432\u043E")) : null),
106762
+ imageData.imageUrl && !validationDisabled && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { size: "small", variant: "outlined", color: "warning", startIcon: imageData.checkStatus === 'checking' ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 16, color: "inherit" })) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_51__["default"], null)), onClick: () => handleRetryCheck(imageData), disabled: imageData.regenerating ||
106430
106763
  imageData.uploading ||
106431
- imageData.checkStatus === 'checking' ||
106432
- !imageData.originalPrompt ||
106433
- !imageData.productImageUrl ||
106434
- (generatingImages && !imageData.imageUrl && !imageData.failed && !imageData.generating), fullWidth: true }, imageData.regenerating
106435
- ? (imageData.failed ? 'Генерация...' : 'Переделка...')
106436
- : ((generatingImages && !imageData.imageUrl && !imageData.failed && !imageData.generating)
106437
- ? 'В очереди'
106438
- : (!imageData.imageUrl ? 'Сгенерировать' : 'Переделать'))),
106439
- imageData.imageUrl && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { size: "small", variant: "outlined", color: "secondary", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_48__["default"], null), onClick: () => handleRegenerateImageFresh(imageData), disabled: imageData.regenerating ||
106440
- imageData.uploading ||
106441
- imageData.checkStatus === 'checking' ||
106442
- !imageData.originalPrompt ||
106443
- !imageData.productImageUrl, fullWidth: true }, "\u041F\u0435\u0440\u0435\u0434\u0435\u043B\u0430\u0442\u044C \u0437\u0430\u043D\u043E\u0432\u043E")),
106444
- !imageData.uploaded && imageData.imageUrl && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { size: "small", variant: "outlined", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_40__["default"], null), onClick: () => {
106764
+ imageData.generating ||
106765
+ imageData.checkStatus === 'checking', fullWidth: true }, imageData.checkStatus === 'checking' ? 'Проверка…' : 'Перепроверить')),
106766
+ !imageData.uploaded && imageData.imageUrl && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { size: "small", variant: "outlined", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_44__["default"], null), onClick: () => {
106445
106767
  const folderId = extractFolderId(driveFolderUrl);
106446
106768
  if (folderId) {
106447
106769
  handleUploadImage(imageData, folderId);
@@ -106449,7 +106771,7 @@ ${imageData.originalPrompt}
106449
106771
  }, disabled: imageData.uploading || imageData.regenerating || !driveFolderUrl.trim(), fullWidth: true }, imageData.uploading ? 'Загрузка...' : 'Загрузить'))))));
106450
106772
  }))),
106451
106773
  generatedImagesData.length > 0 && generatedImagesData.some(img => !img.uploaded && img.imageUrl) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { mt: 2 } },
106452
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["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_40__["default"], null), fullWidth: true }, uploadingImages ? 'Загрузка...' : `Загрузить все (${generatedImagesData.filter(img => !img.uploaded && img.imageUrl).length})`))))),
106774
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { variant: "contained", color: "primary", onClick: handleUploadAllImages, disabled: uploadingImages || generatingImages || !driveFolderUrl.trim(), startIcon: uploadingImages ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 20 }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_44__["default"], null), fullWidth: true }, uploadingImages ? 'Загрузка...' : `Загрузить все (${generatedImagesData.filter(img => !img.uploaded && img.imageUrl).length})`))))),
106453
106775
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { sx: { my: 2 } }),
106454
106776
  (generatedTitlesData.length > 0 || generatedTextsData.length > 0 || loadingContentFromDrive) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { mb: 2 } },
106455
106777
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 1, mb: 1 } },
@@ -106466,7 +106788,7 @@ ${imageData.originalPrompt}
106466
106788
  color: 'text.secondary',
106467
106789
  '&:hover': { opacity: 0.75, color: 'text.primary' },
106468
106790
  } },
106469
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_41__["default"], { sx: { fontSize: 16 } }))))),
106791
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_45__["default"], { sx: { fontSize: 16 } }))))),
106470
106792
  loadingContentFromDrive && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: {
106471
106793
  display: 'flex',
106472
106794
  flexDirection: 'column',
@@ -106481,7 +106803,7 @@ ${imageData.originalPrompt}
106481
106803
  : 'rgba(25, 118, 210, 0.05)',
106482
106804
  mb: 2
106483
106805
  } },
106484
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 40, sx: { color: 'primary.main' } }),
106806
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 40, sx: { color: 'primary.main' } }),
106485
106807
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body1", sx: { color: 'text.secondary', fontWeight: 'bold' } }, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u043A\u043E\u043D\u0442\u0435\u043D\u0442\u0430 \u0438\u0437 Google Drive..."))),
106486
106808
  (generatedTitlesData.length > 0 || generatedTextsData.length > 0) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { spacing: 2 }, Array.from({
106487
106809
  length: Math.max(generatedTitlesData.length, generatedTextsData.length)
@@ -106525,79 +106847,93 @@ ${imageData.originalPrompt}
106525
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" },
106526
106848
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null,
106527
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 },
106528
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_42__["default"], { fontSize: "small" })))),
106529
- 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 } })) : pairTranslations[i] ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { title: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
106530
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { fontWeight: 700, display: 'block', mb: 0.5 } }, "\u0417\u0430\u0433\u043E\u043B\u043E\u0432\u043E\u043A:"),
106531
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { display: 'block', mb: 1 } }, pairTranslations[i].titleRu),
106532
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { fontWeight: 700, display: 'block', mb: 0.5 } }, "\u0422\u0435\u043A\u0441\u0442:"),
106533
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { display: 'block' } }, pairTranslations[i].textRu)), placement: "right", arrow: true, componentsProps: {
106534
- tooltip: { sx: { maxWidth: 360, fontSize: 12, lineHeight: 1.6, p: '10px 14px' } },
106535
- } },
106536
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_8__["default"], { size: "small", sx: { p: 0.25, color: 'text.disabled', '&:hover': { color: 'primary.main' } } },
106537
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_44__["default"], { sx: { fontSize: 14 } })))) : null),
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),
106538
106852
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { spacing: 1.5 },
106539
- titleData && (titleData.generating ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
106540
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 16, sx: { color: 'primary.main' } }),
106541
- 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_15__["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) => {
106542
- const newValue = e.target.value;
106543
- setGeneratedTitlesData(prev => {
106544
- const updated = prev.map(t => t.index === titleData.index
106545
- ? { ...t, title: newValue, failed: false, errorMessage: undefined }
106546
- : t);
106547
- setTitles(updated.map(t => t.title).filter(Boolean).join('\n'));
106548
- return updated;
106549
- });
106550
- }, sx: {
106551
- '& .MuiInputBase-root': {
106552
- backgroundColor: (theme) => theme.palette.mode === 'dark'
106553
- ? 'rgba(244,67,54,0.08)'
106554
- : 'rgba(244,67,54,0.04)'
106555
- }
106556
- } })) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { label: `Заголовок ${i + 1}`, variant: "outlined", fullWidth: true, size: "small", value: titleData.title, onChange: (e) => {
106557
- const newValue = e.target.value;
106558
- setGeneratedTitlesData(prev => {
106559
- const updated = prev.map(t => t.index === titleData.index ? { ...t, title: newValue } : t);
106560
- setTitles(updated.map(t => t.title).filter(Boolean).join('\n'));
106561
- return updated;
106562
- });
106563
- }, sx: {
106564
- '& .MuiInputBase-root': {
106565
- backgroundColor: (theme) => theme.palette.mode === 'dark'
106566
- ? 'rgba(76,175,80,0.08)'
106567
- : 'rgba(76,175,80,0.04)'
106568
- }
106569
- } }))),
106570
- textData && (textData.generating ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
106571
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 16, sx: { color: 'primary.main' } }),
106572
- 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_15__["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) => {
106573
- const newValue = e.target.value;
106574
- setGeneratedTextsData(prev => {
106575
- const updated = prev.map(t => t.index === textData.index
106576
- ? { ...t, text: newValue, failed: false, errorMessage: undefined }
106577
- : t);
106578
- setTexts(updated.map(t => t.text));
106579
- return updated;
106580
- });
106581
- }, sx: {
106582
- '& .MuiInputBase-root': {
106583
- backgroundColor: (theme) => theme.palette.mode === 'dark'
106584
- ? 'rgba(244,67,54,0.08)'
106585
- : 'rgba(244,67,54,0.04)'
106586
- }
106587
- } })) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { label: `Текст ${i + 1}`, variant: "outlined", fullWidth: true, multiline: true, minRows: 3, value: textData.text, onChange: (e) => {
106588
- const newValue = e.target.value;
106589
- setGeneratedTextsData(prev => {
106590
- const updated = prev.map(t => t.index === textData.index ? { ...t, text: newValue } : t);
106591
- setTexts(updated.map(t => t.text).filter(Boolean));
106592
- return updated;
106593
- });
106594
- }, sx: {
106595
- '& .MuiInputBase-root': {
106596
- backgroundColor: (theme) => theme.palette.mode === 'dark'
106597
- ? 'rgba(76,175,80,0.08)'
106598
- : 'rgba(76,175,80,0.04)'
106599
- }
106600
- } }))))));
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,
106932
+ color: 'text.secondary',
106933
+ opacity: 0.92,
106934
+ whiteSpace: 'pre-wrap',
106935
+ wordBreak: 'break-word',
106936
+ } }, pairTranslations[i].textRu)) : null)))));
106601
106937
  }))))),
106602
106938
  (landingGenerationLogs.length > 0 || generatingLanding) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { mb: 2 } },
106603
106939
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h6", gutterBottom: true, sx: { fontWeight: 'bold' } }, "\u041F\u0440\u043E\u0446\u0435\u0441\u0441 \u0441\u043E\u0437\u0434\u0430\u043D\u0438\u044F \u043B\u0435\u043D\u0434\u0438\u043D\u0433\u0430"),
@@ -106625,10 +106961,38 @@ ${imageData.originalPrompt}
106625
106961
  'text.primary'
106626
106962
  } }, log)))))),
106627
106963
  generatingLanding && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1, mb: 2 } },
106628
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 20 }),
106964
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], { size: 20 }),
106629
106965
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", sx: { color: 'text.secondary' } }, formatElapsedTime(elapsedTime.landing)))),
106630
- !generatingLanding && generatedLandingHTML && generatedLandingImageBlob && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { variant: "outlined", color: "primary", onClick: handlePreviewLanding, startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_50__["default"], null), fullWidth: true }, "\u041F\u0440\u0435\u0434\u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440 \u043B\u0435\u043D\u0434\u0438\u043D\u0433\u0430"))))))))),
106631
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_PromptManagerDialog__WEBPACK_IMPORTED_MODULE_51__["default"], { open: promptManagerOpen, onClose: () => setPromptManagerOpen(false) }))));
106966
+ !generatingLanding && generatedLandingHTML && generatedLandingImageBlob && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { variant: "outlined", color: "primary", onClick: handlePreviewLanding, startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_53__["default"], null), fullWidth: true }, "\u041F\u0440\u0435\u0434\u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440 \u043B\u0435\u043D\u0434\u0438\u043D\u0433\u0430"))))))))),
106967
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_32__["default"], { open: approachLoadChoice !== null, onClose: handleApproachLoadKeepCurrent, maxWidth: "md", fullWidth: true }, approachLoadChoice ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
106968
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_33__["default"], null, "\u041F\u043E\u0434\u0445\u043E\u0434\u044B \u0438\u0437 \u0444\u0430\u0439\u043B\u0430 \u043E\u0444\u0444\u0435\u0440\u0430"),
106969
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_34__["default"], null,
106970
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", color: "text.secondary", sx: { mb: 2 } }, "\u0412 project-settings.json \u0435\u0441\u0442\u044C \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D\u043D\u044B\u0435 \u043F\u043E\u0434\u0445\u043E\u0434\u044B \u0434\u043B\u044F \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u043E\u0432/\u0442\u0435\u043A\u0441\u0442\u043E\u0432 \u0438 \u0434\u043B\u044F \u043A\u0440\u0435\u043E. \u041F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u044C \u0438\u0445 \u0432 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0438 \u0438\u043B\u0438 \u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0442\u0435, \u0447\u0442\u043E \u0443\u0436\u0435 \u0432\u044B\u0431\u0440\u0430\u043D\u044B \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E?"),
106971
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { spacing: 2 },
106972
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "subtitle2" }, "\u0417\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0438 \u0438 \u0442\u0435\u043A\u0441\u0442\u044B (\u043F\u0430\u0440\u044B \u043F\u043E\u0434\u0445\u043E\u0434\u043E\u0432)"),
106973
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: { xs: 'column', sm: 'row' }, spacing: 2 },
106974
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_31__["default"], { variant: "outlined", sx: { p: 1.5, flex: 1 } },
106975
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", color: "text.secondary", display: "block", gutterBottom: true }, "\u0421\u0435\u0439\u0447\u0430\u0441 \u0432 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0438"),
106976
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", component: "div", sx: { whiteSpace: 'pre-wrap', fontFamily: 'inherit' } }, formatPairApproachesForDisplay(approachLoadChoice.currentPairs))),
106977
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_31__["default"], { variant: "outlined", sx: { p: 1.5, flex: 1 } },
106978
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", color: "text.secondary", display: "block", gutterBottom: true }, "\u0412 \u0444\u0430\u0439\u043B\u0435 \u043E\u0444\u0444\u0435\u0440\u0430"),
106979
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", component: "div", sx: { whiteSpace: 'pre-wrap', fontFamily: 'inherit' } }, approachLoadChoice.savedPairs
106980
+ ? formatPairApproachesForDisplay(approachLoadChoice.savedPairs)
106981
+ : '— не указано (список для текстов не изменится)'))),
106982
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "subtitle2" }, "\u0418\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u044F (\u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u043F\u043E \u043F\u043E\u0434\u0445\u043E\u0434\u0443)"),
106983
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: { xs: 'column', sm: 'row' }, spacing: 2 },
106984
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_31__["default"], { variant: "outlined", sx: { p: 1.5, flex: 1 } },
106985
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", color: "text.secondary", display: "block", gutterBottom: true }, "\u0421\u0435\u0439\u0447\u0430\u0441 \u0432 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0438"),
106986
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", component: "div", sx: { whiteSpace: 'pre-wrap', fontFamily: 'inherit' } }, formatImageCountsForDisplay(approachLoadChoice.currentCounts))),
106987
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_31__["default"], { variant: "outlined", sx: { p: 1.5, flex: 1 } },
106988
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", color: "text.secondary", display: "block", gutterBottom: true }, "\u0412 \u0444\u0430\u0439\u043B\u0435 \u043E\u0444\u0444\u0435\u0440\u0430"),
106989
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", component: "div", sx: { whiteSpace: 'pre-wrap', fontFamily: 'inherit' } }, approachLoadChoice.savedCounts
106990
+ ? formatImageCountsForDisplay(approachLoadChoice.savedCounts)
106991
+ : '— не указано (количества крео не изменятся)'))))),
106992
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_35__["default"], null,
106993
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { onClick: handleApproachLoadKeepCurrent }, "\u041E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u043A\u0430\u043A \u0441\u0435\u0439\u0447\u0430\u0441"),
106994
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { variant: "contained", onClick: handleApproachLoadApplyFromFile }, "\u041F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u044C \u0438\u0437 \u0444\u0430\u0439\u043B\u0430")))) : null),
106995
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_PromptManagerDialog__WEBPACK_IMPORTED_MODULE_54__["default"], { open: promptManagerOpen, onClose: () => setPromptManagerOpen(false) }))));
106632
106996
  }
106633
106997
  /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (App);
106634
106998
 
@@ -107724,6 +108088,7 @@ __webpack_require__.r(__webpack_exports__);
107724
108088
  /* harmony export */ PRODUCT_TYPE_IMAGE_PRESETS: () => (/* binding */ PRODUCT_TYPE_IMAGE_PRESETS),
107725
108089
  /* harmony export */ PRODUCT_TYPE_TEXT_PAIR_PRESETS: () => (/* binding */ PRODUCT_TYPE_TEXT_PAIR_PRESETS),
107726
108090
  /* harmony export */ PROMPT_OVERRIDES_SAVED_EVENT: () => (/* binding */ PROMPT_OVERRIDES_SAVED_EVENT),
108091
+ /* harmony export */ applyParsedApproachesFromDrive: () => (/* binding */ applyParsedApproachesFromDrive),
107727
108092
  /* harmony export */ getCreoApproachOverride: () => (/* binding */ getCreoApproachOverride),
107728
108093
  /* harmony export */ getImageApproachCounts: () => (/* binding */ getImageApproachCounts),
107729
108094
  /* harmony export */ getPairsCount: () => (/* binding */ getPairsCount),
@@ -107734,6 +108099,7 @@ __webpack_require__.r(__webpack_exports__);
107734
108099
  /* harmony export */ mergePromptApproachesFromDriveFile: () => (/* binding */ mergePromptApproachesFromDriveFile),
107735
108100
  /* harmony export */ normalizeImageApproachCountsFromDrive: () => (/* binding */ normalizeImageApproachCountsFromDrive),
107736
108101
  /* harmony export */ normalizeSelectedPairApproachesFromDrive: () => (/* binding */ normalizeSelectedPairApproachesFromDrive),
108102
+ /* harmony export */ parseApproachesFromDriveData: () => (/* binding */ parseApproachesFromDriveData),
107737
108103
  /* harmony export */ productPresetNumbersToImageCounts: () => (/* binding */ productPresetNumbersToImageCounts),
107738
108104
  /* harmony export */ productPresetNumbersToIndices: () => (/* binding */ productPresetNumbersToIndices),
107739
108105
  /* harmony export */ resetAllOverrides: () => (/* binding */ resetAllOverrides),
@@ -107919,12 +108285,17 @@ function savePromptOverrides(overrides) {
107919
108285
  }
107920
108286
  /** Имя события после сохранения подходов в менеджере промптов — чтобы синхронизировать JSON на Drive. */
107921
108287
  const PROMPT_OVERRIDES_SAVED_EVENT = 'docs-combiner-prompt-overrides-saved';
108288
+ /** Прочитать валидные подходы из объекта настроек с диска (без записи в localStorage). */
108289
+ function parseApproachesFromDriveData(loadedData) {
108290
+ return {
108291
+ pairs: normalizeSelectedPairApproachesFromDrive(loadedData.selectedPairApproaches),
108292
+ counts: normalizeImageApproachCountsFromDrive(loadedData.imageApproachCounts),
108293
+ };
108294
+ }
107922
108295
  /**
107923
- * Подмешать в localStorage выбранные подходы из project-settings.json на Drive (если поля валидны).
108296
+ * Применить к localStorage только те части, для которых переданы ненулевые массивы.
107924
108297
  */
107925
- function mergePromptApproachesFromDriveFile(loadedData) {
107926
- const pairs = normalizeSelectedPairApproachesFromDrive(loadedData.selectedPairApproaches);
107927
- const counts = normalizeImageApproachCountsFromDrive(loadedData.imageApproachCounts);
108298
+ function applyParsedApproachesFromDrive(pairs, counts) {
107928
108299
  if (!pairs && !counts)
107929
108300
  return false;
107930
108301
  const overrides = loadPromptOverrides();
@@ -107943,6 +108314,13 @@ function mergePromptApproachesFromDriveFile(loadedData) {
107943
108314
  }
107944
108315
  return false;
107945
108316
  }
108317
+ /**
108318
+ * Подмешать в localStorage выбранные подходы из project-settings.json на Drive (если поля валидны).
108319
+ */
108320
+ function mergePromptApproachesFromDriveFile(loadedData) {
108321
+ const { pairs, counts } = parseApproachesFromDriveData(loadedData);
108322
+ return applyParsedApproachesFromDrive(pairs, counts);
108323
+ }
107946
108324
  /**
107947
108325
  * Получить оверрайд для простого промпта
107948
108326
  */
@@ -108138,57 +108516,57 @@ function _debugLog(...args) {
108138
108516
  const PAIR_APPROACH_POOL = [
108139
108517
  {
108140
108518
  name: 'болевая точка',
108141
- titleApproach: 'одна ясная боль (не две проблемы в одном заголовке). Усиливай через несправедливость / ощущение «так несправедливо» (устал бороться, снова срыв, тело не слушается) — не только сухое перечисление симптомов. ЗАГОЛОВОК = КОНСТАТАЦИЯ, не вопрос: ЗАПРЕЩЕНО заканчивать на «почему?», «perché?», «why?» и подобное — боль называешь фактом, не спрашиваешь',
108519
+ titleApproach: 'одна ясная боль (не две проблемы в одном заголовке). Усиливай через несправедливость / ощущение «так несправедливо» (устал бороться, снова срыв, тело не слушается) — не только сухое перечисление симптомов. ЗАГОЛОВОК = КОНСТАТАЦИЯ, не вопрос: ЗАПРЕЩЕНО заканчивать на «почему?», «зачем?», «why?» и подобное — боль называешь фактом, не спрашиваешь',
108142
108520
  textApproach: 'Одна центральная проблема за текст — не распыляйся на несколько болей. ЗАПРЕЩЕНО перечислять ингредиенты, состав, matcha/MCT/«формулу» — это территория «авторитет/экспертиза», не болевой точки. Опиши дискомфорт и как продукт снимает именно эту боль; без каталога компонентов.',
108143
108521
  },
108144
108522
  {
108145
108523
  name: 'трансформация до/после',
108146
- titleApproach: 'контраст СОСТОЯНИЙ и ощущений до/после (вздутие/усталость → лёгкость, тяжесть → контроль, «не узнаю себя в зеркале» → спокойствие) с якорем во времени во фразе, но БЕЗ цифр на весах. ЗАПРЕЩЕНО в заголовке: −X kg, «persi N kg», весы, любой явный минус килограммов — это только у testimonial',
108147
- textApproach: 'СТРОГО от первого лица («Я / Io / Yo…»). Это не отзыв с именем — это МОЙ контраст «как было плохо / как стало лучше» по телу и эмоциям. ЗАПРЕЩЕНО копировать формат отзыва: не начинай как реклама «я попробовала продукт и минус X кг» подряд; сначала ощущения и жизнь до/после, продукт — связка, а не пересказ цифр с весов (цифры веса допустимы в тексте лишь второстепенно, если уместно; акцент — на смене состояния). ЗАПРЕЩЕНО соцдоказательство («многие отмечают», «molte persone»).',
108524
+ titleApproach: 'контраст СОСТОЯНИЙ и ощущений до/после (вздутие/усталость → лёгкость, тяжесть → контроль, «не узнаю себя в зеркале» → спокойствие) с якорем во времени во фразе, но БЕЗ цифр на весах. ЗАПРЕЩЕНО в заголовке: −X kg, «минус N кг», весы, любой явный минус килограммов (для похудения такой акцент — только у testimonial и только если продукт явно про вес)',
108525
+ textApproach: 'СТРОГО от первого лица (на языке GEO естественное «я» для локали). Это не отзыв с именем — это МОЙ контраст «как было плохо / как стало лучше» по телу и эмоциям. ЗАПРЕЩЕНО копировать формат отзыва: не начинай как реклама «я попробовала продукт и минус X кг» подряд; сначала ощущения и жизнь до/после, продукт — связка, а не пересказ цифр с весов (цифры веса допустимы в тексте лишь второстепенно, если уместно; акцент — на смене состояния). ЗАПРЕЩЕНО соцдоказательство («многие отмечают», «тысячи уже выбрали» вместо личной истории).',
108148
108526
  },
108149
108527
  {
108150
108528
  name: 'testimonial',
108151
108529
  isTestimonial: true,
108152
- titleApproach: 'короткая «живая» фраза с конкретикой результата на весах/сроке («−2,8 kg in 10 giorni… incredibile 😅»)здесь УМЕСТНЫ −кг и срок; это отличает testimonial от «трансформации». НЕ используй «Имя, возраст:» в заголовке — только в тексте',
108530
+ titleApproach: 'короткая «живая» фраза с **конкретным результатом по теме продукта** и сроком (7–14 дней). Цифры −кг, весы, «минус на весах» **только** если продукт явно про похудение / контроль веса. Для паразитов/ЖКТ/суставов/простаты/сна и т.п. результат = облегчение симптомов, комфорт, сон, подвижность, меньше дискомфорта (без выдуманных кг). Это отличает testimonial от «трансформации». НЕ используй «Имя, возраст:» в заголовке — только в тексте',
108153
108531
  textApproach: `Это единственный подход-ОТЗЫВ: читатель должен поверить в реального человека.
108154
- * Обязательно начало текста с «Имя, возраст:» («Giulia, 49 anni:», «Kasia, 41 lat:»)
108532
+ * Обязательно начало текста с «Имя, возраст:» (формат на языке GEO, например «Анна, 49 лет:», «Олег, 41 год:»)
108155
108533
  * История только от первого лица после имени
108156
- * Конкретный результат с таймингом (7–14 дней), можно килограммы и детали быта
108534
+ * Конкретный результат с таймингом (7–14 дней), релевантный категории продукта; килограммы упоминай только если оффер действительно про вес/похудение
108157
108535
  * Заканчивается приглашением попробовать + CTA`,
108158
108536
  },
108159
108537
  {
108160
108538
  name: 'срочность/дефицит',
108161
108539
  titleApproach: 'ТОЛЬКО дефицит оффера: время до конкретного дедлайна, остаток штук, «до полуночи», «осталось N упаковок» — ЗАПРЕЩЕНО в заголовке проблема, тело, продукт, «лишние кг», категория товара. Не баннер магазина — конкретная граница (день, час, число мест)',
108162
- textApproach: 'Начни с конкретного сигнала ограниченности — времени или количества («Tylko dziś», «Últimas unidades», «Осталось 12 упаковок», «Акция до конца дня»). Весь текст строится ИСКЛЮЧИТЕЛЬНО вокруг дефицита или дедлайна: осталось мало, время заканчивается, потом будет дороже или не будет вообще. ЗАПРЕЩЕНО упоминать состав, ингредиенты, свойства или пользу продукта — только ограниченность оффера. CTA максимально срочный.',
108540
+ textApproach: 'Начни с конкретного сигнала ограниченности — времени или количества («только сегодня», «последние упаковки», «Осталось 12 упаковок», «Акция до конца дня» — всё на языке GEO). Весь текст строится ИСКЛЮЧИТЕЛЬНО вокруг дефицита или дедлайна: осталось мало, время заканчивается, потом будет дороже или не будет вообще. ЗАПРЕЩЕНО упоминать состав, ингредиенты, свойства или пользу продукта — только ограниченность оффера. CTA максимально срочный.',
108163
108541
  },
108164
108542
  {
108165
108543
  name: 'социальное доказательство',
108166
- titleApproach: 'одна ЧИТАЕМАЯ фраза с крупной цифрой (например «50.000 italiane hanno già scelto 🔥») — грамматика и смысл на языке GEO должны сходиться; читатель понимает фразу с первого раза. ЗАПРЕЩЕНО несвязные склейки («9 su 10 scelgono controllo 🔥 fame») и обрывки, где цифра + существительные не составляют нормального высказывания',
108167
- textApproach: 'Начни с цифры или массового факта («Уже 50 000 клиентов», «9 din 10 femei observă diferența»). Акцент на том, что проблему уже решили тысячи — и ты тоже можешь.',
108544
+ titleApproach: 'одна ЧИТАЕМАЯ фраза с крупной цифрой (уровень: «Уже 50 000 женщин выбрали 🔥» на языке GEO) — грамматика и смысл на языке GEO должны сходиться; читатель понимает фразу с первого раза. ЗАПРЕЩЕНО несвязные склейки («9 из 10 выбирают контроль 🔥 голод» — бессмыслица) и обрывки, где цифра + существительные не составляют нормального высказывания',
108545
+ textApproach: 'Начни с цифры или массового факта («Уже 50 000 клиентов», «9 из 10 отмечают разницу» — на языке GEO). Акцент на том, что проблему уже решили тысячи — и ты тоже можешь.',
108168
108546
  },
108169
108547
  {
108170
108548
  name: 'любопытство/интрига',
108171
- titleApproach: 'провокационный вопрос или утверждение, которое ломает сразу ДВА привычных решения (диета И спорт / dieta e palestra…), — но ОДНОЙ ЦЕЛЬНОЙ фразой: связный синтаксис, без рубки на обрывки через запятую и эмодзи посередине. Пример уровня: «Dieta e palestra ma la pancia resta? 🤔» — а не три оторванных куска',
108172
- textApproach: 'Начни с провокационного вопроса или неожиданного инсайта («Знаете, почему обычные средства не помогают?», «¿Sabías que…?»). Раскрой неочевидный факт о проблеме или механизме решения и выведи к продукту.',
108549
+ titleApproach: 'провокационный вопрос или утверждение, которое ломает сразу ДВА привычных решения (например диета и спорт одновременно) — но ОДНОЙ ЦЕЛЬНОЙ фразой: связный синтаксис, без рубки на обрывки через запятую и эмодзи посередине. Пример уровня на русском для тона: «Диета и зал, а живот всё равно? 🤔» — на GEO перепиши естественно; не три оторванных куска',
108550
+ textApproach: 'Начни с провокационного вопроса или неожиданного инсайта («Знаете, почему обычные средства не помогают?», «А вы знали, что…» — на языке GEO). Раскрой неочевидный факт о проблеме или механизме решения и выведи к продукту.',
108173
108551
  },
108174
108552
  {
108175
108553
  name: 'авторитет/экспертиза',
108176
- titleApproach: 'information gap: в заголовке ЗАПРЕЩЕНО называть ингредиенты и раскрывать метод/механизм напрямую — никаких matcha, MCT, verde, «formula con…», «metodo naturale per…», «accelerare il metabolismo», «supporta il metabolismo» и т.п.; иначе ответ уже в заголовке и клик не нужен. Подай состав или подход как тайну / открытие / «что скрывают» / «почему это не то же самое, что обычные средства» — без конкретики, ответ только в основном тексте. ЗАПРЕЩЕНЫ эмодзи-подсказки по составу (например 🍵, если по смыслу выдаёт чай), если они заменяют то, что должен открыть текст. Не закрывай интригу формулировками уровня «Perché X e Y supportano…»',
108177
- textApproach: 'Начни с ФАКТА/утверждения об экспертизе («Włoska receptura», «Hecho en Italia», «3 składniki naturalne», «Разработано фармацевтами»). Акцент на составе, происхождении или механизме действия. Почему это работает — объясни просто и убедительно.',
108554
+ titleApproach: 'information gap: в заголовке ЗАПРЕЩЕНО называть ингредиенты и раскрывать метод/механизм напрямую — никаких matcha, MCT, «зелёный чай», «формула с…», «натуральный метод для…», «ускорить обмен веществ», «поддерживает обмен веществ» и т.п.; иначе ответ уже в заголовке и клик не нужен. Подай состав или подход как тайну / открытие / «что скрывают» / «почему это не то же самое, что обычные средства» — без конкретики, ответ только в основном тексте. ЗАПРЕЩЕНЫ эмодзи-подсказки по составу (например 🍵, если по смыслу выдаёт чай), если они заменяют то, что должен открыть текст. Не закрывай интригу сухими схемами вроде «Почему X и Y поддерживают…»',
108555
+ textApproach: 'Начни с ФАКТА/утверждения об экспертизе («Итальянская рецептура», «Произведено в Италии», «3 натуральных компонента», «Разработано фармацевтами» — перенеси смысл на язык GEO). Акцент на составе, происхождении или механизме действия. Почему это работает — объясни просто и убедительно.',
108178
108556
  },
108179
108557
  {
108180
108558
  name: 'страх бездействия',
108181
- titleApproach: 'узнаваемый паттерн промедления — ОДНА СВЯЗНАЯ фраза (допустимы многоточие, союз «но», тире), а не перечень обрывков через запятую. Пример уровня: «Rimandi a lunedì… ma la fame non aspetta 😣» — смысл течёт от начала к концу',
108182
- textApproach: 'Начни с описания негативного сценария бездействия («С каждым днём становится хуже…», «Если не взять под контроль сейчас…»). Покажи, что проблема прогрессирует. Финальный CTA — ТОЛЬКО про заботу о себе / начать сейчас / не откладывать (на языке GEO). ЗАПРЕЩЕНЫ прямые призывы к заказу в финале: «ordina ora», «закажи», «купи сейчас», «clicca e acquista» — вместо них: взять под контроль, сделать первый шаг, начать сегодня и т.п.',
108559
+ titleApproach: 'узнаваемый паттерн промедления — ОДНА СВЯЗНАЯ фраза (допустимы многоточие, союз «но», тире), а не перечень обрывков через запятую. Пример уровня: «Отложила на понедельник… а голод не ждёт 😣» — на GEO перепиши естественно; смысл течёт от начала к концу',
108560
+ textApproach: 'Начни с описания негативного сценария бездействия («С каждым днём становится хуже…», «Если не взять под контроль сейчас…»). Покажи, что проблема прогрессирует. Финальный CTA — ТОЛЬКО про заботу о себе / начать сейчас / не откладывать (на языке GEO). ЗАПРЕЩЕНЫ прямые призывы к заказу в финале: «закажи», «закажи сейчас», «купи», «купи сейчас», «оформи заказ» — вместо них: взять под контроль, сделать первый шаг, начать сегодня и т.п.',
108183
108561
  },
108184
108562
  {
108185
108563
  name: 'прямой оффер/выгода',
108186
- titleApproach: 'конкретная числовая выгода в заголовке сколько экономишь или что получаешь (например «-50%», «Вторая упаковка бесплатно», «Цена как раньше»). НЕ просто «скидка есть» а ЧТО именно и сколько. ОБЯЗАТЕЛЬНО минимум один эмодзи в заголовке (💸 🔥 ✨ 🎁 ⏳ 📦 и т.п.) как у остальных подходов в наборе, без эмодзи заголовок считается ошибкой',
108187
- textApproach: 'Аудитория уже думает о покупке текст снимает последнее сомнение, а не убеждает в проблеме. Начни с оффера числом: скидка -50%, конкретная цена, что получаешь за эти деньги. Ответь на вопрос «почему взять сейчас, а не завтра» цена вырастет, акция закончится, запас ограничен. ЗАПРЕЩЕНО объяснять пользу продукта или описывать проблему читатель уже всё знает. Только сделка, только экономика, только CTA с акцентом на выгоде прямо сейчас.',
108564
+ titleApproach: 'выгода в заголовке = **-50%** как есть (знак процента), плюс по желанию 2–4 слова срочности на языке GEO («только сегодня», «сейчас»). ОБЯЗАТЕЛЬНО минимум один эмодзи (💸 🔥 ✨ 🎁 ⏳ 📦). **Не придумывай** другую механику: никаких «2-й пакет», «вторая упаковка», «полцены» вместо простого **-50%**, если в задании пользователя явно не сказано иначе. **-50% = просто -50%.**',
108565
+ textApproach: 'Коротко про сделку: **-50% не перефразируй** длинно («платишь половину», «акция на сегодня», «скидка на вторую упаковку»), если пользователь этого не писал повтори **-50%** и добавь одну короткую фразу срочности + CTA. ЗАПРЕЩЕНО расписывать математику скидки и выдумывать условия акции. ЗАПРЕЩЕНО объяснять пользу продукта или боли — только **-50%**, срочность, CTA.',
108188
108566
  },
108189
108567
  {
108190
108568
  name: 'ситуационный/узнаваемый момент',
108191
- titleApproach: 'одна яркая узнаваемая сцена в 1–2 коротких связных сегментах (лучше одна картинка), без перечня симптомов через запятую. Пример уровня: «Dopo pranzo i pantaloni tirano 😩» — сразу видно момент и боль; ЗАПРЕЩЕНО нагромождать «после обеда 😩 джинсы, живот, тесно» отдельными ярлыками',
108569
+ titleApproach: 'одна яркая узнаваемая сцена в 1–2 коротких связных сегментах (лучше одна картинка), без перечня симптомов через запятую. Пример уровня: «После обеда джинсы жмут 😩» — на GEO перепиши естественно; сразу видно момент и боль; ЗАПРЕЩЕНО нагромождать «после обеда 😩 джинсы, живот, тесно» отдельными ярлыками',
108192
108570
  textApproach: 'Начни с конкретной жизненной сцены, где проблема ощущается («[ключевой симптом категории продукта]…», «Вечером перед сном не можешь уснуть из-за дискомфорта…» — адаптируй под категорию). Создай узнаваемую ситуацию — читатель должен сказать «это про меня» — и предложи решение.',
108193
108571
  },
108194
108572
  ];
@@ -108305,7 +108683,7 @@ ${distributionLines}
108305
108683
 
108306
108684
  КРИТИЧНО — СРОК + ОФФЕР ДЛЯ КОНВЕРСИИ:
108307
108685
  ${deadlineNote}
108308
- - Опционально (не обязательно): усили CTA упоминанием оффера «-50 и ограниченности по времени («только сегодня/ограничено») — но коротко и только в конце текста.${hasTestimonial ? '' : ''}
108686
+ - Опционально (не обязательно): усили CTA упоминанием **-50%** как есть и ограниченности по времени («только сегодня/ограничено») — коротко, в конце; **не** заменяй -50% на «полцены», «вторая упаковка» и т.п., если это не в задании.${hasTestimonial ? '' : ''}
108309
108687
 
108310
108688
  Требования к стилю и тону:
108311
108689
  - Копия должна звучать как прямая реклама, а не как рассказ
@@ -108326,7 +108704,7 @@ ${deadlineNote}
108326
108704
  - КРИТИЧНО: Каждый текст должен содержать хотя бы одно ясное ключевое слово проблемы, релевантное категории продукта (например, для суставов: боль/скованность/подвижность; для пищеварения: дискомфорт/тяжесть; для сна: бессонница/усталость — примеры только для формата, адаптируй под категорию). Если ключевое слово проблемы отсутствует — перепиши текст
108327
108705
  - КРИТИЧНО: Каждый текст должен заканчиваться сильным и явным призывом к действию (кликни, попробуй сейчас, закажи сегодня, узнай больше и т.д.)
108328
108706
  - Избегай слабых окончаний или информационного тона. Слабые или нейтральные окончания не допускаются
108329
- - CTA должен быть “нажимным”: допускаются формулировки «не откладывай», «последний шанс», «только сегодня», «забери -50%» (всё строго на языке GEO)
108707
+ - CTA должен быть “нажимным”: допускаются формулировки «не откладывай», «последний шанс», «только сегодня», «забери -50%» (всё строго на языке GEO). Если пишешь скидку — **-50%** буквально, без выдуманной механики акции
108330
108708
  - В каждом тексте строго следуй заданному формату старта согласно его подходу (см. распределение выше)
108331
108709
  - Если ингредиенты предоставлены в дополнительной информации, упоминай их ТОЛЬКО если они естественно подходят выбранному рекламному подходу. Например: подходы авторитета/экспертизы выигрывают от деталей ингредиентов, в то время как подходы срочности/эмоционального триггера могут не нуждаться в них. Упоминай только релевантные ингредиенты, не обязательно все из них. Если ингредиенты не подходят подходу или не предоставлены — полностью пропусти их упоминание и не изобретай и не упоминай никакие ингредиенты
108332
108710
  - Добавляй только существенные поддерживающие детали, которые усиливают убеждение или ясность, а не заполнители или общие утверждения. Будь кратким и прямым
@@ -108339,7 +108717,7 @@ ${deadlineNote}
108339
108717
  - Разделяй разные тексты пустой строкой (двойной перевод строки)
108340
108718
  - КРИТИЧНО: Каждый текст ДОЛЖЕН быть многострочным с естественными переносами строк. Используй переносы строк для разделения предложений, выделения ключевых моментов или создания визуальной структуры. НЕ пиши описания как одиночные непрерывные строки
108341
108719
  - КРИТИЧНО: Все тексты ДОЛЖНЫ быть написаны на языке ${geo}. Используй точное название языка "${geo}" — не используй русский, английский или любой другой язык
108342
- - ВСЕ слова в тексте должны быть на языке GEO. Английские фразы вроде «Made in Italy», «Made in Germany» ЗАПРЕЩЕНЫ — используй перевод: «Wyprodukowano we Włoszech», «Hecho en Italia», «Fabricat în Italia» и т.д.
108720
+ - ВСЕ слова в тексте должны быть на языке GEO. Английские фразы вроде «Made in Italy», «Made in Germany» ЗАПРЕЩЕНЫ — используй перевод на язык GEO (например «Произведено в Италии», «Сделано в Германии» и аналоги).
108343
108721
  - После создания проверь их правильность И убедись в максимальном разнообразии между текстами
108344
108722
  - Верни только финальные результаты. Не добавляй объяснений или комментариев`;
108345
108723
  }
@@ -108417,6 +108795,7 @@ function getPairsSystemPrompt(geo, noOverride, count = 3, selectedIndices) {
108417
108795
  - Служебные метки в начале строки пиши РОВНО как в примере: слово «ЗАГОЛОВОК» целиком кириллицей (не ZAG, не смесь латиницы и кириллицы), слово «ТЕКСТ» целиком кириллицей.
108418
108796
  - Не вставляй строки «ЗАГОЛОВОК N:» внутрь текста объявления; каждый заголовок — отдельная строка перед своим «ТЕКСТ N:».
108419
108797
  - Контент объявления (заголовок и текст) — на языке ${geo}; только метки ЗАГОЛОВОК/ТЕКСТ остаются кириллицей как шаблон.
108798
+ - Язык ${geo}: если это название страны — пиши на **официальном языке этой страны**; не путай близкие коды и локали (например Словения ≠ Словакия, хорватский ≠ словенский).
108420
108799
 
108421
108800
  РАСПРЕДЕЛЕНИЕ ПОДХОДОВ ПО ПАРАМ:
108422
108801
  ${distributionLines}
@@ -108426,27 +108805,28 @@ ${distributionLines}
108426
108805
  - Текст — раскрывает тот же угол глубже, добавляет детали, заканчивается CTA
108427
108806
  - Текст НЕ повторяет заголовок дословно, но развивает его смысл
108428
108807
  - Не подмешивай чужие углы: например в «трансформация до/после» не используй формулировки соцдоказательства («многие отмечают», «клиенты говорят»)
108429
- ${hasTransformAndTestimonial ? '\n- РАЗВЕДЕНИЕ «трансформация до/после» и «testimonial» в одном ответе: у трансформации заголовок = контраст ощущений/состояния (без −кг и без «минус X kg за N дней»); у testimonial заголовок может содержать −кг, весы, срок как убедительный отзыв. Не делай два заголовка в одном формате «цифра + срок на весах».' : ''}
108808
+ ${hasTransformAndTestimonial ? '\n- РАЗВЕДЕНИЕ «трансформация до/после» и «testimonial» в одном ответе: у трансформации заголовок = контраст ощущений/состояния (без −кг и без «минус X kg за N дней»); у testimonial −кг и весы в заголовке**только если продукт в задании явно про похудение**; иначе testimonial = конкретный симптомный/комфортный результат + срок. Не делай два заголовка в одном формате «цифра + срок на весах».' : ''}
108430
108809
 
108431
108810
  ТРЕБОВАНИЯ К ЗАГОЛОВКУ:
108432
108811
  - До 55–60 символов, максимум 6–7 слов
108433
108812
  - Цельность: заголовок читается как одно высказывание (или один связный вопрос целиком). ЗАПРЕЩЕНО склеивать несвязные ярлыки запятыми ради ключевых слов; эмодзи не должно разрывать грамматику так, что фраза перестаёт быть человеческой
108434
108813
  - ЗАПРЕЩЕНО двоеточие (:) — используй тире (—) или переформулируй
108435
108814
  - 1–2 эмодзи естественно (часто в конце фразы или после целого смыслового блока); избегай ⚠️ и 🚨${hasDirectOffer ? '\n- Для пары «прямой оффер/выгода» эмодзи в заголовке ОБЯЗАТЕЛЕН (минимум один) — визуально вровень с остальными парами набора' : ''}
108436
- - Хотя бы одно ключевое слово проблемы, релевантное продукту${hasUrgencyScarcity ? '\n- ИСКЛЮЧЕНИЕ: для пары с подходом «срочность/дефицит» в заголовке ЗАПРЕЩЕНЫ симптомы, проблема, тело, продукт — только лимит оффера (время, количество, дедлайн)' : ''}${hasAuthorityExpertise ? '\n- Пара «авторитет/экспертиза»: заголовок держит information gap — без ингредиентов и без прямого названия механизма; состав и «как это работает» — только в тексте пары' : ''}
108437
- - НЕ упоминай название продукта — заголовок = боль + триггер${hasUrgencyScarcity ? ' (кроме пары «срочность/дефицит»: там заголовок = только дефицит/дедлайн, без продукта и без боли)' : ''}${hasSocialProof ? '\n- Исключение: в паре «социальное доказательство» название продукта в заголовке допустимо, если оно входит в цельную фразу про массовый выбор (например «N persone hanno già scelto [продукт]»)' : ''}
108815
+ - Хотя бы одно ключевое слово проблемы, релевантное продукту${hasUrgencyScarcity ? '\n- ИСКЛЮЧЕНИЕ: для пары с подходом «срочность/дефицит» в заголовке ЗАПРЕЩЕНЫ симптомы, проблема, тело, продукт — только лимит оффера (время, количество, дедлайн)' : ''}${hasDirectOffer ? '\n- ИСКЛЮЧЕНИЕ: для пары «прямой оффер/выгода» ключевое слово проблемы в заголовке **не обязательно**; ядро — **-50%** (+эмодзи + короткая срочность). **Не выдумывай** «2-й пакет / вторая упаковка / полцены» без данных в задании — **-50% это просто -50%.**' : ''}${hasAuthorityExpertise ? '\n- Пара «авторитет/экспертиза»: заголовок держит information gap — без ингредиентов и без прямого названия механизма; состав и «как это работает» — только в тексте пары' : ''}
108816
+ - НЕ упоминай название продукта — заголовок = боль + триггер${hasUrgencyScarcity ? ' (кроме пары «срочность/дефицит»: там заголовок = только дефицит/дедлайн, без продукта и без боли)' : ''}${hasSocialProof ? '\n- Исключение: в паре «социальное доказательство» название продукта в заголовке допустимо, если оно входит в цельную фразу про массовый выбор (например «Уже N тысяч человек выбрали [продукт]» на языке GEO)' : ''}
108438
108817
  - Звучит как живая рекламная фраза, а не строка ключевых слов
108439
108818
 
108440
108819
  ТРЕБОВАНИЯ К ТЕКСТУ:
108441
- - 150–280 символов (testimonial — до 300)
108442
- - Хотя бы одно ключевое слово проблемы (для «срочность/дефицит» в тексте допустимо слабее, если весь фокус — на лимите оффера)
108443
- - Сильный явный CTA в конце («кликни», «попробуй сейчас», «закажи», «не жди»)${hasFearInaction ? '\n- ИСКЛЮЧЕНИЕ: для пары «страх бездействия» финальная фраза — не заказ, а забота о себе / начать сейчас; без «ordina ora», «закажи», «купи», «clicca e acquista» в конце' : ''}
108444
- - Короткие рубленые фразы, императивы, FOMO допустим${hasTestimonial ? '\n- Testimonial: строго от первого лица, начинается с «Имя, возраст:», конкретный результат с таймингом' : ''}
108820
+ - 150–280 символов (testimonial — до 300)${hasDirectOffer ? '; для пары «прямой оффер/выгода» достаточно **120–200** символов, если текст = **-50%** + срочность + CTA без воды' : ''}
108821
+ - Хотя бы одно ключевое слово проблемы (для «срочность/дефицит» в тексте допустимо слабее, если весь фокус — на лимите оффера)${hasDirectOffer ? '; для «прямой оффер/выгода» ключевое слово проблемы **не обязательно** — фокус на **-50%** и CTA' : ''}
108822
+ - Сильный явный CTA в конце («кликни», «попробуй сейчас», «закажи», «не жди»)${hasFearInaction ? '\n- ИСКЛЮЧЕНИЕ: для пары «страх бездействия» финальная фраза — не заказ, а забота о себе / начать сейчас; без «закажи», «купи», «оформи заказ» в конце' : ''}
108823
+ - Короткие рубленые фразы, императивы, FOMO допустим${hasTestimonial ? '\n- Testimonial: строго от первого лица, начинается с «Имя, возраст:», конкретный результат с таймингом; тема результата = как в названии/описании продукта (антипаразитарное → ЖКТ/вздутие/дискомфорт и т.д.), не минус кг если продукт не про вес' : ''}
108445
108824
  - Ингредиенты и состав из доп. информации — только в углах «авторитет/экспертиза» (и сходных); в «болевой точке» и чисто эмоциональных углах состав не перечислять
108446
108825
  ${deadlineNote}
108447
108826
 
108448
108827
  ОБЩИЕ ТРЕБОВАНИЯ:
108449
108828
  - Язык: ${geo} — все тексты строго на этом языке
108829
+ - Релевантность продукту из пользовательского сообщения: не подменяй категорию. Капли/средства от паразитов, детокс ЖКТ → симптомы и облегчение в этой зоне; **не делай главным мотивом −кг и весы**, если в задании нет явного похудения. Аналогично не смешивай суставы с простатой и т.д.
108450
108830
  - Естественный человеческий тон, без академизма и AI-звучания
108451
108831
  - Разные психологические триггеры в каждой паре, минимальное пересечение
108452
108832
  - Верни ТОЛЬКО пары в указанном формате, без объяснений и комментариев`;
@@ -108569,10 +108949,10 @@ OTHER_TEXT: ["..."] (любой другой рекламный текст на
108569
108949
  ШАГ 1 — HOOK/HEADLINE (критично):
108570
108950
  - Должен быть ВЫШЕ всех элементов и читабелен (хорошо читается на телефоне)
108571
108951
  - Допускается 1–4 строки (это НЕ ошибка). Ошибка только если текст нечитабелен или есть разрыв/перенос внутри слова.
108572
- - Смысл: релевантно проблеме/выгоде продукта. Допускаются и боль/дискомфорт, и обещание/выгода (например «Könnyebb napok»). НЕ считай ошибкой формулировки обещаний результата или клеймы.
108952
+ - Смысл: релевантно проблеме/выгоде продукта. Допускаются и боль/дискомфорт, и обещание/выгода (например «станет легче день ото дня» — смысл на языке GEO). НЕ считай ошибкой формулировки обещаний результата или клеймы.
108573
108953
  - ОБЯЗАТЕЛЬНО: ключевое слово проблемы явно в формулировке. Примеры по категориям: простата → простатит, простата; похудение → лишний вес, похудение; суставы → боль, колени, скованность; пищеварение → дискомфорт, вздутие; сон → бессонница, усталость. Отсутствие ключевого слова — ОШИБКА. Дополнительно для ориентира: ${keywords.join(', ')} (можно синонимы).
108574
- - Проверь вторую часть заголовка. Если она состоит только из абстрактных слов («Mai ușor», «Mai bine», «Soluția», «Ajutor») без конкретного бенефита — отметь: РЕКОМЕНДАЦИЯ: заголовок можно усилить конкретикой
108575
- - Если заголовок слишком общий/абстрактный («Mai ușor», «Mai bine» без конкретики) — отметь как РЕКОМЕНДАЦИЯ к улучшению (не ошибка, но слабо)
108954
+ - Проверь вторую часть заголовка. Если она состоит только из абстрактных слов («легче», «лучше», «решение», «помощь» без конкретики на языке GEO) без конкретного бенефита — отметь: РЕКОМЕНДАЦИЯ: заголовок можно усилить конкретикой
108955
+ - Если заголовок слишком общий/абстрактный («легче», «лучше» без конкретики) — отметь как РЕКОМЕНДАЦИЯ к улучшению (не ошибка, но слабо)
108576
108956
  - РЕКОМЕНДАЦИЯ (CTR): верхний текст лучше делать как HOOK — ОЧЕНЬ крупный, жирный, ВСЕ БУКВЫ ПРОПИСНЫЕ, на яркой контрастной плашке. Если это не так — не ошибка, но отметь рекомендацией
108577
108957
 
108578
108958
  ${step2Bullets}
@@ -108643,6 +109023,8 @@ function getImageGenerationBasePrompt(generateGeo, generatePrice, generateCurren
108643
109023
  Создай рекламный креатив для Facebook Ads ${ratio} (${ratioLabel}).
108644
109024
  ВАЖНО: изображение должно быть ${ratioShape} — строго соблюдай пропорции холста.
108645
109025
  Язык текста: ${generateGeo}.
109026
+ Если GEO — название страны (например «Чехия», «Румыния»), используй **основной официальный язык этой страны** на макете (Чехия → чешский; Румыния → румынский и т.д.). Валюта/символ цены из брифа и язык текста должны соответствовать одному рынку: при Kč — весь рекламный текст на чешском, не на румынском/польском/венгерском.
109027
+ ЗАПРЕЩЕНО копировать на макет примеры формулировок из этого промпта, если они на другом языке, чем требуется для ${generateGeo} — примеры только про структуру, слова придумывай на целевом языке.
108646
109028
 
108647
109029
  🚫 СЛУЖЕБНЫЕ СЛОВА НЕ РИСОВАТЬ НА КАРТИНКЕ (КРИТИЧНО):
108648
109030
  Слова HOOK, CTA, CAPS, BULLET, HEADLINE, PRICE, DISCOUNT, BULLETS, LAYER и любые похожие английские метки из этой инструкции (в т.ч. thumb‑stop) — только для тебя; на макете их НЕТ ни на кнопке, ни на плашках, ни мелким текстом. Кнопка: только реальный призыв на языке ${generateGeo} (1–2 слова), без префикса «CTA». Заголовок: только живой текст оффера, без слова HOOK. Заголовок делай прописными буквами на языке ${generateGeo}, но не пиши на изображении слово CAPS и не подписывай блоки ярлыками.
@@ -108678,20 +109060,20 @@ HOOK / HEADLINE (строгое правило):
108678
109060
  - ОБЯЗАТЕЛЬНО содержит ключевое слово проблемы ЯВНО. Примеры по категориям: простата → простатит, простата; похудение → лишний вес, похудение; суставы → боль в суставах, колени, скованность; пищеварение → дискомфорт, вздутие, тяжесть; сон → бессонница, усталость. Без ключевого слова — ОШИБКА
108679
109061
  - HOOK ВЫШЕ всех элементов (продукт, буллеты, CTA, цена). Самый крупный текстовый элемент на креативе
108680
109062
  - HOOK должен быть ВИЗУАЛЬНО “тяжёлым”: ОЧЕНЬ крупный, ТОЛСТЫЙ/жирный шрифт, весь текст заголовка ПРОПИСНЫМИ буквами на языке ${generateGeo}, на яркой контрастной плашке/подложке. Это верхний главный элемент, цепляющий внимание в ленте
108681
- - Можно (не обязательно) включить СРОК прямо в HOOK (например «7 dni», «14 dni») как триггер
108682
- - Заголовок должен быть РЕЗКИМ и призывным: короткие рубленые фразы, вопросы и предупреждения допустимы. Можно использовать обращение на «ты» (в языке GEO: «Masz…», «Twój…») и вопросительный знак
109063
+ - Можно (не обязательно) включить СРОК прямо в HOOK (например «7 дней», «14 дней» — перенеси число и слово «дней» на язык ${generateGeo}) как триггер
109064
+ - Заголовок должен быть РЕЗКИМ и призывным: короткие рубленые фразы, вопросы и предупреждения допустимы. Можно использовать обращение на «ты» (естественное для языка ${generateGeo}) и вопросительный знак
108683
109065
  - заголовок НЕ должен быть абстрактным слоганом/лозунгом или метафорой без конкретной боли. Допускается «thumb‑stop» стиль (вызов/вопрос/предупреждение), если это конкретно и релевантно категории
108684
109066
  - если не влезает: сначала измени композицию (расширь блок/переставь элементы), затем перепиши короче. НЕЛЬЗЯ уменьшать шрифт. HOOK всегда остаётся выше всех остальных элементов
108685
109067
  - Не повторяй один и тот же заголовок в разных подходах: для текущего подхода придумай новый, уникальный заголовок. Варьируй: угол боли, формулировку, акцент (проблема vs облегчение)
108686
- - Вторая часть заголовка = конкретное улучшение, не абстрактное слово. ПРАВИЛЬНО: «Zile fără dureri», «Mișcare ușoară», «Confort activ» (для суставов); «Zile fără disconfort», «Nopți liniștite» (для сна) адаптируй под категорию. НЕ ТАК: «Mai ușor», «Mai bine», «Soluția»
109068
+ - Вторая часть заголовка = конкретное улучшение, не абстрактное слово. ПРАВИЛЬНО (структура; на макете только язык ${generateGeo}): боль/вопрос + конкретное облегчение. НЕ ТАК: размытые «легче», «лучше», «решение» без конкретики
108687
109069
  - Специфические медицинские термины или диагнозы разрешены в заголовке, но не обязательны. Варьируй между прямыми формулировками (если релевантно категории) и мягкими формулировками, фокусирующимися на симптомах и дискомфорте, для разных креативов. Не используй один и тот же термин во всех креативах
108688
- ПРИМЕР (HU, только как формат НЕ копируй слова, определи категорию продукта сам):
108689
- - НЕ ТАК: «Makacs méreganyag ellen» (лозунг, абстрактно)
108690
- - ТАК (пищеварение): «Puffadás gond? Könnyebb napok»
108691
- - ТАК (суставы): «Ízületi fájdalom? Mozogj könnyebben»
108692
- - ТАК (сон): «Álmatlan éjszakák? Pihenj végre»
108693
- - ТАК (простата): «Prostată? Alinare rapidă»
108694
- - ТАК (похудение): «Greutate în plus? Fără diete extreme»
109070
+ ПРИМЕР (русский только формат и ритм; на макете пиши строго на языке ${generateGeo}, не копируй русский текст, если GEO не русскоязычный):
109071
+ - НЕ ТАК: «Борьба с токсинами» (лозунг, абстрактно)
109072
+ - ТАК (пищеварение): «Вздутие мучает? Легче к вечеру»
109073
+ - ТАК (суставы): «Боль в суставах? Двигайся свободнее»
109074
+ - ТАК (сон): «Бессонные ночи? Выспишься наконец»
109075
+ - ТАК (простата): «Простата беспокоит? Быстрое облегчение»
109076
+ - ТАК (похудение): «Лишний вес? Без жёстких диет»
108695
109077
  ❗ Если заголовок > 12 слов, похож на длинное предложение или звучит как лозунг без ключевого слова проблемы — ОШИБКА → пересоздай вариант.
108696
109078
 
108697
109079
  BULLETS (жёсткое правило, ровно 3):
@@ -108701,14 +109083,14 @@ BULLETS (жёсткое правило, ровно 3):
108701
109083
  - ВИЗУАЛ БУЛЛЕТОВ: крупные, с иконками/галочками (✓), на контрастных подложках. Буллеты ВНЕ упаковки/банки — не на продукте.
108702
109084
  - Буллиты должны быть расположены ВЕРТИКАЛЬНО (столбиком), не горизонтально в одну строку. Минимальный размер шрифта буллитов — чтобы читались на экране телефона без зума
108703
109085
 
108704
- ❗ ВЫБОР БУЛЛЕТОВ (КРИТИЧНО): Выбери СЛУЧАЙНЫЕ 3 буллета из пула ниже в ПРОИЗВОЛЬНОМ порядке. Адаптируй формулировки под язык ${generateGeo} и категорию продукта. НЕ используй один и тот же набор для разных подходовкаждый креатив должен иметь УНИКАЛЬНУЮ комбинацию из 3 буллетов.
109086
+ ❗ ВЫБОР БУЛЛЕТОВ (КРИТИЧНО): Если выше по промпту задан блок «ОБЯЗАТЕЛЬНЫЕ БУЛЛЕТЫ» используй ТОЛЬКО его (перевод на язык ${generateGeo}). Если такого блока нет выбери 3 случайных смысла из пула ниже; пул на английском только для смысла, на макете **ноль английского** короткие фразы строго на языке ${generateGeo}. Не копируй на макет язык примеров из этого промпта, кроме целевого ${generateGeo}.
108705
109087
 
108706
- ПУЛ ВАРИАНТОВ (выбери 3 случайных, порядок произвольный; адаптируй под GEO и категорию):
108707
- Действие/бенефит: Reduce disconfortul, Reduce balonarea, Reduce durerea, Ulga rapidă, Confort zilnic, Mișcare ușoară, Mai puține simptome, Fără dureri, Zile fără disconfort, Stomac liniștit, Nopți liniștite, Rezultat vizibil
108708
- Срок/скорость: Efect în 7-14 zile, Efect în 2 săptămâni, Rezultat rapid, Ulga od 1. dnia, Efekt w 7 dni, szybciej, De la prima zi, În primele zile
108709
- Соц. доказательство: 9 din 10 recomandă, 9 din 10 bărbați recomandă, 93% potwierdza, 8 din 10 mulțumiți, Mii de clienți mulțumiți
108710
- Формула/состав: Formulă naturală, Ingrediente naturale, Fără chimicale, Compoziție selectată, Testat dermatologic
108711
- Качество жизни: Confort activ, Zile fără griji, Mai multă energie, Somn odihnitor, Viață mai ușoară
109088
+ ПУЛ СМЫСЛОВ (английский не печатать; переписать на ${generateGeo}, 2–4 слова каждый):
109089
+ Action/benefit: less discomfort, less bloating, less pain, fast relief, daily comfort, easier movement, fewer symptoms, pain-free, calm stomach, calm nights, visible result
109090
+ Time/speed: effect in 714 days, effect in 2 weeks, fast result, relief from day one, 3× faster, from first day, in the first days
109091
+ Social proof: 9 of 10 recommend, 9 of 10 men recommend, 93% confirm, 8 of 10 satisfied, thousands happy customers
109092
+ Formula: natural formula, natural ingredients, no harsh chemicals, selected composition, dermatologically tested
109093
+ Quality of life: active comfort, worry-free days, more energy, restful sleep, easier life
108712
109094
 
108713
109095
  ❗ Если хоть один буллет > 4 слов или похож на предложение/обещание — ОШИБКА → пересоздай.
108714
109096
 
@@ -108716,9 +109098,9 @@ TEXT LIMIT:
108716
109098
  - кроме: HOOK, 3 буллетов, цены, скидки, кнопки — ЛЮБОЙ другой текст запрещён (ярлыки/подписи/дисклеймеры/пояснения)
108717
109099
  - ИСКЛЮЧЕНИЕ: если 🎯 ПОДХОД = ${noBulletsCond} → кроме HOOK, цены, скидки, CTA — НИКАКОГО другого текста. Буллиты запрещены. Other_text/urgency/trust‑печати запрещены
108718
109100
  - ДОПУСКАЕТСЯ (не обязательно) ОДИН бейдж срочности: только ясные формулировки («только сегодня», «последние штуки», «акция до конца дня»). ЗАПРЕЩЕНО: «24h» — непонятно что означает.
108719
- - ДОПУСКАЕТСЯ (не обязательно) 1–3 trust‑печати: печати цветные, яркие, в стиле FDA. Текст СТРОГО на языке ${generateGeo} — запрещены английские слова («NATURAL», «QUALITY» и т.д.). Примеры для RO: «naturale», «Ingrediente naturale»; для PL: «naturalna». Размер как минимум как у буллитов. Мелкие печати — ОШИБКА.
109101
+ - ДОПУСКАЕТСЯ (не обязательно) 1–3 trust‑печати: печати цветные, яркие, в стиле FDA. Текст СТРОГО на языке ${generateGeo} — запрещены английские слова («NATURAL», «QUALITY» и т.д.). По смыслу: «натуральный», «натуральный состав» оформи короткой фразой на языке ${generateGeo}, не латиницей. Размер как минимум как у буллитов. Мелкие печати — ОШИБКА.
108720
109102
  - HOOK + буллеты <= 24 слова суммарно (HOOK до 12, буллеты до 12); если больше — ОШИБКА → пересоздай
108721
- - Если хочешь добавить ощущение срочности — делай это через формулировки в HOOK/BULLETS, через реквизит/иконки, ИЛИ (не обязательно) через один короткий бейдж срочности. Только ясные формулировки («Ostatnie sztuki», «Tylko dziś», «Koniec dziś»). ЗАПРЕЩЕНО «24h» — непонятно что означает.
109103
+ - Если хочешь добавить ощущение срочности — делай это через формулировки в HOOK/BULLETS, через реквизит/иконки, ИЛИ (не обязательно) через один короткий бейдж срочности. Только ясные формулировки на языке ${generateGeo} (уровень смысла: «последние штуки», «только сегодня», «акция до конца дня»). ЗАПРЕЩЕНО «24h» — непонятно что означает.
108722
109104
 
108723
109105
  CTA > PRICE:
108724
109106
  - кнопка CTA (1–2 слова на языке ${generateGeo}) — главный якорь нижнего блока (самый контрастный элемент внизу)
@@ -108734,7 +109116,7 @@ CTA > PRICE:
108734
109116
  - Скидка: «-50%» ОБЯЗАТЕЛЬНО отдельным видимым бейджем (не только в цене!). Процент скидки должен быть явно читаем. Ярко, заметно, строго НЕ на банке/упаковке, визуально слабее CTA
108735
109117
  - Кнопка CTA (1-2 слова)
108736
109118
  - Опционально: 1 короткий бейдж срочности (ясные формулировки: «только сегодня», «последние штуки»; ЗАПРЕЩЕНО «24h»). Бейдж в углу кадра, контрастный, но меньше CTA. НЕ обязателен
108737
- - Опционально: 1–3 trust‑печати (цветные, яркие, в стиле FDA; натуральность, премиальность, безопасность). Текст СТРОГО на языке ${generateGeo} — никакого английского. Для RO: «naturale», «Ingrediente naturale», «Calitate»; для PL: «naturalna», «naturalne»; НЕ «NATURAL», «QUALITY». Размер как минимум как у буллитов. НЕ обязательны
109119
+ - Опционально: 1–3 trust‑печати (цветные, яркие, в стиле FDA; натуральность, премиальность, безопасность). Текст СТРОГО на языке ${generateGeo} — никакого английского. По смыслу: «натуральный», «натуральный состав», «качество» коротко на ${generateGeo}; НЕ «NATURAL», «QUALITY». Размер как минимум как у буллитов. НЕ обязательны
108738
109120
  - ИСКЛЮЧЕНИЕ: если 🎯 ПОДХОД = ${noBulletsCond} → показывай только: HOOK + PRICE + DISCOUNT + CTA. Буллиты/urgency/trust‑печати запрещены
108739
109121
 
108740
109122
  ВИЗУАЛ: