@brftech/filex-core 0.21.5 → 0.22.0
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/filex-core.js +4332 -4260
- package/dist/filex-core.js.map +1 -1
- package/dist/filex-core.umd.cjs +39 -39
- package/dist/filex-core.umd.cjs.map +1 -1
- package/dist/index.d.ts +26 -0
- package/package.json +1 -1
- package/src/FileExplorer.vue +97 -0
- package/src/locales/en.ts +6 -0
- package/src/locales/tr.ts +6 -0
- package/src/types/ExplorerConfig.ts +24 -0
package/dist/index.d.ts
CHANGED
|
@@ -824,6 +824,32 @@ export declare interface ExplorerConfig {
|
|
|
824
824
|
driver?: string;
|
|
825
825
|
readOnly?: boolean;
|
|
826
826
|
}>;
|
|
827
|
+
/**
|
|
828
|
+
* Desktop-shell hook — selective sync ("keep on this computer").
|
|
829
|
+
*
|
|
830
|
+
* Present only when the explorer runs inside the filex desktop app; the
|
|
831
|
+
* shell passes functions that talk to its sync engine, and the explorer
|
|
832
|
+
* grows "Keep on this computer" / "Online only" entries on folder menus.
|
|
833
|
+
* Absent (web admin, embeds): nothing about it renders.
|
|
834
|
+
*
|
|
835
|
+
* Folders only, by design: the sync engine pairs directories, and a file
|
|
836
|
+
* rides along with the folder that holds it.
|
|
837
|
+
*/
|
|
838
|
+
desktopSync?: {
|
|
839
|
+
/** Kept folders for the mounted account, adapter-qualified remotes. */
|
|
840
|
+
kept: () => Promise<Array<{
|
|
841
|
+
remote: string;
|
|
842
|
+
local: string;
|
|
843
|
+
}>>;
|
|
844
|
+
/** Start keeping a folder. Resolves once the pair is registered (or the
|
|
845
|
+
* user cancelled the shell's root-folder prompt — re-read `kept`). */
|
|
846
|
+
keep: (remote: string) => Promise<void>;
|
|
847
|
+
/** Stop keeping. The SHELL owns the "what happens to the local copy"
|
|
848
|
+
* question — it asks natively and may cancel; re-read `kept` after. */
|
|
849
|
+
unkeep: (remote: string) => Promise<void>;
|
|
850
|
+
/** Open the folder's local mirror in the OS file manager. */
|
|
851
|
+
reveal: (remote: string) => Promise<void>;
|
|
852
|
+
};
|
|
827
853
|
}
|
|
828
854
|
|
|
829
855
|
/** Component emits — the parent listens for these events. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brftech/filex-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "filex core — Vue 3 source of truth for the filex file manager (FileExplorer + ConnectionsPanel SFCs, composables, types)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/filex-core.umd.cjs",
|
package/src/FileExplorer.vue
CHANGED
|
@@ -1419,8 +1419,68 @@ async function onToolbarAction(key: string) {
|
|
|
1419
1419
|
await dispatchItemAction(key, sel);
|
|
1420
1420
|
}
|
|
1421
1421
|
|
|
1422
|
+
// ─── desktop selective sync — "keep on this computer" ──────────────────
|
|
1423
|
+
// Present only when the desktop shell passes config.desktopSync; the web
|
|
1424
|
+
// admin and the embeds never see these entries. State is PULLED, not pushed:
|
|
1425
|
+
// the kept list is re-read as a menu opens, so the component needs no event
|
|
1426
|
+
// channel back to the shell and cannot go stale in a way that outlives one
|
|
1427
|
+
// right-click.
|
|
1428
|
+
const desktopSync = computed(() => props.config.desktopSync ?? null);
|
|
1429
|
+
const keptPairs = ref<Array<{ remote: string; local: string }>>([]);
|
|
1430
|
+
|
|
1431
|
+
async function refreshKept(): Promise<void> {
|
|
1432
|
+
if (!desktopSync.value) return;
|
|
1433
|
+
try {
|
|
1434
|
+
keptPairs.value = await desktopSync.value.kept();
|
|
1435
|
+
} catch {
|
|
1436
|
+
// Shell went away mid-call; a stale entry only mislabels a menu item.
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
onMounted(() => void refreshKept());
|
|
1440
|
+
|
|
1441
|
+
/** Adapter-qualified remote for a row. Virtual storage rows carry a bare
|
|
1442
|
+
* name (`docs`), real rows a wire path (`docs://reports`). */
|
|
1443
|
+
function keepRemoteOf(node: FileNode): string {
|
|
1444
|
+
const p = String(node.path ?? '');
|
|
1445
|
+
return p.includes('://') ? p.replace(/\/+$/, '') : `${p}://`;
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
type KeepState = 'none' | 'kept' | 'inherited' | 'partial';
|
|
1449
|
+
|
|
1450
|
+
/** How `remote` relates to the kept set: exactly a pair, inside one
|
|
1451
|
+
* (inherited), an ancestor of some (partial), or unrelated. */
|
|
1452
|
+
function keepStateOf(remote: string): KeepState {
|
|
1453
|
+
const inside = (child: string, parent: string) =>
|
|
1454
|
+
parent.endsWith('://')
|
|
1455
|
+
? child.startsWith(parent) && child !== parent
|
|
1456
|
+
: child === parent
|
|
1457
|
+
? false
|
|
1458
|
+
: child.startsWith(parent + '/');
|
|
1459
|
+
if (keptPairs.value.some((p) => p.remote === remote)) return 'kept';
|
|
1460
|
+
if (keptPairs.value.some((p) => inside(remote, p.remote))) return 'inherited';
|
|
1461
|
+
if (keptPairs.value.some((p) => inside(p.remote, remote))) return 'partial';
|
|
1462
|
+
return 'none';
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
/** Menu entries for one selected folder, by its keep state. Empty for
|
|
1466
|
+
* multi-selections, files, trash, encrypted folders, or a web mount. */
|
|
1467
|
+
function keepActionsFor(sel: FileNode[]): ContextAction[] {
|
|
1468
|
+
const ds = desktopSync.value;
|
|
1469
|
+
const single = sel.length === 1 && sel[0]?.type === 'dir';
|
|
1470
|
+
if (!ds || !single || trashActive.value || e2eActive.value || sel[0]?.e2e === true) return [];
|
|
1471
|
+
const st = keepStateOf(keepRemoteOf(sel[0]!));
|
|
1472
|
+
return [
|
|
1473
|
+
{ divider: true, key: 'sep-keep', label: '' },
|
|
1474
|
+
{ key: 'keep-local', label: t('ctx.keep_local'), icon: '📌', hidden: st === 'kept' || st === 'inherited' },
|
|
1475
|
+
{ key: 'keep-online', label: t('ctx.keep_online'), icon: '☁', hidden: st !== 'kept' },
|
|
1476
|
+
{ key: 'keep-inherited', label: t('ctx.keep_inherited'), icon: '📌', disabled: true, hidden: st !== 'inherited' },
|
|
1477
|
+
{ key: 'keep-reveal', label: t('ctx.keep_reveal'), icon: '📂', hidden: st !== 'kept' && st !== 'inherited' },
|
|
1478
|
+
];
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1422
1481
|
async function onContextTarget(node: FileNode, ev: MouseEvent) {
|
|
1423
1482
|
ctxMode.value = 'selection';
|
|
1483
|
+
void refreshKept(); // menu labels react if the kept set changed since last look
|
|
1424
1484
|
if (!selection.has(node.path)) {
|
|
1425
1485
|
selection.click(node.path);
|
|
1426
1486
|
await nextTick();
|
|
@@ -1447,6 +1507,7 @@ function onCrumbContext(payload: { x: number; y: number; adapterPath: string; la
|
|
|
1447
1507
|
* dispatchItemAction'a gider ve ctxMode==='pane' iken pane-route'lanır. */
|
|
1448
1508
|
function onPaneContext(node: FileNode | null, ev: MouseEvent) {
|
|
1449
1509
|
activePane.value = 'split';
|
|
1510
|
+
void refreshKept();
|
|
1450
1511
|
const sel = splitPaneRef.value?.selectedNodes() ?? [];
|
|
1451
1512
|
// node=null (boş alana sağ-tık): seçimsiz menü (Yeni Klasör + Yapıştır).
|
|
1452
1513
|
// Aksi halde pane seçimi (yoksa tıklanan node) hedeftir.
|
|
@@ -1507,6 +1568,9 @@ const contextActions = computed<ContextAction[]>(() => {
|
|
|
1507
1568
|
return [
|
|
1508
1569
|
{ key: 'open', label: t('ctx.open'), icon: '↗' },
|
|
1509
1570
|
{ key: 'open-tab', label: t('ctx.open_new_tab'), icon: '⧉' } /* wiring:d1 */,
|
|
1571
|
+
// A whole storage can be kept too — that IS the "sync everything"
|
|
1572
|
+
// shape, and it is one pair, not one per subfolder.
|
|
1573
|
+
...keepActionsFor(sel),
|
|
1510
1574
|
];
|
|
1511
1575
|
}
|
|
1512
1576
|
|
|
@@ -1571,6 +1635,7 @@ function selectionActionList(sel: FileNode[]): ContextAction[] {
|
|
|
1571
1635
|
{ key: 'paste', label: t('ctx.paste'), icon: '📋', hidden: !w, disabled: !clipboard.value.mode },
|
|
1572
1636
|
{ divider: true, key: 'sep-meta', label: '', hidden: !singleHasId },
|
|
1573
1637
|
{ key: 'tags', label: tagsLabel, icon: '🏷', hidden: !singleHasId, disabled: !singleHasId },
|
|
1638
|
+
...keepActionsFor(sel),
|
|
1574
1639
|
{ divider: true, key: 'sep2', label: '', hidden: !w },
|
|
1575
1640
|
{ key: 'delete', label: t('ctx.delete'), icon: '🗑', danger: true, hidden: !any || !w, disabled: !any },
|
|
1576
1641
|
];
|
|
@@ -1631,6 +1696,38 @@ async function dispatchItemAction(key: string, targets: FileNode[]) {
|
|
|
1631
1696
|
case 'download':
|
|
1632
1697
|
if (targets[0]) downloadFile(targets[0]);
|
|
1633
1698
|
break;
|
|
1699
|
+
case 'keep-local': {
|
|
1700
|
+
const ds = desktopSync.value;
|
|
1701
|
+
if (!ds || !targets[0]) break;
|
|
1702
|
+
const remote = keepRemoteOf(targets[0]);
|
|
1703
|
+
try {
|
|
1704
|
+
await ds.keep(remote);
|
|
1705
|
+
await refreshKept();
|
|
1706
|
+
// The shell may have shown its root-folder prompt and been cancelled —
|
|
1707
|
+
// only claim success when the pair is really there now.
|
|
1708
|
+
if (keepStateOf(remote) !== 'none') flashToast(t('keep.started'));
|
|
1709
|
+
} catch (e) {
|
|
1710
|
+
await refreshKept();
|
|
1711
|
+
flashToast(`${t('keep.failed')}: ${String((e as Error)?.message ?? e)}`);
|
|
1712
|
+
}
|
|
1713
|
+
break;
|
|
1714
|
+
}
|
|
1715
|
+
case 'keep-online': {
|
|
1716
|
+
const ds = desktopSync.value;
|
|
1717
|
+
if (!ds || !targets[0]) break;
|
|
1718
|
+
try {
|
|
1719
|
+
await ds.unkeep(keepRemoteOf(targets[0]));
|
|
1720
|
+
} catch {
|
|
1721
|
+
// The shell owns the confirm dialog and reports its own failures.
|
|
1722
|
+
}
|
|
1723
|
+
await refreshKept();
|
|
1724
|
+
break;
|
|
1725
|
+
}
|
|
1726
|
+
case 'keep-reveal': {
|
|
1727
|
+
const ds = desktopSync.value;
|
|
1728
|
+
if (ds && targets[0]) void ds.reveal(keepRemoteOf(targets[0]));
|
|
1729
|
+
break;
|
|
1730
|
+
}
|
|
1634
1731
|
case 'convert':
|
|
1635
1732
|
if (targets[0]) openConvert(targets[0]);
|
|
1636
1733
|
break;
|
package/src/locales/en.ts
CHANGED
|
@@ -23,6 +23,12 @@ export const en: Record<string, string> = {
|
|
|
23
23
|
'ctx.cut': 'Cut',
|
|
24
24
|
'ctx.show_hidden': 'Show hidden files',
|
|
25
25
|
'ctx.hide_hidden': 'Hide hidden files',
|
|
26
|
+
'ctx.keep_local': 'Keep on this computer',
|
|
27
|
+
'ctx.keep_online': 'Keep online only',
|
|
28
|
+
'ctx.keep_reveal': 'Open local folder',
|
|
29
|
+
'ctx.keep_inherited': 'Kept on this computer with its parent',
|
|
30
|
+
'keep.started': 'This folder will be kept on this computer — sync started',
|
|
31
|
+
'keep.failed': 'Could not keep on this computer',
|
|
26
32
|
'ctx.paste': 'Paste',
|
|
27
33
|
'ctx.info': 'Info',
|
|
28
34
|
'ctx.duplicate': 'Duplicate',
|
package/src/locales/tr.ts
CHANGED
|
@@ -23,6 +23,12 @@ export const tr: Record<string, string> = {
|
|
|
23
23
|
'ctx.cut': 'Kes',
|
|
24
24
|
'ctx.show_hidden': 'Gizli dosyaları göster',
|
|
25
25
|
'ctx.hide_hidden': 'Gizli dosyaları gizle',
|
|
26
|
+
'ctx.keep_local': 'Bilgisayarda tut',
|
|
27
|
+
'ctx.keep_online': 'Yalnızca çevrimiçi tut',
|
|
28
|
+
'ctx.keep_reveal': 'Yerel klasörü aç',
|
|
29
|
+
'ctx.keep_inherited': 'Üst klasörle bilgisayarda tutuluyor',
|
|
30
|
+
'keep.started': 'Klasör bilgisayarda tutulacak — eşitleme başladı',
|
|
31
|
+
'keep.failed': 'Bilgisayarda tutulamadı',
|
|
26
32
|
'ctx.paste': 'Yapıştır',
|
|
27
33
|
'ctx.info': 'Bilgi',
|
|
28
34
|
'ctx.duplicate': 'Kopyasını Oluştur',
|
|
@@ -308,6 +308,30 @@ export interface ExplorerConfig {
|
|
|
308
308
|
driver?: string;
|
|
309
309
|
readOnly?: boolean;
|
|
310
310
|
}>;
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Desktop-shell hook — selective sync ("keep on this computer").
|
|
314
|
+
*
|
|
315
|
+
* Present only when the explorer runs inside the filex desktop app; the
|
|
316
|
+
* shell passes functions that talk to its sync engine, and the explorer
|
|
317
|
+
* grows "Keep on this computer" / "Online only" entries on folder menus.
|
|
318
|
+
* Absent (web admin, embeds): nothing about it renders.
|
|
319
|
+
*
|
|
320
|
+
* Folders only, by design: the sync engine pairs directories, and a file
|
|
321
|
+
* rides along with the folder that holds it.
|
|
322
|
+
*/
|
|
323
|
+
desktopSync?: {
|
|
324
|
+
/** Kept folders for the mounted account, adapter-qualified remotes. */
|
|
325
|
+
kept: () => Promise<Array<{ remote: string; local: string }>>;
|
|
326
|
+
/** Start keeping a folder. Resolves once the pair is registered (or the
|
|
327
|
+
* user cancelled the shell's root-folder prompt — re-read `kept`). */
|
|
328
|
+
keep: (remote: string) => Promise<void>;
|
|
329
|
+
/** Stop keeping. The SHELL owns the "what happens to the local copy"
|
|
330
|
+
* question — it asks natively and may cancel; re-read `kept` after. */
|
|
331
|
+
unkeep: (remote: string) => Promise<void>;
|
|
332
|
+
/** Open the folder's local mirror in the OS file manager. */
|
|
333
|
+
reveal: (remote: string) => Promise<void>;
|
|
334
|
+
};
|
|
311
335
|
}
|
|
312
336
|
|
|
313
337
|
/** Component emits — the parent listens for these events. */
|