20260513 mindmap优化01
This commit is contained in:
@@ -288,7 +288,7 @@ pub(crate) fn build_editor_bootstrap_json_with_ids(
|
||||
.unwrap_or_else(|_| "{}".to_string())
|
||||
}
|
||||
|
||||
fn build_document_panes_bootstrap_json(
|
||||
pub(crate) fn build_document_panes_bootstrap_json(
|
||||
aggregate: &PageAggregate,
|
||||
context: &RequestContext,
|
||||
source_kind: Option<&str>,
|
||||
@@ -815,6 +815,28 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
|
||||
return { type: 'codeBlock', attrs: withTextAlign({ blockId, language: block?.props?.language || null }), content };
|
||||
}
|
||||
if (type === 'divider' || type === 'horizontalRule' || type === 'horizontal_rule') return { type: 'horizontalRule', attrs: { blockId } };
|
||||
if (type === 'mindmap') {
|
||||
const data = block?.props?.data && typeof block.props.data === 'object' ? block.props.data : null;
|
||||
const mindmapId = firstNonEmptyText(
|
||||
block?.props?.mindmapId,
|
||||
block?.props?.mindmap_id,
|
||||
block?.mindmapId,
|
||||
block?.mindmap_id,
|
||||
data?.mindmapId,
|
||||
data?.mindmap_id,
|
||||
data?.id
|
||||
);
|
||||
const rootNodeId = firstNonEmptyText(block?.props?.rootNodeId, block?.props?.root_node_id, data?.rootNodeId, data?.root_node_id) || 'root';
|
||||
return {
|
||||
type: 'paragraph',
|
||||
attrs: withTextAlign({
|
||||
blockId,
|
||||
mnoteBlockType: 'mindmap',
|
||||
mindmapId,
|
||||
rootNodeId,
|
||||
}),
|
||||
};
|
||||
}
|
||||
if (type === 'media') {
|
||||
const sourcePath = firstNonEmptyText(block?.props?.sourcePath, block?.props?.url, block?.props?.src);
|
||||
const name = firstNonEmptyText(block?.props?.name, block?.props?.fileName, block?.props?.title, sourcePath);
|
||||
@@ -882,6 +904,41 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
|
||||
content.type === 'doc'
|
||||
);
|
||||
|
||||
const mindmapDomDescriptors = (root) => {
|
||||
if (!(root instanceof HTMLElement)) return [];
|
||||
return Array.from(root.querySelectorAll('[data-testid="mnote-mindmap-editor-root"]'))
|
||||
.flatMap((node) => {
|
||||
if (!(node instanceof HTMLElement)) return [];
|
||||
const mindmapId = typeof node.dataset.mnoteMindmapId === 'string' ? node.dataset.mnoteMindmapId.trim() : '';
|
||||
if (!mindmapId) return [];
|
||||
const rootNodeId = typeof node.dataset.mnoteRootNodeId === 'string' && node.dataset.mnoteRootNodeId.trim()
|
||||
? node.dataset.mnoteRootNodeId.trim()
|
||||
: 'root';
|
||||
return [{ mindmapId, rootNodeId }];
|
||||
});
|
||||
};
|
||||
|
||||
const hydrateMindmapAttrsFromDom = (tiptapDocument, root) => {
|
||||
if (!isTiptapDocument(tiptapDocument) || !Array.isArray(tiptapDocument.content)) return tiptapDocument;
|
||||
const descriptors = mindmapDomDescriptors(root);
|
||||
if (!descriptors.length) return tiptapDocument;
|
||||
let index = 0;
|
||||
for (const node of tiptapDocument.content) {
|
||||
if (node?.type !== 'paragraph' || node?.attrs?.mnoteBlockType !== 'mindmap') continue;
|
||||
const descriptor = descriptors[index];
|
||||
index += 1;
|
||||
if (!descriptor) continue;
|
||||
node.attrs = node.attrs && typeof node.attrs === 'object' ? node.attrs : {};
|
||||
if (typeof node.attrs.mindmapId !== 'string' || !node.attrs.mindmapId.trim()) {
|
||||
node.attrs.mindmapId = descriptor.mindmapId;
|
||||
}
|
||||
if (typeof node.attrs.rootNodeId !== 'string' || !node.attrs.rootNodeId.trim()) {
|
||||
node.attrs.rootNodeId = descriptor.rootNodeId;
|
||||
}
|
||||
}
|
||||
return tiptapDocument;
|
||||
};
|
||||
|
||||
const toTiptapDocument = (content, fallbackText = '') => {
|
||||
if (isTiptapDocument(content)) return content;
|
||||
const blocks = Array.isArray(content) ? content : Array.isArray(content?.blocks) ? content.blocks : [];
|
||||
@@ -923,9 +980,36 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
|
||||
const raw = typeof node?.attrs?.blockId === 'string' ? node.attrs.blockId.trim() : '';
|
||||
return raw || `block-${index + 1}`;
|
||||
};
|
||||
const mindmapPropsFromAttrs = (attrs, fallbackMindmapId) => {
|
||||
const data = attrs?.data && typeof attrs.data === 'object' ? attrs.data : {};
|
||||
const mindmapId = firstNonEmptyText(
|
||||
attrs?.mindmapId,
|
||||
attrs?.mindmap_id,
|
||||
data?.mindmapId,
|
||||
data?.mindmap_id,
|
||||
data?.id,
|
||||
fallbackMindmapId
|
||||
);
|
||||
const rootNodeId = firstNonEmptyText(attrs?.rootNodeId, attrs?.root_node_id, data?.rootNodeId, data?.root_node_id) || 'root';
|
||||
const projectionVersion = Number(attrs?.projectionVersion ?? attrs?.projection_version);
|
||||
return {
|
||||
mindmapId,
|
||||
rootNodeId,
|
||||
...(Number.isFinite(projectionVersion) ? { projectionVersion } : {}),
|
||||
};
|
||||
};
|
||||
|
||||
const tiptapNodeToEditorBlock = (node, index) => {
|
||||
const blockId = blockIdOf(node, index);
|
||||
if (node?.type === 'paragraph' && node?.attrs?.mnoteBlockType === 'mindmap') {
|
||||
return {
|
||||
blockId,
|
||||
blockType: 'mindmap',
|
||||
props: mindmapPropsFromAttrs(node?.attrs, blockId),
|
||||
contentNodes: [],
|
||||
childBlockIds: [],
|
||||
};
|
||||
}
|
||||
if (node?.type === 'paragraph') return { blockId, blockType: 'paragraph', props: {}, contentNodes: inlineTextNodes(node), childBlockIds: [] };
|
||||
if (node?.type === 'heading') {
|
||||
const level = Math.max(1, Math.min(6, Number(node?.attrs?.level || 1) || 1));
|
||||
@@ -967,6 +1051,8 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
|
||||
? { checked: Boolean(block.props?.checked) }
|
||||
: block.blockType === 'code_block'
|
||||
? { language: block.props?.language || null }
|
||||
: block.blockType === 'mindmap'
|
||||
? mindmapPropsFromAttrs(block.props || {}, block.blockId)
|
||||
: block.blockType === 'image'
|
||||
? { ...(block.props || {}) }
|
||||
: block.blockType === 'toc'
|
||||
@@ -974,7 +1060,9 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
|
||||
: block.blockType === 'table'
|
||||
? { ...(block.props || {}) }
|
||||
: undefined,
|
||||
content: Array.isArray(block.contentNodes)
|
||||
content: block.blockType === 'mindmap'
|
||||
? ''
|
||||
: Array.isArray(block.contentNodes)
|
||||
? block.contentNodes.map((node) => {
|
||||
if (!node || typeof node !== 'object') return null;
|
||||
const text = typeof node.text === 'string' ? node.text : '';
|
||||
@@ -1060,6 +1148,7 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
|
||||
const paneViewRegistry = new Map();
|
||||
let nextViewId = 1;
|
||||
const externalConflictMessage = '本地 Markdown 文件已在外部更新,请刷新或保存前先处理冲突';
|
||||
const treeExternalConflictMessage = '当前页面已在其它窗口更新,请刷新或保存前先处理冲突';
|
||||
const SESSION_RELEASE_DELAY_MS = 1200;
|
||||
|
||||
const parseLocalFolderEventPayload = (event) => {
|
||||
@@ -1316,6 +1405,11 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
|
||||
|
||||
const persistSession = async (session) => {
|
||||
if (session.readOnly || session.saving || session.hasExternalConflict) return;
|
||||
const hydrateView = sessionViews(session).find((item) => item.mountId != null) || sessionViews(session)[0];
|
||||
if (hydrateView) {
|
||||
hydrateMindmapAttrsFromDom(session.currentTiptapDocument, hydrateView.runtimeDescriptor.root);
|
||||
session.currentSerialized = JSON.stringify(session.currentTiptapDocument);
|
||||
}
|
||||
const serialized = session.currentSerialized;
|
||||
if (!session.dirty && serialized === session.lastPersistedSerialized) {
|
||||
setSessionStatus(session, 'saved');
|
||||
@@ -1389,16 +1483,20 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
|
||||
}
|
||||
};
|
||||
|
||||
const scheduleSessionExternalRefresh = (session) => {
|
||||
const scheduleSessionExternalRefresh = (session, source) => {
|
||||
if (session.externalRefreshTimer) return;
|
||||
session.externalRefreshSource = source || session.externalRefreshSource || 'mnote-web-external-change';
|
||||
session.externalRefreshTimer = window.setTimeout(() => {
|
||||
const refreshSource = session.externalRefreshSource || 'mnote-web-external-change';
|
||||
session.externalRefreshSource = '';
|
||||
session.externalRefreshTimer = 0;
|
||||
void refreshSessionFromExternalFileChange(session);
|
||||
void refreshSessionFromExternalChange(session, refreshSource);
|
||||
}, 120);
|
||||
};
|
||||
|
||||
const refreshSessionFromExternalFileChange = async (session) => {
|
||||
if (session.sourceKind !== 'local_folder' || !session.rootUri || document.hidden) return;
|
||||
const refreshSessionFromExternalChange = async (session, source) => {
|
||||
if (document.hidden) return;
|
||||
if (session.sourceKind === 'local_folder' && !session.rootUri) return;
|
||||
try {
|
||||
const response = await fetch(pageAggregateUrl({
|
||||
documentId: session.documentId,
|
||||
@@ -1439,14 +1537,19 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
|
||||
session.hasExternalConflict = false;
|
||||
session.lastUserInputAt = 0;
|
||||
sessionViews(session).forEach((view) => {
|
||||
if (view.mountId != null) dispatchSessionContentToView(session, view, 'mnote-web-local-folder-watch');
|
||||
if (view.mountId != null) dispatchSessionContentToView(session, view, source || 'mnote-web-external-change');
|
||||
});
|
||||
setSessionStatus(session, 'synced-external-change');
|
||||
} catch (error) {
|
||||
console.warn('mnote local folder 外部更新检测失败', error);
|
||||
console.warn('mnote 页面外部更新检测失败', error);
|
||||
}
|
||||
};
|
||||
|
||||
const refreshSessionFromExternalFileChange = async (session) => {
|
||||
if (session.sourceKind !== 'local_folder') return;
|
||||
await refreshSessionFromExternalChange(session, 'mnote-web-local-folder-watch');
|
||||
};
|
||||
|
||||
const ensureLocalFolderEventChannel = (session) => {
|
||||
if (session.sourceKind !== 'local_folder' || !session.rootUri || typeof window.EventSource !== 'function') {
|
||||
return;
|
||||
@@ -1473,7 +1576,7 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
|
||||
markSessionExternalConflict(targetSession, externalConflictMessage);
|
||||
return;
|
||||
}
|
||||
scheduleSessionExternalRefresh(targetSession);
|
||||
scheduleSessionExternalRefresh(targetSession, 'mnote-web-local-folder-watch');
|
||||
});
|
||||
});
|
||||
eventSource.onerror = () => {
|
||||
@@ -1485,6 +1588,164 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
|
||||
session.localFolderChannel = channel;
|
||||
};
|
||||
|
||||
const readTreePayloadData = (payload) => (
|
||||
payload && typeof payload === 'object'
|
||||
? (payload.data || payload.delta || payload)
|
||||
: null
|
||||
);
|
||||
|
||||
const readTreePayloadOverview = (payload) => (
|
||||
payload && typeof payload === 'object' && payload.overview && typeof payload.overview === 'object'
|
||||
? payload.overview
|
||||
: null
|
||||
);
|
||||
|
||||
const readTreePayloadCursor = (payload) => {
|
||||
const raw = String(payload?.cursor || payload?.revision || '').trim();
|
||||
if (!raw) return { id: '', createdAt: '', raw: '' };
|
||||
try {
|
||||
const parsed = JSON.parse(raw);
|
||||
if (parsed && typeof parsed === 'object') {
|
||||
return {
|
||||
id: String(parsed.id || parsed.commandId || parsed.command_id || '').trim(),
|
||||
createdAt: String(parsed.createdAt || parsed.created_at || '').trim(),
|
||||
raw,
|
||||
};
|
||||
}
|
||||
} catch (_) {}
|
||||
return { id: raw, createdAt: '', raw };
|
||||
};
|
||||
|
||||
const treeRecordMatchesPayloadCursor = (record, payload) => {
|
||||
if (!record || typeof record !== 'object') return false;
|
||||
const cursor = readTreePayloadCursor(payload);
|
||||
if (!cursor.id && !cursor.createdAt && !cursor.raw) return false;
|
||||
const ids = [
|
||||
record.id,
|
||||
record._id,
|
||||
record.command_log_id,
|
||||
record.commandLogId,
|
||||
record.domain_event_id,
|
||||
record.domainEventId,
|
||||
record.command_id,
|
||||
record.commandId,
|
||||
].map((value) => String(value || '').trim()).filter(Boolean);
|
||||
if (cursor.id && ids.includes(cursor.id)) return true;
|
||||
const createdAt = String(record.created_at || record.createdAt || '').trim();
|
||||
return Boolean(cursor.createdAt && createdAt && cursor.createdAt === createdAt);
|
||||
};
|
||||
|
||||
const treeRecordTargetsDocument = (record, documentId) => {
|
||||
if (!record || typeof record !== 'object' || !documentId) return false;
|
||||
const targetPageId = String(record.target_page_id || record.targetPageId || '').trim();
|
||||
const aggregateId = String(record.aggregate_id || record.aggregateId || '').trim();
|
||||
if (targetPageId === documentId || aggregateId === documentId) return true;
|
||||
const payload = record.payload && typeof record.payload === 'object' ? record.payload : null;
|
||||
if (!payload) return false;
|
||||
const streamDelta = payload.streamDelta || payload.stream_delta || null;
|
||||
const deltaDocumentId = streamDelta && typeof streamDelta === 'object'
|
||||
? String(streamDelta.documentId || streamDelta.pageId || streamDelta.document_id || streamDelta.page_id || '').trim()
|
||||
: '';
|
||||
return deltaDocumentId === documentId;
|
||||
};
|
||||
|
||||
const collectMindmapIdsFromTreeRecord = (record, documentId, out) => {
|
||||
if (!record || typeof record !== 'object' || !documentId) return;
|
||||
if (!treeRecordTargetsDocument(record, documentId)) return;
|
||||
const targetBlockId = String(record.target_block_id || record.targetBlockId || '').trim();
|
||||
if (targetBlockId) out.add(targetBlockId);
|
||||
const aggregateType = String(record.aggregate_type || record.aggregateType || '').trim();
|
||||
const aggregateId = String(record.aggregate_id || record.aggregateId || '').trim();
|
||||
if (aggregateType === 'block' && aggregateId) out.add(aggregateId);
|
||||
const payload = record.payload && typeof record.payload === 'object' ? record.payload : null;
|
||||
const streamDelta = payload && typeof payload === 'object' ? (payload.streamDelta || payload.stream_delta || null) : null;
|
||||
const blockId = streamDelta && typeof streamDelta === 'object'
|
||||
? String(streamDelta.blockId || streamDelta.block_id || '').trim()
|
||||
: '';
|
||||
if (blockId) out.add(blockId);
|
||||
};
|
||||
|
||||
const collectMindmapIdsFromTreePayload = (payload, session) => {
|
||||
const ids = new Set();
|
||||
if (!payload || typeof payload !== 'object' || !session?.documentId) return [];
|
||||
const kind = String(payload.kind || '').trim();
|
||||
const data = readTreePayloadData(payload);
|
||||
if (kind === 'delta' && data && typeof data === 'object') {
|
||||
const documentId = String(data.documentId || data.pageId || data.document_id || data.page_id || '').trim();
|
||||
if (!documentId || documentId === session.documentId) {
|
||||
const blockId = String(data.blockId || data.block_id || '').trim();
|
||||
if (blockId) ids.add(blockId);
|
||||
const streamDelta = data.streamDelta || data.stream_delta || null;
|
||||
const streamBlockId = streamDelta && typeof streamDelta === 'object'
|
||||
? String(streamDelta.blockId || streamDelta.block_id || '').trim()
|
||||
: '';
|
||||
if (streamBlockId) ids.add(streamBlockId);
|
||||
}
|
||||
}
|
||||
if (kind === 'resync') {
|
||||
const overview = readTreePayloadOverview(payload);
|
||||
if (overview) {
|
||||
const commandLogs = Array.isArray(overview.command_logs) ? overview.command_logs : Array.isArray(overview.commandLogs) ? overview.commandLogs : [];
|
||||
const domainEvents = Array.isArray(overview.domain_events) ? overview.domain_events : Array.isArray(overview.domainEvents) ? overview.domainEvents : [];
|
||||
commandLogs
|
||||
.filter((record) => treeRecordMatchesPayloadCursor(record, payload))
|
||||
.forEach((record) => collectMindmapIdsFromTreeRecord(record, session.documentId, ids));
|
||||
domainEvents
|
||||
.filter((record) => treeRecordMatchesPayloadCursor(record, payload))
|
||||
.forEach((record) => collectMindmapIdsFromTreeRecord(record, session.documentId, ids));
|
||||
}
|
||||
}
|
||||
return Array.from(ids);
|
||||
};
|
||||
|
||||
const refreshMindmapRuntimesFromTreePayload = (payload, session) => {
|
||||
const registry = window.__MNOTE_LEPTOS_MINDMAP_BRIDGES__ || {};
|
||||
collectMindmapIdsFromTreePayload(payload, session).forEach((mindmapId) => {
|
||||
const bridge = registry[mindmapId];
|
||||
if (bridge && typeof bridge.refreshProjection === 'function') {
|
||||
void bridge.refreshProjection('mnote-web-tree-live');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const treePayloadTargetsDocument = (payload, session) => {
|
||||
if (!payload || typeof payload !== 'object' || !session?.documentId) return false;
|
||||
if (payload.workspaceId && session.workspaceId && String(payload.workspaceId) !== String(session.workspaceId)) return false;
|
||||
const data = readTreePayloadData(payload);
|
||||
if (data && typeof data === 'object') {
|
||||
const documentId = String(data.documentId || data.pageId || data.document_id || data.page_id || '').trim();
|
||||
if (documentId === session.documentId) return true;
|
||||
const documents = Array.isArray(data.upsertDocuments) ? data.upsertDocuments : Array.isArray(data.upsert_documents) ? data.upsert_documents : [];
|
||||
if (documents.some((item) => String(item?.id || item?.documentId || '').trim() === session.documentId)) return true;
|
||||
}
|
||||
const overview = readTreePayloadOverview(payload);
|
||||
if (!overview) return false;
|
||||
const commandLogs = Array.isArray(overview.command_logs) ? overview.command_logs : Array.isArray(overview.commandLogs) ? overview.commandLogs : [];
|
||||
const domainEvents = Array.isArray(overview.domain_events) ? overview.domain_events : Array.isArray(overview.domainEvents) ? overview.domainEvents : [];
|
||||
return commandLogs.some((record) => treeRecordTargetsDocument(record, session.documentId))
|
||||
|| domainEvents.some((record) => treeRecordTargetsDocument(record, session.documentId));
|
||||
};
|
||||
|
||||
const handleTreeExternalChange = (event) => {
|
||||
const payload = event?.detail?.payload || event?.detail || null;
|
||||
if (!payload) return;
|
||||
Array.from(documentSessionRegistry.values()).forEach((session) => {
|
||||
if (session.sourceKind === 'local_folder') return;
|
||||
if (!treePayloadTargetsDocument(payload, session)) return;
|
||||
refreshMindmapRuntimesFromTreePayload(payload, session);
|
||||
session.lastExternalChangeSignalAt = Date.now();
|
||||
session.externalChangePending = true;
|
||||
if (session.hasExternalConflict || sessionHasRecentLocalInput(session) || session.dirty || session.saveTimer || session.saving) {
|
||||
markSessionExternalConflict(session, treeExternalConflictMessage);
|
||||
return;
|
||||
}
|
||||
scheduleSessionExternalRefresh(session, 'mnote-web-tree-live');
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener('tree:delta', handleTreeExternalChange);
|
||||
window.addEventListener('tree:resync', handleTreeExternalChange);
|
||||
|
||||
const createDocumentSession = (runtimeDescriptor) => {
|
||||
const pageBody = runtimeDescriptor.aggregate.body || {};
|
||||
const permissions = runtimeDescriptor.aggregate.head?.permissions || {};
|
||||
@@ -1513,6 +1774,7 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
|
||||
saving: false,
|
||||
hasExternalConflict: false,
|
||||
externalChangePending: false,
|
||||
externalRefreshSource: '',
|
||||
lastExternalChangeSignalAt: 0,
|
||||
lastUserInputAt: 0,
|
||||
status: 'booting',
|
||||
@@ -1714,10 +1976,10 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
|
||||
const pendingExternalChange = session.sourceKind === 'local_folder' && session.externalChangePending;
|
||||
const recentExternalChange = sessionHasRecentExternalSignal(session);
|
||||
const recentLocalInput = sessionHasRecentLocalInput(session);
|
||||
const tiptapDocument = toTiptapDocument(
|
||||
const tiptapDocument = hydrateMindmapAttrsFromDom(toTiptapDocument(
|
||||
payload?.tiptapDocument || payload?.editorDocument || payload?.content,
|
||||
currentEditorText(view),
|
||||
);
|
||||
), view.runtimeDescriptor.root);
|
||||
const serialized = JSON.stringify(tiptapDocument);
|
||||
if (view.suppressedSerialized && view.suppressedSerialized === serialized) {
|
||||
view.suppressedSerialized = null;
|
||||
@@ -2614,6 +2876,13 @@ mod tests {
|
||||
assert!(html.contains("btn.getAttribute('data-page-openable') === 'false'"));
|
||||
assert!(html.contains("data-mnote-action=\"open-local-folder\""));
|
||||
assert!(html.contains("refreshSessionFromExternalFileChange"));
|
||||
assert!(html.contains("refreshSessionFromExternalChange"));
|
||||
assert!(html.contains("treeExternalConflictMessage"));
|
||||
assert!(html.contains("tree:delta"));
|
||||
assert!(html.contains("tree:resync"));
|
||||
assert!(html.contains("mnote-web-tree-live"));
|
||||
assert!(html.contains("refreshMindmapRuntimesFromTreePayload"));
|
||||
assert!(html.contains("__MNOTE_LEPTOS_MINDMAP_BRIDGES__"));
|
||||
assert!(html.contains("/api/local-folder/events"));
|
||||
assert!(html.contains("new EventSource(url.toString())"));
|
||||
assert!(html.contains("localFolderEventRegistry"));
|
||||
@@ -2714,6 +2983,14 @@ mod tests {
|
||||
assert!(html.contains("marks.push({ type: 'link', attrs: { href } })"));
|
||||
assert!(html.contains("styles.link = href"));
|
||||
assert!(html.contains("contentNodes.map((node) => {"));
|
||||
assert!(html.contains("node?.attrs?.mnoteBlockType === 'mindmap'"));
|
||||
assert!(html.contains("blockType: 'mindmap'"));
|
||||
assert!(html.contains("props: mindmapPropsFromAttrs(node?.attrs, blockId)"));
|
||||
assert!(html.contains("mindmapPropsFromAttrs(block.props || {}, block.blockId)"));
|
||||
assert!(html.contains("mnoteBlockType: 'mindmap'"));
|
||||
assert!(html.contains("block.blockType === 'mindmap'"));
|
||||
assert!(html.contains("content: block.blockType === 'mindmap'"));
|
||||
assert!(html.contains("? ''"));
|
||||
assert!(!html.contains(
|
||||
"block.contentNodes.map((node) => typeof node?.text === 'string' ? node.text : '').join('')"
|
||||
));
|
||||
|
||||
Reference in New Issue
Block a user