@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.
- package/dist/{ArchiveViewer-Qi4L2jj9.js → ArchiveViewer-xGV6PJ2d.js} +2 -2
- package/dist/{ArchiveViewer-Qi4L2jj9.js.map → ArchiveViewer-xGV6PJ2d.js.map} +1 -1
- package/dist/{CsvViewer-IOIrJDsJ.js → CsvViewer-C_VfZCbP.js} +2 -2
- package/dist/{CsvViewer-IOIrJDsJ.js.map → CsvViewer-C_VfZCbP.js.map} +1 -1
- package/dist/{DrawioViewer-D0KtUQBO.js → DrawioViewer--Wx7CUSa.js} +2 -2
- package/dist/{DrawioViewer-D0KtUQBO.js.map → DrawioViewer--Wx7CUSa.js.map} +1 -1
- package/dist/{EpubViewer-DJOXiOi4.js → EpubViewer-CIlKN5rU.js} +2 -2
- package/dist/{EpubViewer-DJOXiOi4.js.map → EpubViewer-CIlKN5rU.js.map} +1 -1
- package/dist/{IpynbViewer-D16jXVJc.js → IpynbViewer-D9nID_-7.js} +2 -2
- package/dist/{IpynbViewer-D16jXVJc.js.map → IpynbViewer-D9nID_-7.js.map} +1 -1
- package/dist/{MermaidViewer-BnSaVg1L.js → MermaidViewer-BnW75WoJ.js} +2 -2
- package/dist/{MermaidViewer-BnSaVg1L.js.map → MermaidViewer-BnW75WoJ.js.map} +1 -1
- package/dist/{PsdViewer-BUerXaQA.js → PsdViewer-yaU0b1GT.js} +2 -2
- package/dist/{PsdViewer-BUerXaQA.js.map → PsdViewer-yaU0b1GT.js.map} +1 -1
- package/dist/{TiffViewer-D0Qn3IV-.js → TiffViewer-BsLGsy10.js} +2 -2
- package/dist/{TiffViewer-D0Qn3IV-.js.map → TiffViewer-BsLGsy10.js.map} +1 -1
- package/dist/{Viewer3D-BtRK-k1B.js → Viewer3D-DrVf_Uyz.js} +2 -2
- package/dist/{Viewer3D-BtRK-k1B.js.map → Viewer3D-DrVf_Uyz.js.map} +1 -1
- package/dist/filex-core.js +1 -1
- package/dist/filex-core.umd.cjs +36 -36
- package/dist/filex-core.umd.cjs.map +1 -1
- package/dist/{index-CGNE1bSv.js → index-BrUiOc9C.js} +4865 -4819
- package/dist/index-BrUiOc9C.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/FileExplorer.vue +128 -29
- package/src/components/SecondaryPane.vue +19 -3
- package/src/styles/base.css +28 -8
- package/dist/index-CGNE1bSv.js.map +0 -1
package/src/FileExplorer.vue
CHANGED
|
@@ -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
|
-
|
|
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 (
|
|
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
|
-
|
|
1577
|
-
|
|
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
|
-
|
|
1581
|
-
|
|
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
|
-
|
|
1756
|
+
const dirWire = inPane ? qualify(splitPaneRef.value?.getPath() ?? '') : qualify(currentPath.value);
|
|
1757
|
+
await api.newFolder(dirWire, name);
|
|
1680
1758
|
showNewFolder.value = false;
|
|
1681
|
-
await
|
|
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
|
|
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
|
-
|
|
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,
|
|
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(
|
|
1748
|
-
await
|
|
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
|
-
|
|
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
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
172
|
-
*
|
|
173
|
-
*
|
|
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"
|
package/src/styles/base.css
CHANGED
|
@@ -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 > .
|
|
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 > .
|
|
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 > .
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
3266
|
-
border-radius:
|
|
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;
|