feat: 收口本地文件夹入口并推进 page aggregate rust-first
- 为 Rust Web 主入口补齐本地文件夹/云空间切换、最近目录与路径回填体验\n- 对齐 local markdown media 与 inline marks 的 Rust shell / TipTap converter 语义\n- 让 documents/page 优先消费 Rust page aggregate snapshot,并保留 TS fallback\n- 补强 tree live、local markdown 与主入口 smoke,并同步设计稿状态
This commit is contained in:
@@ -11,6 +11,7 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
var FILETREE_DRAG_MIME = 'application/x-mnote-filetree-row-ids';
|
||||
var MNOTE_SIDEBAR_TREE_MODE_KEY = 'mnote.sidebar.tree.mode';
|
||||
var MNOTE_RECENT_LOCAL_ROOTS_KEY = 'mnote.localFolder.recentRoots';
|
||||
var MNOTE_LAST_CLOUD_WORKSPACE_KEY = 'mnote.workspace.lastCloudWorkspaceId';
|
||||
var MNOTE_GLOBAL_SHOW_HEADING_NUMBERS_KEY = 'mnote.global.showHeadingNumbers';
|
||||
var mnoteNavigationInFlight = '';
|
||||
var draggingPageNodeId = '';
|
||||
@@ -298,6 +299,34 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
return (new URLSearchParams(window.location.search).get('workspaceId') || 'default').trim() || 'default';
|
||||
}
|
||||
|
||||
function currentSourceKind() {
|
||||
return (new URLSearchParams(window.location.search).get('sourceKind') || 'convex_workspace').trim() || 'convex_workspace';
|
||||
}
|
||||
|
||||
function rememberCloudWorkspaceId(workspaceId) {
|
||||
var normalized = String(workspaceId || '').trim();
|
||||
if (!normalized || normalized === 'local-folder') return;
|
||||
try {
|
||||
if (window.localStorage) window.localStorage.setItem(MNOTE_LAST_CLOUD_WORKSPACE_KEY, normalized);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
function readLastCloudWorkspaceId() {
|
||||
try {
|
||||
var stored = window.localStorage ? window.localStorage.getItem(MNOTE_LAST_CLOUD_WORKSPACE_KEY) : '';
|
||||
if (stored && stored.trim()) return stored.trim();
|
||||
} catch (_) {}
|
||||
var params = new URLSearchParams(window.location.search);
|
||||
var fromUrl = (params.get('workspaceId') || '').trim();
|
||||
if (fromUrl && currentSourceKind() !== 'local_folder') return fromUrl;
|
||||
var fromDom = document.querySelector('[data-workspace-id]');
|
||||
if (fromDom instanceof HTMLElement) {
|
||||
var value = (fromDom.getAttribute('data-workspace-id') || '').trim();
|
||||
if (value && value !== 'local-folder') return value;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function copyWorkspaceSourceParams(targetUrl) {
|
||||
var params = new URLSearchParams(window.location.search);
|
||||
['sourceKind', 'rootUri', 'secondaryDocumentId', 'secondarySourceKind', 'secondaryRootUri'].forEach(function(name) {
|
||||
@@ -347,7 +376,19 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
}).join('/');
|
||||
}
|
||||
|
||||
function fileRootUriToPathInput(rootUri) {
|
||||
var value = String(rootUri || '').replace(/^file:\/\//, '');
|
||||
try {
|
||||
return decodeURIComponent(value);
|
||||
} catch (_) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
function openLocalFolderRoot(rootUri) {
|
||||
if (currentSourceKind() !== 'local_folder') {
|
||||
rememberCloudWorkspaceId(resolveWorkspaceId(document.body));
|
||||
}
|
||||
rememberLocalRoot(rootUri);
|
||||
var targetUrl = new URL('/', window.location.origin);
|
||||
targetUrl.searchParams.set('treeView', 'filetree');
|
||||
@@ -356,6 +397,14 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
window.location.href = targetUrl.toString();
|
||||
}
|
||||
|
||||
function switchToCloudWorkspace() {
|
||||
var targetUrl = new URL('/', window.location.origin);
|
||||
var workspaceId = readLastCloudWorkspaceId();
|
||||
if (workspaceId) targetUrl.searchParams.set('workspaceId', workspaceId);
|
||||
targetUrl.searchParams.set('sourceKind', 'convex_workspace');
|
||||
window.location.href = targetUrl.toString();
|
||||
}
|
||||
|
||||
function closeLocalFolderDialog() {
|
||||
var existing = document.querySelector('[data-testid="mnote-local-folder-dialog"]');
|
||||
if (existing && existing.parentElement) existing.parentElement.removeChild(existing);
|
||||
@@ -461,7 +510,7 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
input.spellcheck = false;
|
||||
input.placeholder = '/mnt/Data1T/mnote/design/04-tree-domain/done';
|
||||
input.setAttribute('data-testid', 'mnote-local-folder-path-input');
|
||||
input.value = recent.length ? recent[0].replace(/^file:\/\//, '') : '';
|
||||
input.value = recent.length ? fileRootUriToPathInput(recent[0]) : '';
|
||||
input.style.width = '100%';
|
||||
input.style.boxSizing = 'border-box';
|
||||
input.style.border = '1px solid rgba(27, 28, 28, 0.18)';
|
||||
@@ -536,6 +585,11 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
card.appendChild(status);
|
||||
card.appendChild(input);
|
||||
if (recent.length) {
|
||||
var recentTitle = document.createElement('div');
|
||||
recentTitle.style.fontSize = '12px';
|
||||
recentTitle.style.color = '#6b7280';
|
||||
recentTitle.textContent = '最近使用';
|
||||
card.appendChild(recentTitle);
|
||||
var recentList = document.createElement('div');
|
||||
recentList.className = 'mnote-local-folder-dialog__recent';
|
||||
recent.slice(0, 5).forEach(function(rootUri) {
|
||||
@@ -551,13 +605,6 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
});
|
||||
card.appendChild(recentList);
|
||||
}
|
||||
if (recent.length) {
|
||||
var recentTitle = document.createElement('div');
|
||||
recentTitle.style.fontSize = '12px';
|
||||
recentTitle.style.color = '#6b7280';
|
||||
recentTitle.textContent = '最近使用';
|
||||
card.insertBefore(recentTitle, actions);
|
||||
}
|
||||
card.appendChild(actions);
|
||||
dialog.appendChild(card);
|
||||
dialog.addEventListener('click', function(event) {
|
||||
@@ -572,6 +619,79 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
openLocalFolderDialog('');
|
||||
}
|
||||
|
||||
function closeWorkspaceSourceMenu() {
|
||||
var existing = document.querySelector('[data-testid="mnote-workspace-source-menu"]');
|
||||
if (existing && existing.parentElement) existing.parentElement.removeChild(existing);
|
||||
document.querySelectorAll('[data-testid="mnote-workspace-source-trigger"]').forEach(function(trigger) {
|
||||
if (trigger instanceof HTMLElement) trigger.setAttribute('aria-expanded', 'false');
|
||||
});
|
||||
}
|
||||
|
||||
function workspaceSourceLabel(rootUri) {
|
||||
var label = String(rootUri || '').replace(/^file:\/\//, '');
|
||||
try {
|
||||
label = decodeURIComponent(label);
|
||||
} catch (_) {}
|
||||
return label || '本地文件夹';
|
||||
}
|
||||
|
||||
function openWorkspaceSourceMenu(trigger) {
|
||||
closeWorkspaceSourceMenu();
|
||||
if (!(trigger instanceof HTMLElement)) return;
|
||||
trigger.setAttribute('aria-expanded', 'true');
|
||||
var menu = document.createElement('div');
|
||||
menu.className = 'mnote-workspace-source-menu';
|
||||
menu.setAttribute('data-testid', 'mnote-workspace-source-menu');
|
||||
menu.setAttribute('role', 'menu');
|
||||
var cloudButton = document.createElement('button');
|
||||
cloudButton.type = 'button';
|
||||
cloudButton.className = 'mnote-workspace-source-menu__item';
|
||||
cloudButton.setAttribute('data-testid', 'mnote-switch-cloud-workspace');
|
||||
cloudButton.setAttribute('role', 'menuitem');
|
||||
cloudButton.textContent = '云空间';
|
||||
cloudButton.addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
closeWorkspaceSourceMenu();
|
||||
switchToCloudWorkspace();
|
||||
});
|
||||
menu.appendChild(cloudButton);
|
||||
var recent = readRecentLocalRoots();
|
||||
if (recent.length) {
|
||||
var label = document.createElement('div');
|
||||
label.className = 'mnote-workspace-source-menu__label';
|
||||
label.textContent = '最近本地文件夹';
|
||||
menu.appendChild(label);
|
||||
recent.slice(0, 8).forEach(function(rootUri) {
|
||||
var button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.className = 'mnote-workspace-source-menu__item';
|
||||
button.setAttribute('data-testid', 'mnote-recent-local-root');
|
||||
button.setAttribute('data-root-uri', rootUri);
|
||||
button.setAttribute('role', 'menuitem');
|
||||
button.textContent = workspaceSourceLabel(rootUri);
|
||||
button.addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
closeWorkspaceSourceMenu();
|
||||
openLocalFolderRoot(rootUri);
|
||||
});
|
||||
menu.appendChild(button);
|
||||
});
|
||||
}
|
||||
var openOther = document.createElement('button');
|
||||
openOther.type = 'button';
|
||||
openOther.className = 'mnote-workspace-source-menu__item mnote-workspace-source-menu__item--primary';
|
||||
openOther.setAttribute('data-testid', 'mnote-open-other-local-folder');
|
||||
openOther.setAttribute('role', 'menuitem');
|
||||
openOther.textContent = '打开其他本地文件夹';
|
||||
openOther.addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
closeWorkspaceSourceMenu();
|
||||
requestOpenLocalFolder();
|
||||
});
|
||||
menu.appendChild(openOther);
|
||||
trigger.closest('[data-testid="wolai-workspace-identity"]')?.appendChild(menu);
|
||||
}
|
||||
|
||||
function setCommandPending(trigger, pending) {
|
||||
if (!(trigger instanceof HTMLElement)) return;
|
||||
trigger.setAttribute('data-pending', pending ? 'true' : 'false');
|
||||
@@ -2280,6 +2400,17 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
return;
|
||||
}
|
||||
|
||||
var sourceMenuTrigger = closestAction(e.target, '[data-mnote-action="open-workspace-source-menu"]');
|
||||
if (sourceMenuTrigger) {
|
||||
e.preventDefault();
|
||||
var existingSourceMenu = document.querySelector('[data-testid="mnote-workspace-source-menu"]');
|
||||
if (existingSourceMenu) closeWorkspaceSourceMenu();
|
||||
else openWorkspaceSourceMenu(sourceMenuTrigger);
|
||||
return;
|
||||
}
|
||||
var sourceMenu = closestAction(e.target, '[data-testid="mnote-workspace-source-menu"]');
|
||||
if (!sourceMenu) closeWorkspaceSourceMenu();
|
||||
|
||||
var tabTrigger = closestAction(e.target, '[data-mnote-sidebar-tree-tab]');
|
||||
if (tabTrigger) {
|
||||
e.preventDefault();
|
||||
@@ -2871,8 +3002,18 @@ pub fn PageLayout(
|
||||
<aside class="mnote-sidebar wolai-sidebar" data-testid="wolai-sidebar">
|
||||
<div class="mnote-sidebar-header wolai-sidebar-header" data-testid="wolai-workspace-identity">
|
||||
<a href="/" class="mnote-sidebar-brand wolai-avatar" aria-label="工作区首页">"L"</a>
|
||||
<span class="sidebar-workspace-name">{ws_name.clone()}</span>
|
||||
<span class="wolai-sidebar-chevron" aria-hidden="true">"⌄"</span>
|
||||
<button
|
||||
type="button"
|
||||
class="mnote-workspace-source-trigger"
|
||||
data-testid="mnote-workspace-source-trigger"
|
||||
data-mnote-action="open-workspace-source-menu"
|
||||
aria-haspopup="menu"
|
||||
aria-expanded="false"
|
||||
title="切换云空间或本地文件夹"
|
||||
>
|
||||
<span class="sidebar-workspace-name">{ws_name.clone()}</span>
|
||||
<span class="wolai-sidebar-chevron" aria-hidden="true">"⌄"</span>
|
||||
</button>
|
||||
</div>
|
||||
<nav class="mnote-sidebar-nav wolai-quick-actions" aria-label="快捷操作">
|
||||
<a href="/search" class:active={current_nav == "search"} title="搜索" aria-label="搜索" data-mnote-action="open-search-modal"><span class="material-symbols-outlined nav-icon" data-icon="search" aria-hidden="true"></span></a>
|
||||
@@ -2931,6 +3072,7 @@ mod tests {
|
||||
assert!(SIDEBAR_TREE_JS.contains("data-mnote-navigation-pending"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("MNOTE_SIDEBAR_TREE_MODE_KEY"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("MNOTE_GLOBAL_SHOW_HEADING_NUMBERS_KEY"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("MNOTE_LAST_CLOUD_WORKSPACE_KEY"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("data-global-option-checkbox=\"showHeadingNumbers\""));
|
||||
assert!(SIDEBAR_TREE_JS.contains("restoreSidebarTreeTab"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("treeView"));
|
||||
@@ -2947,6 +3089,9 @@ mod tests {
|
||||
assert!(SIDEBAR_TREE_JS.contains("MNOTE_RECENT_LOCAL_ROOTS_KEY"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("requestOpenLocalFolder"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("sourceKind', 'local_folder"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("switchToCloudWorkspace"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("mnote-switch-cloud-workspace"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("mnote-recent-local-root"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user