@emailmaker/filemanager 0.10.92 → 0.10.93

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/file-manager.js CHANGED
@@ -22726,8 +22726,8 @@ __webpack_require__.d(__webpack_exports__, {
22726
22726
 
22727
22727
  // EXTERNAL MODULE: ./src/hooks/core/context.tsx
22728
22728
  var context = __webpack_require__(8361);
22729
- // EXTERNAL MODULE: ./src/hooks/core/files/index.ts + 17 modules
22730
- var core_files = __webpack_require__(9557);
22729
+ // EXTERNAL MODULE: ./src/hooks/core/files/index.ts + 18 modules
22730
+ var core_files = __webpack_require__(8573);
22731
22731
  ;// ./src/hooks/core/useFiles.ts
22732
22732
 
22733
22733
 
@@ -27028,8 +27028,8 @@ __webpack_require__.d(__webpack_exports__, {
27028
27028
  var core = __webpack_require__(4002);
27029
27029
  // EXTERNAL MODULE: external {"root":"React","commonjs2":"react","commonjs":"react","amd":"react"}
27030
27030
  var external_root_React_commonjs2_react_commonjs_react_amd_react_ = __webpack_require__(5442);
27031
- // EXTERNAL MODULE: ./src/hooks/core/files/index.ts + 17 modules
27032
- var core_files = __webpack_require__(9557);
27031
+ // EXTERNAL MODULE: ./src/hooks/core/files/index.ts + 18 modules
27032
+ var core_files = __webpack_require__(8573);
27033
27033
  ;// ./src/hooks/useFileActions.ts
27034
27034
 
27035
27035
 
@@ -28282,513 +28282,229 @@ const FileManagerProvider = ({ children, options }) => {
28282
28282
 
28283
28283
  /***/ }),
28284
28284
 
28285
- /***/ 9142:
28285
+ /***/ 8573:
28286
28286
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
28287
28287
 
28288
28288
  "use strict";
28289
- /* harmony export */ __webpack_require__.d(__webpack_exports__, {
28290
- /* harmony export */ $5: () => (/* binding */ getUniqueFileName),
28291
- /* harmony export */ mF: () => (/* binding */ encodeUrlForImageSrc),
28292
- /* harmony export */ nF: () => (/* binding */ formatFileSizeDisplay),
28293
- /* harmony export */ v7: () => (/* binding */ formatFileSize),
28294
- /* harmony export */ w3: () => (/* binding */ MAX_FILE_SIZE)
28295
- /* harmony export */ });
28296
- /* unused harmony exports MIN_FILE_SIZE, ALLOWED_FILE_TYPES, ALL_ALLOWED_TYPES, validateFile, validateFiles, validateFileUrl, getFileCategory, formatFileSizeShort, sanitizeFileName */
28297
- /* harmony import */ var antd__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(550);
28298
- /* harmony import */ var antd__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(antd__WEBPACK_IMPORTED_MODULE_0__);
28299
- /* harmony import */ var _i18n__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3829);
28300
28289
 
28290
+ // EXPORTS
28291
+ __webpack_require__.d(__webpack_exports__, {
28292
+ fp: () => (/* reexport */ useFileSelectionStore),
28293
+ vD: () => (/* reexport */ useFilesAPI),
28294
+ l_: () => (/* reexport */ useFilesModals),
28295
+ nC: () => (/* reexport */ useFilesSelection)
28296
+ });
28301
28297
 
28302
- const MAX_FILE_SIZE = 50 * 1024 * 1024;
28303
- const MIN_FILE_SIZE = 1;
28304
- const ALLOWED_FILE_TYPES = {
28305
- images: ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml', 'image/bmp', 'image/tiff'],
28306
- documents: [
28307
- 'application/pdf',
28308
- 'text/plain',
28309
- 'application/msword',
28310
- 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
28311
- ],
28312
- archives: ['application/zip', 'application/x-rar-compressed', 'application/x-7z-compressed'],
28313
- videos: ['video/mp4', 'video/avi', 'video/quicktime', 'video/x-msvideo'],
28314
- audio: ['audio/mpeg', 'audio/wav', 'audio/ogg'],
28298
+ // UNUSED EXPORTS: addSubfoldersToFiles
28299
+
28300
+ // EXTERNAL MODULE: external {"root":"React","commonjs2":"react","commonjs":"react","amd":"react"}
28301
+ var external_root_React_commonjs2_react_commonjs_react_amd_react_ = __webpack_require__(5442);
28302
+ var external_root_React_commonjs2_react_commonjs_react_amd_react_default = /*#__PURE__*/__webpack_require__.n(external_root_React_commonjs2_react_commonjs_react_amd_react_);
28303
+ // EXTERNAL MODULE: ./node_modules/react-i18next/dist/es/index.js + 15 modules
28304
+ var es = __webpack_require__(2389);
28305
+ ;// ./src/hooks/core/files/useFilesAPIHelpers.ts
28306
+ const isRootFolder = (folderId) => {
28307
+ return !folderId || folderId === 'root' || folderId === 'folders' || folderId === 'null' || folderId === null;
28315
28308
  };
28316
- const ALL_ALLOWED_TYPES = Object.values(ALLOWED_FILE_TYPES).flat();
28317
- const validateFile = (file, options = {}) => {
28318
- const { maxSize = MAX_FILE_SIZE, minSize = MIN_FILE_SIZE, allowedTypes = ALL_ALLOWED_TYPES, allowAllTypes = false, maxFileNameLength = 255, } = options;
28319
- const errors = [];
28320
- const warnings = [];
28321
- if (file.size > maxSize) {
28322
- errors.push(i18n.t('File too large. Maximum size: {{size}}', { size: formatFileSize(maxSize) }));
28309
+ const isRootFolderItem = (folder) => {
28310
+ return folder.parentId === null || folder.parentId === 'null' || !folder.parentId;
28311
+ };
28312
+ const filterFolders = (allFolders, params) => {
28313
+ const safeFolders = Array.isArray(allFolders) ? allFolders : [];
28314
+ if (isRootFolder(params.folderId)) {
28315
+ return safeFolders.filter(isRootFolderItem);
28323
28316
  }
28324
- if (file.size < minSize) {
28325
- errors.push(i18n.t('File too small. Minimum size: {{size}}', { size: formatFileSize(minSize) }));
28317
+ return safeFolders.filter(folder => folder.parentId === params.folderId);
28318
+ };
28319
+ const addSubfoldersToFiles = async (files, params, getFolders) => {
28320
+ var _a;
28321
+ if ((params === null || params === void 0 ? void 0 : params.itemType) === 'file')
28322
+ return files;
28323
+ let allFolders = [];
28324
+ if (getFolders) {
28325
+ const foldersData = await getFolders({ parentId: params.folderId });
28326
+ allFolders = Array.isArray(foldersData) ? foldersData : [];
28326
28327
  }
28327
- if (!allowAllTypes && !allowedTypes.includes(file.type)) {
28328
- errors.push(i18n.t('Unsupported file type: {{type}}', { type: file.type || i18n.t('unknown') }));
28328
+ else {
28329
+ return files;
28329
28330
  }
28330
- if (file.name.length > maxFileNameLength) {
28331
- errors.push(i18n.t('File name is too long. Maximum {{max}} characters', { max: maxFileNameLength }));
28331
+ let subfolders = filterFolders(allFolders, params);
28332
+ if ((_a = params === null || params === void 0 ? void 0 : params.search) === null || _a === void 0 ? void 0 : _a.trim()) {
28333
+ const search = params.search.trim().toLowerCase();
28334
+ subfolders = subfolders.filter(f => { var _a; return (_a = f.name) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(search); });
28332
28335
  }
28333
- const invalidChars = /[<>:"/\\|?*()#]/;
28334
- const hasControlChars = file.name.split('').some(char => {
28335
- const code = char.charCodeAt(0);
28336
- return code >= 0 && code <= 31;
28337
- });
28338
- if (invalidChars.test(file.name) || hasControlChars) {
28339
- errors.push(i18n.t('File name contains invalid characters'));
28336
+ if (subfolders.length === 0) {
28337
+ return files;
28340
28338
  }
28341
- if (file.size > 10 * 1024 * 1024) {
28342
- warnings.push(i18n.t('Large file may take a long time to upload'));
28339
+ const folderItems = subfolders.map((folder) => ({
28340
+ id: folder.id,
28341
+ name: folder.name,
28342
+ size: undefined,
28343
+ date: '-',
28344
+ isFolder: true,
28345
+ folderId: folder.parentId || '',
28346
+ type: '',
28347
+ selected: false,
28348
+ extension: '',
28349
+ childCount: folder.childCount,
28350
+ }));
28351
+ if ((params === null || params === void 0 ? void 0 : params.itemType) === 'folder') {
28352
+ return folderItems;
28343
28353
  }
28344
- return {
28345
- isValid: errors.length === 0,
28346
- errors,
28347
- warnings,
28348
- };
28349
- };
28350
- const validateFiles = (files, options = {}) => {
28351
- const valid = [];
28352
- const invalid = [];
28353
- files.forEach(file => {
28354
- const result = validateFile(file, options);
28355
- if (result.isValid) {
28356
- valid.push(file);
28357
- }
28358
- else {
28359
- invalid.push({ file, result });
28360
- }
28361
- });
28362
- return { valid, invalid };
28354
+ // getFiles(itemType:'all') может возвращать папки — исключаем их, т.к. folderItems уже содержит папки из getFolders
28355
+ const filesOnly = files.filter(f => !f.isFolder);
28356
+ const result = [...folderItems, ...filesOnly];
28357
+ return result;
28363
28358
  };
28364
- const validateFileUrl = async (url) => {
28365
- const errors = [];
28366
- let notified = false;
28367
- let lastError = undefined;
28368
- const notifyError = (message) => {
28369
- notified = true;
28370
- notification.error({
28371
- key: 'FILE_URL_VALIDATION_FAILED',
28372
- title: message,
28373
- placement: 'bottomRight',
28374
- description: url,
28375
- });
28376
- };
28377
- try {
28378
- new URL(url);
28379
- }
28380
- catch (error) {
28381
- console.error('Invalid URL:', error);
28382
- lastError = error;
28383
- const message = i18n.t('Invalid file URL');
28384
- errors.push(message);
28385
- notifyError(message);
28386
- return { isValid: false, errors };
28359
+
28360
+ // EXTERNAL MODULE: ./src/hooks/core/useSortAndFilter.ts
28361
+ var useSortAndFilter = __webpack_require__(7228);
28362
+ // EXTERNAL MODULE: ./src/hooks/core/useFolders.ts
28363
+ var useFolders = __webpack_require__(5018);
28364
+ // EXTERNAL MODULE: ./src/hooks/useNotifications.tsx
28365
+ var useNotifications = __webpack_require__(3435);
28366
+ // EXTERNAL MODULE: ./src/hooks/core/files/actions/useFilesDelete.ts
28367
+ var useFilesDelete = __webpack_require__(1591);
28368
+ // EXTERNAL MODULE: ./src/utils/errorMessages.ts
28369
+ var errorMessages = __webpack_require__(6377);
28370
+ // EXTERNAL MODULE: ./src/utils/fileValidation.ts
28371
+ var fileValidation = __webpack_require__(9142);
28372
+ // EXTERNAL MODULE: ./src/utils/urlValidation.ts
28373
+ var urlValidation = __webpack_require__(69);
28374
+ // EXTERNAL MODULE: ./src/utils/imageCompression.ts
28375
+ var imageCompression = __webpack_require__(3270);
28376
+ // EXTERNAL MODULE: ./src/utils/cacheClear.ts
28377
+ var cacheClear = __webpack_require__(2332);
28378
+ // EXTERNAL MODULE: ./src/utils/resolveItemMeta.ts
28379
+ var resolveItemMeta = __webpack_require__(1069);
28380
+ // EXTERNAL MODULE: ./src/utils/nameNormalization.ts
28381
+ var nameNormalization = __webpack_require__(7407);
28382
+ // EXTERNAL MODULE: ./src/utils/mimeUtils.ts
28383
+ var mimeUtils = __webpack_require__(3662);
28384
+ ;// ./src/utils/urlNormalization.ts
28385
+ const TRACKING_QUERY_PARAM_PATTERN = /^(utm_[a-z0-9_]+|fbclid|gclid|igshid|mc_cid|mc_eid)$/i;
28386
+ /**
28387
+ * Нормализует внешний URL для детерминированного поиска файла по источнику.
28388
+ * Убирает hash, трекинговые query-параметры, нормализует host и сортирует query.
28389
+ */
28390
+ function normalizeExternalFileUrl(url) {
28391
+ const parsed = new URL(url.trim());
28392
+ parsed.hash = '';
28393
+ parsed.hostname = parsed.hostname.toLowerCase();
28394
+ if ((parsed.protocol === 'https:' && parsed.port === '443') ||
28395
+ (parsed.protocol === 'http:' && parsed.port === '80')) {
28396
+ parsed.port = '';
28387
28397
  }
28398
+ const normalizedParams = Array.from(parsed.searchParams.entries())
28399
+ .filter(([key]) => !TRACKING_QUERY_PARAM_PATTERN.test(key))
28400
+ .sort(([keyA, valueA], [keyB, valueB]) => {
28401
+ const keyCompare = keyA.localeCompare(keyB);
28402
+ return keyCompare !== 0 ? keyCompare : valueA.localeCompare(valueB);
28403
+ });
28404
+ parsed.search = '';
28405
+ normalizedParams.forEach(([key, value]) => parsed.searchParams.append(key, value));
28406
+ return parsed.toString();
28407
+ }
28408
+ /**
28409
+ * Совпадает ли origin URL с текущей страницей (protocol + host + port).
28410
+ * Для same-origin прокси не используем — загрузка напрямую.
28411
+ */
28412
+ function isUrlSameOriginAsCurrentPage(url) {
28413
+ if (typeof window === 'undefined')
28414
+ return false;
28388
28415
  try {
28389
- const response = await fetch(url, { method: 'HEAD' });
28390
- if (!response.ok) {
28391
- errors.push(i18n.t('File unavailable: {{status}} {{statusText}}', {
28392
- status: response.status,
28393
- statusText: response.statusText,
28394
- }));
28395
- }
28396
- const contentLength = response.headers.get('content-length');
28397
- if (contentLength && parseInt(contentLength) > MAX_FILE_SIZE) {
28398
- errors.push(i18n.t('File too large: {{size}}', { size: formatFileSize(parseInt(contentLength)) }));
28399
- }
28400
- const contentType = response.headers.get('content-type');
28401
- if (contentType && !ALL_ALLOWED_TYPES.includes(contentType)) {
28402
- errors.push(i18n.t('Unsupported file type: {{contentType}}', { contentType }));
28403
- }
28416
+ return new URL(url).origin === window.location.origin;
28404
28417
  }
28405
- catch (error) {
28406
- console.error('Failed to validate file URL:', error);
28407
- lastError = error;
28408
- const message = i18n.t('Unable to validate file URL');
28409
- errors.push(message);
28410
- notifyError(message);
28418
+ catch (_a) {
28419
+ return false;
28411
28420
  }
28412
- if (errors.length > 0 && !notified) {
28413
- notifyError(i18n.t('Unable to validate file URL'));
28421
+ }
28422
+
28423
+ // EXTERNAL MODULE: ./src/utils/remoteFileFetchPipeline.ts + 1 modules
28424
+ var remoteFileFetchPipeline = __webpack_require__(5597);
28425
+ // EXTERNAL MODULE: ./src/utils/dedupeUploadNamesOnClient.ts
28426
+ var dedupeUploadNamesOnClient = __webpack_require__(3795);
28427
+ // EXTERNAL MODULE: ./src/utils/resolveUniqueUploadName.ts
28428
+ var utils_resolveUniqueUploadName = __webpack_require__(7742);
28429
+ // EXTERNAL MODULE: ./src/utils/fileFormatUtils.ts
28430
+ var fileFormatUtils = __webpack_require__(6825);
28431
+ ;// ./src/utils/mapWithConcurrency.ts
28432
+ async function mapWithConcurrency(items, limit, fn) {
28433
+ if (items.length === 0) {
28434
+ return [];
28414
28435
  }
28415
- return {
28416
- isValid: errors.length === 0,
28417
- errors,
28436
+ const results = new Array(items.length);
28437
+ let nextIndex = 0;
28438
+ const poolSize = Math.max(1, Math.min(limit, items.length));
28439
+ const worker = async () => {
28440
+ while (true) {
28441
+ const currentIndex = nextIndex++;
28442
+ if (currentIndex >= items.length) {
28443
+ return;
28444
+ }
28445
+ results[currentIndex] = await fn(items[currentIndex], currentIndex);
28446
+ }
28418
28447
  };
28419
- };
28420
- const getFileCategory = (file) => {
28421
- const type = file.type;
28422
- if (ALLOWED_FILE_TYPES.images.includes(type))
28423
- return 'image';
28424
- if (ALLOWED_FILE_TYPES.documents.includes(type))
28425
- return 'document';
28426
- if (ALLOWED_FILE_TYPES.archives.includes(type))
28427
- return 'archive';
28428
- if (ALLOWED_FILE_TYPES.videos.includes(type))
28429
- return 'video';
28430
- if (ALLOWED_FILE_TYPES.audio.includes(type))
28431
- return 'audio';
28432
- return 'other';
28433
- };
28434
- const formatFileSize = (bytes) => {
28435
- if (bytes === 0)
28436
- return '0 Bytes';
28437
- const k = 1024;
28438
- const sizes = ['Bytes', 'KB', 'MB', 'GB'];
28439
- const i = Math.floor(Math.log(bytes) / Math.log(k));
28440
- return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
28441
- };
28442
- const formatFileSizeShort = (bytes) => {
28443
- if (!bytes || bytes <= 0)
28444
- return '';
28445
- const KILOBYTE = 1024;
28446
- const MEGABYTE = KILOBYTE * 1024;
28447
- if (bytes < MEGABYTE) {
28448
- const kb = Math.round(bytes / KILOBYTE);
28449
- return `${kb} Kb`;
28450
- }
28451
- const mb = bytes / MEGABYTE;
28452
- return `${mb.toFixed(2)} Mb`;
28453
- };
28454
- const formatFileSizeDisplay = (value) => {
28455
- if (value === undefined || value === null)
28456
- return '';
28457
- if (typeof value === 'number') {
28458
- return formatFileSizeShort(value);
28459
- }
28460
- const normalized = String(value).trim();
28461
- if (!normalized)
28462
- return '';
28463
- const parsed = Number(normalized);
28464
- if (!Number.isNaN(parsed)) {
28465
- return formatFileSizeShort(parsed);
28466
- }
28467
- return normalized;
28468
- };
28469
- /**
28470
- * Генерирует уникальное имя файла: если name уже есть в existingNames, добавляет _1 (или _2, _3 и т.д.).
28471
- * @param name — исходное имя файла (с расширением)
28472
- * @param existingNames — массив имён файлов/папок в целевой директории
28473
- */
28474
- const getUniqueFileName = (name, existingNames) => {
28475
- const safeNames = existingNames.filter((n) => typeof n === 'string');
28476
- const set = new Set(safeNames.map(n => n.toLowerCase()));
28477
- const safeName = typeof name === 'string' ? name : String(name !== null && name !== void 0 ? name : '');
28478
- if (!set.has(safeName.toLowerCase()))
28479
- return safeName;
28480
- const lastDot = safeName.lastIndexOf('.');
28481
- const baseName = lastDot > 0 ? safeName.slice(0, lastDot) : safeName;
28482
- const ext = lastDot > 0 ? safeName.slice(lastDot) : '';
28483
- let suffix = 1;
28484
- let candidate;
28485
- do {
28486
- candidate = `${baseName}_${suffix}${ext}`;
28487
- suffix++;
28488
- } while (set.has(candidate.toLowerCase()));
28489
- return candidate;
28490
- };
28491
- /**
28492
- * Кодирует URL для безопасного использования в img src. Решает проблему с кириллицей и другими
28493
- * не-ASCII символами в пути (например, emcdn.ru/.../Снимок_экрана.png).
28494
- * data: и blob: URLs возвращаются без изменений.
28495
- */
28496
- const encodeUrlForImageSrc = (url) => {
28497
- if (!url || typeof url !== 'string')
28498
- return undefined;
28499
- if (url.startsWith('data:') || url.startsWith('blob:'))
28500
- return url;
28501
- try {
28502
- if (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('/')) {
28503
- const parsed = new URL(url.startsWith('/') ? `https://x${url}` : url);
28504
- const origin = url.startsWith('/') ? '' : `${parsed.protocol}//${parsed.host}`;
28505
- const pathEncoded = parsed.pathname
28506
- .split('/')
28507
- .map(seg => {
28508
- if (!seg)
28509
- return '';
28510
- try {
28511
- return encodeURIComponent(decodeURIComponent(seg));
28512
- }
28513
- catch (_a) {
28514
- return encodeURIComponent(seg);
28515
- }
28516
- })
28517
- .join('/');
28518
- const result = url.startsWith('/') ? pathEncoded + (parsed.search || '') + (parsed.hash || '') : `${origin}${pathEncoded}${parsed.search}${parsed.hash}`;
28519
- return result;
28520
- }
28521
- }
28522
- catch (_a) {
28523
- // если парсинг не удался, возвращаем как есть
28524
- }
28525
- return url;
28526
- };
28527
- /**
28528
- * @deprecated Эта функция оставлена только для обратной совместимости.
28529
- * Используйте {@link normalizeFileName} из `nameNormalization.ts` для настраиваемой нормализации имен файлов.
28530
- *
28531
- * Упрощенная санитизация имени файла:
28532
- * - Заменяет запрещенные символы на `_`
28533
- * - Заменяет пробелы на `_`
28534
- * - Удаляет множественные подчеркивания
28535
- * - Ограничивает длину до 255 символов
28536
- */
28537
- const sanitizeFileName = (fileName) => {
28538
- return fileName
28539
- .replace(/[<>:"/\\|?*()#]/g, '_')
28540
- .split('')
28541
- .map(char => {
28542
- const code = char.charCodeAt(0);
28543
- return code >= 0 && code <= 31 ? '_' : char;
28544
- })
28545
- .join('')
28546
- .replace(/\s+/g, '_')
28547
- .replace(/_{2,}/g, '_')
28548
- .substring(0, 255);
28549
- };
28448
+ await Promise.all(Array.from({ length: poolSize }, () => worker()));
28449
+ return results;
28450
+ }
28451
+
28452
+ // EXTERNAL MODULE: ./src/hooks/core/context.tsx
28453
+ var context = __webpack_require__(8361);
28454
+ // EXTERNAL MODULE: ./src/constants/index.tsx
28455
+ var constants = __webpack_require__(133);
28456
+ // EXTERNAL MODULE: ./src/shared/fileManagerApiError.ts
28457
+ var fileManagerApiError = __webpack_require__(7036);
28458
+ ;// ./src/hooks/core/files/useFilesAPI.ts
28550
28459
 
28551
28460
 
28552
- /***/ }),
28553
28461
 
28554
- /***/ 9287:
28555
- /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
28556
28462
 
28557
- // Imports
28558
- var ___CSS_LOADER_API_SOURCEMAP_IMPORT___ = __webpack_require__(1354);
28559
- var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(6314);
28560
- var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);
28561
- // Module
28562
- ___CSS_LOADER_EXPORT___.push([module.id, `.FolderSidebar-module__button__Yf2rX.ant-btn.ant-btn-icon-only{padding:calc(var(--paddingXXS)*1px) calc(var(--paddingSM)*1px);cursor:pointer;color:var(--colorTextSecondary);display:flex;gap:calc(var(--marginXS)*1px);align-items:center;justify-content:center;border-radius:calc(var(--borderRadiusSM)*1px);transition:all var(--motionDurationFast);box-shadow:none;border:none;width:32px;height:32px}.FolderSidebar-module__button__Yf2rX.ant-btn.ant-btn-icon-only:hover{background-color:var(--colorBgLayout);color:inherit}.FolderSidebar-module__button__Yf2rX:hover{background-color:var(--colorBgContainer);color:var(--colorPrimary)}.FolderSidebar-module__foldersTitle__Uh6u7{display:flex;align-items:center;flex:1;cursor:pointer}.FolderSidebar-module__folderActions__qA2Bk{position:absolute;top:4px;right:4px;display:flex;align-items:center;gap:4px;z-index:10}.FolderSidebar-module__folderActions__qA2Bk .FolderSidebar-module__button__Yf2rX.FolderSidebar-module__ant-btn__LXAXm.FolderSidebar-module__ant-btn-icon-only__j_wVF{padding:calc(var(--paddingXXS)*1px) calc(var(--paddingSM)*1px);cursor:pointer;color:var(--colorTextSecondary);display:flex;gap:calc(var(--marginXS)*1px);align-items:center;justify-content:center;border-radius:calc(var(--borderRadiusSM)*1px);transition:all var(--motionDurationFast);box-shadow:none;border:none;width:32px;height:32px}.FolderSidebar-module__folderActions__qA2Bk .FolderSidebar-module__button__Yf2rX.FolderSidebar-module__ant-btn__LXAXm.FolderSidebar-module__ant-btn-icon-only__j_wVF:hover{background-color:var(--colorBgLayout);color:inherit}.FolderSidebar-module__customExpandIcon__IhZJl{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;margin-right:8px;cursor:pointer;color:#666;font-size:10px;transition:transform .2s ease}.FolderSidebar-module__customExpandIcon__IhZJl:hover{color:var(--colorPrimary)}.FolderSidebar-module__fileCount__bD9DD{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:100%;padding:0 6px;margin-left:8px;color:#8d8d8d;font-size:14px;font-weight:500;transition:all .2s ease;white-space:nowrap;cursor:default;flex-shrink:0;margin-right:10px}.ant-tree .ant-tree-node-content-wrapper.ant-tree-node-selected{background-color:rgba(0,0,0,0) !important}.FolderSidebar-module__folderMenu__JCzLF .ant-tree-treenode-selected .folder-label span,.FolderSidebar-module__folderMenu__JCzLF .ant-tree .ant-tree-node-content-wrapper.ant-tree-node-selected .folderLabelItem,.FolderSidebar-module__folderMenu__JCzLF .ant-tree-treenode-selected .folderLabelItem,.FolderSidebar-module__folderMenu__JCzLF .ant-tree-node-selected .folderLabelItem{font-weight:600 !important}.FolderSidebar-module__uploadButton__QQUid{width:100%;justify-content:flex-start}.FolderSidebar-module__uploadButton__QQUid button:hover{background-color:var(--colorPrimaryHover) !important}.FolderSidebar-module__folderMenu__JCzLF{padding-left:0;padding-right:0}.FolderSidebar-module__folderMenu__JCzLF .ant-tree-treenode{display:flex;align-items:center;justify-content:space-between;padding:4px 0;border-radius:4px;transition:background-color .2s ease;width:100%;margin-bottom:0}.FolderSidebar-module__folderMenu__JCzLF .ant-tree-node-content-wrapper{flex:1;align-items:center;padding:0;transition:background-color .2s ease}.FolderSidebar-module__folderMenu__JCzLF .ant-tree-node-content-wrapper:hover{background-color:rgba(0,0,0,0)}.FolderSidebar-module__folderMenu__JCzLF .ant-tree-switcher{height:24px;width:24px;margin-top:5px;display:flex !important;align-items:center;justify-content:center;transition:color .2s ease;flex-shrink:0}.FolderSidebar-module__folderMenu__JCzLF .ant-tree-switcher-noop{width:24px}.FolderSidebar-module__folderMenu__JCzLF .ant-tree-switcher-line-icon{color:#d9d9d9}.FolderSidebar-module__folderMenu__JCzLF .FolderSidebar-module__folderItemContainer__byl5b{width:100%;display:flex;align-items:center;justify-content:space-between;padding:2px 0;min-width:0}.FolderSidebar-module__folderMenu__JCzLF .folder-label{display:flex;align-items:center;flex:1;color:#595959;font-size:14px;min-width:0;overflow:hidden}.FolderSidebar-module__folderMenu__JCzLF .ant-tree-treenode[data-key="folders"]{border-bottom:1px solid var(--colorBorderSecondary);margin-bottom:8px;padding-bottom:8px}.FolderSidebar-module__folderMenu__JCzLF .ant-tree-treenode[data-key="folders"].ant-tree-treenode-selected{background-color:rgba(0,0,0,0) !important}.FolderSidebar-module__folderMenu__JCzLF .ant-tree-treenode[data-key="folders"] .ant-tree-node-content-wrapper{padding:0}.FolderSidebar-module__folderMenu__JCzLF .ant-tree-treenode[data-key="folders"] .ant-tree-node-content-wrapper:hover{background-color:rgba(0,0,0,0) !important}.FolderSidebar-module__folderMenu__JCzLF .ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0}.FolderSidebar-module__folderMenu__JCzLF .FolderSidebar-module__emptyFolderContainer__WK6tx{padding:20px;text-align:center}.FolderSidebar-module__folderMenu__JCzLF .ant-tree > .ant-tree-treenode{padding-left:0 !important}.FolderSidebar-module__folderIcon__UoZzH{margin-right:8px;font-size:18px;flex-shrink:0}.FolderSidebar-module__folderLabelItem__uJ062{font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0;user-select:none}.FolderSidebar-module__foldersTitleItem__gapOI{display:flex;align-items:center;flex:1;cursor:pointer;min-width:0;overflow:hidden}.FolderSidebar-module__folderLabel__fHWMp{display:flex;align-items:center;flex:1;min-width:0;overflow:hidden}.FolderSidebar-module__foldersContainer__zdBVU{position:relative;width:100%}.FolderSidebar-module__uploadButton__QQUid .FolderSidebar-module__ant-btn__LXAXm,.FolderSidebar-module__uploadButton__QQUid .ant-btn{display:flex;align-items:center;justify-content:center;gap:3px}.FolderSidebar-module__buttonWrapper__a9Qi5{margin-bottom:24px;display:flex;gap:4px}.FolderSidebar-module__buttonWrapper__a9Qi5>:first-child{flex:1;height:40px;padding-left:10px}.FolderSidebar-module__buttonWrapper__a9Qi5>:last-child{width:40px;height:40px;flex-shrink:0}.FolderSidebar-module__uploadButtonIcon__mWQmq{font-size:20px}.FolderSidebar-module__uploadButtonTitle__wk_o6{line-height:1em}.FolderSidebar-module__emptyFolderContainer__WK6tx{user-select:none;cursor:default;color:var(--colorTextTertiary)}.FolderSidebar-module__folderItemContainer__byl5b{width:100%;display:flex;align-items:center;justify-content:space-between;padding:2px 0;min-width:0}`, "",{"version":3,"sources":["webpack://./src/components/FolderSidebar/FolderSidebar.module.scss"],"names":[],"mappings":"AAAA,+DACI,8DAAA,CACA,cAAA,CACA,+BAAA,CACA,YAAA,CACA,6BAAA,CACA,kBAAA,CACA,sBAAA,CACA,6CAAA,CACA,wCAAA,CACA,eAAA,CAEA,WAAA,CACA,UAAA,CACA,WAAA,CAEA,qEACI,qCAAA,CACA,aAAA,CAIR,2CACI,wCAAA,CACA,yBAAA,CAGJ,2CACE,YAAA,CACA,kBAAA,CACA,MAAA,CACA,cAAA,CAGF,4CACI,iBAAA,CACA,OAAA,CACA,SAAA,CACA,YAAA,CACA,kBAAA,CACA,OAAA,CACA,UAAA,CAEA,qKACE,8DAAA,CACA,cAAA,CACA,+BAAA,CACA,YAAA,CACA,6BAAA,CACA,kBAAA,CACA,sBAAA,CACA,6CAAA,CACA,wCAAA,CACA,eAAA,CAEA,WAAA,CACA,UAAA,CACA,WAAA,CAEA,2KACI,qCAAA,CACA,aAAA,CAKV,+CACE,mBAAA,CACA,kBAAA,CACA,sBAAA,CACA,UAAA,CACA,WAAA,CACA,gBAAA,CACA,cAAA,CACA,UAAA,CACA,cAAA,CACA,6BAAA,CAGF,qDACI,yBAAA,CAGJ,wCACE,mBAAA,CACA,kBAAA,CACA,sBAAA,CACA,cAAA,CACE,WAAA,CACF,aAAA,CACA,eAAA,CACE,aAAA,CACA,cAAA,CACF,eAAA,CACA,uBAAA,CACA,kBAAA,CACA,cAAA,CACA,aAAA,CACA,iBAAA,CAQF,gEACE,yCAAA,CAQF,0XAIE,0BAAA,CAWF,2CACI,UAAA,CACA,0BAAA,CAEA,wDACE,oDAAA,CAIN,yCACE,cAAA,CACA,eAAA,CAGF,4DACE,YAAA,CACA,kBAAA,CACA,6BAAA,CACA,aAAA,CACA,iBAAA,CACA,oCAAA,CACA,UAAA,CACA,eAAA,CAGF,wEACE,MAAA,CAEA,kBAAA,CACA,SAAA,CAEA,oCAAA,CAGF,8EACE,8BAAA,CAGF,4DACE,WAAA,CACA,UAAA,CACA,cAAA,CACA,uBAAA,CACA,kBAAA,CACA,sBAAA,CACA,yBAAA,CACA,aAAA,CAGF,iEACE,UAAA,CAIF,sEACE,aAAA,CAIF,2FACE,UAAA,CACA,YAAA,CACA,kBAAA,CACA,6BAAA,CACA,aAAA,CACA,WAAA,CAGF,uDACE,YAAA,CACA,kBAAA,CACA,MAAA,CACA,aAAA,CACA,cAAA,CACA,WAAA,CACA,eAAA,CAIF,gFACI,mDAAA,CACF,iBAAA,CACA,kBAAA,CAGF,2GACE,yCAAA,CAGF,+GACE,SAAA,CAGF,qHACE,yCAAA,CAIF,mDACE,eAAA,CACA,sBAAA,CACA,kBAAA,CACA,MAAA,CACA,WAAA,CAIF,4FACE,YAAA,CACA,iBAAA,CAIF,wEACE,yBAAA,CAEF,yCACE,gBAAA,CACE,cAAA,CACF,aAAA,CAGF,8CACE,cAAA,CACA,kBAAA,CACA,eAAA,CACA,sBAAA,CACA,MAAA,CACA,WAAA,CACA,gBAAA,CAGF,+CACE,YAAA,CACA,kBAAA,CACA,MAAA,CACA,cAAA,CACA,WAAA,CACA,eAAA,CASF,0CACE,YAAA,CACA,kBAAA,CACA,MAAA,CACA,WAAA,CACA,eAAA,CAGF,+CACE,iBAAA,CACA,UAAA,CAWF,qIAEE,YAAA,CACA,kBAAA,CACA,sBAAA,CACA,OAAA,CAaF,4CAEI,kBAAA,CACA,YAAA,CACA,OAAA,CAIA,yDACI,MAAA,CACA,WAAA,CACA,iBAAA,CAIJ,wDACI,UAAA,CACA,WAAA,CACA,aAAA,CAIR,+CACI,cAAA,CAGJ,gDACI,eAAA,CAGJ,mDACI,gBAAA,CACA,cAAA,CACA,8BAAA,CAGJ,kDACI,UAAA,CACA,YAAA,CACA,kBAAA,CACA,6BAAA,CACA,aAAA,CACA,WAAA","sourcesContent":[".button:global(.ant-btn.ant-btn-icon-only) {\r\n padding: calc(var(--paddingXXS) * 1px) calc(var(--paddingSM) * 1px);\r\n cursor: pointer;\r\n color: var(--colorTextSecondary);\r\n display: flex;\r\n gap: calc(var(--marginXS) * 1px);\r\n align-items: center;\r\n justify-content: center;\r\n border-radius: calc(var(--borderRadiusSM) * 1px);\r\n transition: all var(--motionDurationFast);\r\n box-shadow: none;\r\n\r\n border: none;\r\n width: 32px;\r\n height: 32px;\r\n\r\n &:hover {\r\n background-color: var(--colorBgLayout);\r\n color: inherit;\r\n }\r\n}\r\n\r\n.button:hover {\r\n background-color: var(--colorBgContainer);\r\n color: var(--colorPrimary);\r\n}\r\n\r\n.foldersTitle {\r\n display: flex;\r\n align-items: center;\r\n flex: 1;\r\n cursor: pointer;\r\n}\r\n\r\n.folderActions {\r\n position: absolute;\r\n top: 4px; /* Выравниваем по заголовку Folders */\r\n right: 4px;\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n z-index: 10;\r\n\r\n .button.ant-btn.ant-btn-icon-only {\r\n padding: calc(var(--paddingXXS) * 1px) calc(var(--paddingSM) * 1px);\r\n cursor: pointer;\r\n color: var(--colorTextSecondary);\r\n display: flex;\r\n gap: calc(var(--marginXS) * 1px);\r\n align-items: center;\r\n justify-content: center;\r\n border-radius: calc(var(--borderRadiusSM) * 1px);\r\n transition: all var(--motionDurationFast);\r\n box-shadow: none;\r\n\r\n border: none;\r\n width: 32px;\r\n height: 32px;\r\n\r\n &:hover {\r\n background-color: var(--colorBgLayout);\r\n color: inherit;\r\n }\r\n }\r\n}\r\n\r\n.customExpandIcon {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 16px;\r\n height: 16px;\r\n margin-right: 8px;\r\n cursor: pointer;\r\n color: #666;\r\n font-size: 10px;\r\n transition: transform 0.2s ease;\r\n}\r\n\r\n.customExpandIcon:hover {\r\n color: var(--colorPrimary);\r\n}\r\n\r\n.fileCount {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n min-width: 20px;\r\n height: 100%;\r\n padding: 0 6px;\r\n margin-left: 8px;\r\n color: #8D8D8D;\r\n font-size: 14px;\r\n font-weight: 500;\r\n transition: all 0.2s ease;\r\n white-space: nowrap;\r\n cursor: default;\r\n flex-shrink: 0; /* Запрещаем сжатие счетчика */\r\n margin-right: 10px;\r\n}\r\n\r\n// .fileCount:hover {\r\n// border-color: var(--colorPrimary);\r\n// color: var(--colorPrimary);\r\n// }\r\n\r\n:global(.ant-tree .ant-tree-node-content-wrapper.ant-tree-node-selected) {\r\n background-color: transparent !important;\r\n}\r\n\r\n/* :global(.ant-tree .ant-tree-node-content-wrapper.ant-tree-node-selected):hover {\r\n color: red !important;\r\n} */\r\n\r\n/* Стили для выделения текста в выбранной папке */\r\n.folderMenu :global(.ant-tree-treenode-selected .folder-label span),\r\n.folderMenu :global(.ant-tree .ant-tree-node-content-wrapper.ant-tree-node-selected .folderLabelItem),\r\n.folderMenu :global(.ant-tree-treenode-selected .folderLabelItem),\r\n.folderMenu :global(.ant-tree-node-selected .folderLabelItem) {\r\n font-weight: 600 !important;\r\n}\r\n\r\n/* Стили для иконки в выбранной папке */\r\n/* .folderMenu :global(.ant-tree-treenode-selected .folderIcon),\r\n.folderMenu :global(.ant-tree-node-selected .folderIcon) {\r\n color: red !important;\r\n} */\r\n\r\n/* CSS переменные для упрощения расчетов */\r\n\r\n.uploadButton {\r\n width: 100%;\r\n justify-content: flex-start;\r\n\r\n & button:hover {\r\n background-color: var(--colorPrimaryHover) !important;\r\n }\r\n}\r\n\r\n.folderMenu {\r\n padding-left: 0;\r\n padding-right: 0; /* Место для кнопки справа */\r\n}\r\n\r\n.folderMenu :global(.ant-tree-treenode) {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 4px 0;\r\n border-radius: 4px;\r\n transition: background-color 0.2s ease;\r\n width: 100%;\r\n margin-bottom: 0;\r\n}\r\n\r\n.folderMenu :global(.ant-tree-node-content-wrapper) {\r\n flex: 1;\r\n /* display: flex; */\r\n align-items: center;\r\n padding: 0;\r\n /* border-radius: 4px; */\r\n transition: background-color 0.2s ease;\r\n}\r\n\r\n.folderMenu :global(.ant-tree-node-content-wrapper:hover) {\r\n background-color: transparent;\r\n}\r\n\r\n.folderMenu :global(.ant-tree-switcher) {\r\n height: 24px;\r\n width: 24px;\r\n margin-top: 5px;\r\n display: flex !important;\r\n align-items: center;\r\n justify-content: center;\r\n transition: color 0.2s ease;\r\n flex-shrink: 0; /* Запрещаем сжатие переключателя */\r\n}\r\n\r\n.folderMenu :global(.ant-tree-switcher-noop) {\r\n width: 24px;\r\n}\r\n\r\n/* Стили для линий дерева */\r\n.folderMenu :global(.ant-tree-switcher-line-icon) {\r\n color: #d9d9d9;\r\n}\r\n\r\n/* Улучшение отображения folder-item-container */\r\n.folderMenu .folderItemContainer {\r\n width: 100%;\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 2px 0;\r\n min-width: 0; /* Позволяет flex-элементам сжиматься */\r\n}\r\n\r\n.folderMenu :global(.folder-label) {\r\n display: flex;\r\n align-items: center;\r\n flex: 1;\r\n color: #595959;\r\n font-size: 14px;\r\n min-width: 0;\r\n overflow: hidden;\r\n}\r\n\r\n/* Стили для корневого элемента Folders */\r\n.folderMenu :global(.ant-tree-treenode[data-key='folders']) {\r\n border-bottom: 1px solid var(--colorBorderSecondary);\r\n margin-bottom: 8px;\r\n padding-bottom: 8px;\r\n}\r\n\r\n.folderMenu :global(.ant-tree-treenode[data-key='folders'].ant-tree-treenode-selected) {\r\n background-color: transparent !important;\r\n}\r\n\r\n.folderMenu :global(.ant-tree-treenode[data-key='folders'] .ant-tree-node-content-wrapper) {\r\n padding: 0;\r\n}\r\n\r\n.folderMenu :global(.ant-tree-treenode[data-key='folders'] .ant-tree-node-content-wrapper:hover) {\r\n background-color: transparent !important;\r\n}\r\n\r\n/* Стили для ellipsis - удаляем max-width для гибкости */\r\n.folderMenu :global(.ellipsis) {\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n flex: 1;\r\n min-width: 0;\r\n}\r\n\r\n/* Стили для пустого состояния */\r\n.folderMenu .emptyFolderContainer {\r\n padding: 20px;\r\n text-align: center;\r\n}\r\n\r\n/* Убираем отступы у корневого узла */\r\n.folderMenu :global(.ant-tree > .ant-tree-treenode) {\r\n padding-left: 0 !important;\r\n}\r\n.folderIcon {\r\n margin-right: 8px;\r\n font-size: 18px;\r\n flex-shrink: 0; /* Запрещаем сжатие иконки */\r\n}\r\n\r\n.folderLabelItem {\r\n font-size: 14px;\r\n white-space: nowrap; /* Запрещаем перенос строк */\r\n overflow: hidden; /* Скрываем переполнение */\r\n text-overflow: ellipsis; /* Показываем троеточие */\r\n flex: 1; /* Занимает доступное место */\r\n min-width: 0; /* Позволяет сжиматься */\r\n user-select: none;\r\n}\r\n\r\n.foldersTitleItem {\r\n display: flex;\r\n align-items: center;\r\n flex: 1;\r\n cursor: pointer;\r\n min-width: 0; /* Позволяет сжиматься */\r\n overflow: hidden; /* Скрываем переполнение */\r\n}\r\n\r\n/* .foldersTitleItem .folderLabelItem {\r\n font-weight: 500;\r\n color: #262626;\r\n} */\r\n\r\n/* Класс для заголовка папок */\r\n.folderLabel {\r\n display: flex;\r\n align-items: center;\r\n flex: 1;\r\n min-width: 0;\r\n overflow: hidden;\r\n}\r\n\r\n.foldersContainer {\r\n position: relative;\r\n width: 100%;\r\n}\r\n\r\n/* Дополнительные стили для улучшения визуала */\r\n/* .folderMenu :global(.ant-tree-treenode:hover .folder-label span) {\r\n color: #1890ff;\r\n} */\r\n\r\n/* Стили для дивайдера между папками */\r\n\r\n\r\n.uploadButton .ant-btn,\r\n.uploadButton :global(.ant-btn) {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 3px;\r\n}\r\n/* .folderMenu :global(.ant-tree-treenode-selected) .folderLabelItem {\r\n color: #1890ff !important;\r\n} */\r\n\r\n// .folderMenu :global(.ant-tree-treenode-selected) .folderLabel .folderLabelItem,\r\n// .folderMenu :global(.ant-tree-treenode-selected) .folderLabel .folderIcon,\r\n// .folderMenu :global(.ant-tree-treenode-selected) .folderLabelItem {\r\n// color: var(--colorLink);\r\n// }\r\n\r\n\r\n.buttonWrapper {\r\n // overflow: hidden;\r\n margin-bottom: 24px;\r\n display: flex;\r\n gap: 4px;\r\n // border-radius: calc(var(--borderRadius) * 1px);\r\n \r\n // Левая кнопка занимает всю доступную ширину\r\n > :first-child {\r\n flex: 1;\r\n height: 40px;\r\n padding-left: 10px;\r\n }\r\n \r\n // Правая кнопка строго 40px\r\n > :last-child {\r\n width: 40px;\r\n height: 40px;\r\n flex-shrink: 0;\r\n }\r\n}\r\n\r\n.uploadButtonIcon {\r\n font-size: 20px;\r\n}\r\n\r\n.uploadButtonTitle {\r\n line-height: 1em;\r\n}\r\n\r\n.emptyFolderContainer {\r\n user-select: none;\r\n cursor: default;\r\n color: var(--colorTextTertiary);\r\n}\r\n\r\n.folderItemContainer {\r\n width: 100%;\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 2px 0;\r\n min-width: 0; /* Позволяет flex-элементам сжиматься */\r\n}\r\n"],"sourceRoot":""}]);
28563
- // Exports
28564
- ___CSS_LOADER_EXPORT___.locals = {
28565
- "button": `FolderSidebar-module__button__Yf2rX`,
28566
- "foldersTitle": `FolderSidebar-module__foldersTitle__Uh6u7`,
28567
- "folderActions": `FolderSidebar-module__folderActions__qA2Bk`,
28568
- "ant-btn": `FolderSidebar-module__ant-btn__LXAXm`,
28569
- "antBtn": `FolderSidebar-module__ant-btn__LXAXm`,
28570
- "ant-btn-icon-only": `FolderSidebar-module__ant-btn-icon-only__j_wVF`,
28571
- "antBtnIconOnly": `FolderSidebar-module__ant-btn-icon-only__j_wVF`,
28572
- "customExpandIcon": `FolderSidebar-module__customExpandIcon__IhZJl`,
28573
- "fileCount": `FolderSidebar-module__fileCount__bD9DD`,
28574
- "folderMenu": `FolderSidebar-module__folderMenu__JCzLF`,
28575
- "uploadButton": `FolderSidebar-module__uploadButton__QQUid`,
28576
- "folderItemContainer": `FolderSidebar-module__folderItemContainer__byl5b`,
28577
- "emptyFolderContainer": `FolderSidebar-module__emptyFolderContainer__WK6tx`,
28578
- "folderIcon": `FolderSidebar-module__folderIcon__UoZzH`,
28579
- "folderLabelItem": `FolderSidebar-module__folderLabelItem__uJ062`,
28580
- "foldersTitleItem": `FolderSidebar-module__foldersTitleItem__gapOI`,
28581
- "folderLabel": `FolderSidebar-module__folderLabel__fHWMp`,
28582
- "foldersContainer": `FolderSidebar-module__foldersContainer__zdBVU`,
28583
- "buttonWrapper": `FolderSidebar-module__buttonWrapper__a9Qi5`,
28584
- "uploadButtonIcon": `FolderSidebar-module__uploadButtonIcon__mWQmq`,
28585
- "uploadButtonTitle": `FolderSidebar-module__uploadButtonTitle__wk_o6`
28586
- };
28587
- module.exports = ___CSS_LOADER_EXPORT___;
28588
28463
 
28589
28464
 
28590
- /***/ }),
28591
28465
 
28592
- /***/ 9557:
28593
- /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
28594
28466
 
28595
- "use strict";
28596
28467
 
28597
- // EXPORTS
28598
- __webpack_require__.d(__webpack_exports__, {
28599
- fp: () => (/* reexport */ useFileSelectionStore),
28600
- vD: () => (/* reexport */ useFilesAPI),
28601
- l_: () => (/* reexport */ useFilesModals),
28602
- nC: () => (/* reexport */ useFilesSelection)
28603
- });
28604
28468
 
28605
- // UNUSED EXPORTS: addSubfoldersToFiles
28606
28469
 
28607
- // EXTERNAL MODULE: external {"root":"React","commonjs2":"react","commonjs":"react","amd":"react"}
28608
- var external_root_React_commonjs2_react_commonjs_react_amd_react_ = __webpack_require__(5442);
28609
- var external_root_React_commonjs2_react_commonjs_react_amd_react_default = /*#__PURE__*/__webpack_require__.n(external_root_React_commonjs2_react_commonjs_react_amd_react_);
28610
- // EXTERNAL MODULE: ./node_modules/react-i18next/dist/es/index.js + 15 modules
28611
- var es = __webpack_require__(2389);
28612
- ;// ./src/hooks/core/files/useFilesAPIHelpers.ts
28613
- const isRootFolder = (folderId) => {
28614
- return !folderId || folderId === 'root' || folderId === 'folders' || folderId === 'null' || folderId === null;
28615
- };
28616
- const isRootFolderItem = (folder) => {
28617
- return folder.parentId === null || folder.parentId === 'null' || !folder.parentId;
28618
- };
28619
- const filterFolders = (allFolders, params) => {
28620
- const safeFolders = Array.isArray(allFolders) ? allFolders : [];
28621
- if (isRootFolder(params.folderId)) {
28622
- return safeFolders.filter(isRootFolderItem);
28623
- }
28624
- return safeFolders.filter(folder => folder.parentId === params.folderId);
28625
- };
28626
- const addSubfoldersToFiles = async (files, params, getFolders) => {
28627
- var _a;
28628
- if ((params === null || params === void 0 ? void 0 : params.itemType) === 'file')
28629
- return files;
28630
- let allFolders = [];
28631
- if (getFolders) {
28632
- const foldersData = await getFolders({ parentId: params.folderId });
28633
- allFolders = Array.isArray(foldersData) ? foldersData : [];
28634
- }
28635
- else {
28636
- return files;
28470
+
28471
+
28472
+
28473
+
28474
+
28475
+
28476
+
28477
+
28478
+
28479
+
28480
+
28481
+
28482
+
28483
+
28484
+ const UPLOAD_IMAGE_PREPARE_CONCURRENCY = 4;
28485
+ /**
28486
+ * Проверяет, нужно ли сжимать изображение
28487
+ */
28488
+ function shouldCompressImage(fileType) {
28489
+ return !!(fileType === null || fileType === void 0 ? void 0 : fileType.startsWith('image/')) && fileType !== 'image/gif';
28490
+ }
28491
+ /**
28492
+ * Создаёт новый File объект с нормализованным именем
28493
+ * Если имя не изменилось, возвращает оригинальный файл
28494
+ */
28495
+ function createNormalizedFile(file, normalizedName) {
28496
+ if (normalizedName === file.name) {
28497
+ return file;
28637
28498
  }
28638
- let subfolders = filterFolders(allFolders, params);
28639
- if ((_a = params === null || params === void 0 ? void 0 : params.search) === null || _a === void 0 ? void 0 : _a.trim()) {
28640
- const search = params.search.trim().toLowerCase();
28641
- subfolders = subfolders.filter(f => { var _a; return (_a = f.name) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(search); });
28499
+ try {
28500
+ return new File([file], normalizedName, {
28501
+ type: file.type,
28502
+ lastModified: file.lastModified,
28503
+ });
28642
28504
  }
28643
- if (subfolders.length === 0) {
28644
- return files;
28645
- }
28646
- const folderItems = subfolders.map((folder) => ({
28647
- id: folder.id,
28648
- name: folder.name,
28649
- size: undefined,
28650
- date: '-',
28651
- isFolder: true,
28652
- folderId: folder.parentId || '',
28653
- type: '',
28654
- selected: false,
28655
- extension: '',
28656
- childCount: folder.childCount,
28657
- }));
28658
- if ((params === null || params === void 0 ? void 0 : params.itemType) === 'folder') {
28659
- return folderItems;
28660
- }
28661
- // getFiles(itemType:'all') может возвращать папки — исключаем их, т.к. folderItems уже содержит папки из getFolders
28662
- const filesOnly = files.filter(f => !f.isFolder);
28663
- const result = [...folderItems, ...filesOnly];
28664
- return result;
28665
- };
28666
-
28667
- // EXTERNAL MODULE: ./src/hooks/core/useSortAndFilter.ts
28668
- var useSortAndFilter = __webpack_require__(7228);
28669
- // EXTERNAL MODULE: ./src/hooks/core/useFolders.ts
28670
- var useFolders = __webpack_require__(5018);
28671
- // EXTERNAL MODULE: ./src/hooks/useNotifications.tsx
28672
- var useNotifications = __webpack_require__(3435);
28673
- // EXTERNAL MODULE: ./src/hooks/core/files/actions/useFilesDelete.ts
28674
- var useFilesDelete = __webpack_require__(1591);
28675
- // EXTERNAL MODULE: ./src/utils/errorMessages.ts
28676
- var errorMessages = __webpack_require__(6377);
28677
- // EXTERNAL MODULE: ./src/utils/fileValidation.ts
28678
- var fileValidation = __webpack_require__(9142);
28679
- // EXTERNAL MODULE: ./src/utils/urlValidation.ts
28680
- var urlValidation = __webpack_require__(69);
28681
- // EXTERNAL MODULE: ./src/utils/imageCompression.ts
28682
- var imageCompression = __webpack_require__(3270);
28683
- // EXTERNAL MODULE: ./src/utils/cacheClear.ts
28684
- var cacheClear = __webpack_require__(2332);
28685
- // EXTERNAL MODULE: ./src/utils/resolveItemMeta.ts
28686
- var resolveItemMeta = __webpack_require__(1069);
28687
- // EXTERNAL MODULE: ./src/utils/nameNormalization.ts
28688
- var nameNormalization = __webpack_require__(7407);
28689
- // EXTERNAL MODULE: ./src/utils/mimeUtils.ts
28690
- var mimeUtils = __webpack_require__(3662);
28691
- ;// ./src/utils/urlNormalization.ts
28692
- const TRACKING_QUERY_PARAM_PATTERN = /^(utm_[a-z0-9_]+|fbclid|gclid|igshid|mc_cid|mc_eid)$/i;
28693
- /**
28694
- * Нормализует внешний URL для детерминированного поиска файла по источнику.
28695
- * Убирает hash, трекинговые query-параметры, нормализует host и сортирует query.
28696
- */
28697
- function normalizeExternalFileUrl(url) {
28698
- const parsed = new URL(url.trim());
28699
- parsed.hash = '';
28700
- parsed.hostname = parsed.hostname.toLowerCase();
28701
- if ((parsed.protocol === 'https:' && parsed.port === '443') ||
28702
- (parsed.protocol === 'http:' && parsed.port === '80')) {
28703
- parsed.port = '';
28704
- }
28705
- const normalizedParams = Array.from(parsed.searchParams.entries())
28706
- .filter(([key]) => !TRACKING_QUERY_PARAM_PATTERN.test(key))
28707
- .sort(([keyA, valueA], [keyB, valueB]) => {
28708
- const keyCompare = keyA.localeCompare(keyB);
28709
- return keyCompare !== 0 ? keyCompare : valueA.localeCompare(valueB);
28710
- });
28711
- parsed.search = '';
28712
- normalizedParams.forEach(([key, value]) => parsed.searchParams.append(key, value));
28713
- return parsed.toString();
28714
- }
28715
- /**
28716
- * Совпадает ли origin URL с текущей страницей (protocol + host + port).
28717
- * Для same-origin прокси не используем — загрузка напрямую.
28718
- */
28719
- function isUrlSameOriginAsCurrentPage(url) {
28720
- if (typeof window === 'undefined')
28721
- return false;
28722
- try {
28723
- return new URL(url).origin === window.location.origin;
28724
- }
28725
- catch (_a) {
28726
- return false;
28727
- }
28728
- }
28729
-
28730
- // EXTERNAL MODULE: ./src/utils/remoteFileFetchPipeline.ts + 1 modules
28731
- var remoteFileFetchPipeline = __webpack_require__(5597);
28732
- // EXTERNAL MODULE: ./src/utils/dedupeUploadNamesOnClient.ts
28733
- var dedupeUploadNamesOnClient = __webpack_require__(3795);
28734
- // EXTERNAL MODULE: ./src/utils/resolveUniqueUploadName.ts
28735
- var utils_resolveUniqueUploadName = __webpack_require__(7742);
28736
- // EXTERNAL MODULE: ./src/utils/fileFormatUtils.ts
28737
- var fileFormatUtils = __webpack_require__(6825);
28738
- // EXTERNAL MODULE: ./src/hooks/core/context.tsx
28739
- var context = __webpack_require__(8361);
28740
- // EXTERNAL MODULE: ./src/constants/index.tsx
28741
- var constants = __webpack_require__(133);
28742
- // EXTERNAL MODULE: ./src/shared/fileManagerApiError.ts
28743
- var fileManagerApiError = __webpack_require__(7036);
28744
- ;// ./src/hooks/core/files/useFilesAPI.ts
28745
-
28746
-
28747
-
28748
-
28749
-
28750
-
28751
-
28752
-
28753
-
28754
-
28755
-
28756
-
28757
-
28758
-
28759
-
28760
-
28761
-
28762
-
28763
-
28764
-
28765
-
28766
-
28767
-
28768
-
28769
- /**
28770
- * Проверяет, нужно ли сжимать изображение
28771
- */
28772
- function shouldCompressImage(fileType) {
28773
- return !!(fileType === null || fileType === void 0 ? void 0 : fileType.startsWith('image/')) && fileType !== 'image/gif';
28774
- }
28775
- /**
28776
- * Создаёт новый File объект с нормализованным именем
28777
- * Если имя не изменилось, возвращает оригинальный файл
28778
- */
28779
- function createNormalizedFile(file, normalizedName) {
28780
- if (normalizedName === file.name) {
28781
- return file;
28782
- }
28783
- try {
28784
- return new File([file], normalizedName, {
28785
- type: file.type,
28786
- lastModified: file.lastModified,
28787
- });
28788
- }
28789
- catch (e) {
28790
- console.warn('Failed to create normalized File:', e);
28791
- return file; // fallback на оригинал
28505
+ catch (e) {
28506
+ console.warn('Failed to create normalized File:', e);
28507
+ return file; // fallback на оригинал
28792
28508
  }
28793
28509
  }
28794
28510
  function blobToDataUrl(blob) {
@@ -29674,7 +29390,7 @@ const useFilesAPI = () => {
29674
29390
  }
29675
29391
  }, [resolveTargetFolderId]);
29676
29392
  const uploadFilesBatch = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useCallback)(async (filesToUpload, progressCallbacks) => {
29677
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10;
29393
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0;
29678
29394
  if (filesToUpload.length === 0) {
29679
29395
  return { successCount: 0, failCount: 0, successes: [], errors: [], totalCount: 0 };
29680
29396
  }
@@ -29684,9 +29400,8 @@ const useFilesAPI = () => {
29684
29400
  const filtered = names.filter(Boolean);
29685
29401
  return filtered.length > 0 && filtered.length <= MAX_NAMES_PREVIEW ? filtered.join(', ') : null;
29686
29402
  };
29687
- const preparedItems = [];
29688
- for (let index = 0; index < filesToUpload.length; index++) {
29689
- const file = filesToUpload[index];
29403
+ const preparedResults = await mapWithConcurrency(filesToUpload, UPLOAD_IMAGE_PREPARE_CONCURRENCY, async (file, index) => {
29404
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
29690
29405
  if (!(0,mimeUtils/* isAllowedImageUploadFile */.rc)(file)) {
29691
29406
  const payload = await notification.warning({
29692
29407
  id: 'FILES_UPLOAD_UNSUPPORTED_TYPE',
@@ -29698,8 +29413,8 @@ const useFilesAPI = () => {
29698
29413
  folderId: selectedFolder || null,
29699
29414
  },
29700
29415
  });
29701
- (_b = progressCallbacks === null || progressCallbacks === void 0 ? void 0 : progressCallbacks.onError) === null || _b === void 0 ? void 0 : _b.call(progressCallbacks, file.name, payload.message);
29702
- continue;
29416
+ (_a = progressCallbacks === null || progressCallbacks === void 0 ? void 0 : progressCallbacks.onError) === null || _a === void 0 ? void 0 : _a.call(progressCallbacks, file.name, payload.message);
29417
+ return null;
29703
29418
  }
29704
29419
  // Нормализуем имя файла заранее для подготовки изображения и fallback
29705
29420
  const normalizedFileName = (0,nameNormalization/* normalizeFileName */.Px)(file.name, options === null || options === void 0 ? void 0 : options.config);
@@ -29707,24 +29422,19 @@ const useFilesAPI = () => {
29707
29422
  // eslint-disable-next-line sonarjs/no-dead-store
29708
29423
  let fileToUpload = file;
29709
29424
  try {
29710
- if ((0,mimeUtils/* isAllowedImageUploadFile */.rc)(file)) {
29711
- const maxSizeMB = (_d = (_c = options === null || options === void 0 ? void 0 : options.config) === null || _c === void 0 ? void 0 : _c.compressMaxSize) !== null && _d !== void 0 ? _d : fileFormatUtils/* IMAGE_COMPRESSION_DEFAULTS */.a9.MAX_SIZE_MB;
29712
- const maxWidthOrHeight = (_f = (_e = options === null || options === void 0 ? void 0 : options.config) === null || _e === void 0 ? void 0 : _e.compressMaxWidthOrHeight) !== null && _f !== void 0 ? _f : 2000;
29713
- const compressorUrl = (_h = (_g = options === null || options === void 0 ? void 0 : options.config) === null || _g === void 0 ? void 0 : _g.compessorUrl) !== null && _h !== void 0 ? _h : (_j = options === null || options === void 0 ? void 0 : options.config) === null || _j === void 0 ? void 0 : _j.compressorUrl;
29714
- const effectiveMime = ((_k = file.type) === null || _k === void 0 ? void 0 : _k.trim()) || (0,mimeUtils/* inferImageMimeFromFileName */.xU)(file.name) || file.type;
29715
- fileToUpload = await (0,imageCompression/* prepareImageForUpload */.j)(file, {
29716
- maxSizeMB,
29717
- maxWidthOrHeight,
29718
- mimeType: effectiveMime,
29719
- fileName: normalizedFileName,
29720
- libURL: compressorUrl,
29721
- enableCompression: shouldCompressImage(effectiveMime),
29722
- convertSvgToPng: true,
29723
- });
29724
- }
29725
- else {
29726
- fileToUpload = createNormalizedFile(file, normalizedFileName);
29727
- }
29425
+ const maxSizeMB = (_c = (_b = options === null || options === void 0 ? void 0 : options.config) === null || _b === void 0 ? void 0 : _b.compressMaxSize) !== null && _c !== void 0 ? _c : fileFormatUtils/* IMAGE_COMPRESSION_DEFAULTS */.a9.MAX_SIZE_MB;
29426
+ const maxWidthOrHeight = (_e = (_d = options === null || options === void 0 ? void 0 : options.config) === null || _d === void 0 ? void 0 : _d.compressMaxWidthOrHeight) !== null && _e !== void 0 ? _e : 2000;
29427
+ const compressorUrl = (_g = (_f = options === null || options === void 0 ? void 0 : options.config) === null || _f === void 0 ? void 0 : _f.compessorUrl) !== null && _g !== void 0 ? _g : (_h = options === null || options === void 0 ? void 0 : options.config) === null || _h === void 0 ? void 0 : _h.compressorUrl;
29428
+ const effectiveMime = ((_j = file.type) === null || _j === void 0 ? void 0 : _j.trim()) || (0,mimeUtils/* inferImageMimeFromFileName */.xU)(file.name) || file.type;
29429
+ fileToUpload = await (0,imageCompression/* prepareImageForUpload */.j)(file, {
29430
+ maxSizeMB,
29431
+ maxWidthOrHeight,
29432
+ mimeType: effectiveMime,
29433
+ fileName: normalizedFileName,
29434
+ libURL: compressorUrl,
29435
+ enableCompression: shouldCompressImage(effectiveMime),
29436
+ convertSvgToPng: true,
29437
+ });
29728
29438
  }
29729
29439
  catch (e) {
29730
29440
  console.warn('Image preparation failed, uploading original file', e);
@@ -29735,15 +29445,16 @@ const useFilesAPI = () => {
29735
29445
  // тогда результат будет сопоставлен по индексу массива.
29736
29446
  const requestId = `${Date.now()}-${index}-${Math.random().toString(36).slice(2, 8)}`;
29737
29447
  const targetFolderId = await resolveTargetFolderId(fileToUpload, selectedFolder);
29738
- (_l = progressCallbacks === null || progressCallbacks === void 0 ? void 0 : progressCallbacks.onStart) === null || _l === void 0 ? void 0 : _l.call(progressCallbacks, fileToUpload);
29739
- preparedItems.push({
29448
+ (_k = progressCallbacks === null || progressCallbacks === void 0 ? void 0 : progressCallbacks.onStart) === null || _k === void 0 ? void 0 : _k.call(progressCallbacks, fileToUpload);
29449
+ return {
29740
29450
  file: fileToUpload,
29741
29451
  targetFolderId,
29742
29452
  requestId,
29743
29453
  originalFileName: file.name,
29744
29454
  originalIndex: index,
29745
- });
29746
- }
29455
+ };
29456
+ });
29457
+ const preparedItems = preparedResults.filter((item) => item !== null);
29747
29458
  const totalCount = preparedItems.length;
29748
29459
  if (totalCount === 0) {
29749
29460
  return { successCount: 0, failCount: 0, successes: [], errors: [], totalCount: 0 };
@@ -29825,7 +29536,7 @@ const useFilesAPI = () => {
29825
29536
  },
29826
29537
  }, silent || !shouldEmitAnyNotifications);
29827
29538
  };
29828
- const maxFileSize = (_o = (_m = options === null || options === void 0 ? void 0 : options.config) === null || _m === void 0 ? void 0 : _m.maxFileSize) !== null && _o !== void 0 ? _o : fileValidation/* MAX_FILE_SIZE */.w3;
29539
+ const maxFileSize = (_c = (_b = options === null || options === void 0 ? void 0 : options.config) === null || _b === void 0 ? void 0 : _b.maxFileSize) !== null && _c !== void 0 ? _c : fileValidation/* MAX_FILE_SIZE */.w3;
29829
29540
  const emitErrorNotification = (fileName, folderId, innerException, silent, context) => {
29830
29541
  var _a, _b, _c, _d, _e, _f, _g;
29831
29542
  const apiError = (0,errorMessages/* asFileManagerApiError */.sj)(innerException);
@@ -29929,17 +29640,17 @@ const useFilesAPI = () => {
29929
29640
  });
29930
29641
  progressIntervals.forEach(id => window.clearInterval(id));
29931
29642
  progressIntervals.length = 0;
29932
- const detailedList = (_p = batchResult.detailed) !== null && _p !== void 0 ? _p : [];
29643
+ const detailedList = (_d = batchResult.detailed) !== null && _d !== void 0 ? _d : [];
29933
29644
  const detailedMap = new Map(detailedList.filter(detail => detail.requestId).map(detail => [detail.requestId, detail]));
29934
29645
  if (detailedList.length > 0) {
29935
29646
  for (let index = 0; index < uploadItems.length; index++) {
29936
29647
  const item = uploadItems[index];
29937
29648
  // Пытаемся сопоставить по requestId, а при его отсутствии — по порядку.
29938
- const detail = (_q = (item.requestId ? detailedMap.get(item.requestId) : undefined)) !== null && _q !== void 0 ? _q : detailedList[index];
29649
+ const detail = (_e = (item.requestId ? detailedMap.get(item.requestId) : undefined)) !== null && _e !== void 0 ? _e : detailedList[index];
29939
29650
  if (detail && detail.success) {
29940
29651
  const data = detail.data;
29941
- const normalizedFileName = (_r = data === null || data === void 0 ? void 0 : data.name) !== null && _r !== void 0 ? _r : item.resolvedUploadName;
29942
- const folderId = (_u = (_t = (_s = data === null || data === void 0 ? void 0 : data.folderId) !== null && _s !== void 0 ? _s : item.targetFolderId) !== null && _t !== void 0 ? _t : selectedFolder) !== null && _u !== void 0 ? _u : null;
29652
+ const normalizedFileName = (_f = data === null || data === void 0 ? void 0 : data.name) !== null && _f !== void 0 ? _f : item.resolvedUploadName;
29653
+ const folderId = (_j = (_h = (_g = data === null || data === void 0 ? void 0 : data.folderId) !== null && _g !== void 0 ? _g : item.targetFolderId) !== null && _h !== void 0 ? _h : selectedFolder) !== null && _j !== void 0 ? _j : null;
29943
29654
  const fileId = data === null || data === void 0 ? void 0 : data.id;
29944
29655
  // Сохраняем информацию о загруженном файле (включая size для корректного отображения в попапе)
29945
29656
  uploadedFilesMap.set(normalizedFileName, {
@@ -29956,9 +29667,9 @@ const useFilesAPI = () => {
29956
29667
  }
29957
29668
  else {
29958
29669
  const data = detail === null || detail === void 0 ? void 0 : detail.data;
29959
- const normalizedFileName = (_v = data === null || data === void 0 ? void 0 : data.name) !== null && _v !== void 0 ? _v : item.resolvedUploadName;
29960
- const folderId = (_y = (_x = (_w = data === null || data === void 0 ? void 0 : data.folderId) !== null && _w !== void 0 ? _w : item.targetFolderId) !== null && _x !== void 0 ? _x : selectedFolder) !== null && _y !== void 0 ? _y : null;
29961
- await handleFailure(normalizedFileName, folderId, (_z = detail === null || detail === void 0 ? void 0 : detail.error) !== null && _z !== void 0 ? _z : new Error(t('Error uploading file')), {
29670
+ const normalizedFileName = (_k = data === null || data === void 0 ? void 0 : data.name) !== null && _k !== void 0 ? _k : item.resolvedUploadName;
29671
+ const folderId = (_o = (_m = (_l = data === null || data === void 0 ? void 0 : data.folderId) !== null && _l !== void 0 ? _l : item.targetFolderId) !== null && _m !== void 0 ? _m : selectedFolder) !== null && _o !== void 0 ? _o : null;
29672
+ await handleFailure(normalizedFileName, folderId, (_p = detail === null || detail === void 0 ? void 0 : detail.error) !== null && _p !== void 0 ? _p : new Error(t('Error uploading file')), {
29962
29673
  emitNotification: shouldEmitPerItemNotifications,
29963
29674
  fileSize: item.file.size,
29964
29675
  progressCallbackName: item.originalFileName,
@@ -29970,17 +29681,17 @@ const useFilesAPI = () => {
29970
29681
  else if (batchResult.success) {
29971
29682
  for (const item of uploadItems) {
29972
29683
  const normalizedFileName = item.resolvedUploadName;
29973
- const folderId = (_1 = (_0 = item.targetFolderId) !== null && _0 !== void 0 ? _0 : selectedFolder) !== null && _1 !== void 0 ? _1 : null;
29684
+ const folderId = (_r = (_q = item.targetFolderId) !== null && _q !== void 0 ? _q : selectedFolder) !== null && _r !== void 0 ? _r : null;
29974
29685
  await handleSuccess(normalizedFileName, folderId, undefined, {
29975
29686
  emitNotification: shouldEmitPerItemNotifications,
29976
29687
  });
29977
29688
  }
29978
29689
  }
29979
29690
  else {
29980
- const batchError = (_2 = batchResult.error) !== null && _2 !== void 0 ? _2 : new Error(t('Error uploading file'));
29691
+ const batchError = (_s = batchResult.error) !== null && _s !== void 0 ? _s : new Error(t('Error uploading file'));
29981
29692
  for (const item of uploadItems) {
29982
29693
  const normalizedFileName = item.resolvedUploadName;
29983
- const folderId = (_4 = (_3 = item.targetFolderId) !== null && _3 !== void 0 ? _3 : selectedFolder) !== null && _4 !== void 0 ? _4 : null;
29694
+ const folderId = (_u = (_t = item.targetFolderId) !== null && _t !== void 0 ? _t : selectedFolder) !== null && _u !== void 0 ? _u : null;
29984
29695
  await handleFailure(normalizedFileName, folderId, batchError, {
29985
29696
  emitNotification: shouldEmitPerItemNotifications,
29986
29697
  fileSize: item.file.size,
@@ -29993,7 +29704,7 @@ const useFilesAPI = () => {
29993
29704
  catch (error) {
29994
29705
  for (const item of uploadItems) {
29995
29706
  const normalizedFileName = item.resolvedUploadName;
29996
- const folderId = (_6 = (_5 = item.targetFolderId) !== null && _5 !== void 0 ? _5 : selectedFolder) !== null && _6 !== void 0 ? _6 : null;
29707
+ const folderId = (_w = (_v = item.targetFolderId) !== null && _v !== void 0 ? _v : selectedFolder) !== null && _w !== void 0 ? _w : null;
29997
29708
  await handleFailure(normalizedFileName, folderId, error !== null && error !== void 0 ? error : new Error(t('Error uploading file')), {
29998
29709
  emitNotification: shouldEmitPerItemNotifications,
29999
29710
  fileSize: item.file.size,
@@ -30061,10 +29772,10 @@ const useFilesAPI = () => {
30061
29772
  },
30062
29773
  });
30063
29774
  }
30064
- else if (totalCount === 1 && errorDetails.length > 0 && ((_7 = errorDetails[0].error) === null || _7 === void 0 ? void 0 : _7.code)) {
29775
+ else if (totalCount === 1 && errorDetails.length > 0 && ((_x = errorDetails[0].error) === null || _x === void 0 ? void 0 : _x.code)) {
30065
29776
  // Для одного файла используем конкретное сообщение об ошибке
30066
- const codeMessage = (0,errorMessages/* getErrorMessageFromCode */.j2)((_8 = errorDetails[0].error) === null || _8 === void 0 ? void 0 : _8.code, t);
30067
- const failedFileName = (_10 = (_9 = errors[0]) === null || _9 === void 0 ? void 0 : _9.fileName) !== null && _10 !== void 0 ? _10 : '';
29777
+ const codeMessage = (0,errorMessages/* getErrorMessageFromCode */.j2)((_y = errorDetails[0].error) === null || _y === void 0 ? void 0 : _y.code, t);
29778
+ const failedFileName = (_0 = (_z = errors[0]) === null || _z === void 0 ? void 0 : _z.fileName) !== null && _0 !== void 0 ? _0 : '';
30068
29779
  const failureMessage = codeMessage !== null && codeMessage !== void 0 ? codeMessage : (failedFileName ? `${t('Failed to upload files')}: ${failedFileName}` : t('Failed to upload files'));
30069
29780
  notification.error({
30070
29781
  id: 'FILES_UPLOAD_BATCH_FAILED',
@@ -32020,6 +31731,313 @@ const useFilesModals = (fetchFiles) => {
32020
31731
 
32021
31732
 
32022
31733
 
31734
+ /***/ }),
31735
+
31736
+ /***/ 9142:
31737
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
31738
+
31739
+ "use strict";
31740
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
31741
+ /* harmony export */ $5: () => (/* binding */ getUniqueFileName),
31742
+ /* harmony export */ mF: () => (/* binding */ encodeUrlForImageSrc),
31743
+ /* harmony export */ nF: () => (/* binding */ formatFileSizeDisplay),
31744
+ /* harmony export */ v7: () => (/* binding */ formatFileSize),
31745
+ /* harmony export */ w3: () => (/* binding */ MAX_FILE_SIZE)
31746
+ /* harmony export */ });
31747
+ /* unused harmony exports MIN_FILE_SIZE, ALLOWED_FILE_TYPES, ALL_ALLOWED_TYPES, validateFile, validateFiles, validateFileUrl, getFileCategory, formatFileSizeShort, sanitizeFileName */
31748
+ /* harmony import */ var antd__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(550);
31749
+ /* harmony import */ var antd__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(antd__WEBPACK_IMPORTED_MODULE_0__);
31750
+ /* harmony import */ var _i18n__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3829);
31751
+
31752
+
31753
+ const MAX_FILE_SIZE = 50 * 1024 * 1024;
31754
+ const MIN_FILE_SIZE = 1;
31755
+ const ALLOWED_FILE_TYPES = {
31756
+ images: ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml', 'image/bmp', 'image/tiff'],
31757
+ documents: [
31758
+ 'application/pdf',
31759
+ 'text/plain',
31760
+ 'application/msword',
31761
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
31762
+ ],
31763
+ archives: ['application/zip', 'application/x-rar-compressed', 'application/x-7z-compressed'],
31764
+ videos: ['video/mp4', 'video/avi', 'video/quicktime', 'video/x-msvideo'],
31765
+ audio: ['audio/mpeg', 'audio/wav', 'audio/ogg'],
31766
+ };
31767
+ const ALL_ALLOWED_TYPES = Object.values(ALLOWED_FILE_TYPES).flat();
31768
+ const validateFile = (file, options = {}) => {
31769
+ const { maxSize = MAX_FILE_SIZE, minSize = MIN_FILE_SIZE, allowedTypes = ALL_ALLOWED_TYPES, allowAllTypes = false, maxFileNameLength = 255, } = options;
31770
+ const errors = [];
31771
+ const warnings = [];
31772
+ if (file.size > maxSize) {
31773
+ errors.push(i18n.t('File too large. Maximum size: {{size}}', { size: formatFileSize(maxSize) }));
31774
+ }
31775
+ if (file.size < minSize) {
31776
+ errors.push(i18n.t('File too small. Minimum size: {{size}}', { size: formatFileSize(minSize) }));
31777
+ }
31778
+ if (!allowAllTypes && !allowedTypes.includes(file.type)) {
31779
+ errors.push(i18n.t('Unsupported file type: {{type}}', { type: file.type || i18n.t('unknown') }));
31780
+ }
31781
+ if (file.name.length > maxFileNameLength) {
31782
+ errors.push(i18n.t('File name is too long. Maximum {{max}} characters', { max: maxFileNameLength }));
31783
+ }
31784
+ const invalidChars = /[<>:"/\\|?*()#]/;
31785
+ const hasControlChars = file.name.split('').some(char => {
31786
+ const code = char.charCodeAt(0);
31787
+ return code >= 0 && code <= 31;
31788
+ });
31789
+ if (invalidChars.test(file.name) || hasControlChars) {
31790
+ errors.push(i18n.t('File name contains invalid characters'));
31791
+ }
31792
+ if (file.size > 10 * 1024 * 1024) {
31793
+ warnings.push(i18n.t('Large file may take a long time to upload'));
31794
+ }
31795
+ return {
31796
+ isValid: errors.length === 0,
31797
+ errors,
31798
+ warnings,
31799
+ };
31800
+ };
31801
+ const validateFiles = (files, options = {}) => {
31802
+ const valid = [];
31803
+ const invalid = [];
31804
+ files.forEach(file => {
31805
+ const result = validateFile(file, options);
31806
+ if (result.isValid) {
31807
+ valid.push(file);
31808
+ }
31809
+ else {
31810
+ invalid.push({ file, result });
31811
+ }
31812
+ });
31813
+ return { valid, invalid };
31814
+ };
31815
+ const validateFileUrl = async (url) => {
31816
+ const errors = [];
31817
+ let notified = false;
31818
+ let lastError = undefined;
31819
+ const notifyError = (message) => {
31820
+ notified = true;
31821
+ notification.error({
31822
+ key: 'FILE_URL_VALIDATION_FAILED',
31823
+ title: message,
31824
+ placement: 'bottomRight',
31825
+ description: url,
31826
+ });
31827
+ };
31828
+ try {
31829
+ new URL(url);
31830
+ }
31831
+ catch (error) {
31832
+ console.error('Invalid URL:', error);
31833
+ lastError = error;
31834
+ const message = i18n.t('Invalid file URL');
31835
+ errors.push(message);
31836
+ notifyError(message);
31837
+ return { isValid: false, errors };
31838
+ }
31839
+ try {
31840
+ const response = await fetch(url, { method: 'HEAD' });
31841
+ if (!response.ok) {
31842
+ errors.push(i18n.t('File unavailable: {{status}} {{statusText}}', {
31843
+ status: response.status,
31844
+ statusText: response.statusText,
31845
+ }));
31846
+ }
31847
+ const contentLength = response.headers.get('content-length');
31848
+ if (contentLength && parseInt(contentLength) > MAX_FILE_SIZE) {
31849
+ errors.push(i18n.t('File too large: {{size}}', { size: formatFileSize(parseInt(contentLength)) }));
31850
+ }
31851
+ const contentType = response.headers.get('content-type');
31852
+ if (contentType && !ALL_ALLOWED_TYPES.includes(contentType)) {
31853
+ errors.push(i18n.t('Unsupported file type: {{contentType}}', { contentType }));
31854
+ }
31855
+ }
31856
+ catch (error) {
31857
+ console.error('Failed to validate file URL:', error);
31858
+ lastError = error;
31859
+ const message = i18n.t('Unable to validate file URL');
31860
+ errors.push(message);
31861
+ notifyError(message);
31862
+ }
31863
+ if (errors.length > 0 && !notified) {
31864
+ notifyError(i18n.t('Unable to validate file URL'));
31865
+ }
31866
+ return {
31867
+ isValid: errors.length === 0,
31868
+ errors,
31869
+ };
31870
+ };
31871
+ const getFileCategory = (file) => {
31872
+ const type = file.type;
31873
+ if (ALLOWED_FILE_TYPES.images.includes(type))
31874
+ return 'image';
31875
+ if (ALLOWED_FILE_TYPES.documents.includes(type))
31876
+ return 'document';
31877
+ if (ALLOWED_FILE_TYPES.archives.includes(type))
31878
+ return 'archive';
31879
+ if (ALLOWED_FILE_TYPES.videos.includes(type))
31880
+ return 'video';
31881
+ if (ALLOWED_FILE_TYPES.audio.includes(type))
31882
+ return 'audio';
31883
+ return 'other';
31884
+ };
31885
+ const formatFileSize = (bytes) => {
31886
+ if (bytes === 0)
31887
+ return '0 Bytes';
31888
+ const k = 1024;
31889
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
31890
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
31891
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
31892
+ };
31893
+ const formatFileSizeShort = (bytes) => {
31894
+ if (!bytes || bytes <= 0)
31895
+ return '';
31896
+ const KILOBYTE = 1024;
31897
+ const MEGABYTE = KILOBYTE * 1024;
31898
+ if (bytes < MEGABYTE) {
31899
+ const kb = Math.round(bytes / KILOBYTE);
31900
+ return `${kb} Kb`;
31901
+ }
31902
+ const mb = bytes / MEGABYTE;
31903
+ return `${mb.toFixed(2)} Mb`;
31904
+ };
31905
+ const formatFileSizeDisplay = (value) => {
31906
+ if (value === undefined || value === null)
31907
+ return '';
31908
+ if (typeof value === 'number') {
31909
+ return formatFileSizeShort(value);
31910
+ }
31911
+ const normalized = String(value).trim();
31912
+ if (!normalized)
31913
+ return '';
31914
+ const parsed = Number(normalized);
31915
+ if (!Number.isNaN(parsed)) {
31916
+ return formatFileSizeShort(parsed);
31917
+ }
31918
+ return normalized;
31919
+ };
31920
+ /**
31921
+ * Генерирует уникальное имя файла: если name уже есть в existingNames, добавляет _1 (или _2, _3 и т.д.).
31922
+ * @param name — исходное имя файла (с расширением)
31923
+ * @param existingNames — массив имён файлов/папок в целевой директории
31924
+ */
31925
+ const getUniqueFileName = (name, existingNames) => {
31926
+ const safeNames = existingNames.filter((n) => typeof n === 'string');
31927
+ const set = new Set(safeNames.map(n => n.toLowerCase()));
31928
+ const safeName = typeof name === 'string' ? name : String(name !== null && name !== void 0 ? name : '');
31929
+ if (!set.has(safeName.toLowerCase()))
31930
+ return safeName;
31931
+ const lastDot = safeName.lastIndexOf('.');
31932
+ const baseName = lastDot > 0 ? safeName.slice(0, lastDot) : safeName;
31933
+ const ext = lastDot > 0 ? safeName.slice(lastDot) : '';
31934
+ let suffix = 1;
31935
+ let candidate;
31936
+ do {
31937
+ candidate = `${baseName}_${suffix}${ext}`;
31938
+ suffix++;
31939
+ } while (set.has(candidate.toLowerCase()));
31940
+ return candidate;
31941
+ };
31942
+ /**
31943
+ * Кодирует URL для безопасного использования в img src. Решает проблему с кириллицей и другими
31944
+ * не-ASCII символами в пути (например, emcdn.ru/.../Снимок_экрана.png).
31945
+ * data: и blob: URLs возвращаются без изменений.
31946
+ */
31947
+ const encodeUrlForImageSrc = (url) => {
31948
+ if (!url || typeof url !== 'string')
31949
+ return undefined;
31950
+ if (url.startsWith('data:') || url.startsWith('blob:'))
31951
+ return url;
31952
+ try {
31953
+ if (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('/')) {
31954
+ const parsed = new URL(url.startsWith('/') ? `https://x${url}` : url);
31955
+ const origin = url.startsWith('/') ? '' : `${parsed.protocol}//${parsed.host}`;
31956
+ const pathEncoded = parsed.pathname
31957
+ .split('/')
31958
+ .map(seg => {
31959
+ if (!seg)
31960
+ return '';
31961
+ try {
31962
+ return encodeURIComponent(decodeURIComponent(seg));
31963
+ }
31964
+ catch (_a) {
31965
+ return encodeURIComponent(seg);
31966
+ }
31967
+ })
31968
+ .join('/');
31969
+ const result = url.startsWith('/') ? pathEncoded + (parsed.search || '') + (parsed.hash || '') : `${origin}${pathEncoded}${parsed.search}${parsed.hash}`;
31970
+ return result;
31971
+ }
31972
+ }
31973
+ catch (_a) {
31974
+ // если парсинг не удался, возвращаем как есть
31975
+ }
31976
+ return url;
31977
+ };
31978
+ /**
31979
+ * @deprecated Эта функция оставлена только для обратной совместимости.
31980
+ * Используйте {@link normalizeFileName} из `nameNormalization.ts` для настраиваемой нормализации имен файлов.
31981
+ *
31982
+ * Упрощенная санитизация имени файла:
31983
+ * - Заменяет запрещенные символы на `_`
31984
+ * - Заменяет пробелы на `_`
31985
+ * - Удаляет множественные подчеркивания
31986
+ * - Ограничивает длину до 255 символов
31987
+ */
31988
+ const sanitizeFileName = (fileName) => {
31989
+ return fileName
31990
+ .replace(/[<>:"/\\|?*()#]/g, '_')
31991
+ .split('')
31992
+ .map(char => {
31993
+ const code = char.charCodeAt(0);
31994
+ return code >= 0 && code <= 31 ? '_' : char;
31995
+ })
31996
+ .join('')
31997
+ .replace(/\s+/g, '_')
31998
+ .replace(/_{2,}/g, '_')
31999
+ .substring(0, 255);
32000
+ };
32001
+
32002
+
32003
+ /***/ }),
32004
+
32005
+ /***/ 9287:
32006
+ /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
32007
+
32008
+ // Imports
32009
+ var ___CSS_LOADER_API_SOURCEMAP_IMPORT___ = __webpack_require__(1354);
32010
+ var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(6314);
32011
+ var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);
32012
+ // Module
32013
+ ___CSS_LOADER_EXPORT___.push([module.id, `.FolderSidebar-module__button__Yf2rX.ant-btn.ant-btn-icon-only{padding:calc(var(--paddingXXS)*1px) calc(var(--paddingSM)*1px);cursor:pointer;color:var(--colorTextSecondary);display:flex;gap:calc(var(--marginXS)*1px);align-items:center;justify-content:center;border-radius:calc(var(--borderRadiusSM)*1px);transition:all var(--motionDurationFast);box-shadow:none;border:none;width:32px;height:32px}.FolderSidebar-module__button__Yf2rX.ant-btn.ant-btn-icon-only:hover{background-color:var(--colorBgLayout);color:inherit}.FolderSidebar-module__button__Yf2rX:hover{background-color:var(--colorBgContainer);color:var(--colorPrimary)}.FolderSidebar-module__foldersTitle__Uh6u7{display:flex;align-items:center;flex:1;cursor:pointer}.FolderSidebar-module__folderActions__qA2Bk{position:absolute;top:4px;right:4px;display:flex;align-items:center;gap:4px;z-index:10}.FolderSidebar-module__folderActions__qA2Bk .FolderSidebar-module__button__Yf2rX.FolderSidebar-module__ant-btn__LXAXm.FolderSidebar-module__ant-btn-icon-only__j_wVF{padding:calc(var(--paddingXXS)*1px) calc(var(--paddingSM)*1px);cursor:pointer;color:var(--colorTextSecondary);display:flex;gap:calc(var(--marginXS)*1px);align-items:center;justify-content:center;border-radius:calc(var(--borderRadiusSM)*1px);transition:all var(--motionDurationFast);box-shadow:none;border:none;width:32px;height:32px}.FolderSidebar-module__folderActions__qA2Bk .FolderSidebar-module__button__Yf2rX.FolderSidebar-module__ant-btn__LXAXm.FolderSidebar-module__ant-btn-icon-only__j_wVF:hover{background-color:var(--colorBgLayout);color:inherit}.FolderSidebar-module__customExpandIcon__IhZJl{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;margin-right:8px;cursor:pointer;color:#666;font-size:10px;transition:transform .2s ease}.FolderSidebar-module__customExpandIcon__IhZJl:hover{color:var(--colorPrimary)}.FolderSidebar-module__fileCount__bD9DD{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:100%;padding:0 6px;margin-left:8px;color:#8d8d8d;font-size:14px;font-weight:500;transition:all .2s ease;white-space:nowrap;cursor:default;flex-shrink:0;margin-right:10px}.ant-tree .ant-tree-node-content-wrapper.ant-tree-node-selected{background-color:rgba(0,0,0,0) !important}.FolderSidebar-module__folderMenu__JCzLF .ant-tree-treenode-selected .folder-label span,.FolderSidebar-module__folderMenu__JCzLF .ant-tree .ant-tree-node-content-wrapper.ant-tree-node-selected .folderLabelItem,.FolderSidebar-module__folderMenu__JCzLF .ant-tree-treenode-selected .folderLabelItem,.FolderSidebar-module__folderMenu__JCzLF .ant-tree-node-selected .folderLabelItem{font-weight:600 !important}.FolderSidebar-module__uploadButton__QQUid{width:100%;justify-content:flex-start}.FolderSidebar-module__uploadButton__QQUid button:hover{background-color:var(--colorPrimaryHover) !important}.FolderSidebar-module__folderMenu__JCzLF{padding-left:0;padding-right:0}.FolderSidebar-module__folderMenu__JCzLF .ant-tree-treenode{display:flex;align-items:center;justify-content:space-between;padding:4px 0;border-radius:4px;transition:background-color .2s ease;width:100%;margin-bottom:0}.FolderSidebar-module__folderMenu__JCzLF .ant-tree-node-content-wrapper{flex:1;align-items:center;padding:0;transition:background-color .2s ease}.FolderSidebar-module__folderMenu__JCzLF .ant-tree-node-content-wrapper:hover{background-color:rgba(0,0,0,0)}.FolderSidebar-module__folderMenu__JCzLF .ant-tree-switcher{height:24px;width:24px;margin-top:5px;display:flex !important;align-items:center;justify-content:center;transition:color .2s ease;flex-shrink:0}.FolderSidebar-module__folderMenu__JCzLF .ant-tree-switcher-noop{width:24px}.FolderSidebar-module__folderMenu__JCzLF .ant-tree-switcher-line-icon{color:#d9d9d9}.FolderSidebar-module__folderMenu__JCzLF .FolderSidebar-module__folderItemContainer__byl5b{width:100%;display:flex;align-items:center;justify-content:space-between;padding:2px 0;min-width:0}.FolderSidebar-module__folderMenu__JCzLF .folder-label{display:flex;align-items:center;flex:1;color:#595959;font-size:14px;min-width:0;overflow:hidden}.FolderSidebar-module__folderMenu__JCzLF .ant-tree-treenode[data-key="folders"]{border-bottom:1px solid var(--colorBorderSecondary);margin-bottom:8px;padding-bottom:8px}.FolderSidebar-module__folderMenu__JCzLF .ant-tree-treenode[data-key="folders"].ant-tree-treenode-selected{background-color:rgba(0,0,0,0) !important}.FolderSidebar-module__folderMenu__JCzLF .ant-tree-treenode[data-key="folders"] .ant-tree-node-content-wrapper{padding:0}.FolderSidebar-module__folderMenu__JCzLF .ant-tree-treenode[data-key="folders"] .ant-tree-node-content-wrapper:hover{background-color:rgba(0,0,0,0) !important}.FolderSidebar-module__folderMenu__JCzLF .ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0}.FolderSidebar-module__folderMenu__JCzLF .FolderSidebar-module__emptyFolderContainer__WK6tx{padding:20px;text-align:center}.FolderSidebar-module__folderMenu__JCzLF .ant-tree > .ant-tree-treenode{padding-left:0 !important}.FolderSidebar-module__folderIcon__UoZzH{margin-right:8px;font-size:18px;flex-shrink:0}.FolderSidebar-module__folderLabelItem__uJ062{font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0;user-select:none}.FolderSidebar-module__foldersTitleItem__gapOI{display:flex;align-items:center;flex:1;cursor:pointer;min-width:0;overflow:hidden}.FolderSidebar-module__folderLabel__fHWMp{display:flex;align-items:center;flex:1;min-width:0;overflow:hidden}.FolderSidebar-module__foldersContainer__zdBVU{position:relative;width:100%}.FolderSidebar-module__uploadButton__QQUid .FolderSidebar-module__ant-btn__LXAXm,.FolderSidebar-module__uploadButton__QQUid .ant-btn{display:flex;align-items:center;justify-content:center;gap:3px}.FolderSidebar-module__buttonWrapper__a9Qi5{margin-bottom:24px;display:flex;gap:4px}.FolderSidebar-module__buttonWrapper__a9Qi5>:first-child{flex:1;height:40px;padding-left:10px}.FolderSidebar-module__buttonWrapper__a9Qi5>:last-child{width:40px;height:40px;flex-shrink:0}.FolderSidebar-module__uploadButtonIcon__mWQmq{font-size:20px}.FolderSidebar-module__uploadButtonTitle__wk_o6{line-height:1em}.FolderSidebar-module__emptyFolderContainer__WK6tx{user-select:none;cursor:default;color:var(--colorTextTertiary)}.FolderSidebar-module__folderItemContainer__byl5b{width:100%;display:flex;align-items:center;justify-content:space-between;padding:2px 0;min-width:0}`, "",{"version":3,"sources":["webpack://./src/components/FolderSidebar/FolderSidebar.module.scss"],"names":[],"mappings":"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","sourcesContent":[".button:global(.ant-btn.ant-btn-icon-only) {\r\n padding: calc(var(--paddingXXS) * 1px) calc(var(--paddingSM) * 1px);\r\n cursor: pointer;\r\n color: var(--colorTextSecondary);\r\n display: flex;\r\n gap: calc(var(--marginXS) * 1px);\r\n align-items: center;\r\n justify-content: center;\r\n border-radius: calc(var(--borderRadiusSM) * 1px);\r\n transition: all var(--motionDurationFast);\r\n box-shadow: none;\r\n\r\n border: none;\r\n width: 32px;\r\n height: 32px;\r\n\r\n &:hover {\r\n background-color: var(--colorBgLayout);\r\n color: inherit;\r\n }\r\n}\r\n\r\n.button:hover {\r\n background-color: var(--colorBgContainer);\r\n color: var(--colorPrimary);\r\n}\r\n\r\n.foldersTitle {\r\n display: flex;\r\n align-items: center;\r\n flex: 1;\r\n cursor: pointer;\r\n}\r\n\r\n.folderActions {\r\n position: absolute;\r\n top: 4px; /* Выравниваем по заголовку Folders */\r\n right: 4px;\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n z-index: 10;\r\n\r\n .button.ant-btn.ant-btn-icon-only {\r\n padding: calc(var(--paddingXXS) * 1px) calc(var(--paddingSM) * 1px);\r\n cursor: pointer;\r\n color: var(--colorTextSecondary);\r\n display: flex;\r\n gap: calc(var(--marginXS) * 1px);\r\n align-items: center;\r\n justify-content: center;\r\n border-radius: calc(var(--borderRadiusSM) * 1px);\r\n transition: all var(--motionDurationFast);\r\n box-shadow: none;\r\n\r\n border: none;\r\n width: 32px;\r\n height: 32px;\r\n\r\n &:hover {\r\n background-color: var(--colorBgLayout);\r\n color: inherit;\r\n }\r\n }\r\n}\r\n\r\n.customExpandIcon {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 16px;\r\n height: 16px;\r\n margin-right: 8px;\r\n cursor: pointer;\r\n color: #666;\r\n font-size: 10px;\r\n transition: transform 0.2s ease;\r\n}\r\n\r\n.customExpandIcon:hover {\r\n color: var(--colorPrimary);\r\n}\r\n\r\n.fileCount {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n min-width: 20px;\r\n height: 100%;\r\n padding: 0 6px;\r\n margin-left: 8px;\r\n color: #8D8D8D;\r\n font-size: 14px;\r\n font-weight: 500;\r\n transition: all 0.2s ease;\r\n white-space: nowrap;\r\n cursor: default;\r\n flex-shrink: 0; /* Запрещаем сжатие счетчика */\r\n margin-right: 10px;\r\n}\r\n\r\n// .fileCount:hover {\r\n// border-color: var(--colorPrimary);\r\n// color: var(--colorPrimary);\r\n// }\r\n\r\n:global(.ant-tree .ant-tree-node-content-wrapper.ant-tree-node-selected) {\r\n background-color: transparent !important;\r\n}\r\n\r\n/* :global(.ant-tree .ant-tree-node-content-wrapper.ant-tree-node-selected):hover {\r\n color: red !important;\r\n} */\r\n\r\n/* Стили для выделения текста в выбранной папке */\r\n.folderMenu :global(.ant-tree-treenode-selected .folder-label span),\r\n.folderMenu :global(.ant-tree .ant-tree-node-content-wrapper.ant-tree-node-selected .folderLabelItem),\r\n.folderMenu :global(.ant-tree-treenode-selected .folderLabelItem),\r\n.folderMenu :global(.ant-tree-node-selected .folderLabelItem) {\r\n font-weight: 600 !important;\r\n}\r\n\r\n/* Стили для иконки в выбранной папке */\r\n/* .folderMenu :global(.ant-tree-treenode-selected .folderIcon),\r\n.folderMenu :global(.ant-tree-node-selected .folderIcon) {\r\n color: red !important;\r\n} */\r\n\r\n/* CSS переменные для упрощения расчетов */\r\n\r\n.uploadButton {\r\n width: 100%;\r\n justify-content: flex-start;\r\n\r\n & button:hover {\r\n background-color: var(--colorPrimaryHover) !important;\r\n }\r\n}\r\n\r\n.folderMenu {\r\n padding-left: 0;\r\n padding-right: 0; /* Место для кнопки справа */\r\n}\r\n\r\n.folderMenu :global(.ant-tree-treenode) {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 4px 0;\r\n border-radius: 4px;\r\n transition: background-color 0.2s ease;\r\n width: 100%;\r\n margin-bottom: 0;\r\n}\r\n\r\n.folderMenu :global(.ant-tree-node-content-wrapper) {\r\n flex: 1;\r\n /* display: flex; */\r\n align-items: center;\r\n padding: 0;\r\n /* border-radius: 4px; */\r\n transition: background-color 0.2s ease;\r\n}\r\n\r\n.folderMenu :global(.ant-tree-node-content-wrapper:hover) {\r\n background-color: transparent;\r\n}\r\n\r\n.folderMenu :global(.ant-tree-switcher) {\r\n height: 24px;\r\n width: 24px;\r\n margin-top: 5px;\r\n display: flex !important;\r\n align-items: center;\r\n justify-content: center;\r\n transition: color 0.2s ease;\r\n flex-shrink: 0; /* Запрещаем сжатие переключателя */\r\n}\r\n\r\n.folderMenu :global(.ant-tree-switcher-noop) {\r\n width: 24px;\r\n}\r\n\r\n/* Стили для линий дерева */\r\n.folderMenu :global(.ant-tree-switcher-line-icon) {\r\n color: #d9d9d9;\r\n}\r\n\r\n/* Улучшение отображения folder-item-container */\r\n.folderMenu .folderItemContainer {\r\n width: 100%;\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 2px 0;\r\n min-width: 0; /* Позволяет flex-элементам сжиматься */\r\n}\r\n\r\n.folderMenu :global(.folder-label) {\r\n display: flex;\r\n align-items: center;\r\n flex: 1;\r\n color: #595959;\r\n font-size: 14px;\r\n min-width: 0;\r\n overflow: hidden;\r\n}\r\n\r\n/* Стили для корневого элемента Folders */\r\n.folderMenu :global(.ant-tree-treenode[data-key='folders']) {\r\n border-bottom: 1px solid var(--colorBorderSecondary);\r\n margin-bottom: 8px;\r\n padding-bottom: 8px;\r\n}\r\n\r\n.folderMenu :global(.ant-tree-treenode[data-key='folders'].ant-tree-treenode-selected) {\r\n background-color: transparent !important;\r\n}\r\n\r\n.folderMenu :global(.ant-tree-treenode[data-key='folders'] .ant-tree-node-content-wrapper) {\r\n padding: 0;\r\n}\r\n\r\n.folderMenu :global(.ant-tree-treenode[data-key='folders'] .ant-tree-node-content-wrapper:hover) {\r\n background-color: transparent !important;\r\n}\r\n\r\n/* Стили для ellipsis - удаляем max-width для гибкости */\r\n.folderMenu :global(.ellipsis) {\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n flex: 1;\r\n min-width: 0;\r\n}\r\n\r\n/* Стили для пустого состояния */\r\n.folderMenu .emptyFolderContainer {\r\n padding: 20px;\r\n text-align: center;\r\n}\r\n\r\n/* Убираем отступы у корневого узла */\r\n.folderMenu :global(.ant-tree > .ant-tree-treenode) {\r\n padding-left: 0 !important;\r\n}\r\n.folderIcon {\r\n margin-right: 8px;\r\n font-size: 18px;\r\n flex-shrink: 0; /* Запрещаем сжатие иконки */\r\n}\r\n\r\n.folderLabelItem {\r\n font-size: 14px;\r\n white-space: nowrap; /* Запрещаем перенос строк */\r\n overflow: hidden; /* Скрываем переполнение */\r\n text-overflow: ellipsis; /* Показываем троеточие */\r\n flex: 1; /* Занимает доступное место */\r\n min-width: 0; /* Позволяет сжиматься */\r\n user-select: none;\r\n}\r\n\r\n.foldersTitleItem {\r\n display: flex;\r\n align-items: center;\r\n flex: 1;\r\n cursor: pointer;\r\n min-width: 0; /* Позволяет сжиматься */\r\n overflow: hidden; /* Скрываем переполнение */\r\n}\r\n\r\n/* .foldersTitleItem .folderLabelItem {\r\n font-weight: 500;\r\n color: #262626;\r\n} */\r\n\r\n/* Класс для заголовка папок */\r\n.folderLabel {\r\n display: flex;\r\n align-items: center;\r\n flex: 1;\r\n min-width: 0;\r\n overflow: hidden;\r\n}\r\n\r\n.foldersContainer {\r\n position: relative;\r\n width: 100%;\r\n}\r\n\r\n/* Дополнительные стили для улучшения визуала */\r\n/* .folderMenu :global(.ant-tree-treenode:hover .folder-label span) {\r\n color: #1890ff;\r\n} */\r\n\r\n/* Стили для дивайдера между папками */\r\n\r\n\r\n.uploadButton .ant-btn,\r\n.uploadButton :global(.ant-btn) {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 3px;\r\n}\r\n/* .folderMenu :global(.ant-tree-treenode-selected) .folderLabelItem {\r\n color: #1890ff !important;\r\n} */\r\n\r\n// .folderMenu :global(.ant-tree-treenode-selected) .folderLabel .folderLabelItem,\r\n// .folderMenu :global(.ant-tree-treenode-selected) .folderLabel .folderIcon,\r\n// .folderMenu :global(.ant-tree-treenode-selected) .folderLabelItem {\r\n// color: var(--colorLink);\r\n// }\r\n\r\n\r\n.buttonWrapper {\r\n // overflow: hidden;\r\n margin-bottom: 24px;\r\n display: flex;\r\n gap: 4px;\r\n // border-radius: calc(var(--borderRadius) * 1px);\r\n \r\n // Левая кнопка занимает всю доступную ширину\r\n > :first-child {\r\n flex: 1;\r\n height: 40px;\r\n padding-left: 10px;\r\n }\r\n \r\n // Правая кнопка строго 40px\r\n > :last-child {\r\n width: 40px;\r\n height: 40px;\r\n flex-shrink: 0;\r\n }\r\n}\r\n\r\n.uploadButtonIcon {\r\n font-size: 20px;\r\n}\r\n\r\n.uploadButtonTitle {\r\n line-height: 1em;\r\n}\r\n\r\n.emptyFolderContainer {\r\n user-select: none;\r\n cursor: default;\r\n color: var(--colorTextTertiary);\r\n}\r\n\r\n.folderItemContainer {\r\n width: 100%;\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 2px 0;\r\n min-width: 0; /* Позволяет flex-элементам сжиматься */\r\n}\r\n"],"sourceRoot":""}]);
32014
+ // Exports
32015
+ ___CSS_LOADER_EXPORT___.locals = {
32016
+ "button": `FolderSidebar-module__button__Yf2rX`,
32017
+ "foldersTitle": `FolderSidebar-module__foldersTitle__Uh6u7`,
32018
+ "folderActions": `FolderSidebar-module__folderActions__qA2Bk`,
32019
+ "ant-btn": `FolderSidebar-module__ant-btn__LXAXm`,
32020
+ "antBtn": `FolderSidebar-module__ant-btn__LXAXm`,
32021
+ "ant-btn-icon-only": `FolderSidebar-module__ant-btn-icon-only__j_wVF`,
32022
+ "antBtnIconOnly": `FolderSidebar-module__ant-btn-icon-only__j_wVF`,
32023
+ "customExpandIcon": `FolderSidebar-module__customExpandIcon__IhZJl`,
32024
+ "fileCount": `FolderSidebar-module__fileCount__bD9DD`,
32025
+ "folderMenu": `FolderSidebar-module__folderMenu__JCzLF`,
32026
+ "uploadButton": `FolderSidebar-module__uploadButton__QQUid`,
32027
+ "folderItemContainer": `FolderSidebar-module__folderItemContainer__byl5b`,
32028
+ "emptyFolderContainer": `FolderSidebar-module__emptyFolderContainer__WK6tx`,
32029
+ "folderIcon": `FolderSidebar-module__folderIcon__UoZzH`,
32030
+ "folderLabelItem": `FolderSidebar-module__folderLabelItem__uJ062`,
32031
+ "foldersTitleItem": `FolderSidebar-module__foldersTitleItem__gapOI`,
32032
+ "folderLabel": `FolderSidebar-module__folderLabel__fHWMp`,
32033
+ "foldersContainer": `FolderSidebar-module__foldersContainer__zdBVU`,
32034
+ "buttonWrapper": `FolderSidebar-module__buttonWrapper__a9Qi5`,
32035
+ "uploadButtonIcon": `FolderSidebar-module__uploadButtonIcon__mWQmq`,
32036
+ "uploadButtonTitle": `FolderSidebar-module__uploadButtonTitle__wk_o6`
32037
+ };
32038
+ module.exports = ___CSS_LOADER_EXPORT___;
32039
+
32040
+
32023
32041
  /***/ }),
32024
32042
 
32025
32043
  /***/ 9589:
@@ -33361,8 +33379,8 @@ const SearchInput = ({ onSearch, placeholder, className }) => {
33361
33379
  ;// ./src/components/SearchInput/index.ts
33362
33380
 
33363
33381
 
33364
- // EXTERNAL MODULE: ./src/hooks/core/files/index.ts + 17 modules
33365
- var core_files = __webpack_require__(9557);
33382
+ // EXTERNAL MODULE: ./src/hooks/core/files/index.ts + 18 modules
33383
+ var core_files = __webpack_require__(8573);
33366
33384
  ;// ./src/hooks/core/types.ts
33367
33385
  /** Полный список опций сортировки в дропдауне (порядок отображения). */
33368
33386
  const ALL_SORT_OPTIONS = [