docs-combiner 0.1.18 → 0.1.19

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
@@ -101114,8 +101114,8 @@ __webpack_require__.r(__webpack_exports__);
101114
101114
  /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/CircularProgress/CircularProgress.js");
101115
101115
  /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Alert/Alert.js");
101116
101116
  /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Divider/Divider.js");
101117
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Button/Button.js");
101118
- /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/InputAdornment/InputAdornment.js");
101117
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/InputAdornment/InputAdornment.js");
101118
+ /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Button/Button.js");
101119
101119
  /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/FormControlLabel/FormControlLabel.js");
101120
101120
  /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/Checkbox/Checkbox.js");
101121
101121
  /* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! @mui/material */ "./node_modules/@mui/material/esm/FormControl/FormControl.js");
@@ -101157,9 +101157,11 @@ __webpack_require__.r(__webpack_exports__);
101157
101157
  /* harmony import */ var _PromptManagerDialog__WEBPACK_IMPORTED_MODULE_62__ = __webpack_require__(/*! ./PromptManagerDialog */ "./src/PromptManagerDialog.tsx");
101158
101158
  /* harmony import */ var _promptOverrides__WEBPACK_IMPORTED_MODULE_63__ = __webpack_require__(/*! ./promptOverrides */ "./src/promptOverrides.ts");
101159
101159
  /* harmony import */ var _appSettingsBundle__WEBPACK_IMPORTED_MODULE_64__ = __webpack_require__(/*! ./appSettingsBundle */ "./src/appSettingsBundle.ts");
101160
- /* harmony import */ var xlsx__WEBPACK_IMPORTED_MODULE_65__ = __webpack_require__(/*! xlsx */ "./node_modules/xlsx/xlsx.mjs");
101161
- /* harmony import */ var jszip__WEBPACK_IMPORTED_MODULE_66__ = __webpack_require__(/*! jszip */ "./node_modules/jszip/dist/jszip.min.js");
101162
- /* harmony import */ var jszip__WEBPACK_IMPORTED_MODULE_66___default = /*#__PURE__*/__webpack_require__.n(jszip__WEBPACK_IMPORTED_MODULE_66__);
101160
+ /* harmony import */ var _flexcardBalance__WEBPACK_IMPORTED_MODULE_65__ = __webpack_require__(/*! ./flexcardBalance */ "./src/flexcardBalance.ts");
101161
+ /* harmony import */ var xlsx__WEBPACK_IMPORTED_MODULE_66__ = __webpack_require__(/*! xlsx */ "./node_modules/xlsx/xlsx.mjs");
101162
+ /* harmony import */ var jszip__WEBPACK_IMPORTED_MODULE_67__ = __webpack_require__(/*! jszip */ "./node_modules/jszip/dist/jszip.min.js");
101163
+ /* harmony import */ var jszip__WEBPACK_IMPORTED_MODULE_67___default = /*#__PURE__*/__webpack_require__.n(jszip__WEBPACK_IMPORTED_MODULE_67__);
101164
+
101163
101165
 
101164
101166
 
101165
101167
 
@@ -101313,19 +101315,51 @@ function extractChatCompletionText(choice) {
101313
101315
  if (!msg)
101314
101316
  return '';
101315
101317
  const c = msg.content;
101316
- if (typeof c === 'string')
101317
- return c;
101318
- if (Array.isArray(c)) {
101319
- return c
101318
+ let fromContent = '';
101319
+ if (typeof c === 'string') {
101320
+ fromContent = c;
101321
+ }
101322
+ else if (Array.isArray(c)) {
101323
+ fromContent = c
101320
101324
  .map((part) => (typeof part === 'string' ? part : part?.text ?? part?.content ?? ''))
101321
101325
  .filter(Boolean)
101322
101326
  .join('');
101323
101327
  }
101324
- if (c != null && typeof c === 'object' && typeof c.text === 'string') {
101325
- return c.text;
101328
+ else if (c != null && typeof c === 'object' && typeof c.text === 'string') {
101329
+ fromContent = c.text;
101330
+ }
101331
+ if (fromContent.trim())
101332
+ return fromContent;
101333
+ // Часть моделей (reasoning) кладёт текст в reasoning/refusal — иначе «Полный ответ» пустой.
101334
+ const reasoning = msg.reasoning;
101335
+ if (typeof reasoning === 'string' && reasoning.trim())
101336
+ return reasoning;
101337
+ if (Array.isArray(reasoning)) {
101338
+ const r = reasoning
101339
+ .map((p) => (typeof p === 'string' ? p : p?.text ?? ''))
101340
+ .filter(Boolean)
101341
+ .join('\n');
101342
+ if (r.trim())
101343
+ return r;
101326
101344
  }
101345
+ const refusal = msg.refusal;
101346
+ if (typeof refusal === 'string' && refusal.trim())
101347
+ return refusal;
101327
101348
  return '';
101328
101349
  }
101350
+ /** Сырой JSON, если в message не извлекли ни одной буквы — иначе «Полный ответ» = пусто и UI показывает лишь подсказку из checkErrors. */
101351
+ const VALIDATOR_RESULT_RAW_JSON_MAX = 48_000;
101352
+ function buildValidatorStoredResultText(content, data) {
101353
+ if (content.trim().length > 0) {
101354
+ return content;
101355
+ }
101356
+ const raw = JSON.stringify(data ?? {}, null, 2);
101357
+ const pre = 'Текст ответа модели не извлечён из message.content (проверьте reasoning/формат в JSON ниже). Ответ API:\n\n';
101358
+ if (raw.length <= VALIDATOR_RESULT_RAW_JSON_MAX) {
101359
+ return pre + raw;
101360
+ }
101361
+ return `${pre}${raw.slice(0, VALIDATOR_RESULT_RAW_JSON_MAX)}…\n[обрезано по ${VALIDATOR_RESULT_RAW_JSON_MAX} симв.]`;
101362
+ }
101329
101363
  /** Обрыв ответа по лимиту токенов (OpenRouter / OpenAI-совместимый completion). */
101330
101364
  function isCompletionTruncatedByTokenLimit(choice) {
101331
101365
  if (!choice)
@@ -101426,7 +101460,8 @@ function isValidationNegationLine(s) {
101426
101460
  const t = normalizeValidationErrorText(s).toLowerCase();
101427
101461
  if (!t)
101428
101462
  return true;
101429
- if (t === 'нет')
101463
+ const tBare = t.replace(/\.+$/g, '').trim();
101464
+ if (tBare === 'нет')
101430
101465
  return true;
101431
101466
  if (t.includes('нет ошибок') || t.includes('ошибок нет'))
101432
101467
  return true;
@@ -101513,6 +101548,17 @@ function extractLeadingPriceNumber(value) {
101513
101548
  return m[1].replace(',', '.');
101514
101549
  return '99';
101515
101550
  }
101551
+ /** Крео подходит под «Загрузить правильные» — как зелёная подпись «Проверка пройдена»: статус ok и реальная проверка (не «отключена»). Список checkErrors при ok не используем: парсер иногда оставляет ложные строки вроде «нет.». */
101552
+ function isCreoPassedValidatorForDriveUpload(img) {
101553
+ return img.checkStatus === 'ok' && img.checkResult !== 'Проверка отключена';
101554
+ }
101555
+ /** Автозагрузка на Drive только после реального OK валидатора (не при отключённой проверке). */
101556
+ function shouldUploadCreoAfterValidatorOk(uploadAfterValidatorOk, validationDisabled, validationResult) {
101557
+ return (uploadAfterValidatorOk &&
101558
+ !validationDisabled &&
101559
+ validationResult.status === 'ok' &&
101560
+ validationResult.result !== 'Проверка отключена');
101561
+ }
101516
101562
  /** Фон карточки крео: после 1-й переделки — светло-жёлтый, далее темнеет с каждой следующей. */
101517
101563
  function getRemakeHighlightBackground(remakeCount, dark) {
101518
101564
  if (remakeCount <= 0)
@@ -101616,6 +101662,13 @@ function App() {
101616
101662
  const [openRouterAccountBalance, setOpenRouterAccountBalance] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
101617
101663
  const [openRouterBalanceLoading, setOpenRouterBalanceLoading] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
101618
101664
  const fetchBalanceRef = react__WEBPACK_IMPORTED_MODULE_0___default().useRef(null);
101665
+ const [flexcardApiKey, setFlexcardApiKey] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
101666
+ /** Сумма USD по всем строкам `GET /api/v1/finance/accounts/`; null — ошибка / нет данных */
101667
+ const [flexCardUsdTotal, setFlexCardUsdTotal] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
101668
+ const [flexCardBalanceLoading, setFlexCardBalanceLoading] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
101669
+ const [telegramBotToken, setTelegramBotToken] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
101670
+ const [telegramChatId, setTelegramChatId] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
101671
+ const [telegramTestSending, setTelegramTestSending] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
101619
101672
  const [generateProduct, setGenerateProduct] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
101620
101673
  const [generateGeo, setGenerateGeo] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
101621
101674
  const [generateAdditionalInfo, setGenerateAdditionalInfo] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)('');
@@ -101678,6 +101731,14 @@ function App() {
101678
101731
  return false;
101679
101732
  }
101680
101733
  });
101734
+ const [uploadAfterValidatorOk, setUploadAfterValidatorOk] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(() => {
101735
+ try {
101736
+ return localStorage.getItem('uploadAfterValidatorOk') === 'true';
101737
+ }
101738
+ catch {
101739
+ return false;
101740
+ }
101741
+ });
101681
101742
  const [imageAspectRatio, setImageAspectRatio] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(() => {
101682
101743
  const saved = localStorage.getItem('imageAspectRatio');
101683
101744
  // Migrate legacy values (4:5, 9:16) to 2:3
@@ -101737,6 +101798,7 @@ function App() {
101737
101798
  'rub': 'RUB',
101738
101799
  'forint': 'HUF',
101739
101800
  'forints': 'HUF',
101801
+ 'ft': 'HUF', // Hungarian forint (e.g. "11 400 Ft")
101740
101802
  'huf': 'HUF',
101741
101803
  'zloty': 'PLN',
101742
101804
  'zlotys': 'PLN',
@@ -101751,6 +101813,7 @@ function App() {
101751
101813
  'krone': 'DKK',
101752
101814
  'kroner': 'DKK',
101753
101815
  'dkk': 'DKK',
101816
+ 'kč': 'CZK',
101754
101817
  'koruna': 'CZK',
101755
101818
  'koruny': 'CZK',
101756
101819
  'czk': 'CZK',
@@ -101876,6 +101939,14 @@ function App() {
101876
101939
  const [generatingImages, setGeneratingImages] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
101877
101940
  const [imagesGenerationLogs, setImagesGenerationLogs] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)([]);
101878
101941
  const [generatedImagesData, setGeneratedImagesData] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)([]);
101942
+ const { pendingDriveUploadCreos, pendingValidatorOkDriveUploadCreos } = react__WEBPACK_IMPORTED_MODULE_0___default().useMemo(() => {
101943
+ const pending = generatedImagesData.filter(img => !img.uploaded && img.imageUrl);
101944
+ return {
101945
+ pendingDriveUploadCreos: pending,
101946
+ pendingValidatorOkDriveUploadCreos: pending.filter(isCreoPassedValidatorForDriveUpload)
101947
+ };
101948
+ }, [generatedImagesData]);
101949
+ const creoAutoUploadAfterValidatorOkRef = react__WEBPACK_IMPORTED_MODULE_0___default().useRef(async () => { });
101879
101950
  const [checkingImages, setCheckingImages] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
101880
101951
  const [uploadingImages, setUploadingImages] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
101881
101952
  /** Индекс крео, для которого идёт удаление файла с Drive (кнопка «Удалить с Диска»). */
@@ -102098,6 +102169,13 @@ function App() {
102098
102169
  setOpenaiApiKey(config.openaiApiKey);
102099
102170
  // Balance will be fetched automatically by useEffect when openaiApiKey changes
102100
102171
  }
102172
+ if (config.flexcardApiKey) {
102173
+ setFlexcardApiKey(String(config.flexcardApiKey));
102174
+ }
102175
+ if (config.telegramBotToken)
102176
+ setTelegramBotToken(String(config.telegramBotToken));
102177
+ if (config.telegramChatId)
102178
+ setTelegramChatId(String(config.telegramChatId));
102101
102179
  if (config.secretUnlockPassed === true) {
102102
102180
  setUnlocked(true);
102103
102181
  try {
@@ -102413,6 +102491,25 @@ function App() {
102413
102491
  return () => clearInterval(intervalId);
102414
102492
  // eslint-disable-next-line react-hooks/exhaustive-deps
102415
102493
  }, [openaiApiKey]);
102494
+ // FlexCard: баланс при ключе, каждые 10 минут и при открытии диалога резервной копии
102495
+ (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
102496
+ if (!flexcardApiKey.trim()) {
102497
+ setFlexCardUsdTotal(null);
102498
+ return;
102499
+ }
102500
+ void fetchFlexCardBalance();
102501
+ const intervalId = setInterval(() => {
102502
+ void fetchFlexCardBalance();
102503
+ }, 600000);
102504
+ return () => clearInterval(intervalId);
102505
+ // eslint-disable-next-line react-hooks/exhaustive-deps
102506
+ }, [flexcardApiKey]);
102507
+ (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
102508
+ if (!settingsBackupDialogOpen || !flexcardApiKey.trim())
102509
+ return;
102510
+ void fetchFlexCardBalance();
102511
+ // eslint-disable-next-line react-hooks/exhaustive-deps
102512
+ }, [settingsBackupDialogOpen]);
102416
102513
  // Fetch image models when API key changes
102417
102514
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
102418
102515
  if (!openaiApiKey) {
@@ -102702,7 +102799,7 @@ function App() {
102702
102799
  clearTimeout(pollTimeoutId);
102703
102800
  };
102704
102801
  }, [driveFolderUrl, accessToken, refreshToken]);
102705
- const saveConfig = async (newClientId, newClientSecret, newAccessToken, newRefreshToken, newOpenaiApiKey) => {
102802
+ const saveConfig = async (newClientId, newClientSecret, newAccessToken, newRefreshToken, newOpenaiApiKey, newFlexcardApiKey, newTelegramBotToken, newTelegramChatId) => {
102706
102803
  try {
102707
102804
  const api = getElectronAPI();
102708
102805
  if (api) {
@@ -102713,7 +102810,10 @@ function App() {
102713
102810
  clientSecret: newClientSecret ?? clientSecret,
102714
102811
  accessToken: newAccessToken ?? accessToken,
102715
102812
  refreshToken: newRefreshToken ?? refreshToken,
102716
- openaiApiKey: newOpenaiApiKey ?? openaiApiKey
102813
+ openaiApiKey: newOpenaiApiKey ?? openaiApiKey,
102814
+ flexcardApiKey: newFlexcardApiKey ?? flexcardApiKey,
102815
+ telegramBotToken: newTelegramBotToken ?? telegramBotToken,
102816
+ telegramChatId: newTelegramChatId ?? telegramChatId,
102717
102817
  });
102718
102818
  }
102719
102819
  }
@@ -102946,6 +103046,15 @@ function App() {
102946
103046
  /* ignore */
102947
103047
  }
102948
103048
  };
103049
+ const handleUploadAfterValidatorOkChange = (checked) => {
103050
+ setUploadAfterValidatorOk(checked);
103051
+ try {
103052
+ localStorage.setItem('uploadAfterValidatorOk', String(checked));
103053
+ }
103054
+ catch {
103055
+ /* ignore */
103056
+ }
103057
+ };
102949
103058
  // Fetch OpenRouter account balance and key limit
102950
103059
  const fetchOpenRouterBalance = async (apiKey) => {
102951
103060
  const keyToUse = apiKey ?? openaiApiKey;
@@ -103131,6 +103240,73 @@ function App() {
103131
103240
  fetchBalanceRef.current = fetchPromise;
103132
103241
  await fetchPromise;
103133
103242
  };
103243
+ const fetchFlexCardBalance = async (apiKey) => {
103244
+ const keyToUse = (apiKey ?? flexcardApiKey).trim();
103245
+ if (!keyToUse) {
103246
+ setFlexCardUsdTotal(null);
103247
+ return;
103248
+ }
103249
+ setFlexCardBalanceLoading(true);
103250
+ try {
103251
+ const total = await (0,_flexcardBalance__WEBPACK_IMPORTED_MODULE_65__.fetchFlexCardFinanceAccountsUsdTotal)(keyToUse);
103252
+ setFlexCardUsdTotal(total);
103253
+ if (total === null) {
103254
+ logToTerminal('warn', '⚠️ FlexCard: не удалось загрузить finance/accounts (ключ или права API).');
103255
+ }
103256
+ }
103257
+ catch (e) {
103258
+ setFlexCardUsdTotal(null);
103259
+ logToTerminal('error', '❌ FlexCard:', e instanceof Error ? e.message : String(e));
103260
+ }
103261
+ finally {
103262
+ setFlexCardBalanceLoading(false);
103263
+ }
103264
+ };
103265
+ const handleFlexcardApiKeyChange = (val) => {
103266
+ setFlexcardApiKey(val);
103267
+ saveConfig(undefined, undefined, undefined, undefined, undefined, val);
103268
+ if (val.trim()) {
103269
+ void fetchFlexCardBalance(val);
103270
+ }
103271
+ else {
103272
+ setFlexCardUsdTotal(null);
103273
+ }
103274
+ };
103275
+ const handleTelegramBotTokenChange = (val) => {
103276
+ setTelegramBotToken(val);
103277
+ saveConfig(undefined, undefined, undefined, undefined, undefined, undefined, val, undefined);
103278
+ };
103279
+ const handleTelegramChatIdChange = (val) => {
103280
+ setTelegramChatId(val);
103281
+ saveConfig(undefined, undefined, undefined, undefined, undefined, undefined, undefined, val);
103282
+ };
103283
+ const handleTelegramTestSend = async () => {
103284
+ const api = getElectronAPI();
103285
+ if (!api?.telegramSendTest) {
103286
+ alert('Отправка теста доступна в приложении Electron.');
103287
+ return;
103288
+ }
103289
+ const token = telegramBotToken.trim();
103290
+ const chat = telegramChatId.trim();
103291
+ if (!token || !chat) {
103292
+ alert('Укажите токен бота и ID чата.');
103293
+ return;
103294
+ }
103295
+ setTelegramTestSending(true);
103296
+ try {
103297
+ const r = await api.telegramSendTest(token, chat);
103298
+ if (r.ok)
103299
+ alert('Сообщение отправлено.');
103300
+ else
103301
+ alert(r.error || 'Ошибка отправки.');
103302
+ }
103303
+ catch (e) {
103304
+ alert(e instanceof Error ? e.message : String(e));
103305
+ }
103306
+ finally {
103307
+ setTelegramTestSending(false);
103308
+ }
103309
+ };
103134
103310
  // Fetch available image generation models from OpenRouter
103135
103311
  const fetchImageModels = async (apiKey) => {
103136
103312
  const keyToUse = apiKey ?? openaiApiKey;
@@ -103189,14 +103365,6 @@ function App() {
103189
103365
  name: model.name || model.id
103190
103366
  }))
103191
103367
  .sort((a, b) => a.name.localeCompare(b.name));
103192
- // Debug: log raw structure of a few representative models
103193
- const debugModels = (data.data || []).filter((m) => m.id?.includes('flux') || m.id?.includes('riverflow') || m.id?.includes('gemini')).slice(0, 6);
103194
- if (debugModels.length > 0) {
103195
- logToTerminal('log', '🔬 Image model raw structures (flux/riverflow/gemini sample):');
103196
- debugModels.forEach((m) => {
103197
- logToTerminal('log', ` ${m.id} → architecture=${JSON.stringify(m.architecture)}, modalities=${JSON.stringify(m.modalities)}`);
103198
- });
103199
- }
103200
103368
  logToTerminal('log', `✅ Found ${imageGenModels.length} image generation models`);
103201
103369
  setImageModels(imageGenModels);
103202
103370
  // Если выбранной модели нет в списке API — сброс на дефолт (отдельно для 1:1 и 2:3)
@@ -103766,6 +103934,20 @@ function App() {
103766
103934
  });
103767
103935
  return result;
103768
103936
  };
103937
+ /** Уведомление в Telegram, если в настройках заданы токен и chat id (только Electron). */
103938
+ const sendTelegramNotification = async (text) => {
103939
+ const api = getElectronAPI();
103940
+ const token = telegramBotToken.trim();
103941
+ const chat = telegramChatId.trim();
103942
+ if (!api?.telegramSendMessage || !token || !chat)
103943
+ return;
103944
+ try {
103945
+ await api.telegramSendMessage(token, chat, text);
103946
+ }
103947
+ catch {
103948
+ /* ignore */
103949
+ }
103950
+ };
103769
103951
  /**
103770
103952
  * Переводит массив сгенерированных пар на русский одним запросом.
103771
103953
  * При ошибке выставляется pairTranslationFailed (кнопка «Запросить перевод»).
@@ -103830,6 +104012,7 @@ function App() {
103830
104012
  setPairTranslations(translations);
103831
104013
  setPairTranslationFailed(false);
103832
104014
  logToTerminal('log', '[Translate RU] OK, пар переведено:', parsed.length);
104015
+ void sendTelegramNotification('Docs Combiner: тексты сгенерированы, перевод на русский выполнен.');
103833
104016
  }
103834
104017
  catch {
103835
104018
  logToTerminal('warn', '[Translate RU] Ошибка запроса или разбора — см. консоль / лог');
@@ -104397,7 +104580,7 @@ function App() {
104397
104580
  const oldStr = Number.isFinite(oldNum)
104398
104581
  ? (Number.isInteger(oldNum) ? String(oldNum) : (Math.round(oldNum * 100) / 100).toString().replace(/\.?0+$/, ''))
104399
104582
  : '';
104400
- priceBriefForValidation = `Новая цена по брифу (после скидки -50%): ${briefPrice} ${briefCurrency}${currencySymbol ? `, символ валюты: ${currencySymbol}` : ''}. ${Number.isFinite(oldNum) ? `Ожидаемая старая цена до скидки: ${oldStr} ${briefCurrency} (2× новой).` : 'Старая цена на макете должна быть в 2 раза больше новой.'} Любые другие суммы — ошибка «не совпадает с брифом».`;
104583
+ priceBriefForValidation = `Новая цена по брифу (после скидки -50%): ${briefPrice} ${briefCurrency}${currencySymbol ? `, символ валюты: ${currencySymbol}` : ''}. ${Number.isFinite(oldNum) ? `Ожидаемая старая цена до скидки: ${oldStr} ${briefCurrency} (2× новой).` : 'Старая цена на макете должна быть в 2 раза больше новой.'} Любые другие суммы — ошибка «не совпадает с брифом». На макете в зоне цен должна быть **именно эта валюта** (${briefCurrency}${currencySymbol ? ` или эквивалентный символ ${currencySymbol}` : ''}); иная валюта или её сокращения (например лв/BGN при EUR) — ошибка валидатора, нужна пересборка.`;
104401
104584
  logMsg('log', `💶 Эталон цен для валидации: новая ${briefPrice} ${briefCurrency}${oldStr ? `, старая ${oldStr}` : ''}`);
104402
104585
  }
104403
104586
  else {
@@ -104601,7 +104784,7 @@ function App() {
104601
104784
  : status === 'needs_rebuild'
104602
104785
  ? (snippet
104603
104786
  ? [`Валидатор не выписал строки «ОШИБКА:». Фрагмент ответа: ${snippet}`]
104604
- : ['Валидатор указал пересборку, но не перечислил причины (нет строк «ОШИБКА:»). Нажмите «Полный ответ валидатора» под карточкой или откройте лог; при необходимости повторите проверку.'])
104787
+ : ['Валидатор требует пересборки, а строки «ОШИБКА:» не разобрали. Сырой ответ/JSON в «Полном ответе валидатора» под карточкой; при пустом контенте модели смотрите и лог.'])
104605
104788
  : [];
104606
104789
  if (truncatedByLimit) {
104607
104790
  const truncationMsg = 'ОШИБКА: ответ валидатора обрезан по лимиту токенов (OpenRouter/провайдер вернули неполный текст — обычно finish_reason=length). Повторите проверку или выберите модель без тяжёлого reasoning.';
@@ -104618,7 +104801,7 @@ function App() {
104618
104801
  }
104619
104802
  return {
104620
104803
  status,
104621
- result: content,
104804
+ result: buildValidatorStoredResultText(content, data),
104622
104805
  errors: finalErrors
104623
104806
  };
104624
104807
  };
@@ -104913,7 +105096,7 @@ function App() {
104913
105096
  addLog(formatLogMessage(level, ...args));
104914
105097
  };
104915
105098
  logMsg('log', '📦 Creating ZIP archive with HTML and product image...');
104916
- const zip = new (jszip__WEBPACK_IMPORTED_MODULE_66___default())();
105099
+ const zip = new (jszip__WEBPACK_IMPORTED_MODULE_67___default())();
104917
105100
  // Replace product image path in HTML to match actual filename (png/jpg/webp)
104918
105101
  const htmlWithProductPath = htmlContent.replace(/src=["']product\.(png|jpe?g|webp)["']/gi, `src="${productImageName}"`);
104919
105102
  zip.file('index.html', htmlWithProductPath);
@@ -105309,16 +105492,104 @@ function App() {
105309
105492
  }));
105310
105493
  setGeneratedImagesData(initialPlaceholders);
105311
105494
  addLog(formatLogMessage('log', `📝 Generated prompts for ${tasks.length} images${useBoth ? ' (1:1 + 2:3 на каждый подход)' : ''}`));
105312
- const maxParallel = 100; // like infinity
105495
+ const maxParallel = 12;
105313
105496
  addLog(formatLogMessage('log', `🚀 Generating ${tasks.length} images in parallel (up to ${maxParallel} at a time)...`));
105314
105497
  const generationStartTime = Date.now();
105315
105498
  const resultsMap = new Map();
105499
+ const validationTasks = [];
105316
105500
  // Ensure each slot has prompt+product reference from the start (needed for regeneration even on failures)
105317
105501
  setGeneratedImagesData(prev => prev.map((img, i) => ({
105318
105502
  ...img,
105319
105503
  originalPrompt: img.originalPrompt || imagePrompts[i],
105320
105504
  productImageUrl: img.productImageUrl || productImage.url
105321
105505
  })));
105506
+ const runValidationForGeneratedImage = async (imageIndex, imageUrl, prompt, approachName, approachLabel, ratio) => {
105507
+ const validationResult = validationDisabled
105508
+ ? { status: 'ok', result: 'Проверка отключена', errors: [], checkFailed: false }
105509
+ : await (async () => {
105510
+ try {
105511
+ const res = await validateCreativeImage(imageUrl, generateProduct, generateGeo, addLog, approachName, productImage.url);
105512
+ if (!res)
105513
+ throw new Error('No validation result');
105514
+ return { ...res, checkFailed: false };
105515
+ }
105516
+ catch (validationErr) {
105517
+ const msg = validationErr?.message || String(validationErr);
105518
+ addLog(formatLogMessage('error', `❌ Ошибка проверки изображения ${imageIndex}: ${msg}`));
105519
+ return {
105520
+ status: 'needs_rebuild',
105521
+ result: `Ошибка проверки: ${msg}`,
105522
+ errors: [msg],
105523
+ checkFailed: true
105524
+ };
105525
+ }
105526
+ })();
105527
+ let scheduleValidatorAutoRemake = false;
105528
+ const formattedValidationErrors = formatValidationErrorsForRegeneratePrompt(validationResult.errors);
105529
+ const validatorTransportRemake = validationResult.checkFailed === true;
105530
+ (0,react_dom__WEBPACK_IMPORTED_MODULE_1__.flushSync)(() => {
105531
+ setGeneratedImagesData(prev => {
105532
+ const cur = prev.find(i => i.index === imageIndex);
105533
+ const useAuto = shouldAutoRemakeAfterValidation(autoRemakeOnValidatorError, validationDisabled, validationResult, imageUrl, cur?.validatorTransportAutoRemakeDone);
105534
+ scheduleValidatorAutoRemake = useAuto;
105535
+ return prev.map(img => img.index === imageIndex
105536
+ ? {
105537
+ ...img,
105538
+ checking: false,
105539
+ checkFailed: validationResult.checkFailed ?? false,
105540
+ checkStatus: validationResult.status,
105541
+ checkResult: validationResult.result,
105542
+ checkErrors: validationResult.errors,
105543
+ customRegeneratePrompt: useAuto && validatorTransportRemake
105544
+ ? ''
105545
+ : formattedValidationErrors,
105546
+ originalPrompt: img.originalPrompt || prompt,
105547
+ productImageUrl: img.productImageUrl || productImage.url,
105548
+ validatorTransportAutoRemakeDone: useAuto
105549
+ ? true
105550
+ : (img.validatorTransportAutoRemakeDone ?? false)
105551
+ }
105552
+ : img);
105553
+ });
105554
+ });
105555
+ let verified;
105556
+ if (scheduleValidatorAutoRemake) {
105557
+ addLog(formatLogMessage('log', `🔄 Автопеределка с нуля изображения ${imageIndex} после ошибки валидатора (один раз)...`));
105558
+ const fr = await handleRegenerateImageFresh({
105559
+ index: imageIndex,
105560
+ approach: approachLabel,
105561
+ aspectRatio: ratio,
105562
+ originalPrompt: prompt,
105563
+ productImageUrl: productImage.url,
105564
+ creoApproachUiNumber: tasks[imageIndex - 1].poolIndex + 1,
105565
+ }, { fromAutoValidatorRemake: true });
105566
+ verified = fr?.verified === true;
105567
+ }
105568
+ else {
105569
+ verified = validationDisabled || validationResult.status === 'ok';
105570
+ if (shouldUploadCreoAfterValidatorOk(uploadAfterValidatorOk, validationDisabled, validationResult)) {
105571
+ const taskMeta = tasks[imageIndex - 1];
105572
+ if (taskMeta && folderId) {
105573
+ await creoAutoUploadAfterValidatorOkRef.current({
105574
+ imageData: {
105575
+ index: imageIndex,
105576
+ imageUrl,
105577
+ approach: approachLabel,
105578
+ creoApproachUiNumber: taskMeta.poolIndex + 1,
105579
+ aspectRatio: ratio,
105580
+ },
105581
+ folderId,
105582
+ validationResult,
105583
+ });
105584
+ }
105585
+ }
105586
+ }
105587
+ const existingResult = resultsMap.get(imageIndex);
105588
+ if (existingResult) {
105589
+ resultsMap.set(imageIndex, { ...existingResult, verified });
105590
+ }
105591
+ return { index: imageIndex, verified };
105592
+ };
105322
105593
  const runOne = async (i, isRetry) => {
105323
105594
  const imageIndex = i + 1;
105324
105595
  const task = tasks[i];
@@ -105362,70 +105633,14 @@ function App() {
105362
105633
  }
105363
105634
  : img));
105364
105635
  addLog(formatLogMessage('log', `✅ Изображение ${imageIndex}/${tasks.length} сгенерировано`));
105365
- // Validate right after generation (skip if validation disabled)
105366
- const validationResult = validationDisabled
105367
- ? { status: 'ok', result: 'Проверка отключена', errors: [], checkFailed: false }
105368
- : await (async () => {
105369
- try {
105370
- const res = await validateCreativeImage(imageUrl, generateProduct, generateGeo, addLog, approachName, productImage.url);
105371
- if (!res)
105372
- throw new Error('No validation result');
105373
- return { ...res, checkFailed: false };
105374
- }
105375
- catch (validationErr) {
105376
- const msg = validationErr?.message || String(validationErr);
105377
- addLog(formatLogMessage('error', `❌ Ошибка проверки изображения ${imageIndex}: ${msg}`));
105378
- return {
105379
- status: 'needs_rebuild',
105380
- result: `Ошибка проверки: ${msg}`,
105381
- errors: [msg],
105382
- checkFailed: true
105383
- };
105384
- }
105385
- })();
105386
105636
  const approachLabel = approachName + (useBoth ? ` (${ratio})` : '');
105387
- let scheduleValidatorAutoRemake = false;
105388
- const formattedValidationErrors = formatValidationErrorsForRegeneratePrompt(validationResult.errors);
105389
- const validatorTransportRemake = validationResult.checkFailed === true;
105390
- (0,react_dom__WEBPACK_IMPORTED_MODULE_1__.flushSync)(() => {
105391
- setGeneratedImagesData(prev => {
105392
- const cur = prev.find(i => i.index === imageIndex);
105393
- const useAuto = shouldAutoRemakeAfterValidation(autoRemakeOnValidatorError, validationDisabled, validationResult, imageUrl, cur?.validatorTransportAutoRemakeDone);
105394
- scheduleValidatorAutoRemake = useAuto;
105395
- return prev.map(img => img.index === imageIndex
105396
- ? {
105397
- ...img,
105398
- checking: false,
105399
- checkFailed: validationResult.checkFailed ?? false,
105400
- checkStatus: validationResult.status,
105401
- checkResult: validationResult.result,
105402
- checkErrors: validationResult.errors,
105403
- customRegeneratePrompt: useAuto && validatorTransportRemake
105404
- ? ''
105405
- : formattedValidationErrors,
105406
- originalPrompt: img.originalPrompt || prompt,
105407
- productImageUrl: img.productImageUrl || productImage.url,
105408
- validatorTransportAutoRemakeDone: useAuto
105409
- ? true
105410
- : (img.validatorTransportAutoRemakeDone ?? false)
105411
- }
105412
- : img);
105413
- });
105414
- });
105415
- if (scheduleValidatorAutoRemake) {
105416
- addLog(formatLogMessage('log', `🔄 Автопеределка с нуля изображения ${imageIndex} после ошибки валидатора (один раз)...`));
105417
- await handleRegenerateImageFresh({
105418
- index: imageIndex,
105419
- approach: approachLabel,
105420
- aspectRatio: ratio,
105421
- originalPrompt: prompt,
105422
- productImageUrl: productImage.url,
105423
- }, { fromAutoValidatorRemake: true });
105424
- }
105637
+ setCheckingImages(!validationDisabled);
105638
+ validationTasks.push(runValidationForGeneratedImage(imageIndex, imageUrl, prompt, approachName, approachLabel, ratio));
105425
105639
  return {
105426
105640
  index: imageIndex,
105427
105641
  imageUrl,
105428
105642
  success: true,
105643
+ verified: validationDisabled,
105429
105644
  approach: approachName,
105430
105645
  originalPrompt: prompt,
105431
105646
  productImageUrl: productImage.url
@@ -105449,6 +105664,7 @@ function App() {
105449
105664
  index: imageIndex,
105450
105665
  imageUrl: null,
105451
105666
  success: false,
105667
+ verified: false,
105452
105668
  error: err,
105453
105669
  approach: approachName,
105454
105670
  originalPrompt: prompt,
@@ -105483,6 +105699,7 @@ function App() {
105483
105699
  index: idx,
105484
105700
  imageUrl: null,
105485
105701
  success: false,
105702
+ verified: false,
105486
105703
  error: new Error('Generation did not complete'),
105487
105704
  approach: tasks[i]?.approach.name || 'Unknown',
105488
105705
  originalPrompt: imagePrompts[i],
@@ -105538,6 +105755,24 @@ function App() {
105538
105755
  await Promise.all(retryWorkers);
105539
105756
  generationResults = Array.from(resultsMap.values()).sort((a, b) => a.index - b.index);
105540
105757
  }
105758
+ if (validationTasks.length > 0) {
105759
+ addLog(formatLogMessage('log', `🔍 Ожидание проверок: ${validationTasks.length} задач(и)...`));
105760
+ const validationResults = await Promise.all(validationTasks.map(task => task.catch((err) => {
105761
+ const msg = err?.message || String(err);
105762
+ addLog(formatLogMessage('error', `❌ Ошибка задачи проверки: ${msg}`));
105763
+ return { index: -1, verified: false };
105764
+ })));
105765
+ validationResults.forEach(result => {
105766
+ if (result.index < 0)
105767
+ return;
105768
+ const existingResult = resultsMap.get(result.index);
105769
+ if (existingResult) {
105770
+ resultsMap.set(result.index, { ...existingResult, verified: result.verified });
105771
+ }
105772
+ });
105773
+ generationResults = Array.from(resultsMap.values()).sort((a, b) => a.index - b.index);
105774
+ setCheckingImages(false);
105775
+ }
105541
105776
  const generationDuration = Date.now() - generationStartTime;
105542
105777
  const successfulImages = generationResults.filter(r => r.success);
105543
105778
  const finalFailedImages = generationResults.filter(r => !r.success);
@@ -105552,6 +105787,10 @@ function App() {
105552
105787
  addLog(formatLogMessage('log', `🎉 === Image generation process completed ===`));
105553
105788
  addLog(formatLogMessage('log', `⏱️ Total time: ${overallDuration}ms (${(overallDuration / 1000).toFixed(2)}s)`));
105554
105789
  addLog(formatLogMessage('log', `✅ Successfully generated ${successfulImages.length}/${imagePrompts.length} images`));
105790
+ if (imagePrompts.length > 0 &&
105791
+ generationResults.every(r => r.success && r.verified)) {
105792
+ void sendTelegramNotification('Docs Combiner: все изображения сгенерированы и проверены.');
105793
+ }
105555
105794
  // Stop generating state to hide backdrop and show images
105556
105795
  setGeneratingImages(false);
105557
105796
  setCheckingImages(false);
@@ -105565,10 +105804,12 @@ function App() {
105565
105804
  }
105566
105805
  finally {
105567
105806
  setGeneratingImages(false);
105807
+ setCheckingImages(false);
105568
105808
  }
105569
105809
  }
105570
105810
  catch (err) {
105571
105811
  setGeneratingImages(false);
105812
+ setCheckingImages(false);
105572
105813
  logToTerminal('error', '❌ Error:', err?.message || err);
105573
105814
  }
105574
105815
  };
@@ -105763,16 +106004,30 @@ ${imageData.originalPrompt}
105763
106004
  // Run validation on the new image (skip if disabled)
105764
106005
  let validationResult;
105765
106006
  if (validationDisabled) {
105766
- validationResult = { status: 'ok', result: 'Проверка отключена', errors: [] };
106007
+ validationResult = { status: 'ok', result: 'Проверка отключена', errors: [], checkFailed: false };
105767
106008
  }
105768
106009
  else {
105769
106010
  addLog(formatLogMessage('log', `🔍 Проверка переделанного изображения ${imageData.index}...`));
105770
- validationResult = await validateCreativeImage(newImageUrl, generateProduct, generateGeo, addLog, imageData.approach, imageData.productImageUrl);
106011
+ try {
106012
+ const res = await validateCreativeImage(newImageUrl, generateProduct, generateGeo, addLog, imageData.approach, imageData.productImageUrl);
106013
+ validationResult = { ...res, checkFailed: false };
106014
+ }
106015
+ catch (validationErr) {
106016
+ const msg = validationErr?.message || String(validationErr);
106017
+ addLog(formatLogMessage('error', `❌ Ошибка проверки переделанного изображения ${imageData.index}: ${msg}`));
106018
+ validationResult = {
106019
+ status: 'needs_rebuild',
106020
+ result: `Ошибка проверки: ${msg}`,
106021
+ errors: [msg],
106022
+ checkFailed: true,
106023
+ };
106024
+ }
105771
106025
  }
105772
106026
  // Update with validation result (+ одна автопеределка при needs_rebuild с замечаниями, если включено)
105773
106027
  const outcomeForAuto = {
105774
106028
  status: validationResult.status,
105775
106029
  errors: validationResult.errors,
106030
+ checkFailed: validationResult.checkFailed,
105776
106031
  };
105777
106032
  const formattedRegenErrors = formatValidationErrorsForRegeneratePrompt(validationResult.errors);
105778
106033
  const regenTransportRemake = outcomeForAuto.checkFailed === true;
@@ -105786,6 +106041,7 @@ ${imageData.originalPrompt}
105786
106041
  ? {
105787
106042
  ...img,
105788
106043
  checking: false,
106044
+ checkFailed: validationResult.checkFailed ?? false,
105789
106045
  checkStatus: validationResult.status,
105790
106046
  checkResult: validationResult.result,
105791
106047
  checkErrors: validationResult.errors,
@@ -105807,8 +106063,26 @@ ${imageData.originalPrompt}
105807
106063
  aspectRatio: imageData.aspectRatio,
105808
106064
  originalPrompt: imageData.originalPrompt,
105809
106065
  productImageUrl: imageData.productImageUrl,
106066
+ creoApproachUiNumber: currentImageData?.creoApproachUiNumber,
105810
106067
  }, { fromAutoValidatorRemake: true });
105811
106068
  }
106069
+ if (!scheduleAfterRegenValidation &&
106070
+ shouldUploadCreoAfterValidatorOk(uploadAfterValidatorOk, validationDisabled, validationResult)) {
106071
+ const creoNum = currentImageData?.creoApproachUiNumber ??
106072
+ generatedImagesData.find(img => img.index === imageData.index)?.creoApproachUiNumber ??
106073
+ 1;
106074
+ await creoAutoUploadAfterValidatorOkRef.current({
106075
+ imageData: {
106076
+ index: imageData.index,
106077
+ imageUrl: updatedImageUrl,
106078
+ approach: imageData.approach,
106079
+ creoApproachUiNumber: creoNum,
106080
+ aspectRatio: imageData.aspectRatio,
106081
+ },
106082
+ folderId: regenFolderId,
106083
+ validationResult,
106084
+ });
106085
+ }
105812
106086
  const statusEmoji = validationResult.status === 'ok' ? '✅' : '❌';
105813
106087
  addLog(formatLogMessage('log', `${statusEmoji} Переделанное изображение ${imageData.index}: ${validationResult.status === 'ok' ? 'OK' : 'НУЖНА ПЕРЕСБОРКА'}`));
105814
106088
  if (validationResult.errors.length > 0) {
@@ -105841,12 +106115,12 @@ ${imageData.originalPrompt}
105841
106115
  const handleRegenerateImageFresh = async (imageData, opts) => {
105842
106116
  if (!imageData.originalPrompt || !imageData.productImageUrl) {
105843
106117
  alert('Не удалось найти оригинальный промпт или изображение продукта для переделки');
105844
- return;
106118
+ return { verified: false };
105845
106119
  }
105846
106120
  const freshFolderId = extractFolderId(driveFolderUrl);
105847
106121
  if (!freshFolderId) {
105848
106122
  alert('Укажите корректный URL папки оффера на Google Drive');
105849
- return;
106123
+ return { verified: false };
105850
106124
  }
105851
106125
  const addLog = (msg) => {
105852
106126
  setImagesGenerationLogs(prev => [...prev, msg]);
@@ -105856,12 +106130,12 @@ ${imageData.originalPrompt}
105856
106130
  const freshTok = await getValidAccessToken();
105857
106131
  if (!freshTok) {
105858
106132
  alert('Please log in with Google first');
105859
- return;
106133
+ return { verified: false };
105860
106134
  }
105861
106135
  const freshPub = await verifyOfferFolderHasAnyoneLinkForImageGen(freshTok, freshFolderId);
105862
106136
  if (!freshPub.ok) {
105863
106137
  alert(freshPub.message);
105864
- return;
106138
+ return { verified: false };
105865
106139
  }
105866
106140
  // Mark as regenerating (show overlay timer) - clear old image URL immediately to force re-render
105867
106141
  setGeneratedImagesData(prev => prev.map(img => img.index === imageData.index
@@ -105919,15 +106193,29 @@ ${imageData.originalPrompt}
105919
106193
  // Validate new image (skip if disabled)
105920
106194
  let validationResult;
105921
106195
  if (validationDisabled) {
105922
- validationResult = { status: 'ok', result: 'Проверка отключена', errors: [] };
106196
+ validationResult = { status: 'ok', result: 'Проверка отключена', errors: [], checkFailed: false };
105923
106197
  }
105924
106198
  else {
105925
106199
  addLog(formatLogMessage('log', `🔍 Проверка изображения ${imageData.index} (с нуля)...`));
105926
- validationResult = await validateCreativeImage(newImageUrl, generateProduct, generateGeo, addLog, imageData.approach, imageData.productImageUrl);
106200
+ try {
106201
+ const res = await validateCreativeImage(newImageUrl, generateProduct, generateGeo, addLog, imageData.approach, imageData.productImageUrl);
106202
+ validationResult = { ...res, checkFailed: false };
106203
+ }
106204
+ catch (validationErr) {
106205
+ const msg = validationErr?.message || String(validationErr);
106206
+ addLog(formatLogMessage('error', `❌ Ошибка проверки изображения ${imageData.index} (с нуля): ${msg}`));
106207
+ validationResult = {
106208
+ status: 'needs_rebuild',
106209
+ result: `Ошибка проверки: ${msg}`,
106210
+ errors: [msg],
106211
+ checkFailed: true,
106212
+ };
106213
+ }
105927
106214
  }
105928
106215
  const outcomeFresh = {
105929
106216
  status: validationResult.status,
105930
106217
  errors: validationResult.errors,
106218
+ checkFailed: validationResult.checkFailed,
105931
106219
  };
105932
106220
  const formattedFreshErrors = formatValidationErrorsForRegeneratePrompt(validationResult.errors);
105933
106221
  const freshTransportRemake = outcomeFresh.checkFailed === true;
@@ -105941,6 +106229,7 @@ ${imageData.originalPrompt}
105941
106229
  ? {
105942
106230
  ...img,
105943
106231
  checking: false,
106232
+ checkFailed: validationResult.checkFailed ?? false,
105944
106233
  checkStatus: validationResult.status,
105945
106234
  checkResult: validationResult.result,
105946
106235
  checkErrors: validationResult.errors,
@@ -105956,14 +106245,34 @@ ${imageData.originalPrompt}
105956
106245
  });
105957
106246
  if (scheduleAfterFreshValidation && imageData.originalPrompt && imageData.productImageUrl) {
105958
106247
  addLog(formatLogMessage('log', `🔄 Автопеределка с нуля изображения ${imageData.index} после ошибки валидатора (один раз)...`));
105959
- await handleRegenerateImageFresh({
106248
+ return await handleRegenerateImageFresh({
105960
106249
  index: imageData.index,
105961
106250
  approach: imageData.approach,
105962
106251
  aspectRatio: imageData.aspectRatio,
105963
106252
  originalPrompt: imageData.originalPrompt,
105964
106253
  productImageUrl: imageData.productImageUrl,
106254
+ creoApproachUiNumber: imageData.creoApproachUiNumber,
105965
106255
  }, { fromAutoValidatorRemake: true });
105966
106256
  }
106257
+ const creoApproachUiNumberFresh = imageData.creoApproachUiNumber ??
106258
+ generatedImagesData.find(img => img.index === imageData.index)?.creoApproachUiNumber ??
106259
+ 1;
106260
+ if (shouldUploadCreoAfterValidatorOk(uploadAfterValidatorOk, validationDisabled, validationResult)) {
106261
+ await creoAutoUploadAfterValidatorOkRef.current({
106262
+ imageData: {
106263
+ index: imageData.index,
106264
+ imageUrl: updatedImageUrl,
106265
+ approach: imageData.approach,
106266
+ creoApproachUiNumber: creoApproachUiNumberFresh,
106267
+ aspectRatio: imageData.aspectRatio,
106268
+ },
106269
+ folderId: freshFolderId,
106270
+ validationResult,
106271
+ });
106272
+ }
106273
+ return {
106274
+ verified: validationDisabled || validationResult.status === 'ok',
106275
+ };
105967
106276
  }
105968
106277
  catch (err) {
105969
106278
  addLog(formatLogMessage('error', `❌ Ошибка при переделке заново изображения ${imageData.index}: ${err.message}`));
@@ -105980,6 +106289,7 @@ ${imageData.originalPrompt}
105980
106289
  }
105981
106290
  : img));
105982
106291
  alert('Ошибка при переделке заново: ' + err.message);
106292
+ return { verified: false };
105983
106293
  }
105984
106294
  };
105985
106295
  const handleRetryCheck = async (imageData) => {
@@ -106049,6 +106359,8 @@ ${imageData.originalPrompt}
106049
106359
  aspectRatio: imageData.aspectRatio,
106050
106360
  originalPrompt: imageData.originalPrompt,
106051
106361
  productImageUrl: imageData.productImageUrl,
106362
+ creoApproachUiNumber: imageData.creoApproachUiNumber ??
106363
+ generatedImagesData.find(img => img.index === imageData.index)?.creoApproachUiNumber,
106052
106364
  }, { fromAutoValidatorRemake: true });
106053
106365
  }
106054
106366
  return;
@@ -106089,8 +106401,30 @@ ${imageData.originalPrompt}
106089
106401
  aspectRatio: imageData.aspectRatio,
106090
106402
  originalPrompt: imageData.originalPrompt,
106091
106403
  productImageUrl: imageData.productImageUrl,
106404
+ creoApproachUiNumber: imageData.creoApproachUiNumber ??
106405
+ generatedImagesData.find(img => img.index === imageData.index)?.creoApproachUiNumber,
106092
106406
  }, { fromAutoValidatorRemake: true });
106093
106407
  }
106408
+ if (!scheduleValidatorAutoRemakeRetry &&
106409
+ shouldUploadCreoAfterValidatorOk(uploadAfterValidatorOk, validationDisabled, validationResult)) {
106410
+ const retryFid = extractFolderId(driveFolderUrl);
106411
+ if (retryFid && imageData.imageUrl) {
106412
+ const creoNum = imageData.creoApproachUiNumber ??
106413
+ generatedImagesData.find(img => img.index === imageData.index)?.creoApproachUiNumber ??
106414
+ 1;
106415
+ await creoAutoUploadAfterValidatorOkRef.current({
106416
+ imageData: {
106417
+ index: imageData.index,
106418
+ imageUrl: imageData.imageUrl,
106419
+ approach: imageData.approach,
106420
+ creoApproachUiNumber: creoNum,
106421
+ aspectRatio: imageData.aspectRatio,
106422
+ },
106423
+ folderId: retryFid,
106424
+ validationResult,
106425
+ });
106426
+ }
106427
+ }
106094
106428
  const statusEmoji = validationResult.status === 'ok' ? '✅' : '❌';
106095
106429
  addLog(formatLogMessage('log', `${statusEmoji} Повторная проверка изображения ${imageData.index}: ${validationResult.status === 'ok' ? 'OK' : 'НУЖНА ПЕРЕСБОРКА'}`));
106096
106430
  };
@@ -106133,7 +106467,15 @@ ${imageData.originalPrompt}
106133
106467
  : img));
106134
106468
  }
106135
106469
  };
106136
- const handleUploadAllImages = async () => {
106470
+ creoAutoUploadAfterValidatorOkRef.current = async (args) => {
106471
+ if (!shouldUploadCreoAfterValidatorOk(uploadAfterValidatorOk, validationDisabled, args.validationResult)) {
106472
+ return;
106473
+ }
106474
+ if (!args.folderId?.trim() || !args.imageData.imageUrl?.trim())
106475
+ return;
106476
+ await handleUploadImage(args.imageData, args.folderId);
106477
+ };
106478
+ const handleUploadAllImages = async (opts) => {
106137
106479
  if (!driveFolderUrl.trim()) {
106138
106480
  alert('Please fill in Google Drive Folder URL');
106139
106481
  return;
@@ -106143,9 +106485,11 @@ ${imageData.originalPrompt}
106143
106485
  alert('Invalid Google Drive Folder URL');
106144
106486
  return;
106145
106487
  }
106146
- const notUploaded = generatedImagesData.filter(img => !img.uploaded && img.imageUrl);
106488
+ const notUploaded = opts?.correctOnly ? pendingValidatorOkDriveUploadCreos : pendingDriveUploadCreos;
106147
106489
  if (notUploaded.length === 0) {
106148
- alert('All images are already uploaded');
106490
+ alert(opts?.correctOnly
106491
+ ? 'Нет незагруженных крео с успешной проверкой валидатором (зелёная рамка, «Проверка пройдена»).'
106492
+ : 'All images are already uploaded');
106149
106493
  return;
106150
106494
  }
106151
106495
  setUploadingImages(true);
@@ -106158,7 +106502,9 @@ ${imageData.originalPrompt}
106158
106502
  ? { ...img, uploading: true, uploadStartTime: Date.now() }
106159
106503
  : img));
106160
106504
  try {
106161
- addLog(formatLogMessage('log', `📤 Uploading ${notUploaded.length} image(s) to Drive (parallel)...`));
106505
+ addLog(formatLogMessage('log', opts?.correctOnly
106506
+ ? `📤 Uploading ${notUploaded.length} image(s) with validator OK to Drive (parallel)...`
106507
+ : `📤 Uploading ${notUploaded.length} image(s) to Drive (parallel)...`));
106162
106508
  const results = await Promise.allSettled(notUploaded.map((imageData) => {
106163
106509
  const uploadAspect = imageData.aspectRatio ?? (imageAspectRatio === '2:3' ? '2:3' : '1:1');
106164
106510
  const filename = creativeImageUploadFilename(imageData.creoApproachUiNumber, uploadAspect);
@@ -106326,6 +106672,19 @@ ${imageData.originalPrompt}
106326
106672
  alert('Не удалось прочитать буфер обмена. Разрешите доступ к буферу в браузере или вставьте ссылку вручную (Ctrl+V / Cmd+V).');
106327
106673
  }
106328
106674
  };
106675
+ const handlePasteFlexcardApiKey = async () => {
106676
+ try {
106677
+ const text = (await navigator.clipboard.readText()).trim();
106678
+ if (!text) {
106679
+ alert('Буфер обмена пуст.');
106680
+ return;
106681
+ }
106682
+ handleFlexcardApiKeyChange(text);
106683
+ }
106684
+ catch {
106685
+ alert('Не удалось прочитать буфер обмена. Разрешите доступ к буферу в браузере или вставьте ключ вручную (Ctrl+V / Cmd+V).');
106686
+ }
106687
+ };
106329
106688
  const handleLinkPaste = (e) => {
106330
106689
  e.preventDefault();
106331
106690
  const pasted = e.clipboardData.getData('text/plain');
@@ -106927,8 +107286,8 @@ ${imageData.originalPrompt}
106927
107286
  }
106928
107287
  setGeneratedData(rows);
106929
107288
  // Create workbook
106930
- const wb = xlsx__WEBPACK_IMPORTED_MODULE_65__.utils.book_new();
106931
- const ws = xlsx__WEBPACK_IMPORTED_MODULE_65__.utils.aoa_to_sheet(rows);
107289
+ const wb = xlsx__WEBPACK_IMPORTED_MODULE_66__.utils.book_new();
107290
+ const ws = xlsx__WEBPACK_IMPORTED_MODULE_66__.utils.aoa_to_sheet(rows);
106932
107291
  // Set column widths (approximate pixel width / 7)
106933
107292
  ws['!cols'] = [
106934
107293
  { wch: 20 }, // id
@@ -106941,9 +107300,9 @@ ${imageData.originalPrompt}
106941
107300
  { wch: 40 }, // image_link
106942
107301
  { wch: 20 } // brand
106943
107302
  ];
106944
- xlsx__WEBPACK_IMPORTED_MODULE_65__.utils.book_append_sheet(wb, ws, "Products");
107303
+ xlsx__WEBPACK_IMPORTED_MODULE_66__.utils.book_append_sheet(wb, ws, "Products");
106945
107304
  // Generate buffer
106946
- const wbout = xlsx__WEBPACK_IMPORTED_MODULE_65__.write(wb, { bookType: 'xlsx', type: 'array' });
107305
+ const wbout = xlsx__WEBPACK_IMPORTED_MODULE_66__.write(wb, { bookType: 'xlsx', type: 'array' });
106947
107306
  // Upload to Drive (имя файла по бренду)
106948
107307
  const dateStr = new Date().toISOString().split('T')[0];
106949
107308
  const fileName = `${brand}-${dateStr}.xlsx`;
@@ -107073,13 +107432,13 @@ ${imageData.originalPrompt}
107073
107432
  setTestLoading(true);
107074
107433
  try {
107075
107434
  // Create simple test workbook with structure
107076
- const wb = xlsx__WEBPACK_IMPORTED_MODULE_65__.utils.book_new();
107435
+ const wb = xlsx__WEBPACK_IMPORTED_MODULE_66__.utils.book_new();
107077
107436
  const rows = [
107078
107437
  INSTRUCTION_ROW,
107079
107438
  ['id', 'title', 'description', 'availability', 'condition', 'price', 'link', 'image_link', 'brand'],
107080
107439
  ['test1', 'Test Title', 'Test Description', 'in stock', 'new', testPrice, 'http://test.com', 'http://test.com/img.jpg', 'TestBrand']
107081
107440
  ];
107082
- const ws = xlsx__WEBPACK_IMPORTED_MODULE_65__.utils.aoa_to_sheet(rows);
107441
+ const ws = xlsx__WEBPACK_IMPORTED_MODULE_66__.utils.aoa_to_sheet(rows);
107083
107442
  // Set column widths
107084
107443
  ws['!cols'] = [
107085
107444
  { wch: 20 }, // id
@@ -107092,8 +107451,8 @@ ${imageData.originalPrompt}
107092
107451
  { wch: 40 }, // image_link
107093
107452
  { wch: 20 } // brand
107094
107453
  ];
107095
- xlsx__WEBPACK_IMPORTED_MODULE_65__.utils.book_append_sheet(wb, ws, "Test");
107096
- const wbout = xlsx__WEBPACK_IMPORTED_MODULE_65__.write(wb, { bookType: 'xlsx', type: 'array' });
107454
+ xlsx__WEBPACK_IMPORTED_MODULE_66__.utils.book_append_sheet(wb, ws, "Test");
107455
+ const wbout = xlsx__WEBPACK_IMPORTED_MODULE_66__.write(wb, { bookType: 'xlsx', type: 'array' });
107097
107456
  // Try to extract folder ID if available, otherwise upload to root
107098
107457
  const folderId = driveFolderUrl ? extractFolderId(driveFolderUrl) : undefined;
107099
107458
  const result = await uploadFileToDrive(wbout, 'test_table.xlsx', folderId || undefined);
@@ -107166,6 +107525,9 @@ ${imageData.originalPrompt}
107166
107525
  if (g('autoRemakeOnValidatorError') !== undefined) {
107167
107526
  setAutoRemakeOnValidatorError(g('autoRemakeOnValidatorError') === 'true');
107168
107527
  }
107528
+ if (g('uploadAfterValidatorOk') !== undefined) {
107529
+ setUploadAfterValidatorOk(g('uploadAfterValidatorOk') === 'true');
107530
+ }
107169
107531
  const ar = g('imageAspectRatio');
107170
107532
  if (ar === '1:1' || ar === '2:3' || ar === 'both')
107171
107533
  setImageAspectRatio(ar);
@@ -107276,6 +107638,12 @@ ${imageData.originalPrompt}
107276
107638
  setClientSecret(String(ec.clientSecret));
107277
107639
  if (ec.openaiApiKey !== undefined)
107278
107640
  setOpenaiApiKey(String(ec.openaiApiKey));
107641
+ if (ec.flexcardApiKey !== undefined)
107642
+ setFlexcardApiKey(String(ec.flexcardApiKey));
107643
+ if (ec.telegramBotToken !== undefined)
107644
+ setTelegramBotToken(String(ec.telegramBotToken));
107645
+ if (ec.telegramChatId !== undefined)
107646
+ setTelegramChatId(String(ec.telegramChatId));
107279
107647
  if (ec.accessToken !== undefined)
107280
107648
  setAccessToken(String(ec.accessToken));
107281
107649
  if (ec.refreshToken !== undefined)
@@ -107386,29 +107754,52 @@ ${imageData.originalPrompt}
107386
107754
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["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)" }),
107387
107755
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { label: "Client Secret", variant: "outlined", fullWidth: true, value: clientSecret, onChange: (e) => handleClientSecretChange(e.target.value) }),
107388
107756
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], null),
107389
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { label: "OpenRouter API Key", variant: "outlined", fullWidth: true, value: openaiApiKey, onChange: (e) => handleOpenaiApiKeyChange(e.target.value), helperText: "Required for AI generation (images and text). Your key is stored locally. Get your key at openrouter.ai" })))),
107390
- openaiApiKey && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 2, mt: 2 } },
107391
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_41__["default"], { color: openRouterAccountBalance !== null ? 'primary' : 'disabled' }),
107392
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", color: openRouterAccountBalance !== null ? 'text.primary' : 'text.secondary' },
107393
- "\u0411\u0430\u043B\u0430\u043D\u0441: ",
107394
- openRouterBalanceLoading ? 'Loading...' : (openRouterAccountBalance !== null ? `$${openRouterAccountBalance.toFixed(2)}` : 'N/A')),
107395
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", color: "text.secondary" }, "\u2022"),
107396
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", color: openRouterBalance !== null ? 'text.primary' : 'text.secondary' },
107397
- "\u041B\u0438\u043C\u0438\u0442 \u043A\u043B\u044E\u0447\u0430: ",
107398
- openRouterBalanceLoading ? 'Loading...' : (openRouterBalance === -1 ? 'Без лимита' : (openRouterBalance !== null ? `${openRouterBalance.toFixed(4)}` : 'N/A'))))),
107757
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { label: "OpenRouter API Key", variant: "outlined", fullWidth: true, value: openaiApiKey, onChange: (e) => handleOpenaiApiKeyChange(e.target.value), helperText: "Required for AI generation (images and text). Your key is stored locally. Get your key at openrouter.ai" }),
107758
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { label: "FlexCard API Key", variant: "outlined", fullWidth: true, value: flexcardApiKey, onChange: (e) => handleFlexcardApiKeyChange(e.target.value), helperText: "\u041E\u043F\u0446\u0438\u043E\u043D\u0430\u043B\u044C\u043D\u043E: \u0431\u0430\u043B\u0430\u043D\u0441 \u043A\u043E\u0448\u0435\u043B\u044C\u043A\u043E\u0432 \u043D\u0430 flexcard.cards (\u0440\u0430\u0437\u0434\u0435\u043B Team \u2192 API). \u041A\u043B\u044E\u0447 \u0445\u0440\u0430\u043D\u0438\u0442\u0441\u044F \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E \u0432 config.", InputProps: {
107759
+ endAdornment: (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { position: "end" },
107760
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_9__["default"], { title: "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0438\u0437 \u0431\u0443\u0444\u0435\u0440\u0430 \u043E\u0431\u043C\u0435\u043D\u0430" },
107761
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_10__["default"], { edge: "end", "aria-label": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u043A\u043B\u044E\u0447 FlexCard \u0438\u0437 \u0431\u0443\u0444\u0435\u0440\u0430", onClick: () => void handlePasteFlexcardApiKey() },
107762
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_51__["default"], null))))),
107763
+ } }),
107764
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], { direction: "row", spacing: 1, alignItems: "center", sx: { flexWrap: 'nowrap', gap: 1 } },
107765
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { label: "Telegram bot token", size: "small", fullWidth: true, sx: { flex: 2, minWidth: 0 }, value: telegramBotToken, onChange: (e) => handleTelegramBotTokenChange(e.target.value), placeholder: "123456789:ABC...", autoComplete: "off" }),
107766
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { label: "Chat ID", size: "small", sx: { flex: 1, minWidth: 72, maxWidth: 200 }, value: telegramChatId, onChange: (e) => handleTelegramChatIdChange(e.target.value), placeholder: "-100\u2026", autoComplete: "off" }),
107767
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { variant: "outlined", size: "small", disabled: telegramTestSending || !telegramBotToken.trim() || !telegramChatId.trim(), onClick: () => void handleTelegramTestSend(), sx: { flexShrink: 0, whiteSpace: 'nowrap', minWidth: 'auto' } }, telegramTestSending ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { size: 18, color: "inherit" })) : ('Тест'))),
107768
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { sx: { mt: 0 } }, "Telegram \u2014 \u0434\u043B\u044F \u0431\u0443\u0434\u0443\u0449\u0438\u0445 \u043E\u043F\u043E\u0432\u0435\u0449\u0435\u043D\u0438\u0439; \u0442\u0435\u0441\u0442 \u0448\u043B\u0451\u0442 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0438\u0437 Electron (\u043E\u0431\u0445\u043E\u0434 CORS).")))),
107769
+ (openaiApiKey || flexcardApiKey.trim()) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 2, mt: 2, flexWrap: 'wrap' } },
107770
+ openaiApiKey && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
107771
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_41__["default"], { color: openRouterAccountBalance !== null ? 'primary' : 'disabled' }),
107772
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", color: openRouterAccountBalance !== null ? 'text.primary' : 'text.secondary' },
107773
+ "OpenRouter \u2014 \u0431\u0430\u043B\u0430\u043D\u0441: ",
107774
+ openRouterBalanceLoading ? '…' : (openRouterAccountBalance !== null ? `$${openRouterAccountBalance.toFixed(2)}` : 'N/A')),
107775
+ (openRouterBalanceLoading || openRouterBalance !== -1) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
107776
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", color: "text.secondary" }, "\u2022"),
107777
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", color: openRouterBalance !== null ? 'text.primary' : 'text.secondary' },
107778
+ "\u043B\u0438\u043C\u0438\u0442 \u043A\u043B\u044E\u0447\u0430:",
107779
+ ' ',
107780
+ openRouterBalanceLoading ? '…' : openRouterBalance === null ? 'N/A' : `${openRouterBalance.toFixed(4)}`))))),
107781
+ openaiApiKey && flexcardApiKey.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", color: "text.secondary" }, "\u2022")),
107782
+ flexcardApiKey.trim() && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { component: "span", variant: "body2", color: flexCardUsdTotal !== null ? 'text.primary' : 'text.secondary' },
107783
+ "flexcard",
107784
+ ' ',
107785
+ flexCardBalanceLoading
107786
+ ? '…'
107787
+ : flexCardUsdTotal === null
107788
+ ? 'N/A'
107789
+ : `$${flexCardUsdTotal.toFixed(2)}`)))),
107399
107790
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], { direction: "row", spacing: 2, alignItems: "center", sx: { mt: 2 }, flexWrap: "wrap", useFlexGap: true },
107400
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["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_19__["default"], { size: 20, color: "inherit" }) : (accessToken ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_47__["default"], null) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_55__["default"], null)), sx: { flexGrow: 1, minWidth: 200 } }, authLoading ? 'Logging in...' : (accessToken ? 'Logged In' : 'Login with Google')),
107791
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["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_19__["default"], { size: 20, color: "inherit" }) : (accessToken ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_47__["default"], null) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_55__["default"], null)), sx: { flexGrow: 1, minWidth: 200 } }, authLoading ? 'Logging in...' : (accessToken ? 'Logged In' : 'Login with Google')),
107401
107792
  authLoading && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
107402
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { variant: "outlined", color: "primary", onClick: () => void handleReopenAuthBrowser(), startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_58__["default"], null) }, "\u041E\u0442\u043A\u0440\u044B\u0442\u044C \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0443 \u0432\u0445\u043E\u0434\u0430 \u0441\u043D\u043E\u0432\u0430"),
107403
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { variant: "outlined", color: "inherit", onClick: () => void handleCancelPendingAuth(), startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_48__["default"], null) }, "\u041E\u0442\u043C\u0435\u043D\u0438\u0442\u044C \u0432\u0445\u043E\u0434"))),
107404
- accessToken && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { variant: "outlined", color: "error", onClick: handleLogout, startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_56__["default"], null) }, "Logout")))),
107793
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { variant: "outlined", color: "primary", onClick: () => void handleReopenAuthBrowser(), startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_58__["default"], null) }, "\u041E\u0442\u043A\u0440\u044B\u0442\u044C \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0443 \u0432\u0445\u043E\u0434\u0430 \u0441\u043D\u043E\u0432\u0430"),
107794
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { variant: "outlined", color: "inherit", onClick: () => void handleCancelPendingAuth(), startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_48__["default"], null) }, "\u041E\u0442\u043C\u0435\u043D\u0438\u0442\u044C \u0432\u0445\u043E\u0434"))),
107795
+ accessToken && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { variant: "outlined", color: "error", onClick: handleLogout, startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_56__["default"], null) }, "Logout")))),
107405
107796
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { sx: { my: 2 } }),
107406
107797
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], null,
107407
107798
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "h6", gutterBottom: true }, "Google Drive Folder"),
107408
107799
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], { direction: { xs: 'column', sm: 'row' }, spacing: 2 },
107409
107800
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { flexGrow: 1 } },
107410
107801
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { label: "Google Drive Folder URL", variant: "outlined", fullWidth: true, value: driveFolderUrl, onChange: (e) => setDriveFolderUrl(e.target.value), placeholder: "https://drive.google.com/drive/folders/...", InputProps: {
107411
- endAdornment: (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { position: "end" },
107802
+ endAdornment: (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { position: "end" },
107412
107803
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_9__["default"], { title: "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0438\u0437 \u0431\u0443\u0444\u0435\u0440\u0430 \u043E\u0431\u043C\u0435\u043D\u0430" },
107413
107804
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_10__["default"], { edge: "end", "aria-label": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0441\u0441\u044B\u043B\u043A\u0443 \u043D\u0430 \u043F\u0430\u043F\u043A\u0443 \u0438\u0437 \u0431\u0443\u0444\u0435\u0440\u0430", onClick: () => void handlePasteDriveFolderUrl() },
107414
107805
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_51__["default"], null)))))
@@ -107447,7 +107838,7 @@ ${imageData.originalPrompt}
107447
107838
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { flex: 1, minWidth: 0 } },
107448
107839
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { label: "Link", variant: "outlined", fullWidth: true, value: link, onChange: (e) => handleLinkChange(e.target.value), onBlur: handleLinkBlur, error: !!linkError, helperText: linkError, placeholder: "https://example.com/product/", inputRef: linkInputRef, InputProps: {
107449
107840
  onPaste: handleLinkPaste,
107450
- endAdornment: (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { position: "end" },
107841
+ endAdornment: (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { position: "end" },
107451
107842
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_9__["default"], { title: "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0438\u0437 \u0431\u0443\u0444\u0435\u0440\u0430 \u043E\u0431\u043C\u0435\u043D\u0430" },
107452
107843
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_10__["default"], { edge: "end", "aria-label": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0441\u0441\u044B\u043B\u043A\u0443 \u043D\u0430 \u0442\u043E\u0432\u0430\u0440 \u0438\u0437 \u0431\u0443\u0444\u0435\u0440\u0430", onClick: () => void handlePasteProductLink() },
107453
107844
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_51__["default"], null)))))
@@ -107455,8 +107846,8 @@ ${imageData.originalPrompt}
107455
107846
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', gap: 1, alignItems: 'flex-start', mt: 1.5 } },
107456
107847
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { label: "\u0414\u043E\u043F. \u043C\u0430\u043A\u0440\u043E\u0441\u044B (\u043A\u0430\u0442\u0430\u043B\u043E\u0433)", variant: "outlined", size: "small", fullWidth: true, multiline: true, minRows: 2, value: catalogLinkExtraMacros, onChange: (e) => setCatalogLinkExtraMacros(e.target.value), placeholder: DEFAULT_CATALOG_LINK_EXTRA_MACROS, helperText: "\u0414\u043E\u0431\u0430\u0432\u043B\u044F\u0435\u0442\u0441\u044F \u043F\u043E\u0441\u043B\u0435 creative_id \u0438 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u043E\u0432 \u043F\u043E\u0434\u0445\u043E\u0434\u043E\u0432 (sub18, sub19, sub20 \u043F\u0440\u0438 \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u0438). \u0421\u043E\u0445\u0440\u0430\u043D\u044F\u0435\u0442\u0441\u044F \u0432 JSON \u043D\u0430\u0441\u0442\u0440\u043E\u0435\u043A \u043F\u0430\u043F\u043A\u0438.", sx: { '& .MuiInputBase-input': { fontFamily: 'monospace', fontSize: 12 } } }),
107457
107848
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], { spacing: 0.5, sx: { flexShrink: 0, mt: 0.5 } },
107458
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { size: "small", variant: "outlined", onClick: () => setCatalogLinkExtraMacros(DEFAULT_CATALOG_LINK_EXTRA_MACROS), sx: { textTransform: 'none', whiteSpace: 'nowrap' } }, "redtrack"),
107459
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { size: "small", variant: "outlined", onClick: () => setCatalogLinkExtraMacros(DEFAULT_CATALOG_LINK_KEITARO_MACROS), sx: { textTransform: 'none', whiteSpace: 'nowrap' } }, "keitaro"))),
107849
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { size: "small", variant: "outlined", onClick: () => setCatalogLinkExtraMacros(DEFAULT_CATALOG_LINK_EXTRA_MACROS), sx: { textTransform: 'none', whiteSpace: 'nowrap' } }, "redtrack"),
107850
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { size: "small", variant: "outlined", onClick: () => setCatalogLinkExtraMacros(DEFAULT_CATALOG_LINK_KEITARO_MACROS), sx: { textTransform: 'none', whiteSpace: 'nowrap' } }, "keitaro"))),
107460
107851
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], { spacing: 1.5, sx: { mt: 1 } },
107461
107852
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], { direction: { xs: 'column', sm: 'row' }, spacing: 2, alignItems: { xs: 'stretch', sm: 'flex-start' } },
107462
107853
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { control: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_25__["default"], { checked: catalogUrlIncludeTextApproach, onChange: e => setCatalogUrlIncludeTextApproach(e.target.checked), size: "small" }), label: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], null,
@@ -107480,7 +107871,7 @@ ${imageData.originalPrompt}
107480
107871
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], { direction: "row", spacing: 2, sx: { mb: 2 } },
107481
107872
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { label: "\u0413\u0435\u043E", variant: "outlined", fullWidth: true, value: generateGeo, onChange: (e) => setGenerateGeo(e.target.value), placeholder: "\u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440: \u0420\u0443\u043C\u044B\u043D\u0438\u044F" }),
107482
107873
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__["default"], { label: "\u0426\u0435\u043D\u0430 \u0438 \u0432\u0430\u043B\u044E\u0442\u0430", variant: "outlined", fullWidth: true, value: generatePriceWithCurrency, onChange: (e) => setGeneratePriceWithCurrency(e.target.value), placeholder: "\u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440: 29 euro, 11400 HUF, 149 RON \u0438\u043B\u0438 \u043B\u044E\u0431\u043E\u0439 \u0434\u0440\u0443\u0433\u043E\u0439 \u0444\u043E\u0440\u043C\u0430\u0442", helperText: "\u041B\u044E\u0431\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0434\u043B\u044F \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u044F \u0432 \u043F\u0440\u043E\u043C\u043F\u0442\u0435 \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0439. \u041A\u043D\u043E\u043F\u043A\u0438 \u0441\u043F\u0440\u0430\u0432\u0430 \u043F\u043E\u0434\u0441\u0442\u0430\u0432\u043B\u044F\u044E\u0442 \u0441\u0438\u043C\u0432\u043E\u043B \u0432\u0430\u043B\u044E\u0442\u044B, \u0446\u0438\u0444\u0440\u0443 \u0431\u0435\u0440\u0443\u0442 \u0438\u0437 \u043F\u043E\u043B\u044F (\u0438\u043B\u0438 99).", InputProps: {
107483
- endAdornment: (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { position: "end" },
107874
+ endAdornment: (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { position: "end" },
107484
107875
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 0.25, mr: -0.5 } },
107485
107876
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_9__["default"], { title: "\u041F\u043E\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044C $ (\u0446\u0438\u0444\u0440\u0430 \u0438\u0437 \u043F\u043E\u043B\u044F \u0438\u043B\u0438 99)" },
107486
107877
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_10__["default"], { size: "small", "aria-label": "\u0411\u044B\u0441\u0442\u0440\u043E \u0434\u043E\u043B\u043B\u0430\u0440", onClick: () => {
@@ -107505,10 +107896,10 @@ ${imageData.originalPrompt}
107505
107896
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], { direction: { xs: 'column', md: 'row' }, spacing: 2, sx: { mb: 2, alignItems: 'stretch' } },
107506
107897
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_26__["default"], { fullWidth: true, variant: "outlined", sx: { flex: { md: '1 1 0' }, minWidth: { md: 0 } } },
107507
107898
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_27__["default"], null, "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u043A\u0432\u0430\u0434\u0440\u0430\u0442\u043E\u0432 (1:1)"),
107508
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { value: selectedImageModelSquare, onChange: (e) => handleImageModelSquareChange(e.target.value), label: "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u043A\u0432\u0430\u0434\u0440\u0430\u0442\u043E\u0432 (1:1)", disabled: loadingImageModels || imageModels.length === 0 }, loadingImageModels ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { disabled: true },
107899
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { value: imageModels.some(m => m.id === selectedImageModelSquare) ? selectedImageModelSquare : '', onChange: (e) => handleImageModelSquareChange(e.target.value), label: "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u043A\u0432\u0430\u0434\u0440\u0430\u0442\u043E\u0432 (1:1)", disabled: loadingImageModels || imageModels.length === 0 }, loadingImageModels ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { value: "", disabled: true },
107509
107900
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
107510
107901
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { size: 16 }),
107511
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u043C\u043E\u0434\u0435\u043B\u0435\u0439...")))) : imageModels.length === 0 ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { disabled: true }, "\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0445 \u043C\u043E\u0434\u0435\u043B\u0435\u0439")) : (imageModels.map((model) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { key: model.id, value: model.id }, model.name))))),
107902
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u043C\u043E\u0434\u0435\u043B\u0435\u0439...")))) : imageModels.length === 0 ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { value: "", disabled: true }, "\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0445 \u043C\u043E\u0434\u0435\u043B\u0435\u0439")) : (imageModels.map((model) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { key: model.id, value: model.id }, model.name))))),
107512
107903
  !loadingImageModels && imageModels.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], null, selectedImageModelSquare === _models__WEBPACK_IMPORTED_MODULE_3__.MODELS.imageGeneration
107513
107904
  ? 'По умолчанию для квадрата'
107514
107905
  : '1:1: ' +
@@ -107516,10 +107907,10 @@ ${imageData.originalPrompt}
107516
107907
  selectedImageModelSquare)))),
107517
107908
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_26__["default"], { fullWidth: true, variant: "outlined", sx: { flex: { md: '1 1 0' }, minWidth: { md: 0 } } },
107518
107909
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_27__["default"], null, "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u043F\u0440\u044F\u043C\u043E\u0443\u0433\u043E\u043B\u044C\u043D\u0438\u043A\u043E\u0432 (2:3)"),
107519
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { value: selectedImageModelRect, onChange: (e) => handleImageModelRectChange(e.target.value), label: "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u043F\u0440\u044F\u043C\u043E\u0443\u0433\u043E\u043B\u044C\u043D\u0438\u043A\u043E\u0432 (2:3)", disabled: loadingImageModels || imageModels.length === 0 }, loadingImageModels ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { disabled: true },
107910
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { value: imageModels.some(m => m.id === selectedImageModelRect) ? selectedImageModelRect : '', onChange: (e) => handleImageModelRectChange(e.target.value), label: "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u043F\u0440\u044F\u043C\u043E\u0443\u0433\u043E\u043B\u044C\u043D\u0438\u043A\u043E\u0432 (2:3)", disabled: loadingImageModels || imageModels.length === 0 }, loadingImageModels ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { value: "", disabled: true },
107520
107911
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
107521
107912
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { size: 16 }),
107522
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u043C\u043E\u0434\u0435\u043B\u0435\u0439...")))) : imageModels.length === 0 ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { disabled: true }, "\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0445 \u043C\u043E\u0434\u0435\u043B\u0435\u0439")) : (imageModels.map((model) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { key: model.id, value: model.id }, model.name))))),
107913
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u043C\u043E\u0434\u0435\u043B\u0435\u0439...")))) : imageModels.length === 0 ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { value: "", disabled: true }, "\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0445 \u043C\u043E\u0434\u0435\u043B\u0435\u0439")) : (imageModels.map((model) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { key: model.id, value: model.id }, model.name))))),
107523
107914
  !loadingImageModels && imageModels.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], null, selectedImageModelRect === _models__WEBPACK_IMPORTED_MODULE_3__.MODELS.imageGeneration
107524
107915
  ? 'По умолчанию для 2:3'
107525
107916
  : '2:3: ' +
@@ -107527,25 +107918,29 @@ ${imageData.originalPrompt}
107527
107918
  selectedImageModelRect)))),
107528
107919
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_26__["default"], { fullWidth: true, variant: "outlined", sx: { flex: { md: '1 1 0' }, minWidth: { md: 0 } } },
107529
107920
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_27__["default"], null, "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u043A\u0440\u0435\u0430\u0442\u0438\u0432\u043E\u0432"),
107530
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { value: selectedValidationModel, onChange: (e) => handleValidationModelChange(e.target.value), label: "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u043A\u0440\u0435\u0430\u0442\u0438\u0432\u043E\u0432", disabled: loadingValidationModels || validationModels.length === 0 }, loadingValidationModels ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { disabled: true },
107921
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { value: validationModels.some(m => m.id === selectedValidationModel) ? selectedValidationModel : '', onChange: (e) => handleValidationModelChange(e.target.value), label: "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u043A\u0440\u0435\u0430\u0442\u0438\u0432\u043E\u0432", disabled: loadingValidationModels || validationModels.length === 0 }, loadingValidationModels ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { value: "", disabled: true },
107531
107922
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
107532
107923
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { size: 16 }),
107533
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u043C\u043E\u0434\u0435\u043B\u0435\u0439...")))) : validationModels.length === 0 ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { disabled: true }, "\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0445 \u043C\u043E\u0434\u0435\u043B\u0435\u0439")) : (validationModels.map((model) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { key: model.id, value: model.id }, model.name))))),
107924
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u043C\u043E\u0434\u0435\u043B\u0435\u0439...")))) : validationModels.length === 0 ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { value: "", disabled: true }, "\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0445 \u043C\u043E\u0434\u0435\u043B\u0435\u0439")) : (validationModels.map((model) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { key: model.id, value: model.id }, model.name))))),
107534
107925
  !loadingValidationModels && validationModels.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], null, selectedValidationModel === _models__WEBPACK_IMPORTED_MODULE_3__.MODELS.creativeValidation
107535
107926
  ? 'Используется модель по умолчанию'
107536
107927
  : 'Выбрана модель: ' + (validationModels.find(m => m.id === selectedValidationModel)?.name || selectedValidationModel))),
107537
107928
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { control: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_25__["default"], { checked: validationDisabled, onChange: (e) => handleValidationDisabledChange(e.target.checked), color: "primary" }), label: "\u041E\u0442\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443", sx: { mt: 1 } }),
107538
107929
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { control: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_25__["default"], { checked: autoRemakeOnValidatorError, onChange: (e) => handleAutoRemakeOnValidatorErrorChange(e.target.checked), color: "primary", disabled: validationDisabled }), label: "\u0430\u0432\u0442\u043E\u043F\u0435\u0440\u0435\u0434\u0435\u043B\u043A\u0430", sx: { mt: 0.5, display: 'block' } }),
107930
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { control: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_25__["default"], { checked: uploadAfterValidatorOk, onChange: (e) => handleUploadAfterValidatorOkChange(e.target.checked), color: "primary", disabled: validationDisabled }), label: "\u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044C \u043F\u043E\u0441\u043B\u0435 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438", sx: { mt: 0.5, display: 'block' } }),
107539
107931
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { sx: { mt: 0, mx: 0 } }, validationDisabled
107540
107932
  ? 'Недоступно, пока проверка отключена.'
107541
- : 'Один раз на слот креатива: при сбое проверки (сеть, таймаут, ошибка API) или когда валидатор вернул «нужна пересборка» с замечаниями — автоматически одна полная переделка с нуля (исходный промпт + product, без правок по списку ошибок).'))),
107933
+ : 'Один раз на слот креатива: при сбое проверки (сеть, таймаут, ошибка API) или когда валидатор вернул «нужна пересборка» с замечаниями — автоматически одна полная переделка с нуля (исходный промпт + product, без правок по списку ошибок).'),
107934
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], { sx: { mt: 0.5, mx: 0 } }, validationDisabled
107935
+ ? ''
107936
+ : '«Загружать после проверки»: при ответе валидатора OK файл сразу загружается в папку оффера на Google Drive (нужны URL папки и вход в Google).'))),
107542
107937
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], { direction: { xs: 'column', md: 'row' }, spacing: 2, sx: { mb: 2, alignItems: 'stretch' } },
107543
107938
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_26__["default"], { fullWidth: true, variant: "outlined", sx: { flex: { md: '1 1 0' }, minWidth: { md: 0 } } },
107544
107939
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_27__["default"], null, "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u043E\u0432 \u0438 \u0442\u0435\u043A\u0441\u0442\u043E\u0432"),
107545
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { value: selectedContentModel, onChange: (e) => handleContentModelChange(e.target.value), label: "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u043E\u0432 \u0438 \u0442\u0435\u043A\u0441\u0442\u043E\u0432", disabled: loadingValidationModels || chatModels.length === 0 }, loadingValidationModels ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { disabled: true },
107940
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { value: chatModels.some(m => m.id === selectedContentModel) ? selectedContentModel : '', onChange: (e) => handleContentModelChange(e.target.value), label: "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u043E\u0432 \u0438 \u0442\u0435\u043A\u0441\u0442\u043E\u0432", disabled: loadingValidationModels || chatModels.length === 0 }, loadingValidationModels ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { value: "", disabled: true },
107546
107941
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
107547
107942
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { size: 16 }),
107548
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u043C\u043E\u0434\u0435\u043B\u0435\u0439...")))) : chatModels.length === 0 ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { disabled: true }, "\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0445 \u043C\u043E\u0434\u0435\u043B\u0435\u0439")) : (chatModels.map((model) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { key: model.id, value: model.id }, model.name))))),
107943
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u043C\u043E\u0434\u0435\u043B\u0435\u0439...")))) : chatModels.length === 0 ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { value: "", disabled: true }, "\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0445 \u043C\u043E\u0434\u0435\u043B\u0435\u0439")) : (chatModels.map((model) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { key: model.id, value: model.id }, model.name))))),
107549
107944
  !loadingValidationModels && chatModels.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], null, selectedContentModel === _models__WEBPACK_IMPORTED_MODULE_3__.MODELS.contentGeneration
107550
107945
  ? 'Заголовки, описания, пары и перевод — модель по умолчанию из кода'
107551
107946
  : 'Заголовки и тексты: ' +
@@ -107553,17 +107948,17 @@ ${imageData.originalPrompt}
107553
107948
  selectedContentModel)))),
107554
107949
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_26__["default"], { fullWidth: true, variant: "outlined", sx: { flex: { md: '1 1 0' }, minWidth: { md: 0 } } },
107555
107950
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_27__["default"], null, "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u043B\u0435\u043D\u0434\u0438\u043D\u0433\u0430"),
107556
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { value: selectedLandingModel, onChange: (e) => handleLandingModelChange(e.target.value), label: "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u043B\u0435\u043D\u0434\u0438\u043D\u0433\u0430", disabled: loadingValidationModels || chatModels.length === 0 }, loadingValidationModels ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { disabled: true },
107951
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_28__["default"], { value: chatModels.some(m => m.id === selectedLandingModel) ? selectedLandingModel : '', onChange: (e) => handleLandingModelChange(e.target.value), label: "\u041C\u043E\u0434\u0435\u043B\u044C \u0434\u043B\u044F \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0438 \u043B\u0435\u043D\u0434\u0438\u043D\u0433\u0430", disabled: loadingValidationModels || chatModels.length === 0 }, loadingValidationModels ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { value: "", disabled: true },
107557
107952
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
107558
107953
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { size: 16 }),
107559
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u043C\u043E\u0434\u0435\u043B\u0435\u0439...")))) : chatModels.length === 0 ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { disabled: true }, "\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0445 \u043C\u043E\u0434\u0435\u043B\u0435\u0439")) : (chatModels.map((model) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { key: model.id, value: model.id }, model.name))))),
107954
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u043C\u043E\u0434\u0435\u043B\u0435\u0439...")))) : chatModels.length === 0 ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { value: "", disabled: true }, "\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0445 \u043C\u043E\u0434\u0435\u043B\u0435\u0439")) : (chatModels.map((model) => (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_29__["default"], { key: model.id, value: model.id }, model.name))))),
107560
107955
  !loadingValidationModels && chatModels.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__["default"], null, selectedLandingModel === _models__WEBPACK_IMPORTED_MODULE_3__.MODELS.landingGeneration
107561
107956
  ? 'HTML лендинга — модель по умолчанию из кода'
107562
107957
  : 'Лендинг: ' +
107563
107958
  (chatModels.find(m => m.id === selectedLandingModel)?.name ||
107564
107959
  selectedLandingModel))))))),
107565
107960
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], { direction: "row", spacing: 2, sx: { mb: 2 } },
107566
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { variant: "contained", color: "primary", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_43__["default"], null), onClick: handleGenerateContent, disabled: generating || loadingContentFromDrive || !openaiApiKey || !generateProduct.trim() || !generateGeo.trim(), sx: { flexGrow: 1 }, size: "large" }, generating ? 'Generating...' : 'Generate Titles & Descriptions'),
107961
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { variant: "contained", color: "primary", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_43__["default"], null), onClick: handleGenerateContent, disabled: generating || loadingContentFromDrive || !openaiApiKey || !generateProduct.trim() || !generateGeo.trim(), sx: { flexGrow: 1 }, size: "large" }, generating ? 'Generating...' : 'Generate Titles & Descriptions'),
107567
107962
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], { direction: "row", spacing: 1, alignItems: "center", sx: { flexGrow: 1 } },
107568
107963
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_9__["default"], { title: imageAspectRatio === '1:1'
107569
107964
  ? '1:1 — квадрат (1024×1024 px)'
@@ -107590,7 +107985,7 @@ ${imageData.originalPrompt}
107590
107985
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_31__["default"], { value: "1:1", sx: { px: 1.5, fontWeight: 600, fontSize: '0.8rem' } }, "1:1"),
107591
107986
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_31__["default"], { value: "2:3", sx: { px: 1.5, fontWeight: 600, fontSize: '0.8rem' } }, "2:3"),
107592
107987
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_31__["default"], { value: "both", sx: { px: 1.5, fontWeight: 600, fontSize: '0.8rem' } }, "\u041E\u0431\u0430")))),
107593
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { variant: "contained", color: "secondary", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_43__["default"], null), onClick: handleGenerateImages, disabled: generatingImages ||
107988
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { variant: "contained", color: "secondary", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_43__["default"], null), onClick: handleGenerateImages, disabled: generatingImages ||
107594
107989
  !openaiApiKey ||
107595
107990
  (!accessToken && !refreshToken) ||
107596
107991
  !generateProduct.trim() ||
@@ -107608,7 +108003,7 @@ ${imageData.originalPrompt}
107608
108003
  : !driveFolderUrl.trim()
107609
108004
  ? 'Заполните URL папки Google Drive'
107610
108005
  : undefined }, generatingImages ? 'Generating Images...' : 'Generate Images')),
107611
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { variant: "contained", color: "success", size: "large", onClick: handleGenerate, disabled: loading, startIcon: loading ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { size: 20, color: "inherit" }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_49__["default"], null), sx: { py: 1.5, fontSize: '1.1rem', flexGrow: 1 } }, loading ? 'Generating...' : 'Generate Catalog'),
108006
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { variant: "contained", color: "success", size: "large", onClick: handleGenerate, disabled: loading, startIcon: loading ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { size: 20, color: "inherit" }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_49__["default"], null), sx: { py: 1.5, fontSize: '1.1rem', flexGrow: 1 } }, loading ? 'Generating...' : 'Generate Catalog'),
107612
108007
  uploadedLink && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { severity: "success", sx: { flexGrow: 1, minWidth: 0 }, action: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_10__["default"], { "aria-label": "copy link", color: "inherit", size: "small", onClick: handleCopyLink, sx: { ml: 1 } },
107613
108008
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_50__["default"], { fontSize: "small" })) },
107614
108009
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1, flexWrap: 'wrap' } },
@@ -107629,8 +108024,8 @@ ${imageData.originalPrompt}
107629
108024
  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"))))),
107630
108025
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { type: "file", ref: productFileInputRef, accept: "image/png,image/jpeg,image/jpg,image/webp", style: { display: 'none' }, onChange: handleProductFileSelected }),
107631
108026
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], { direction: "row", spacing: 2, sx: { mb: 2 } },
107632
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { variant: "contained", color: "success", startIcon: uploadingProduct ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { size: 20, color: "inherit" }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_49__["default"], null), onClick: handleUploadProductFile, disabled: uploadingProduct || !accessToken || !driveFolderUrl.trim(), sx: { flexGrow: 1 }, size: "large" }, uploadingProduct ? 'Загрузка...' : 'Загрузить product.png/jpg/webp'),
107633
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { variant: "contained", color: "info", startIcon: generatingLanding ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { size: 20, color: "inherit" }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_57__["default"], null), onClick: handleCreateLanding, disabled: generatingLanding ||
108027
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { variant: "contained", color: "success", startIcon: uploadingProduct ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { size: 20, color: "inherit" }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_49__["default"], null), onClick: handleUploadProductFile, disabled: uploadingProduct || !accessToken || !driveFolderUrl.trim(), sx: { flexGrow: 1 }, size: "large" }, uploadingProduct ? 'Загрузка...' : 'Загрузить product.png/jpg/webp'),
108028
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { variant: "contained", color: "info", startIcon: generatingLanding ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { size: 20, color: "inherit" }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_57__["default"], null), onClick: handleCreateLanding, disabled: generatingLanding ||
107634
108029
  !openaiApiKey ||
107635
108030
  !accessToken ||
107636
108031
  !driveFolderUrl.trim() ||
@@ -107647,8 +108042,20 @@ ${imageData.originalPrompt}
107647
108042
  checkingImages && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1, mb: 2 } },
107648
108043
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { size: 16 }),
107649
108044
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", sx: { color: 'text.secondary' } }, "\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043A\u0430\u0447\u0435\u0441\u0442\u0432\u0430..."))),
107650
- generatedImagesData.length > 0 && generatedImagesData.some(img => !img.uploaded && img.imageUrl) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { mb: 2 } },
107651
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["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_19__["default"], { size: 20 }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_49__["default"], null), fullWidth: true }, uploadingImages ? 'Загрузка...' : `Загрузить все (${generatedImagesData.filter(img => !img.uploaded && img.imageUrl).length})`))),
108045
+ pendingDriveUploadCreos.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], { direction: { xs: 'column', sm: 'row' }, spacing: 1, sx: { mb: 2 }, useFlexGap: true },
108046
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { variant: "contained", color: "primary", onClick: () => void handleUploadAllImages(), disabled: uploadingImages || generatingImages || !driveFolderUrl.trim(), startIcon: uploadingImages ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { size: 20 }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_49__["default"], null), fullWidth: true, sx: { flex: 1 } }, uploadingImages
108047
+ ? 'Загрузка...'
108048
+ : `Загрузить все (${pendingDriveUploadCreos.length})`),
108049
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_9__["default"], { title: pendingValidatorOkDriveUploadCreos.length === 0
108050
+ ? 'Нет незагруженных крео с успешной проверкой валидатором (ожидайте проверки или исправьте ошибки).'
108051
+ : 'Только крео со статусом «Проверка пройдена», как в карточке (валидатор вернул OK).' },
108052
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { style: { flex: 1, display: 'flex' } },
108053
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { variant: "contained", color: "success", onClick: () => void handleUploadAllImages({ correctOnly: true }), disabled: uploadingImages ||
108054
+ generatingImages ||
108055
+ !driveFolderUrl.trim() ||
108056
+ pendingValidatorOkDriveUploadCreos.length === 0, startIcon: uploadingImages ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { size: 20 }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_47__["default"], null), fullWidth: true, sx: { flex: 1 } }, uploadingImages
108057
+ ? 'Загрузка...'
108058
+ : `Загрузить правильные (${pendingValidatorOkDriveUploadCreos.length})`))))),
107652
108059
  generatedImagesData.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: {
107653
108060
  display: 'grid',
107654
108061
  gridTemplateColumns: { xs: '1fr', sm: 'repeat(2, 1fr)', md: 'repeat(3, 1fr)' },
@@ -107797,13 +108204,13 @@ ${imageData.originalPrompt}
107797
108204
  } },
107798
108205
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", color: "text.secondary", align: "center", sx: { px: 1 } }, "\u0417\u0430\u0433\u0440\u0443\u0436\u0435\u043D\u043E \u043D\u0430 Google \u0414\u0438\u0441\u043A \u2014 \u043F\u0440\u0435\u0432\u044C\u044E \u0441\u043A\u0440\u044B\u0442\u043E"),
107799
108206
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], { direction: "row", spacing: 1, flexWrap: "wrap", justifyContent: "center" },
107800
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { size: "small", variant: "contained", onClick: () => setGeneratedImagesData(prev => prev.map(img => img.index === imageData.index
108207
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { size: "small", variant: "contained", onClick: () => setGeneratedImagesData(prev => prev.map(img => img.index === imageData.index
107801
108208
  ? { ...img, uploadedPreviewHidden: false }
107802
108209
  : img)) }, "\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u043F\u0440\u0435\u0432\u044C\u044E"),
107803
- imageData.driveUploadedFileId ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { size: "small", variant: "outlined", color: "error", disabled: deletingDriveImageIndex === imageData.index, onClick: () => void handleDeleteCreativeFromDrive(imageData) }, deletingDriveImageIndex === imageData.index
108210
+ imageData.driveUploadedFileId ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { size: "small", variant: "outlined", color: "error", disabled: deletingDriveImageIndex === imageData.index, onClick: () => void handleDeleteCreativeFromDrive(imageData) }, deletingDriveImageIndex === imageData.index
107804
108211
  ? 'Удаление…'
107805
108212
  : 'Удалить с Диска')) : null,
107806
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { size: "small", variant: "outlined", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_54__["default"], null), onClick: () => setCreoFullscreen({
108213
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { size: "small", variant: "outlined", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_54__["default"], null), onClick: () => setCreoFullscreen({
107807
108214
  url: imageData.imageUrl,
107808
108215
  title: `${imageData.index}. ${imageData.approach}`
107809
108216
  }), disabled: !!imageData.uploading || deletingDriveImageIndex === imageData.index }, "\u041D\u0430 \u0432\u0435\u0441\u044C \u044D\u043A\u0440\u0430\u043D")))) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
@@ -107829,10 +108236,10 @@ ${imageData.originalPrompt}
107829
108236
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { size: 40, sx: { color: 'primary.main' } }),
107830
108237
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", sx: { color: 'white', fontWeight: 'bold', textShadow: '1px 1px 2px rgba(0,0,0,0.8)' } }, "\u041F\u0435\u0440\u0435\u0434\u0435\u043B\u043A\u0430..."))),
107831
108238
  imageData.uploaded && !imageData.regenerating ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', justifyContent: 'center', mt: 0.5 } },
107832
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { size: "small", variant: "text", onClick: () => setGeneratedImagesData(prev => prev.map(img => img.index === imageData.index
108239
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { size: "small", variant: "text", onClick: () => setGeneratedImagesData(prev => prev.map(img => img.index === imageData.index
107833
108240
  ? { ...img, uploadedPreviewHidden: true }
107834
108241
  : img)) }, "\u0421\u043A\u0440\u044B\u0442\u044C \u043F\u0440\u0435\u0432\u044C\u044E"),
107835
- imageData.driveUploadedFileId ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { size: "small", variant: "outlined", color: "error", sx: { ml: 1 }, disabled: deletingDriveImageIndex === imageData.index, onClick: () => void handleDeleteCreativeFromDrive(imageData) }, deletingDriveImageIndex === imageData.index
108242
+ imageData.driveUploadedFileId ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { size: "small", variant: "outlined", color: "error", sx: { ml: 1 }, disabled: deletingDriveImageIndex === imageData.index, onClick: () => void handleDeleteCreativeFromDrive(imageData) }, deletingDriveImageIndex === imageData.index
107836
108243
  ? 'Удаление…'
107837
108244
  : 'Удалить с Диска')) : null)) : null)))) : imageData.failed ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: {
107838
108245
  width: '100%',
@@ -107910,7 +108317,7 @@ ${imageData.originalPrompt}
107910
108317
  : '');
107911
108318
  if (!body)
107912
108319
  return null;
107913
- return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { size: "small", variant: "text", sx: { mt: 0.5, p: 0, minHeight: 'auto', fontSize: '0.7rem', textTransform: 'none', alignSelf: 'flex-start', display: 'block' }, onClick: () => setValidatorDebugDialog({
108320
+ return (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { size: "small", variant: "text", sx: { mt: 0.5, p: 0, minHeight: 'auto', fontSize: '0.7rem', textTransform: 'none', alignSelf: 'flex-start', display: 'block' }, onClick: () => setValidatorDebugDialog({
107914
108321
  kind: imageData.checkFailed ? 'transport' : 'validator',
107915
108322
  subtitle: `${imageData.index}. ${imageData.approach}`,
107916
108323
  text: body
@@ -107939,7 +108346,7 @@ ${imageData.originalPrompt}
107939
108346
  }
107940
108347
  } }),
107941
108348
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], { direction: "row", spacing: 1, sx: { width: '100%', alignItems: 'stretch' } },
107942
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["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_19__["default"], { size: 16 }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_43__["default"], null), onClick: () => handleRegenerateImage(imageData), disabled: imageData.regenerating ||
108349
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["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_19__["default"], { size: 16 }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_43__["default"], null), onClick: () => handleRegenerateImage(imageData), disabled: imageData.regenerating ||
107943
108350
  imageData.uploading ||
107944
108351
  imageData.checkStatus === 'checking' ||
107945
108352
  !imageData.originalPrompt ||
@@ -107949,24 +108356,36 @@ ${imageData.originalPrompt}
107949
108356
  : ((generatingImages && !imageData.imageUrl && !imageData.failed && !imageData.generating)
107950
108357
  ? 'В очереди'
107951
108358
  : (!imageData.imageUrl ? 'Сгенерировать' : 'Переделать'))),
107952
- imageData.imageUrl ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { size: "small", variant: "outlined", color: "secondary", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_59__["default"], null), onClick: () => handleRegenerateImageFresh(imageData), disabled: imageData.regenerating ||
108359
+ imageData.imageUrl ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { size: "small", variant: "outlined", color: "secondary", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_59__["default"], null), onClick: () => handleRegenerateImageFresh(imageData), disabled: imageData.regenerating ||
107953
108360
  imageData.uploading ||
107954
108361
  imageData.checkStatus === 'checking' ||
107955
108362
  !imageData.originalPrompt ||
107956
108363
  !imageData.productImageUrl, sx: { flex: 1, minWidth: 0, py: 1, lineHeight: 1.2, whiteSpace: 'normal' } }, "\u041F\u0435\u0440\u0435\u0434\u0435\u043B\u0430\u0442\u044C \u0437\u0430\u043D\u043E\u0432\u043E")) : null),
107957
- imageData.imageUrl && !validationDisabled && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { size: "small", variant: "outlined", color: "warning", startIcon: imageData.checkStatus === 'checking' ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { size: 16, color: "inherit" })) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_59__["default"], null)), onClick: () => handleRetryCheck(imageData), disabled: imageData.regenerating ||
108364
+ imageData.imageUrl && !validationDisabled && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { size: "small", variant: "outlined", color: "warning", startIcon: imageData.checkStatus === 'checking' ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { size: 16, color: "inherit" })) : (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_59__["default"], null)), onClick: () => handleRetryCheck(imageData), disabled: imageData.regenerating ||
107958
108365
  imageData.uploading ||
107959
108366
  imageData.generating ||
107960
108367
  imageData.checkStatus === 'checking', fullWidth: true }, imageData.checkStatus === 'checking' ? 'Проверка…' : 'Перепроверить')),
107961
- !imageData.uploaded && imageData.imageUrl && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { size: "small", variant: "outlined", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_49__["default"], null), onClick: () => {
108368
+ !imageData.uploaded && imageData.imageUrl && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { size: "small", variant: "outlined", startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_49__["default"], null), onClick: () => {
107962
108369
  const folderId = extractFolderId(driveFolderUrl);
107963
108370
  if (folderId) {
107964
108371
  handleUploadImage(imageData, folderId);
107965
108372
  }
107966
108373
  }, disabled: imageData.uploading || imageData.regenerating || !driveFolderUrl.trim(), fullWidth: true }, imageData.uploading ? 'Загрузка...' : 'Загрузить'))))));
107967
108374
  }))),
107968
- generatedImagesData.length > 0 && generatedImagesData.some(img => !img.uploaded && img.imageUrl) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { mt: 2 } },
107969
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["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_19__["default"], { size: 20 }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_49__["default"], null), fullWidth: true }, uploadingImages ? 'Загрузка...' : `Загрузить все (${generatedImagesData.filter(img => !img.uploaded && img.imageUrl).length})`))),
108375
+ pendingDriveUploadCreos.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], { direction: { xs: 'column', sm: 'row' }, spacing: 1, sx: { mt: 2 }, useFlexGap: true },
108376
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { variant: "contained", color: "primary", onClick: () => void handleUploadAllImages(), disabled: uploadingImages || generatingImages || !driveFolderUrl.trim(), startIcon: uploadingImages ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { size: 20 }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_49__["default"], null), fullWidth: true, sx: { flex: 1 } }, uploadingImages
108377
+ ? 'Загрузка...'
108378
+ : `Загрузить все (${pendingDriveUploadCreos.length})`),
108379
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_9__["default"], { title: pendingValidatorOkDriveUploadCreos.length === 0
108380
+ ? 'Нет незагруженных крео с успешной проверкой валидатором (ожидайте проверки или исправьте ошибки).'
108381
+ : 'Только крео со статусом «Проверка пройдена», как в карточке (валидатор вернул OK).' },
108382
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { style: { flex: 1, display: 'flex' } },
108383
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { variant: "contained", color: "success", onClick: () => void handleUploadAllImages({ correctOnly: true }), disabled: uploadingImages ||
108384
+ generatingImages ||
108385
+ !driveFolderUrl.trim() ||
108386
+ pendingValidatorOkDriveUploadCreos.length === 0, startIcon: uploadingImages ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { size: 20 }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_47__["default"], null), fullWidth: true, sx: { flex: 1 } }, uploadingImages
108387
+ ? 'Загрузка...'
108388
+ : `Загрузить правильные (${pendingValidatorOkDriveUploadCreos.length})`))))),
107970
108389
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_32__["default"], { fullScreen: true, open: Boolean(creoFullscreen), onClose: () => setCreoFullscreen(null), PaperProps: {
107971
108390
  sx: {
107972
108391
  m: 0,
@@ -108021,12 +108440,12 @@ ${imageData.originalPrompt}
108021
108440
  }
108022
108441
  } })),
108023
108442
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_35__["default"], { sx: { px: 2, py: 1.5 } },
108024
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_50__["default"], null), onClick: () => {
108443
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_50__["default"], null), onClick: () => {
108025
108444
  const t = validatorDebugDialog?.text;
108026
108445
  if (t)
108027
108446
  void navigator.clipboard.writeText(t);
108028
108447
  }, disabled: !validatorDebugDialog?.text }, "\u041A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C"),
108029
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { variant: "contained", onClick: () => setValidatorDebugDialog(null) }, "\u0417\u0430\u043A\u0440\u044B\u0442\u044C"))))),
108448
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { variant: "contained", onClick: () => setValidatorDebugDialog(null) }, "\u0417\u0430\u043A\u0440\u044B\u0442\u044C"))))),
108030
108449
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_21__["default"], { sx: { my: 2 } }),
108031
108450
  (generatedTitlesData.length > 0 || generatedTextsData.length > 0 || loadingContentFromDrive) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { mb: 2 } },
108032
108451
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 1, mb: 1 } },
@@ -108061,7 +108480,7 @@ ${imageData.originalPrompt}
108061
108480
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { size: 40, sx: { color: 'primary.main' } }),
108062
108481
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body1", sx: { color: 'text.secondary', fontWeight: 'bold' } }, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u043A\u043E\u043D\u0442\u0435\u043D\u0442\u0430 \u0438\u0437 Google Drive..."))),
108063
108482
  (generatedTitlesData.length > 0 || generatedTextsData.length > 0) && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], { spacing: 2 },
108064
- pairTranslationFailed && !translatingPairs && !generating ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { severity: "warning", action: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { color: "inherit", size: "small", onClick: handleRetryPairTranslation, sx: { textTransform: 'none', whiteSpace: 'nowrap' } }, "\u0417\u0430\u043F\u0440\u043E\u0441\u0438\u0442\u044C \u043F\u0435\u0440\u0435\u0432\u043E\u0434") }, "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u043F\u0435\u0440\u0435\u0432\u043E\u0434 \u043F\u0430\u0440 \u043D\u0430 \u0440\u0443\u0441\u0441\u043A\u0438\u0439 (\u043E\u0442\u0434\u0435\u043B\u044C\u043D\u044B\u0439 \u0437\u0430\u043F\u0440\u043E\u0441 \u043A \u043C\u043E\u0434\u0435\u043B\u0438).")) : null,
108483
+ pairTranslationFailed && !translatingPairs && !generating ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_20__["default"], { severity: "warning", action: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { color: "inherit", size: "small", onClick: handleRetryPairTranslation, sx: { textTransform: 'none', whiteSpace: 'nowrap' } }, "\u0417\u0430\u043F\u0440\u043E\u0441\u0438\u0442\u044C \u043F\u0435\u0440\u0435\u0432\u043E\u0434") }, "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u043F\u0435\u0440\u0435\u0432\u043E\u0434 \u043F\u0430\u0440 \u043D\u0430 \u0440\u0443\u0441\u0441\u043A\u0438\u0439 (\u043E\u0442\u0434\u0435\u043B\u044C\u043D\u044B\u0439 \u0437\u0430\u043F\u0440\u043E\u0441 \u043A \u043C\u043E\u0434\u0435\u043B\u0438).")) : null,
108065
108484
  Array.from({
108066
108485
  length: Math.max(generatedTitlesData.length, generatedTextsData.length)
108067
108486
  }, (_, i) => i).map((i) => {
@@ -108220,7 +108639,7 @@ ${imageData.originalPrompt}
108220
108639
  generatingLanding && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { display: 'flex', alignItems: 'center', gap: 1, mb: 2 } },
108221
108640
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { size: 20 }),
108222
108641
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", sx: { color: 'text.secondary' } }, formatElapsedTime(elapsedTime.landing)))),
108223
- !generatingLanding && generatedLandingHTML && generatedLandingImageBlob && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { variant: "outlined", color: "primary", onClick: handlePreviewLanding, startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_61__["default"], null), fullWidth: true }, "\u041F\u0440\u0435\u0434\u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440 \u043B\u0435\u043D\u0434\u0438\u043D\u0433\u0430"))))))))),
108642
+ !generatingLanding && generatedLandingHTML && generatedLandingImageBlob && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { variant: "outlined", color: "primary", onClick: handlePreviewLanding, startIcon: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_61__["default"], null), fullWidth: true }, "\u041F\u0440\u0435\u0434\u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440 \u043B\u0435\u043D\u0434\u0438\u043D\u0433\u0430"))))))))),
108224
108643
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_32__["default"], { open: approachLoadChoice !== null, onClose: handleApproachLoadKeepCurrent, maxWidth: "md", fullWidth: true }, approachLoadChoice ? (react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null,
108225
108644
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_33__["default"], null, "\u041F\u043E\u0434\u0445\u043E\u0434\u044B \u0438\u0437 \u0444\u0430\u0439\u043B\u0430 \u043E\u0444\u0444\u0435\u0440\u0430"),
108226
108645
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_34__["default"], null,
@@ -108247,8 +108666,8 @@ ${imageData.originalPrompt}
108247
108666
  ? formatImageCountsForDisplay(approachLoadChoice.savedCounts)
108248
108667
  : '— не указано (количества крео не изменятся)'))))),
108249
108668
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_35__["default"], null,
108250
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { onClick: handleApproachLoadKeepCurrent }, "\u041E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u043A\u0430\u043A \u0441\u0435\u0439\u0447\u0430\u0441"),
108251
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { variant: "contained", onClick: handleApproachLoadApplyFromFile }, "\u041F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u044C \u0438\u0437 \u0444\u0430\u0439\u043B\u0430")))) : null),
108669
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { onClick: handleApproachLoadKeepCurrent }, "\u041E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u043A\u0430\u043A \u0441\u0435\u0439\u0447\u0430\u0441"),
108670
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { variant: "contained", onClick: handleApproachLoadApplyFromFile }, "\u041F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u044C \u0438\u0437 \u0444\u0430\u0439\u043B\u0430")))) : null),
108252
108671
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_32__["default"], { open: settingsFileMissingDialog.open, onClose: (_, reason) => {
108253
108672
  if (settingsFileMissingDialog.retrying)
108254
108673
  return;
@@ -108270,8 +108689,8 @@ ${imageData.originalPrompt}
108270
108689
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", color: "text.secondary", sx: { mb: 1 } }, "\u041C\u043E\u0436\u043D\u043E \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443 (\u0438\u043D\u043E\u0433\u0434\u0430 Google \u0414\u0438\u0441\u043A \u0435\u0449\u0451 \u043D\u0435 \u043E\u0442\u0434\u0430\u043B \u0441\u043F\u0438\u0441\u043E\u043A \u0444\u0430\u0439\u043B\u043E\u0432) \u0438\u043B\u0438 \u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u043A\u0430\u043A \u0435\u0441\u0442\u044C, \u0435\u0441\u043B\u0438 \u0434\u043B\u044F \u044D\u0442\u043E\u0439 \u043F\u0430\u043F\u043A\u0438 \u0444\u0430\u0439\u043B\u0430 \u0438 \u043D\u0435 \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C."),
108271
108690
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "body2", color: "text.secondary" }, "\u041F\u043E\u043A\u0430 \u043E\u043A\u043D\u043E \u043E\u0442\u043A\u0440\u044B\u0442\u043E, \u0430\u0432\u0442\u043E\u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0438\u0435 \u0438 \u0437\u0430\u043F\u0438\u0441\u044C \u043D\u0430\u0441\u0442\u0440\u043E\u0435\u043A \u0432 \u044D\u0442\u0443 \u043F\u0430\u043F\u043A\u0443 \u043D\u0430 \u0414\u0438\u0441\u043A\u0435 \u043D\u0435 \u0432\u044B\u043F\u043E\u043B\u043D\u044F\u044E\u0442\u0441\u044F \u2014 \u0447\u0442\u043E\u0431\u044B \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u0442\u044C \u043D\u043E\u0432\u044B\u0439 \u0444\u0430\u0439\u043B, \u043F\u043E\u043A\u0430 \u0432\u044B \u043D\u0435 \u0440\u0435\u0448\u0438\u0442\u0435, \u0435\u0441\u0442\u044C \u043B\u0438 \u0443\u0436\u0435 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438.")),
108272
108691
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_35__["default"], { sx: { px: 3, pb: 2, gap: 1 } },
108273
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { onClick: handleSettingsFileMissingRetry, variant: "outlined", disabled: settingsFileMissingDialog.retrying, startIcon: settingsFileMissingDialog.retrying ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { size: 16, color: "inherit" }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_59__["default"], null) }, "\u041F\u0435\u0440\u0435\u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C"),
108274
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { onClick: handleSettingsFileMissingDismissOk, variant: "contained", disabled: settingsFileMissingDialog.retrying }, "\u044D\u0442\u043E \u043D\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u043E"))),
108692
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { onClick: handleSettingsFileMissingRetry, variant: "outlined", disabled: settingsFileMissingDialog.retrying, startIcon: settingsFileMissingDialog.retrying ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__["default"], { size: 16, color: "inherit" }) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material__WEBPACK_IMPORTED_MODULE_59__["default"], null) }, "\u041F\u0435\u0440\u0435\u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C"),
108693
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { onClick: handleSettingsFileMissingDismissOk, variant: "contained", disabled: settingsFileMissingDialog.retrying }, "\u044D\u0442\u043E \u043D\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u043E"))),
108275
108694
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_PromptManagerDialog__WEBPACK_IMPORTED_MODULE_62__["default"], { open: promptManagerOpen, onClose: () => setPromptManagerOpen(false) }),
108276
108695
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_32__["default"], { open: settingsBackupDialogOpen, onClose: () => {
108277
108696
  setSettingsBackupDialogOpen(false);
@@ -108305,11 +108724,12 @@ ${imageData.originalPrompt}
108305
108724
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { control: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_25__["default"], { checked: settingsExportFlags.localStorageUi, onChange: e => setSettingsExportFlags(f => ({ ...f, localStorageUi: e.target.checked })) }), label: "\u041C\u043E\u0434\u0435\u043B\u0438, \u0432\u0430\u043B\u0438\u0434\u0430\u0442\u043E\u0440, \u0442\u0435\u043C\u0430, \u0441\u043E\u043E\u0442\u043D\u043E\u0448\u0435\u043D\u0438\u0435 \u0441\u0442\u043E\u0440\u043E\u043D" }),
108306
108725
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { control: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_25__["default"], { checked: settingsExportFlags.googleOAuth, onChange: e => setSettingsExportFlags(f => ({ ...f, googleOAuth: e.target.checked })) }), label: "Google Client ID / Secret (\u0438\u0437 config)" }),
108307
108726
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { control: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_25__["default"], { checked: settingsExportFlags.openRouterKey, onChange: e => setSettingsExportFlags(f => ({ ...f, openRouterKey: e.target.checked })) }), label: "\u041A\u043B\u044E\u0447 OpenRouter (\u0438\u0437 config; \u0445\u0440\u0430\u043D\u0438\u0442\u0435 \u0444\u0430\u0439\u043B \u0432 \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u043E\u0441\u0442\u0438)" }),
108727
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { control: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_25__["default"], { checked: settingsExportFlags.flexCardKey, onChange: e => setSettingsExportFlags(f => ({ ...f, flexCardKey: e.target.checked })) }), label: "\u041A\u043B\u044E\u0447 FlexCard API (\u0438\u0437 config)" }),
108308
108728
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { control: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_25__["default"], { checked: settingsExportFlags.googleTokens, onChange: e => setSettingsExportFlags(f => ({ ...f, googleTokens: e.target.checked })) }), label: "\u0422\u043E\u043A\u0435\u043D\u044B Google (\u0434\u043E\u0441\u0442\u0443\u043F/\u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435) \u2014 \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E \u0432\u044B\u043A\u043B., \u0440\u0438\u0441\u043A \u0443\u0442\u0435\u0447\u043A\u0438" })),
108309
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { variant: "outlined", sx: { mt: 2 }, onClick: handleSettingsExport }, "\u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C JSON\u2026")),
108729
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { variant: "outlined", sx: { mt: 2 }, onClick: handleSettingsExport }, "\u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C JSON\u2026")),
108310
108730
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { flex: 1, minWidth: 260 } },
108311
108731
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "subtitle2", sx: { mb: 1, fontWeight: 600 } }, "\u0418\u043C\u043F\u043E\u0440\u0442"),
108312
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { variant: "outlined", onClick: () => settingsImportFileInputRef.current?.click() }, "\u0412\u044B\u0431\u0440\u0430\u0442\u044C JSON\u2026"),
108732
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { variant: "outlined", onClick: () => settingsImportFileInputRef.current?.click() }, "\u0412\u044B\u0431\u0440\u0430\u0442\u044C JSON\u2026"),
108313
108733
  settingsImportBundle && settingsImportFlags && (react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__["default"], { sx: { mt: 2, p: 1.5, border: 1, borderColor: 'divider', borderRadius: 1 } },
108314
108734
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__["default"], { variant: "caption", color: "text.secondary", display: "block", sx: { mb: 1 } },
108315
108735
  "\u0424\u0430\u0439\u043B: ",
@@ -108319,13 +108739,13 @@ ${imageData.originalPrompt}
108319
108739
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { control: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_25__["default"], { checked: settingsImportFlags.localStorage, disabled: !(0,_appSettingsBundle__WEBPACK_IMPORTED_MODULE_64__.getPresentSections)(settingsImportBundle).localStorage, onChange: e => setSettingsImportFlags(f => f ? { ...f, localStorage: e.target.checked } : f) }), label: "LocalStorage (\u043C\u043E\u0434\u0435\u043B\u0438, \u0442\u0435\u043C\u0430, \u043A\u044D\u0448 \u043F\u0430\u043F\u043E\u043A, \u2026)" }),
108320
108740
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_24__["default"], { control: react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_25__["default"], { checked: settingsImportFlags.electronConfig, disabled: !(0,_appSettingsBundle__WEBPACK_IMPORTED_MODULE_64__.getPresentSections)(settingsImportBundle).electronConfig, onChange: e => setSettingsImportFlags(f => f ? { ...f, electronConfig: e.target.checked } : f) }), label: "Config: OAuth, \u043A\u043B\u044E\u0447, \u0442\u043E\u043A\u0435\u043D\u044B (\u0435\u0441\u043B\u0438 \u0435\u0441\u0442\u044C \u0432 \u0444\u0430\u0439\u043B\u0435)" })),
108321
108741
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__["default"], { direction: "row", spacing: 1, sx: { mt: 2 } },
108322
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { variant: "contained", onClick: handleSettingsImportApply }, "\u041F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u044C"),
108323
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { onClick: () => {
108742
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { variant: "contained", onClick: handleSettingsImportApply }, "\u041F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u044C"),
108743
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { onClick: () => {
108324
108744
  setSettingsImportBundle(null);
108325
108745
  setSettingsImportFlags(null);
108326
108746
  } }, "\u041E\u0442\u043C\u0435\u043D\u0430"))))))),
108327
108747
  react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_35__["default"], null,
108328
- react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_22__["default"], { onClick: () => {
108748
+ react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_23__["default"], { onClick: () => {
108329
108749
  setSettingsBackupDialogOpen(false);
108330
108750
  setSettingsImportBundle(null);
108331
108751
  setSettingsImportFlags(null);
@@ -109292,6 +109712,7 @@ const LOCAL_STORAGE_UI_KEYS = [
109292
109712
  'selectedLandingModel',
109293
109713
  'validationDisabled',
109294
109714
  'autoRemakeOnValidatorError',
109715
+ 'uploadAfterValidatorOk',
109295
109716
  'imageAspectRatio',
109296
109717
  ];
109297
109718
  const DEFAULT_EXPORT_FLAGS = {
@@ -109299,6 +109720,7 @@ const DEFAULT_EXPORT_FLAGS = {
109299
109720
  localStorageUi: true,
109300
109721
  googleOAuth: false,
109301
109722
  openRouterKey: false,
109723
+ flexCardKey: false,
109302
109724
  googleTokens: false,
109303
109725
  };
109304
109726
  function collectLocalStorageKeys(keys) {
@@ -109350,6 +109772,9 @@ function buildSettingsBundle(flags, fullElectronConfig) {
109350
109772
  if (flags.openRouterKey && typeof fullElectronConfig.openaiApiKey === 'string') {
109351
109773
  ec.openaiApiKey = fullElectronConfig.openaiApiKey;
109352
109774
  }
109775
+ if (flags.flexCardKey && typeof fullElectronConfig.flexcardApiKey === 'string') {
109776
+ ec.flexcardApiKey = fullElectronConfig.flexcardApiKey;
109777
+ }
109353
109778
  if (flags.googleTokens) {
109354
109779
  if (typeof fullElectronConfig.accessToken === 'string')
109355
109780
  ec.accessToken = fullElectronConfig.accessToken;
@@ -109434,7 +109859,16 @@ function mergeElectronImport(current, bundle, on) {
109434
109859
  return current;
109435
109860
  const next = { ...current };
109436
109861
  const ec = bundle.electronConfig;
109437
- ['clientId', 'clientSecret', 'openaiApiKey', 'accessToken', 'refreshToken'].forEach(f => {
109862
+ [
109863
+ 'clientId',
109864
+ 'clientSecret',
109865
+ 'openaiApiKey',
109866
+ 'flexcardApiKey',
109867
+ 'telegramBotToken',
109868
+ 'telegramChatId',
109869
+ 'accessToken',
109870
+ 'refreshToken',
109871
+ ].forEach(f => {
109438
109872
  if (ec[f] !== undefined)
109439
109873
  next[f] = ec[f];
109440
109874
  });
@@ -109442,6 +109876,90 @@ function mergeElectronImport(current, bundle, on) {
109442
109876
  }
109443
109877
 
109444
109878
 
109879
+ /***/ }),
109880
+
109881
+ /***/ "./src/flexcardBalance.ts":
109882
+ /*!********************************!*\
109883
+ !*** ./src/flexcardBalance.ts ***!
109884
+ \********************************/
109885
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
109886
+
109887
+ "use strict";
109888
+ __webpack_require__.r(__webpack_exports__);
109889
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
109890
+ /* harmony export */ FLEXCARD_FINANCE_ACCOUNTS_URL: () => (/* binding */ FLEXCARD_FINANCE_ACCOUNTS_URL),
109891
+ /* harmony export */ fetchFlexCardFinanceAccountsUsdTotal: () => (/* binding */ fetchFlexCardFinanceAccountsUsdTotal)
109892
+ /* harmony export */ });
109893
+ /**
109894
+ * FlexCard — `GET /api/v1/finance/accounts/` с `X-API-Key`.
109895
+ * Суммируем только поле `balance` (строка или число) по строкам с `currency` === USD.
109896
+ */
109897
+ const FLEXCARD_FINANCE_ACCOUNTS_URL = 'https://flexcard.cards/api/v1/finance/accounts/';
109898
+ function num(v) {
109899
+ if (typeof v === 'number' && !Number.isNaN(v))
109900
+ return v;
109901
+ const n = parseFloat(String(v));
109902
+ return Number.isFinite(n) ? n : 0;
109903
+ }
109904
+ async function fetchPaginatedResults(apiKey, initialUrl) {
109905
+ const trimmed = apiKey.trim();
109906
+ const out = [];
109907
+ let url = initialUrl;
109908
+ for (let page = 0; page < 50 && url; page++) {
109909
+ const res = await fetch(url, {
109910
+ method: 'GET',
109911
+ headers: {
109912
+ 'X-API-Key': trimmed,
109913
+ Accept: 'application/json',
109914
+ },
109915
+ });
109916
+ if (!res.ok)
109917
+ return null;
109918
+ const data = (await res.json());
109919
+ let batch;
109920
+ let next;
109921
+ if (Array.isArray(data)) {
109922
+ batch = data;
109923
+ next = null;
109924
+ }
109925
+ else if (data && typeof data === 'object') {
109926
+ const o = data;
109927
+ const r = o.results;
109928
+ batch = Array.isArray(r) ? r : [];
109929
+ next = o.next ?? null;
109930
+ if (!Array.isArray(r))
109931
+ return null;
109932
+ }
109933
+ else {
109934
+ return null;
109935
+ }
109936
+ out.push(...batch);
109937
+ url = next ?? null;
109938
+ }
109939
+ return out;
109940
+ }
109941
+ /** Сумма `balance` по всем счетам с валютой USD. */
109942
+ async function fetchFlexCardFinanceAccountsUsdTotal(apiKey) {
109943
+ const trimmed = apiKey.trim();
109944
+ if (!trimmed)
109945
+ return null;
109946
+ const rows = await fetchPaginatedResults(trimmed, FLEXCARD_FINANCE_ACCOUNTS_URL);
109947
+ if (rows === null)
109948
+ return null;
109949
+ let sum = 0;
109950
+ for (const raw of rows) {
109951
+ if (!raw || typeof raw !== 'object')
109952
+ continue;
109953
+ const r = raw;
109954
+ const cur = String(r.currency ?? '').toUpperCase();
109955
+ if (cur !== 'USD' && cur !== 'US')
109956
+ continue;
109957
+ sum += num(r.balance);
109958
+ }
109959
+ return sum;
109960
+ }
109961
+
109962
+
109445
109963
  /***/ }),
109446
109964
 
109447
109965
  /***/ "./src/index.css":
@@ -110544,12 +111062,16 @@ ${priceBriefTrim}
110544
111062
  1) ВНУТРЕННЯЯ ЛОГИКА НА МАКЕТЕ (главное):
110545
111063
  - Должны быть видны две суммы: новая (акционная) и старая (зачёркнутая). Числовое отношение: старая ≈ вдвое новой при скидке -50% (допуск до ~2% из‑за округления; «1 180» и «1180» — одно и то же). На макете **не должно** быть текста «2×»/«2x» перед суммой как множитель — только две суммы (см. ШАГ 4).
110546
111064
  - Если отношение старая/новая ≈ 2 (в пределах допуска) — это уже доказательство корректной пары -50% на макете. В этом случае ЗАПРЕЩЕНО выводить ОШИБКУ формулировками вроде «выдуманные цены», «неверные суммы», «не соответствуют брифу» — даже если цифры в брифе другие или бриф кажется не тем.
110547
- - Если пара математически согласована с -50% и процент «-50%» где‑либо читаем в зоне оффера (см. ШАГ 4) — не придумывай расхождение с брифом из‑за символа валюты (€ / EUR / MXN и т.д.) или пробелов.
111065
+ - Если пара математически согласована с -50% и процент «-50%» где‑либо читаем в зоне оффера (см. ШАГ 4) — не придумывай расхождение с брифом из‑за **эквивалентного написания той же валюты**, что в брифе: символ ↔ код ↔ локальное слово для **одной** валюты (например € / EUR / «евро» для евро; $ / USD; MXN и т.п.) и из‑за пробелов/типографики. Это **не** отменяет пункт 1a ниже: другая денежная единица — всегда ошибка.
111066
+
111067
+ 1a) ВАЛЮТА ОБЯЗАТЕЛЬНО КАК В БРИФЕ (критично):
111068
+ - В брифе указана конкретная валюта (например EUR). На макете в блоке цен должна быть **именно она** (символ €, код EUR, или привычное для GEO написание «евро» **как валюта цены**, если это явно евро). **ОШИБКА, НУЖНА ПЕРЕСБОРКА**, если читается **иная** национальная валюта или её сокращение: при брифе EUR — **лв**, **лв.**, BGN, **лев**, руб/₽, zł, lei, kn, Kč и любые другие явные обозначения **не евро** рядом с суммами. Пример из практики: «50 лв» / «25 лв» при брифе в EUR — **недопустимо**, даже если 50/25 математически дают -50%.
111069
+ - Если валюту на макете нельзя прочитать уверенно — не гадай в пользу OK: при явном брифе с кодом валюты лучше отметь риск; при двусмысленности между двумя валютами — **ОШИБКА** или явное требование пересборки с читаемой валютой из брифа.
110548
111070
 
110549
111071
  2) СВЕРКА С БРИФОМ (только когда сопоставимо):
110550
111072
  - Сравнивай число НОВОЙ цены с брифом ТОЛЬКО если валюта на макете явно та же, что в брифе (EUR↔EUR, MXN↔MXN и т.д.; символ € и код EUR — одна валюта). Допускается разное оформление числа (пробелы тысяч, запятая/точка).
110551
- - Если валюта на креативе другая, чем в брифе, или не уверен в коде валютыне требуй совпадения цифр с брифом: достаточно пункта (1).
110552
- - Ошибку по ценам ставь только если: (а) на макете нет двух цен / нет согласованности -50%, ИЛИ (б) валюта однозначно совпадает с брифом, а число новой цены явно другое (не в пределах округления).
111073
+ - Если валюта на креативе **однозначно другая**, чем в брифе это уже **ОШИБКА** по п. 1a; совпадение цифр не спасает. Если валюта нечитаемасм. п. 1a. Если валюта совпадает с брифом сравнивай числа как раньше.
111074
+ - Ошибку по ценам ставь если: (а) на макете нет двух цен / нет согласованности -50%, ИЛИ (а') **неверная валюта относительно брифа** (п. 1a), ИЛИ (б) валюта однозначно совпадает с брифом, а число новой цены явно другое (не в пределах округления).
110553
111075
 
110554
111076
  3) Цена не должна быть оформлена как строка буллета с галочкой — отдельный блок.`
110555
111077
  : `ШАГ P — ЦЕНЫ (бриф числом не задан):
@@ -110610,10 +111132,11 @@ ${step3OtherText}
110610
111132
  - Если скидка указана и она не равна -50% — ошибка.
110611
111133
 
110612
111134
  ШАГ 5 — ЯЗЫК (критично):
110613
- - В рекламном тексте (HOOK/HEADLINE, текст на буллетах если есть, CTA/PRICE/DISCOUNT/OTHER_TEXT) НЕТ английских слов. Если есть — ошибка.
110614
- - ИСКЛЮЧЕНИЕ: внутри **графики интерфейса телефона** (шапка окна приложения, подпись вкладки вроде «Reviews», «App Store»-стиль бейджа в углу рамки) английские слова **не** считать ошибкой это имитация ОС/магазина, не рекламный текст баннера. Ошибкой остаётся английский в HOOK над макетом, в CTA, в буллетах, на плашках цены/скидки, в теле отзыва как пользовательском тексте (не в подписи UI).
111135
+ - В рекламном тексте (HOOK/HEADLINE, текст на буллетах если есть, CTA, PRICE, OTHER_TEXT) НЕТ произвольных английских слов и фраз. Если есть — ошибка.
111136
+ - **ИСКЛЮЧЕНИЕ бейдж скидки (DISCOUNT):** на малой плашке с процентом допустимы привычные глобальные маркеры **OFF**, **SALE** рядом с «-50%» / числом процента (например «-50% OFF», «SALE −50%») **не** считать смешением языков и **не** ставить ОШИБКУ только из‑за этих слов **в этом блоке скидки**. HOOK, CTA, буллеты и прочий рекламный текст по-прежнему строго на языке GEO.
111137
+ - ИСКЛЮЧЕНИЕ: внутри **графики интерфейса телефона** (шапка окна приложения, подпись вкладки вроде «Reviews», «App Store»-стиль бейджа в углу рамки) английские слова **не** считать ошибкой — это имитация ОС/магазина, не рекламный текст баннера. Ошибкой остаётся английский в HOOK над макетом, в CTA, в буллетах, на строках **сумм** (кроме кода валюты ISO и символа €/$ и т.п.), в теле отзыва как пользовательском тексте (не в подписи UI). Бейдж скидки — см. исключение OFF/SALE выше.
110615
111138
  - ЗАПРЕЩЁННЫЕ служебные подписи на макете (частая ошибка модели): слова HOOK, CTA, CAPS, BULLET, HEADLINE, PRICE, DISCOUNT как видимый текст на плашках или кнопке — ОШИБКА (это метки из брифа, не для читателя).
110616
- - Все слова соответствуют GEO/языку ${geo}. Если смешение языков — ошибка.
111139
+ - Все слова соответствуют GEO/языку ${geo}. Смешение языков — ошибка, **кроме** исключений выше (OFF/SALE на бейдже скидки; UI телефона).
110617
111140
  - ПСЕВДОСЛОВА И ОПЕЧАТКИ: если на плашке скидки или в буллетах видно **несуществующее** или явно ошибочное слово для языка GEO (не словарное), либо «калька» под английский без нормы (пример для испанского: «DISCONTA» вместо «DESCUENTO»; на буллете англ. «COMFORT» вместо исп. «CONFORT»/«COMODIDAD») — ОШИБКА языка (приземлённая формулировка в строке ОШИБКА).
110618
111141
 
110619
111142
  ШАГ 6 — КОМПОЗИЦИЯ: