Agent task: 开发: Phase B1+D — radix 依赖 + 图标统一 (leptos-tiptap-spike)

This commit is contained in:
Agent Board
2026-06-16 01:27:21 +08:00
parent 1f483e3d7c
commit 428501ae70
9 changed files with 191 additions and 79 deletions
@@ -15,6 +15,7 @@ path = "src/main.rs"
[dependencies]
console_error_panic_hook = "0.1.7"
radix-leptos-primitives = "0.9.0"
leptos = { version = "0.8.19", features = ["csr"] }
leptos_dom = "0.8.8"
serde = { version = "1", features = ["derive"] }
@@ -13,6 +13,7 @@ use crate::editor_runtime::{
dom_events::trap_scroll_inside_menu,
dom_selection::runtime_block_id_from_index,
editor_focus::schedule_editor_focus,
icons::material_icon,
persistence::{persist_document_state, persisted_document_identity, PersistedDocumentIdentity},
slash_actions::{SlashActionKind, SLASH_ACTIONS},
};
@@ -34,25 +35,25 @@ const FOLDED_HEADING_ACTIONS: [FoldedHeadingAction; 4] = [
FoldedHeadingAction {
level: 1,
id: "1",
icon: "H1",
icon: "looks_one",
label: "折叠主标题",
},
FoldedHeadingAction {
level: 2,
id: "2",
icon: "H2",
icon: "looks_two",
label: "折叠大标题",
},
FoldedHeadingAction {
level: 3,
id: "3",
icon: "H3",
icon: "looks_3",
label: "折叠中标题",
},
FoldedHeadingAction {
level: 4,
id: "4",
icon: "H4",
icon: "looks_4",
label: "折叠小标题",
},
];
@@ -244,7 +245,7 @@ fn block_transform_submenu_view(
handle_turn_into_action_click(editor, block_index, kind, label, signals);
}
>
<span class="block-drag-menu-icon">{icon}</span>
<span class="block-drag-menu-icon material-symbols-outlined">{icon}</span>
<span>{label}</span>
<span class="block-drag-menu-shortcut">{shortcut}</span>
</button>
@@ -274,12 +275,12 @@ fn block_transform_submenu_view(
);
}
>
<span class="block-drag-menu-icon">""</span>
<span class="block-drag-menu-icon">{material_icon("description")}</span>
<span>"页面"</span>
<span class="block-drag-menu-shortcut">{page_current_marker}</span>
</button>
<button class="block-drag-menu-item" data-testid="block-transform-item-folded-list" disabled=true>
<span class="block-drag-menu-icon">""</span>
<span class="block-drag-menu-icon">{material_icon("format_list_bulleted")}</span>
<span>"折叠列表"</span>
<span class="block-drag-menu-shortcut">"Ctrl+Shift+8"</span>
</button>
@@ -294,21 +295,21 @@ fn block_transform_submenu_view(
signals.set_block_folded_title_open.set(true);
}
>
<span class="block-drag-menu-icon">"H1"</span>
<span class="block-drag-menu-icon">{material_icon("looks_one")}</span>
<span>"折叠标题"</span>
<span class="block-drag-menu-arrow">""</span>
<span class="block-drag-menu-arrow">{material_icon("chevron_right")}</span>
</button>
<button class="block-drag-menu-item" data-testid="block-transform-item-folded-todo" disabled=true>
<span class="block-drag-menu-icon">""</span>
<span class="block-drag-menu-icon">{material_icon("checklist")}</span>
<span>"折叠待办"</span>
<span class="block-drag-menu-arrow">""</span>
<span class="block-drag-menu-arrow">{material_icon("chevron_right")}</span>
</button>
<button class="block-drag-menu-item" data-testid="block-transform-item-emphasis" disabled=true>
<span class="block-drag-menu-icon">"Aa"</span>
<span class="block-drag-menu-icon">{material_icon("text_fields")}</span>
<span>"着重文字"</span>
</button>
<button class="block-drag-menu-item" data-testid="block-transform-item-math" disabled=true>
<span class="block-drag-menu-icon">"f(x)"</span>
<span class="block-drag-menu-icon">{material_icon("function")}</span>
<span>"数学公式"</span>
<span class="block-drag-menu-shortcut">"Ctrl+Alt++"</span>
</button>
@@ -330,7 +331,7 @@ fn block_transform_submenu_view(
handle_folded_heading_click(editor, block_index, action, signals);
}
>
<span class="block-drag-menu-icon">{action.icon}</span>
<span class="block-drag-menu-icon material-symbols-outlined">{action.icon}</span>
<span>{action.label}</span>
</button>
}
@@ -415,7 +416,7 @@ pub(crate) fn block_handle_menu_view(props: BlockHandleMenuViewProps) -> impl In
);
}
>
<span class="block-drag-menu-icon">""</span>
<span class="block-drag-menu-icon">{material_icon("auto_awesome")}</span>
<span>"AI 助理"</span>
<span class="block-drag-menu-shortcut">"Ctrl+J"</span>
</button>
@@ -430,9 +431,9 @@ pub(crate) fn block_handle_menu_view(props: BlockHandleMenuViewProps) -> impl In
signals.set_block_turn_into_open.set(true);
}
>
<span class="block-drag-menu-icon">""</span>
<span class="block-drag-menu-icon">{material_icon("autorenew")}</span>
<span>"转换为"</span>
<span class="block-drag-menu-arrow">""</span>
<span class="block-drag-menu-arrow">{material_icon("chevron_right")}</span>
</button>
</div>
<div class="block-drag-menu-divider"></div>
@@ -462,7 +463,7 @@ pub(crate) fn block_handle_menu_view(props: BlockHandleMenuViewProps) -> impl In
}
}
>
<span class="block-drag-menu-icon">""</span>
<span class="block-drag-menu-icon">{material_icon("content_copy")}</span>
<span>"拷贝副本"</span>
<span class="block-drag-menu-shortcut">"Ctrl+D"</span>
</button>
@@ -497,7 +498,7 @@ pub(crate) fn block_handle_menu_view(props: BlockHandleMenuViewProps) -> impl In
}
}
>
<span class="block-drag-menu-icon">""</span>
<span class="block-drag-menu-icon">{material_icon("delete")}</span>
<span>"删除"</span>
<span class="block-drag-menu-shortcut">"Del"</span>
</button>
@@ -538,9 +539,9 @@ pub(crate) fn block_handle_menu_view(props: BlockHandleMenuViewProps) -> impl In
});
}
>
<span class="block-drag-menu-icon">"🔗"</span>
<span class="block-drag-menu-icon">{material_icon("link")}</span>
<span>"复制链接"</span>
<span class="block-drag-menu-arrow">""</span>
<span class="block-drag-menu-arrow">{material_icon("chevron_right")}</span>
</button>
<button
class="block-drag-menu-item"
@@ -552,7 +553,7 @@ pub(crate) fn block_handle_menu_view(props: BlockHandleMenuViewProps) -> impl In
.set("移动/嵌入流程后续接 Page Aggregate 命令".to_string());
}
>
<span class="block-drag-menu-icon">""</span>
<span class="block-drag-menu-icon">{material_icon("arrow_outward")}</span>
<span>"移动/嵌入到..."</span>
<span class="block-drag-menu-shortcut">"Alt+Shift+M/G"</span>
</button>
@@ -566,7 +567,7 @@ pub(crate) fn block_handle_menu_view(props: BlockHandleMenuViewProps) -> impl In
.set("块历史入口已记录,恢复流程后续接版本链路".to_string());
}
>
<span class="block-drag-menu-icon">""</span>
<span class="block-drag-menu-icon">{material_icon("history")}</span>
<span>"块历史..."</span>
</button>
<button
@@ -577,7 +578,7 @@ pub(crate) fn block_handle_menu_view(props: BlockHandleMenuViewProps) -> impl In
signals.set_command_feedback.set("评论入口已打开".to_string());
}
>
<span class="block-drag-menu-icon">""</span>
<span class="block-drag-menu-icon">{material_icon("chat")}</span>
<span>"评论"</span>
<span class="block-drag-menu-shortcut">"Ctrl+Alt+M"</span>
</button>
@@ -596,9 +597,9 @@ pub(crate) fn block_handle_menu_view(props: BlockHandleMenuViewProps) -> impl In
.set("颜色子菜单后续按 Wolai 继续补齐".to_string());
}
>
<span class="block-drag-menu-icon">""</span>
<span class="block-drag-menu-icon">{material_icon("palette")}</span>
<span>"颜色"</span>
<span class="block-drag-menu-arrow">""</span>
<span class="block-drag-menu-arrow">{material_icon("chevron_right")}</span>
</button>
<button
class="block-drag-menu-item"
@@ -637,17 +638,17 @@ pub(crate) fn block_handle_menu_view(props: BlockHandleMenuViewProps) -> impl In
}
}
>
<span class="block-drag-menu-icon">""</span>
<span class="block-drag-menu-icon">{material_icon("format_align_center")}</span>
<span>"文字居中"</span>
<span class="block-drag-menu-switch"></span>
</button>
<button class="block-drag-menu-item" data-testid="block-drag-menu-item-translate" disabled=true>
<span class="block-drag-menu-icon">""</span>
<span class="block-drag-menu-icon">{material_icon("translate")}</span>
<span>"文字翻译"</span>
<span class="block-drag-menu-arrow">""</span>
<span class="block-drag-menu-arrow">{material_icon("chevron_right")}</span>
</button>
<button class="block-drag-menu-item" data-testid="block-drag-menu-item-poster" disabled=true>
<span class="block-drag-menu-icon">""</span>
<span class="block-drag-menu-icon">{material_icon("dashboard")}</span>
<span>"生成海报"</span>
</button>
</div>
@@ -0,0 +1,83 @@
//! Unified icon helpers — Material Symbols ligatures for all editor runtime icons.
//!
//! Every icon rendered in the leptos-tiptap editor shell should flow through
//! `material_icon()` or consume `ICON_MAP` so that the set of Material Symbols
//! ligatures stays centralised and we can swap font providers later.
use leptos::prelude::*;
/// Semantic name → Material Symbols ligature.
///
/// Keys are stable semantic identifiers; values are the literal text content
/// that `material-symbols-outlined` will turn into a glyph via ligature.
pub(crate) const ICON_MAP: &[(&str, &str)] = &[
("heading_1", "looks_one"),
("heading_2", "looks_two"),
("heading_3", "looks_3"),
("heading_4", "looks_4"),
("chevron_right", "chevron_right"),
("chevron_left", "chevron_left"),
("check", "check"),
("text_fields", "text_fields"),
("auto_awesome", "auto_awesome"),
("edit", "edit"),
("redo", "redo"),
("summarize", "auto_awesome"),
("more_horiz", "more_horiz"),
("checklist", "checklist"),
("format_list_bulleted", "format_list_bulleted"),
("format_list_numbered", "format_list_numbered"),
("format_quote", "format_quote"),
("code", "code"),
("horizontal_rule", "horizontal_rule"),
("table_chart", "table_chart"),
("account_tree", "account_tree"),
("image", "image"),
("upload_file", "upload_file"),
("toc", "toc"),
("playlist_add", "playlist_add"),
("view_column", "view_column"),
("delete", "delete"),
("backspace", "backspace"),
("close", "close"),
("settings", "settings"),
("remove", "remove"),
("fullscreen_exit", "fullscreen_exit"),
("fullscreen", "fullscreen"),
("lock", "lock"),
("lock_open", "lock_open"),
("autorenew", "autorenew"),
("link", "link"),
("arrow_outward", "arrow_outward"),
("history", "history"),
("chat", "chat"),
("palette", "palette"),
("format_align_center", "format_align_center"),
("translate", "translate"),
("function", "function"),
("description", "description"),
("content_copy", "content_copy"),
("format_bold", "format_bold"),
("dashboard", "dashboard"),
("home", "home"),
("search", "search"),
("add", "add"),
];
/// Look up a Material Symbols ligature by semantic name.
pub(crate) fn icon_ligature(name: &str) -> Option<&'static str> {
ICON_MAP
.iter()
.find(|(key, _)| *key == name)
.map(|(_, value)| *value)
}
/// Render a `<span class="material-symbols-outlined">` icon.
///
/// The `name` parameter must be a Material Symbols ligature name
/// (e.g. `"auto_awesome"`, `"chevron_right"`, `"looks_one"`).
pub(crate) fn material_icon(name: &str) -> impl IntoView {
view! {
<span class="material-symbols-outlined" aria-hidden="true">{name}</span>
}
}
@@ -21,6 +21,7 @@ use wasm_bindgen::{JsCast, JsValue};
use web_sys::{CustomEvent, CustomEventInit, EventTarget, HtmlElement, HtmlInputElement};
use crate::editor_runtime::mount::next_mount_id;
use crate::editor_runtime::icons::material_icon;
thread_local! {
static MOUNTED_MINDMAP_SHELLS: std::cell::RefCell<HashMap<u32, MountedMindmapShell>> = std::cell::RefCell::new(HashMap::new());
@@ -493,7 +494,7 @@ pub(crate) fn MindmapShell(
);
}
>
<span class="mnote-mindmap-tool-icon" aria-hidden="true">""</span>
<span class="mnote-mindmap-tool-icon" aria-hidden="true">{material_icon("more_horiz")}</span>
<span class="mnote-mindmap-tool-label">"更多"</span>
</button>
{if toolbar_more_open {
@@ -557,7 +558,7 @@ pub(crate) fn MindmapShell(
}
}
>
""
{material_icon("chevron_right")}
</button>
{sidebar_panels
.iter()
@@ -616,7 +617,7 @@ pub(crate) fn MindmapShell(
}
}
>
""
{material_icon("chevron_left")}
</button>
}
.into_any()
@@ -660,7 +661,7 @@ pub(crate) fn MindmapShell(
}
}
>
"×"
{material_icon("close")}
</button>
</div>
<div class="mnote-mindmap-side-options" data-testid=format!("mindmap-schema-sidebar-options-{}", panel.id)>
@@ -711,7 +712,7 @@ pub(crate) fn MindmapShell(
compat_path: None,
source: None,
})
}><span class="material-symbols-outlined" data-icon="home" aria-hidden="true"></span></button>
}><span class="mnote-mindmap-tool-icon" aria-hidden="true">{material_icon("home")}</span></button>
<div class="mnote-mindmap-schema-navigator-search-wrap">
<button type="button" class="mnote-mindmap-schema-navigator-button" data-testid="mindmap-schema-navigator-action-search" data-mindmap-action-id="search" data-active=search_open.to_string() title="搜索" aria-label="搜索" on:click={
let action_target = action_target.clone();
@@ -721,7 +722,7 @@ pub(crate) fn MindmapShell(
compat_path: None,
source: None,
})
}><span class="material-symbols-outlined" data-icon="search" aria-hidden="true"></span></button>
}><span class="mnote-mindmap-tool-icon" aria-hidden="true">{material_icon("search")}</span></button>
{if search_open {
view! {
<input
@@ -765,7 +766,7 @@ pub(crate) fn MindmapShell(
<button type="button" class="mnote-mindmap-schema-navigator-button" data-testid="mindmap-schema-navigator-action-minimap" data-active=minimap_open.to_string() title="小地图" aria-label="小地图" on:click={
let action_target = action_target.clone();
move |_| dispatch_mindmap_shell_event(&action_target, MINDMAP_SHELL_MINIMAP_EVENT, &MindmapShellMinimapPayload { open: !minimap_open })
}><span class="material-symbols-outlined" data-icon="account_tree" aria-hidden="true"></span></button>
}><span class="mnote-mindmap-tool-icon" aria-hidden="true">{material_icon("account_tree")}</span></button>
<button type="button" class="mnote-mindmap-schema-navigator-button" data-testid="mindmap-schema-navigator-action-settings" data-active=(sidebar_panel_open && sidebar_trigger_visible).to_string() title="设置" aria-label="设置" on:click={
let action_target = action_target.clone();
let active_panel_id = active_panel_id.clone();
@@ -776,7 +777,7 @@ pub(crate) fn MindmapShell(
panel_id: active_panel_id.as_deref(),
})
}
}><span class="mnote-mindmap-tool-icon" aria-hidden="true">""</span></button>
}><span class="mnote-mindmap-tool-icon" aria-hidden="true">{material_icon("settings")}</span></button>
</div>
<div class="mnote-mindmap-schema-navigator-group">
<button type="button" class="mnote-mindmap-schema-navigator-button" data-testid="mindmap-schema-navigator-action-zoomOut" data-mindmap-action-id="zoomOut" title="缩小" aria-label="缩小" on:click={
@@ -787,7 +788,7 @@ pub(crate) fn MindmapShell(
compat_path: None,
source: None,
})
}><span class="mnote-mindmap-tool-icon" aria-hidden="true">""</span></button>
}><span class="mnote-mindmap-tool-icon" aria-hidden="true">{material_icon("remove")}</span></button>
<input
class="mnote-mindmap-schema-navigator-zoom"
data-testid="mindmap-schema-navigator-zoom-input"
@@ -826,7 +827,7 @@ pub(crate) fn MindmapShell(
compat_path: None,
source: None,
})
}><span class="material-symbols-outlined" data-icon="add" aria-hidden="true"></span></button>
}><span class="mnote-mindmap-tool-icon" aria-hidden="true">{material_icon("add")}</span></button>
<button type="button" class="mnote-mindmap-schema-navigator-button" data-testid="mindmap-schema-navigator-action-fullscreenCanvas" data-mindmap-action-id="fullscreenCanvas" data-active=shell.fullscreen.is_fullscreen.to_string() data-disabled-reason=if shell.fullscreen.api_available { "" } else { "fullscreen-unavailable" } disabled=!shell.fullscreen.api_available title=if shell.fullscreen.is_fullscreen { "退出全屏" } else { "全屏查看" } aria-label=if shell.fullscreen.is_fullscreen { "退出全屏" } else { "全屏查看" } on:click={
let action_target = action_target.clone();
move |_| dispatch_mindmap_shell_event(&action_target, MINDMAP_SHELL_ACTION_EVENT, &MindmapShellActionPayload {
@@ -835,7 +836,7 @@ pub(crate) fn MindmapShell(
compat_path: None,
source: None,
})
}><span class="mnote-mindmap-tool-icon" aria-hidden="true">{if shell.fullscreen.is_fullscreen { "" } else { "" }}</span></button>
}><span class="mnote-mindmap-tool-icon" aria-hidden="true">{if shell.fullscreen.is_fullscreen { material_icon("fullscreen_exit") } else { material_icon("fullscreen") }}</span></button>
<button type="button" class="mnote-mindmap-schema-navigator-button" data-testid="mindmap-schema-navigator-action-readonly" data-mindmap-action-id="readonly" data-active=navigator.readonly.to_string() title=if navigator.readonly { "切回编辑" } else { "切换只读" } aria-label=if navigator.readonly { "切回编辑" } else { "切换只读" } on:click={
let action_target = action_target;
move |_| dispatch_mindmap_shell_event(&action_target, MINDMAP_SHELL_ACTION_EVENT, &MindmapShellActionPayload {
@@ -844,7 +845,7 @@ pub(crate) fn MindmapShell(
compat_path: None,
source: None,
})
}><span class="mnote-mindmap-tool-icon" aria-hidden="true">{if navigator.readonly { "" } else { "" }}</span></button>
}><span class="mnote-mindmap-tool-icon" aria-hidden="true">{if navigator.readonly { material_icon("lock") } else { material_icon("lock_open") }}</span></button>
</div>
</div> }.into_any()
} else {
@@ -2,6 +2,7 @@ pub(crate) mod attachment_links;
pub(crate) mod attachment_upload;
pub(crate) mod block_dnd;
pub(crate) mod block_hover_state;
pub(crate) mod icons;
pub(crate) mod block_handle_menu_view;
pub(crate) mod block_menu_document;
pub(crate) mod block_menu_overlay;
@@ -40,7 +40,7 @@ pub(crate) const SLASH_ACTIONS: [SlashAction; 22] = [
kind: SlashActionKind::AiAssistant,
id: "ai-assistant",
category: "AI 助理",
icon: "",
icon: "auto_awesome",
label: "AI 助理",
description: "按 Wolai 基线保留 AI 入口",
shortcut: "/ai",
@@ -49,7 +49,7 @@ pub(crate) const SLASH_ACTIONS: [SlashAction; 22] = [
kind: SlashActionKind::AiWrite,
id: "ai-write",
category: "AI 助理",
icon: "",
icon: "edit",
label: "用 AI 写作",
description: "唤起写作辅助入口",
shortcut: "/yaixz",
@@ -58,7 +58,7 @@ pub(crate) const SLASH_ACTIONS: [SlashAction; 22] = [
kind: SlashActionKind::ContinueWriting,
id: "continue-writing",
category: "AI 助理",
icon: "",
icon: "redo",
label: "续写",
description: "按当前上下文继续写作",
shortcut: "/xx",
@@ -67,7 +67,7 @@ pub(crate) const SLASH_ACTIONS: [SlashAction; 22] = [
kind: SlashActionKind::Summarize,
id: "summarize",
category: "AI 助理",
icon: "",
icon: "auto_awesome",
label: "总结",
description: "对当前内容生成摘要",
shortcut: "/zj",
@@ -76,7 +76,7 @@ pub(crate) const SLASH_ACTIONS: [SlashAction; 22] = [
kind: SlashActionKind::MoreAi,
id: "more-ai",
category: "AI 助理",
icon: "",
icon: "more_horiz",
label: "更多",
description: "更多 AI 命令",
shortcut: "",
@@ -85,7 +85,7 @@ pub(crate) const SLASH_ACTIONS: [SlashAction; 22] = [
kind: SlashActionKind::Paragraph,
id: "paragraph",
category: "基础块列表",
icon: "Aa",
icon: "text_fields",
label: "文本",
description: "普通正文块",
shortcut: "/wb",
@@ -94,7 +94,7 @@ pub(crate) const SLASH_ACTIONS: [SlashAction; 22] = [
kind: SlashActionKind::Todo,
id: "todo",
category: "基础块列表",
icon: "",
icon: "checklist",
label: "待办列表",
description: "创建可勾选任务",
shortcut: "/dblb",
@@ -103,7 +103,7 @@ pub(crate) const SLASH_ACTIONS: [SlashAction; 22] = [
kind: SlashActionKind::AdvancedTodo,
id: "advanced-todo",
category: "基础块列表",
icon: "",
icon: "checklist",
label: "高级待办列表",
description: "保留 Wolai 高级待办入口",
shortcut: "/gjdblb",
@@ -112,7 +112,7 @@ pub(crate) const SLASH_ACTIONS: [SlashAction; 22] = [
kind: SlashActionKind::Heading1,
id: "heading-1",
category: "基础块列表",
icon: "H1",
icon: "looks_one",
label: "主标题",
description: "一级标题",
shortcut: "/h1",
@@ -121,7 +121,7 @@ pub(crate) const SLASH_ACTIONS: [SlashAction; 22] = [
kind: SlashActionKind::Heading2,
id: "heading-2",
category: "基础块列表",
icon: "H2",
icon: "looks_two",
label: "大标题",
description: "二级标题",
shortcut: "/h2",
@@ -130,7 +130,7 @@ pub(crate) const SLASH_ACTIONS: [SlashAction; 22] = [
kind: SlashActionKind::Heading3,
id: "heading-3",
category: "基础块列表",
icon: "H3",
icon: "looks_3",
label: "中标题",
description: "三级标题",
shortcut: "/h3",
@@ -139,7 +139,7 @@ pub(crate) const SLASH_ACTIONS: [SlashAction; 22] = [
kind: SlashActionKind::Heading4,
id: "heading-4",
category: "基础块列表",
icon: "H4",
icon: "looks_4",
label: "小标题",
description: "四级标题",
shortcut: "/h4",
@@ -148,7 +148,7 @@ pub(crate) const SLASH_ACTIONS: [SlashAction; 22] = [
kind: SlashActionKind::BulletList,
id: "bullet",
category: "基础块列表",
icon: "",
icon: "format_list_bulleted",
label: "列表",
description: "记录普通要点",
shortcut: "/lb",
@@ -157,7 +157,7 @@ pub(crate) const SLASH_ACTIONS: [SlashAction; 22] = [
kind: SlashActionKind::OrderedList,
id: "ordered",
category: "基础块列表",
icon: "1.",
icon: "format_list_numbered",
label: "数字列表",
description: "记录步骤顺序",
shortcut: "/szlb",
@@ -166,7 +166,7 @@ pub(crate) const SLASH_ACTIONS: [SlashAction; 22] = [
kind: SlashActionKind::Quote,
id: "quote",
category: "基础块列表",
icon: "",
icon: "format_quote",
label: "引述文字",
description: "包住注释与摘录",
shortcut: "/ys",
@@ -175,7 +175,7 @@ pub(crate) const SLASH_ACTIONS: [SlashAction; 22] = [
kind: SlashActionKind::CodeBlock,
id: "code-block",
category: "基础块列表",
icon: "<> ",
icon: "code",
label: "代码片段",
description: "插入带语义的代码块",
shortcut: "/dm",
@@ -184,7 +184,7 @@ pub(crate) const SLASH_ACTIONS: [SlashAction; 22] = [
kind: SlashActionKind::Divider,
id: "divider",
category: "基础块列表",
icon: "",
icon: "horizontal_rule",
label: "分割线",
description: "插入分隔线",
shortcut: "/fgx",
@@ -193,7 +193,7 @@ pub(crate) const SLASH_ACTIONS: [SlashAction; 22] = [
kind: SlashActionKind::SimpleTable,
id: "simple-table",
category: "进阶块列表",
icon: "",
icon: "table_chart",
label: "简单表格",
description: "插入基础表格",
shortcut: "/jdbg",
@@ -202,7 +202,7 @@ pub(crate) const SLASH_ACTIONS: [SlashAction; 22] = [
kind: SlashActionKind::Mindmap,
id: "mindmap",
category: "进阶块列表",
icon: "",
icon: "account_tree",
label: "思维导图",
description: "插入 kernel projection 导图块",
shortcut: "/dt",
@@ -211,7 +211,7 @@ pub(crate) const SLASH_ACTIONS: [SlashAction; 22] = [
kind: SlashActionKind::Image,
id: "image",
category: "媒体与附件",
icon: "",
icon: "image",
label: "图片",
description: "上传并插入图片",
shortcut: "/tp",
@@ -220,7 +220,7 @@ pub(crate) const SLASH_ACTIONS: [SlashAction; 22] = [
kind: SlashActionKind::UploadAttachment,
id: "upload-attachment",
category: "媒体与附件",
icon: "",
icon: "upload_file",
label: "上传附件",
description: "上传 Office、PDF 或其他文件",
shortcut: "/fj",
@@ -229,7 +229,7 @@ pub(crate) const SLASH_ACTIONS: [SlashAction; 22] = [
kind: SlashActionKind::Toc,
id: "toc",
category: "进阶块列表",
icon: "",
icon: "toc",
label: "页面目录",
description: "根据当前标题生成目录",
shortcut: "/toc",
@@ -3,6 +3,7 @@ use crate::editor_runtime::{
block_transform::run_slash_action,
bridge_events::{ChangeMetaPayload, ChangePayload},
command_sync::read_editor_snapshot,
icons::material_icon,
overlays::toolbar_overlay_locked,
persistence::{persist_document_state, persisted_document_identity, PersistedDocumentIdentity},
runtime_bridge::dispatch_change_event_to_target,
@@ -59,6 +60,29 @@ fn runtime_persisted_identity(
persisted_document_identity(document_id.get_untracked(), workspace_id.get_untracked())
}
fn render_slash_shortcut(shortcut: &str) -> impl IntoView {
// Hardcoded Unicode→Material Symbols mappings for shortcuts that use
// non-text glyphs (chevron, arrows, triangles, etc.).
match shortcut {
"" => view! {
<span class="slash-item-shortcut">{material_icon("chevron_right")}</span>
}
.into_any(),
"" => view! {
<span class="slash-item-shortcut">{material_icon("autorenew")}</span>
}
.into_any(),
"" => view! {
<span class="slash-item-shortcut">{material_icon("arrow_drop_down")}</span>
}
.into_any(),
_ => view! {
<span class="slash-item-shortcut">{shortcut}</span>
}
.into_any(),
}
}
fn filtered_slash_intro_view(
query: &str,
command_feedback: WriteSignal<String>,
@@ -83,12 +107,12 @@ fn filtered_slash_intro_view(
}
>
<span class="slash-item-row">
<span class="slash-item-icon">""</span>
<span class="slash-item-icon">{material_icon("autorenew")}</span>
<span class="slash-item-copy">
<strong>"转换为"</strong>
<span>"打开块类型转换入口"</span>
</span>
<span class="slash-item-shortcut">"/zhw"</span>
<span class="slash-item-shortcut">{render_slash_shortcut("/zhw")}</span>
</span>
</button>
</>
@@ -109,12 +133,12 @@ fn filtered_slash_intro_view(
}
>
<span class="slash-item-row">
<span class="slash-item-icon">""</span>
<span class="slash-item-icon">{material_icon("description")}</span>
<span class="slash-item-copy">
<strong>"页面"</strong>
<span>"创建或转换为页面块"</span>
</span>
<span class="slash-item-shortcut">"/ym"</span>
<span class="slash-item-shortcut">{render_slash_shortcut("/ym")}</span>
</span>
</button>
</>
@@ -255,12 +279,12 @@ pub(crate) fn slash_menu_view(props: SlashMenuViewProps) -> impl IntoView {
}
>
<span class="slash-item-row">
<span class="slash-item-icon">{icon}</span>
<span class="slash-item-icon material-symbols-outlined">{icon}</span>
<span class="slash-item-copy">
<strong>{label}</strong>
<span>{description}</span>
</span>
<span class="slash-item-shortcut">{shortcut}</span>
<span class="slash-item-shortcut">{render_slash_shortcut(shortcut)}</span>
</span>
</button>
</>
@@ -32,12 +32,12 @@ impl TableToolbarAction {
pub(crate) fn icon(self) -> &'static str {
match self {
Self::AddRowAfter => "+R",
Self::AddColumnAfter => "+C",
Self::DeleteRow => "-R",
Self::DeleteColumn => "-C",
Self::ClearCell => "",
Self::DeleteTable => "×",
Self::AddRowAfter => "playlist_add",
Self::AddColumnAfter => "view_column",
Self::DeleteRow => "delete",
Self::DeleteColumn => "delete",
Self::ClearCell => "backspace",
Self::DeleteTable => "close",
}
}
@@ -1,5 +1,6 @@
use crate::editor_runtime::{
command_sync::sync_persisted_editor_command,
icons::material_icon,
overlays::{
current_table_overlay_anchor, table_col_aux_style, table_row_aux_style,
table_selection_overlay_style, TableOverlayAnchor, TableSelectionKind,
@@ -141,7 +142,7 @@ pub(crate) fn table_toolbar_view(
Err(err) => set_command_feedback.set(err),
}
}
>"+"</button>
>{material_icon("add")}</button>
}
}).collect_view()}
{(0..anchor.cols).map(|index| {
@@ -203,7 +204,7 @@ pub(crate) fn table_toolbar_view(
Err(err) => set_command_feedback.set(err),
}
}
>"+"</button>
>{material_icon("add")}</button>
}
}).collect_view()}
</div>
@@ -287,7 +288,7 @@ pub(crate) fn table_toolbar_view(
}
}
>
{action.icon()}
{material_icon(action.icon())}
</button>
</>
}