feat: vault core/CLI/workbench, vaultd token path, filetree view-state cleanup
Land password-vault dedicated workbench and mnote-vault-core/CLI, agent token read path design, vault transport split, and retire obsolete filetree smokes. Ignore local vault reimport scripts that trip secret scanners.
This commit is contained in:
@@ -11,6 +11,7 @@ var FILETREE_DRAG_MIME = 'application/x-mnote-file-tree';
|
||||
|
||||
var draggingFileTreeRowIds = [];
|
||||
var activeFileTreeDropRow = null;
|
||||
var activeFileTreeDropPosition = null;
|
||||
|
||||
// ─── 纯 helper ─────────────────────────────────────────
|
||||
|
||||
@@ -22,10 +23,34 @@ function filetreeDragPayload(rowIds) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wolai-style drop zones on a row:
|
||||
* - top 25% → before (sibling reorder line)
|
||||
* - bottom 25% → after
|
||||
* - middle → inside folders/dirs; leaf rows treat middle as after
|
||||
*/
|
||||
function fileTreeDropPosition(event, row) {
|
||||
if (!(row instanceof HTMLElement) || !event) return 'inside';
|
||||
var rect = row.getBoundingClientRect();
|
||||
var ratio = rect.height > 0 ? (event.clientY - rect.top) / rect.height : 0.5;
|
||||
if (ratio < 0.25) return 'before';
|
||||
if (ratio > 0.75) return 'after';
|
||||
var kind = String(row.getAttribute('data-row-kind') || '').trim();
|
||||
if (kind === 'folder' || kind === 'directory' || kind === 'doc' || kind === 'document') {
|
||||
// documents can host children in page/file tree hybrid; folders always accept inside
|
||||
if (kind === 'folder' || kind === 'directory') return 'inside';
|
||||
// expandable document rows also accept nest-into
|
||||
if (row.getAttribute('aria-expanded') != null && row.querySelector('.tree-toggle')) return 'inside';
|
||||
}
|
||||
// non-container leaf: middle zone still sorts after the row
|
||||
return 'after';
|
||||
}
|
||||
|
||||
function filetreeDropDetail(targetRow, fileTree, deps) {
|
||||
deps = deps || {};
|
||||
var resolveWorkspaceId = deps.resolveWorkspaceId || function() { return ''; };
|
||||
var fileTreeRowLocalUploadTargetRelativePath = deps.fileTreeRowLocalUploadTargetRelativePath || function() { return ''; };
|
||||
var dropPosition = deps.dropPosition || null;
|
||||
return {
|
||||
workspaceId: resolveWorkspaceId(targetRow || fileTree),
|
||||
targetRowId: targetRow ? targetRow.getAttribute('data-row-id') : null,
|
||||
@@ -36,6 +61,7 @@ function filetreeDropDetail(targetRow, fileTree, deps) {
|
||||
assetId: targetRow ? targetRow.getAttribute('data-asset-id') : null,
|
||||
targetRelativePath: targetRow ? fileTreeRowLocalUploadTargetRelativePath(targetRow) : '',
|
||||
uploadIntent: 'filetree.folder.drop',
|
||||
dropPosition: dropPosition || (targetRow ? 'inside' : 'inside'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -59,8 +85,21 @@ function parseFileTreeDragPayload(raw) {
|
||||
function clearFileTreeDropFeedback() {
|
||||
if (activeFileTreeDropRow instanceof HTMLElement) {
|
||||
activeFileTreeDropRow.setAttribute('data-drop-target', 'false');
|
||||
activeFileTreeDropRow.removeAttribute('data-drop-position');
|
||||
activeFileTreeDropRow.setAttribute('data-drop-feedback', 'false');
|
||||
}
|
||||
activeFileTreeDropRow = null;
|
||||
activeFileTreeDropPosition = null;
|
||||
}
|
||||
|
||||
function setFileTreeDropFeedback(row, position) {
|
||||
clearFileTreeDropFeedback();
|
||||
if (!(row instanceof HTMLElement)) return;
|
||||
activeFileTreeDropRow = row;
|
||||
activeFileTreeDropPosition = position || 'inside';
|
||||
activeFileTreeDropRow.setAttribute('data-drop-target', 'true');
|
||||
activeFileTreeDropRow.setAttribute('data-drop-feedback', 'true');
|
||||
activeFileTreeDropRow.setAttribute('data-drop-position', activeFileTreeDropPosition);
|
||||
}
|
||||
|
||||
function resetFileTreeDragState() {
|
||||
@@ -95,10 +134,27 @@ function handleFileTreeDragOver(event, deps) {
|
||||
if (!hasFiles && !hasInternal && draggingFileTreeRowIds.length === 0) return false;
|
||||
|
||||
event.preventDefault();
|
||||
clearFileTreeDropFeedback();
|
||||
activeFileTreeDropRow = closestAction(event.target, '.tree-row[data-shell-mode="filetree"]') || fileTree;
|
||||
if (activeFileTreeDropRow instanceof HTMLElement) {
|
||||
activeFileTreeDropRow.setAttribute('data-drop-target', 'true');
|
||||
var row = closestAction(event.target, '.tree-row[data-shell-mode="filetree"]');
|
||||
if (row instanceof HTMLElement) {
|
||||
var position = hasFiles ? 'inside' : fileTreeDropPosition(event, row);
|
||||
// external file drops only nest into containers
|
||||
if (hasFiles) {
|
||||
var kind = String(row.getAttribute('data-row-kind') || '').trim();
|
||||
if (kind !== 'folder' && kind !== 'directory' && kind !== 'doc' && kind !== 'document') {
|
||||
// drop onto leaf file → treat as after (parent folder context resolved at drop)
|
||||
position = 'after';
|
||||
} else if (kind === 'folder' || kind === 'directory') {
|
||||
position = 'inside';
|
||||
}
|
||||
}
|
||||
setFileTreeDropFeedback(row, position);
|
||||
} else {
|
||||
clearFileTreeDropFeedback();
|
||||
activeFileTreeDropRow = fileTree;
|
||||
if (activeFileTreeDropRow instanceof HTMLElement) {
|
||||
activeFileTreeDropRow.setAttribute('data-drop-target', 'true');
|
||||
}
|
||||
activeFileTreeDropPosition = 'inside';
|
||||
}
|
||||
if (event.dataTransfer) {
|
||||
var copyModifier = event.altKey || event.ctrlKey || event.metaKey;
|
||||
@@ -134,9 +190,16 @@ function handleFileTreeDrop(event, deps) {
|
||||
if (!files.length && !rowIds.length) return;
|
||||
|
||||
event.preventDefault();
|
||||
var dropPosition = activeFileTreeDropPosition
|
||||
|| (targetRow ? fileTreeDropPosition(event, targetRow) : 'inside');
|
||||
if (files.length) {
|
||||
var fileKind = targetRow ? String(targetRow.getAttribute('data-row-kind') || '').trim() : '';
|
||||
if (fileKind === 'folder' || fileKind === 'directory') dropPosition = 'inside';
|
||||
}
|
||||
var detail = filetreeDropDetail(targetRow, fileTree, {
|
||||
resolveWorkspaceId: resolveWorkspaceId,
|
||||
fileTreeRowLocalUploadTargetRelativePath: fileTreeRowLocalUploadTargetRelativePath,
|
||||
dropPosition: dropPosition,
|
||||
});
|
||||
clearFileTreeDropFeedback();
|
||||
if (files.length) {
|
||||
@@ -149,6 +212,7 @@ function handleFileTreeDrop(event, deps) {
|
||||
dispatchSidebarEvent('tree.filetree.internal-drop', Object.assign({}, detail, {
|
||||
rowIds: rowIds,
|
||||
copy: copyModifier,
|
||||
dropPosition: dropPosition,
|
||||
}));
|
||||
});
|
||||
}
|
||||
@@ -159,14 +223,18 @@ function handleFileTreeDrop(event, deps) {
|
||||
|
||||
window.__mnoteFileTreeDndRuntime = {
|
||||
FILETREE_DRAG_MIME: FILETREE_DRAG_MIME,
|
||||
draggingFileTreeRowIds: draggingFileTreeRowIds,
|
||||
activeFileTreeDropRow: activeFileTreeDropRow,
|
||||
get draggingFileTreeRowIds() { return draggingFileTreeRowIds; },
|
||||
set draggingFileTreeRowIds(value) { draggingFileTreeRowIds = value; },
|
||||
get activeFileTreeDropRow() { return activeFileTreeDropRow; },
|
||||
get activeFileTreeDropPosition() { return activeFileTreeDropPosition; },
|
||||
filetreeDragPayload: filetreeDragPayload,
|
||||
filetreeDropDetail: filetreeDropDetail,
|
||||
fileTreeDropPosition: fileTreeDropPosition,
|
||||
filetreeHasFiles: filetreeHasFiles,
|
||||
filetreeHasInternalDrag: filetreeHasInternalDrag,
|
||||
parseFileTreeDragPayload: parseFileTreeDragPayload,
|
||||
clearFileTreeDropFeedback: clearFileTreeDropFeedback,
|
||||
setFileTreeDropFeedback: setFileTreeDropFeedback,
|
||||
resetFileTreeDragState: resetFileTreeDragState,
|
||||
startFileTreeDrag: startFileTreeDrag,
|
||||
handleFileTreeDragOver: handleFileTreeDragOver,
|
||||
|
||||
Reference in New Issue
Block a user