docs-combiner 0.1.9 → 0.1.10

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
@@ -99616,37 +99616,39 @@ __webpack_require__.r(__webpack_exports__);
99616
99616
  /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/InputLabel/InputLabel.js");
99617
99617
  /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Select/Select.js");
99618
99618
  /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/MenuItem/MenuItem.js");
99619
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Tooltip/Tooltip.js");
99620
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/ToggleButtonGroup/ToggleButtonGroup.js");
99621
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/ToggleButton/ToggleButton.js");
99622
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Paper/Paper.js");
99623
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Dialog/Dialog.js");
99624
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Backdrop/Backdrop.js");
99625
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/DialogTitle/DialogTitle.js");
99626
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/DialogContent/DialogContent.js");
99627
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/DialogActions/DialogActions.js");
99628
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/styles/createTheme.js");
99629
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/styles/ThemeProvider.js");
99630
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/AccountBalance.js");
99631
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/AutoAwesome.js");
99632
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Brightness4.js");
99633
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_39__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Brightness7.js");
99634
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/CheckCircle.js");
99635
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/CloudDownload.js");
99636
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/ContentCopy.js");
99637
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/ExpandMore.js");
99638
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_44__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/InfoOutlined.js");
99639
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_45__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Login.js");
99640
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Logout.js");
99641
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/NoteAdd.js");
99642
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Replay.js");
99643
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Settings.js");
99644
- /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_50__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Visibility.js");
99645
- /* harmony import */ var _PromptManagerDialog__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(/*! ./PromptManagerDialog */ "./src/PromptManagerDialog.tsx");
99646
- /* harmony import */ var _promptOverrides__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(/*! ./promptOverrides */ "./src/promptOverrides.ts");
99647
- /* harmony import */ var xlsx__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(/*! xlsx */ "./node_modules/xlsx/xlsx.mjs");
99648
- /* harmony import */ var jszip__WEBPACK_IMPORTED_MODULE_54__ = __webpack_require__(/*! jszip */ "./node_modules/jszip/dist/jszip.min.js");
99649
- /* harmony import */ var jszip__WEBPACK_IMPORTED_MODULE_54___default = /*#__PURE__*/__webpack_require__.n(jszip__WEBPACK_IMPORTED_MODULE_54__);
99619
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/FormControlLabel/FormControlLabel.js");
99620
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Checkbox/Checkbox.js");
99621
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Tooltip/Tooltip.js");
99622
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/ToggleButtonGroup/ToggleButtonGroup.js");
99623
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/ToggleButton/ToggleButton.js");
99624
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Paper/Paper.js");
99625
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Dialog/Dialog.js");
99626
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Backdrop/Backdrop.js");
99627
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/DialogTitle/DialogTitle.js");
99628
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/DialogContent/DialogContent.js");
99629
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/DialogActions/DialogActions.js");
99630
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/styles/createTheme.js");
99631
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/styles/ThemeProvider.js");
99632
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/AccountBalance.js");
99633
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_39__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/AutoAwesome.js");
99634
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Brightness4.js");
99635
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Brightness7.js");
99636
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/CheckCircle.js");
99637
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/CloudDownload.js");
99638
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_44__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/ContentCopy.js");
99639
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_45__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/ExpandMore.js");
99640
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/InfoOutlined.js");
99641
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Login.js");
99642
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Logout.js");
99643
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/NoteAdd.js");
99644
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_50__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Replay.js");
99645
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Settings.js");
99646
+ /* harmony import */ var _mui_icons_material__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(/*! @mui/icons-material */ "./node_modules/@mui/icons-material/esm/Visibility.js");
99647
+ /* harmony import */ var _PromptManagerDialog__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(/*! ./PromptManagerDialog */ "./src/PromptManagerDialog.tsx");
99648
+ /* harmony import */ var _promptOverrides__WEBPACK_IMPORTED_MODULE_54__ = __webpack_require__(/*! ./promptOverrides */ "./src/promptOverrides.ts");
99649
+ /* harmony import */ var xlsx__WEBPACK_IMPORTED_MODULE_55__ = __webpack_require__(/*! xlsx */ "./node_modules/xlsx/xlsx.mjs");
99650
+ /* harmony import */ var jszip__WEBPACK_IMPORTED_MODULE_56__ = __webpack_require__(/*! jszip */ "./node_modules/jszip/dist/jszip.min.js");
99651
+ /* harmony import */ var jszip__WEBPACK_IMPORTED_MODULE_56___default = /*#__PURE__*/__webpack_require__.n(jszip__WEBPACK_IMPORTED_MODULE_56__);
99650
99652
 
99651
99653
 
99652
99654
 
@@ -99747,6 +99749,10 @@ function App() {
99747
99749
  return saved || _models__WEBPACK_IMPORTED_MODULE_2__.MODELS.creativeValidation;
99748
99750
  });
99749
99751
  const [loadingValidationModels, setLoadingValidationModels] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
99752
+ const [validationDisabled, setValidationDisabled] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(() => {
99753
+ const saved = localStorage.getItem('validationDisabled');
99754
+ return saved === 'true';
99755
+ });
99750
99756
  const [imageAspectRatio, setImageAspectRatio] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(() => {
99751
99757
  const saved = localStorage.getItem('imageAspectRatio');
99752
99758
  // Migrate legacy values (4:5, 9:16) to 2:3
@@ -99958,7 +99964,7 @@ function App() {
99958
99964
  }
99959
99965
  };
99960
99966
  // Create theme based on mode
99961
- const theme = react__WEBPACK_IMPORTED_MODULE_0___default().useMemo(() => (0,_mui_material__WEBPACK_IMPORTED_MODULE_34__["default"])({
99967
+ const theme = react__WEBPACK_IMPORTED_MODULE_0___default().useMemo(() => (0,_mui_material__WEBPACK_IMPORTED_MODULE_36__["default"])({
99962
99968
  palette: {
99963
99969
  mode: darkMode ? 'dark' : 'light',
99964
99970
  ...(darkMode
@@ -100050,7 +100056,7 @@ function App() {
100050
100056
  };
100051
100057
  loadKey();
100052
100058
  // Load prompt overrides from Electron config
100053
- (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_52__.loadOverridesFromElectron)();
100059
+ (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_54__.loadOverridesFromElectron)();
100054
100060
  }, []);
100055
100061
  // Save form fields to localStorage whenever they change
100056
100062
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
@@ -100146,15 +100152,8 @@ function App() {
100146
100152
  logToTerminal('error', '[Load] Error loading content from Google Drive:', err);
100147
100153
  setLoadingContentFromDrive(false);
100148
100154
  });
100149
- // Load images data from Google Drive (30s timeout)
100150
- const imagesLoadTimeout = new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout: загрузка изображений заняла более 30s')), 30000));
100151
- Promise.race([loadGeneratedImagesFromDrive(folderId), imagesLoadTimeout]).then((result) => {
100152
- logToTerminal('log', '[Load] Images loading completed, found:', result.found);
100153
- setLoadingImagesFromDrive(false);
100154
- }).catch((err) => {
100155
- logToTerminal('warn', '[Load] Images loading stopped:', err?.message || err);
100156
- setLoadingImagesFromDrive(false);
100157
- });
100155
+ // Креативы не кешируются не загружаем изображения при открытии
100156
+ setLoadingImagesFromDrive(false);
100158
100157
  }, [driveFolderUrl]);
100159
100158
  // Sync generatedTitlesData with titles when titles changes (except when user is editing in UI)
100160
100159
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
@@ -100679,6 +100678,10 @@ function App() {
100679
100678
  setSelectedValidationModel(modelId);
100680
100679
  localStorage.setItem('selectedValidationModel', modelId);
100681
100680
  };
100681
+ const handleValidationDisabledChange = (checked) => {
100682
+ setValidationDisabled(checked);
100683
+ localStorage.setItem('validationDisabled', String(checked));
100684
+ };
100682
100685
  // Fetch OpenRouter account balance and key limit
100683
100686
  const fetchOpenRouterBalance = async (apiKey) => {
100684
100687
  const keyToUse = apiKey ?? openaiApiKey;
@@ -101537,7 +101540,7 @@ function App() {
101537
101540
  setTexts(['']);
101538
101541
  setPairTranslations({});
101539
101542
  // Read pairs count from settings (3–10, default 3)
101540
- const pairsCountInit = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_52__.getPairsCount)();
101543
+ const pairsCountInit = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_54__.getPairsCount)();
101541
101544
  // Initialize placeholders
101542
101545
  const initialTitles = Array.from({ length: pairsCountInit }, (_, index) => ({
101543
101546
  index: index + 1,
@@ -101566,7 +101569,7 @@ function App() {
101566
101569
  }
101567
101570
  // Generate all pairs (title + text) in a single request
101568
101571
  addLog(formatLogMessage('log', '📋 Generating title+text pairs in a single request...'));
101569
- const selectedIndices = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_52__.getSelectedPairApproaches)();
101572
+ const selectedIndices = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_54__.getSelectedPairApproaches)();
101570
101573
  setLastUsedApproachIndices(selectedIndices);
101571
101574
  const pairsCount = selectedIndices.length;
101572
101575
  addLog(formatLogMessage('log', `⚙️ Generating ${pairsCount} pairs (approaches: ${selectedIndices.join(', ')})`));
@@ -101661,7 +101664,7 @@ function App() {
101661
101664
  setGenerating(false);
101662
101665
  }
101663
101666
  };
101664
- const generateImageWithDALLE = async (prompt, referenceImageUrl) => {
101667
+ const generateImageWithDALLE = async (prompt, referenceImageUrl, aspectRatioOverride) => {
101665
101668
  if (!openaiApiKey) {
101666
101669
  throw new Error('OpenRouter API key is not set');
101667
101670
  }
@@ -101719,6 +101722,8 @@ function App() {
101719
101722
  // For image generation models, content must be an array with text and image_url objects
101720
101723
  const finalContent = referenceImageApiUrls.length > 0 ? content : prompt;
101721
101724
  // Build image_config based on the model family.
101725
+ // When aspectRatioOverride is provided (e.g. in 'both' mode), use it. Otherwise use imageAspectRatio.
101726
+ const effectiveRatio = aspectRatioOverride ?? (imageAspectRatio === 'both' ? '1:1' : imageAspectRatio);
101722
101727
  // openai/gpt-5-image* models use OpenAI's gpt-image-1 underneath, which only supports
101723
101728
  // three discrete sizes: 1024x1024, 1024x1536, 1536x1024.
101724
101729
  // The generic OpenRouter `aspect_ratio` param is ignored for these models.
@@ -101732,7 +101737,7 @@ function App() {
101732
101737
  '1:1': '1024x1024',
101733
101738
  '2:3': '1024x1536',
101734
101739
  };
101735
- const gptSize = gptSizeMap[imageAspectRatio] || '1024x1024';
101740
+ const gptSize = gptSizeMap[effectiveRatio] || '1024x1024';
101736
101741
  imageConfig = { size: gptSize };
101737
101742
  imageAspectRatioParam = gptSize;
101738
101743
  }
@@ -101742,7 +101747,7 @@ function App() {
101742
101747
  '1:1': '1:1',
101743
101748
  '2:3': '2:3',
101744
101749
  };
101745
- imageAspectRatioParam = aspectRatioMap[imageAspectRatio] || '1:1';
101750
+ imageAspectRatioParam = aspectRatioMap[effectiveRatio] || '1:1';
101746
101751
  imageConfig = { aspect_ratio: imageAspectRatioParam };
101747
101752
  }
101748
101753
  const requestBody = {
@@ -101921,7 +101926,7 @@ function App() {
101921
101926
  if (imageUrl) {
101922
101927
  const img = new Image();
101923
101928
  img.onload = () => {
101924
- logToTerminal('log', `📐 Actual image dimensions: ${img.naturalWidth}×${img.naturalHeight} (requested: ${imageAspectRatio}, sent image_config: ${JSON.stringify(imageConfig)})`);
101929
+ logToTerminal('log', `📐 Actual image dimensions: ${img.naturalWidth}×${img.naturalHeight} (requested: ${effectiveRatio}, sent image_config: ${JSON.stringify(imageConfig)})`);
101925
101930
  };
101926
101931
  img.src = imageUrl;
101927
101932
  }
@@ -102796,7 +102801,7 @@ function App() {
102796
102801
  addLog(formatLogMessage(level, ...args));
102797
102802
  };
102798
102803
  logMsg('log', '📦 Creating ZIP archive with HTML and product image...');
102799
- const zip = new (jszip__WEBPACK_IMPORTED_MODULE_54___default())();
102804
+ const zip = new (jszip__WEBPACK_IMPORTED_MODULE_56___default())();
102800
102805
  zip.file('index.html', htmlContent);
102801
102806
  zip.file('product.png', productImageBlob);
102802
102807
  logMsg('log', '✅ Files added to archive: index.html, product.png');
@@ -103115,18 +103120,35 @@ function App() {
103115
103120
  throw new Error(errorMsg);
103116
103121
  }
103117
103122
  addLog(formatLogMessage('log', '✅ product.png found'));
103118
- // Base prompt structure
103119
- // NOTE: This generation prompt is intentionally STRICTER than the image validator (`validateCreativeImage`).
103120
- // Keep the prompt strict even if validator allows some "acceptable" deviations (e.g. 2-line headline, benefit headline, CTA position, discount emphasis).
103121
- // Use original currency symbol if available, otherwise use currency code
103122
- const basePromptStructure = (0,_prompts__WEBPACK_IMPORTED_MODULE_1__.getImageGenerationBasePrompt)(generateGeo, generatePrice, currencyForPrompt, undefined, imageAspectRatio);
103123
- // Generate images with different approaches (one per approach)
103123
+ // Generate images with different approaches (количество по каждому подходу)
103124
103124
  const approaches = (0,_prompts__WEBPACK_IMPORTED_MODULE_1__.getCreoApproaches)();
103125
+ if (approaches.length === 0) {
103126
+ addLog(formatLogMessage('error', '❌ Укажите количество изображений (хотя бы 1) в настройках подходов'));
103127
+ alert('Укажите количество изображений (1–4) хотя бы для одного подхода в настройках');
103128
+ setGeneratingImages(false);
103129
+ return;
103130
+ }
103131
+ // При "both" — на каждый подход генерируем и 1:1, и 2:3
103132
+ const useBoth = imageAspectRatio === 'both';
103133
+ const tasks = useBoth
103134
+ ? approaches.flatMap(a => [
103135
+ { approach: a, ratio: '1:1' },
103136
+ { approach: a, ratio: '2:3' }
103137
+ ])
103138
+ : approaches.map(a => ({ approach: a, ratio: imageAspectRatio }));
103139
+ const additionalInfoLine = generateAdditionalInfo.trim()
103140
+ ? `\n📋 ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ О ПРОДУКТЕ: ${generateAdditionalInfo.trim()}`
103141
+ : '';
103142
+ const imagePrompts = tasks.map(t => {
103143
+ const basePromptStructure = (0,_prompts__WEBPACK_IMPORTED_MODULE_1__.getImageGenerationBasePrompt)(generateGeo, generatePrice, currencyForPrompt, undefined, t.ratio);
103144
+ return `🏷️ ПРОДУКТ: ${generateProduct}${additionalInfoLine}\n\n${basePromptStructure}🏷️ ПРОДУКТ (повторение для ясности): ${generateProduct}${additionalInfoLine}\n🎯 ПОДХОД: ${t.approach.name}\n\n${t.approach.prompt}\n\n${t.approach.headlineAngle}\n\n${t.approach.bulletsFocus}\n\nТекст — строго следуй правилам этого подхода (HOOK обязателен; буллиты добавляй только если они разрешены для подхода).`;
103145
+ });
103125
103146
  // Initialize placeholders for all images
103126
- const initialPlaceholders = approaches.map((approach, index) => ({
103147
+ const initialPlaceholders = tasks.map((t, index) => ({
103127
103148
  index: index + 1,
103128
103149
  imageUrl: undefined,
103129
- approach: approach.name,
103150
+ approach: t.approach.name + (useBoth ? ` (${t.ratio})` : ''),
103151
+ aspectRatio: t.ratio,
103130
103152
  uploaded: false,
103131
103153
  checking: false,
103132
103154
  checkStatus: 'pending',
@@ -103140,13 +103162,9 @@ function App() {
103140
103162
  generating: false // In sequential mode, images start queued (not generating)
103141
103163
  }));
103142
103164
  setGeneratedImagesData(initialPlaceholders);
103143
- const additionalInfoLine = generateAdditionalInfo.trim()
103144
- ? `\n📋 ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ О ПРОДУКТЕ: ${generateAdditionalInfo.trim()}`
103145
- : '';
103146
- const imagePrompts = approaches.map(approach => `🏷️ ПРОДУКТ: ${generateProduct}${additionalInfoLine}\n\n${basePromptStructure}🏷️ ПРОДУКТ (повторение для ясности): ${generateProduct}${additionalInfoLine}\n🎯 ПОДХОД: ${approach.name}\n\n${approach.prompt}\n\n${approach.headlineAngle}\n\n${approach.bulletsFocus}\n\nТекст — строго следуй правилам этого подхода (HOOK обязателен; буллиты добавляй только если они разрешены для подхода).`);
103147
- addLog(formatLogMessage('log', `📝 Generated prompts for ${approaches.length} approaches`));
103148
- const maxParallel = 3;
103149
- addLog(formatLogMessage('log', `🚀 Generating ${approaches.length} images in parallel (up to ${maxParallel} at a time)...`));
103165
+ addLog(formatLogMessage('log', `📝 Generated prompts for ${tasks.length} images${useBoth ? ' (1:1 + 2:3 на каждый подход)' : ''}`));
103166
+ const maxParallel = 100; // like infinity
103167
+ addLog(formatLogMessage('log', `🚀 Generating ${tasks.length} images in parallel (up to ${maxParallel} at a time)...`));
103150
103168
  const generationStartTime = Date.now();
103151
103169
  const resultsMap = new Map();
103152
103170
  // Ensure each slot has prompt+product reference from the start (needed for regeneration even on failures)
@@ -103157,9 +103175,11 @@ function App() {
103157
103175
  })));
103158
103176
  const runOne = async (i, isRetry) => {
103159
103177
  const imageIndex = i + 1;
103160
- const approachName = approaches[i]?.name || 'Unknown';
103178
+ const task = tasks[i];
103179
+ const approachName = task?.approach.name || 'Unknown';
103161
103180
  const prompt = imagePrompts[i];
103162
- addLog(formatLogMessage('log', `${isRetry ? '🔄' : '🎨'} Генерация изображения ${imageIndex}/${approaches.length} (${approachName})...`));
103181
+ const ratio = task?.ratio || '1:1';
103182
+ addLog(formatLogMessage('log', `${isRetry ? '🔄' : '🎨'} Генерация изображения ${imageIndex}/${tasks.length} (${approachName} ${ratio})...`));
103163
103183
  // Reset slot state for this run
103164
103184
  setGeneratedImagesData(prev => prev.map(img => img.index === imageIndex
103165
103185
  ? {
@@ -103177,7 +103197,7 @@ function App() {
103177
103197
  }
103178
103198
  : img));
103179
103199
  try {
103180
- const imageUrl = await generateImageWithDALLE(prompt, productImage.url);
103200
+ const imageUrl = await generateImageWithDALLE(prompt, productImage.url, ratio);
103181
103201
  if (!imageUrl || typeof imageUrl !== 'string' || imageUrl.trim() === '') {
103182
103202
  throw new Error('Generated image URL is empty or invalid');
103183
103203
  }
@@ -103194,59 +103214,46 @@ function App() {
103194
103214
  productImageUrl: productImage.url
103195
103215
  }
103196
103216
  : img));
103197
- addLog(formatLogMessage('log', `✅ Изображение ${imageIndex}/${approaches.length} сгенерировано`));
103198
- // Validate right after generation
103199
- try {
103200
- const validationResult = await validateCreativeImage(imageUrl, generateProduct, generateGeo, addLog, approachName);
103201
- if (!validationResult)
103202
- throw new Error('No validation result');
103203
- setGeneratedImagesData(prev => {
103204
- const updated = prev.map(img => img.index === imageIndex
103205
- ? {
103206
- ...img,
103207
- checking: false,
103208
- checkStatus: validationResult.status,
103209
- checkResult: validationResult.result,
103210
- checkErrors: validationResult.errors,
103211
- customRegeneratePrompt: validationResult.errors.length > 0
103212
- ? validationResult.errors.map(err => err.replace(/^ОШИБКА:\s*/i, '')).join('\n')
103213
- : '',
103214
- originalPrompt: img.originalPrompt || prompt,
103215
- productImageUrl: img.productImageUrl || productImage.url
103216
- }
103217
- : img);
103218
- // Save single image to Google Drive after validation
103219
- if (driveFolderUrl) {
103220
- const folderId = extractFolderId(driveFolderUrl);
103221
- if (folderId) {
103222
- const updatedImage = updated.find(img => img.index === imageIndex);
103223
- if (updatedImage) {
103224
- saveSingleImageToDrive(folderId, updatedImage).catch((err) => {
103225
- console.log(`Failed to save image ${imageIndex} after validation:`, err);
103226
- });
103227
- }
103228
- }
103217
+ addLog(formatLogMessage('log', `✅ Изображение ${imageIndex}/${tasks.length} сгенерировано`));
103218
+ // Validate right after generation (skip if validation disabled)
103219
+ const validationResult = validationDisabled
103220
+ ? { status: 'ok', result: 'Проверка отключена', errors: [], checkFailed: false }
103221
+ : await (async () => {
103222
+ try {
103223
+ const res = await validateCreativeImage(imageUrl, generateProduct, generateGeo, addLog, approachName);
103224
+ if (!res)
103225
+ throw new Error('No validation result');
103226
+ return { ...res, checkFailed: false };
103229
103227
  }
103230
- return updated;
103231
- });
103232
- }
103233
- catch (validationErr) {
103234
- const msg = validationErr?.message || String(validationErr);
103235
- addLog(formatLogMessage('error', `❌ Ошибка проверки изображения ${imageIndex}: ${msg}`));
103236
- setGeneratedImagesData(prev => prev.map(img => img.index === imageIndex
103228
+ catch (validationErr) {
103229
+ const msg = validationErr?.message || String(validationErr);
103230
+ addLog(formatLogMessage('error', `❌ Ошибка проверки изображения ${imageIndex}: ${msg}`));
103231
+ return {
103232
+ status: 'needs_rebuild',
103233
+ result: `Ошибка проверки: ${msg}`,
103234
+ errors: [msg],
103235
+ checkFailed: true
103236
+ };
103237
+ }
103238
+ })();
103239
+ setGeneratedImagesData(prev => {
103240
+ const updated = prev.map(img => img.index === imageIndex
103237
103241
  ? {
103238
103242
  ...img,
103239
103243
  checking: false,
103240
- checkStatus: 'needs_rebuild',
103241
- checkFailed: true,
103242
- checkResult: `Ошибка проверки: ${msg}`,
103243
- checkErrors: [msg],
103244
- customRegeneratePrompt: '',
103244
+ checkFailed: validationResult.checkFailed ?? false,
103245
+ checkStatus: validationResult.status,
103246
+ checkResult: validationResult.result,
103247
+ checkErrors: validationResult.errors,
103248
+ customRegeneratePrompt: validationResult.errors.length > 0
103249
+ ? validationResult.errors.map(err => err.replace(/^ОШИБКА:\s*/i, '')).join('\n')
103250
+ : '',
103245
103251
  originalPrompt: img.originalPrompt || prompt,
103246
103252
  productImageUrl: img.productImageUrl || productImage.url
103247
103253
  }
103248
- : img));
103249
- }
103254
+ : img);
103255
+ return updated;
103256
+ });
103250
103257
  return {
103251
103258
  index: imageIndex,
103252
103259
  imageUrl,
@@ -103258,7 +103265,7 @@ function App() {
103258
103265
  }
103259
103266
  catch (err) {
103260
103267
  const errorMessage = err?.message || String(err);
103261
- addLog(formatLogMessage('error', `❌ Не удалось сгенерировать изображение ${imageIndex}/${approaches.length}: ${errorMessage}`));
103268
+ addLog(formatLogMessage('error', `❌ Не удалось сгенерировать изображение ${imageIndex}/${tasks.length}: ${errorMessage}`));
103262
103269
  setGeneratedImagesData(prev => prev.map(img => img.index === imageIndex
103263
103270
  ? {
103264
103271
  ...img,
@@ -103554,7 +103561,7 @@ ${imageData.originalPrompt}
103554
103561
  });
103555
103562
  }
103556
103563
  addLog(formatLogMessage('log', `📤 Первые 300 символов промпта: ${improvedPrompt.substring(0, 300)}...`));
103557
- const newImageUrl = await generateImageWithDALLE(improvedPrompt, referenceImages);
103564
+ const newImageUrl = await generateImageWithDALLE(improvedPrompt, referenceImages, imageData.aspectRatio ?? '1:1');
103558
103565
  if (!newImageUrl)
103559
103566
  throw new Error('No image URL returned');
103560
103567
  addLog(formatLogMessage('log', `✅ Изображение ${imageData.index} переделано успешно`));
@@ -103582,9 +103589,15 @@ ${imageData.originalPrompt}
103582
103589
  failed: false // Mark as successful
103583
103590
  }
103584
103591
  : img));
103585
- // Run validation on the new image
103586
- addLog(formatLogMessage('log', `🔍 Проверка переделанного изображения ${imageData.index}...`));
103587
- const validationResult = await validateCreativeImage(newImageUrl, generateProduct, generateGeo, addLog, imageData.approach);
103592
+ // Run validation on the new image (skip if disabled)
103593
+ let validationResult;
103594
+ if (validationDisabled) {
103595
+ validationResult = { status: 'ok', result: 'Проверка отключена', errors: [] };
103596
+ }
103597
+ else {
103598
+ addLog(formatLogMessage('log', `🔍 Проверка переделанного изображения ${imageData.index}...`));
103599
+ validationResult = await validateCreativeImage(newImageUrl, generateProduct, generateGeo, addLog, imageData.approach);
103600
+ }
103588
103601
  // Update with validation result
103589
103602
  setGeneratedImagesData(prev => {
103590
103603
  const updated = prev.map(img => img.index === imageData.index
@@ -103602,18 +103615,6 @@ ${imageData.originalPrompt}
103602
103615
  productImageUrl: img.productImageUrl || imageData.productImageUrl
103603
103616
  }
103604
103617
  : img);
103605
- // Save single image to Google Drive after regeneration
103606
- if (driveFolderUrl) {
103607
- const folderId = extractFolderId(driveFolderUrl);
103608
- if (folderId) {
103609
- const updatedImage = updated.find(img => img.index === imageData.index);
103610
- if (updatedImage) {
103611
- saveSingleImageToDrive(folderId, updatedImage).catch((err) => {
103612
- console.log(`Failed to save image ${imageData.index} after regeneration:`, err);
103613
- });
103614
- }
103615
- }
103616
- }
103617
103618
  return updated;
103618
103619
  });
103619
103620
  const statusEmoji = validationResult.status === 'ok' ? '✅' : '❌';
@@ -103679,7 +103680,7 @@ ${imageData.originalPrompt}
103679
103680
  ${imageData.originalPrompt}
103680
103681
 
103681
103682
  ВАЖНО: Твой ответ — это ИЗОБРАЖЕНИЕ, не текст. Не пиши план, не перечисляй элементы текстом, не рассуждай, не вызывай tools. Сразу генерируй визуальный креатив как картинку.`;
103682
- const newImageUrl = await generateImageWithDALLE(freshPrompt, imageData.productImageUrl);
103683
+ const newImageUrl = await generateImageWithDALLE(freshPrompt, imageData.productImageUrl, imageData.aspectRatio ?? '1:1');
103683
103684
  addLog(formatLogMessage('log', `✅ Изображение ${imageData.index} (с нуля) сгенерировано`));
103684
103685
  // Update image data - always add timestamp for HTTP URLs to prevent browser caching
103685
103686
  let updatedImageUrl = newImageUrl;
@@ -103704,9 +103705,15 @@ ${imageData.originalPrompt}
103704
103705
  failed: false
103705
103706
  }
103706
103707
  : img));
103707
- // Validate new image
103708
- addLog(formatLogMessage('log', `🔍 Проверка изображения ${imageData.index} (с нуля)...`));
103709
- const validationResult = await validateCreativeImage(newImageUrl, generateProduct, generateGeo, addLog, imageData.approach);
103708
+ // Validate new image (skip if disabled)
103709
+ let validationResult;
103710
+ if (validationDisabled) {
103711
+ validationResult = { status: 'ok', result: 'Проверка отключена', errors: [] };
103712
+ }
103713
+ else {
103714
+ addLog(formatLogMessage('log', `🔍 Проверка изображения ${imageData.index} (с нуля)...`));
103715
+ validationResult = await validateCreativeImage(newImageUrl, generateProduct, generateGeo, addLog, imageData.approach);
103716
+ }
103710
103717
  setGeneratedImagesData(prev => {
103711
103718
  const updated = prev.map(img => img.index === imageData.index
103712
103719
  ? {
@@ -103722,18 +103729,6 @@ ${imageData.originalPrompt}
103722
103729
  productImageUrl: img.productImageUrl || imageData.productImageUrl
103723
103730
  }
103724
103731
  : img);
103725
- // Save single image to Google Drive after regeneration
103726
- if (driveFolderUrl) {
103727
- const folderId = extractFolderId(driveFolderUrl);
103728
- if (folderId) {
103729
- const updatedImage = updated.find(img => img.index === imageData.index);
103730
- if (updatedImage) {
103731
- saveSingleImageToDrive(folderId, updatedImage).catch((err) => {
103732
- console.log(`Failed to save image ${imageData.index} after regeneration:`, err);
103733
- });
103734
- }
103735
- }
103736
- }
103737
103732
  return updated;
103738
103733
  });
103739
103734
  }
@@ -103774,40 +103769,47 @@ ${imageData.originalPrompt}
103774
103769
  checkErrors: undefined,
103775
103770
  }
103776
103771
  : img));
103777
- addLog(formatLogMessage('log', `🔍 Повторная проверка изображения ${imageData.index}...`));
103778
- try {
103779
- const validationResult = await validateCreativeImage(imageData.imageUrl, generateProduct, generateGeo, addLog, imageData.approach);
103780
- setGeneratedImagesData(prev => prev.map(img => img.index === imageData.index
103781
- ? {
103782
- ...img,
103783
- checking: false,
103784
- checkFailed: false,
103785
- checkStatus: validationResult.status,
103786
- checkResult: validationResult.result,
103787
- checkErrors: validationResult.errors,
103788
- customRegeneratePrompt: validationResult.errors.length > 0
103789
- ? validationResult.errors.map(err => err.replace(/^ОШИБКА:\s*/i, '')).join('\n')
103790
- : '',
103791
- }
103792
- : img));
103793
- const statusEmoji = validationResult.status === 'ok' ? '✅' : '❌';
103794
- addLog(formatLogMessage('log', `${statusEmoji} Повторная проверка изображения ${imageData.index}: ${validationResult.status === 'ok' ? 'OK' : 'НУЖНА ПЕРЕСБОРКА'}`));
103772
+ let validationResult;
103773
+ if (validationDisabled) {
103774
+ validationResult = { status: 'ok', result: 'Проверка отключена', errors: [] };
103795
103775
  }
103796
- catch (err) {
103797
- const msg = err?.message || String(err);
103798
- addLog(formatLogMessage('error', `❌ Ошибка повторной проверки изображения ${imageData.index}: ${msg}`));
103799
- setGeneratedImagesData(prev => prev.map(img => img.index === imageData.index
103800
- ? {
103801
- ...img,
103802
- checking: false,
103803
- checkFailed: true,
103804
- checkStatus: 'needs_rebuild',
103805
- checkResult: `Ошибка проверки: ${msg}`,
103806
- checkErrors: [msg],
103807
- customRegeneratePrompt: '',
103808
- }
103809
- : img));
103776
+ else {
103777
+ addLog(formatLogMessage('log', `🔍 Повторная проверка изображения ${imageData.index}...`));
103778
+ try {
103779
+ validationResult = await validateCreativeImage(imageData.imageUrl, generateProduct, generateGeo, addLog, imageData.approach);
103780
+ }
103781
+ catch (err) {
103782
+ const msg = err?.message || String(err);
103783
+ addLog(formatLogMessage('error', `❌ Ошибка повторной проверки изображения ${imageData.index}: ${msg}`));
103784
+ setGeneratedImagesData(prev => prev.map(img => img.index === imageData.index
103785
+ ? {
103786
+ ...img,
103787
+ checking: false,
103788
+ checkFailed: true,
103789
+ checkStatus: 'needs_rebuild',
103790
+ checkResult: `Ошибка проверки: ${msg}`,
103791
+ checkErrors: [msg],
103792
+ customRegeneratePrompt: '',
103793
+ }
103794
+ : img));
103795
+ return;
103796
+ }
103810
103797
  }
103798
+ setGeneratedImagesData(prev => prev.map(img => img.index === imageData.index
103799
+ ? {
103800
+ ...img,
103801
+ checking: false,
103802
+ checkFailed: false,
103803
+ checkStatus: validationResult.status,
103804
+ checkResult: validationResult.result,
103805
+ checkErrors: validationResult.errors,
103806
+ customRegeneratePrompt: validationResult.errors.length > 0
103807
+ ? validationResult.errors.map(err => err.replace(/^ОШИБКА:\s*/i, '')).join('\n')
103808
+ : '',
103809
+ }
103810
+ : img));
103811
+ const statusEmoji = validationResult.status === 'ok' ? '✅' : '❌';
103812
+ addLog(formatLogMessage('log', `${statusEmoji} Повторная проверка изображения ${imageData.index}: ${validationResult.status === 'ok' ? 'OK' : 'НУЖНА ПЕРЕСБОРКА'}`));
103811
103813
  };
103812
103814
  const handleUploadImage = async (imageData, folderId) => {
103813
103815
  if (!imageData.imageUrl) {
@@ -103934,11 +103936,33 @@ ${imageData.originalPrompt}
103934
103936
  const handleLinkChange = (val) => {
103935
103937
  setLink(val);
103936
103938
  try {
103937
- new URL(val);
103939
+ const toTest = /^https?:\/\//i.test(val.trim()) ? val : 'https://' + val.trim();
103940
+ new URL(toTest);
103941
+ setLinkError('');
103942
+ }
103943
+ catch {
103944
+ setLinkError(val.trim() ? 'Invalid URL format' : '');
103945
+ }
103946
+ };
103947
+ const handleLinkBlur = () => {
103948
+ const trimmed = link.trim();
103949
+ if (!trimmed)
103950
+ return;
103951
+ let toParse = trimmed;
103952
+ if (!/^https?:\/\//i.test(trimmed)) {
103953
+ toParse = 'https://' + trimmed;
103954
+ }
103955
+ try {
103956
+ const parsed = new URL(toParse);
103957
+ let path = parsed.pathname || '/';
103958
+ if (!path.endsWith('/'))
103959
+ path += '/';
103960
+ const result = `https://${parsed.host}${path}${parsed.search}${parsed.hash}`;
103961
+ setLink(result);
103938
103962
  setLinkError('');
103939
103963
  }
103940
103964
  catch {
103941
- setLinkError('Invalid URL format');
103965
+ // invalid — leave as is
103942
103966
  }
103943
103967
  };
103944
103968
  const extractFolderId = (url) => {
@@ -104715,8 +104739,8 @@ ${imageData.originalPrompt}
104715
104739
  }
104716
104740
  setGeneratedData(rows);
104717
104741
  // Create workbook
104718
- const wb = xlsx__WEBPACK_IMPORTED_MODULE_53__.utils.book_new();
104719
- const ws = xlsx__WEBPACK_IMPORTED_MODULE_53__.utils.aoa_to_sheet(rows);
104742
+ const wb = xlsx__WEBPACK_IMPORTED_MODULE_55__.utils.book_new();
104743
+ const ws = xlsx__WEBPACK_IMPORTED_MODULE_55__.utils.aoa_to_sheet(rows);
104720
104744
  // Set column widths (approximate pixel width / 7)
104721
104745
  ws['!cols'] = [
104722
104746
  { wch: 20 }, // id
@@ -104729,9 +104753,9 @@ ${imageData.originalPrompt}
104729
104753
  { wch: 40 }, // image_link
104730
104754
  { wch: 20 } // brand
104731
104755
  ];
104732
- xlsx__WEBPACK_IMPORTED_MODULE_53__.utils.book_append_sheet(wb, ws, "Products");
104756
+ xlsx__WEBPACK_IMPORTED_MODULE_55__.utils.book_append_sheet(wb, ws, "Products");
104733
104757
  // Generate buffer
104734
- const wbout = xlsx__WEBPACK_IMPORTED_MODULE_53__.write(wb, { bookType: 'xlsx', type: 'array' });
104758
+ const wbout = xlsx__WEBPACK_IMPORTED_MODULE_55__.write(wb, { bookType: 'xlsx', type: 'array' });
104735
104759
  // Upload to Drive
104736
104760
  const dateStr = new Date().toISOString().split('T')[0];
104737
104761
  const fileName = `${brand}-${dateStr}.xlsx`;
@@ -104853,13 +104877,13 @@ ${imageData.originalPrompt}
104853
104877
  setTestLoading(true);
104854
104878
  try {
104855
104879
  // Create simple test workbook with structure
104856
- const wb = xlsx__WEBPACK_IMPORTED_MODULE_53__.utils.book_new();
104880
+ const wb = xlsx__WEBPACK_IMPORTED_MODULE_55__.utils.book_new();
104857
104881
  const rows = [
104858
104882
  INSTRUCTION_ROW,
104859
104883
  ['id', 'title', 'description', 'availability', 'condition', 'price', 'link', 'image_link', 'brand'],
104860
104884
  ['test1', 'Test Title', 'Test Description', 'in stock', 'new', '10.00 USD', 'http://test.com', 'http://test.com/img.jpg', 'TestBrand']
104861
104885
  ];
104862
- const ws = xlsx__WEBPACK_IMPORTED_MODULE_53__.utils.aoa_to_sheet(rows);
104886
+ const ws = xlsx__WEBPACK_IMPORTED_MODULE_55__.utils.aoa_to_sheet(rows);
104863
104887
  // Set column widths
104864
104888
  ws['!cols'] = [
104865
104889
  { wch: 20 }, // id
@@ -104872,8 +104896,8 @@ ${imageData.originalPrompt}
104872
104896
  { wch: 40 }, // image_link
104873
104897
  { wch: 20 } // brand
104874
104898
  ];
104875
- xlsx__WEBPACK_IMPORTED_MODULE_53__.utils.book_append_sheet(wb, ws, "Test");
104876
- const wbout = xlsx__WEBPACK_IMPORTED_MODULE_53__.write(wb, { bookType: 'xlsx', type: 'array' });
104899
+ xlsx__WEBPACK_IMPORTED_MODULE_55__.utils.book_append_sheet(wb, ws, "Test");
104900
+ const wbout = xlsx__WEBPACK_IMPORTED_MODULE_55__.write(wb, { bookType: 'xlsx', type: 'array' });
104877
104901
  // Try to extract folder ID if available, otherwise upload to root
104878
104902
  const folderId = driveFolderUrl ? extractFolderId(driveFolderUrl) : undefined;
104879
104903
  const result = await uploadFileToDrive(wbout, 'test_table.xlsx', folderId || undefined);
@@ -104907,7 +104931,7 @@ ${imageData.originalPrompt}
104907
104931
  };
104908
104932
  // Show lock screen if not unlocked
104909
104933
  if (!unlocked) {
104910
- return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_35__["default"], { theme: theme },
104934
+ return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_37__["default"], { theme: theme },
104911
104935
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_4__["default"], null),
104912
104936
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { onClick: handleSecretClick, sx: {
104913
104937
  width: '100vw',
@@ -104947,24 +104971,24 @@ ${imageData.originalPrompt}
104947
104971
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("br", null),
104948
104972
  "Please contact system administrator"))));
104949
104973
  }
104950
- return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_35__["default"], { theme: theme },
104974
+ return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_37__["default"], { theme: theme },
104951
104975
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_4__["default"], null),
104952
104976
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { maxWidth: "lg", sx: { py: 4 } },
104953
104977
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 } },
104954
104978
  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"),
104955
104979
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
104956
104980
  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 } },
104957
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_49__["default"], null)),
104958
- 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_39__["default"], null) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_38__["default"], null)))),
104981
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_51__["default"], null)),
104982
+ 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_41__["default"], null) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_40__["default"], null)))),
104959
104983
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_9__["default"], { variant: "outlined", sx: { mb: 4 } },
104960
104984
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_10__["default"], null,
104961
104985
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { spacing: 3 },
104962
104986
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
104963
104987
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h6", gutterBottom: true }, "Google Drive Authentication"),
104964
104988
  accessToken ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__["default"], null,
104965
- 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) },
104989
+ 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_45__["default"], null) },
104966
104990
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', color: 'success.main' } },
104967
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_40__["default"], { sx: { mr: 1 } }),
104991
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_42__["default"], { sx: { mr: 1 } }),
104968
104992
  " Logged In (Credentials Hidden)")),
104969
104993
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__["default"], null,
104970
104994
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { spacing: 2 },
@@ -104975,7 +104999,7 @@ ${imageData.originalPrompt}
104975
104999
  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)" }),
104976
105000
  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) }))),
104977
105001
  openaiApiKey && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 2, mt: 2 } },
104978
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_36__["default"], { color: openRouterAccountBalance !== null ? 'primary' : 'disabled' }),
105002
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_38__["default"], { color: openRouterAccountBalance !== null ? 'primary' : 'disabled' }),
104979
105003
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", color: openRouterAccountBalance !== null ? 'text.primary' : 'text.secondary' },
104980
105004
  "\u0411\u0430\u043B\u0430\u043D\u0441: ",
104981
105005
  openRouterBalanceLoading ? 'Loading...' : (openRouterAccountBalance !== null ? `$${openRouterAccountBalance.toFixed(2)}` : 'N/A')),
@@ -104984,8 +105008,8 @@ ${imageData.originalPrompt}
104984
105008
  "\u041B\u0438\u043C\u0438\u0442 \u043A\u043B\u044E\u0447\u0430: ",
104985
105009
  openRouterBalanceLoading ? 'Loading...' : (openRouterBalance === -1 ? 'Без лимита' : (openRouterBalance !== null ? `${openRouterBalance.toFixed(4)}` : 'N/A'))))),
104986
105010
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: "row", spacing: 2, alignItems: "center", sx: { mt: 2 } },
104987
- 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_40__["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')),
104988
- 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")))),
105011
+ 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_42__["default"], null) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_47__["default"], null)), sx: { flexGrow: 1 } }, authLoading ? 'Logging in...' : (accessToken ? 'Logged In' : 'Login with Google')),
105012
+ 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_48__["default"], null) }, "Logout")))),
104989
105013
  !accessToken && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
104990
105014
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], null),
104991
105015
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
@@ -105010,9 +105034,9 @@ ${imageData.originalPrompt}
105010
105034
  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"),
105011
105035
  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."))) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
105012
105036
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: { xs: 'column', sm: 'row' }, spacing: 2 },
105013
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { label: "Brand (Short ID)", variant: "outlined", fullWidth: true, value: brand, onChange: (e) => setBrand(e.target.value), placeholder: "e.g. NIKE" }),
105014
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { flexGrow: 1 } },
105015
- 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), error: !!linkError, helperText: linkError, placeholder: "http://example.com/product" }))),
105037
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], { label: "Brand (Short ID)", variant: "outlined", sx: { flex: '0 0 160px', minWidth: 140 }, value: brand, onChange: (e) => setBrand(e.target.value), placeholder: "e.g. NIKE" }),
105038
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { flex: 1, minWidth: 0 } },
105039
+ 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/" }))),
105016
105040
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { sx: { my: 2 } }),
105017
105041
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h6", gutterBottom: true }, "AI Generation Settings"),
105018
105042
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
@@ -105040,18 +105064,22 @@ ${imageData.originalPrompt}
105040
105064
  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_24__["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_24__["default"], { key: model.id, value: model.id }, model.name))))),
105041
105065
  !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
105042
105066
  ? 'Используется модель по умолчанию'
105043
- : 'Выбрана модель: ' + (validationModels.find(m => m.id === selectedValidationModel)?.name || selectedValidationModel)))))),
105067
+ : 'Выбрана модель: ' + (validationModels.find(m => m.id === selectedValidationModel)?.name || selectedValidationModel))),
105068
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_25__["default"], { control: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_26__["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 } })))),
105044
105069
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: "row", spacing: 2, sx: { mb: 2 } },
105045
- 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_37__["default"], null), onClick: handleGenerateContent, disabled: generating || loadingContentFromDrive || !openaiApiKey || !generateProduct.trim() || !generateGeo.trim(), sx: { flexGrow: 1 }, size: "large" }, generating ? 'Generating...' : 'Generate Titles & Descriptions'),
105070
+ 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_39__["default"], null), onClick: handleGenerateContent, disabled: generating || loadingContentFromDrive || !openaiApiKey || !generateProduct.trim() || !generateGeo.trim(), sx: { flexGrow: 1 }, size: "large" }, generating ? 'Generating...' : 'Generate Titles & Descriptions'),
105046
105071
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: "row", spacing: 1, alignItems: "center", sx: { flexGrow: 1 } },
105047
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_25__["default"], { title: imageAspectRatio === '1:1'
105072
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_27__["default"], { title: imageAspectRatio === '1:1'
105048
105073
  ? '1:1 — квадрат (1024×1024 px)'
105049
- : '2:3 — портрет (1024×1536 px)', placement: "top" },
105074
+ : imageAspectRatio === '2:3'
105075
+ ? '2:3 — портрет (1024×1536 px)'
105076
+ : 'Оба — квадрат и портрет на каждый подход', placement: "top" },
105050
105077
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null,
105051
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_26__["default"], { value: imageAspectRatio, exclusive: true, onChange: (_e, val) => {
105078
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { value: imageAspectRatio, exclusive: true, onChange: (_e, val) => {
105052
105079
  if (val && val !== imageAspectRatio) {
105053
105080
  if (generatedImagesData.length > 0) {
105054
- if (window.confirm(`Сменить формат на ${val}? Сгенерированные изображения будут очищены.`)) {
105081
+ const label = val === 'both' ? 'оба (1:1 + 2:3)' : val;
105082
+ if (window.confirm(`Сменить формат на ${label}? Сгенерированные изображения будут очищены.`)) {
105055
105083
  setGeneratedImagesData([]);
105056
105084
  setImageAspectRatio(val);
105057
105085
  localStorage.setItem('imageAspectRatio', val);
@@ -105063,9 +105091,10 @@ ${imageData.originalPrompt}
105063
105091
  }
105064
105092
  }
105065
105093
  }, size: "small", disabled: generatingImages, sx: { height: 42 } },
105066
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_27__["default"], { value: "1:1", sx: { px: 1.5, fontWeight: 600, fontSize: '0.8rem' } }, "1:1"),
105067
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_27__["default"], { value: "2:3", sx: { px: 1.5, fontWeight: 600, fontSize: '0.8rem' } }, "2:3")))),
105068
- 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_37__["default"], null), onClick: handleGenerateImages, disabled: generatingImages ||
105094
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { value: "1:1", sx: { px: 1.5, fontWeight: 600, fontSize: '0.8rem' } }, "1:1"),
105095
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { value: "2:3", sx: { px: 1.5, fontWeight: 600, fontSize: '0.8rem' } }, "2:3"),
105096
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { value: "both", sx: { px: 1.5, fontWeight: 600, fontSize: '0.8rem' } }, "\u041E\u0431\u0430")))),
105097
+ 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_39__["default"], null), onClick: handleGenerateImages, disabled: generatingImages ||
105069
105098
  loadingImagesFromDrive ||
105070
105099
  !openaiApiKey ||
105071
105100
  (!accessToken && !refreshToken) ||
@@ -105092,10 +105121,10 @@ ${imageData.originalPrompt}
105092
105121
  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")),
105093
105122
  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"))))),
105094
105123
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: "row", spacing: 2, sx: { mb: 2 } },
105095
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { variant: "contained", color: "success", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_37__["default"], null), onClick: handleGenerateProduct, disabled: generatingProduct || !openaiApiKey || !accessToken || !driveFolderUrl.trim(), sx: { flexGrow: 1 }, size: "large" }, generatingProduct
105124
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { variant: "contained", color: "success", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_39__["default"], null), onClick: handleGenerateProduct, disabled: generatingProduct || !openaiApiKey || !accessToken || !driveFolderUrl.trim(), sx: { flexGrow: 1 }, size: "large" }, generatingProduct
105096
105125
  ? 'Generating Product...'
105097
105126
  : (productGeneratedImage ? 'Перегенерировать Product' : 'Generate Product from Banka')),
105098
- 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 ||
105127
+ 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_49__["default"], null), onClick: handleCreateLanding, disabled: generatingLanding ||
105099
105128
  !openaiApiKey ||
105100
105129
  !accessToken ||
105101
105130
  !driveFolderUrl.trim() ||
@@ -105133,168 +105162,172 @@ ${imageData.originalPrompt}
105133
105162
  gridTemplateColumns: { xs: '1fr', sm: 'repeat(2, 1fr)', md: 'repeat(3, 1fr)' },
105134
105163
  gap: 2,
105135
105164
  mt: 1
105136
- } }, generatedImagesData.map((imageData) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { key: imageData.index, sx: { position: 'relative' } },
105137
- imageData.generating ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: {
105138
- width: '100%',
105139
- aspectRatio: imageAspectRatio === '2:3' ? '2 / 3' : '1 / 1',
105140
- border: (theme) => `2px dashed ${theme.palette.primary.main}`,
105141
- borderRadius: 1,
105142
- backgroundColor: (theme) => theme.palette.mode === 'dark'
105143
- ? 'rgba(25, 118, 210, 0.1)'
105144
- : 'rgba(25, 118, 210, 0.05)',
105145
- display: 'flex',
105146
- flexDirection: 'column',
105147
- alignItems: 'center',
105148
- justifyContent: 'center',
105149
- gap: 2,
105150
- p: 3
105151
- } },
105152
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 40, sx: { color: 'primary.main' } }),
105153
- 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..."),
105154
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'text.secondary', textAlign: 'center' } }, imageData.approach),
105155
- generatingImages && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'text.secondary', textAlign: 'center' } },
105156
- "\u23F1\uFE0F \u041F\u0440\u043E\u0448\u043B\u043E: ",
105157
- formatElapsedTime(elapsedTime.images))))) : imageData.imageUrl ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
105158
- 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: {
105165
+ } }, generatedImagesData.map((imageData) => {
105166
+ const imgRatio = imageData.aspectRatio ?? (imageAspectRatio === 'both' ? '1:1' : imageAspectRatio);
105167
+ const aspectRatioCss = imgRatio === '2:3' ? '2 / 3' : '1 / 1';
105168
+ return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { key: imageData.index, sx: { position: 'relative' } },
105169
+ imageData.generating ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: {
105159
105170
  width: '100%',
105160
- height: 'auto',
105161
- display: 'block',
105162
- objectFit: 'contain',
105163
- border: (theme) => {
105164
- if (imageData.uploaded || imageData.checkStatus === 'ok') {
105165
- return `2px solid ${theme.palette.success.main}`;
105166
- }
105167
- else if (imageData.checkStatus === 'needs_rebuild') {
105168
- return `2px solid ${theme.palette.error.main}`;
105169
- }
105170
- else if (imageData.checkStatus === 'checking') {
105171
- return `2px solid ${theme.palette.warning.main}`;
105172
- }
105173
- else {
105174
- return `2px solid ${theme.palette.primary.main}`;
105175
- }
105176
- },
105171
+ aspectRatio: aspectRatioCss,
105172
+ border: (theme) => `2px dashed ${theme.palette.primary.main}`,
105177
105173
  borderRadius: 1,
105178
- backgroundColor: (theme) => theme.palette.mode === 'dark' ? '#2a2a2a' : '#f5f5f5'
105179
- } }),
105180
- imageData.regenerating && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: {
105181
- position: 'absolute',
105182
- top: '50%',
105183
- left: '50%',
105184
- transform: 'translate(-50%, -50%)',
105174
+ backgroundColor: (theme) => theme.palette.mode === 'dark'
105175
+ ? 'rgba(25, 118, 210, 0.1)'
105176
+ : 'rgba(25, 118, 210, 0.05)',
105185
105177
  display: 'flex',
105186
105178
  flexDirection: 'column',
105187
105179
  alignItems: 'center',
105188
- gap: 1
105180
+ justifyContent: 'center',
105181
+ gap: 2,
105182
+ p: 3
105189
105183
  } },
105190
105184
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 40, sx: { color: 'primary.main' } }),
105191
- 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: {
105192
- width: '100%',
105193
- aspectRatio: imageAspectRatio === '2:3' ? '2 / 3' : '1 / 1',
105194
- border: (theme) => `2px dashed ${theme.palette.error.main}`,
105195
- borderRadius: 1,
105196
- backgroundColor: (theme) => theme.palette.mode === 'dark'
105197
- ? 'rgba(244, 67, 54, 0.1)'
105198
- : '#ffebee',
105199
- display: 'flex',
105200
- flexDirection: 'column',
105201
- alignItems: 'center',
105202
- justifyContent: 'center',
105203
- gap: 2,
105204
- p: 3
105205
- } },
105206
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h4", sx: { color: 'error.main' } }, "\u274C"),
105207
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", sx: { color: 'error.main', textAlign: 'center', fontWeight: 'bold' } }, "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0441\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C"),
105208
- imageData.errorMessage && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'error.main', textAlign: 'center', fontSize: '0.7rem' } }, imageData.errorMessage.length > 100
105209
- ? imageData.errorMessage.substring(0, 100) + '...'
105210
- : imageData.errorMessage)))) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: {
105211
- width: '100%',
105212
- aspectRatio: imageAspectRatio === '2:3' ? '2 / 3' : '1 / 1',
105213
- border: (theme) => `2px dashed ${theme.palette.divider}`,
105214
- borderRadius: 1,
105215
- backgroundColor: (theme) => theme.palette.mode === 'dark'
105216
- ? 'rgba(255,255,255,0.04)'
105217
- : '#f5f5f5',
105218
- display: 'flex',
105219
- flexDirection: 'column',
105220
- alignItems: 'center',
105221
- justifyContent: 'center',
105222
- gap: 2,
105223
- p: 3
105224
- } },
105225
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h4", sx: { color: 'text.secondary' } }, "\u23F3"),
105226
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", sx: { color: 'text.secondary', textAlign: 'center', fontWeight: 'bold' } }, "\u0412 \u043E\u0447\u0435\u0440\u0435\u0434\u0438"),
105227
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'text.secondary', textAlign: 'center' } }, generatingImages ? 'Ожидает генерации...' : 'Готово к генерации'),
105228
- generatingImages && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'text.secondary', textAlign: 'center' } },
105229
- "\u23F1\uFE0F \u041F\u0440\u043E\u0448\u043B\u043E: ",
105230
- formatElapsedTime(elapsedTime.images))))),
105231
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { mt: 1, mb: 1 } },
105232
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { display: 'block', fontWeight: 'bold' } },
105233
- imageData.index,
105234
- ". ",
105235
- imageData.approach),
105236
- imageData.checkStatus === 'ok' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'success.main', display: 'block', fontWeight: 'bold' } }, "\u2705 \u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u0440\u043E\u0439\u0434\u0435\u043D\u0430")),
105237
- imageData.checkStatus === 'needs_rebuild' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
105238
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'error.main', display: 'block', fontWeight: 'bold' } }, imageData.checkFailed ? '⚠️ Ошибка проверки' : '❌ Требует пересборки'),
105239
- imageData.checkErrors && imageData.checkErrors.length > 0 && !imageData.checkFailed && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { mt: 0.5 } },
105240
- imageData.checkErrors.slice(0, 2).map((error, idx) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { key: idx, variant: "caption", sx: {
105241
- color: 'error.main',
105242
- display: 'block',
105243
- fontSize: '0.7rem',
105244
- lineHeight: 1.2
105245
- } },
105246
- "\u2022 ",
105247
- error.length > 50 ? error.substring(0, 50) + '...' : error))),
105248
- 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' } },
105249
- "+",
105250
- imageData.checkErrors.length - 2,
105251
- " \u0435\u0449\u0451")))),
105252
- 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")))),
105253
- 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...")),
105254
- 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")),
105255
- 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")),
105256
- 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"))),
105257
- !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 } },
105258
- 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) => {
105259
- setGeneratedImagesData(prev => prev.map(img => img.index === imageData.index
105260
- ? { ...img, customRegeneratePrompt: e.target.value }
105261
- : img));
105262
- }, disabled: imageData.regenerating ||
105263
- imageData.uploading ||
105264
- imageData.checkStatus === 'checking' ||
105265
- (generatingImages && !imageData.imageUrl && !imageData.failed && !imageData.generating), sx: {
105266
- '& .MuiInputBase-root': {
105267
- fontSize: '0.875rem',
105268
- backgroundColor: (theme) => imageData.checkStatus === 'needs_rebuild'
105269
- ? (theme.palette.mode === 'dark'
105270
- ? 'rgba(244, 67, 54, 0.15)'
105271
- : 'rgba(244, 67, 54, 0.05)')
105272
- : 'transparent'
105273
- }
105274
- } }),
105275
- 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_37__["default"], null), onClick: () => handleRegenerateImage(imageData), disabled: imageData.regenerating ||
105276
- imageData.uploading ||
105277
- imageData.checkStatus === 'checking' ||
105278
- !imageData.originalPrompt ||
105279
- !imageData.productImageUrl ||
105280
- (generatingImages && !imageData.imageUrl && !imageData.failed && !imageData.generating), fullWidth: true }, imageData.regenerating
105281
- ? (imageData.failed ? 'Генерация...' : 'Переделка...')
105282
- : ((generatingImages && !imageData.imageUrl && !imageData.failed && !imageData.generating)
105283
- ? 'В очереди'
105284
- : (!imageData.imageUrl ? 'Сгенерировать' : 'Переделать'))),
105285
- 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 ||
105286
- imageData.uploading ||
105287
- imageData.checkStatus === 'checking' ||
105288
- !imageData.originalPrompt ||
105289
- !imageData.productImageUrl, fullWidth: true }, "\u041F\u0435\u0440\u0435\u0434\u0435\u043B\u0430\u0442\u044C \u0437\u0430\u043D\u043E\u0432\u043E")),
105290
- !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_41__["default"], null), onClick: () => {
105291
- const folderId = extractFolderId(driveFolderUrl);
105292
- if (folderId) {
105293
- handleUploadImage(imageData, folderId);
105294
- }
105295
- }, disabled: imageData.uploading || imageData.regenerating || !driveFolderUrl.trim(), fullWidth: true }, imageData.uploading ? 'Загрузка...' : 'Загрузить'))))))))),
105185
+ 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..."),
105186
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'text.secondary', textAlign: 'center' } }, imageData.approach),
105187
+ generatingImages && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'text.secondary', textAlign: 'center' } },
105188
+ "\u23F1\uFE0F \u041F\u0440\u043E\u0448\u043B\u043E: ",
105189
+ formatElapsedTime(elapsedTime.images))))) : imageData.imageUrl ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
105190
+ 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: {
105191
+ width: '100%',
105192
+ height: 'auto',
105193
+ display: 'block',
105194
+ objectFit: 'contain',
105195
+ border: (theme) => {
105196
+ if (imageData.uploaded || imageData.checkStatus === 'ok') {
105197
+ return `2px solid ${theme.palette.success.main}`;
105198
+ }
105199
+ else if (imageData.checkStatus === 'needs_rebuild') {
105200
+ return `2px solid ${theme.palette.error.main}`;
105201
+ }
105202
+ else if (imageData.checkStatus === 'checking') {
105203
+ return `2px solid ${theme.palette.warning.main}`;
105204
+ }
105205
+ else {
105206
+ return `2px solid ${theme.palette.primary.main}`;
105207
+ }
105208
+ },
105209
+ borderRadius: 1,
105210
+ backgroundColor: (theme) => theme.palette.mode === 'dark' ? '#2a2a2a' : '#f5f5f5'
105211
+ } }),
105212
+ imageData.regenerating && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: {
105213
+ position: 'absolute',
105214
+ top: '50%',
105215
+ left: '50%',
105216
+ transform: 'translate(-50%, -50%)',
105217
+ display: 'flex',
105218
+ flexDirection: 'column',
105219
+ alignItems: 'center',
105220
+ gap: 1
105221
+ } },
105222
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 40, sx: { color: 'primary.main' } }),
105223
+ 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: {
105224
+ width: '100%',
105225
+ aspectRatio: aspectRatioCss,
105226
+ border: (theme) => `2px dashed ${theme.palette.error.main}`,
105227
+ borderRadius: 1,
105228
+ backgroundColor: (theme) => theme.palette.mode === 'dark'
105229
+ ? 'rgba(244, 67, 54, 0.1)'
105230
+ : '#ffebee',
105231
+ display: 'flex',
105232
+ flexDirection: 'column',
105233
+ alignItems: 'center',
105234
+ justifyContent: 'center',
105235
+ gap: 2,
105236
+ p: 3
105237
+ } },
105238
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h4", sx: { color: 'error.main' } }, "\u274C"),
105239
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", sx: { color: 'error.main', textAlign: 'center', fontWeight: 'bold' } }, "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0441\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C"),
105240
+ imageData.errorMessage && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'error.main', textAlign: 'center', fontSize: '0.7rem' } }, imageData.errorMessage.length > 100
105241
+ ? imageData.errorMessage.substring(0, 100) + '...'
105242
+ : imageData.errorMessage)))) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: {
105243
+ width: '100%',
105244
+ aspectRatio: aspectRatioCss,
105245
+ border: (theme) => `2px dashed ${theme.palette.divider}`,
105246
+ borderRadius: 1,
105247
+ backgroundColor: (theme) => theme.palette.mode === 'dark'
105248
+ ? 'rgba(255,255,255,0.04)'
105249
+ : '#f5f5f5',
105250
+ display: 'flex',
105251
+ flexDirection: 'column',
105252
+ alignItems: 'center',
105253
+ justifyContent: 'center',
105254
+ gap: 2,
105255
+ p: 3
105256
+ } },
105257
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h4", sx: { color: 'text.secondary' } }, "\u23F3"),
105258
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", sx: { color: 'text.secondary', textAlign: 'center', fontWeight: 'bold' } }, "\u0412 \u043E\u0447\u0435\u0440\u0435\u0434\u0438"),
105259
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'text.secondary', textAlign: 'center' } }, generatingImages ? 'Ожидает генерации...' : 'Готово к генерации'),
105260
+ generatingImages && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'text.secondary', textAlign: 'center' } },
105261
+ "\u23F1\uFE0F \u041F\u0440\u043E\u0448\u043B\u043E: ",
105262
+ formatElapsedTime(elapsedTime.images))))),
105263
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { mt: 1, mb: 1 } },
105264
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { display: 'block', fontWeight: 'bold' } },
105265
+ imageData.index,
105266
+ ". ",
105267
+ imageData.approach),
105268
+ imageData.checkStatus === 'ok' && imageData.checkResult !== 'Проверка отключена' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'success.main', display: 'block', fontWeight: 'bold' } }, "\u2705 \u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u0440\u043E\u0439\u0434\u0435\u043D\u0430")),
105269
+ imageData.checkStatus === 'needs_rebuild' && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
105270
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'error.main', display: 'block', fontWeight: 'bold' } }, imageData.checkFailed ? '⚠️ Ошибка проверки' : '❌ Требует пересборки'),
105271
+ imageData.checkErrors && imageData.checkErrors.length > 0 && !imageData.checkFailed && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { mt: 0.5 } },
105272
+ imageData.checkErrors.slice(0, 2).map((error, idx) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { key: idx, variant: "caption", sx: {
105273
+ color: 'error.main',
105274
+ display: 'block',
105275
+ fontSize: '0.7rem',
105276
+ lineHeight: 1.2
105277
+ } },
105278
+ "\u2022 ",
105279
+ error.length > 50 ? error.substring(0, 50) + '...' : error))),
105280
+ 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' } },
105281
+ "+",
105282
+ imageData.checkErrors.length - 2,
105283
+ " \u0435\u0449\u0451")))),
105284
+ 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_50__["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")))),
105285
+ 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...")),
105286
+ 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")),
105287
+ 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")),
105288
+ 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"))),
105289
+ !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 } },
105290
+ 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) => {
105291
+ setGeneratedImagesData(prev => prev.map(img => img.index === imageData.index
105292
+ ? { ...img, customRegeneratePrompt: e.target.value }
105293
+ : img));
105294
+ }, disabled: imageData.regenerating ||
105295
+ imageData.uploading ||
105296
+ imageData.checkStatus === 'checking' ||
105297
+ (generatingImages && !imageData.imageUrl && !imageData.failed && !imageData.generating), sx: {
105298
+ '& .MuiInputBase-root': {
105299
+ fontSize: '0.875rem',
105300
+ backgroundColor: (theme) => imageData.checkStatus === 'needs_rebuild'
105301
+ ? (theme.palette.mode === 'dark'
105302
+ ? 'rgba(244, 67, 54, 0.15)'
105303
+ : 'rgba(244, 67, 54, 0.05)')
105304
+ : 'transparent'
105305
+ }
105306
+ } }),
105307
+ 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_39__["default"], null), onClick: () => handleRegenerateImage(imageData), disabled: imageData.regenerating ||
105308
+ imageData.uploading ||
105309
+ imageData.checkStatus === 'checking' ||
105310
+ !imageData.originalPrompt ||
105311
+ !imageData.productImageUrl ||
105312
+ (generatingImages && !imageData.imageUrl && !imageData.failed && !imageData.generating), fullWidth: true }, imageData.regenerating
105313
+ ? (imageData.failed ? 'Генерация...' : 'Переделка...')
105314
+ : ((generatingImages && !imageData.imageUrl && !imageData.failed && !imageData.generating)
105315
+ ? 'В очереди'
105316
+ : (!imageData.imageUrl ? 'Сгенерировать' : 'Переделать'))),
105317
+ 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_50__["default"], null), onClick: () => handleRegenerateImageFresh(imageData), disabled: imageData.regenerating ||
105318
+ imageData.uploading ||
105319
+ imageData.checkStatus === 'checking' ||
105320
+ !imageData.originalPrompt ||
105321
+ !imageData.productImageUrl, fullWidth: true }, "\u041F\u0435\u0440\u0435\u0434\u0435\u043B\u0430\u0442\u044C \u0437\u0430\u043D\u043E\u0432\u043E")),
105322
+ !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_43__["default"], null), onClick: () => {
105323
+ const folderId = extractFolderId(driveFolderUrl);
105324
+ if (folderId) {
105325
+ handleUploadImage(imageData, folderId);
105326
+ }
105327
+ }, disabled: imageData.uploading || imageData.regenerating || !driveFolderUrl.trim(), fullWidth: true }, imageData.uploading ? 'Загрузка...' : 'Загрузить'))))));
105328
+ }))),
105296
105329
  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 } },
105297
- 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_41__["default"], null), fullWidth: true }, uploadingImages ? 'Загрузка...' : `Загрузить все (${generatedImagesData.filter(img => !img.uploaded && img.imageUrl).length})`))))),
105330
+ 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_43__["default"], null), fullWidth: true }, uploadingImages ? 'Загрузка...' : `Загрузить все (${generatedImagesData.filter(img => !img.uploaded && img.imageUrl).length})`))))),
105298
105331
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { sx: { my: 2 } }),
105299
105332
  (generatedTitlesData.length > 0 || generatedTextsData.length > 0 || loadingContentFromDrive) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { mb: 2 } },
105300
105333
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h6", gutterBottom: true, sx: { fontWeight: 'bold' } },
@@ -105329,7 +105362,7 @@ ${imageData.originalPrompt}
105329
105362
  const pairLabel = pairApproach ? pairApproach.name : `пара ${i + 1}`;
105330
105363
  const pairGenerating = (titleData?.generating || textData?.generating);
105331
105364
  const pairFailed = (!pairGenerating && titleData?.failed && textData?.failed);
105332
- return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { key: i, variant: "outlined", sx: {
105365
+ return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_30__["default"], { key: i, variant: "outlined", sx: {
105333
105366
  p: 2,
105334
105367
  borderColor: pairFailed
105335
105368
  ? 'error.main'
@@ -105357,7 +105390,7 @@ ${imageData.originalPrompt}
105357
105390
  "\u041F\u0430\u0440\u0430 ",
105358
105391
  i + 1),
105359
105392
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", color: "text.secondary" }, pairLabel),
105360
- 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_25__["default"], { title: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
105393
+ 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_27__["default"], { title: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
105361
105394
  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:"),
105362
105395
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { display: 'block', mb: 1 } }, pairTranslations[i].titleRu),
105363
105396
  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:"),
@@ -105365,7 +105398,7 @@ ${imageData.originalPrompt}
105365
105398
  tooltip: { sx: { maxWidth: 360, fontSize: 12, lineHeight: 1.6, p: '10px 14px' } },
105366
105399
  } },
105367
105400
  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' } } },
105368
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_44__["default"], { sx: { fontSize: 14 } })))) : null),
105401
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_46__["default"], { sx: { fontSize: 14 } })))) : null),
105369
105402
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { spacing: 1.5 },
105370
105403
  titleData && (titleData.generating ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
105371
105404
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 16, sx: { color: 'primary.main' } }),
@@ -105406,7 +105439,7 @@ ${imageData.originalPrompt}
105406
105439
  }))))),
105407
105440
  (landingGenerationLogs.length > 0 || generatingLanding) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { mb: 2 } },
105408
105441
  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"),
105409
- landingGenerationLogs.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { sx: {
105442
+ landingGenerationLogs.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_30__["default"], { sx: {
105410
105443
  p: 2,
105411
105444
  backgroundColor: (theme) => theme.palette.mode === 'dark'
105412
105445
  ? 'rgba(25, 118, 210, 0.1)'
@@ -105432,11 +105465,11 @@ ${imageData.originalPrompt}
105432
105465
  generatingLanding && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1, mb: 2 } },
105433
105466
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 20 }),
105434
105467
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "body2", sx: { color: 'text.secondary' } }, formatElapsedTime(elapsedTime.landing)))),
105435
- !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")))),
105468
+ !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_52__["default"], null), fullWidth: true }, "\u041F\u0440\u0435\u0434\u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440 \u043B\u0435\u043D\u0434\u0438\u043D\u0433\u0430")))),
105436
105469
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { direction: "row", spacing: 2 },
105437
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { variant: "contained", size: "large", onClick: handleGenerate, disabled: loading || !accessToken, 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_41__["default"], null), sx: { py: 1.5, fontSize: '1.1rem', flexGrow: 1 } }, loading ? 'Generating...' : 'Generate Table')),
105470
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { variant: "contained", size: "large", onClick: handleGenerate, disabled: loading || !accessToken, 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_43__["default"], null), sx: { py: 1.5, fontSize: '1.1rem', flexGrow: 1 } }, loading ? 'Generating...' : 'Generate Table')),
105438
105471
  uploadedLink && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { severity: "success", sx: { mt: 2 }, 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 } },
105439
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_42__["default"], { fontSize: "small" })) },
105472
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_44__["default"], { fontSize: "small" })) },
105440
105473
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
105441
105474
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null,
105442
105475
  "File Uploaded!",
@@ -105446,8 +105479,8 @@ ${imageData.originalPrompt}
105446
105479
  getElectronAPI().openExternal(uploadedLink);
105447
105480
  }, style: { cursor: 'pointer', textDecoration: 'underline', color: 'inherit' } }, "Click here to open in Google Drive")),
105448
105481
  linkCopied && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "caption", sx: { color: 'success.dark', fontWeight: 'bold' } }, "Copied!")))))))))),
105449
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { open: productModalOpen, onClose: () => !generatingProduct && !uploadingProduct && setProductModalOpen(false), maxWidth: "lg", fullWidth: true },
105450
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_30__["default"], { open: generatingProduct || uploadingProduct, sx: {
105482
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_31__["default"], { open: productModalOpen, onClose: () => !generatingProduct && !uploadingProduct && setProductModalOpen(false), maxWidth: "lg", fullWidth: true },
105483
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_32__["default"], { open: generatingProduct || uploadingProduct, sx: {
105451
105484
  position: 'absolute',
105452
105485
  zIndex: (theme) => theme.zIndex.modal + 1,
105453
105486
  backgroundColor: 'rgba(0, 0, 0, 0.7)',
@@ -105458,7 +105491,7 @@ ${imageData.originalPrompt}
105458
105491
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { sx: { display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 2 } },
105459
105492
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { size: 60, sx: { color: 'white' } }),
105460
105493
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h6", sx: { color: 'white', fontWeight: 'bold' } }, formatElapsedTime(elapsedTime.product))),
105461
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { sx: {
105494
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_30__["default"], { sx: {
105462
105495
  p: 3,
105463
105496
  maxWidth: '80%',
105464
105497
  maxHeight: '60%',
@@ -105482,9 +105515,9 @@ ${imageData.originalPrompt}
105482
105515
  log.includes('⚠️') || log.toLowerCase().includes('warn') ? 'warning.main' :
105483
105516
  'text.primary'
105484
105517
  } }, log))))))),
105485
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_31__["default"], null,
105518
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_33__["default"], null,
105486
105519
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "h6" }, "Product Image Generation")),
105487
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_32__["default"], null,
105520
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_34__["default"], null,
105488
105521
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__["default"], { spacing: 3, sx: { mt: 1 } },
105489
105522
  productSourceImages.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], null,
105490
105523
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { variant: "subtitle1", gutterBottom: true, sx: { fontWeight: 'bold' } },
@@ -105523,11 +105556,11 @@ ${imageData.originalPrompt}
105523
105556
  mt: 1
105524
105557
  } }))),
105525
105558
  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\u044B\u0435 \u0442\u0440\u0435\u0431\u043E\u0432\u0430\u043D\u0438\u044F \u043A \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438", variant: "outlined", fullWidth: true, multiline: true, minRows: 2, value: productRegeneratePrompt, onChange: (e) => setProductRegeneratePrompt(e.target.value), placeholder: "\u041D\u0430\u043F\u0440\u0438\u043C\u0435\u0440: \u0441\u0434\u0435\u043B\u0430\u0439 \u0444\u043E\u043D \u0431\u0435\u043B\u044B\u043C, \u0443\u0432\u0435\u043B\u0438\u0447\u044C \u043B\u043E\u0433\u043E\u0442\u0438\u043F...", helperText: "\u042D\u0442\u0438 \u0442\u0440\u0435\u0431\u043E\u0432\u0430\u043D\u0438\u044F \u0431\u0443\u0434\u0443\u0442 \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u044B \u043A \u0431\u0430\u0437\u043E\u0432\u043E\u043C\u0443 \u043F\u0440\u043E\u043C\u043F\u0442\u0443 (\u043D\u0435 \u0437\u0430\u043C\u0435\u043D\u044F\u044E\u0442 \u0435\u0433\u043E). \u041E\u0441\u0442\u0430\u0432\u044C\u0442\u0435 \u043F\u0443\u0441\u0442\u044B\u043C \u0434\u043B\u044F \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u044F \u0442\u043E\u043B\u044C\u043A\u043E \u0431\u0430\u0437\u043E\u0432\u043E\u0433\u043E \u043F\u0440\u043E\u043C\u043F\u0442\u0430.", disabled: generatingProduct || uploadingProduct }))),
105526
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_33__["default"], { sx: { px: 3, pb: 2 } },
105559
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_35__["default"], { sx: { px: 3, pb: 2 } },
105527
105560
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { onClick: () => setProductModalOpen(false), disabled: generatingProduct || uploadingProduct }, "\u0417\u0430\u043A\u0440\u044B\u0442\u044C"),
105528
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { variant: "outlined", color: "secondary", onClick: handleRegenerateProduct, disabled: generatingProduct || uploadingProduct || !productSourceImages.length || !productGeneratedImage, startIcon: generatingProduct ? 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_37__["default"], null) }, generatingProduct ? 'Перегенерация...' : 'Перегенерировать'),
105529
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { variant: "contained", color: "primary", onClick: handleUploadProduct, disabled: generatingProduct || uploadingProduct || !productGeneratedImage || !driveFolderUrl.trim(), startIcon: uploadingProduct ? 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_41__["default"], null) }, uploadingProduct ? 'Загрузка...' : 'Загрузить (product.png)'))),
105530
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_PromptManagerDialog__WEBPACK_IMPORTED_MODULE_51__["default"], { open: promptManagerOpen, onClose: () => setPromptManagerOpen(false) }))));
105561
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { variant: "outlined", color: "secondary", onClick: handleRegenerateProduct, disabled: generatingProduct || uploadingProduct || !productSourceImages.length || !productGeneratedImage, startIcon: generatingProduct ? 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_39__["default"], null) }, generatingProduct ? 'Перегенерация...' : 'Перегенерировать'),
105562
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { variant: "contained", color: "primary", onClick: handleUploadProduct, disabled: generatingProduct || uploadingProduct || !productGeneratedImage || !driveFolderUrl.trim(), startIcon: uploadingProduct ? 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_43__["default"], null) }, uploadingProduct ? 'Загрузка...' : 'Загрузить (product.png)'))),
105563
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_PromptManagerDialog__WEBPACK_IMPORTED_MODULE_53__["default"], { open: promptManagerOpen, onClose: () => setPromptManagerOpen(false) }))));
105531
105564
  }
105532
105565
  /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (App);
105533
105566
 
@@ -105962,12 +105995,15 @@ function PromptManagerDialog({ open, onClose }) {
105962
105995
  const [viewModes, setViewModes] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)({});
105963
105996
  // selectedApproaches: array of indices from PAIR_APPROACH_POOL, ordered
105964
105997
  const [selectedApproaches, setSelectedApproaches] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)([0, 1, 2]);
105998
+ // imageApproachCounts: 8 элементов, каждый 0–4
105999
+ const [imageApproachCounts, setImageApproachCounts] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)([1, 1, 1, 1, 1, 1, 1, 1]);
105965
106000
  // Загрузить оверрайды при открытии
105966
106001
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
105967
106002
  if (open) {
105968
106003
  const loaded = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_36__.loadPromptOverrides)();
105969
106004
  setOverrides(loaded);
105970
106005
  setSelectedApproaches((0,_promptOverrides__WEBPACK_IMPORTED_MODULE_36__.getSelectedPairApproaches)());
106006
+ setImageApproachCounts((0,_promptOverrides__WEBPACK_IMPORTED_MODULE_36__.getImageApproachCounts)());
105971
106007
  setHasChanges(false);
105972
106008
  }
105973
106009
  }, [open]);
@@ -105992,6 +106028,17 @@ function PromptManagerDialog({ open, onClose }) {
105992
106028
  return next;
105993
106029
  });
105994
106030
  };
106031
+ const handleImageApproachCountChange = (idx, count) => {
106032
+ const clamped = Math.max(0, Math.min(4, count));
106033
+ setImageApproachCounts(prev => {
106034
+ const next = [...prev];
106035
+ if (idx >= 0 && idx < next.length)
106036
+ next[idx] = clamped;
106037
+ setOverrides(o => ({ ...o, imageApproachCounts: next }));
106038
+ setHasChanges(true);
106039
+ return next;
106040
+ });
106041
+ };
105995
106042
  const handleToggleOverride = (promptName, enabled) => {
105996
106043
  setOverrides(prev => {
105997
106044
  const prevOverride = prev[promptName];
@@ -106169,14 +106216,14 @@ function PromptManagerDialog({ open, onClose }) {
106169
106216
  hasChanges && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { label: "\u0415\u0441\u0442\u044C \u043D\u0435\u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u043D\u044B\u0435 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F", color: "warning", size: "small" })))),
106170
106217
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__["default"], null,
106171
106218
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_16__["default"], { value: tabValue, onChange: handleTabChange, sx: { borderBottom: 1, borderColor: 'divider' } },
106172
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { label: "\u0417\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0438 \u0438 \u0442\u0435\u043A\u0441\u0442\u044B" }),
106219
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { label: "\u041F\u043E\u0434\u0445\u043E\u0434\u044B" }),
106173
106220
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { label: "\u0418\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u044F" }),
106174
106221
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { label: "\u0412\u0430\u043B\u0438\u0434\u0430\u0446\u0438\u044F" }),
106175
106222
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { label: "\u041B\u0435\u043D\u0434\u0438\u043D\u0433" })),
106176
106223
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(TabPanel, { value: tabValue, index: 0 },
106177
106224
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_1__["default"], { sx: { mb: 3, p: 2, border: '1px solid', borderColor: 'divider', borderRadius: 1 } },
106178
106225
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { direction: "row", spacing: 2, alignItems: "center", sx: { mb: 1.5 } },
106179
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_3__["default"], { variant: "subtitle1", sx: { fontWeight: 600 } }, "\u041F\u043E\u0434\u0445\u043E\u0434\u044B \u0434\u043B\u044F \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u043F\u0430\u0440"),
106226
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_3__["default"], { variant: "subtitle1", sx: { fontWeight: 600 } }, "\u041F\u043E\u0434\u0445\u043E\u0434\u044B \u0434\u043B\u044F \u0442\u0435\u043A\u0441\u0442\u043E\u0432 (\u043F\u0430\u0440\u044B \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043E\u043A + \u0442\u0435\u043A\u0441\u0442)"),
106180
106227
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { label: `Выбрано: ${selectedApproaches.length}`, color: "primary", size: "small" }),
106181
106228
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_3__["default"], { variant: "caption", color: "text.secondary" }, "(\u043C\u0438\u043D\u0438\u043C\u0443\u043C 2, \u043C\u0430\u043A\u0441\u0438\u043C\u0443\u043C 10)")),
106182
106229
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_1__["default"], { sx: { overflowX: 'auto' } },
@@ -106204,11 +106251,34 @@ function PromptManagerDialog({ open, onClose }) {
106204
106251
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("td", { style: { padding: '4px 8px', color: '#777' } }, p.titleApproach),
106205
106252
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("td", { style: { padding: '4px 8px', color: '#777' } }, p.textApproach.split('\n')[0])));
106206
106253
  }))))),
106254
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_1__["default"], { sx: { mb: 3, p: 2, border: '1px solid', borderColor: 'divider', borderRadius: 1 } },
106255
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { direction: "row", spacing: 2, alignItems: "center", sx: { mb: 1.5 } },
106256
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_3__["default"], { variant: "subtitle1", sx: { fontWeight: 600 } }, "\u041F\u043E\u0434\u0445\u043E\u0434\u044B \u0434\u043B\u044F \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0439"),
106257
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { label: `Всего: ${imageApproachCounts.reduce((a, b) => a + b, 0)}`, color: "primary", size: "small" }),
106258
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_3__["default"], { variant: "caption", color: "text.secondary" }, "(0\u20134 \u043D\u0430 \u043A\u0430\u0436\u0434\u044B\u0439 \u043F\u043E\u0434\u0445\u043E\u0434)")),
106259
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_1__["default"], { sx: { overflowX: 'auto' } },
106260
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("table", { style: { width: '100%', borderCollapse: 'collapse', fontSize: 12 } },
106261
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("thead", null,
106262
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("tr", null,
106263
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("th", { style: { textAlign: 'center', padding: '4px 8px', borderBottom: '1px solid #ccc', width: 70 } }, "\u041A\u043E\u043B-\u0432\u043E"),
106264
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("th", { style: { textAlign: 'left', padding: '4px 8px', borderBottom: '1px solid #ccc', whiteSpace: 'nowrap', width: 180 } }, "\u041F\u043E\u0434\u0445\u043E\u0434"),
106265
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("th", { style: { textAlign: 'left', padding: '4px 8px', borderBottom: '1px solid #ccc' } }, "\u041E\u043F\u0438\u0441\u0430\u043D\u0438\u0435"))),
106266
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("tbody", null, _prompts__WEBPACK_IMPORTED_MODULE_34__.CREO_APPROACHES.map((approach, i) => {
106267
+ const count = imageApproachCounts[i] ?? 0;
106268
+ return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement("tr", { key: i, style: { opacity: count > 0 ? 1 : 0.6 } },
106269
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("td", { style: { textAlign: 'center', padding: '2px 8px', verticalAlign: 'middle' } },
106270
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__["default"], { type: "number", size: "small", value: count, onChange: (e) => {
106271
+ const v = parseInt(e.target.value, 10);
106272
+ handleImageApproachCountChange(i, isNaN(v) ? 0 : v);
106273
+ }, inputProps: { min: 0, max: 4, step: 1 }, sx: { width: 56, '& .MuiInputBase-input': { textAlign: 'center', py: 0.5 } } })),
106274
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("td", { style: { padding: '4px 8px', fontWeight: 500, whiteSpace: 'nowrap' } }, approach.name),
106275
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("td", { style: { padding: '4px 8px', color: '#777' } }, approach.prompt.split('\n')[0])));
106276
+ }))))),
106207
106277
  renderPromptEditor('getPairsSystemPrompt', 'Системный промпт (пары заголовок + текст)', 'Единый системный промпт, генерирующий все пары одним запросом. Переменные: ${geo}, ${count}, ${n}'),
106208
106278
  renderPromptEditor('getPairsUserPrompt', 'Пользовательский промпт (пары)', 'Пользовательский промпт для генерации пар. Переменные: ${product}, ${geo}, ${additionalInfo}, ${count}, ${n}')),
106209
106279
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(TabPanel, { value: tabValue, index: 1 },
106210
106280
  renderPromptEditor('getImageGenerationBasePrompt', 'Базовый промпт для изображений', 'Базовый промпт, используемый для всех подходов'),
106211
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_3__["default"], { variant: "h6", sx: { mt: 3, mb: 2 } }, "\u041F\u043E\u0434\u0445\u043E\u0434\u044B \u0434\u043B\u044F \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u043A\u0440\u0435\u0430\u0442\u0438\u0432\u043E\u0432"),
106281
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_3__["default"], { variant: "h6", sx: { mt: 3, mb: 2 } }, "\u0420\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u0435 \u043F\u043E\u0434\u0445\u043E\u0434\u043E\u0432 \u0434\u043B\u044F \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u043A\u0440\u0435\u0430\u0442\u0438\u0432\u043E\u0432"),
106212
106282
  _prompts__WEBPACK_IMPORTED_MODULE_34__.CREO_APPROACHES.map((approach) => {
106213
106283
  const override = overrides.creoApproaches?.[approach.name];
106214
106284
  const enabled = override?.enabled || false;
@@ -106478,6 +106548,7 @@ const MODELS = {
106478
106548
  __webpack_require__.r(__webpack_exports__);
106479
106549
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
106480
106550
  /* harmony export */ getCreoApproachOverride: () => (/* binding */ getCreoApproachOverride),
106551
+ /* harmony export */ getImageApproachCounts: () => (/* binding */ getImageApproachCounts),
106481
106552
  /* harmony export */ getPairsCount: () => (/* binding */ getPairsCount),
106482
106553
  /* harmony export */ getPromptOverride: () => (/* binding */ getPromptOverride),
106483
106554
  /* harmony export */ getSelectedPairApproaches: () => (/* binding */ getSelectedPairApproaches),
@@ -106525,6 +106596,26 @@ function getSelectedPairApproaches() {
106525
106596
  function getPairsCount() {
106526
106597
  return getSelectedPairApproaches().length;
106527
106598
  }
106599
+ /**
106600
+ * Получить количество изображений по каждому подходу (8 элементов, 0–4).
106601
+ * По умолчанию — по 1 на каждый подход.
106602
+ */
106603
+ function getImageApproachCounts() {
106604
+ const overrides = loadPromptOverrides();
106605
+ if (overrides.imageApproachCounts && overrides.imageApproachCounts.length === 8) {
106606
+ return overrides.imageApproachCounts.map(c => Math.max(0, Math.min(4, c)));
106607
+ }
106608
+ // backward compat: selectedImageApproaches -> counts (1 for selected, 0 for not)
106609
+ if (overrides.selectedImageApproaches && overrides.selectedImageApproaches.length >= 1) {
106610
+ const counts = Array(8).fill(0);
106611
+ for (const i of overrides.selectedImageApproaches) {
106612
+ if (i >= 0 && i < 8)
106613
+ counts[i] = 1;
106614
+ }
106615
+ return counts;
106616
+ }
106617
+ return [1, 1, 1, 1, 1, 1, 1, 1];
106618
+ }
106528
106619
  /**
106529
106620
  * Загрузить оверрайды из localStorage
106530
106621
  */
@@ -107096,36 +107187,32 @@ function getValidationPrompt(product, geo, keywords, approachName, noOverride) {
107096
107187
  : `ШАГ 2 — BULLETS (критично):
107097
107188
  - РОВНО 3 буллета. Если буллетов не 3 — ОШИБКА.
107098
107189
  - Каждый буллет должен быть читабельным (хорошо читается на телефоне, не микрошрифт)
107099
- - Каждый буллет: 2–4 слова, без запятых
107190
+ - Буллеты: крупные, с иконками/галочками (✓), на контрастных подложках. Буллеты НЕ на упаковке/банке.
107100
107191
  - Формат бенефита РАЗРЕШЁН: «Чувствуешь себя легче», «Больше энергии», «Без дискомфорта», «Лёгкость движений» — это бенефиты, НЕ предложения
107101
- - ПРЕДЛОЖЕНИЕ (ошибка) = полная грамматическая конструкция с подлежащим + сказуемым + дополнением, например «Продукт быстро снижает дискомфорт». Короткие бенефиты из 2-4 слов — не считать предложениями
107192
+ - ПРЕДЛОЖЕНИЕ (ошибка) = полная грамматическая конструкция с подлежащим + сказуемым + дополнением, например «Продукт быстро снижает дискомфорт». Короткие бенефиты — не считать предложениями
107102
107193
  - Буллиты с глаголом во 2-м лице («Чувствуешь...», «Получаешь...») или безличные («Больше энергии», «Без дискомфорта») — НЕ ошибка
107103
- - Буллеты = свойства, характеристики ИЛИ мягкие бенефиты. Разрешено: «Reduce disconfortul», «Mai puține treziri», «Efect în 14 zile», цифры соц. доказательства. Запрещено: жёсткие гарантии («лечит», «вылечивает», «гарантируем», «навсегда»).
107104
- - Буллеты не на упаковке
107105
- - Буллиты должны быть расположены вертикально (столбиком). Если буллиты идут горизонтально в одну строку ОШИБКА: плохая читаемость на мобиле`;
107106
- // ШАГ 3 — TEXT LIMIT: PVP has no badges allowed, normal allows urgency/trust
107194
+ - Буллеты = свойства, характеристики ИЛИ бенефиты. Разрешено: «Reduce disconfortul», «Mai puține treziri», «Efect în 14 zile», цифры соц. доказательства.
107195
+ - Буллиты должны быть расположены вертикально (столбиком). Если буллиты идут горизонтально в одну строку — ОШИБКА: плохая читаемость на мобиле
107196
+ - Цена визуально ОТЛИЧНА от буллитов (цена компактный блок без иконок; буллетыс иконками/галочками). Если цена выглядит как буллет — ОШИБКА.`;
107197
+ // ШАГ 3 — TEXT LIMIT: PVP has no badges allowed, normal allows urgency/trust (без проверок по длине/количеству слов)
107107
107198
  const step3TextLimit = isNoBullets
107108
- ? `ШАГ 3 — TEXT LIMIT (критично):
107109
- - Общее число слов в HOOK/HEADLINE <= 5. Если большеошибка.
107110
- - OTHER_TEXT должен быть ПУСТЫМ (0 элементов). Никаких дополнительных бейджей, подписей, ярлыков, trust/urgency — НИЧЕГО.
107111
- - Любой дополнительный текст (бейджи/подписи/пояснения/urgency/trust) — ОШИБКА: слишком много текста для punch‑креатива.
107199
+ ? `ШАГ 3 — OTHER_TEXT (критично):
107200
+ - OTHER_TEXT должен быть ПУСТЫМ (0 элементов). Никаких дополнительных бейджей, подписей, ярлыков, trust‑печатей/urgency НИЧЕГО.
107201
+ - Любой дополнительный текст (бейджи/подписи/пояснения/urgency/trust‑печати) ОШИБКА: слишком много текста для punch‑креатива.
107112
107202
  - На креативе допустимы ТОЛЬКО: HOOK, цена, скидка, кнопка CTA.`
107113
- : `ШАГ 3 — TEXT LIMIT (критично):
107114
- - Общее число слов в HOOK/HEADLINE + BULLETS <= 16. Если больше — ошибка.
107203
+ : `ШАГ 3 — OTHER_TEXT (критично):
107115
107204
  - По умолчанию любой другой рекламный текст (бейджи/подписи/пояснения) запрещён.
107116
- - НО допускаются (не обязательно) дополнительные маленькие бейджи ВНЕ упаковки — это НЕ ошибка, если ВСЕ элементы OTHER_TEXT подпадают под разрешённые типы:
107117
- A) URGENCY (0–1 шт): короткий бейдж срочности/дефицита/лимита времени.
107118
- B) TRUST (0–3 шт): бейджи доверия/сервиса (качество/доставка/поддержка/проверено/рекомендуют).
107205
+ - НО допускаются (не обязательно) дополнительные бейджи ВНЕ упаковки — это НЕ ошибка, если ВСЕ элементы OTHER_TEXT подпадают под разрешённые типы:
107206
+ A) URGENCY (0–1 шт): бейдж срочности/дефицита — только ясные формулировки («только сегодня», «последние штуки», «акция до конца дня»). ЗАПРЕЩЕНО: «24h» и подобные — непонятно что означает.
107207
+ B) TRUST (0–3 шт, для «Минимализм / Clean Big Text» — макс 1): печати цветные, яркие, очень похожие на печать FDA. Подчёркивают: натуральность состава, премиальность, безопасность. Допустимо: качество, натуральные ингредиенты, экологичность, контроль качества. Печати — размер как минимум как у буллитов, крупные. Даже одна печать — крупная.
107119
107208
  Правила для каждого элемента OTHER_TEXT:
107120
- * короткий: 1–3 слова ИЛИ число+слово (например «24h»)
107121
107209
  * строго на языке ${geo} (без английских слов)
107122
107210
  * без цены/скидки/процентов (кроме обязательного поля DISCOUNT), без доменов/ссылок
107123
- * без диагнозов и без жёстких медицинских обещаний
107124
- * TRUST‑бейджи должны быть про доверие/сервис (качество/доставка/поддержка/проверено/рекомендуют), не про «лечит/результаты». Запрещено: «рекомендовано врачами», «клинически доказано», «одобрено Минздравом» и любые фразы, которые требуют доказательств/авторитетов
107211
+ * TRUST‑печати про натуральность/премиальность/безопасность (не про доставку/поддержку)
107125
107212
  Примеры стиля (адаптируй к языку GEO, не требуются):
107126
- - URGENCY: «Ostatnie sztuki», «Tylko dziś», «Koniec dziś», «Tylko 24h»
107127
- - TRUST: «Wysoka jakość», «Szybka dostawa», «Wsparcie klienta», «Sprawdzone», «Polecane»
107128
- - Если OTHER_TEXT содержит что-то вне этих типов, или URGENCY > 1, или TRUST > 3 — ОШИБКА.`;
107213
+ - URGENCY: «Ostatnie sztuki», «Tylko dziś», «Koniec dziś» (НЕ «24h» — непонятно)
107214
+ - TRUST: «Naturalna formuła», «Wysoka jakość», «Bezpieczne», «Eko», «Kontrola jakości»
107215
+ - Если OTHER_TEXT содержит что-то вне этих типов, или URGENCY > 1, или TRUST > 3 (для Минимализма TRUST > 1) — ОШИБКА.`;
107129
107216
  return `Ты — СТРОГИЙ валидатор рекламных креативов (1:1). Не улучшай и не предлагай идеи — только проверка и вердикт.
107130
107217
  Продукт: ${product}. GEO/язык: ${geo}.${approachLine}
107131
107218
 
@@ -107142,15 +107229,13 @@ DISCOUNT: "..." (если нет — "нет")
107142
107229
  CTA: "..." (если нет — "нет")
107143
107230
  OTHER_TEXT: ["..."] (любой другой рекламный текст на макете ВНЕ упаковки и ВНЕ элементов выше; название/бренд на упаковке НЕ включай)
107144
107231
  (если можешь, для ясности — не обязательно):
107145
- URGENCY_BADGE: "..." (короткий бейдж срочности, если есть)
107146
- TRUST_BADGES: ["..."] (бейджи доверия/сервиса, если есть)
107232
+ URGENCY_BADGE: "..." (бейдж срочности, если есть; «24h» — ОШИБКА, непонятно что означает)
107233
+ TRUST_BADGES: ["..."] (печати цветные, яркие, в стиле FDA; про натуральность/премиальность/безопасность; размер как у буллитов — крупные, если есть)
107147
107234
 
107148
107235
  ШАГ 1 — HOOK/HEADLINE (критично):
107149
107236
  - Должен быть в верхнем блоке и читабелен (хорошо читается на телефоне)
107150
- - РОВНО 3–5 слов
107151
107237
  - Допускается 1–2 строки (это НЕ ошибка). Ошибка только если текст нечитабелен или есть разрыв/перенос внутри слова.
107152
- - Смысл: релевантно проблеме/выгоде продукта. Допускаются и боль/дискомфорт, и обещание/выгода (например «Könnyebb napok»). НЕ считай ошибкой, если по смыслу это обещание результата/выгоды.
107153
- - Мягкие обещания результата («Nopți liniștite», «Stomac fără griji») — не ошибка. Ошибка только если есть медицинская гарантия или диагноз.
107238
+ - Смысл: релевантно проблеме/выгоде продукта. Допускаются и боль/дискомфорт, и обещание/выгода (например «Könnyebb napok»). НЕ считай ошибкой формулировки обещаний результата или клеймы.
107154
107239
  - Желательно: ключевое слово боли/проблемы в формулировке (как часть смысла), но отсутствие ключевого слова само по себе НЕ является ошибкой. Примеры для ориентира: ${keywords.join(', ')} (можно синонимы).
107155
107240
  - Проверь вторую часть заголовка. Если она состоит только из абстрактных слов («Mai ușor», «Mai bine», «Soluția», «Ajutor») без конкретного бенефита — отметь: РЕКОМЕНДАЦИЯ: заголовок можно усилить конкретикой
107156
107241
  - Если заголовок слишком общий/абстрактный («Mai ușor», «Mai bine» без конкретики) — отметь как РЕКОМЕНДАЦИЯ к улучшению (не ошибка, но слабо)
@@ -107158,19 +107243,15 @@ TRUST_BADGES: ["..."] (бейджи доверия/сервиса, если ес
107158
107243
 
107159
107244
  ${step2Bullets}
107160
107245
 
107161
- ШАГ 2.5 — CLAIMS CHECK (критично):
107162
- - Мягкие клеймы РАЗРЕШЕНЫ: «помогает», «поддерживает», «способствует», «может снизить», «выводит токсины», «устраняет дискомфорт», «очищает организм», сроки как ожидание («через 2 недели», «за 7-14 дней»), цифры без гарантий («9 из 10 рекомендуют»)
107163
- - ЗАПРЕЩЕНЫ только: «лечит», «вылечивает», «гарантируем», «100%», «навсегда», «полностью избавляет», диагностика заболеваний
107164
- - «Выводит/устраняет токсины» для детокс-продуктов — это мягкий клейм, НЕ медицинская гарантия
107165
- - Если найден запрещённый клейм — ошибка. Мягкие клеймы — не ошибка.
107166
- - Специфические медицинские термины или диагнозы — разрешены, но не должны повторяться чрезмерно. Если в HOOK/HEADLINE + BULLETS один и тот же специфический термин встречается более 1 раза — РЕКОМЕНДАЦИЯ: разнообразить формулировки, использовать синонимы или фокус на симптомах вместо повторения термина
107246
+ ШАГ 2.5 — CLAIMS CHECK:
107247
+ - Клеймы по результату (жёсткие/абсолютные или мягкие) НЕ проверяются и НЕ запрещаются. Не блокируй за формулировки обещаний результата.
107167
107248
 
107168
107249
  ${step3TextLimit}
107169
107250
 
107170
107251
  ШАГ 4 — CTA > PRICE (критично):
107171
- - CTA: 1–2 слова на языке ${geo}, хорошо заметная кнопка (позиция НЕ важна: можно вправо/влево/по центру). Главное — CTA читабельна и выглядит как кнопка.
107172
- - Цена видна и читабельна
107173
- - Скидка НЕ обязательна. Если скидка есть, она должна быть "-50%" (возможны варианты символа минуса: -, – или −), не на упаковке
107252
+ - CTA: на языке ${geo}, хорошо заметная кнопка (позиция НЕ важна: можно вправо/влево/по центру). Главное — CTA читабельна и выглядит как кнопка.
107253
+ - Цена: ОБЯЗАТЕЛЬНО ДВЕ — старая (до скидки) + новая. Если только одна цена — ОШИБКА. Старая зачёркнута ТОЛСТОЙ линией (не тонкой!), новая выразительно. Тонкое/незаметное зачёркивание — ОШИБКА.
107254
+ - Скидка ОБЯЗАТЕЛЬНА: «-50 (возможны варианты символа минуса: -, – или −). Ярко, заметно. Строго НЕ на упаковке/банке.
107174
107255
  - Не считать ошибкой, если скидка/цена конкурируют с CTA по акценту — это допустимо, пока CTA остаётся хорошо заметной и читабельной.
107175
107256
  - Если скидка указана и она не равна -50% — ошибка.
107176
107257
 
@@ -107181,13 +107262,18 @@ ${step3TextLimit}
107181
107262
  ШАГ 6 — КОМПОЗИЦИЯ:
107182
107263
  - Если креатив без человека (lifestyle/clean), проверь наличие контекста использования (кухня, стол, тумбочка, и т.д.). Продукт на полностью пустом/белом фоне без контекста — РЕКОМЕНДАЦИЯ к улучшению (не критичная ошибка, но слабый визуал)
107183
107264
  - КОНТРАСТ ПЛАШЕК: если хотя бы одна текстовая плашка (HOOK, буллеты, CTA или цена) визуально сливается с фоном и текст плохо читается — это ОШИБКА.
107265
+ - ЦЕНА: ОБЯЗАТЕЛЬНО ДВЕ — старая зачёркнута ТОЛСТОЙ линией (не тонкой!), новая выразительно. Одна цена или тонкое зачёркивание — ОШИБКА. Цена не должна выглядеть как буллет.
107266
+ - TRUST‑печати: если печати мелкие или текст нечитабелен на телефоне — ОШИБКА (размер как минимум как у буллитов).
107267
+ - СКИДКА «-50%»: ярко, заметно, на контрастной подложке. Строго НЕ на упаковке/банке. Если скидка на продукте или бледная/незаметна — ОШИБКА.
107268
+ - ЦЕНА: ОБЯЗАТЕЛЬНО ДВЕ — старая зачёркнута ТОЛСТОЙ линией (не тонкой!), новая выразительно. Одна цена или тонкое зачёркивание — ОШИБКА.
107269
+ - Бейджи срочности: «24h» и подобные неясные формулировки — ОШИБКА (непонятно что означает). Ясные («только сегодня», «последние штуки») — ок.
107184
107270
 
107185
107271
  ФИНАЛ:
107186
107272
  Выведи строго:
107187
107273
  СТАТУС: OK / НУЖНА ПЕРЕСБОРКА
107188
107274
  Затем список ошибок, каждая строка начинается с "ОШИБКА:" (кратко, по делу). Если ошибок нет — напиши "ОШИБКА: нет".
107189
107275
  Затем список рекомендаций (если есть), каждая строка начинается с "РЕКОМЕНДАЦИЯ:" (кратко, по делу). Если рекомендаций нет — напиши "РЕКОМЕНДАЦИЯ: нет".
107190
- Не блокируй за мягкие бенефиты, сроки или цифры — только за жёсткие гарантии и медицинские клеймы.`;
107276
+ Не блокируй за клеймы по результату или формулировки обещаний.`;
107191
107277
  }
107192
107278
  /**
107193
107279
  * Базовый промпт для генерации изображений креативов
@@ -107205,10 +107291,12 @@ function getImageGenerationBasePrompt(generateGeo, generatePrice, generateCurren
107205
107291
  }
107206
107292
  _debugLog('⚠️ Using DEFAULT getImageGenerationBasePrompt');
107207
107293
  }
107208
- // Подходы без буллитов — вычисляем динамически из массива
107209
- const noBulletsNames = CREO_APPROACHES.filter(a => a.noBullets).map(a => a.name);
107294
+ // Подходы без буллитов — из подходов с count > 0
107295
+ const counts = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_0__.getImageApproachCounts)();
107296
+ const selectedApproaches = CREO_APPROACHES.filter((_, i) => counts[i] > 0);
107297
+ const noBulletsNames = selectedApproaches.filter(a => a.noBullets).map(a => a.name);
107210
107298
  const noBulletsCond = noBulletsNames.map(n => `"${n}"`).join(' или ');
107211
- const totalApproaches = CREO_APPROACHES.length;
107299
+ const totalApproaches = counts.reduce((a, b) => a + b, 0);
107212
107300
  const ratio = aspectRatio || '1:1';
107213
107301
  const ratioLabel = ratio === '1:1' ? '1:1 (квадрат, 1024×1024 px)' : '2:3 (портрет, 1024×1536 px)';
107214
107302
  const ratioShape = ratio === '1:1' ? 'квадратным' : 'вертикальным 2:3 (портрет)';
@@ -107236,8 +107324,13 @@ CRITICAL RULES (это ВАЛИДАЦИЯ, не рекомендации; есл
107236
107324
  - Цена/скидка = триггер "сейчас"
107237
107325
  - CTA = финальный шаг к действию
107238
107326
 
107327
+ ❗ ЦЕНА (КРИТИЧНО — НЕ ПРОПУСКАЙ):
107328
+ - ОБЯЗАТЕЛЬНО ДВЕ цены: старая (до скидки) + новая (${generatePrice} ${generateCurrency}). Одна цена = ОШИБКА.
107329
+ - Старая = 2× новой (при -50%). Новая — выразительно, крупно. Старая — зачёркнута.
107330
+ - Зачёркивание: ТОЛСТАЯ линия (не тонкая!), контрастная, хорошо видимая. Тонкая/незаметная линия = ОШИБКА.
107331
+
107239
107332
  ЯЗЫК + ТОН:
107240
- - ВСЕ слова строго на языке ${generateGeo} (HOOK/буллеты/CTA/скидка/опциональный бейдж срочности/опциональные trust‑бейджи). Английские слова запрещены. Исключений нет.
107333
+ - ВСЕ слова строго на языке ${generateGeo} (HOOK/буллеты/CTA/скидка/опциональный бейдж срочности/опциональные trust‑печати). Английские слова запрещены. Исключений нет.
107241
107334
  - Тон: убедительный, но честный. Лёгкая эмоция и срочность — ок. Запрещено: ложные обещания, диагностика заболеваний, слова «лечит/вылечивает/гарантируем».
107242
107335
 
107243
107336
  HOOK / HEADLINE (строгое правило):
@@ -107260,12 +107353,13 @@ HOOK / HEADLINE (строгое правило):
107260
107353
  - ТАК (сон): «Álmatlan éjszakák? Pihenj végre»
107261
107354
  ❗ Если заголовок > 5 слов, похож на предложение, распадается на 2 смысловых блока или звучит как лозунг — ОШИБКА → пересоздай вариант.
107262
107355
 
107263
- BULLETS (жёсткое правило):
107356
+ BULLETS (жёсткое правило, ровно 3):
107264
107357
  - По умолчанию: ровно 3 буллета
107265
107358
  - ИСКЛЮЧЕНИЕ: если 🎯 ПОДХОД = ${noBulletsCond} → буллиты ЗАПРЕЩЕНЫ (0 буллитов). Не добавляй буллет‑блок вообще
107266
107359
  - каждый 2–3 слова (макс 4); без запятых; буллет НЕ должен выглядеть как предложение
107267
107360
  - буллеты = свойства ИЛИ ощущаемые преимущества (комфорт, лёгкость, спокойствие). Запрещено: диагнозы, жёсткие медицинские гарантии («лечит», «вылечивает», «избавляет навсегда»). Разрешено: мягкие результаты («Reduce disconfortul», «Mai puține simptome») — примеры формата, адаптируй под категорию, сроки без гарантий («Efect în 7-14 zile», «Rezultat vizibil rapid»), цифры как соц. доказательство («9 din 10 bărbați recomandă»).
107268
107361
  - Делай буллеты более активными и конкретными: глаголы действия + сроки + цифры. Примеры формата: «Reduce X», «Ulga od 1. dnia», «Efekt w 7 dni», «93% potwierdza», «3× szybciej» (адаптируй под язык GEO и категорию)
107362
+ - ВИЗУАЛ БУЛЛЕТОВ: крупные, с иконками/галочками (✓), на контрастных подложках. Буллеты ВНЕ упаковки/банки — не на продукте.
107269
107363
  - Буллиты должны быть расположены ВЕРТИКАЛЬНО (столбиком), не горизонтально в одну строку. Минимальный размер шрифта буллитов — чтобы читались на экране телефона без зума
107270
107364
  - Не повторяй один и тот же набор буллитов во всех подходах: для текущего подхода используй свой набор и акценты. Варианты: свойства (Formulă naturală), бенефиты (Reduce disconfortul), сроки (Efect în 7-14 zile), соц. доказательство (9 din 10 recomandă)
107271
107365
  ПРАВИЛЬНО (пример формата — адаптируй под категорию продукта): «Confort zilnic», «Fără dureri», «Reduce disconfortul», «Mișcare ușoară», «Efect în 2 săptămâni».
@@ -107274,30 +107368,34 @@ BULLETS (жёсткое правило):
107274
107368
 
107275
107369
  TEXT LIMIT:
107276
107370
  - кроме: HOOK, 3 буллетов, цены, скидки, кнопки — ЛЮБОЙ другой текст запрещён (ярлыки/подписи/дисклеймеры/пояснения)
107277
- - ИСКЛЮЧЕНИЕ: если 🎯 ПОДХОД = ${noBulletsCond} → кроме HOOK, цены, скидки, CTA — НИКАКОГО другого текста. Буллиты запрещены. Other_text/urgency/trust-бейджи запрещены
107278
- - ДОПУСКАЕТСЯ (не обязательно) ОДИН небольшой бейдж срочности/дефицита/лимита времени: 1–3 слова ИЛИ число+слово (например «24h»), строго на языке ${generateGeo}, без цены/скидки/процентов, без доменов/ссылок, без диагнозов и жёстких обещаний. Бейдж должен быть в углу кадра (не в центре), контрастный, но заметно меньше CTA и не конкурирует с CTA по вниманию
107279
- - ДОПУСКАЕТСЯ (не обязательно) 1–3 TRUST‑бейджа (печати/стампы доверия) на языке ${generateGeo}: качество/доставка/поддержка/проверено/рекомендуют. Каждый бейдж короткий (1–3 слова), читабельный на телефоне, без английских слов, без цен/скидок/процентов, без доменов/ссылок. Размещай их компактно (обычно в нижней части/по низу), меньше CTA и не конкурируют с CTA. Запрещено: «рекомендовано врачами», «клинически доказано», «одобрено Минздравом» и любые фразы, требующие доказательств
107371
+ - ИСКЛЮЧЕНИЕ: если 🎯 ПОДХОД = ${noBulletsCond} → кроме HOOK, цены, скидки, CTA — НИКАКОГО другого текста. Буллиты запрещены. Other_text/urgency/trust‑печати запрещены
107372
+ - ДОПУСКАЕТСЯ (не обязательно) ОДИН бейдж срочности: только ясные формулировки («только сегодня», «последние штуки», «акция до конца дня»). ЗАПРЕЩЕНО: «24h» непонятно что означает.
107373
+ - ДОПУСКАЕТСЯ (не обязательно) 1–3 trust‑печати: печати цветные, яркие, очень похожие на печать FDA. Подчёркивают: натуральность состава, премиальность, безопасность продукта. Допустимо также: качество, натуральные ингредиенты, экологичность, контроль качества. Короткий текст (1–3 слова) на языке ${generateGeo}, компактно по низу. КРИТИЧНО: печати должны быть КРУПНЫМИ размер как минимум как у буллитов, лучше крупнее. Текст читабелен на телефоне без зума. Даже одна печать — крупная. Мелкие печати — ОШИБКА. Запрещено: «рекомендовано врачами», «клинически доказано», «одобрено Минздравом» и любые фразы, требующие доказательств
107280
107374
  - HOOK + буллеты <= 16 слов суммарно; если больше — ОШИБКА → пересоздай
107281
- - Если хочешь добавить ощущение срочности — делай это через формулировки в HOOK/BULLETS, через реквизит/иконки, ИЛИ (не обязательно) через один короткий текстовый бейдж срочности. Примеры стиля (адаптируй к языку GEO, не обязаны): «Ostatnie sztuki», «Tylko dziś», «Koniec dziś», «Tylko 24h»
107375
+ - Если хочешь добавить ощущение срочности — делай это через формулировки в HOOK/BULLETS, через реквизит/иконки, ИЛИ (не обязательно) через один короткий бейдж срочности. Только ясные формулировки («Ostatnie sztuki», «Tylko dziś», «Koniec dziś»). ЗАПРЕЩЕНО «24h» — непонятно что означает.
107282
107376
 
107283
107377
  CTA > PRICE:
107284
107378
  - кнопка CTA (1–2 слова на языке ${generateGeo}) — главный якорь нижнего блока (самый контрастный элемент внизу)
107285
107379
  - цена и скидка видны, но слабее CTA; скидка «-50%» ОБЯЗАТЕЛЬНА и не должна конкурировать с кнопкой
107380
+ - ЦЕНА: ОБЯЗАТЕЛЬНО ДВЕ — старая зачёркнута ТОЛСТОЙ линией (не тонкой!), новая выразительно. Одна цена или тонкое зачёркивание — ОШИБКА. Цена не должна выглядеть как буллет.
107286
107381
  ❗ Если цена/скидка/бейдж по контрасту или размеру равны или сильнее кнопки — ОШИБКА → ослабь цену/скидку или усили CTA и пересоздай.
107287
107382
  ❗ ИСКЛЮЧЕНИЕ: если 🎯 ПОДХОД = ${noBulletsCond} → цена и «-50%» могут быть более крупными и заметными, но CTA всё равно должен оставаться очень заметным и выглядеть как кнопка (не теряется на фоне)
107288
107383
 
107289
107384
  ПОКАЗЫВАЙ ТОЛЬКО ЭТИ ТЕКСТОВЫЕ ЭЛЕМЕНТЫ:
107290
107385
  - HOOK (1 строка; CAPS; жирный; на яркой контрастной подложке)
107291
- - 3 буллета
107292
- - Цена: ${generatePrice} ${generateCurrency} (обязательно, не на упаковке)
107293
- - Скидка: -50% (обязательно, отдельным бейджем, не на упаковке; визуально слабее CTA)
107386
+ - 3 буллета (крупные, с иконками/галочками ✓, на контрастных подложках, НЕ на банке/упаковке)
107387
+ - Цена: ОБЯЗАТЕЛЬНО ДВЕ — старая (2×${generatePrice} ${generateCurrency}) зачёркнута ТОЛСТОЙ контрастной линией + новая ${generatePrice} ${generateCurrency} выразительно. Одна цена = ОШИБКА. Тонкая линия зачёркивания = ОШИБКА. Без слов. Не на упаковке.
107388
+ - Скидка: «-50 (обязательно, отдельным бейджем; ярко, заметно; строго НЕ на банке/упаковке; визуально слабее CTA)
107294
107389
  - Кнопка CTA (1-2 слова)
107295
- - Опционально: 1 короткий бейдж срочности (1–3 слова ИЛИ число+слово, строго на языке ${generateGeo}; без цены/скидки/процентов). Бейдж в углу кадра (не в центре), контрастный, но меньше CTA. НЕ обязателен
107296
- - Опционально: 1–3 TRUST‑бейджа (печати доверия) на языке ${generateGeo} (качество/доставка/поддержка), короткие и читабельные. НЕ обязательны
107297
- - ИСКЛЮЧЕНИЕ: если 🎯 ПОДХОД = ${noBulletsCond} → показывай только: HOOK + PRICE + DISCOUNT + CTA. Буллиты/urgency/trust запрещены
107390
+ - Опционально: 1 короткий бейдж срочности (ясные формулировки: «только сегодня», «последние штуки»; ЗАПРЕЩЕНО «24h»). Бейдж в углу кадра, контрастный, но меньше CTA. НЕ обязателен
107391
+ - Опционально: 1–3 trust‑печати (цветные, яркие, очень похожие на печать FDA; подчёркивают натуральность, премиальность, безопасность; допустимо: качество, натуральные ингредиенты, экологичность, контроль качества). Короткий текст на языке ${generateGeo}. Размер как минимум как у буллитов, лучше крупнее — читабельны на телефоне без зума. Даже одна печать — крупная. Для подхода «Минимализм» — макс 1 печать. НЕ обязательны
107392
+ - ИСКЛЮЧЕНИЕ: если 🎯 ПОДХОД = ${noBulletsCond} → показывай только: HOOK + PRICE + DISCOUNT + CTA. Буллиты/urgency/trust‑печати запрещены
107298
107393
 
107299
107394
  ВИЗУАЛ:
107300
107395
  - Иерархия: HOOK > продукт > CTA > цена > буллеты
107396
+ - Буллеты: крупные, с иконками/галочками (✓), на контрастных подложках. Строго ВНЕ упаковки/банки.
107397
+ - Цена: ОБЯЗАТЕЛЬНО ДВЕ — старая зачёркнута ТОЛСТОЙ линией (не тонкой!), новая выразительно. Одна цена или тонкое зачёркивание = ОШИБКА. Визуально ОТЛИЧНА от буллитов.
107398
+ - Скидка «-50%»: ярко, заметно, на контрастной подложке. Строго ВНЕ упаковки/банки.
107301
107399
  - Текст на контрастных плашках, читабельно на телефоне. Допускается мультяшная/комиксовая стилистика текста (не обязательно)
107302
107400
  - КОНТРАСТ ПЛАШЕК: каждая текстовая плашка (HOOK, буллеты, CTA, цена) должна иметь высокий контраст с фоном — светлый текст на тёмной подложке ИЛИ тёмный текст на светлой. Плашка, сливающаяся с фоном = ОШИБКА → добавь подложку или измени цвет фона/плашки.
107303
107401
  - АНГЛИЙСКИЙ НА УПАКОВКЕ: если на этикетке продукта есть английский текст (например название/описание на упаковке) — не приближай её настолько, чтобы эти надписи были читабельны как отдельный контент. Упаковка = визуальный объект; мелкий этикеточный текст должен быть нечитабелен или едва различим.
@@ -107311,20 +107409,21 @@ ANTI-TEMPLATE DIVERSITY (КРИТИЧНО):
107311
107409
  - Ты сейчас создаёшь ОДИН креатив для текущего подхода. В проекте будет серия из ${totalApproaches} креативов, поэтому у каждого подхода должна быть своя узнаваемая композиция.
107312
107410
  - Для ЭТОГО креатива НЕ используй универсальный шаблон. Обязательно вариируй: (1) крупность/кроп (close-up vs medium vs flat-lay), (2) угол камеры (прямо vs сверху vs диагональ), (3) расположение блоков HOOK/BULLETS/CTA/PRICE/DISCOUNT, (4) свет/фон (но без лишнего текста).
107313
107411
  - Ориентируйся на строку «🎯 ПОДХОД: ...» и примени соответствующую раскладку ниже (только одну, соответствующую текущему подходу):
107314
- * 🎯 ПОДХОД: Эксперт / Авторитет → БЕЗ человека. Профессиональный контекст с реквизитом экспертизы вокруг продукта. Продукт в центре. HOOK сверху справа, BULLETS справа ниже, CTA снизу справа, PRICE/DISCOUNT рядом с CTA (слабее CTA). Trust‑бейджи (если есть) — компактно по низу.
107315
- * 🎯 ПОДХОД: Lifestyle / Момент приёма → FLAT-LAY/СВЕРХУ или 3/4 сверху на столе/тумбочке. Продукт в центре, реквизит "момент приёма" вокруг. HOOK сверху по центру, BULLETS сбоку (вертикально, шрифт достаточно крупный для чтения на телефоне без зума), CTA снизу по центру, PRICE/DISCOUNT рядом (слабее CTA). Urgency‑бейдж (если есть) — в углу.
107316
- * 🎯 ПОДХОД: Эмоция / Портрет → ЭТО ЕДИНСТВЕННЫЙ ПОДХОД С ЧЕЛОВЕКОМ В СЕРИИ. ОЧЕНЬ крупный портрет лица (thumb‑stop), взгляд в камеру — лицо занимает верхние 2/3 кадра и доминирует. Продукт в руке или у подбородка. HOOK сверху по центру. BULLETS, CTA, PRICE/DISCOUNT — строго в нижней трети, НЕ перекрывают лицо и не конкурируют с ним по размеру.
107317
- * 🎯 ПОДХОД: Визуализация проблемы → Инфографика/схема: слева проблема (иконка/схема зоны тела, релевантной продукту), справа решение + продукт. HOOK сверху по центру, BULLETS справа или снизу (вертикально), CTA снизу справа, PRICE/DISCOUNT возле CTA. Красный акцент только на проблеме.
107318
- * 🎯 ПОДХОД: Power / Сила решения → БЕЗ человека. ДИНАМИЧНЫЙ комикс‑кадр, диагональная композиция. Продукт как “герой” + power‑иконки (щит/молния/бёрст). HOOK сверху слева на яркой плашке, BULLETS слева ниже, CTA снизу справа, PRICE/DISCOUNT возле CTA.
107319
- * 🎯 ПОДХОД: Минимализм / Clean Big Text → Белый/градиентный фон, минимум элементов. ОГРОМНЫЙ HOOK занимает верх/центр, продукт крупно (центр/право), CTA снизу по центру, PRICE/DISCOUNT рядом (слабее CTA). Trust‑бейджи (если есть) — очень маленькие и аккуратные.
107412
+ * 🎯 ПОДХОД: Эксперт / Авторитет → БЕЗ человека. Профессиональный контекст с реквизитом экспертизы вокруг продукта. Продукт в центре. HOOK сверху справа, BULLETS справа ниже, CTA снизу справа, PRICE/DISCOUNT рядом с CTA (слабее CTA). Trust‑печати (если есть) — компактно по низу, размер как минимум как у буллитов (крупные, читабельны на телефоне).
107413
+ * 🎯 ПОДХОД: Lifestyle / Момент приёма → FLAT-LAY/СВЕРХУ или 3/4 сверху на столе/тумбочке. Продукт в центре, реквизит "момент приёма" вокруг. HOOK сверху по центру, BULLETS сбоку (вертикально, шрифт достаточно крупный для чтения на телефоне без зума), CTA снизу по центру, PRICE/DISCOUNT рядом (слабее CTA). Urgency‑бейдж (если есть) — в углу, только ясные формулировки, НЕ «24h». Trust‑печати (если есть) — компактно по низу, размер как минимум как у буллитов (крупные, читабельны на телефоне).
107414
+ * 🎯 ПОДХОД: Эмоция / Портрет → ЭТО ЕДИНСТВЕННЫЙ ПОДХОД С ЧЕЛОВЕКОМ В СЕРИИ. ОЧЕНЬ крупный портрет лица (thumb‑stop), взгляд в камеру — лицо занимает верхние 2/3 кадра и доминирует. Продукт в руке или у подбородка. HOOK сверху по центру. BULLETS, CTA, PRICE/DISCOUNT, trust‑печати — строго в нижней трети, НЕ перекрывают лицо и не конкурируют с ним по размеру.
107415
+ * 🎯 ПОДХОД: Визуализация проблемы → Инфографика/схема: слева проблема (иконка/схема зоны тела, релевантной продукту), справа решение + продукт. HOOK сверху по центру, BULLETS справа или снизу (вертикально), CTA снизу справа, PRICE/DISCOUNT возле CTA. Trust‑печати (если есть) — компактно по низу, размер как минимум как у буллитов (крупные, читабельны на телефоне). Красный акцент только на проблеме.
107416
+ * 🎯 ПОДХОД: Power / Сила решения → БЕЗ человека. ДИНАМИЧНЫЙ комикс‑кадр, диагональная композиция. Продукт как “герой” + power‑иконки (щит/молния/бёрст). HOOK сверху слева на яркой плашке, BULLETS слева ниже, CTA снизу справа, PRICE/DISCOUNT возле CTA. Trust‑печати (если есть) — компактно по низу, размер как минимум как у буллитов (крупные, читабельны на телефоне).
107417
+ * 🎯 ПОДХОД: Минимализм / Clean Big Text → Белый/градиентный фон, минимум элементов. ОГРОМНЫЙ HOOK занимает верх/центр, продукт крупно (центр/право), CTA снизу по центру, PRICE/DISCOUNT рядом (слабее CTA). Trust‑печати (если есть) — размер как минимум как у буллитов, крупные и читабельные.
107320
107418
  * 🎯 ПОДХОД: Problem Visual Punch → БЕЗ человека. Яркий сплошной фон (красный/оранжевый) или агрессивный градиент. В центре КРУПНАЯ иконка/схема зоны тела, релевантной продукту (для суставов — колено; для пищеварения — желудок), с красным свечением. Продукт крупно рядом. HOOK 3–4 слова МАКС, огромный CAPS. БЕЗ буллитов. PRICE + «-50%» крупно и заметно. CTA контрастной кнопкой
107321
107419
  * 🎯 ПОДХОД: Любительский Примитивизм → БЕЗ человека. Чёрный или кислотный сплошной фон. Продукт по центру или справа без декора. HOOK сверху — крупный, грубый bold/рукописный, CAPS, 3–4 слова. Крупные надписи цены/скидки вокруг продукта. CTA — плоская кнопка без градиентов. БЕЗ буллитов, бейджей, теней, иконок.
107322
107420
 
107323
107421
  AUTO-CHECK (перед финалом):
107324
107422
  - HOOK: 3–5 слов, 1 строка, CAPS, жирный на контрастной плашке, боль + направление облегчения (не лозунг), язык ${generateGeo} без английских слов
107325
107423
  - Буллеты: по умолчанию ровно 3, каждый <= 4 слов, без запятых, не предложения. ИСКЛЮЧЕНИЕ: если 🎯 ПОДХОД = ${noBulletsCond} → буллетов 0 (запрещены)
107326
- - Нет лишнего текста кроме: HOOK, буллетов, цены, скидки, кнопки (+ опционально 1 короткий бейдж срочности + опционально 1–3 trust‑бейджа)
107424
+ - Нет лишнего текста кроме: HOOK, буллетов, цены, скидки, кнопки (+ опционально 1 бейдж срочности + опционально 1–3 trust‑печати)
107327
107425
  - Есть скидка «-50%» (вне упаковки) и она слабее CTA
107426
+ - Цена: ОБЯЗАТЕЛЬНО ДВЕ (старая + новая). Старая зачёркнута ТОЛСТОЙ контрастной линией. Одна цена = ОШИБКА. Тонкая/незаметная линия зачёркивания = ОШИБКА
107328
107427
  - CTA визуально сильнее цены/скидки
107329
107428
  - Контраст: каждая текстовая плашка читабельна на телефоне — ни одна не сливается с фоном
107330
107429
  Если хоть один пункт нарушен — пересоздай креатив.
@@ -107332,57 +107431,68 @@ AUTO-CHECK (перед финалом):
107332
107431
  ВАЖНО: Твой ответ — это ИЗОБРАЖЕНИЕ, не текст. Не пиши план, не перечисляй элементы текстом, не рассуждай, не вызывай tools. Сразу генерируй визуальный креатив как картинку.`;
107333
107432
  }
107334
107433
  /**
107335
- * Получить подходы с учетом оверрайдов
107434
+ * Получить подходы с учетом количества и оверрайдов.
107435
+ * Каждый подход повторяется N раз (0–4), где N = imageApproachCounts[i].
107336
107436
  */
107337
107437
  function getCreoApproaches() {
107338
- return CREO_APPROACHES.map(approach => {
107438
+ const counts = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_0__.getImageApproachCounts)();
107439
+ const result = [];
107440
+ for (let i = 0; i < CREO_APPROACHES.length && i < counts.length; i++) {
107441
+ const approach = CREO_APPROACHES[i];
107442
+ if (!approach)
107443
+ continue;
107339
107444
  const override = (0,_promptOverrides__WEBPACK_IMPORTED_MODULE_0__.getCreoApproachOverride)(approach.name);
107340
- if (override?.enabled) {
107341
- _debugLog(`✅ Using CUSTOM approach: ${approach.name}`, `hasPrompt=${!!override.customPrompt}`, `hasHeadline=${!!override.customHeadlineAngle}`, `hasBullets=${!!override.customBulletsFocus}`);
107342
- return {
107445
+ const resolved = override?.enabled
107446
+ ? {
107343
107447
  ...approach,
107344
107448
  prompt: override.customPrompt || approach.prompt,
107345
107449
  headlineAngle: override.customHeadlineAngle || approach.headlineAngle,
107346
107450
  bulletsFocus: override.customBulletsFocus || approach.bulletsFocus
107347
- };
107451
+ }
107452
+ : approach;
107453
+ if (override?.enabled) {
107454
+ _debugLog(`✅ Using CUSTOM approach: ${approach.name}`, `hasPrompt=${!!override.customPrompt}`, `hasHeadline=${!!override.customHeadlineAngle}`, `hasBullets=${!!override.customBulletsFocus}`);
107348
107455
  }
107349
- return approach;
107350
- });
107456
+ for (let k = 0; k < counts[i]; k++) {
107457
+ result.push(resolved);
107458
+ }
107459
+ }
107460
+ return result;
107351
107461
  }
107352
107462
  const CREO_APPROACHES = [
107353
107463
  {
107354
107464
  name: 'Эксперт / Авторитет',
107355
- prompt: `ЭКСПЕРТ / АВТОРИТЕТ (без человека): стиль “рекомендация эксперта”, но БЕЗ человека. Профессиональный контекст — аптечные полки на фоне / медицинский планшет / стетоскоп / рецептурный блокнот как реквизит рядом с продуктом. Продукт в центре как “рекомендованное решение”. Чистый профессиональный фон, высокий контраст. Trust‑бейджи: выбери 1–3 варианта из (качество/проверено/рекомендуют/доставка/поддержка), маленькие и аккуратныеусиливают авторитет.`,
107465
+ prompt: `ЭКСПЕРТ / АВТОРИТЕТ (без человека): стиль “рекомендация эксперта”, но БЕЗ человека. Профессиональный контекст — аптечные полки на фоне / медицинский планшет / стетоскоп / рецептурный блокнот как реквизит рядом с продуктом. Продукт в центре как “рекомендованное решение”. Чистый профессиональный фон, высокий контраст. Trust‑печати (1–3 шт): цветные, яркие, очень похожие на печать FDA; подчёркивают натуральность состава, премиальность, безопасность. Размер как минимум как у буллитов крупные, читабельны на телефоне. Даже одна печать — крупная.`,
107356
107466
  headlineAngle: `HOOK: угол «предупреждение / интрига / специалисты знают». Формат: вопрос‑предупреждение ИЛИ “специалисты знают/используют” (адаптируй под GEO). 3–5 слов, 1 строка, CAPS.`,
107357
107467
  bulletsFocus: `БУЛЛИТЫ: “доверие + действие + конкретика”. Минимум 1 буллет с действием на главную боль (глагол), минимум 1 со сроком/скоростью, плюс 1 с цифрой/соц.доказательством (если уместно). Всё строго на языке GEO и релевантно категории. ЗАПРЕЩЕНО: абстрактные буллиты без действия/результата/срока/цифры (например «Naturalna formuła» сама по себе).`
107358
107468
  },
107359
107469
  {
107360
107470
  name: 'Lifestyle / Момент приёма',
107361
- prompt: `LIFESTYLE / МОМЕНТ ПРИЁМА: продукт в контексте использования (кухня/стол/спальня/тумбочка). Без человека, но с "историей" (стакан воды, чай, тарелка, будильник/часы/календарь). Срочность: реквизит (часы/таймер‑иконка) и/или опциональный urgency‑бейдж (1–3 слова) в углу кадра, контрастный, но меньше CTA. Высокий контраст, яркий акцент на продукте. Опционально: 1–3 trust‑бейджа по низу. ВАЖНО: буллиты располагаются вертикально и всегда читабельны на экране телефона без зума — даже если уходят вбок, минимальный размер шрифта буллитов не уменьшается. Если буллиты не вмещаются сбоку с нужным шрифтом — переставь их вниз.`,
107471
+ prompt: `LIFESTYLE / МОМЕНТ ПРИЁМА: продукт в контексте использования (кухня/стол/спальня/тумбочка). Без человека, но с "историей" (стакан воды, чай, тарелка, будильник/часы/календарь). Срочность: реквизит (часы/таймер‑иконка) и/или опциональный urgency‑бейдж (1–3 слова) в углу кадра только ясные формулировки («только сегодня», «последние штуки»), ЗАПРЕЩЕНО «24h». Высокий контраст, яркий акцент на продукте. Опционально: 1–3 trust‑печати по низу (цветные, яркие, очень похожие на печать FDA; натуральность/премиальность/безопасность). Размер как минимум как у буллитов — крупные, читабельны на телефоне. Даже одна печать — крупная. ВАЖНО: буллиты располагаются вертикально и всегда читабельны на экране телефона без зума — даже если уходят вбок, минимальный размер шрифта буллитов не уменьшается. Если буллиты не вмещаются сбоку с нужным шрифтом — переставь их вниз.`,
107362
107472
  headlineAngle: `HOOK: угол «цифры + срочность». Формат: число/процент + ограничение времени/“сейчас/сегодня” (адаптируй под GEO), 3–5 слов, 1 строка, CAPS.`,
107363
107473
  bulletsFocus: `БУЛЛИТЫ: активные глаголы + сроки + цифры. Минимум 1 буллет со сроком/скоростью, минимум 1 с цифрой/соц.доказательством. Всё 2–4 слова, без запятых, строго на языке GEO и релевантно категории. ЗАПРЕЩЕНО: абстрактные буллиты без действия/результата/срока/цифры.`
107364
107474
  },
107365
107475
  {
107366
107476
  name: 'Эмоция / Портрет',
107367
- prompt: `ЭМОЦИЯ / ПОРТРЕТ: ЭТО ЕДИНСТВЕННЫЙ ПОДХОД С ЧЕЛОВЕКОМ В СЕРИИ. Используй максимально: ОЧЕНЬ крупный план лица (thumb‑stop), прямой взгляд в камеру, сильная эмоция облегчения/надежды (без широкой стоковой улыбки). Возраст человека подбери под категорию и ЦА продукта: по умолчанию 50–60, но для категорий с более молодой аудиторией (например похудение/фитнес) допускается 35–55. Продукт в руке на уровне лица или рядом, хорошо виден. Свет "тень → свет" на лице допустим. Минимум отвлекающих деталей, высокий контраст. Опционально: 1–3 trust‑бейджа по низу. ИЕРАРХИЯ: лицо — главный и доминирующий визуальный элемент (верхние 2/3 кадра). Все текстовые блоки (HOOK исключение — сверху) уходят в нижнюю треть. Буллиты, цена, CTA НЕ перекрывают лицо и НЕ конкурируют с ним по визуальному весу — они заметно меньше и ниже.`,
107477
+ prompt: `ЭМОЦИЯ / ПОРТРЕТ: ЭТО ЕДИНСТВЕННЫЙ ПОДХОД С ЧЕЛОВЕКОМ В СЕРИИ. Используй максимально: ОЧЕНЬ крупный план лица (thumb‑stop), прямой взгляд в камеру, сильная эмоция облегчения/надежды (без широкой стоковой улыбки). Возраст человека подбери под категорию и ЦА продукта: по умолчанию 50–60, но для категорий с более молодой аудиторией (например похудение/фитнес) допускается 35–55. Продукт в руке на уровне лица или рядом, хорошо виден. Свет "тень → свет" на лице допустим. Минимум отвлекающих деталей, высокий контраст. Опционально: 1–3 trust‑печати по низу (цветные, яркие, очень похожие на печать FDA; натуральность/премиальность/безопасность). Размер как минимум как у буллитов — крупные, читабельны на телефоне. Даже одна печать — крупная. ИЕРАРХИЯ: лицо — главный и доминирующий визуальный элемент (верхние 2/3 кадра). Все текстовые блоки (HOOK исключение — сверху) уходят в нижнюю треть. Буллиты, цена, CTA НЕ перекрывают лицо и НЕ конкурируют с ним по визуальному весу — они заметно меньше и ниже.`,
107368
107478
  headlineAngle: `HOOK: угол «персонально на “ты” + результат/облегчение». 3–5 слов, 1 строка, CAPS. Тон максимально личный и прямой.`,
107369
107479
  bulletsFocus: `БУЛЛИТЫ: про ощущение и качество жизни, но в формате действий/результата + срок/скорость + (если уместно) цифра. Всё строго на GEO. ЗАПРЕЩЕНО: абстрактные буллиты без действия/результата/срока/цифры.`
107370
107480
  },
107371
107481
  {
107372
107482
  name: 'Визуализация проблемы',
107373
- prompt: `ВИЗУАЛИЗАЦИЯ ПРОБЛЕМЫ (без человека): схема/иконка проблемной зоны тела, строго соответствующей категории продукта (для суставов — колено/сустав/позвоночник; для пищеварения — желудок; для простаты — силуэт мужчины) + мягкий красный акцент на этой зоне (не шок‑контент, без графики). Рядом продукт как решение. Можно добавить простую стрелку/переход “проблема → облегчение” как графику (без лишнего текста). Чистый фон, высокая читабельность. Опционально: 1–3 trust‑бейджа по низу. БЕЗ человека.`,
107483
+ prompt: `ВИЗУАЛИЗАЦИЯ ПРОБЛЕМЫ (без человека): схема/иконка проблемной зоны тела, строго соответствующей категории продукта (для суставов — колено/сустав/позвоночник; для пищеварения — желудок; для простаты — силуэт мужчины) + мягкий красный акцент на этой зоне (не шок‑контент, без графики). Рядом продукт как решение. Можно добавить простую стрелку/переход “проблема → облегчение” как графику (без лишнего текста). Чистый фон, высокая читабельность. Опционально: 1–3 trust‑печати по низу (цветные, яркие, очень похожие на печать FDA; натуральность/премиальность/безопасность). Размер как минимум как у буллитов — крупные, читабельны на телефоне. Даже одна печать — крупная. БЕЗ человека.`,
107374
107484
  headlineAngle: `HOOK: прямой вопрос о боли/дискомфорте (релевантно категории). 3–5 слов, 1 строка, CAPS, вопросительный знак допустим.`,
107375
107485
  bulletsFocus: `БУЛЛИТЫ: механика/результат в активной форме + срок/скорость + конкретика. Без диагнозов и гарантий. Всё строго на GEO. ЗАПРЕЩЕНО: абстрактные буллиты без действия/результата/срока/цифры.`
107376
107486
  },
107377
107487
  {
107378
107488
  name: 'Power / Сила решения',
107379
- prompt: `POWER / СИЛА РЕШЕНИЯ (без человека): метафоры силы и победы над проблемой: щит, молния, энергия, взрыв‑бёрст, мощные стикеры/иконки. Продукт как “герой” в центре, высокая энергия, контрастные агрессивные цвета. Можно комикс/anti‑design подачу, но всё должно быть читабельно. Без лишнего текста. Опционально: 1–3 trust‑бейджа по низу. БЕЗ человека. Только продукт + power‑иконки.`,
107489
+ prompt: `POWER / СИЛА РЕШЕНИЯ (без человека): метафоры силы и победы над проблемой: щит, молния, энергия, взрыв‑бёрст, мощные стикеры/иконки. Продукт как “герой” в центре, высокая энергия, контрастные агрессивные цвета. Можно комикс/anti‑design подачу, но всё должно быть читабельно. Без лишнего текста. Опционально: 1–3 trust‑печати по низу (цветные, яркие, очень похожие на печать FDA; натуральность/премиальность/безопасность). Размер как минимум как у буллитов — крупные, читабельны на телефоне. Даже одна печать — крупная. БЕЗ человека. Только продукт + power‑иконки.`,
107380
107490
  headlineAngle: `HOOK: угол «было → стало / победа над проблемой». Можно использовать стрелку “→” как часть перехода. 3–5 слов, 1 строка, CAPS.`,
107381
107491
  bulletsFocus: `БУЛЛИТЫ: активные глаголы + скорость/срок + конкретика + (если уместно) цифра. Без гарантий. Всё строго на GEO. ЗАПРЕЩЕНО: абстрактные буллиты без действия/результата/срока/цифры.`
107382
107492
  },
107383
107493
  {
107384
107494
  name: 'Минимализм / Clean Big Text',
107385
- prompt: `МИНИМАЛИЗМ / CLEAN BIG TEXT (без человека): белый или мягкий градиентный фон, премиальное ощущение. ОГРОМНЫЙ HOOK как главный элемент (CAPS, жирный, на контрастной плашке). Продукт крупно, минимум реквизита, минимум шума. Тени/премиальные материалы допустимы, но без лишнего текста. БЕЗ человека. Urgency и trust‑бейджи в этом подходе НЕ рекомендуются — они нарушают чистоту и ощущение премиальности. Добавляй бейджи только если это критически необходимо, не более 1 и только самый маленький.`,
107495
+ prompt: `МИНИМАЛИЗМ / CLEAN BIG TEXT (без человека): белый или мягкий градиентный фон, премиальное ощущение. ОГРОМНЫЙ HOOK как главный элемент (CAPS, жирный, на контрастной плашке). Продукт крупно, минимум реквизита, минимум шума. Тени/премиальные материалы допустимы, но без лишнего текста. БЕЗ человека. Urgency и trust‑печати в этом подходе НЕ рекомендуются — они нарушают чистоту и ощущение премиальности. Добавляй печати только если это критически необходимо, не более 1, но крупную размер как у буллитов.`,
107386
107496
  headlineAngle: `HOOK: одна максимально простая сильная фраза (коротко и ясно), 3–5 слов, 1 строка, CAPS.`,
107387
107497
  bulletsFocus: `БУЛЛИТЫ: максимально коротко, но конкретно: действие + срок/скорость + цифра/доказательство (если уместно). Никаких абстрактных буллитов без конкретики. Всё строго на GEO.`
107388
107498
  },
@@ -107399,7 +107509,7 @@ const CREO_APPROACHES = [
107399
107509
  - CTA: контрастная яркая кнопка
107400
107510
 
107401
107511
  Цель: считывание за 1 секунду. Минимум текста, максимум визуального удара.
107402
- БЕЗ человека. Никаких trust/urgency бейджей — только HOOK + PRICE + -50% + CTA.`,
107512
+ БЕЗ человека. Никаких trust‑печатей/urgency бейджей — только HOOK + PRICE + -50% + CTA.`,
107403
107513
  headlineAngle: `HOOK: «ПРОЩАЙ/КОНЕЦ/СТОП + [проблема]!». Эмоция победы/избавления. 3–4 слова, CAPS.`,
107404
107514
  bulletsFocus: `БУЛЛИТЫ: ЗАПРЕЩЕНЫ. Вся информация — в HOOK и крупных надписях.`
107405
107515
  },
@@ -107414,7 +107524,7 @@ const CREO_APPROACHES = [
107414
107524
  - Элементы выглядят «вырезанными и приклеенными»: неровные края, отсутствие выравнивания по сетке
107415
107525
  - Крупные текстовые надписи рядом с продуктом: цена, скидка; допустимо одно короткое слово-восклицание («РАБОТАЕТ!», «ПРОВЕРЕНО!») на языке GEO
107416
107526
  - Кнопка CTA: плоская, без градиентов, контрастный сплошной цвет
107417
- - НИКАКИХ буллитов, печатей, trust-бейджей, urgency-бейджей, иконок — только продукт и текст`,
107527
+ - НИКАКИХ буллитов, trust‑печатей, urgency‑бейджей, иконок — только продукт и текст`,
107418
107528
  headlineAngle: `HOOK: максимально прямолинейный — короткий, грубый, без украшений. 3–4 слова, CAPS, как объявление на столбе. Никакого маркетингового лоска, никаких абстрактных слоганов.`,
107419
107529
  bulletsFocus: `БУЛЛИТЫ: ЗАПРЕЩЕНЫ. Всё — в HOOK и крупных надписях вокруг продукта (цена, скидка, одно слово-восклицание).`
107420
107530
  }