@brftech/filex-core 0.7.2 → 0.7.3

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.
Files changed (27) hide show
  1. package/dist/{ArchiveViewer-DAb5peBU.js → ArchiveViewer-xGV6PJ2d.js} +2 -2
  2. package/dist/{ArchiveViewer-DAb5peBU.js.map → ArchiveViewer-xGV6PJ2d.js.map} +1 -1
  3. package/dist/{CsvViewer-_bnmnpjn.js → CsvViewer-C_VfZCbP.js} +2 -2
  4. package/dist/{CsvViewer-_bnmnpjn.js.map → CsvViewer-C_VfZCbP.js.map} +1 -1
  5. package/dist/{DrawioViewer-Bjns4ob8.js → DrawioViewer--Wx7CUSa.js} +2 -2
  6. package/dist/{DrawioViewer-Bjns4ob8.js.map → DrawioViewer--Wx7CUSa.js.map} +1 -1
  7. package/dist/{EpubViewer-DoFuZTtA.js → EpubViewer-CIlKN5rU.js} +2 -2
  8. package/dist/{EpubViewer-DoFuZTtA.js.map → EpubViewer-CIlKN5rU.js.map} +1 -1
  9. package/dist/{IpynbViewer-eg1ySMD4.js → IpynbViewer-D9nID_-7.js} +2 -2
  10. package/dist/{IpynbViewer-eg1ySMD4.js.map → IpynbViewer-D9nID_-7.js.map} +1 -1
  11. package/dist/{MermaidViewer-BZ13vA6S.js → MermaidViewer-BnW75WoJ.js} +2 -2
  12. package/dist/{MermaidViewer-BZ13vA6S.js.map → MermaidViewer-BnW75WoJ.js.map} +1 -1
  13. package/dist/{PsdViewer-qFkE4KCB.js → PsdViewer-yaU0b1GT.js} +2 -2
  14. package/dist/{PsdViewer-qFkE4KCB.js.map → PsdViewer-yaU0b1GT.js.map} +1 -1
  15. package/dist/{TiffViewer-eHJTCYsd.js → TiffViewer-BsLGsy10.js} +2 -2
  16. package/dist/{TiffViewer-eHJTCYsd.js.map → TiffViewer-BsLGsy10.js.map} +1 -1
  17. package/dist/{Viewer3D-C4--ljdO.js → Viewer3D-DrVf_Uyz.js} +2 -2
  18. package/dist/{Viewer3D-C4--ljdO.js.map → Viewer3D-DrVf_Uyz.js.map} +1 -1
  19. package/dist/filex-core.js +1 -1
  20. package/dist/filex-core.umd.cjs +25 -25
  21. package/dist/filex-core.umd.cjs.map +1 -1
  22. package/dist/{index-BSDT0PU8.js → index-BrUiOc9C.js} +2882 -2893
  23. package/dist/{index-BSDT0PU8.js.map → index-BrUiOc9C.js.map} +1 -1
  24. package/dist/style.css +1 -1
  25. package/package.json +1 -1
  26. package/src/FileExplorer.vue +100 -74
  27. package/src/styles/base.css +11 -5
@@ -438,6 +438,9 @@ const showDelete = ref(false);
438
438
  const showShare = ref(false);
439
439
  const showPreview = ref(false);
440
440
  const renameTarget = ref<FileNode | null>(null);
441
+ /* ui-fix — açık olan rename/delete/new-folder modal'ı yan panele mi ait?
442
+ * (menü ana panelle aynı; mutasyonlar doğru panele yönlensin diye.) */
443
+ const mutationInPane = ref(false);
441
444
  const shareTarget = ref<FileNode | null>(null);
442
445
  const activeShare = ref<(ShareInfo & { url: string; filename?: string }) | null>(null);
443
446
  const previewTarget = ref<FileNode | null>(null);
@@ -891,6 +894,20 @@ function wireParent(p: string): string {
891
894
  return slash === -1 ? prefix : prefix + rel.slice(0, slash);
892
895
  }
893
896
 
897
+ /* ui-fix — iki wire yolun AYNI dizini gösterip göstermediği (trailing-slash
898
+ * ve kök `adapter://` güvenli). Bir öğeyi ZATEN bulunduğu klasöre bırakma
899
+ * (source parent === target) no-op olmalı; aksi halde backend "kendine
900
+ * kopyala" 400'ü döner. */
901
+ function sameDir(a: string, b: string): boolean {
902
+ const norm = (s: string) => {
903
+ const i = s.indexOf('://');
904
+ const pre = i === -1 ? '' : s.slice(0, i + 3);
905
+ const rel = (i === -1 ? s : s.slice(i + 3)).replace(/\/+$/, '');
906
+ return pre + rel;
907
+ };
908
+ return norm(a) === norm(b);
909
+ }
910
+
894
911
  function wireJoin(dir: string, name: string): string {
895
912
  if (!dir) return name;
896
913
  return dir.endsWith('://') || dir.endsWith('/') ? dir + name : `${dir}/${name}`;
@@ -1105,11 +1122,30 @@ async function openSearchHit(hit: GlobalSearchHit) {
1105
1122
 
1106
1123
  useKeyboardShortcuts(rootEl, {
1107
1124
  onDelete: () => {
1108
- if (!selection.isEmpty.value) showDelete.value = true;
1125
+ /* ui-fix — kısayol da aktif panele gider (menüyle tutarlı). */
1126
+ if (paneIsActive.value) {
1127
+ const psel = splitPaneRef.value?.selectedNodes() ?? [];
1128
+ if (psel.length) {
1129
+ paneCtxTargets.value = psel;
1130
+ mutationInPane.value = true;
1131
+ showDelete.value = true;
1132
+ }
1133
+ } else if (!selection.isEmpty.value) {
1134
+ mutationInPane.value = false;
1135
+ showDelete.value = true;
1136
+ }
1109
1137
  },
1110
1138
  onRename: () => {
1111
- if (selection.nodes.value.length === 1) {
1139
+ if (paneIsActive.value) {
1140
+ const psel = splitPaneRef.value?.selectedNodes() ?? [];
1141
+ if (psel.length === 1) {
1142
+ renameTarget.value = psel[0];
1143
+ mutationInPane.value = true;
1144
+ showRename.value = true;
1145
+ }
1146
+ } else if (selection.nodes.value.length === 1) {
1112
1147
  renameTarget.value = selection.nodes.value[0];
1148
+ mutationInPane.value = false;
1113
1149
  showRename.value = true;
1114
1150
  }
1115
1151
  },
@@ -1390,61 +1426,19 @@ function onCrumbContext(payload: { x: number; y: number; adapterPath: string; la
1390
1426
  ctxRef.value?.show({ clientX: payload.x, clientY: payload.y }, []);
1391
1427
  }
1392
1428
 
1393
- /* ui-fix — yan (ikincil) panelde sağ-tık: pane'i aktifleştir, seçili
1394
- * node'lara göre menüyü aç. Pane "hafif" olduğundan (delete/rename modal'ları
1395
- * ana panelde) menü aç/yeni-sekme/indir/kopyala/kes/yapıştır ile sınırlı;
1396
- * hepsi pane-route'lu (dispatchPaneAction). */
1429
+ /* ui-fix — yan (ikincil) panelde sağ-tık: pane'i aktifleştir + menüyü aç.
1430
+ * Menü ana panelle BİREBİR aynı (selectionActionList tek kaynak); aksiyonlar
1431
+ * dispatchItemAction'a gider ve ctxMode==='pane' iken pane-route'lanır. */
1397
1432
  function onPaneContext(node: FileNode | null, ev: MouseEvent) {
1398
1433
  activePane.value = 'split';
1399
1434
  const sel = splitPaneRef.value?.selectedNodes() ?? [];
1400
- // node=null (boş alana sağ-tık): seçimsiz menü (yalnız Yapıştır). Aksi
1401
- // halde pane seçimi (yoksa tıklanan node) hedeftir.
1435
+ // node=null (boş alana sağ-tık): seçimsiz menü (Yeni Klasör + Yapıştır).
1436
+ // Aksi halde pane seçimi (yoksa tıklanan node) hedeftir.
1402
1437
  paneCtxTargets.value = node ? (sel.length > 0 ? sel : [node]) : [];
1403
1438
  ctxMode.value = 'pane';
1404
1439
  ctxRef.value?.show({ clientX: ev.clientX, clientY: ev.clientY }, paneCtxTargets.value);
1405
1440
  }
1406
1441
 
1407
- function paneActionList(sel: FileNode[]): ContextAction[] {
1408
- const single = sel.length === 1;
1409
- const isFile = single && sel[0]?.type === 'file';
1410
- const isDir = single && sel[0]?.type === 'dir';
1411
- const any = sel.length > 0;
1412
- return [
1413
- { key: 'open', label: t('ctx.open'), icon: '↗', hidden: !single },
1414
- { key: 'open-tab', label: t('ctx.open_new_tab'), icon: '⧉', hidden: !isDir } /* wiring:d1 */,
1415
- { key: 'download', label: t('ctx.download'), icon: '⬇', hidden: !single, disabled: !isFile },
1416
- { divider: true, key: 'psep', label: '', hidden: !any },
1417
- { key: 'copy', label: t('ctx.copy'), icon: '❐', hidden: !any },
1418
- { key: 'cut', label: t('ctx.cut'), icon: '✂', hidden: !any },
1419
- { key: 'paste', label: t('ctx.paste'), icon: '📋', disabled: !clipboard.value.mode },
1420
- ];
1421
- }
1422
-
1423
- async function dispatchPaneAction(key: string, targets: FileNode[]) {
1424
- switch (key) {
1425
- case 'open':
1426
- if (targets[0]) openNodeInNewTab(targets[0]);
1427
- break;
1428
- case 'open-tab':
1429
- if (targets[0]) {
1430
- tabsApi.openTab(paneToUser(targets[0].path), { viewMode: viewMode.value, background: true });
1431
- }
1432
- break;
1433
- case 'download':
1434
- if (targets[0]) downloadFile(targets[0]);
1435
- break;
1436
- case 'copy':
1437
- paneCopy();
1438
- break;
1439
- case 'cut':
1440
- paneCut();
1441
- break;
1442
- case 'paste':
1443
- await panePaste();
1444
- break;
1445
- }
1446
- }
1447
-
1448
1442
  const contextActions = computed<ContextAction[]>(() => {
1449
1443
  if (ctxMode.value === 'breadcrumb') {
1450
1444
  return [
@@ -1452,8 +1446,16 @@ const contextActions = computed<ContextAction[]>(() => {
1452
1446
  { key: 'copy-path', label: t('breadcrumb.copy_path'), icon: '📋' },
1453
1447
  ];
1454
1448
  }
1455
- if (ctxMode.value === 'pane' /* ui-fix — yan panel menüsü */) {
1456
- return paneActionList(paneCtxTargets.value);
1449
+ if (ctxMode.value === 'pane' /* ui-fix — yan panel menüsü ana panelle BİREBİR aynı */) {
1450
+ const psel = paneCtxTargets.value;
1451
+ if (psel.length === 0) {
1452
+ // Boş alana sağ-tık: ana paneldeki canvas menüsüyle aynı.
1453
+ return [
1454
+ { key: 'new-folder', label: t('toolbar.new_folder'), icon: '📁' },
1455
+ { key: 'paste', label: t('ctx.paste'), icon: '📋', disabled: !clipboard.value.mode },
1456
+ ];
1457
+ }
1458
+ return selectionActionList(psel);
1457
1459
  }
1458
1460
 
1459
1461
  const sel = selection.nodes.value;
@@ -1577,8 +1579,8 @@ async function onContextAction(action: ContextAction, targets: FileNode[]) {
1577
1579
  }
1578
1580
  return;
1579
1581
  }
1580
- if (ctxMode.value === 'pane' /* ui-fix — yan panel aksiyonları pane-route */) {
1581
- await dispatchPaneAction(action.key, paneCtxTargets.value);
1582
+ if (ctxMode.value === 'pane' /* ui-fix — yan panel: aynı dispatch, pane-route */) {
1583
+ await dispatchItemAction(action.key, paneCtxTargets.value);
1582
1584
  return;
1583
1585
  }
1584
1586
  await dispatchItemAction(action.key, targets);
@@ -1633,30 +1635,40 @@ async function dispatchItemAction(key: string, targets: FileNode[]) {
1633
1635
  case 'rename':
1634
1636
  if (targets[0]) {
1635
1637
  renameTarget.value = targets[0];
1638
+ mutationInPane.value = ctxMode.value === 'pane'; /* ui-fix */
1636
1639
  showRename.value = true;
1637
1640
  }
1638
1641
  break;
1639
1642
  case 'cut':
1640
- clipboard.value = { mode: 'cut', items: targets, sourcePath: currentPath.value };
1641
- flashToast('Kes Yapıştır hazır');
1643
+ /* ui-fix pane bağlamında pano kaynağı pane'in dizini olmalı. */
1644
+ if (ctxMode.value === 'pane') paneCut();
1645
+ else {
1646
+ clipboard.value = { mode: 'cut', items: targets, sourcePath: currentPath.value };
1647
+ flashToast('Kes → Yapıştır hazır');
1648
+ }
1642
1649
  break;
1643
1650
  case 'copy':
1644
- clipboard.value = { mode: 'copy', items: targets, sourcePath: currentPath.value };
1645
- flashToast('Kopyala → Yapıştır hazır');
1651
+ if (ctxMode.value === 'pane') paneCopy();
1652
+ else {
1653
+ clipboard.value = { mode: 'copy', items: targets, sourcePath: currentPath.value };
1654
+ flashToast('Kopyala → Yapıştır hazır');
1655
+ }
1646
1656
  break;
1647
1657
  case 'paste':
1648
1658
  /* ui-fix — sağ-klik menüsünden yapıştırma da aktif panele gider
1649
1659
  * (klavye kısayolu zaten pane-route'luydu; menü değildi). */
1650
- if (paneIsActive.value) await panePaste();
1660
+ if (ctxMode.value === 'pane' || paneIsActive.value) await panePaste();
1651
1661
  else await paste();
1652
1662
  break;
1653
1663
  case 'delete':
1664
+ mutationInPane.value = ctxMode.value === 'pane'; /* ui-fix */
1654
1665
  showDelete.value = true;
1655
1666
  break;
1656
1667
  case 'restore':
1657
1668
  if (targets.length > 0) await restoreSelection(targets);
1658
1669
  break;
1659
1670
  case 'new-folder':
1671
+ mutationInPane.value = ctxMode.value === 'pane'; /* ui-fix */
1660
1672
  showNewFolder.value = true;
1661
1673
  break;
1662
1674
  case 'duplicate':
@@ -1739,10 +1751,13 @@ function downloadFile(n: FileNode) {
1739
1751
  // ------- Modals -------
1740
1752
 
1741
1753
  async function submitNewFolder(name: string) {
1754
+ const inPane = mutationInPane.value; /* ui-fix — yan panelde yeni klasör */
1742
1755
  try {
1743
- await api.newFolder(qualify(currentPath.value), name);
1756
+ const dirWire = inPane ? qualify(splitPaneRef.value?.getPath() ?? '') : qualify(currentPath.value);
1757
+ await api.newFolder(dirWire, name);
1744
1758
  showNewFolder.value = false;
1745
- await load();
1759
+ if (inPane) await splitPaneRef.value?.reload();
1760
+ else await load();
1746
1761
  } catch (err) {
1747
1762
  emit('error', { message: (err as Error).message, context: { op: 'newfolder' } });
1748
1763
  }
@@ -1751,14 +1766,16 @@ async function submitNewFolder(name: string) {
1751
1766
  async function submitRename(name: string) {
1752
1767
  const target = renameTarget.value;
1753
1768
  if (!target) return;
1769
+ const inPane = mutationInPane.value; /* ui-fix — yan panelden yeniden adlandırma */
1754
1770
  try {
1755
- const dirWire = qualify(currentPath.value);
1771
+ const dirWire = inPane ? qualify(splitPaneRef.value?.getPath() ?? '') : qualify(currentPath.value);
1756
1772
  const oldPath = target.path; // qualified
1757
1773
  const oldName = target.basename;
1758
1774
  await api.rename(dirWire, oldPath, name);
1759
1775
  showRename.value = false;
1760
1776
  renameTarget.value = null;
1761
- await load();
1777
+ if (inPane) await splitPaneRef.value?.reload();
1778
+ else await load();
1762
1779
  // Clean inverse: rename the new path back to the old basename.
1763
1780
  if (name && name !== oldName) {
1764
1781
  const newPath = wireJoin(wireParent(oldPath), name);
@@ -1780,7 +1797,10 @@ async function confirmDelete() {
1780
1797
  flashToast('Çöpteki öğeler saklama süresi sonunda otomatik silinir. Kalıcı silme yönetici panelinden yapılır.');
1781
1798
  return;
1782
1799
  }
1783
- const targets = selection.nodes.value;
1800
+ /* ui-fix yan panelden silme: hedefler + dizin + tazeleme pane'e ait. */
1801
+ const inPane = mutationInPane.value;
1802
+ const targets = inPane ? paneCtxTargets.value : selection.nodes.value;
1803
+ const dirWire = inPane ? qualify(splitPaneRef.value?.getPath() ?? '') : qualify(currentPath.value);
1784
1804
  const items = targets.map((n) => n.path);
1785
1805
  if (items.length === 0) {
1786
1806
  showDelete.value = false;
@@ -1801,19 +1821,21 @@ async function confirmDelete() {
1801
1821
  : null;
1802
1822
  try {
1803
1823
  if (api.endpoints.deleteAsync) {
1804
- const { op } = await api.deleteAsync(items, qualify(currentPath.value));
1824
+ const { op } = await api.deleteAsync(items, dirWire);
1805
1825
  if (restoreUndo) {
1806
1826
  opUndo.set(op.id, { message: t('toast.trashed'), fn: restoreUndo });
1807
1827
  }
1808
1828
  pendingOps.register(op);
1809
1829
  flashToast('Silme kuyruğa alındı');
1810
1830
  } else {
1811
- await api.deleteItems(qualify(currentPath.value), items);
1812
- await load();
1831
+ await api.deleteItems(dirWire, items);
1832
+ if (inPane) await splitPaneRef.value?.reload();
1833
+ else await load();
1813
1834
  if (restoreUndo) undoToast(t('toast.trashed'), restoreUndo);
1814
1835
  }
1815
1836
  showDelete.value = false;
1816
- selection.clear();
1837
+ if (inPane) void splitPaneRef.value?.reload();
1838
+ else selection.clear();
1817
1839
  } catch (err) {
1818
1840
  emit('error', { message: (err as Error).message, context: { op: 'delete' } });
1819
1841
  }
@@ -2180,11 +2202,10 @@ async function onItemDropInto(target: FileNode, ev: DragEvent) {
2180
2202
  const targetDir = target.path; // qualified
2181
2203
  const sources = items
2182
2204
  .map((i) => i.path)
2183
- .filter((p) => p && p !== targetDir && !targetDir.startsWith(p + '/'));
2184
- if (sources.length === 0) {
2185
- flashToast('Aynı klasöre taşınamaz');
2186
- return;
2187
- }
2205
+ // ui-fix öğe zaten targetDir'in içindeyse (parent===target) atla: yerinde
2206
+ // bırakma no-op, backend "kendine kopyala" 400'ü tetiklemez.
2207
+ .filter((p) => p && p !== targetDir && !targetDir.startsWith(p + '/') && !sameDir(wireParent(p), targetDir));
2208
+ if (sources.length === 0) return; // sessiz no-op (yerinde bırakma)
2188
2209
  await transferItems(sources, targetDir, dndOrigin(ev)); /* wiring:d1 — depo-farkında aktarım */
2189
2210
  }
2190
2211
 
@@ -2202,7 +2223,8 @@ async function onCrumbDropInto(adapterPath: string, ev: DragEvent) {
2202
2223
  const targetDir = adapterPath; // already qualified by breadcrumb
2203
2224
  const sources = items
2204
2225
  .map((i) => i.path)
2205
- .filter((p) => p && p !== targetDir && !targetDir.startsWith(p + '/'));
2226
+ // ui-fix kırıntıya (aynı klasöre) yerinde bırakma no-op.
2227
+ .filter((p) => p && p !== targetDir && !targetDir.startsWith(p + '/') && !sameDir(wireParent(p), targetDir));
2206
2228
  if (sources.length === 0) return;
2207
2229
  await transferItems(sources, targetDir, dndOrigin(ev)); /* wiring:d1 — depo-farkında aktarım */
2208
2230
  }
@@ -2686,7 +2708,11 @@ async function transferItems(
2686
2708
  originWire?: string,
2687
2709
  forceCopy = false,
2688
2710
  ): Promise<void> {
2689
- const list = sources.filter((p) => p && p !== targetWire && !targetWire.startsWith(p + '/'));
2711
+ // ui-fix yerinde bırakma (source parent === target) no-op: backend
2712
+ // "kendine kopyala" 400'ü engellenir (paneller arası + pano yolu).
2713
+ const list = sources.filter(
2714
+ (p) => p && p !== targetWire && !targetWire.startsWith(p + '/') && !sameDir(wireParent(p), targetWire),
2715
+ );
2690
2716
  if (list.length === 0 || !targetWire) return;
2691
2717
  const targetAdapter = wireAdapterOf(targetWire);
2692
2718
  const cross = list.some((p) => wireAdapterOf(p) !== targetAdapter);
@@ -266,6 +266,9 @@ filex-explorer {
266
266
  gap: 4px;
267
267
  flex-wrap: wrap;
268
268
  padding: 6px 12px;
269
+ /* ui-fix — split'te yan panel .fe-split__crumbs ile aynı yükseklik (ana
270
+ panelin "Yola Git" ✏ butonu yoksa da satırlar aynı y'den başlasın). */
271
+ min-height: 37px;
269
272
  background: var(--fe-bg);
270
273
  border-bottom: 1px solid var(--fe-border);
271
274
  font-size: 13px;
@@ -3262,11 +3265,14 @@ filex-explorer {
3262
3265
  .fe-split__crumbs {
3263
3266
  display: flex;
3264
3267
  align-items: center;
3265
- gap: 2px;
3268
+ /* ui-fix — ana panel .fe-breadcrumb ile BİREBİR aynı ölçüler (gap/padding/
3269
+ font) → split'te iki breadcrumb tam hizalı, ince kayma yok. */
3270
+ gap: 4px;
3266
3271
  flex: 0 0 auto;
3267
- padding: 6px 8px;
3272
+ padding: 6px 12px;
3273
+ min-height: 37px; /* ui-fix — ana panel .fe-breadcrumb ile aynı yükseklik */
3268
3274
  border-bottom: 1px solid var(--fe-border);
3269
- font-size: 12px;
3275
+ font-size: 13px;
3270
3276
  color: var(--fe-text-muted);
3271
3277
  overflow: hidden;
3272
3278
  white-space: nowrap;
@@ -3276,8 +3282,8 @@ filex-explorer {
3276
3282
  background: transparent;
3277
3283
  color: inherit;
3278
3284
  font: inherit;
3279
- padding: 2px 5px;
3280
- border-radius: 5px;
3285
+ padding: 2px 6px; /* ui-fix — .fe-breadcrumb__crumb ile aynı */
3286
+ border-radius: var(--fe-radius-sm);
3281
3287
  cursor: pointer;
3282
3288
  max-width: 140px;
3283
3289
  overflow: hidden;