refactor: split editor mount and sidebar page ai
This commit is contained in:
@@ -20,7 +20,7 @@ use std::collections::HashMap;
|
||||
use wasm_bindgen::{JsCast, JsValue};
|
||||
use web_sys::{CustomEvent, CustomEventInit, EventTarget, HtmlElement, HtmlInputElement};
|
||||
|
||||
use crate::next_mount_id;
|
||||
use crate::editor_runtime::mount::next_mount_id;
|
||||
|
||||
thread_local! {
|
||||
static MOUNTED_MINDMAP_SHELLS: std::cell::RefCell<HashMap<u32, MountedMindmapShell>> = std::cell::RefCell::new(HashMap::new());
|
||||
|
||||
@@ -14,6 +14,7 @@ pub(crate) mod dom_selection;
|
||||
pub(crate) mod editor_focus;
|
||||
pub(crate) mod history_safe_commands;
|
||||
pub(crate) mod mindmap_node_view;
|
||||
pub(crate) mod mount;
|
||||
pub(crate) mod overlays;
|
||||
pub(crate) mod persistence;
|
||||
pub(crate) mod runtime_bridge;
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
//! Leptos Tiptap island 挂载上下文与 embedded/standalone delivery mode。
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
use leptos::mount::mount_to;
|
||||
use leptos::prelude::*;
|
||||
use leptos_tiptap::TiptapContent;
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{window, EventTarget, HtmlElement};
|
||||
|
||||
use crate::editor_runtime::bridge_events::{RuntimePageOptions, COMMAND_EVENT};
|
||||
use crate::editor_runtime::runtime_bridge::{build_command_listener, register_unmount_handle};
|
||||
use crate::App;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum RuntimeDeliveryMode {
|
||||
Standalone,
|
||||
Embedded,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct MountOptions {
|
||||
pub(crate) document_id: Option<String>,
|
||||
pub(crate) workspace_id: Option<String>,
|
||||
pub(crate) title: Option<String>,
|
||||
pub(crate) content: Option<Value>,
|
||||
pub(crate) html: Option<String>,
|
||||
pub(crate) editable: Option<bool>,
|
||||
pub(crate) read_only: Option<bool>,
|
||||
pub(crate) revision: Option<i64>,
|
||||
pub(crate) conflict_detection_key: Option<String>,
|
||||
pub(crate) page_options: Option<RuntimePageOptions>,
|
||||
#[serde(default)]
|
||||
pub(crate) standalone_object: Option<RuntimeStandaloneObject>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct RuntimeStandaloneObject {
|
||||
pub(crate) kind: Option<String>,
|
||||
pub(crate) document_id: Option<String>,
|
||||
pub(crate) mindmap_id: Option<String>,
|
||||
}
|
||||
|
||||
struct RuntimeMountContext {
|
||||
id: u32,
|
||||
target: EventTarget,
|
||||
mode: RuntimeDeliveryMode,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[allow(dead_code)]
|
||||
struct RuntimeMountOptions {
|
||||
options: MountOptions,
|
||||
mode: RuntimeDeliveryMode,
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static RUNTIME_MOUNT_CONTEXT: std::cell::RefCell<Option<RuntimeMountContext>> = const { std::cell::RefCell::new(None) };
|
||||
static RUNTIME_MOUNT_OPTIONS: std::cell::RefCell<Option<RuntimeMountOptions>> = const { std::cell::RefCell::new(None) };
|
||||
static NEXT_MOUNT_ID: Cell<u32> = const { Cell::new(1) };
|
||||
}
|
||||
|
||||
pub(crate) fn next_mount_id() -> u32 {
|
||||
NEXT_MOUNT_ID.with(|cell| {
|
||||
let next = cell.get();
|
||||
cell.set(next.saturating_add(1).max(1));
|
||||
next
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn set_runtime_mount_context(context: Option<(u32, EventTarget, RuntimeDeliveryMode)>) {
|
||||
RUNTIME_MOUNT_CONTEXT.with(|cell| {
|
||||
*cell.borrow_mut() = context.map(|(id, target, mode)| RuntimeMountContext {
|
||||
id,
|
||||
target,
|
||||
mode,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn runtime_mount_context() -> Option<(u32, EventTarget, RuntimeDeliveryMode)> {
|
||||
RUNTIME_MOUNT_CONTEXT.with(|cell| {
|
||||
cell.borrow()
|
||||
.as_ref()
|
||||
.map(|context| (context.id, context.target.clone(), context.mode))
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn runtime_delivery_mode() -> RuntimeDeliveryMode {
|
||||
resolve_runtime_delivery_mode(
|
||||
runtime_mount_context().map(|(_, _, mode)| mode),
|
||||
url_requests_embedded_mode(),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn set_runtime_mount_options(options: Option<MountOptions>, mode: RuntimeDeliveryMode) {
|
||||
RUNTIME_MOUNT_OPTIONS.with(|cell| {
|
||||
*cell.borrow_mut() = options.map(|options| RuntimeMountOptions { options, mode });
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn runtime_editor_instance_id(mount_id: Option<u32>) -> String {
|
||||
mount_id
|
||||
.filter(|value| *value > 0)
|
||||
.map(|value| format!("mnote-leptos-tiptap-spike-{value}"))
|
||||
.unwrap_or_else(|| "mnote-leptos-tiptap-spike-standalone".to_string())
|
||||
}
|
||||
|
||||
fn url_requests_embedded_mode() -> bool {
|
||||
window()
|
||||
.and_then(|win| win.location().search().ok())
|
||||
.map(|search| search.contains("embedded=1"))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_runtime_delivery_mode(
|
||||
explicit_mode: Option<RuntimeDeliveryMode>,
|
||||
url_embedded: bool,
|
||||
) -> RuntimeDeliveryMode {
|
||||
if let Some(mode) = explicit_mode {
|
||||
return mode;
|
||||
}
|
||||
|
||||
if url_embedded {
|
||||
RuntimeDeliveryMode::Embedded
|
||||
} else {
|
||||
RuntimeDeliveryMode::Standalone
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn mount_app_into(
|
||||
target: HtmlElement,
|
||||
options: MountOptions,
|
||||
mode: RuntimeDeliveryMode,
|
||||
) -> u32 {
|
||||
let mount_id = next_mount_id();
|
||||
let context_target: EventTarget = target.clone().into();
|
||||
|
||||
set_runtime_mount_options(Some(options.clone()), mode);
|
||||
set_runtime_mount_context(Some((mount_id, context_target.clone(), mode)));
|
||||
|
||||
let handle = mount_to(target, move || {
|
||||
view! {
|
||||
<App mount_options=options />
|
||||
}
|
||||
});
|
||||
|
||||
let command_listener = build_command_listener(mount_id);
|
||||
let _ = context_target
|
||||
.add_event_listener_with_callback(COMMAND_EVENT, command_listener.as_ref().unchecked_ref());
|
||||
|
||||
register_unmount_handle(mount_id, context_target, command_listener, handle);
|
||||
mount_id
|
||||
}
|
||||
|
||||
pub(crate) fn initial_content() -> TiptapContent {
|
||||
TiptapContent::json(serde_json::json!({
|
||||
"type": "doc",
|
||||
"content": []
|
||||
}))
|
||||
}
|
||||
Reference in New Issue
Block a user