Improve local filetree view state and sidebar performance
This commit is contained in:
@@ -88,6 +88,7 @@ pub fn PageLayout(
|
||||
Some(sidebar_tree_html.as_str()),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
});
|
||||
let tree_live_bootstrap = serde_json::json!({
|
||||
@@ -175,8 +176,8 @@ pub fn PageLayout(
|
||||
</nav>
|
||||
</div>
|
||||
<div class="wolai-topbar-actions" aria-label="页面操作">
|
||||
<span class="wolai-public-pill" data-testid="wolai-public-state">"全网公开"</span>
|
||||
<button type="button" class="wolai-icon-button" title="收藏" aria-label="收藏"><span class="material-symbols-outlined" data-icon="star" aria-hidden="true"></span></button>
|
||||
<span class="wolai-public-pill" data-testid="wolai-public-state" hidden></span>
|
||||
<button type="button" class="wolai-icon-button" title="星标置顶" aria-label="星标置顶" data-mnote-action="toggle-sidebar-shortcut" data-mnote-shortcut-kind="page"><span class="material-symbols-outlined" data-icon="star" aria-hidden="true"></span></button>
|
||||
<button type="button" class="wolai-icon-button" title="演示" aria-label="演示"><span class="material-symbols-outlined" data-icon="slideshow" aria-hidden="true"></span></button>
|
||||
<button type="button" class="wolai-icon-button" title="评论" aria-label="评论"><span class="material-symbols-outlined" data-icon="comment" aria-hidden="true"></span></button>
|
||||
<button type="button" class="wolai-icon-button" title="分享" aria-label="分享"><span class="material-symbols-outlined" data-icon="share" aria-hidden="true"></span></button>
|
||||
@@ -199,6 +200,8 @@ pub fn PageLayout(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use leptos::prelude::ElementChild;
|
||||
|
||||
const SIDEBAR_TREE_RUNTIME_JS: &str = include_str!("../../../browser/sidebar-tree-runtime.js");
|
||||
const SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS: &str =
|
||||
include_str!("../../../browser/sidebar-tree-live-apply-runtime.js");
|
||||
@@ -260,6 +263,8 @@ mod tests {
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("MNOTE_LAST_CLOUD_WORKSPACE_KEY"));
|
||||
assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS
|
||||
.contains("data-global-option-checkbox=\"showHeadingNumbers\""));
|
||||
assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("/api/ui/preferences"));
|
||||
assert!(!SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("localStorage.setItem"));
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("restoreSidebarTreeTab"));
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("treeView"));
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("tree:local-command"));
|
||||
@@ -341,6 +346,51 @@ mod tests {
|
||||
assert!(SIDEBAR_WORKSPACE_RUNTIME_JS.contains("fetch('/api/user/access-policy'"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_layout_hides_public_state_and_exposes_sidebar_shortcut_star() {
|
||||
let html = crate::ssr::render_view(leptos::view! {
|
||||
<super::PageLayout current_nav="documents" topbar_title={"个人".to_string()}>
|
||||
<main>"正文"</main>
|
||||
</super::PageLayout>
|
||||
});
|
||||
|
||||
assert!(!html.contains(r#"data-testid="wolai-public-state">全网公开"#));
|
||||
assert!(html.contains(r#"data-mnote-action="toggle-sidebar-shortcut""#));
|
||||
assert!(html.contains(r#"data-mnote-shortcut-kind="page""#));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sidebar_runtime_supports_shortcuts_and_scoped_filetree() {
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("/api/sidebar/shortcuts"));
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("toggle-sidebar-shortcut"));
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("openStarredFolderShortcut"));
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("rootUri: currentRootUri()"));
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("var rootUri = readShortcutRootUri(row);"));
|
||||
assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("inferLocalRootUriFromWorkspaceId"));
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-mnote-filetree-scope"));
|
||||
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("toggle-sidebar-folder-shortcut"));
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("/api/tree/projections/sidebar"));
|
||||
let scope_start = SIDEBAR_TREE_RUNTIME_JS
|
||||
.find("persistStarredFolderScope(workspaceId, rootUri, relativePath);")
|
||||
.expect("starred folder should persist scope before loading");
|
||||
let sidebar_fetch_start = SIDEBAR_TREE_RUNTIME_JS[scope_start..]
|
||||
.find("fetch(sidebarUrl.toString()")
|
||||
.expect("starred folder should fetch local page projection")
|
||||
+ scope_start;
|
||||
let fetch_start = SIDEBAR_TREE_RUNTIME_JS[scope_start..]
|
||||
.find("fetch(url.toString()")
|
||||
.expect("starred folder should fetch scoped projection")
|
||||
+ scope_start;
|
||||
assert!(
|
||||
sidebar_fetch_start < fetch_start,
|
||||
"星标文件夹请求 scoped filetree 前必须先拉本地页面树投影,避免我的空间旧页面残留"
|
||||
);
|
||||
assert!(
|
||||
scope_start < fetch_start,
|
||||
"星标文件夹应先写入 fileTreeScope,再请求大目录,避免 live refresh 把视图折回根目录"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_ai_context_uses_page_aggregate_subtree_as_single_truth() {
|
||||
assert!(
|
||||
@@ -503,9 +553,10 @@ mod tests {
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("/api/tree/projections/sidebar"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("/api/tree/projections/file"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("renderSidebarSnapshot(sidebarPayload.result || sidebarPayload)"));
|
||||
.contains("var resolvedSidebarPayload = sidebarPayload ? (sidebarPayload.result || sidebarPayload) : null"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("renderFileProjection(filePayload.result || filePayload)"));
|
||||
.contains("var renderedPage = resolvedSidebarPayload && hasProjectionItems(resolvedSidebarPayload)"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("refreshFileTreeParent(fileTreeScope)"));
|
||||
assert!(
|
||||
SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("data-mnote-local-folder-watch-applied")
|
||||
);
|
||||
@@ -565,6 +616,29 @@ mod tests {
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("editorRoot: uploadContext.root"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sidebar_attachment_open_runtime_receives_closest_action_dependency() {
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function closestAction"));
|
||||
let injection_start = SIDEBAR_TREE_RUNTIME_JS
|
||||
.find("const sidebarAttachmentOpen = createSidebarAttachmentOpenRuntime({")
|
||||
.expect("attachment runtime injection");
|
||||
let injection_end = SIDEBAR_TREE_RUNTIME_JS[injection_start..]
|
||||
.find(" });")
|
||||
.map(|offset| injection_start + offset)
|
||||
.expect("attachment runtime injection end");
|
||||
let injection = &SIDEBAR_TREE_RUNTIME_JS[injection_start..injection_end];
|
||||
assert!(
|
||||
injection.contains("closestAction"),
|
||||
"attachment open runtime 需要显式注入 closestAction,避免首屏点击监听安装时 ReferenceError"
|
||||
);
|
||||
assert!(
|
||||
SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS
|
||||
.contains("closestAction = typeof injectedClosestAction === 'function'")
|
||||
|| SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("dependencies.closestAction"),
|
||||
"attachment open runtime 应从 dependencies 读取 closestAction,而不是依赖外层闭包"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_upload_runtime_contains_editor_upload_context_helpers() {
|
||||
const LOCAL_UPLOAD_RUNTIME_JS: &str =
|
||||
@@ -803,8 +877,9 @@ mod tests {
|
||||
"selectSidebarFileTreeDocument",
|
||||
);
|
||||
assert!(
|
||||
select_document_body
|
||||
.contains("selectSidebarFileTreeRow(row, { ctrlKey: false, metaKey: false, shiftKey: false })"),
|
||||
select_document_body.contains("activateSidebarFileTreeRow(row, options)")
|
||||
&& SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("function activateSidebarFileTreeRow")
|
||||
&& SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("selectSidebarFileTreeRow(row, { ctrlKey: false, metaKey: false, shiftKey: false })"),
|
||||
"document selection 应复用 filetree row selection runtime/fallback"
|
||||
);
|
||||
}
|
||||
@@ -1006,8 +1081,173 @@ mod tests {
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("/api/tree/projections/file/children"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("parentRelativePath"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("data-filetree-children-loaded"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("fileTreeLazyChildrenCache"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("rowsByParent: new Map()"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("loadedParents: new Set()"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("loadingParents: new Map()"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("dirtyParents: new Set()"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("fileTreeExpandedRelativePaths"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("if (fileTreeState.loadingParents.has(key))"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("return fileTreeState.loadingParents.get(key);"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("function markExistingFileTreeChildrenLoaded"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("if (markExistingFileTreeChildrenLoaded(row, button))"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("scheduleRestorePersistedFileTreeExpansionState();"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("function patchFileTreeParentChildren"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("return patchFileTreeParentChildren(parentRelativePath, rows);"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("var renderedPage = resolvedSidebarPayload && hasProjectionItems(resolvedSidebarPayload)"));
|
||||
let render_start = SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.find("function renderFileProjection(projection)")
|
||||
.expect("renderFileProjection");
|
||||
let render_end = SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS[render_start..]
|
||||
.find("function renderSidebarSnapshot(payload)")
|
||||
.map(|offset| render_start + offset)
|
||||
.expect("renderFileProjection end");
|
||||
let render_body = &SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS[render_start..render_end];
|
||||
assert!(!render_body.contains("tree.innerHTML ="));
|
||||
assert!(render_body.contains("tree.replaceChildren"));
|
||||
let patch_branch = render_body
|
||||
.find("patchFileTreeParentChildren(parentRelativePath, rows)")
|
||||
.expect("non-root parent patch");
|
||||
let root_replace = render_body
|
||||
.find("tree.replaceChildren")
|
||||
.expect("root replacement");
|
||||
assert!(
|
||||
patch_branch < root_replace,
|
||||
"非 scope parent projection 必须先局部 patch,不能替换整棵 filetree"
|
||||
);
|
||||
let load_start = SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.find("async function loadFileTreeChildren(row, button)")
|
||||
.expect("lazy children loader");
|
||||
let optimistic_expand = SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS[load_start..]
|
||||
.find("setTreeRowExpanded(row, button, true);")
|
||||
.expect("lazy loading should mark the requested folder expanded before fetch")
|
||||
+ load_start;
|
||||
let fetch_start = SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS[load_start..]
|
||||
.find("getFileTreeChildren(relativePath)")
|
||||
.expect("lazy children datasource call")
|
||||
+ load_start;
|
||||
assert!(
|
||||
optimistic_expand < fetch_start,
|
||||
"慢目录加载期间必须先保存 expanded 状态,否则 refresh/create-page 会把刚点开的文件夹折叠"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sidebar_filetree_runtime_discards_stale_generation_results() {
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("staleParents: new Set()"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("requestGeneration: 0"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("function beginFileTreeRequest"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("function isLatestFileTreeRequest"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("if (!isLatestFileTreeRequest(key, generation))"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("fileTreeState.staleParents.add(key)"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("fileTreeState.requestGeneration += 1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sidebar_filetree_runtime_prefers_command_affected_parents() {
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("function addAffectedParentsFromCommandResult"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("Array.isArray(result && result.affectedParents)"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("result && result.execution && result.execution.affectedParents"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("addAffectedParentsFromCommandResult(parents, result)"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("data-mnote-filetree-command-refresh-fallback"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sidebar_filetree_runtime_batches_local_command_refresh() {
|
||||
assert!(
|
||||
SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("function nextFileTreeOperationBatchId")
|
||||
);
|
||||
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("removeFileTreeAssetRow"));
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("removeFileTreeAssetRow,"));
|
||||
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("batchId: batchId"));
|
||||
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("tree:local-command-batch-complete"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("function queueFileTreeBatchRefresh"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("function flushFileTreeBatchRefresh"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("data-mnote-filetree-batch-refresh-pending"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("data-mnote-filetree-batch-refresh-applied"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sidebar_filetree_runtime_handles_watch_batch_and_structured_error() {
|
||||
assert!(TREE_LIVE_CONTROLLER_JS.contains("watch_batch"));
|
||||
assert!(TREE_LIVE_CONTROLLER_JS.contains("tree:local-folder-watch-batch"));
|
||||
assert!(TREE_LIVE_CONTROLLER_JS.contains("tree_error"));
|
||||
assert!(TREE_LIVE_CONTROLLER_JS.contains("tree:error"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("function applyLocalFolderWatchBatch"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("data-mnote-local-folder-watch-batch-applied"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("data-mnote-tree-live-error-schema"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sidebar_filetree_runtime_has_view_state_namespace() {
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("var fileTreeViewState = {"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("expandedParents: new Set()"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("selectedRowIds: new Set()"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("focusedRowId: ''"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("activeRowId: ''"));
|
||||
assert!(
|
||||
SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("var fileTreeState = fileTreeViewState;")
|
||||
);
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("var fileTreeExpandedRelativePaths = fileTreeViewState.expandedParents;"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains(
|
||||
"if (rootUri === fileTreeLazyCacheRootUri && scope === fileTreeState.scope) return;"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sidebar_filetree_runtime_reprojects_selection_focus_after_patch() {
|
||||
assert!(
|
||||
SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("function rememberFileTreeSelectionState")
|
||||
);
|
||||
assert!(
|
||||
SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("function reprojectFileTreeSelectionState")
|
||||
);
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("fileTreeViewState.selectedRowIds.add(rowId)"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("row.setAttribute('data-selected', String(fileTreeViewState.selectedRowIds.has(rowId)))"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains(
|
||||
"row.setAttribute('data-focused', String(rowId === fileTreeViewState.focusedRowId))"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sidebar_filetree_runtime_can_reveal_unloaded_resource_path() {
|
||||
assert!(
|
||||
SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("async function revealFileTreeResource")
|
||||
);
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("function fileTreeParentChainForRelativePath"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("await getFileTreeChildren(parentRelativePath)"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("fileTreeViewState.selectedRowIds = new Set([targetRowId])"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("revealFileTreeResource,"));
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("const revealFileTreeResource = (...args) => sidebarTreeLiveApply.revealFileTreeResource(...args);"));
|
||||
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("revealFileTreeResource({"));
|
||||
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
|
||||
.contains("decodeLocalEncodedPath(id.slice('local-md:'.length))"));
|
||||
assert!(
|
||||
SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("function localMarkdownBundleParentPath")
|
||||
);
|
||||
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
|
||||
.contains("visibleFileTreeRowByRelativePath(bundleParentPath)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1522,6 +1522,18 @@ body {
|
||||
color: var(--atelier-text);
|
||||
}
|
||||
|
||||
.wolai-section-add + .wolai-section-add {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.wolai-section-add .material-symbols-outlined {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
font-size: 15px;
|
||||
display: block;
|
||||
color: currentColor;
|
||||
}
|
||||
|
||||
.wolai-page-row {
|
||||
min-height: 30px;
|
||||
gap: 7px;
|
||||
@@ -1560,6 +1572,39 @@ body {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.wolai-row-more {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
flex: 0 0 26px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 0;
|
||||
border-radius: 4px;
|
||||
background: transparent;
|
||||
color: #8C8983;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.wolai-page-row:hover .wolai-row-more,
|
||||
.wolai-row-more:focus-visible,
|
||||
.wolai-row-more[aria-expanded="true"] {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.wolai-row-more:hover {
|
||||
background: #E7E4E0;
|
||||
color: #4B4945;
|
||||
}
|
||||
|
||||
.wolai-row-more .material-symbols-outlined {
|
||||
font-size: 18px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.sidebar-tree-section {
|
||||
|
||||
Reference in New Issue
Block a user