···142142143143/**
144144 * Convert a list_item node to lexicon format.
145145- * Extracts text from the first paragraph child.
145145+ * Extracts text from the first paragraph child and any nested list.
146146 */
147147function listItemToLexicon(node: ProseMirrorNode): Typed<ListItem> {
148148- const firstParagraph = node.firstChild;
148148+ const children = childrenOf(node);
149149+ const firstParagraph = children[0];
150150+151151+ let text: string | undefined;
152152+ let facets: Facet[] | undefined;
153153+149154 if (firstParagraph?.type.name === "paragraph") {
150150- const { text, facets } = extractTextAndFacets(firstParagraph);
151151- return {
152152- $type: "com.deckbelcher.richtext#listItem",
153153- text: text || undefined,
154154- facets: facets.length > 0 ? facets : undefined,
155155- };
155155+ const extracted = extractTextAndFacets(firstParagraph);
156156+ text = extracted.text || undefined;
157157+ facets = extracted.facets.length > 0 ? extracted.facets : undefined;
156158 }
157157- return { $type: "com.deckbelcher.richtext#listItem" };
159159+160160+ // Look for nested list after the first paragraph
161161+ let sublist: Typed<BulletListBlock> | Typed<OrderedListBlock> | undefined;
162162+ for (let i = 1; i < children.length; i++) {
163163+ const child = children[i];
164164+ if (child.type.name === "bullet_list") {
165165+ sublist = bulletListToLexicon(child);
166166+ break;
167167+ }
168168+ if (child.type.name === "ordered_list") {
169169+ sublist = orderedListToLexicon(child);
170170+ break;
171171+ }
172172+ }
173173+174174+ return {
175175+ $type: "com.deckbelcher.richtext#listItem",
176176+ text,
177177+ facets,
178178+ sublist,
179179+ };
158180}
159181160182/**
···381403382404function lexiconListItemToTree(item: ListItem): ProseMirrorNode {
383405 const text = item.text || "";
384384- if (!text) {
385385- return schema.node("list_item", null, [schema.node("paragraph")]);
406406+ const content: ProseMirrorNode[] = [];
407407+408408+ // First paragraph (required by schema)
409409+ if (text) {
410410+ const nodes = textAndFacetsToNodes(text, item.facets || []);
411411+ content.push(
412412+ schema.node("paragraph", null, nodes.length > 0 ? nodes : undefined),
413413+ );
414414+ } else {
415415+ content.push(schema.node("paragraph"));
386416 }
387387- const nodes = textAndFacetsToNodes(text, item.facets || []);
388388- return schema.node("list_item", null, [
389389- schema.node("paragraph", null, nodes.length > 0 ? nodes : undefined),
390390- ]);
417417+418418+ // Nested sublist if present
419419+ if (item.sublist) {
420420+ const sublistType = (item.sublist as { $type?: string }).$type;
421421+ if (sublistType === "com.deckbelcher.richtext#bulletListBlock") {
422422+ content.push(
423423+ lexiconBulletListToTree(item.sublist as unknown as BulletListBlock),
424424+ );
425425+ } else if (sublistType === "com.deckbelcher.richtext#orderedListBlock") {
426426+ content.push(
427427+ lexiconOrderedListToTree(item.sublist as unknown as OrderedListBlock),
428428+ );
429429+ }
430430+ }
431431+432432+ return schema.node("list_item", null, content);
391433}
392434393435export interface Segment {
+4-1
typelex/richtext.tsp
···9191 start?: integer;
9292 }
93939494- /** A single list item with text and optional facets. */
9494+ /** A single list item with text, optional facets, and optional sublist. */
9595 model ListItem {
9696 /** The plain text content (no markdown symbols). */
9797 @maxGraphemes(10000)
···100100101101 /** Annotations of text (formatting, mentions, links, etc). */
102102 facets?: com.deckbelcher.richtext.facet.Main[];
103103+104104+ /** Optional nested sublist (bullet or ordered). */
105105+ sublist?: BulletListBlock | OrderedListBlock | unknown;
103106 }
104107105108 /** A horizontal rule (thematic break). */