fix: align rust web stitch sidebar tabs
This commit is contained in:
@@ -37,11 +37,21 @@ pub fn DocumentPage(
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty() && *value != "null")
|
||||
.is_some();
|
||||
let workspace_label = workspace_name
|
||||
.clone()
|
||||
.map(|value| value.trim().to_string())
|
||||
.filter(|value| !value.is_empty())
|
||||
.unwrap_or_else(|| "个人空间".to_string());
|
||||
view! {
|
||||
<PageLayout current_nav="documents" sidebar_tree_html={sidebar_tree_html.unwrap_or_default()} workspace_name={workspace_name.unwrap_or_default()} workspace_sidebar_html={workspace_sidebar_html.unwrap_or_default()} topbar_title={title.clone()}>
|
||||
<main class="document-shell" data-editor-host="leptos_tiptap_island" data-document-id={document_id.clone()}>
|
||||
<header class="document-shell-header">
|
||||
<div class="document-page-icon" aria-hidden="true">"⌂"</div>
|
||||
<h1>{title}</h1>
|
||||
<div class="document-shell-meta" aria-label="页面元信息">
|
||||
<span><span aria-hidden="true">"◌"</span>{workspace_label}</span>
|
||||
<span><span aria-hidden="true">"▣"</span>"已同步"</span>
|
||||
</div>
|
||||
</header>
|
||||
<section data-page-aggregate-snapshot="mnote.page_aggregate.v1"></section>
|
||||
<section
|
||||
|
||||
@@ -43,9 +43,14 @@ pub fn HomePage(
|
||||
<PageLayout current_nav="home" sidebar_tree_html={sidebar_tree_html.unwrap_or_default()} workspace_name={workspace_name.unwrap_or_default()} workspace_sidebar_html={workspace_sidebar_html.unwrap_or_default()} topbar_title={active_page_title.clone()}>
|
||||
{move || if has_active_page {
|
||||
view! {
|
||||
<main class="document-shell" data-root-active-page-id={active_page_id.clone()} data-editor-host="leptos_tiptap_island">
|
||||
<main class="document-shell document-shell--workspace-entry" data-root-active-page-id={active_page_id.clone()} data-editor-host="leptos_tiptap_island">
|
||||
<header class="document-shell-header">
|
||||
<div class="document-page-icon" aria-hidden="true">"⌂"</div>
|
||||
<h1>{active_page_title.clone()}</h1>
|
||||
<div class="document-shell-meta" aria-label="页面元信息">
|
||||
<span><span aria-hidden="true">"◌"</span>"个人空间"</span>
|
||||
<span><span aria-hidden="true">"▣"</span>"工作区首页"</span>
|
||||
</div>
|
||||
</header>
|
||||
<section class="mnote-workspace-active-state" data-testid="mnote-workspace-active-page-state">
|
||||
<a class="mnote-workspace-active-link" href={active_href.clone()}>"打开当前页面"</a>
|
||||
|
||||
@@ -51,7 +51,32 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
window.location.href = '/documents/' + encodeURIComponent(result.documentId) + '?workspaceId=' + encodeURIComponent(nextWorkspaceId);
|
||||
}
|
||||
|
||||
function switchSidebarTreeTab(trigger) {
|
||||
var mode = (trigger.getAttribute('data-mnote-sidebar-tree-tab') || 'page').trim();
|
||||
var shell = trigger.closest('[data-testid="wolai-sidebar-page-tree-shell"]');
|
||||
if (!shell) return;
|
||||
var tabs = shell.querySelectorAll('[data-mnote-sidebar-tree-tab]');
|
||||
for (var i = 0; i < tabs.length; i++) {
|
||||
var isActive = tabs[i].getAttribute('data-mnote-sidebar-tree-tab') === mode;
|
||||
tabs[i].setAttribute('aria-selected', isActive ? 'true' : 'false');
|
||||
tabs[i].classList.toggle('wolai-sidebar-tab-active', isActive);
|
||||
tabs[i].classList.toggle('wolai-sidebar-tab-muted', !isActive);
|
||||
}
|
||||
var panels = shell.querySelectorAll('[data-mnote-sidebar-tree-panel]');
|
||||
for (var j = 0; j < panels.length; j++) {
|
||||
var isCurrentPanel = panels[j].getAttribute('data-mnote-sidebar-tree-panel') === mode;
|
||||
panels[j].hidden = !isCurrentPanel;
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('click', function(e) {
|
||||
var tabTrigger = closestAction(e.target, '[data-mnote-sidebar-tree-tab]');
|
||||
if (tabTrigger) {
|
||||
e.preventDefault();
|
||||
switchSidebarTreeTab(tabTrigger);
|
||||
return;
|
||||
}
|
||||
|
||||
var createTrigger = closestAction(e.target, '[data-mnote-action="create-page"]');
|
||||
if (createTrigger) {
|
||||
e.preventDefault();
|
||||
@@ -184,10 +209,10 @@ pub fn PageLayout(
|
||||
</div>
|
||||
<nav class="mnote-sidebar-nav wolai-quick-actions" aria-label="快捷操作">
|
||||
<a href="/search" class:active={current_nav == "search"} title="搜索"><span class="nav-icon">"⌕"</span></a>
|
||||
<a href="/graph" title="关系图"><span class="nav-icon">"⌬"</span></a>
|
||||
<a href="/graph" title="关系图"><span class="nav-icon">"⌘"</span></a>
|
||||
<a href="/actions" title="快捷动作"><span class="nav-icon">"↯"</span></a>
|
||||
<a href="/inbox" title="收纳"><span class="nav-icon">"▣"</span></a>
|
||||
<a href="/files" title="文件"><span class="nav-icon">"▱"</span></a>
|
||||
<a href="/help" title="帮助"><span class="nav-icon">"?"</span></a>
|
||||
<a href="/files" title="文件"><span class="nav-icon">"▣"</span></a>
|
||||
<a href="/more" title="更多"><span class="nav-icon">"…"</span></a>
|
||||
</nav>
|
||||
<div class="wolai-sidebar-body" inner_html={sidebar_sections_html}></div>
|
||||
@@ -195,23 +220,29 @@ pub fn PageLayout(
|
||||
</aside>
|
||||
<div class="mnote-main">
|
||||
<header class="wolai-topbar" data-testid="wolai-topbar">
|
||||
<div class="wolai-topbar-left"><span class="wolai-menu-icon">"☰"</span><span class="wolai-home-icon">"⌂"</span><span>{topbar_title}</span></div>
|
||||
<div class="wolai-topbar-left">
|
||||
<button type="button" class="wolai-icon-button wolai-menu-button" aria-label="切换侧栏">"☰"</button>
|
||||
<nav class="wolai-breadcrumb" aria-label="页面路径">
|
||||
<span class="wolai-breadcrumb-root">"The Digital Atelier"</span>
|
||||
<span class="wolai-breadcrumb-separator" aria-hidden="true">"/"</span>
|
||||
<span class="wolai-breadcrumb-current"><span class="wolai-home-icon" aria-hidden="true">"⌂"</span>{topbar_title}</span>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="wolai-topbar-actions" aria-label="页面操作">
|
||||
<span title="收藏">"☆"</span>
|
||||
<span title="演示">"▻"</span>
|
||||
<span title="评论">"◴"</span>
|
||||
<span title="关系">"⌬"</span>
|
||||
<span title="成员">"♙+"</span>
|
||||
<span title="历史">"◷"</span>
|
||||
<span title="更多">"…"</span>
|
||||
<span class="wolai-public-pill"><span aria-hidden="true">"●"</span>"Public"</span>
|
||||
<button type="button" class="wolai-icon-button" title="收藏" aria-label="收藏">"☆"</button>
|
||||
<button type="button" class="wolai-icon-button" title="历史" aria-label="历史">"◷"</button>
|
||||
<button type="button" class="wolai-icon-button wolai-ai-inline" title="AI" aria-label="AI">"✦"</button>
|
||||
<button type="button" class="wolai-icon-button" title="搜索" aria-label="搜索">"⌕"</button>
|
||||
<button type="button" class="wolai-icon-button" title="更多" aria-label="更多">"…"</button>
|
||||
</div>
|
||||
</header>
|
||||
<article class="mnote-content">
|
||||
{children()}
|
||||
</article>
|
||||
<div class="wolai-floating-actions" aria-label="浮动操作">
|
||||
<button type="button" data-testid="wolai-floating-ai" class="wolai-floating-button">"AI"</button>
|
||||
<button type="button" data-testid="wolai-floating-help" class="wolai-floating-button">"?"</button>
|
||||
<button type="button" data-testid="wolai-floating-help" class="wolai-floating-button wolai-floating-button--help" aria-label="帮助">"?"</button>
|
||||
<button type="button" data-testid="wolai-floating-ai" class="wolai-floating-button wolai-floating-button--ai" aria-label="AI 助手">"✦"</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -865,6 +865,705 @@ a:hover {
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== Stitch 260429 UI parity overrides ===== */
|
||||
:root {
|
||||
--atelier-surface: #FAF9F9;
|
||||
--atelier-sidebar: #F4F3F3;
|
||||
--atelier-sidebar-hover: #ECEBE9;
|
||||
--atelier-sidebar-active: #E3E2E2;
|
||||
--atelier-document: #FFFFFF;
|
||||
--atelier-text: #1B1C1C;
|
||||
--atelier-text-muted: #8A8782;
|
||||
--atelier-text-soft: #B7B4AF;
|
||||
--atelier-outline: #EAEAEA;
|
||||
--atelier-primary: #006E28;
|
||||
--atelier-primary-container: #31AA4D;
|
||||
--atelier-secondary: #2367F6;
|
||||
--atelier-shadow: 0 16px 32px rgba(27, 28, 28, 0.04);
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
background: var(--atelier-document);
|
||||
}
|
||||
|
||||
body {
|
||||
color: var(--atelier-text);
|
||||
font-family: Inter, var(--wolai-font-sans);
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.mnote-shell,
|
||||
.wolai-workspace-shell {
|
||||
background: var(--atelier-document);
|
||||
}
|
||||
|
||||
.mnote-sidebar,
|
||||
.wolai-sidebar {
|
||||
width: 240px;
|
||||
background: var(--atelier-sidebar);
|
||||
border-right: 0;
|
||||
box-shadow: inset -1px 0 0 rgba(27, 28, 28, 0.06);
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.mnote-sidebar-header,
|
||||
.wolai-sidebar-header {
|
||||
height: 68px;
|
||||
padding: 16px 12px 8px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.wolai-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 5px;
|
||||
background: #D6534D;
|
||||
font-size: 17px;
|
||||
font-weight: 650;
|
||||
}
|
||||
|
||||
.wolai-sidebar-header .sidebar-workspace-name,
|
||||
.sidebar-workspace-name {
|
||||
max-width: 160px;
|
||||
color: var(--atelier-text);
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.wolai-sidebar-chevron {
|
||||
color: #55514C;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.wolai-quick-actions {
|
||||
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
padding: 8px 16px 26px;
|
||||
}
|
||||
|
||||
.wolai-quick-actions a,
|
||||
.mnote-sidebar-nav a {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.wolai-quick-actions a {
|
||||
height: 28px;
|
||||
color: #3F3D39;
|
||||
font-size: 19px;
|
||||
}
|
||||
|
||||
.wolai-quick-actions a:hover {
|
||||
background: rgba(27, 28, 28, 0.07);
|
||||
}
|
||||
|
||||
.wolai-sidebar-body {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.wolai-sidebar-section {
|
||||
padding: 0 8px 18px;
|
||||
}
|
||||
|
||||
.wolai-section-title,
|
||||
.wolai-sidebar-tabs {
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 0 8px;
|
||||
color: var(--atelier-text-muted);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.wolai-section-icon {
|
||||
color: #FF9F1A;
|
||||
}
|
||||
|
||||
.wolai-section-caret {
|
||||
color: var(--atelier-text-soft);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.wolai-sidebar-tabs {
|
||||
gap: 16px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.wolai-sidebar-tab {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
min-height: 26px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
color: var(--atelier-text-muted);
|
||||
font: inherit;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.wolai-sidebar-tab:hover {
|
||||
color: var(--atelier-text);
|
||||
}
|
||||
|
||||
.wolai-sidebar-tab-active {
|
||||
color: var(--atelier-text);
|
||||
border-bottom: 2px solid var(--atelier-primary-container);
|
||||
}
|
||||
|
||||
.wolai-sidebar-tab-muted {
|
||||
color: #9B9892;
|
||||
}
|
||||
|
||||
.wolai-sidebar-tab-panels {
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.wolai-sidebar-tab-panel[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.wolai-folder-icon {
|
||||
color: #A8A49E;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.wolai-section-add {
|
||||
margin-left: auto;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: 4px;
|
||||
color: var(--atelier-text-soft);
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.wolai-section-add:hover {
|
||||
background: var(--atelier-sidebar-hover);
|
||||
color: var(--atelier-text);
|
||||
}
|
||||
|
||||
.wolai-page-row {
|
||||
min-height: 30px;
|
||||
gap: 7px;
|
||||
margin: 1px 4px;
|
||||
padding: 0 8px 0 12px;
|
||||
border-radius: 4px;
|
||||
color: #2D2E2E;
|
||||
font-size: 14px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.wolai-page-row:hover {
|
||||
background: var(--atelier-sidebar-hover);
|
||||
}
|
||||
|
||||
.wolai-page-row[data-active="true"],
|
||||
.wolai-active-row {
|
||||
background: var(--atelier-sidebar-active);
|
||||
color: var(--atelier-text);
|
||||
}
|
||||
|
||||
.wolai-row-caret {
|
||||
width: 12px;
|
||||
flex: 0 0 12px;
|
||||
color: #B8B5AF;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.wolai-row-icon {
|
||||
width: 20px;
|
||||
color: #111111;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.wolai-row-title {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.sidebar-tree-section {
|
||||
border-top: 0;
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.sidebar-tree {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.sidebar-tree .tree-empty,
|
||||
.wolai-sidebar-empty {
|
||||
padding: 6px 12px;
|
||||
color: var(--atelier-text-soft);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.sidebar-tree .tree-row {
|
||||
height: 29px;
|
||||
gap: 5px;
|
||||
margin: 1px 4px;
|
||||
padding: 0 8px;
|
||||
border-radius: 4px;
|
||||
color: #2D2E2E;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.sidebar-tree .tree-row:hover {
|
||||
background: var(--atelier-sidebar-hover);
|
||||
}
|
||||
|
||||
.sidebar-tree .tree-row[data-active="true"],
|
||||
.sidebar-tree .tree-row[data-selected="true"] {
|
||||
background: var(--atelier-sidebar-active);
|
||||
color: var(--atelier-text);
|
||||
}
|
||||
|
||||
.sidebar-tree .tree-toggle,
|
||||
.sidebar-tree .tree-spacer {
|
||||
width: 16px;
|
||||
height: 22px;
|
||||
color: #B8B5AF;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.sidebar-tree .tree-toggle:hover {
|
||||
background: rgba(27, 28, 28, 0.06);
|
||||
}
|
||||
|
||||
.sidebar-tree .tree-kind-badge {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 0 0 20px;
|
||||
color: #111111;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.sidebar-tree .tree-kind-badge::before {
|
||||
content: "□";
|
||||
}
|
||||
|
||||
.sidebar-tree .tree-kind-badge[data-kind="page"]::before {
|
||||
content: "⌂";
|
||||
}
|
||||
|
||||
.sidebar-tree[data-tree-shell-mode="filetree"] .tree-kind-badge[data-kind="page"]::before,
|
||||
.sidebar-tree .tree-kind-badge[data-kind="document"]::before {
|
||||
content: "◇";
|
||||
}
|
||||
|
||||
.sidebar-tree .tree-kind-badge[data-kind="index"]::before {
|
||||
content: "▤";
|
||||
color: var(--atelier-secondary);
|
||||
}
|
||||
|
||||
.sidebar-tree .tree-kind-badge[data-kind="folder"]::before {
|
||||
content: "▱";
|
||||
color: #D08A1F;
|
||||
}
|
||||
|
||||
.sidebar-tree .tree-link {
|
||||
padding: 0 2px;
|
||||
color: inherit;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.sidebar-tree .tree-link-title {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.sidebar-tree .tree-row[data-active="true"] .tree-link-title {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.sidebar-tree .tree-actions {
|
||||
gap: 1px;
|
||||
}
|
||||
|
||||
.sidebar-tree .tree-action {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: #9A968F;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.sidebar-tree .tree-action:hover {
|
||||
background: rgba(27, 28, 28, 0.07);
|
||||
color: var(--atelier-text);
|
||||
}
|
||||
|
||||
.sidebar-tree .tree-node .tree-children .tree-row {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.sidebar-tree .tree-node .tree-children .tree-children .tree-row {
|
||||
padding-left: 32px;
|
||||
}
|
||||
|
||||
.sidebar-tree .tree-node .tree-children .tree-children .tree-children .tree-row {
|
||||
padding-left: 44px;
|
||||
}
|
||||
|
||||
.sidebar-tree .tree-node .tree-children .tree-children .tree-children .tree-children .tree-row {
|
||||
padding-left: 56px;
|
||||
}
|
||||
|
||||
.wolai-file-tree-section {
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.wolai-explorer-title {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.wolai-sidebar-footer {
|
||||
border-top: 1px solid rgba(27, 28, 28, 0.08);
|
||||
background: var(--atelier-sidebar);
|
||||
}
|
||||
|
||||
.wolai-footer-entry {
|
||||
height: 44px;
|
||||
gap: 8px;
|
||||
color: #55514C;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.wolai-footer-entry + .wolai-footer-entry {
|
||||
border-left: 1px solid rgba(27, 28, 28, 0.08);
|
||||
}
|
||||
|
||||
.mnote-main,
|
||||
.mnote-content {
|
||||
background: var(--atelier-document);
|
||||
}
|
||||
|
||||
.wolai-topbar {
|
||||
height: 44px;
|
||||
padding: 0 20px 0 22px;
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
color: #5A5A5A;
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
backdrop-filter: blur(16px);
|
||||
}
|
||||
|
||||
.wolai-topbar-left,
|
||||
.wolai-topbar-actions {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.wolai-breadcrumb {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 9px;
|
||||
min-width: 0;
|
||||
color: #6D6A65;
|
||||
}
|
||||
|
||||
.wolai-breadcrumb-root {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.wolai-breadcrumb-separator {
|
||||
color: #D0CDC8;
|
||||
}
|
||||
|
||||
.wolai-breadcrumb-current {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
min-width: 0;
|
||||
color: var(--atelier-text);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.wolai-home-icon {
|
||||
color: #111111;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.wolai-icon-button {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 0;
|
||||
border-radius: 4px;
|
||||
background: transparent;
|
||||
color: #5A5A5A;
|
||||
font: inherit;
|
||||
font-size: 18px;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.wolai-icon-button:hover {
|
||||
background: #F4F3F3;
|
||||
color: var(--atelier-text);
|
||||
}
|
||||
|
||||
.wolai-menu-button {
|
||||
color: #3D3B37;
|
||||
font-size: 21px;
|
||||
}
|
||||
|
||||
.wolai-public-pill {
|
||||
height: 26px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding: 0 11px;
|
||||
border: 1px solid #D6F2DC;
|
||||
border-radius: 999px;
|
||||
background: #ECFCEF;
|
||||
color: #087B31;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wolai-public-pill span {
|
||||
color: var(--atelier-primary-container);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.wolai-ai-inline {
|
||||
color: var(--atelier-primary-container);
|
||||
}
|
||||
|
||||
.document-shell {
|
||||
max-width: 880px;
|
||||
min-height: calc(100vh - 44px);
|
||||
padding: 78px 48px 160px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.document-shell-header {
|
||||
max-width: none;
|
||||
padding: 0;
|
||||
margin: 0 0 28px;
|
||||
}
|
||||
|
||||
.document-page-icon {
|
||||
height: 82px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #111111;
|
||||
font-size: 72px;
|
||||
line-height: 1;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.document-shell-header h1 {
|
||||
padding: 0;
|
||||
margin: 0 0 14px;
|
||||
color: var(--atelier-text);
|
||||
font-size: 42px;
|
||||
font-weight: 700;
|
||||
line-height: 1.12;
|
||||
letter-spacing: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.document-shell-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 18px;
|
||||
padding-bottom: 28px;
|
||||
border-bottom: 1px solid #F0EFED;
|
||||
color: #A19D97;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.document-shell-meta span {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
#mnote-editor-island {
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
min-height: 360px;
|
||||
margin: 0;
|
||||
padding: 0 0 48px;
|
||||
}
|
||||
|
||||
#mnote-leptos-tiptap-island-editor-root {
|
||||
min-height: 320px;
|
||||
}
|
||||
|
||||
#mnote-leptos-tiptap-island-editor-root .editor-surface {
|
||||
border: 0 !important;
|
||||
box-shadow: none !important;
|
||||
background: transparent !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
#mnote-leptos-tiptap-island-editor-root .ProseMirror,
|
||||
.ProseMirror {
|
||||
color: var(--atelier-text);
|
||||
font-size: 16px;
|
||||
line-height: 1.65;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#mnote-leptos-tiptap-island-editor-root .ProseMirror {
|
||||
width: 100% !important;
|
||||
min-height: 280px;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
#mnote-leptos-tiptap-island-editor-root .ProseMirror p {
|
||||
margin: 0 0 24px;
|
||||
}
|
||||
|
||||
#mnote-leptos-tiptap-island-editor-root .ProseMirror h1,
|
||||
#mnote-leptos-tiptap-island-editor-root .ProseMirror h2,
|
||||
#mnote-leptos-tiptap-island-editor-root .ProseMirror h3 {
|
||||
line-height: 1.2;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.mnote-workspace-active-state {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.mnote-workspace-active-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
min-height: 30px;
|
||||
color: var(--atelier-text);
|
||||
font-size: 15px;
|
||||
text-decoration: underline;
|
||||
text-decoration-color: #D7D4CE;
|
||||
text-underline-offset: 5px;
|
||||
}
|
||||
|
||||
.mnote-workspace-active-link::before {
|
||||
content: "▤";
|
||||
color: #A19D97;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.wolai-floating-actions {
|
||||
right: 24px;
|
||||
bottom: 24px;
|
||||
gap: 10px;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
.wolai-floating-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 999px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.wolai-floating-button--help {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 1px solid rgba(27, 28, 28, 0.1);
|
||||
background: rgba(255, 255, 255, 0.86);
|
||||
color: #5A5A5A;
|
||||
box-shadow: 0 10px 24px rgba(27, 28, 28, 0.08);
|
||||
backdrop-filter: blur(16px);
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.wolai-floating-button--ai {
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
border: 0;
|
||||
background: linear-gradient(135deg, var(--atelier-primary), var(--atelier-primary-container));
|
||||
color: #FFFFFF;
|
||||
box-shadow: 0 16px 32px rgba(0, 110, 40, 0.22);
|
||||
font-size: 23px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.mnote-sidebar,
|
||||
.wolai-sidebar {
|
||||
width: 220px;
|
||||
}
|
||||
|
||||
.wolai-topbar {
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.wolai-public-pill,
|
||||
.wolai-breadcrumb-root,
|
||||
.wolai-breadcrumb-separator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.document-shell {
|
||||
padding: 48px 28px 120px;
|
||||
}
|
||||
|
||||
.document-page-icon {
|
||||
height: 60px;
|
||||
font-size: 54px;
|
||||
}
|
||||
|
||||
.document-shell-header h1 {
|
||||
font-size: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.mnote-shell {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.mnote-sidebar,
|
||||
.wolai-sidebar {
|
||||
width: 100%;
|
||||
max-height: 42vh;
|
||||
border-bottom: 1px solid rgba(27, 28, 28, 0.08);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.document-shell {
|
||||
padding: 36px 20px 112px;
|
||||
}
|
||||
|
||||
.document-shell-header h1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.wolai-topbar-actions {
|
||||
gap: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
"##;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -919,6 +1618,6 @@ mod tests {
|
||||
// 至少 2000 字符才能包含完整样式
|
||||
assert!(MNOTE_CSS.len() > 2000);
|
||||
// 最多 20000 字符避免过于臃肿
|
||||
assert!(MNOTE_CSS.len() < 20000);
|
||||
assert!(MNOTE_CSS.len() < 32000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,13 +102,13 @@ pub fn build_workspace_shell_projection(
|
||||
id: "trash".into(),
|
||||
label: "垃圾箱".into(),
|
||||
href: "/trash".into(),
|
||||
icon: "🗑".into(),
|
||||
icon: "⌫".into(),
|
||||
},
|
||||
WorkspaceShellEntry {
|
||||
id: "templates".into(),
|
||||
label: "模板中心".into(),
|
||||
href: "/templates".into(),
|
||||
icon: "📦".into(),
|
||||
icon: "◇".into(),
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -323,6 +323,35 @@ mod tests {
|
||||
assert!(html.contains("data-mnote-action=\"create-page\""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn workspace_shell_sidebar_html_uses_single_tabbed_tree_host() {
|
||||
let dataset = json!({
|
||||
"workspaces": [{ "id": "ws_demo", "name": "开发用户 的工作区" }],
|
||||
"documents": [
|
||||
{ "id": "doc_root", "workspace_id": "ws_demo", "title": "Root", "parent_id": null, "sort_order": 0 }
|
||||
]
|
||||
});
|
||||
let projection = build_workspace_shell_projection(
|
||||
&dataset,
|
||||
"ws_demo",
|
||||
Some("doc_root"),
|
||||
"开发用户 的工作区",
|
||||
);
|
||||
let html = render_workspace_shell_sidebar_html(
|
||||
&projection,
|
||||
Some(r#"<ul data-rust-page-renderer="initial_v1"></ul>"#),
|
||||
Some(r#"<ul data-rust-filetree-renderer="initial_v1"></ul>"#),
|
||||
);
|
||||
|
||||
assert!(html.contains(r#"data-mnote-sidebar-tree-tab="page""#));
|
||||
assert!(html.contains(r#"data-mnote-sidebar-tree-tab="filetree""#));
|
||||
assert!(html.contains(r#"data-mnote-sidebar-tree-panel="page""#));
|
||||
assert!(html.contains(r#"data-mnote-sidebar-tree-panel="filetree" hidden"#));
|
||||
assert!(html.contains(r#"id="sidebar-tree-root""#));
|
||||
assert!(html.contains(r#"id="sidebar-file-tree-root""#));
|
||||
assert!(!html.contains("wolai-file-tree-section"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn workspace_shell_sidebar_html_outputs_empty_state() {
|
||||
let dataset = json!({
|
||||
@@ -368,16 +397,18 @@ pub fn render_workspace_shell_sidebar_html(
|
||||
)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let file_tree_panel = file_tree_html
|
||||
let file_tree_content = file_tree_html
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(|html| {
|
||||
format!(
|
||||
r#"<section class="wolai-sidebar-section wolai-file-tree-section" aria-label="文件树" data-testid="wolai-sidebar-file-tree-section"><div class="wolai-section-title"><span>文件树</span><span class="wolai-section-caret">⌄</span></div><div class="sidebar-tree-section sidebar-file-tree-section"><div id="sidebar-file-tree-root" class="sidebar-tree" data-tree-shell-mode="filetree" data-workspace-id="{}">{html}</div></div></section>"#,
|
||||
r#"<div class="sidebar-tree-section sidebar-file-tree-section"><div id="sidebar-file-tree-root" class="sidebar-tree" data-tree-shell-mode="filetree" data-workspace-id="{}">{html}</div></div>"#,
|
||||
escape_html(&projection.workspace_id),
|
||||
)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
.unwrap_or_else(|| {
|
||||
r#"<div class="wolai-sidebar-empty" data-testid="wolai-sidebar-filetree-empty-state">暂无文件或页面</div>"#.to_string()
|
||||
});
|
||||
let empty_state = r#"<div class="wolai-sidebar-empty" data-testid="wolai-sidebar-empty-state">暂无页面</div>"#;
|
||||
let my_pages = if !tree_html.is_empty() {
|
||||
tree_html
|
||||
@@ -401,7 +432,7 @@ pub fn render_workspace_shell_sidebar_html(
|
||||
.join("");
|
||||
|
||||
format!(
|
||||
r#"<section class="wolai-sidebar-section wolai-starred-section" aria-label="星标置顶"><div class="wolai-section-title"><span class="wolai-section-icon">★</span>星标置顶<span class="wolai-section-caret">⌄</span></div>{starred_rows}</section><section class="wolai-sidebar-section wolai-my-pages-section" aria-label="页面树" data-testid="wolai-sidebar-page-tree-shell"><div class="wolai-section-title"><span>我的页面</span><span class="wolai-section-caret">⌄</span><button type="button" class="wolai-section-add" data-testid="wolai-sidebar-create-page" data-mnote-action="create-page" data-workspace-id="{}" title="新建页面" aria-label="新建页面">+</button></div>{my_pages}</section>{file_tree_panel}<div class="wolai-sidebar-footer">{bottom_entries}</div>"#,
|
||||
r#"<section class="wolai-sidebar-section wolai-starred-section" aria-label="星标置顶"><div class="wolai-section-title"><span class="wolai-section-icon">★</span>星标置顶<span class="wolai-section-caret">⌄</span></div>{starred_rows}</section><section class="wolai-sidebar-section wolai-my-pages-section" aria-label="页面树" data-testid="wolai-sidebar-page-tree-shell"><div class="wolai-sidebar-tabs" data-testid="wolai-sidebar-tree-tabs" role="tablist" aria-label="页面树和文件树"><button type="button" class="wolai-sidebar-tab wolai-sidebar-tab-active" data-mnote-sidebar-tree-tab="page" aria-selected="true" aria-controls="wolai-sidebar-page-tree-panel">我的页面</button><button type="button" class="wolai-sidebar-tab wolai-sidebar-tab-muted" data-mnote-sidebar-tree-tab="filetree" aria-selected="false" aria-controls="wolai-sidebar-file-tree-panel"><span class="wolai-folder-icon">▱</span>Explorer</button><span class="wolai-section-caret">⌄</span><button type="button" class="wolai-section-add" data-testid="wolai-sidebar-create-page" data-mnote-action="create-page" data-workspace-id="{}" title="新建页面" aria-label="新建页面">+</button></div><div class="wolai-sidebar-tab-panels"><div id="wolai-sidebar-page-tree-panel" class="wolai-sidebar-tab-panel" data-mnote-sidebar-tree-panel="page">{my_pages}</div><div id="wolai-sidebar-file-tree-panel" class="wolai-sidebar-tab-panel" data-mnote-sidebar-tree-panel="filetree" hidden>{file_tree_content}</div></div></section><div class="wolai-sidebar-footer">{bottom_entries}</div>"#,
|
||||
escape_html(&projection.workspace_id),
|
||||
)
|
||||
}
|
||||
@@ -411,7 +442,7 @@ fn render_item_row(item: &WorkspaceShellItem) -> String {
|
||||
let depth_style = if item.depth == 0 {
|
||||
String::new()
|
||||
} else {
|
||||
format!(r#" style="padding-left:{}px""#, 12 + item.depth.min(6) * 18)
|
||||
format!(r#" style="padding-left:{}px""#, 12 + item.depth.min(6) * 12)
|
||||
};
|
||||
let parent_attr = item
|
||||
.parent_id
|
||||
@@ -424,7 +455,7 @@ fn render_item_row(item: &WorkspaceShellItem) -> String {
|
||||
""
|
||||
};
|
||||
format!(
|
||||
r#"<a class="wolai-page-row{active_class}" href="{}" data-testid="wolai-sidebar-row" data-node-id="{}"{parent_attr} data-depth="{}" data-active="{}"{aria_current}{depth_style}><span class="wolai-row-icon">{}</span><span class="wolai-row-title">{}</span></a>"#,
|
||||
r#"<a class="wolai-page-row{active_class}" href="{}" data-testid="wolai-sidebar-row" data-node-id="{}"{parent_attr} data-depth="{}" data-active="{}"{aria_current}{depth_style}><span class="wolai-row-caret" aria-hidden="true">›</span><span class="wolai-row-icon">{}</span><span class="wolai-row-title">{}</span></a>"#,
|
||||
escape_html(&item.href),
|
||||
escape_html(&item.id),
|
||||
item.depth,
|
||||
|
||||
Reference in New Issue
Block a user