@brftech/filex-core 0.7.1 → 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 (30) hide show
  1. package/dist/{ArchiveViewer-Qi4L2jj9.js → ArchiveViewer-xGV6PJ2d.js} +2 -2
  2. package/dist/{ArchiveViewer-Qi4L2jj9.js.map → ArchiveViewer-xGV6PJ2d.js.map} +1 -1
  3. package/dist/{CsvViewer-IOIrJDsJ.js → CsvViewer-C_VfZCbP.js} +2 -2
  4. package/dist/{CsvViewer-IOIrJDsJ.js.map → CsvViewer-C_VfZCbP.js.map} +1 -1
  5. package/dist/{DrawioViewer-D0KtUQBO.js → DrawioViewer--Wx7CUSa.js} +2 -2
  6. package/dist/{DrawioViewer-D0KtUQBO.js.map → DrawioViewer--Wx7CUSa.js.map} +1 -1
  7. package/dist/{EpubViewer-DJOXiOi4.js → EpubViewer-CIlKN5rU.js} +2 -2
  8. package/dist/{EpubViewer-DJOXiOi4.js.map → EpubViewer-CIlKN5rU.js.map} +1 -1
  9. package/dist/{IpynbViewer-D16jXVJc.js → IpynbViewer-D9nID_-7.js} +2 -2
  10. package/dist/{IpynbViewer-D16jXVJc.js.map → IpynbViewer-D9nID_-7.js.map} +1 -1
  11. package/dist/{MermaidViewer-BnSaVg1L.js → MermaidViewer-BnW75WoJ.js} +2 -2
  12. package/dist/{MermaidViewer-BnSaVg1L.js.map → MermaidViewer-BnW75WoJ.js.map} +1 -1
  13. package/dist/{PsdViewer-BUerXaQA.js → PsdViewer-yaU0b1GT.js} +2 -2
  14. package/dist/{PsdViewer-BUerXaQA.js.map → PsdViewer-yaU0b1GT.js.map} +1 -1
  15. package/dist/{TiffViewer-D0Qn3IV-.js → TiffViewer-BsLGsy10.js} +2 -2
  16. package/dist/{TiffViewer-D0Qn3IV-.js.map → TiffViewer-BsLGsy10.js.map} +1 -1
  17. package/dist/{Viewer3D-BtRK-k1B.js → Viewer3D-DrVf_Uyz.js} +2 -2
  18. package/dist/{Viewer3D-BtRK-k1B.js.map → Viewer3D-DrVf_Uyz.js.map} +1 -1
  19. package/dist/filex-core.js +1 -1
  20. package/dist/filex-core.umd.cjs +36 -36
  21. package/dist/filex-core.umd.cjs.map +1 -1
  22. package/dist/{index-CGNE1bSv.js → index-BrUiOc9C.js} +4865 -4819
  23. package/dist/index-BrUiOc9C.js.map +1 -0
  24. package/dist/index.d.ts +5 -0
  25. package/dist/style.css +1 -1
  26. package/package.json +1 -1
  27. package/src/FileExplorer.vue +128 -29
  28. package/src/components/SecondaryPane.vue +19 -3
  29. package/src/styles/base.css +28 -8
  30. package/dist/index-CGNE1bSv.js.map +0 -1
@@ -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
  },
@@ -1341,9 +1377,11 @@ function openNodeInNewTab(n: FileNode) {
1341
1377
  void markRecent(n);
1342
1378
  }
1343
1379
 
1344
- type ContextMode = 'selection' | 'breadcrumb';
1380
+ type ContextMode = 'selection' | 'breadcrumb' | 'pane' /* ui-fix — yan panel sağ-tık */;
1345
1381
  const ctxMode = ref<ContextMode>('selection');
1346
1382
  const breadcrumbCtxPath = ref<string>('');
1383
+ /* ui-fix — yan panel sağ-tık menüsünün hedef node'ları (pane selection). */
1384
+ const paneCtxTargets = ref<FileNode[]>([]);
1347
1385
  const breadcrumbCtxLabel = ref<string>('');
1348
1386
 
1349
1387
  const selectionMode = computed<SelectionMode>(() => {
@@ -1388,6 +1426,19 @@ function onCrumbContext(payload: { x: number; y: number; adapterPath: string; la
1388
1426
  ctxRef.value?.show({ clientX: payload.x, clientY: payload.y }, []);
1389
1427
  }
1390
1428
 
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. */
1432
+ function onPaneContext(node: FileNode | null, ev: MouseEvent) {
1433
+ activePane.value = 'split';
1434
+ const sel = splitPaneRef.value?.selectedNodes() ?? [];
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.
1437
+ paneCtxTargets.value = node ? (sel.length > 0 ? sel : [node]) : [];
1438
+ ctxMode.value = 'pane';
1439
+ ctxRef.value?.show({ clientX: ev.clientX, clientY: ev.clientY }, paneCtxTargets.value);
1440
+ }
1441
+
1391
1442
  const contextActions = computed<ContextAction[]>(() => {
1392
1443
  if (ctxMode.value === 'breadcrumb') {
1393
1444
  return [
@@ -1395,6 +1446,17 @@ const contextActions = computed<ContextAction[]>(() => {
1395
1446
  { key: 'copy-path', label: t('breadcrumb.copy_path'), icon: '📋' },
1396
1447
  ];
1397
1448
  }
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);
1459
+ }
1398
1460
 
1399
1461
  const sel = selection.nodes.value;
1400
1462
  const any = sel.length > 0;
@@ -1517,6 +1579,10 @@ async function onContextAction(action: ContextAction, targets: FileNode[]) {
1517
1579
  }
1518
1580
  return;
1519
1581
  }
1582
+ if (ctxMode.value === 'pane' /* ui-fix — yan panel: aynı dispatch, pane-route */) {
1583
+ await dispatchItemAction(action.key, paneCtxTargets.value);
1584
+ return;
1585
+ }
1520
1586
  await dispatchItemAction(action.key, targets);
1521
1587
  }
1522
1588
 
@@ -1569,30 +1635,40 @@ async function dispatchItemAction(key: string, targets: FileNode[]) {
1569
1635
  case 'rename':
1570
1636
  if (targets[0]) {
1571
1637
  renameTarget.value = targets[0];
1638
+ mutationInPane.value = ctxMode.value === 'pane'; /* ui-fix */
1572
1639
  showRename.value = true;
1573
1640
  }
1574
1641
  break;
1575
1642
  case 'cut':
1576
- clipboard.value = { mode: 'cut', items: targets, sourcePath: currentPath.value };
1577
- 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
+ }
1578
1649
  break;
1579
1650
  case 'copy':
1580
- clipboard.value = { mode: 'copy', items: targets, sourcePath: currentPath.value };
1581
- 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
+ }
1582
1656
  break;
1583
1657
  case 'paste':
1584
1658
  /* ui-fix — sağ-klik menüsünden yapıştırma da aktif panele gider
1585
1659
  * (klavye kısayolu zaten pane-route'luydu; menü değildi). */
1586
- if (paneIsActive.value) await panePaste();
1660
+ if (ctxMode.value === 'pane' || paneIsActive.value) await panePaste();
1587
1661
  else await paste();
1588
1662
  break;
1589
1663
  case 'delete':
1664
+ mutationInPane.value = ctxMode.value === 'pane'; /* ui-fix */
1590
1665
  showDelete.value = true;
1591
1666
  break;
1592
1667
  case 'restore':
1593
1668
  if (targets.length > 0) await restoreSelection(targets);
1594
1669
  break;
1595
1670
  case 'new-folder':
1671
+ mutationInPane.value = ctxMode.value === 'pane'; /* ui-fix */
1596
1672
  showNewFolder.value = true;
1597
1673
  break;
1598
1674
  case 'duplicate':
@@ -1675,10 +1751,13 @@ function downloadFile(n: FileNode) {
1675
1751
  // ------- Modals -------
1676
1752
 
1677
1753
  async function submitNewFolder(name: string) {
1754
+ const inPane = mutationInPane.value; /* ui-fix — yan panelde yeni klasör */
1678
1755
  try {
1679
- await api.newFolder(qualify(currentPath.value), name);
1756
+ const dirWire = inPane ? qualify(splitPaneRef.value?.getPath() ?? '') : qualify(currentPath.value);
1757
+ await api.newFolder(dirWire, name);
1680
1758
  showNewFolder.value = false;
1681
- await load();
1759
+ if (inPane) await splitPaneRef.value?.reload();
1760
+ else await load();
1682
1761
  } catch (err) {
1683
1762
  emit('error', { message: (err as Error).message, context: { op: 'newfolder' } });
1684
1763
  }
@@ -1687,14 +1766,16 @@ async function submitNewFolder(name: string) {
1687
1766
  async function submitRename(name: string) {
1688
1767
  const target = renameTarget.value;
1689
1768
  if (!target) return;
1769
+ const inPane = mutationInPane.value; /* ui-fix — yan panelden yeniden adlandırma */
1690
1770
  try {
1691
- const dirWire = qualify(currentPath.value);
1771
+ const dirWire = inPane ? qualify(splitPaneRef.value?.getPath() ?? '') : qualify(currentPath.value);
1692
1772
  const oldPath = target.path; // qualified
1693
1773
  const oldName = target.basename;
1694
1774
  await api.rename(dirWire, oldPath, name);
1695
1775
  showRename.value = false;
1696
1776
  renameTarget.value = null;
1697
- await load();
1777
+ if (inPane) await splitPaneRef.value?.reload();
1778
+ else await load();
1698
1779
  // Clean inverse: rename the new path back to the old basename.
1699
1780
  if (name && name !== oldName) {
1700
1781
  const newPath = wireJoin(wireParent(oldPath), name);
@@ -1716,7 +1797,10 @@ async function confirmDelete() {
1716
1797
  flashToast('Çöpteki öğeler saklama süresi sonunda otomatik silinir. Kalıcı silme yönetici panelinden yapılır.');
1717
1798
  return;
1718
1799
  }
1719
- 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);
1720
1804
  const items = targets.map((n) => n.path);
1721
1805
  if (items.length === 0) {
1722
1806
  showDelete.value = false;
@@ -1737,19 +1821,21 @@ async function confirmDelete() {
1737
1821
  : null;
1738
1822
  try {
1739
1823
  if (api.endpoints.deleteAsync) {
1740
- const { op } = await api.deleteAsync(items, qualify(currentPath.value));
1824
+ const { op } = await api.deleteAsync(items, dirWire);
1741
1825
  if (restoreUndo) {
1742
1826
  opUndo.set(op.id, { message: t('toast.trashed'), fn: restoreUndo });
1743
1827
  }
1744
1828
  pendingOps.register(op);
1745
1829
  flashToast('Silme kuyruğa alındı');
1746
1830
  } else {
1747
- await api.deleteItems(qualify(currentPath.value), items);
1748
- await load();
1831
+ await api.deleteItems(dirWire, items);
1832
+ if (inPane) await splitPaneRef.value?.reload();
1833
+ else await load();
1749
1834
  if (restoreUndo) undoToast(t('toast.trashed'), restoreUndo);
1750
1835
  }
1751
1836
  showDelete.value = false;
1752
- selection.clear();
1837
+ if (inPane) void splitPaneRef.value?.reload();
1838
+ else selection.clear();
1753
1839
  } catch (err) {
1754
1840
  emit('error', { message: (err as Error).message, context: { op: 'delete' } });
1755
1841
  }
@@ -2116,11 +2202,10 @@ async function onItemDropInto(target: FileNode, ev: DragEvent) {
2116
2202
  const targetDir = target.path; // qualified
2117
2203
  const sources = items
2118
2204
  .map((i) => i.path)
2119
- .filter((p) => p && p !== targetDir && !targetDir.startsWith(p + '/'));
2120
- if (sources.length === 0) {
2121
- flashToast('Aynı klasöre taşınamaz');
2122
- return;
2123
- }
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)
2124
2209
  await transferItems(sources, targetDir, dndOrigin(ev)); /* wiring:d1 — depo-farkında aktarım */
2125
2210
  }
2126
2211
 
@@ -2138,7 +2223,8 @@ async function onCrumbDropInto(adapterPath: string, ev: DragEvent) {
2138
2223
  const targetDir = adapterPath; // already qualified by breadcrumb
2139
2224
  const sources = items
2140
2225
  .map((i) => i.path)
2141
- .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));
2142
2228
  if (sources.length === 0) return;
2143
2229
  await transferItems(sources, targetDir, dndOrigin(ev)); /* wiring:d1 — depo-farkında aktarım */
2144
2230
  }
@@ -2622,7 +2708,11 @@ async function transferItems(
2622
2708
  originWire?: string,
2623
2709
  forceCopy = false,
2624
2710
  ): Promise<void> {
2625
- 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
+ );
2626
2716
  if (list.length === 0 || !targetWire) return;
2627
2717
  const targetAdapter = wireAdapterOf(targetWire);
2628
2718
  const cross = list.some((p) => wireAdapterOf(p) !== targetAdapter);
@@ -2930,6 +3020,18 @@ async function submitEncryptedFolder(payload: { name: string; password: string }
2930
3020
  @open-recents="showRecents = true"
2931
3021
  />
2932
3022
 
3023
+ <!-- koru:k1 — fe__main lays the listing body and the inspector panel out
3024
+ as flex siblings (row). Without the inspector open it is visually
3025
+ identical to the previous direct-child fe__body. -->
3026
+ <div class="fe__main" :class="{ 'fe__main--split': splitVisible } /* wiring:d1 */">
3027
+ <!-- ui-fix — sol panelin başlığı (breadcrumb + durum şeritleri + body)
3028
+ tek bir sarmalda: split modunda bu sarmal sol yarıya sığar, böylece
3029
+ breadcrumb tüm sayfayı değil kendi panelini kaplar (SecondaryPane'in
3030
+ kendi kırıntısıyla simetrik). Aktif-panel vurgusu da bu sarmalda. -->
3031
+ <div
3032
+ class="fe__primary"
3033
+ :class="{ 'fe-pane--focus': mainPaneFocus } /* wiring:d1 — aktif panel vurgusu */"
3034
+ >
2933
3035
  <Breadcrumb
2934
3036
  :dirname="dirname"
2935
3037
  :adapter="adapter"
@@ -2971,13 +3073,8 @@ async function submitEncryptedFolder(payload: { name: string; password: string }
2971
3073
  </div>
2972
3074
  <!-- /wiring:e2 -->
2973
3075
 
2974
- <!-- koru:k1 — fe__main lays the listing body and the inspector panel out
2975
- as flex siblings (row). Without the inspector open it is visually
2976
- identical to the previous direct-child fe__body. -->
2977
- <div class="fe__main" :class="{ 'fe__main--split': splitVisible } /* wiring:d1 */">
2978
3076
  <div
2979
3077
  class="fe__body"
2980
- :class="{ 'fe-pane--focus': mainPaneFocus } /* wiring:d1 — aktif panel vurgusu */"
2981
3078
  @pointerdown.capture="setPaneMain() /* wiring:d1 */"
2982
3079
  @click.self="selection.clear()"
2983
3080
  >
@@ -3230,6 +3327,7 @@ async function submitEncryptedFolder(payload: { name: string; password: string }
3230
3327
  />
3231
3328
  <!-- /wiring:d2 -->
3232
3329
  </div>
3330
+ </div><!-- /fe__primary ui-fix -->
3233
3331
 
3234
3332
  <!-- wiring:d1 — tab başına split: sağ ikincil panel (dar modda kapalı).
3235
3333
  :key tab kimliğine bağlı — tab geçişinde pane kendi konumuyla temiz
@@ -3256,6 +3354,7 @@ async function submitEncryptedFolder(payload: { name: string; password: string }
3256
3354
  @close="closeSplit"
3257
3355
  @open-tab="(p: string) => tabsApi.openTab(p, { viewMode: viewMode, background: true })"
3258
3356
  @transfer="onPaneTransfer"
3357
+ @context="onPaneContext /* ui-fix — yan panel sağ-tık menüsü */"
3259
3358
  />
3260
3359
  <!-- /wiring:d1 -->
3261
3360
 
@@ -65,6 +65,9 @@ const emit = defineEmits<{
65
65
  (e: 'close'): void;
66
66
  (e: 'open-tab', path: string): void;
67
67
  (e: 'transfer', p: { sources: string[]; targetWire: string; originWire?: string }): void;
68
+ /* ui-fix — yan panelde sağ-tık: menü ana bileşende (FileExplorer) açılır.
69
+ * node=null → boş alana sağ-tık (seçimsiz, yalnız Yapıştır). */
70
+ (e: 'context', node: FileNode | null, ev: MouseEvent): void;
68
71
  }>();
69
72
 
70
73
  const { t } = useLocale(() => props.locale);
@@ -168,14 +171,26 @@ function onViewClick(n: FileNode, mod: { ctrl: boolean; shift: boolean }) {
168
171
  selected.value = next;
169
172
  }
170
173
 
171
- /* ui-fix — right-click on a pane item: activate + select it, no menu
172
- * (the pane has no item menu; letting the main panel's menu open here
173
- * would act on the WRONG panel's selection). */
174
+ /* ui-fix — right-click on a pane item: activate + select it, then let the
175
+ * host open the pane context menu (its actions are pane-routed). Selecting
176
+ * first keeps the menu's targets on THIS pane, not the main panel. */
174
177
  function onViewContext(n: FileNode, ev: MouseEvent) {
175
178
  ev.preventDefault();
176
179
  ev.stopPropagation();
177
180
  emit('activate');
178
181
  if (!selected.value.has(n.path)) selected.value = new Set([n.path]);
182
+ emit('context', n, ev);
183
+ }
184
+
185
+ /* ui-fix — boş alana (arka plan) sağ-tık: seçimsiz menü (yalnız Yapıştır)
186
+ * → boş klasöre / mevcut klasöre dosya yapıştırma. Satır menüsü stopProp
187
+ * ettiğinden bu yalnız gerçek boş alanda tetiklenir. */
188
+ function onBgContext(ev: MouseEvent) {
189
+ ev.preventDefault();
190
+ ev.stopPropagation(); // FileExplorer kökündeki canvas menüsüne bubble etmesin
191
+ emit('activate');
192
+ selected.value = new Set();
193
+ emit('context', null, ev);
179
194
  }
180
195
 
181
196
  /* ui-fix — drop-into from the shared views: same payload path as the
@@ -344,6 +359,7 @@ defineExpose({ reload, goUp, selectAll, openSelected, selectedNodes, getPath });
344
359
  class="fe-split__body"
345
360
  :class="{ 'is-dropover': dropBg }"
346
361
  @click.self="clearSelection"
362
+ @contextmenu="onBgContext"
347
363
  @dragover="onBgDragOver"
348
364
  @dragleave="onBgDragLeave"
349
365
  @drop="onBgDrop"
@@ -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;
@@ -3220,9 +3223,23 @@ filex-explorer {
3220
3223
  display: none;
3221
3224
  }
3222
3225
 
3226
+ /* ui-fix — sol panel sarmalı: breadcrumb + durum şeritleri + body bir
3227
+ flex-column. Split olmadan tüm genişlik; split olunca sol yarı (böylece
3228
+ breadcrumb kendi panelini kaplar, SecondaryPane ile simetrik). */
3229
+ .fe__primary {
3230
+ display: flex;
3231
+ flex-direction: column;
3232
+ flex: 1 1 auto;
3233
+ min-width: 0;
3234
+ min-height: 0;
3235
+ }
3236
+ .fe__primary > .fe__body {
3237
+ min-width: 0;
3238
+ }
3239
+
3223
3240
  /* Split layout — equal halves inside fe__main; the inspector (koru:k1)
3224
3241
  keeps its fixed width to the right of both. */
3225
- .fe__main--split > .fe__body {
3242
+ .fe__main--split > .fe__primary {
3226
3243
  flex: 1 1 50%;
3227
3244
  }
3228
3245
  .fe-split {
@@ -3236,11 +3253,11 @@ filex-explorer {
3236
3253
  color: var(--fe-text);
3237
3254
  }
3238
3255
  /* Active-panel accent: a thin top line on whichever pane owns the keyboard. */
3239
- .fe__main--split > .fe__body,
3256
+ .fe__main--split > .fe__primary,
3240
3257
  .fe-split {
3241
3258
  box-shadow: inset 0 2px 0 transparent;
3242
3259
  }
3243
- .fe__main--split > .fe__body.fe-pane--focus,
3260
+ .fe__main--split > .fe__primary.fe-pane--focus,
3244
3261
  .fe-split.fe-pane--focus {
3245
3262
  box-shadow: inset 0 2px 0 var(--fe-primary);
3246
3263
  }
@@ -3248,11 +3265,14 @@ filex-explorer {
3248
3265
  .fe-split__crumbs {
3249
3266
  display: flex;
3250
3267
  align-items: center;
3251
- 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;
3252
3271
  flex: 0 0 auto;
3253
- padding: 6px 8px;
3272
+ padding: 6px 12px;
3273
+ min-height: 37px; /* ui-fix — ana panel .fe-breadcrumb ile aynı yükseklik */
3254
3274
  border-bottom: 1px solid var(--fe-border);
3255
- font-size: 12px;
3275
+ font-size: 13px;
3256
3276
  color: var(--fe-text-muted);
3257
3277
  overflow: hidden;
3258
3278
  white-space: nowrap;
@@ -3262,8 +3282,8 @@ filex-explorer {
3262
3282
  background: transparent;
3263
3283
  color: inherit;
3264
3284
  font: inherit;
3265
- padding: 2px 5px;
3266
- border-radius: 5px;
3285
+ padding: 2px 6px; /* ui-fix — .fe-breadcrumb__crumb ile aynı */
3286
+ border-radius: var(--fe-radius-sm);
3267
3287
  cursor: pointer;
3268
3288
  max-width: 140px;
3269
3289
  overflow: hidden;