mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2025-04-06 10:46:57 +00:00
feat: adapt prosemirror-flat-list
This commit is contained in:
parent
3605acb70e
commit
dbe1c20c29
@ -7,27 +7,27 @@ Get the Wiki AST from a Prosemirror AST
|
||||
|
||||
\*/
|
||||
|
||||
function doc(context, node) {
|
||||
return convertNodes(context, node.content);
|
||||
function doc(builder, node) {
|
||||
return convertNodes(builder, node.content);
|
||||
}
|
||||
|
||||
function paragraph(context, node) {
|
||||
function paragraph(builder, node) {
|
||||
return {
|
||||
type: "element",
|
||||
tag: "p",
|
||||
rule: "parseblock",
|
||||
children: convertNodes(context, node.content)
|
||||
children: convertNodes(builder, node.content)
|
||||
};
|
||||
}
|
||||
|
||||
function text(context, node) {
|
||||
function text(builder, node) {
|
||||
return {
|
||||
type: "text",
|
||||
text: node.text
|
||||
}
|
||||
}
|
||||
|
||||
function heading(context, node) {
|
||||
function heading(builder, node) {
|
||||
return {
|
||||
type: "element",
|
||||
tag: "h" + node.attrs.level,
|
||||
@ -35,21 +35,50 @@ function heading(context, node) {
|
||||
attributes: {
|
||||
// TODO: restore class if any
|
||||
},
|
||||
children: convertNodes(context, node.content)
|
||||
children: convertNodes(builder, node.content)
|
||||
};
|
||||
}
|
||||
|
||||
function list(context, node) {
|
||||
function list(builder, node, context) {
|
||||
const listType = node.attrs && node.attrs.kind === "ordered" ? "ol" : "ul";
|
||||
|
||||
const listItems = node.content.map(item => {
|
||||
return {
|
||||
// Prepare an array to store all list items
|
||||
let listItems = [];
|
||||
|
||||
// Add content from current node to list items
|
||||
node.content.forEach(item => {
|
||||
listItems.push({
|
||||
type: "element",
|
||||
tag: "li",
|
||||
children: convertANode(context, item)
|
||||
};
|
||||
children: convertANode(builder, item)
|
||||
});
|
||||
});
|
||||
|
||||
// Check if there are adjacent lists of the same type
|
||||
while (context && context.nodes && context.nodes.length > 0) {
|
||||
const nextNode = context.nodes[0];
|
||||
|
||||
// If next node is also a list of the same type
|
||||
if (nextNode.type === 'list' &&
|
||||
((node.attrs && node.attrs.kind) === (nextNode.attrs && nextNode.attrs.kind))) {
|
||||
|
||||
// Remove and consume the next node
|
||||
const consumedNode = context.nodes.shift();
|
||||
|
||||
// Merge its content into current list
|
||||
consumedNode.content.forEach(item => {
|
||||
listItems.push({
|
||||
type: "element",
|
||||
tag: "li",
|
||||
children: convertANode(builder, item)
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// If next node is not a list of the same type, stop merging
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: "element",
|
||||
tag: listType,
|
||||
@ -80,19 +109,26 @@ function convertNodes(builders, nodes) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return nodes.reduce((accumulator, node) => {
|
||||
return [...accumulator, ...convertANode(builders, node)];
|
||||
}, []);
|
||||
const result = [];
|
||||
const nodesCopy = [...nodes]; // Create a copy to avoid modifying the original array
|
||||
|
||||
while (nodesCopy.length > 0) {
|
||||
const node = nodesCopy.shift(); // Get and remove the first node
|
||||
const convertedNodes = convertANode(builders, node, { nodes: nodesCopy });
|
||||
result.push(...convertedNodes);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function restoreMetadata(node) {
|
||||
// TODO: restore attributes, orderedAttributes, isBlock
|
||||
return {};
|
||||
}
|
||||
function convertANode(builders, node) {
|
||||
function convertANode(builders, node, context) {
|
||||
var builder = builders[node.type];
|
||||
if (typeof builder === 'function') {
|
||||
var convertedNode = builder(builders, node);
|
||||
var convertedNode = builder(builders, node, context);
|
||||
var arrayOfNodes = (Array.isArray(convertedNode)
|
||||
? convertedNode : [convertedNode]);
|
||||
return arrayOfNodes.map((child) => ({ ...restoreMetadata(node), ...child }));
|
||||
|
@ -7,59 +7,25 @@ Get the Prosemirror AST from a Wiki AST
|
||||
|
||||
\*/
|
||||
|
||||
/**
|
||||
* Many node shares same type `element` in wikiAst, we need to distinguish them by tag.
|
||||
*/
|
||||
const elementBuilders = {
|
||||
p: function(context, node) {
|
||||
return {
|
||||
type: "paragraph",
|
||||
content: convertNodes(context, node.children)
|
||||
};
|
||||
},
|
||||
h1: function(context, node) {
|
||||
return {
|
||||
type: "heading",
|
||||
attrs: { level: 1 },
|
||||
content: convertNodes(context, node.children)
|
||||
};
|
||||
},
|
||||
h2: function(context, node) {
|
||||
return {
|
||||
type: "heading",
|
||||
attrs: { level: 2 },
|
||||
content: convertNodes(context, node.children)
|
||||
};
|
||||
},
|
||||
h3: function(context, node) {
|
||||
return {
|
||||
type: "heading",
|
||||
attrs: { level: 3 },
|
||||
content: convertNodes(context, node.children)
|
||||
};
|
||||
},
|
||||
h4: function(context, node) {
|
||||
return {
|
||||
type: "heading",
|
||||
attrs: { level: 4 },
|
||||
content: convertNodes(context, node.children)
|
||||
};
|
||||
},
|
||||
h5: function(context, node) {
|
||||
return {
|
||||
type: "heading",
|
||||
attrs: { level: 5 },
|
||||
content: convertNodes(context, node.children)
|
||||
};
|
||||
},
|
||||
h6: function(context, node) {
|
||||
return {
|
||||
type: "heading",
|
||||
attrs: { level: 6 },
|
||||
content: convertNodes(context, node.children)
|
||||
};
|
||||
},
|
||||
ul: function(context, node) {
|
||||
function buildParagraph(context, node) {
|
||||
return {
|
||||
type: "paragraph",
|
||||
content: convertNodes(context, node.children)
|
||||
};
|
||||
}
|
||||
|
||||
function buildHeading(context, node, level) {
|
||||
return {
|
||||
type: "heading",
|
||||
attrs: { level: level },
|
||||
content: convertNodes(context, node.children)
|
||||
};
|
||||
}
|
||||
|
||||
function buildUnorderedList(context, node) {
|
||||
// Prosemirror requires split all lists into separate lists with single items
|
||||
return node.children.map(item => {
|
||||
const processedItem = convertANode({...context, level: context.level + 1}, item);
|
||||
return {
|
||||
type: "list",
|
||||
attrs: {
|
||||
@ -68,10 +34,14 @@ const elementBuilders = {
|
||||
checked: false,
|
||||
collapsed: false
|
||||
},
|
||||
content: convertNodes(context, node.children)
|
||||
content: processedItem
|
||||
};
|
||||
},
|
||||
ol: function(context, node) {
|
||||
});
|
||||
}
|
||||
|
||||
function buildOrderedList(context, node) {
|
||||
return node.children.map(item => {
|
||||
const processedItem = convertANode({...context, level: context.level + 1}, item);
|
||||
return {
|
||||
type: "list",
|
||||
attrs: {
|
||||
@ -80,17 +50,11 @@ const elementBuilders = {
|
||||
checked: false,
|
||||
collapsed: false
|
||||
},
|
||||
content: convertNodes(context, node.children)
|
||||
content: processedItem
|
||||
};
|
||||
},
|
||||
li: function(context, node) {
|
||||
// In ProseMirror, list items are converted to paragraphs or other block content
|
||||
// directly under the list node, no special list_item type needed
|
||||
const processedContent = convertNodes(context, node.children);
|
||||
// Ensure content starts with a block element (typically paragraph)
|
||||
return wrapTextNodesInParagraphs(context, processedContent);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to ensure text nodes in list items are wrapped in paragraphs
|
||||
@ -132,6 +96,28 @@ function wrapTextNodesInParagraphs(context, nodes) {
|
||||
return result;
|
||||
}
|
||||
|
||||
function buildListItem(context, node) {
|
||||
const processedContent = convertNodes({...context, level: context.level + 1}, node.children);
|
||||
// Ensure content starts with a block element (typically paragraph)
|
||||
return wrapTextNodesInParagraphs(context, processedContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Many node shares same type `element` in wikiAst, we need to distinguish them by tag.
|
||||
*/
|
||||
const elementBuilders = {
|
||||
p: buildParagraph,
|
||||
h1: (context, node) => buildHeading(context, node, 1),
|
||||
h2: (context, node) => buildHeading(context, node, 2),
|
||||
h3: (context, node) => buildHeading(context, node, 3),
|
||||
h4: (context, node) => buildHeading(context, node, 4),
|
||||
h5: (context, node) => buildHeading(context, node, 5),
|
||||
h6: (context, node) => buildHeading(context, node, 6),
|
||||
ul: buildUnorderedList,
|
||||
ol: buildOrderedList,
|
||||
li: buildListItem
|
||||
};
|
||||
|
||||
function element(context, node) {
|
||||
const builder = elementBuilders[node.tag];
|
||||
if (builder) {
|
||||
@ -158,7 +144,8 @@ const builders = {
|
||||
};
|
||||
|
||||
function wikiAstToProsemirrorAst(node, options) {
|
||||
const context = { ...builders, ...options };
|
||||
// Initialize context with level tracking
|
||||
const context = { ...builders, ...options, level: 0 };
|
||||
const result = convertNodes(context, Array.isArray(node) ? node : [node]);
|
||||
|
||||
// Wrap in a doc if needed
|
||||
|
@ -5,3 +5,5 @@ Test `<$prosemirror />` widget below, see console for content JSON.
|
||||
<$edit-prosemirror tiddler="$:/plugins/tiddlywiki/prosemirror/example"/>
|
||||
|
||||
<$edit-text tiddler="$:/plugins/tiddlywiki/prosemirror/example" class="tc-edit-texteditor"/>
|
||||
|
||||
{{$:/plugins/tiddlywiki/prosemirror/example}}
|
||||
|
@ -46,6 +46,8 @@ ProsemirrorWidget.prototype.render = function(parent,nextSibling) {
|
||||
var tiddler = this.getAttribute("tiddler");
|
||||
var initialText = this.wiki.getTiddlerText(tiddler, "");
|
||||
var initialWikiAst = $tw.wiki.parseText(null, initialText).tree;
|
||||
// DEBUG: console initialWikiAst
|
||||
console.log(`initialWikiAst`, initialWikiAst);
|
||||
var doc = wikiAstToProseMirrorAst(initialWikiAst);
|
||||
// DEBUG: console doc
|
||||
console.log(`initial doc`, doc);
|
||||
|
Loading…
x
Reference in New Issue
Block a user