@emailmaker/filemanager 0.10.91 → 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',
@@ -31890,134 +31601,441 @@ const useFilesDeleteModal = (fetchFiles) => {
31890
31601
  },
31891
31602
  });
31892
31603
  }
31893
- else {
31894
- // Полное удаление (в том числе из корзины)
31895
- await notification.success({
31896
- id: 'FILES_DELETE_SUCCESS',
31897
- message: t('Files deleted'),
31898
- placement: 'bottomRight',
31899
- data: {
31900
- successCount,
31901
- totalCount,
31902
- itemIds,
31903
- folderId: selectedFolder || null,
31904
- },
31905
- });
31604
+ else {
31605
+ // Полное удаление (в том числе из корзины)
31606
+ await notification.success({
31607
+ id: 'FILES_DELETE_SUCCESS',
31608
+ message: t('Files deleted'),
31609
+ placement: 'bottomRight',
31610
+ data: {
31611
+ successCount,
31612
+ totalCount,
31613
+ itemIds,
31614
+ folderId: selectedFolder || null,
31615
+ },
31616
+ });
31617
+ }
31618
+ }
31619
+ if (failCount > 0) {
31620
+ const failureNames = getItemNamesByIds(itemIds);
31621
+ const preview = getNamesPreview(failureNames);
31622
+ const totalCount = itemIds.length;
31623
+ const hasSingleItemWithCode = totalCount === 1 && errorDetails.length > 0 && ((_a = errorDetails[0].error) === null || _a === void 0 ? void 0 : _a.code);
31624
+ const contextIsTrashMove = (config === null || config === void 0 ? void 0 : config.enableTrashFolder) && !isTrashContext;
31625
+ const defaultMessage = contextIsTrashMove
31626
+ ? t('При перемещении в корзину произошли ошибки')
31627
+ : t('При удалении произошли ошибки');
31628
+ const codeMessage = hasSingleItemWithCode ? (0,errorMessages/* getErrorMessageFromCode */.j2)((_b = errorDetails[0].error) === null || _b === void 0 ? void 0 : _b.code, t) : undefined;
31629
+ const failureMessage = codeMessage !== null && codeMessage !== void 0 ? codeMessage : (preview ? t('Failed', { names: preview }) : defaultMessage);
31630
+ notification.error({
31631
+ id: 'FILES_DELETE_ERROR',
31632
+ message: failureMessage,
31633
+ placement: 'bottomRight',
31634
+ innerException: errorDetails.length ? errorDetails : undefined,
31635
+ data: {
31636
+ failCount,
31637
+ isTrashMove: contextIsTrashMove,
31638
+ totalCount,
31639
+ itemNames: failureNames,
31640
+ errors: errorDetails.length ? errorDetails : undefined,
31641
+ },
31642
+ });
31643
+ }
31644
+ clearSelection();
31645
+ setIsDeleteConfirmVisible === null || setIsDeleteConfirmVisible === void 0 ? void 0 : setIsDeleteConfirmVisible(false);
31646
+ await refreshFolders({
31647
+ parentIds: Array.from(parentIdsOfDeletedFolders),
31648
+ selectedFolder,
31649
+ });
31650
+ let targetPage = paginationBeforeOperation.current;
31651
+ if (successCount > 0 &&
31652
+ filesOnPageBeforeOperation > 0 &&
31653
+ successCount >= filesOnPageBeforeOperation &&
31654
+ paginationBeforeOperation.current > 1) {
31655
+ targetPage = paginationBeforeOperation.current - 1;
31656
+ }
31657
+ if (successCount > 0 && paginationBeforeOperation.total >= successCount) {
31658
+ const newTotal = Math.max(0, ((_c = paginationBeforeOperation.total) !== null && _c !== void 0 ? _c : 0) - successCount);
31659
+ setPagination === null || setPagination === void 0 ? void 0 : setPagination({
31660
+ current: targetPage,
31661
+ pageSize: paginationBeforeOperation.pageSize,
31662
+ total: newTotal,
31663
+ });
31664
+ }
31665
+ await refreshFiles({
31666
+ fetchFiles,
31667
+ selectedFolder,
31668
+ page: targetPage,
31669
+ limit: paginationBeforeOperation.pageSize,
31670
+ });
31671
+ }
31672
+ catch (error) {
31673
+ console.error('Error when deleting:', error);
31674
+ notification.error({
31675
+ id: 'FILES_DELETE_ERROR',
31676
+ message: t('Error deleting'),
31677
+ placement: 'bottomRight',
31678
+ innerException: error,
31679
+ data: {
31680
+ failCount,
31681
+ totalCount: items.length,
31682
+ },
31683
+ });
31684
+ }
31685
+ }, [
31686
+ currentMenuFile,
31687
+ selectedFiles,
31688
+ setIsDeleteConfirmVisible,
31689
+ fetchFiles,
31690
+ selectedFolder,
31691
+ pagination,
31692
+ setUndoDeletion,
31693
+ setPagination,
31694
+ files,
31695
+ t,
31696
+ notification,
31697
+ deleteItemsApi,
31698
+ clearSelection,
31699
+ config === null || config === void 0 ? void 0 : config.enableTrashFolder,
31700
+ ]);
31701
+ return {
31702
+ actions: {
31703
+ showDeleteConfirm,
31704
+ handleDeleteConfirmCancel,
31705
+ handleDeleteConfirmOk,
31706
+ },
31707
+ };
31708
+ };
31709
+
31710
+ ;// ./src/hooks/core/files/useFilesModals.ts
31711
+
31712
+
31713
+
31714
+
31715
+ const useFilesModals = (fetchFiles) => {
31716
+ const renameModal = useFilesRenameModal(fetchFiles);
31717
+ const moveModal = useFilesMoveModal(fetchFiles);
31718
+ const copyModal = useFilesCopyModal(fetchFiles);
31719
+ const deleteModal = useFilesDeleteModal(fetchFiles);
31720
+ return {
31721
+ state: renameModal.state,
31722
+ actions: Object.assign(Object.assign(Object.assign(Object.assign({}, renameModal.actions), moveModal.actions), copyModal.actions), deleteModal.actions),
31723
+ };
31724
+ };
31725
+
31726
+ ;// ./src/hooks/core/files/index.ts
31727
+
31728
+
31729
+
31730
+
31731
+
31732
+
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);
31906
31966
  }
31907
- }
31908
- if (failCount > 0) {
31909
- const failureNames = getItemNamesByIds(itemIds);
31910
- const preview = getNamesPreview(failureNames);
31911
- const totalCount = itemIds.length;
31912
- const hasSingleItemWithCode = totalCount === 1 && errorDetails.length > 0 && ((_a = errorDetails[0].error) === null || _a === void 0 ? void 0 : _a.code);
31913
- const contextIsTrashMove = (config === null || config === void 0 ? void 0 : config.enableTrashFolder) && !isTrashContext;
31914
- const defaultMessage = contextIsTrashMove
31915
- ? t('При перемещении в корзину произошли ошибки')
31916
- : t('При удалении произошли ошибки');
31917
- const codeMessage = hasSingleItemWithCode ? (0,errorMessages/* getErrorMessageFromCode */.j2)((_b = errorDetails[0].error) === null || _b === void 0 ? void 0 : _b.code, t) : undefined;
31918
- const failureMessage = codeMessage !== null && codeMessage !== void 0 ? codeMessage : (preview ? t('Failed', { names: preview }) : defaultMessage);
31919
- notification.error({
31920
- id: 'FILES_DELETE_ERROR',
31921
- message: failureMessage,
31922
- placement: 'bottomRight',
31923
- innerException: errorDetails.length ? errorDetails : undefined,
31924
- data: {
31925
- failCount,
31926
- isTrashMove: contextIsTrashMove,
31927
- totalCount,
31928
- itemNames: failureNames,
31929
- errors: errorDetails.length ? errorDetails : undefined,
31930
- },
31931
- });
31932
- }
31933
- clearSelection();
31934
- setIsDeleteConfirmVisible === null || setIsDeleteConfirmVisible === void 0 ? void 0 : setIsDeleteConfirmVisible(false);
31935
- await refreshFolders({
31936
- parentIds: Array.from(parentIdsOfDeletedFolders),
31937
- selectedFolder,
31938
- });
31939
- let targetPage = paginationBeforeOperation.current;
31940
- if (successCount > 0 &&
31941
- filesOnPageBeforeOperation > 0 &&
31942
- successCount >= filesOnPageBeforeOperation &&
31943
- paginationBeforeOperation.current > 1) {
31944
- targetPage = paginationBeforeOperation.current - 1;
31945
- }
31946
- if (successCount > 0 && paginationBeforeOperation.total >= successCount) {
31947
- const newTotal = Math.max(0, ((_c = paginationBeforeOperation.total) !== null && _c !== void 0 ? _c : 0) - successCount);
31948
- setPagination === null || setPagination === void 0 ? void 0 : setPagination({
31949
- current: targetPage,
31950
- pageSize: paginationBeforeOperation.pageSize,
31951
- total: newTotal,
31952
- });
31953
- }
31954
- await refreshFiles({
31955
- fetchFiles,
31956
- selectedFolder,
31957
- page: targetPage,
31958
- limit: paginationBeforeOperation.pageSize,
31959
- });
31960
- }
31961
- catch (error) {
31962
- console.error('Error when deleting:', error);
31963
- notification.error({
31964
- id: 'FILES_DELETE_ERROR',
31965
- message: t('Error deleting'),
31966
- placement: 'bottomRight',
31967
- innerException: error,
31968
- data: {
31969
- failCount,
31970
- totalCount: items.length,
31971
- },
31972
- });
31967
+ })
31968
+ .join('/');
31969
+ const result = url.startsWith('/') ? pathEncoded + (parsed.search || '') + (parsed.hash || '') : `${origin}${pathEncoded}${parsed.search}${parsed.hash}`;
31970
+ return result;
31973
31971
  }
31974
- }, [
31975
- currentMenuFile,
31976
- selectedFiles,
31977
- setIsDeleteConfirmVisible,
31978
- fetchFiles,
31979
- selectedFolder,
31980
- pagination,
31981
- setUndoDeletion,
31982
- setPagination,
31983
- files,
31984
- t,
31985
- notification,
31986
- deleteItemsApi,
31987
- clearSelection,
31988
- config === null || config === void 0 ? void 0 : config.enableTrashFolder,
31989
- ]);
31990
- return {
31991
- actions: {
31992
- showDeleteConfirm,
31993
- handleDeleteConfirmCancel,
31994
- handleDeleteConfirmOk,
31995
- },
31996
- };
31972
+ }
31973
+ catch (_a) {
31974
+ // если парсинг не удался, возвращаем как есть
31975
+ }
31976
+ return url;
31997
31977
  };
31998
-
31999
- ;// ./src/hooks/core/files/useFilesModals.ts
32000
-
32001
-
32002
-
32003
-
32004
- const useFilesModals = (fetchFiles) => {
32005
- const renameModal = useFilesRenameModal(fetchFiles);
32006
- const moveModal = useFilesMoveModal(fetchFiles);
32007
- const copyModal = useFilesCopyModal(fetchFiles);
32008
- const deleteModal = useFilesDeleteModal(fetchFiles);
32009
- return {
32010
- state: renameModal.state,
32011
- actions: Object.assign(Object.assign(Object.assign(Object.assign({}, renameModal.actions), moveModal.actions), copyModal.actions), deleteModal.actions),
32012
- };
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);
32013
32000
  };
32014
32001
 
32015
- ;// ./src/hooks/core/files/index.ts
32016
-
32017
-
32018
32002
 
32003
+ /***/ }),
32019
32004
 
32005
+ /***/ 9287:
32006
+ /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
32020
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":"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":""}]);
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___;
32021
32039
 
32022
32040
 
32023
32041
  /***/ }),
@@ -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 = [
@@ -39572,6 +39590,19 @@ const PixieOnlyApp = props => {
39572
39590
  return null;
39573
39591
  }
39574
39592
  }, [dataProviders]);
39593
+ const resolveUploadedFileById = external_root_React_commonjs2_react_commonjs_react_amd_react_default().useCallback(async (fileId) => {
39594
+ var _a;
39595
+ if (!fileId || typeof (dataProviders === null || dataProviders === void 0 ? void 0 : dataProviders.getFileById) !== 'function') {
39596
+ return null;
39597
+ }
39598
+ try {
39599
+ return (_a = (await dataProviders.getFileById(fileId))) !== null && _a !== void 0 ? _a : null;
39600
+ }
39601
+ catch (error) {
39602
+ console.warn('Standalone Pixie: failed to resolve uploaded file by id', error);
39603
+ return null;
39604
+ }
39605
+ }, [dataProviders]);
39575
39606
  const file = external_root_React_commonjs2_react_commonjs_react_amd_react_default().useMemo(() => {
39576
39607
  if (props.file)
39577
39608
  return props.file;
@@ -39614,7 +39645,7 @@ const PixieOnlyApp = props => {
39614
39645
  return null;
39615
39646
  }, [dataProviders]);
39616
39647
  const saveFileCopyToServer = external_root_React_commonjs2_react_commonjs_react_amd_react_default().useCallback(async (updatedFile, payload) => {
39617
- var _a, _b, _c, _d, _e, _f;
39648
+ var _a, _b, _c, _d, _e;
39618
39649
  if (!updatedFile.thumbnail) {
39619
39650
  return null;
39620
39651
  }
@@ -39670,23 +39701,15 @@ const PixieOnlyApp = props => {
39670
39701
  if (resolvedFile) {
39671
39702
  return resolvedFile;
39672
39703
  }
39673
- return {
39674
- id: firstResult.data.id || updatedFile.id || '',
39675
- name: firstResult.data.name || updatedFile.name,
39676
- folderId: (_f = firstResult.data.folderId) !== null && _f !== void 0 ? _f : targetFolderId,
39677
- type: uploadFile.type,
39678
- size: uploadFile.size,
39679
- extension: (0,mimeUtils/* getExtensionFromMime */.wh)(mimeType),
39680
- url: firstResult.data.url,
39681
- thumbnailUrl: firstResult.data.thumbnailUrl,
39682
- thumbnail: firstResult.data.thumbnail,
39683
- isFolder: false,
39684
- selected: false,
39685
- };
39704
+ const resolvedById = await resolveUploadedFileById(firstResult.data.id);
39705
+ if (resolvedById) {
39706
+ return resolvedById;
39707
+ }
39708
+ return null;
39686
39709
  }
39687
39710
  }
39688
39711
  if (dataProviders === null || dataProviders === void 0 ? void 0 : dataProviders.uploadFile) {
39689
- return await dataProviders.uploadFile({
39712
+ const uploaded = await dataProviders.uploadFile({
39690
39713
  name: uploadFile.name,
39691
39714
  size: uploadFile.size,
39692
39715
  type: uploadFile.type,
@@ -39695,6 +39718,17 @@ const PixieOnlyApp = props => {
39695
39718
  data: uploadFile,
39696
39719
  extension: (0,mimeUtils/* getExtensionFromMime */.wh)(mimeType),
39697
39720
  });
39721
+ if (!uploaded) {
39722
+ return null;
39723
+ }
39724
+ if (uploaded.url || uploaded.thumbnailUrl || uploaded.thumbnail) {
39725
+ return uploaded;
39726
+ }
39727
+ const resolvedById = await resolveUploadedFileById(uploaded.id);
39728
+ if (resolvedById) {
39729
+ return resolvedById;
39730
+ }
39731
+ return null;
39698
39732
  }
39699
39733
  return null;
39700
39734
  }
@@ -39702,7 +39736,7 @@ const PixieOnlyApp = props => {
39702
39736
  console.error('Error saving file copy:', error);
39703
39737
  return null;
39704
39738
  }
39705
- }, [dataProviders, resolveUploadedFileFromProvider, mergedPixieConfig]);
39739
+ }, [dataProviders, resolveUploadedFileFromProvider, resolveUploadedFileById, mergedPixieConfig]);
39706
39740
  // Обёртка над onSave если передан явный колбэк — используем его, иначе через дата провайдер
39707
39741
  const handleSave = external_root_React_commonjs2_react_commonjs_react_amd_react_default().useCallback(async (updated, payload) => {
39708
39742
  if ((dataProviders === null || dataProviders === void 0 ? void 0 : dataProviders.updateFile) && updated.id && updated.thumbnail) {
@@ -39725,25 +39759,26 @@ const PixieOnlyApp = props => {
39725
39759
  return updated;
39726
39760
  }, [dataProviders, onSave, onClose, saveFileToServer]);
39727
39761
  const handleSaveCopy = external_root_React_commonjs2_react_commonjs_react_amd_react_default().useCallback(async (updated, payload) => {
39728
- let result = updated;
39729
- if (updated.thumbnail) {
39730
- try {
39731
- const savedCopy = await saveFileCopyToServer(updated, payload);
39732
- if (savedCopy) {
39733
- result = savedCopy;
39734
- }
39762
+ if (!updated.thumbnail) {
39763
+ return undefined;
39764
+ }
39765
+ try {
39766
+ const savedCopy = await saveFileCopyToServer(updated, payload);
39767
+ if (!savedCopy) {
39768
+ return undefined;
39735
39769
  }
39736
- catch (e) {
39737
- console.error('Standalone Pixie: saveFileCopyToServer error', e);
39770
+ if (onSave) {
39771
+ await onSave(savedCopy, payload);
39738
39772
  }
39773
+ if (onClose) {
39774
+ onClose();
39775
+ }
39776
+ return savedCopy;
39739
39777
  }
39740
- if (onSave) {
39741
- await onSave(result, payload);
39742
- }
39743
- if (onClose) {
39744
- onClose();
39778
+ catch (e) {
39779
+ console.error('Standalone Pixie: saveFileCopyToServer error', e);
39780
+ return undefined;
39745
39781
  }
39746
- return result;
39747
39782
  }, [onClose, onSave, saveFileCopyToServer]);
39748
39783
  const modalRootRef = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useRef)(null);
39749
39784
  const getModalContainer = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useCallback)(() => { var _a; return (_a = modalRootRef.current) !== null && _a !== void 0 ? _a : document.body; }, []);
@@ -41684,8 +41719,27 @@ class DataSourceProxyBase {
41684
41719
  getTrashFolder() {
41685
41720
  return this._findOrCreateFolderByPath(this._pathOptions.trashPath || DEFAULT_TRASH_PATH);
41686
41721
  }
41687
- getTempFolder() {
41688
- return this._findOrCreateFolderByPath(this._pathOptions.tempPath || DEFAULT_TEMP_PATH);
41722
+ async getTempFolder() {
41723
+ var _a;
41724
+ // `??`, не `||`: пустой tempPath ('') — это осознанный выбор «грузить в корень»,
41725
+ // а не повод откатиться на дефолтную папку 'Edited'.
41726
+ const tempPath = (_a = this._pathOptions.tempPath) !== null && _a !== void 0 ? _a : DEFAULT_TEMP_PATH;
41727
+ // Пустой путь = корень: temp-папка не нужна, createFolder не вызывается.
41728
+ // Провайдеру с плоским каталогом (без папок) достаточно задать tempPath: ''.
41729
+ if (!tempPath) {
41730
+ return undefined;
41731
+ }
41732
+ try {
41733
+ return await this._findOrCreateFolderByPath(tempPath);
41734
+ }
41735
+ catch (error) {
41736
+ // Ошибки валидации пути — не маскируем; остальное (нет createFolder, throw провайдера) → корень.
41737
+ if (error instanceof FileManagerApiError && error.code === 'ValidationError') {
41738
+ throw error;
41739
+ }
41740
+ console.warn('getTempFolder: temp folder unavailable, falling back to root', error);
41741
+ return undefined;
41742
+ }
41689
41743
  }
41690
41744
  constructor(_dataProvider, _options = {}, _pathOptions, config = {}) {
41691
41745
  var _a, _b;
@@ -41855,31 +41909,37 @@ class DataSourceProxyBase {
41855
41909
  }
41856
41910
  async _getUrl(file) {
41857
41911
  var _a;
41912
+ if (!file) {
41913
+ return undefined;
41914
+ }
41858
41915
  if (file.url) {
41859
41916
  return file.url;
41860
41917
  }
41861
41918
  try {
41862
- return typeof ((_a = this._dataProvider) === null || _a === void 0 ? void 0 : _a.getUrl) === 'function' ? (this._dataProvider.getUrl(file) || '') : '';
41919
+ return typeof ((_a = this._dataProvider) === null || _a === void 0 ? void 0 : _a.getUrl) === 'function' ? this._dataProvider.getUrl(file) || undefined : undefined;
41863
41920
  }
41864
41921
  catch (e) {
41865
41922
  console.log(e);
41866
41923
  }
41867
- return '';
41924
+ return undefined;
41868
41925
  }
41869
41926
  async _getThumbnailUrl(file) {
41870
41927
  var _a;
41928
+ if (!file) {
41929
+ return undefined;
41930
+ }
41871
41931
  if (file.thumbnailUrl) {
41872
41932
  return file.thumbnailUrl;
41873
41933
  }
41874
41934
  try {
41875
41935
  return typeof ((_a = this._dataProvider) === null || _a === void 0 ? void 0 : _a.getThumbnailUrl) === 'function' ?
41876
- (this._dataProvider.getThumbnailUrl(file) || '')
41877
- : '';
41936
+ this._dataProvider.getThumbnailUrl(file) || undefined
41937
+ : undefined;
41878
41938
  }
41879
41939
  catch (e) {
41880
41940
  console.log(e);
41881
41941
  }
41882
- return '';
41942
+ return undefined;
41883
41943
  }
41884
41944
  /**
41885
41945
  * Проверяет, является ли targetFolderId дочерней папкой sourceFolderId (циклическая ссылка)
@@ -41948,7 +42008,10 @@ class DataSourceProxyBase {
41948
42008
  return this.getFile(folderId, name);
41949
42009
  }
41950
42010
  async getFileUrl(folderId, name) {
41951
- const file = await this.getFile(folderId, name);
42011
+ const file = await this.getFile(folderId !== null && folderId !== void 0 ? folderId : '', name);
42012
+ if (!file) {
42013
+ return undefined;
42014
+ }
41952
42015
  return (await this._getUrl(file)) || (await this._getThumbnailUrl(file)) || file.thumbnail;
41953
42016
  }
41954
42017
  /**
@@ -42247,7 +42310,7 @@ class DataSourceProxyBase {
42247
42310
  * Загружает файл на сервер
42248
42311
  */
42249
42312
  async _uploadFile(file) {
42250
- var _a;
42313
+ var _a, _b;
42251
42314
  const folderIdNormalized = this.normalizeId((_a = file.folderId) !== null && _a !== void 0 ? _a : this._rootId);
42252
42315
  if (typeof this._dataProvider.uploadFile !== 'function') {
42253
42316
  throw new FileManagerApiError('Upload file operation is not supported by data provider', 'UnsupportedOperation');
@@ -42263,7 +42326,9 @@ class DataSourceProxyBase {
42263
42326
  }
42264
42327
  }
42265
42328
  const uploaded = await this._dataProvider.uploadFile(Object.assign(Object.assign({}, file), { folderId: folderIdNormalized }));
42266
- const result = normalizeFileSystemItem(Object.assign(Object.assign({ name: file.name, size: file.size }, uploaded), { folderId: folderIdNormalized }));
42329
+ const result = normalizeFileSystemItem(Object.assign(Object.assign({ name: file.name, size: file.size }, uploaded), {
42330
+ // Сохраняем folderId провайдера (flat-каталог enKod → '1'), иначе затираем корнем запроса.
42331
+ folderId: (_b = uploaded.folderId) !== null && _b !== void 0 ? _b : folderIdNormalized }));
42267
42332
  this._cache.upsertEntity(result);
42268
42333
  return result;
42269
42334
  }
@@ -42308,7 +42373,14 @@ class DataSourceProxyBase {
42308
42373
  batchResult = mapBatchResult(providerResult, (data) => {
42309
42374
  if (!data)
42310
42375
  return undefined;
42311
- return { id: data.id, name: normalizeFileSystemItemName(data.name), folderId: data.folderId };
42376
+ return {
42377
+ id: data.id,
42378
+ name: normalizeFileSystemItemName(data.name),
42379
+ folderId: data.folderId,
42380
+ url: data.url,
42381
+ thumbnailUrl: data.thumbnailUrl,
42382
+ thumbnail: data.thumbnail,
42383
+ };
42312
42384
  });
42313
42385
  if (batchResult.detailed && batchResult.detailed.length) {
42314
42386
  for (let i = 0; i < batchResult.detailed.length; i++) {
@@ -42887,7 +42959,9 @@ class DataSourceProxyBase {
42887
42959
  var _a;
42888
42960
  if (data.noFolder) {
42889
42961
  const tempFolder = await this.getTempFolder();
42890
- data = Object.assign(Object.assign({}, data), { folderId: tempFolder.id });
42962
+ // tempFolder может быть undefined (tempPath: '' или недоступный createFolder) —
42963
+ // тогда folderId остаётся неопределённым и ниже нормализуется в корень (_rootId).
42964
+ data = Object.assign(Object.assign({}, data), { folderId: tempFolder === null || tempFolder === void 0 ? void 0 : tempFolder.id });
42891
42965
  }
42892
42966
  const folderIdNormalized = this.normalizeId((_a = data.folderId) !== null && _a !== void 0 ? _a : this._rootId);
42893
42967
  try {