Merge branch 'group/d7066a76/1-phase-b1-d-radix-leptos-tiptap'
This commit is contained in:
@@ -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>
|
||||
</>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user