v1.0.8 - Fix task list checkbox loss and empty line preservation on markdown roundtrip

This commit is contained in:
Yuri Karamian
2026-02-13 13:11:54 +01:00
parent ea323e508b
commit f32f77dcd5
5 changed files with 20 additions and 8 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "helixnotes",
"private": true,
"license": "AGPL-3.0-or-later",
"version": "1.0.7",
"version": "1.0.8",
"type": "module",
"scripts": {
"dev": "vite dev",
+1 -1
View File
@@ -1778,7 +1778,7 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "helixnotes"
version = "1.0.7"
version = "1.0.8"
dependencies = [
"chrono",
"dirs",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "helixnotes"
version = "1.0.7"
version = "1.0.8"
description = "Local-first markdown note-taking app"
authors = ["HelixNotes"]
license = "AGPL-3.0-or-later"
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"productName": "HelixNotes",
"version": "1.0.7",
"version": "1.0.8",
"identifier": "com.helixnotes.app",
"build": {
"frontendDist": "../build",
+16 -4
View File
@@ -1071,14 +1071,23 @@
}
function prosemirrorToMarkdown(doc: any): string {
const listTypes = new Set(['bulletList', 'orderedList', 'taskList']);
const parts: string[] = [];
let prevType = '';
let preserveEmptyParas = false;
doc.forEach((node: any) => {
// Skip empty paragraphs right after code blocks (TipTap cursor placeholder)
if (node.type.name === 'paragraph' && node.childCount === 0 && prevType === 'codeBlock') {
const isEmpty = node.type.name === 'paragraph' && node.childCount === 0;
// After a list or code block, preserve empty paragraphs as HTML comments
// so markdown-it doesn't merge adjacent lists or collapse spacing
if (listTypes.has(prevType) || prevType === 'codeBlock') {
preserveEmptyParas = true;
}
if (isEmpty && preserveEmptyParas) {
parts.push('<!-- -->');
prevType = node.type.name;
return;
}
preserveEmptyParas = false;
parts.push(serializeNode(node));
prevType = node.type.name;
});
@@ -1488,8 +1497,11 @@
html = html.replace(/<code([^>]*)>\n?/g, '<code$1>');
html = html.replace(/\n<\/code>/g, '</code>');
// Post-process: convert task list items to TipTap format
html = html.replace(/<li><tiptask checked="(true|false)">([\s\S]*?)<\/tiptask><\/li>/gi, (_, checked, content) => {
// Post-process: convert list-separator comments back to empty paragraphs for TipTap
html = html.replace(/<!-- -->/g, '<p></p>');
// Post-process: convert task list items to TipTap format (handle both tight and loose lists — loose lists wrap content in <p> tags)
html = html.replace(/<li>\s*(?:<p>)?\s*<tiptask checked="(true|false)">([\s\S]*?)<\/tiptask>\s*(?:<\/p>)?\s*<\/li>/gi, (_, checked, content) => {
return `<li data-type="taskItem" data-checked="${checked}">${content}</li>`;
});
html = html.replace(/<ul>\s*(<li data-type="taskItem")/gi, '<ul data-type="taskList">$1');