切换markdown渲染为cm6

This commit is contained in:
2026-07-03 15:06:53 +08:00
parent a37bb0a894
commit 8f95ea3d39
8 changed files with 917 additions and 688 deletions

View File

@@ -16,5 +16,8 @@
"topic_20260703-022850_50330b4eb93f116d": "auto", "topic_20260703-022850_50330b4eb93f116d": "auto",
"topic_20260703-025626_29ad0ca0ec1414e3": "auto", "topic_20260703-025626_29ad0ca0ec1414e3": "auto",
"topic_20260703-031103_9a945dac33ad9fd9": "auto", "topic_20260703-031103_9a945dac33ad9fd9": "auto",
"topic_20260703-031922_1d2031ca5f2c0dd5": "auto" "topic_20260703-031922_1d2031ca5f2c0dd5": "auto",
"topic_20260703-035328_e939286e13cc8f1b": "auto",
"topic_20260703-060642_54474cc49e29104f": "auto",
"topic_20260703-065009_d937c6f780ebb5de": "auto"
} }

View File

@@ -16,5 +16,8 @@
"topic_20260703-022850_50330b4eb93f116d": "侧边栏展开 的时候在131行的hea…", "topic_20260703-022850_50330b4eb93f116d": "侧边栏展开 的时候在131行的hea…",
"topic_20260703-025626_29ad0ca0ec1414e3": "第174行用户选中的工作空间目录没…", "topic_20260703-025626_29ad0ca0ec1414e3": "第174行用户选中的工作空间目录没…",
"topic_20260703-031103_9a945dac33ad9fd9": "添加一个重要的更新功能,现在侧边栏在…", "topic_20260703-031103_9a945dac33ad9fd9": "添加一个重要的更新功能,现在侧边栏在…",
"topic_20260703-031922_1d2031ca5f2c0dd5": "我希望第126-228行的内容在用…" "topic_20260703-031922_1d2031ca5f2c0dd5": "我希望第126-228行的内容在用…",
"topic_20260703-035328_e939286e13cc8f1b": "我们把整个右侧区域都给了markdo…",
"topic_20260703-060642_54474cc49e29104f": "关于这个核心编辑区域,我需要实现一个…",
"topic_20260703-065009_d937c6f780ebb5de": "在功能上有问题,现在用户输入的内容删…"
} }

View File

@@ -10,14 +10,15 @@
"tauri": "tauri" "tauri": "tauri"
}, },
"dependencies": { "dependencies": {
"@codemirror/commands": "^6.10.4",
"@codemirror/lang-markdown": "^6.5.0",
"@codemirror/language": "^6.12.4",
"@codemirror/state": "^6.7.0",
"@codemirror/view": "^6.43.4",
"@tauri-apps/api": "^2", "@tauri-apps/api": "^2",
"@tauri-apps/plugin-dialog": "^2.7.1", "@tauri-apps/plugin-dialog": "^2.7.1",
"@tauri-apps/plugin-opener": "^2", "@tauri-apps/plugin-opener": "^2",
"@tiptap/extension-placeholder": "^3.27.1", "codemirror": "^6.0.2",
"@tiptap/markdown": "^3.27.1",
"@tiptap/pm": "^3.27.1",
"@tiptap/starter-kit": "^3.27.1",
"@tiptap/vue-3": "^3.27.1",
"remixicon": "^4.9.1", "remixicon": "^4.9.1",
"vue": "^3.5.13" "vue": "^3.5.13"
}, },

722
pnpm-lock.yaml generated
View File

@@ -8,6 +8,21 @@ importers:
.: .:
dependencies: dependencies:
'@codemirror/commands':
specifier: ^6.10.4
version: 6.10.4
'@codemirror/lang-markdown':
specifier: ^6.5.0
version: 6.5.0
'@codemirror/language':
specifier: ^6.12.4
version: 6.12.4
'@codemirror/state':
specifier: ^6.7.0
version: 6.7.0
'@codemirror/view':
specifier: ^6.43.4
version: 6.43.4
'@tauri-apps/api': '@tauri-apps/api':
specifier: ^2 specifier: ^2
version: 2.11.1 version: 2.11.1
@@ -17,21 +32,9 @@ importers:
'@tauri-apps/plugin-opener': '@tauri-apps/plugin-opener':
specifier: ^2 specifier: ^2
version: 2.5.4 version: 2.5.4
'@tiptap/extension-placeholder': codemirror:
specifier: ^3.27.1 specifier: ^6.0.2
version: 3.27.1(@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)) version: 6.0.2
'@tiptap/markdown':
specifier: ^3.27.1
version: 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)
'@tiptap/pm':
specifier: ^3.27.1
version: 3.27.1
'@tiptap/starter-kit':
specifier: ^3.27.1
version: 3.27.1
'@tiptap/vue-3':
specifier: ^3.27.1
version: 3.27.1(@floating-ui/dom@1.7.6)(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)(vue@3.5.39(typescript@5.6.3))
remixicon: remixicon:
specifier: ^4.9.1 specifier: ^4.9.1
version: 4.9.1 version: 4.9.1
@@ -80,6 +83,39 @@ packages:
resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@codemirror/autocomplete@6.20.3':
resolution: {integrity: sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==}
'@codemirror/commands@6.10.4':
resolution: {integrity: sha512-Ryk9y9T0FFVF0cUGhAknveAyUOl/A1qReTFi+qPKtOh2Z9F4AUBz3XOrYD4ZEgZirdugVzHvd/2/Wcwy5OliTg==}
'@codemirror/lang-css@6.3.1':
resolution: {integrity: sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==}
'@codemirror/lang-html@6.4.11':
resolution: {integrity: sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==}
'@codemirror/lang-javascript@6.2.5':
resolution: {integrity: sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==}
'@codemirror/lang-markdown@6.5.0':
resolution: {integrity: sha512-0K40bZ35jpHya6FriukbgaleaqzBLZfOh7HuzqbMxBXkbYMJDxfF39c23xOgxFezR+3G+tR2/Mup+Xk865OMvw==}
'@codemirror/language@6.12.4':
resolution: {integrity: sha512-1q4PaT+o6PbgpkJt4Q8Fv5XJxTy4FUZ4MWETtyiDw3J0Pyr9E2vqcKL+k9wcvjNTIsauxvE7OfmWj3FRPHQ76A==}
'@codemirror/lint@6.9.7':
resolution: {integrity: sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg==}
'@codemirror/search@6.7.1':
resolution: {integrity: sha512-uMe5UO6PamJtSHrXhhHOzSX3ReWtiJrva6GnPMwSOrZtiExb5X5eExhr2OUZQVvdxPsKpY3Ro2mFbQadpPWmHA==}
'@codemirror/state@6.7.0':
resolution: {integrity: sha512-Zbl9NyscLMZkfXPQnNAIIAFftidrA1UbcJEIMp24C0Bukc2I5T8wJS0wsXYsnDOqCFJUeJ1BITGNs5CqPDSmSg==}
'@codemirror/view@6.43.4':
resolution: {integrity: sha512-YImu23iyKfncJzT7sRy+rEqEhSc8RhOHqDxwy4WzXRKJwYm6iwf/9OJk5ctCAdZ6yi2ZqaGEvmf55fSVqMDrgg==}
'@esbuild/aix-ppc64@0.25.12': '@esbuild/aix-ppc64@0.25.12':
resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==}
engines: {node: '>=18'} engines: {node: '>=18'}
@@ -236,15 +272,6 @@ packages:
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
'@floating-ui/core@1.7.5':
resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==}
'@floating-ui/dom@1.7.6':
resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==}
'@floating-ui/utils@0.2.11':
resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==}
'@jridgewell/gen-mapping@0.3.13': '@jridgewell/gen-mapping@0.3.13':
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
@@ -261,6 +288,30 @@ packages:
'@jridgewell/trace-mapping@0.3.31': '@jridgewell/trace-mapping@0.3.31':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
'@lezer/common@1.5.2':
resolution: {integrity: sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==}
'@lezer/css@1.3.4':
resolution: {integrity: sha512-N+tn9tej2hPvyKgHEApMOQfHczDJCwxrRFS3SPn9QjYN+uwHvEDnCgKRrb3mxDYxRS8sKMM8fhC3+lc04Abz5Q==}
'@lezer/highlight@1.2.3':
resolution: {integrity: sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==}
'@lezer/html@1.3.13':
resolution: {integrity: sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==}
'@lezer/javascript@1.5.4':
resolution: {integrity: sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==}
'@lezer/lr@1.4.10':
resolution: {integrity: sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==}
'@lezer/markdown@1.6.4':
resolution: {integrity: sha512-N0SxazMj4k65DBfaf1azqtMZd6u7MqluP84/NZnB/io8Td9aleFmAhz9hcbvSfsxT5tdYlJ5qgv5aMJGY4zEtA==}
'@marijn/find-cluster-break@1.0.3':
resolution: {integrity: sha512-FY+MKLBoTsLNJF/eLWaOsXGdz6uh3Iu1axjPf6TUq92IYumcTcXWHoS747JARLkcdlJ/Waiaxc5wQfFO8jC6NA==}
'@rollup/rollup-android-arm-eabi@4.62.2': '@rollup/rollup-android-arm-eabi@4.62.2':
resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==} resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==}
cpu: [arm] cpu: [arm]
@@ -578,164 +629,6 @@ packages:
'@tauri-apps/plugin-opener@2.5.4': '@tauri-apps/plugin-opener@2.5.4':
resolution: {integrity: sha512-1HnPkb+AmgO29HBazm4uPLKB+r7zzcTBW1d0fyYp1uP+jwtpoiNDGKMMzz58SFp49nOIrxdE3aUJtT57lfO9CQ==} resolution: {integrity: sha512-1HnPkb+AmgO29HBazm4uPLKB+r7zzcTBW1d0fyYp1uP+jwtpoiNDGKMMzz58SFp49nOIrxdE3aUJtT57lfO9CQ==}
'@tiptap/core@3.27.1':
resolution: {integrity: sha512-rV6Qn4wmC6BxfF+4mu6bqGWj9vA4oXXhsrpXaJL2uhjxeHAGofjwcHof2X84VYzeyXgdlsGmqKie4TAppVXZUQ==}
peerDependencies:
'@tiptap/pm': 3.27.1
'@tiptap/extension-blockquote@3.27.1':
resolution: {integrity: sha512-VMF7xJx6qEGiX6DTKNiL31NLqypOcd/4sNjFSe8rb41PwejBJh/nOqVIbBvWkiT6NMGFLxMhj7zJ8/zPo1hXeg==}
peerDependencies:
'@tiptap/core': 3.27.1
'@tiptap/extension-bold@3.27.1':
resolution: {integrity: sha512-TlC5bsS+pqETTrlz4CZz9RO/cKBYtELGIxwtKeivUn3eNfnOxQbbu4WDsiwIfzRFyd0OMnKl6BPM2KnYEehoEQ==}
peerDependencies:
'@tiptap/core': 3.27.1
'@tiptap/extension-bubble-menu@3.27.1':
resolution: {integrity: sha512-j/j8Qp9Z5nViade2m7zjrO/CYH/Ca80Qj7aqo0eUaei6FZQ5izlF9o4XQU5EFMAutV6mwynsPUp8FVo5sCuYfw==}
peerDependencies:
'@tiptap/core': 3.27.1
'@tiptap/pm': 3.27.1
'@tiptap/extension-bullet-list@3.27.1':
resolution: {integrity: sha512-faCUHnRP47o9Zh9VZZX6EX/569udw9Vopm2PgEKPWuKLE2qaS5WBuUVU0iItdJmKUqaWiOZkpoW4jvnDmj0dfg==}
peerDependencies:
'@tiptap/extension-list': 3.27.1
'@tiptap/extension-code-block@3.27.1':
resolution: {integrity: sha512-pHlzmZx2OlHfyQ0yRlT5UL4mGokz947DthZuYefN1OleVqOkHpWBG+2JQwqoNq6bmzMne92zbH32rhcJUEYSjA==}
peerDependencies:
'@tiptap/core': 3.27.1
'@tiptap/pm': 3.27.1
'@tiptap/extension-code@3.27.1':
resolution: {integrity: sha512-epOUpFfEmBzjvnqvjv2qHX7NAuLo5dlOGV690lWu+sAYMjibuJBeVvAiKPyFCfRCCTUxdbDB3jbaOA1yEcEJ7w==}
peerDependencies:
'@tiptap/core': 3.27.1
'@tiptap/extension-document@3.27.1':
resolution: {integrity: sha512-8FbBTkfnRP4iVaoj+2h3iWa+H0eGDD3yTyVCwrmue/sQTkqUNUoSuAZa3GDG4Sd41xdPwTJxl9nUWGgM1qDCnw==}
peerDependencies:
'@tiptap/core': 3.27.1
'@tiptap/extension-dropcursor@3.27.1':
resolution: {integrity: sha512-blFf9x9RG0Qr7P3FoAH/033ffa+mMLZn34trVs8Vi0Ppk6FmJAg5HpYFOtmYoeREdNDJ5rHJKV7SoACbOHgskQ==}
peerDependencies:
'@tiptap/extensions': 3.27.1
'@tiptap/extension-floating-menu@3.27.1':
resolution: {integrity: sha512-BmJF1VqB7dSJkgAalrpVFj88WLhxKjcWPuWHOqf2ITrUU2832BhKLXKmxjWUy1gqV8PfNNVWtGfIERy7I0y0+Q==}
peerDependencies:
'@floating-ui/dom': ^1.0.0
'@tiptap/core': 3.27.1
'@tiptap/pm': 3.27.1
'@tiptap/extension-gapcursor@3.27.1':
resolution: {integrity: sha512-QoezN0wdvXIwLQ4ee2ccWDaX3RG0lzgQpIMpMz55oPDhpUVax1+19ApsS53LkcktpS4EbnPL4xO4DaJk0Vp7PQ==}
peerDependencies:
'@tiptap/extensions': 3.27.1
'@tiptap/extension-hard-break@3.27.1':
resolution: {integrity: sha512-iv/m9hzl6jfSj9Q8UEjAxONvCoUDaP7M9SRCPx3PaLNxA230TTD6RE0Ye4zFJ8ze7ZVoJJMAqg9Qpq1iYg2JOQ==}
peerDependencies:
'@tiptap/core': 3.27.1
'@tiptap/extension-heading@3.27.1':
resolution: {integrity: sha512-SrC4l1kEIyv9ZXFaI/8LQqU2MyMmjczw7XXsWUQOTN4YXv0JyVgMNR3cI/wz0d2xsTfBdZ1N85Tdng+Ga1t0Sg==}
peerDependencies:
'@tiptap/core': 3.27.1
'@tiptap/extension-horizontal-rule@3.27.1':
resolution: {integrity: sha512-QlKE7qn5qMnIGVGhXQlvYedvLtNJ9z0dmit5w8vPb8tKzW4Spk6M7N2kruprrDA8GBwHfeR5wmF+njfUm34qxg==}
peerDependencies:
'@tiptap/core': 3.27.1
'@tiptap/pm': 3.27.1
'@tiptap/extension-italic@3.27.1':
resolution: {integrity: sha512-jGGeyn9uRUnNjSTHpbqhiGsp6KaYTSbV09jDXPJI9cDwfV9hpugLvpaCZd0BMBbhU1B1W6kOfX0BE15qX/HQfA==}
peerDependencies:
'@tiptap/core': 3.27.1
'@tiptap/extension-link@3.27.1':
resolution: {integrity: sha512-/2jBfsxBZUDGJmpZifqRQPz7f1E5qpS1BckTZ39TADzUJX+feKy7RJ3DtQ02+8y6SSMzvP9loGVjrk6zEMTk4g==}
peerDependencies:
'@tiptap/core': 3.27.1
'@tiptap/pm': 3.27.1
'@tiptap/extension-list-item@3.27.1':
resolution: {integrity: sha512-zwRl01ETfCkWUvtvK5fw9bXtAajMPkvlkE3Cq6JvH3LF7XXJwDtNj5Tj7exacMpCaSZmlNc43vFb2rAYnrnwMA==}
peerDependencies:
'@tiptap/extension-list': 3.27.1
'@tiptap/extension-list-keymap@3.27.1':
resolution: {integrity: sha512-OIMZNlzPSO8WRd4ic73Fxckzl4N1tesjjLL2XApaNA/uMpO0LoF6WSRPAWv+Z24Wp92ARRJAnRP7iZoI5+Jxig==}
peerDependencies:
'@tiptap/extension-list': 3.27.1
'@tiptap/extension-list@3.27.1':
resolution: {integrity: sha512-c2Upru7lj0/ZV/Ibww6cNz6sUS8m6Dp/9uygFhYcZOd3X8M0xBIEk42c6m6SQehkPziVA8QOgNJz7sMqsbz1OQ==}
peerDependencies:
'@tiptap/core': 3.27.1
'@tiptap/pm': 3.27.1
'@tiptap/extension-ordered-list@3.27.1':
resolution: {integrity: sha512-GYrKqD//9nHJ2r80uXqbDMzRnFpGzbaEQRTSGaO/SH7DvXWFMow8evkOdjQ7PCQO07jNjJo75+A85Jwu3Ov3AA==}
peerDependencies:
'@tiptap/extension-list': 3.27.1
'@tiptap/extension-paragraph@3.27.1':
resolution: {integrity: sha512-7K7eo1gruOgAsnbK+GCV23AUVUI0cL1bTig8HaPneoFMVbig7vddk8jNLKBWO8TXVbG7TuHdnDN4F98vdtwh5Q==}
peerDependencies:
'@tiptap/core': 3.27.1
'@tiptap/extension-placeholder@3.27.1':
resolution: {integrity: sha512-lhcNDcczQ75yJOSywCHb58Hmtg1aPL/2TdYmeLPxVrP048D7rRs133sfONcgyyw0AvhnfmPOkHLTv3QtKSowhw==}
peerDependencies:
'@tiptap/extensions': 3.27.1
'@tiptap/extension-strike@3.27.1':
resolution: {integrity: sha512-Y3DW1jlSlCNCyMGHP3+3qBNNPS83wuFz4RTYGjZtvRRTCRh7apZme9XRWMq1rN5mJ2Cr7fKocA2/5Bs13KgN6Q==}
peerDependencies:
'@tiptap/core': 3.27.1
'@tiptap/extension-text@3.27.1':
resolution: {integrity: sha512-6ZwaZwSrDh+KFFv6V1J79oO37yPs7y1bFxvk1/9Ih2rn3Xr5AWz+eMS+n8RpH3djBVVAQpdIAeYQgcn+VCSsTg==}
peerDependencies:
'@tiptap/core': 3.27.1
'@tiptap/extension-underline@3.27.1':
resolution: {integrity: sha512-N889J4nXN/TPfVt8uF9N1A0SY82E90zwc1y26lqOcw6KWNLmQrlhMh/9OD4ikLDbekmFpOBq/UicpHf/6S8hbQ==}
peerDependencies:
'@tiptap/core': 3.27.1
'@tiptap/extensions@3.27.1':
resolution: {integrity: sha512-1Tdx9faw8k0/83V6X+xCDVhV8yElGt95JxeW3YMkKQJI56QdlPz0xOdJPlMiSGJKinPyVier+x9LJD/YZUZIaw==}
peerDependencies:
'@tiptap/core': 3.27.1
'@tiptap/pm': 3.27.1
'@tiptap/markdown@3.27.1':
resolution: {integrity: sha512-4m2LZcj/uN0uLlGnXr3i7sfdxbQNNmeMn4wFyFrP2xpshcKPL5WujUAcqT2rgKfaDjKQ8gIK/GpgSQKuRENFwg==}
peerDependencies:
'@tiptap/core': 3.27.1
'@tiptap/pm': 3.27.1
'@tiptap/pm@3.27.1':
resolution: {integrity: sha512-Ffjx+vimmBU7zH/KrpXzJid3+pziCe/VL2aexSTP63cyQwKQ65LkFkCKaIsSpFdQQuakVZBGWjCA5RoBV852pw==}
'@tiptap/starter-kit@3.27.1':
resolution: {integrity: sha512-vfxRsqW8rCc0k4pzo0ilU3wobVi2wqVj88VZI2SlgZlNnUAkrDGDIAph7CTa9k9fshV+O1ivpEgPC5yC046jow==}
'@tiptap/vue-3@3.27.1':
resolution: {integrity: sha512-o5GB6hfUnyf9sCB306rHWmaIYRL+02ROX657EkuY8tEWKHMTuMjHWl2AqHMP47wz0W9DaMOJLvcPpYdAEKq3Mw==}
peerDependencies:
'@floating-ui/dom': ^1.0.0
'@tiptap/core': 3.27.1
'@tiptap/pm': 3.27.1
vue: ^3.0.0
'@types/estree@1.0.9': '@types/estree@1.0.9':
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
@@ -804,6 +697,12 @@ packages:
brace-expansion@2.1.1: brace-expansion@2.1.1:
resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==}
codemirror@6.0.2:
resolution: {integrity: sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==}
crelt@1.0.7:
resolution: {integrity: sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA==}
csstype@3.2.3: csstype@3.2.3:
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
@@ -929,17 +828,9 @@ packages:
resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
linkifyjs@4.3.3:
resolution: {integrity: sha512-P8aEP5U/D1/IlTY2OeYsErdwh9bGuLE30NcXtKEjgdHcahveQoQwM2yZNsioQHsWFz0P7KKudisbrzCgR0sDHg==}
magic-string@0.30.21: magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
marked@17.0.6:
resolution: {integrity: sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA==}
engines: {node: '>= 20'}
hasBin: true
minimatch@9.0.9: minimatch@9.0.9:
resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==}
engines: {node: '>=16 || 14 >=14.17'} engines: {node: '>=16 || 14 >=14.17'}
@@ -952,9 +843,6 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true hasBin: true
orderedmap@2.1.1:
resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==}
path-browserify@1.0.1: path-browserify@1.0.1:
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
@@ -969,45 +857,6 @@ packages:
resolution: {integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==} resolution: {integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==}
engines: {node: ^10 || ^12 || >=14} engines: {node: ^10 || ^12 || >=14}
prosemirror-changeset@2.4.1:
resolution: {integrity: sha512-96WBLhOaYhJ+kPhLg3uW359Tz6I/MfcrQfL4EGv4SrcqKEMC1gmoGrXHecPE8eOwTVCJ4IwgfzM8fFad25wNfw==}
prosemirror-commands@1.7.1:
resolution: {integrity: sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==}
prosemirror-dropcursor@1.8.2:
resolution: {integrity: sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==}
prosemirror-gapcursor@1.4.1:
resolution: {integrity: sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw==}
prosemirror-history@1.5.0:
resolution: {integrity: sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==}
prosemirror-inputrules@1.5.1:
resolution: {integrity: sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==}
prosemirror-keymap@1.2.3:
resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==}
prosemirror-model@1.25.9:
resolution: {integrity: sha512-pRTklkDDMMRopyoAcrr9wV/8g/RYgrLHBuJAb5hlEuYZRdm5yqmPjWId83fpBwPpSFqEdja0H7Dfd7z1X/npcA==}
prosemirror-schema-list@1.5.1:
resolution: {integrity: sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==}
prosemirror-state@1.4.4:
resolution: {integrity: sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==}
prosemirror-tables@1.8.5:
resolution: {integrity: sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==}
prosemirror-transform@1.12.0:
resolution: {integrity: sha512-GxboyN4AMIsoHNtz5uf2r2Ru551i5hWeCMD6E2Ib4Eogqoub0NflniaBPVQ4MrGE5yZ8JV9tUHg9qcZTTrcN4w==}
prosemirror-view@1.41.9:
resolution: {integrity: sha512-clTunTX+eaLbr87L1V1QPheRlEQJyTlL3gXe9x3jQIk3rL0RVWxviDGz8tFaydwIVm+hKhYCyr+R/zBtWr9s6A==}
remixicon@4.9.1: remixicon@4.9.1:
resolution: {integrity: sha512-36gLSoujkabnCFZFDyP17VNh9piuBA/rsXUb4auSJWLGsHVXtmxLj/EM5FjaEAGnk8oIAj1Azob/DZ2N+90lAQ==} resolution: {integrity: sha512-36gLSoujkabnCFZFDyP17VNh9piuBA/rsXUb4auSJWLGsHVXtmxLj/EM5FjaEAGnk8oIAj1Azob/DZ2N+90lAQ==}
@@ -1016,13 +865,13 @@ packages:
engines: {node: '>=18.0.0', npm: '>=8.0.0'} engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true hasBin: true
rope-sequence@1.3.4:
resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==}
source-map-js@1.2.1: source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
style-mod@4.1.3:
resolution: {integrity: sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==}
tailwindcss@4.3.2: tailwindcss@4.3.2:
resolution: {integrity: sha512-WtctNNSH8A9jlMIqxzuYumOHU5uGZyRv0Q5svQl+oEPy5w84YpBxdb7MdqyiSPQge5jTJ6zFQLq0PFygdccSBA==} resolution: {integrity: sha512-WtctNNSH8A9jlMIqxzuYumOHU5uGZyRv0Q5svQl+oEPy5w84YpBxdb7MdqyiSPQge5jTJ6zFQLq0PFygdccSBA==}
@@ -1114,6 +963,92 @@ snapshots:
'@babel/helper-string-parser': 7.29.7 '@babel/helper-string-parser': 7.29.7
'@babel/helper-validator-identifier': 7.29.7 '@babel/helper-validator-identifier': 7.29.7
'@codemirror/autocomplete@6.20.3':
dependencies:
'@codemirror/language': 6.12.4
'@codemirror/state': 6.7.0
'@codemirror/view': 6.43.4
'@lezer/common': 1.5.2
'@codemirror/commands@6.10.4':
dependencies:
'@codemirror/language': 6.12.4
'@codemirror/state': 6.7.0
'@codemirror/view': 6.43.4
'@lezer/common': 1.5.2
'@codemirror/lang-css@6.3.1':
dependencies:
'@codemirror/autocomplete': 6.20.3
'@codemirror/language': 6.12.4
'@codemirror/state': 6.7.0
'@lezer/common': 1.5.2
'@lezer/css': 1.3.4
'@codemirror/lang-html@6.4.11':
dependencies:
'@codemirror/autocomplete': 6.20.3
'@codemirror/lang-css': 6.3.1
'@codemirror/lang-javascript': 6.2.5
'@codemirror/language': 6.12.4
'@codemirror/state': 6.7.0
'@codemirror/view': 6.43.4
'@lezer/common': 1.5.2
'@lezer/css': 1.3.4
'@lezer/html': 1.3.13
'@codemirror/lang-javascript@6.2.5':
dependencies:
'@codemirror/autocomplete': 6.20.3
'@codemirror/language': 6.12.4
'@codemirror/lint': 6.9.7
'@codemirror/state': 6.7.0
'@codemirror/view': 6.43.4
'@lezer/common': 1.5.2
'@lezer/javascript': 1.5.4
'@codemirror/lang-markdown@6.5.0':
dependencies:
'@codemirror/autocomplete': 6.20.3
'@codemirror/lang-html': 6.4.11
'@codemirror/language': 6.12.4
'@codemirror/state': 6.7.0
'@codemirror/view': 6.43.4
'@lezer/common': 1.5.2
'@lezer/markdown': 1.6.4
'@codemirror/language@6.12.4':
dependencies:
'@codemirror/state': 6.7.0
'@codemirror/view': 6.43.4
'@lezer/common': 1.5.2
'@lezer/highlight': 1.2.3
'@lezer/lr': 1.4.10
style-mod: 4.1.3
'@codemirror/lint@6.9.7':
dependencies:
'@codemirror/state': 6.7.0
'@codemirror/view': 6.43.4
crelt: 1.0.7
'@codemirror/search@6.7.1':
dependencies:
'@codemirror/state': 6.7.0
'@codemirror/view': 6.43.4
crelt: 1.0.7
'@codemirror/state@6.7.0':
dependencies:
'@marijn/find-cluster-break': 1.0.3
'@codemirror/view@6.43.4':
dependencies:
'@codemirror/state': 6.7.0
crelt: 1.0.7
style-mod: 4.1.3
w3c-keyname: 2.2.8
'@esbuild/aix-ppc64@0.25.12': '@esbuild/aix-ppc64@0.25.12':
optional: true optional: true
@@ -1192,17 +1127,6 @@ snapshots:
'@esbuild/win32-x64@0.25.12': '@esbuild/win32-x64@0.25.12':
optional: true optional: true
'@floating-ui/core@1.7.5':
dependencies:
'@floating-ui/utils': 0.2.11
'@floating-ui/dom@1.7.6':
dependencies:
'@floating-ui/core': 1.7.5
'@floating-ui/utils': 0.2.11
'@floating-ui/utils@0.2.11': {}
'@jridgewell/gen-mapping@0.3.13': '@jridgewell/gen-mapping@0.3.13':
dependencies: dependencies:
'@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/sourcemap-codec': 1.5.5
@@ -1222,6 +1146,41 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2 '@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/sourcemap-codec': 1.5.5
'@lezer/common@1.5.2': {}
'@lezer/css@1.3.4':
dependencies:
'@lezer/common': 1.5.2
'@lezer/highlight': 1.2.3
'@lezer/lr': 1.4.10
'@lezer/highlight@1.2.3':
dependencies:
'@lezer/common': 1.5.2
'@lezer/html@1.3.13':
dependencies:
'@lezer/common': 1.5.2
'@lezer/highlight': 1.2.3
'@lezer/lr': 1.4.10
'@lezer/javascript@1.5.4':
dependencies:
'@lezer/common': 1.5.2
'@lezer/highlight': 1.2.3
'@lezer/lr': 1.4.10
'@lezer/lr@1.4.10':
dependencies:
'@lezer/common': 1.5.2
'@lezer/markdown@1.6.4':
dependencies:
'@lezer/common': 1.5.2
'@lezer/highlight': 1.2.3
'@marijn/find-cluster-break@1.0.3': {}
'@rollup/rollup-android-arm-eabi@4.62.2': '@rollup/rollup-android-arm-eabi@4.62.2':
optional: true optional: true
@@ -1422,181 +1381,6 @@ snapshots:
dependencies: dependencies:
'@tauri-apps/api': 2.11.1 '@tauri-apps/api': 2.11.1
'@tiptap/core@3.27.1(@tiptap/pm@3.27.1)':
dependencies:
'@tiptap/pm': 3.27.1
'@tiptap/extension-blockquote@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))':
dependencies:
'@tiptap/core': 3.27.1(@tiptap/pm@3.27.1)
'@tiptap/extension-bold@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))':
dependencies:
'@tiptap/core': 3.27.1(@tiptap/pm@3.27.1)
'@tiptap/extension-bubble-menu@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)':
dependencies:
'@floating-ui/dom': 1.7.6
'@tiptap/core': 3.27.1(@tiptap/pm@3.27.1)
'@tiptap/pm': 3.27.1
optional: true
'@tiptap/extension-bullet-list@3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))':
dependencies:
'@tiptap/extension-list': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)
'@tiptap/extension-code-block@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)':
dependencies:
'@tiptap/core': 3.27.1(@tiptap/pm@3.27.1)
'@tiptap/pm': 3.27.1
'@tiptap/extension-code@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))':
dependencies:
'@tiptap/core': 3.27.1(@tiptap/pm@3.27.1)
'@tiptap/extension-document@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))':
dependencies:
'@tiptap/core': 3.27.1(@tiptap/pm@3.27.1)
'@tiptap/extension-dropcursor@3.27.1(@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))':
dependencies:
'@tiptap/extensions': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)
'@tiptap/extension-floating-menu@3.27.1(@floating-ui/dom@1.7.6)(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)':
dependencies:
'@floating-ui/dom': 1.7.6
'@tiptap/core': 3.27.1(@tiptap/pm@3.27.1)
'@tiptap/pm': 3.27.1
optional: true
'@tiptap/extension-gapcursor@3.27.1(@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))':
dependencies:
'@tiptap/extensions': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)
'@tiptap/extension-hard-break@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))':
dependencies:
'@tiptap/core': 3.27.1(@tiptap/pm@3.27.1)
'@tiptap/extension-heading@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))':
dependencies:
'@tiptap/core': 3.27.1(@tiptap/pm@3.27.1)
'@tiptap/extension-horizontal-rule@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)':
dependencies:
'@tiptap/core': 3.27.1(@tiptap/pm@3.27.1)
'@tiptap/pm': 3.27.1
'@tiptap/extension-italic@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))':
dependencies:
'@tiptap/core': 3.27.1(@tiptap/pm@3.27.1)
'@tiptap/extension-link@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)':
dependencies:
'@tiptap/core': 3.27.1(@tiptap/pm@3.27.1)
'@tiptap/pm': 3.27.1
linkifyjs: 4.3.3
'@tiptap/extension-list-item@3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))':
dependencies:
'@tiptap/extension-list': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)
'@tiptap/extension-list-keymap@3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))':
dependencies:
'@tiptap/extension-list': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)
'@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)':
dependencies:
'@tiptap/core': 3.27.1(@tiptap/pm@3.27.1)
'@tiptap/pm': 3.27.1
'@tiptap/extension-ordered-list@3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))':
dependencies:
'@tiptap/extension-list': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)
'@tiptap/extension-paragraph@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))':
dependencies:
'@tiptap/core': 3.27.1(@tiptap/pm@3.27.1)
'@tiptap/extension-placeholder@3.27.1(@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))':
dependencies:
'@tiptap/extensions': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)
'@tiptap/extension-strike@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))':
dependencies:
'@tiptap/core': 3.27.1(@tiptap/pm@3.27.1)
'@tiptap/extension-text@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))':
dependencies:
'@tiptap/core': 3.27.1(@tiptap/pm@3.27.1)
'@tiptap/extension-underline@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))':
dependencies:
'@tiptap/core': 3.27.1(@tiptap/pm@3.27.1)
'@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)':
dependencies:
'@tiptap/core': 3.27.1(@tiptap/pm@3.27.1)
'@tiptap/pm': 3.27.1
'@tiptap/markdown@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)':
dependencies:
'@tiptap/core': 3.27.1(@tiptap/pm@3.27.1)
'@tiptap/pm': 3.27.1
marked: 17.0.6
'@tiptap/pm@3.27.1':
dependencies:
prosemirror-changeset: 2.4.1
prosemirror-commands: 1.7.1
prosemirror-dropcursor: 1.8.2
prosemirror-gapcursor: 1.4.1
prosemirror-history: 1.5.0
prosemirror-inputrules: 1.5.1
prosemirror-keymap: 1.2.3
prosemirror-model: 1.25.9
prosemirror-schema-list: 1.5.1
prosemirror-state: 1.4.4
prosemirror-tables: 1.8.5
prosemirror-transform: 1.12.0
prosemirror-view: 1.41.9
'@tiptap/starter-kit@3.27.1':
dependencies:
'@tiptap/core': 3.27.1(@tiptap/pm@3.27.1)
'@tiptap/extension-blockquote': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))
'@tiptap/extension-bold': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))
'@tiptap/extension-bullet-list': 3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))
'@tiptap/extension-code': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))
'@tiptap/extension-code-block': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)
'@tiptap/extension-document': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))
'@tiptap/extension-dropcursor': 3.27.1(@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))
'@tiptap/extension-gapcursor': 3.27.1(@tiptap/extensions@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))
'@tiptap/extension-hard-break': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))
'@tiptap/extension-heading': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))
'@tiptap/extension-horizontal-rule': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)
'@tiptap/extension-italic': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))
'@tiptap/extension-link': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)
'@tiptap/extension-list': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)
'@tiptap/extension-list-item': 3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))
'@tiptap/extension-list-keymap': 3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))
'@tiptap/extension-ordered-list': 3.27.1(@tiptap/extension-list@3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1))
'@tiptap/extension-paragraph': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))
'@tiptap/extension-strike': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))
'@tiptap/extension-text': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))
'@tiptap/extension-underline': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))
'@tiptap/extensions': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)
'@tiptap/pm': 3.27.1
'@tiptap/vue-3@3.27.1(@floating-ui/dom@1.7.6)(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)(vue@3.5.39(typescript@5.6.3))':
dependencies:
'@floating-ui/dom': 1.7.6
'@tiptap/core': 3.27.1(@tiptap/pm@3.27.1)
'@tiptap/pm': 3.27.1
vue: 3.5.39(typescript@5.6.3)
optionalDependencies:
'@tiptap/extension-bubble-menu': 3.27.1(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)
'@tiptap/extension-floating-menu': 3.27.1(@floating-ui/dom@1.7.6)(@tiptap/core@3.27.1(@tiptap/pm@3.27.1))(@tiptap/pm@3.27.1)
'@types/estree@1.0.9': {} '@types/estree@1.0.9': {}
'@vitejs/plugin-vue@5.2.4(vite@6.4.3(jiti@2.7.0)(lightningcss@1.32.0))(vue@3.5.39(typescript@5.6.3))': '@vitejs/plugin-vue@5.2.4(vite@6.4.3(jiti@2.7.0)(lightningcss@1.32.0))(vue@3.5.39(typescript@5.6.3))':
@@ -1696,6 +1480,18 @@ snapshots:
dependencies: dependencies:
balanced-match: 1.0.2 balanced-match: 1.0.2
codemirror@6.0.2:
dependencies:
'@codemirror/autocomplete': 6.20.3
'@codemirror/commands': 6.10.4
'@codemirror/language': 6.12.4
'@codemirror/lint': 6.9.7
'@codemirror/search': 6.7.1
'@codemirror/state': 6.7.0
'@codemirror/view': 6.43.4
crelt@1.0.7: {}
csstype@3.2.3: {} csstype@3.2.3: {}
de-indent@1.0.2: {} de-indent@1.0.2: {}
@@ -1802,14 +1598,10 @@ snapshots:
lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-arm64-msvc: 1.32.0
lightningcss-win32-x64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0
linkifyjs@4.3.3: {}
magic-string@0.30.21: magic-string@0.30.21:
dependencies: dependencies:
'@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/sourcemap-codec': 1.5.5
marked@17.0.6: {}
minimatch@9.0.9: minimatch@9.0.9:
dependencies: dependencies:
brace-expansion: 2.1.1 brace-expansion: 2.1.1
@@ -1818,8 +1610,6 @@ snapshots:
nanoid@3.3.15: {} nanoid@3.3.15: {}
orderedmap@2.1.1: {}
path-browserify@1.0.1: {} path-browserify@1.0.1: {}
picocolors@1.1.1: {} picocolors@1.1.1: {}
@@ -1832,80 +1622,6 @@ snapshots:
picocolors: 1.1.1 picocolors: 1.1.1
source-map-js: 1.2.1 source-map-js: 1.2.1
prosemirror-changeset@2.4.1:
dependencies:
prosemirror-transform: 1.12.0
prosemirror-commands@1.7.1:
dependencies:
prosemirror-model: 1.25.9
prosemirror-state: 1.4.4
prosemirror-transform: 1.12.0
prosemirror-dropcursor@1.8.2:
dependencies:
prosemirror-state: 1.4.4
prosemirror-transform: 1.12.0
prosemirror-view: 1.41.9
prosemirror-gapcursor@1.4.1:
dependencies:
prosemirror-keymap: 1.2.3
prosemirror-model: 1.25.9
prosemirror-state: 1.4.4
prosemirror-view: 1.41.9
prosemirror-history@1.5.0:
dependencies:
prosemirror-state: 1.4.4
prosemirror-transform: 1.12.0
prosemirror-view: 1.41.9
rope-sequence: 1.3.4
prosemirror-inputrules@1.5.1:
dependencies:
prosemirror-state: 1.4.4
prosemirror-transform: 1.12.0
prosemirror-keymap@1.2.3:
dependencies:
prosemirror-state: 1.4.4
w3c-keyname: 2.2.8
prosemirror-model@1.25.9:
dependencies:
orderedmap: 2.1.1
prosemirror-schema-list@1.5.1:
dependencies:
prosemirror-model: 1.25.9
prosemirror-state: 1.4.4
prosemirror-transform: 1.12.0
prosemirror-state@1.4.4:
dependencies:
prosemirror-model: 1.25.9
prosemirror-transform: 1.12.0
prosemirror-view: 1.41.9
prosemirror-tables@1.8.5:
dependencies:
prosemirror-keymap: 1.2.3
prosemirror-model: 1.25.9
prosemirror-state: 1.4.4
prosemirror-transform: 1.12.0
prosemirror-view: 1.41.9
prosemirror-transform@1.12.0:
dependencies:
prosemirror-model: 1.25.9
prosemirror-view@1.41.9:
dependencies:
prosemirror-model: 1.25.9
prosemirror-state: 1.4.4
prosemirror-transform: 1.12.0
remixicon@4.9.1: {} remixicon@4.9.1: {}
rollup@4.62.2: rollup@4.62.2:
@@ -1939,10 +1655,10 @@ snapshots:
'@rollup/rollup-win32-x64-msvc': 4.62.2 '@rollup/rollup-win32-x64-msvc': 4.62.2
fsevents: 2.3.3 fsevents: 2.3.3
rope-sequence@1.3.4: {}
source-map-js@1.2.1: {} source-map-js@1.2.1: {}
style-mod@4.1.3: {}
tailwindcss@4.3.2: {} tailwindcss@4.3.2: {}
tapable@2.3.3: {} tapable@2.3.3: {}

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue"; import { ref, onMounted, onUnmounted } from "vue";
import { listen } from "@tauri-apps/api/event"; import { listen } from "@tauri-apps/api/event";
import { invoke } from "@tauri-apps/api/core"; import { invoke } from "@tauri-apps/api/core";
import MarkdownEditor from "./components/MarkdownEditor.vue"; import MarkdownEditor from "./components/MarkdownEditor.vue";
@@ -23,6 +23,72 @@ interface DirEntry {
const folderPath = ref(""); const folderPath = ref("");
const dirEntries = ref<DirEntry[]>([]); const dirEntries = ref<DirEntry[]>([]);
// ---- editor zoom (Ctrl+scroll) ----
const ZOOM_MIN = 0.7;
const ZOOM_MAX = 1.5;
const ZOOM_STEP = 0.1;
function loadZoomPref(): number {
try {
const stored = localStorage.getItem("editor-zoom");
if (stored) {
const val = parseFloat(stored);
if (!isNaN(val) && val >= ZOOM_MIN && val <= ZOOM_MAX) return val;
}
} catch { /* localStorage unavailable */ }
return 1.0;
}
const editorZoom = ref(loadZoomPref());
const showZoomIndicator = ref(false);
let zoomIndicatorTimer: ReturnType<typeof setTimeout> | null = null;
function applyZoomCssVars() {
document.documentElement.style.setProperty("--editor-zoom", String(editorZoom.value));
}
function saveZoomPref() {
try { localStorage.setItem("editor-zoom", String(editorZoom.value)); } catch { /* noop */ }
}
function onEditorWheel(e: WheelEvent) {
if (!e.ctrlKey && !e.metaKey) return;
e.preventDefault();
const delta = e.deltaY < 0 ? ZOOM_STEP : -ZOOM_STEP;
const next = Math.round((editorZoom.value + delta) * 10) / 10;
editorZoom.value = Math.min(ZOOM_MAX, Math.max(ZOOM_MIN, next));
applyZoomCssVars();
saveZoomPref();
// Show zoom indicator and reset timer
showZoomIndicator.value = true;
if (zoomIndicatorTimer) clearTimeout(zoomIndicatorTimer);
zoomIndicatorTimer = setTimeout(() => { showZoomIndicator.value = false; }, 1500);
}
// ---- full-width mode ----
function loadFullWidthPref(): boolean {
try {
return localStorage.getItem("editor-full-width") === "true";
} catch { return false; }
}
const isFullWidth = ref(loadFullWidthPref());
function onToggleFullWidth(val: boolean) {
isFullWidth.value = val;
try { localStorage.setItem("editor-full-width", String(val)); } catch { /* noop */ }
}
onMounted(() => {
applyZoomCssVars();
window.addEventListener("wheel", onEditorWheel, { passive: false });
});
onUnmounted(() => {
window.removeEventListener("wheel", onEditorWheel);
if (zoomIndicatorTimer) clearTimeout(zoomIndicatorTimer);
});
function stripMarkdown(md: string): string { function stripMarkdown(md: string): string {
return md return md
// Remove images ![alt](url) // Remove images ![alt](url)
@@ -55,13 +121,11 @@ function onUpdate(md: string) {
} }
function onHeadingClick(heading: { text: string; level: number }) { function onHeadingClick(heading: { text: string; level: number }) {
// Navigate to heading in the editor — find the heading element and scroll it into view // Navigate to heading in the CodeMirror editor
const editorEl = document.querySelector(".tiptap-editor .ProseMirror"); const className = `.cm-lp-h${heading.level}`;
if (!editorEl) return; const headingEls = document.querySelectorAll(className);
for (const el of headingEls) {
const headingTags = editorEl.querySelectorAll("h1, h2, h3"); if (el.textContent?.trim() === heading.text) {
for (const el of headingTags) {
if (el.textContent?.trim() === heading.text && el.tagName === `H${heading.level}`) {
el.scrollIntoView({ behavior: "smooth", block: "start" }); el.scrollIntoView({ behavior: "smooth", block: "start" });
break; break;
} }
@@ -202,21 +266,34 @@ listen("menu-event", (event) => {
:entries="dirEntries" :entries="dirEntries"
:content="content" :content="content"
:current-file-path="currentFilePath" :current-file-path="currentFilePath"
:is-full-width="isFullWidth"
@heading-click="onHeadingClick" @heading-click="onHeadingClick"
@open-folder="onOpenFolder" @open-folder="onOpenFolder"
@toggle-folder="onToggleFolder" @toggle-folder="onToggleFolder"
@open-file="onOpenFile" @open-file="onOpenFile"
@toggle-full-width="onToggleFullWidth"
/> />
<!-- Right: Editor area --> <!-- Right: Editor area -->
<main class="flex-1 flex flex-col overflow-y-auto"> <main class="flex-1 flex flex-col overflow-hidden relative">
<MarkdownEditor <MarkdownEditor
v-model="content" :model-value="content"
:is-full-width="isFullWidth"
placeholder="输入 Markdown 内容... 支持标题、粗体、斜体、代码块等 ✨" placeholder="输入 Markdown 内容... 支持标题、粗体、斜体、代码块等 ✨"
@update:model-value="onUpdate" @update:model-value="onUpdate"
@save-shortcut="doSave" @save-shortcut="doSave"
class="flex-1 w-full" class="flex-1 w-full"
/> />
<!-- Zoom indicator -->
<Transition name="zoom-fade">
<div
v-if="showZoomIndicator"
class="fixed top-4 right-4 z-50 px-2.5 py-1 rounded-md bg-[#38342e]/80 text-white text-xs font-medium pointer-events-none select-none backdrop-blur-sm"
>
{{ Math.round(editorZoom * 100) }}%
</div>
</Transition>
</main> </main>
</div> </div>
<footer class="border-t border-[#e8e4da] bg-[#f4f1eb]/60 backdrop-blur-sm"> <footer class="border-t border-[#e8e4da] bg-[#f4f1eb]/60 backdrop-blur-sm">
@@ -243,4 +320,15 @@ listen("menu-event", (event) => {
</div> </div>
</template> </template>
<style>
.zoom-fade-enter-active,
.zoom-fade-leave-active {
transition: opacity 0.2s ease;
}
.zoom-fade-enter-from,
.zoom-fade-leave-to {
opacity: 0;
}
</style>

View File

@@ -15,10 +15,12 @@ const props = withDefaults(
entries: DirEntry[]; entries: DirEntry[];
content?: string; content?: string;
currentFilePath?: string | null; currentFilePath?: string | null;
isFullWidth?: boolean;
}>(), }>(),
{ {
content: "", content: "",
currentFilePath: null, currentFilePath: null,
isFullWidth: false,
}, },
); );
@@ -29,6 +31,7 @@ const emit = defineEmits<{
openFile: [path: string]; openFile: [path: string];
createFile: []; createFile: [];
createFolder: []; createFolder: [];
toggleFullWidth: [value: boolean];
}>(); }>();
// Wrap the workspace root as a top-level tree node // Wrap the workspace root as a top-level tree node
@@ -244,10 +247,31 @@ const showSettings = ref(false);
<i class="ri-close-line text-base"></i> <i class="ri-close-line text-base"></i>
</button> </button>
</div> </div>
<div class="px-4 py-6"> <div class="px-4 py-6 space-y-5">
<p class="text-xs text-[#b8b3a8] text-center leading-relaxed"> <!-- Full-width mode toggle -->
设置功能即将上线 <div class="flex items-center justify-between">
</p> <div class="flex-1 mr-3">
<p class="text-sm text-[#38342e] font-medium">全宽模式</p>
<p class="text-xs text-[#8c877d] mt-0.5 leading-relaxed">关闭后编辑器居中显示,适合专注书写</p>
</div>
<button
type="button"
role="switch"
:aria-checked="props.isFullWidth"
:class="[
'relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none',
props.isFullWidth ? 'bg-[#bf6a3b]' : 'bg-[#d4cfc4]',
]"
@click="emit('toggleFullWidth', !props.isFullWidth)"
>
<span
:class="[
'pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow-sm ring-0 transition-transform duration-200 ease-in-out',
props.isFullWidth ? 'translate-x-4' : 'translate-x-0',
]"
/>
</button>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,18 +1,25 @@
<script setup lang="ts"> <script setup lang="ts">
import { watch } from "vue"; import { ref, watch, onMounted, onBeforeUnmount } from "vue";
import { useEditor, EditorContent } from "@tiptap/vue-3"; import {
import StarterKit from "@tiptap/starter-kit"; EditorView,
import Placeholder from "@tiptap/extension-placeholder"; placeholder,
import { Markdown } from "@tiptap/markdown"; keymap,
} from "@codemirror/view";
import { EditorState, Annotation, Transaction } from "@codemirror/state";
import { markdown } from "@codemirror/lang-markdown";
import { defaultKeymap, history, historyKeymap } from "@codemirror/commands";
import { livePreview } from "../editor/livePreview";
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
modelValue?: string; modelValue?: string;
placeholder?: string; placeholder?: string;
isFullWidth?: boolean;
}>(), }>(),
{ {
modelValue: "", modelValue: "",
placeholder: "开始写作...", placeholder: "开始写作...",
isFullWidth: false,
}, },
); );
@@ -21,42 +28,173 @@ const emit = defineEmits<{
"save-shortcut": []; "save-shortcut": [];
}>(); }>();
const editor = useEditor({ const editorContainer = ref<HTMLDivElement>();
content: props.modelValue, const editorView = ref<EditorView>();
extensions: [
StarterKit.configure({ // Annotation to mark sync transactions, so we don't re-emit to parent
heading: { levels: [1, 2, 3] }, const syncAnnotation = Annotation.define<boolean>();
}),
Placeholder.configure({ // ── Editor theme (warm color palette, matching original design) ────
placeholder: props.placeholder,
}), const editorTheme = EditorView.theme(
Markdown, {
], "&": {
editorProps: { maxHeight: "100%",
attributes: { fontSize: "calc(1rem * var(--editor-zoom, 1))",
class: backgroundColor: "white",
"min-h-[320px] px-6 py-4 outline-none focus:outline-none",
}, },
handleKeyDown: (_view, event) => { ".cm-scroller": {
if ((event.ctrlKey || event.metaKey) && event.key === "s") { overflow: "auto",
event.preventDefault(); fontFamily:
"ui-serif, Georgia, Cambria, 'Times New Roman', Times, serif",
lineHeight: "1.625",
},
".cm-content": {
padding: "2rem 2.5rem 10rem",
minHeight: "320px",
color: "#38342e",
caretColor: "#38342e",
},
".cm-cursor": {
borderLeftColor: "#38342e",
},
".cm-selectionBackground, .cm-content ::selection": {
backgroundColor: "#d4cfc4",
},
// ── Live Preview: styled content ──
".cm-lp-bold": {
fontWeight: "700",
color: "#2d2a26",
},
".cm-lp-italic": {
fontStyle: "italic",
},
".cm-lp-strikethrough": {
textDecoration: "line-through",
color: "#8c877d",
},
".cm-lp-code": {
backgroundColor: "#f4f1ea",
color: "#bf6a3b",
borderRadius: "0.25rem",
padding: "0.125rem 0.375rem",
fontSize: "0.875em",
fontFamily:
"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
},
".cm-lp-link": {
color: "#bf6a3b",
textDecoration: "underline",
cursor: "pointer",
},
".cm-lp-heading": {
fontWeight: "700",
color: "#2d2a26",
},
".cm-lp-h1": {
fontSize: "1.5em",
},
".cm-lp-h2": {
fontSize: "1.25em",
},
".cm-lp-h3": {
fontSize: "1.125em",
},
".cm-lp-blockquote": {
borderLeft: "4px solid #bf6a3b",
paddingLeft: "1rem",
color: "#8c877d",
fontStyle: "italic",
},
// Placeholder
".cm-placeholder": {
color: "#b8b3a8",
},
// ── Remove focus outline ──
"&.cm-editor.cm-focused": {
outline: "none",
},
"&.cm-editor .cm-content": {
outline: "none",
},
},
{ dark: false },
);
// ── Extensions ─────────────────────────────────────────────────────
function createExtensions() {
return [
markdown(),
livePreview(),
EditorView.lineWrapping,
placeholder(props.placeholder),
keymap.of([
{
key: "Mod-s",
run: () => {
emit("save-shortcut"); emit("save-shortcut");
return true; return true;
},
preventDefault: true,
},
]),
history(),
keymap.of([...defaultKeymap, ...historyKeymap]),
EditorView.updateListener.of((update) => {
if (!update.docChanged) return;
const isSync = update.transactions.some((tr) =>
tr.annotation(syncAnnotation),
);
if (!isSync) {
emit("update:modelValue", update.state.doc.toString());
} }
return false; }),
}, editorTheme,
}, ];
onUpdate: ({ editor }) => { }
emit("update:modelValue", editor.getMarkdown());
}, // ── Lifecycle ──────────────────────────────────────────────────────
onMounted(() => {
if (!editorContainer.value) return;
const state = EditorState.create({
doc: props.modelValue,
extensions: createExtensions(),
});
editorView.value = new EditorView({
state,
parent: editorContainer.value,
});
});
onBeforeUnmount(() => {
editorView.value?.destroy();
editorView.value = undefined;
}); });
// Sync external modelValue changes back into the editor // Sync external modelValue changes back into the editor
watch( watch(
() => props.modelValue, () => props.modelValue,
(val) => { (val) => {
if (editor.value && val !== editor.value.getMarkdown()) { const view = editorView.value;
editor.value.commands.setContent(val, { contentType: "markdown", emitUpdate: false }); if (!view) return;
if (val !== view.state.doc.toString()) {
view.dispatch({
changes: { from: 0, to: view.state.doc.length, insert: val },
annotations: [
syncAnnotation.of(true),
Transaction.addToHistory.of(false),
],
// Try to preserve cursor; if the replacement makes it invalid,
// CM6 will clamp it to a valid position.
selection: view.state.selection,
});
} }
}, },
); );
@@ -64,137 +202,16 @@ watch(
<template> <template>
<div <div
class="flex flex-col flex-1 w-full border-[#e0dbcf] bg-white shadow-2xl overflow-y-auto" :class="[
'flex flex-col flex-1 w-full border border-[#e0dbcf] bg-white shadow-2xl overflow-hidden',
!props.isFullWidth && 'mx-auto',
]"
:style="
!props.isFullWidth
? { maxWidth: `calc(800px * var(--editor-zoom, 1))` }
: undefined
"
> >
<!-- Editor content --> <div ref="editorContainer" class="flex-1 min-h-0" />
<EditorContent :editor="editor" class="tiptap-editor flex-1 flex flex-col" />
</div> </div>
</template> </template>
<style>
/* TipTap / ProseMirror content styling */
.tiptap-editor .ProseMirror {
color: #38342e;
min-height: 320px;
flex: 1;
outline: none;
overflow-y: auto;
}
.tiptap-editor .ProseMirror p.is-editor-empty:first-child::before {
color: #b8b3a8;
float: left;
height: 0;
pointer-events: none;
content: attr(data-placeholder);
}
/* Headings */
.tiptap-editor .ProseMirror h1 {
font-size: 1.5rem;
font-weight: 700;
margin-top: 1.5rem;
margin-bottom: 0.75rem;
color: #2d2a26;
}
.tiptap-editor .ProseMirror h2 {
font-size: 1.25rem;
font-weight: 700;
margin-top: 1.25rem;
margin-bottom: 0.5rem;
color: #2d2a26;
}
.tiptap-editor .ProseMirror h3 {
font-size: 1.125rem;
font-weight: 600;
margin-top: 1rem;
margin-bottom: 0.5rem;
color: #2d2a26;
}
/* Blockquote */
.tiptap-editor .ProseMirror blockquote {
border-left: 4px solid #bf6a3b;
padding-left: 1rem;
margin-top: 0.75rem;
margin-bottom: 0.75rem;
color: #8c877d;
font-style: italic;
}
/* Inline code */
.tiptap-editor .ProseMirror code {
background-color: #f4f1ea;
color: #bf6a3b;
padding: 0.125rem 0.375rem;
border-radius: 0.25rem;
font-size: 0.875rem;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
}
/* Code block */
.tiptap-editor .ProseMirror pre {
background-color: #f4f1ea;
border-radius: 0.5rem;
padding: 1rem;
margin-top: 0.75rem;
margin-bottom: 0.75rem;
overflow-x: auto;
}
.tiptap-editor .ProseMirror pre code {
background-color: transparent;
color: #5c574e;
padding: 0;
}
/* Lists */
.tiptap-editor .ProseMirror ul {
list-style-type: disc;
padding-left: 1.5rem;
margin-top: 0.5rem;
margin-bottom: 0.5rem;
}
.tiptap-editor .ProseMirror ol {
list-style-type: decimal;
padding-left: 1.5rem;
margin-top: 0.5rem;
margin-bottom: 0.5rem;
}
.tiptap-editor .ProseMirror li {
margin-top: 0.25rem;
margin-bottom: 0.25rem;
}
/* Horizontal rule */
.tiptap-editor .ProseMirror hr {
border-color: #e8e4da;
margin-top: 1.5rem;
margin-bottom: 1.5rem;
}
/* Links */
.tiptap-editor .ProseMirror a {
color: #bf6a3b;
text-decoration: underline;
}
/* Bold / Italic / Strike */
.tiptap-editor .ProseMirror strong {
font-weight: 700;
color: #2d2a26;
}
.tiptap-editor .ProseMirror em {
font-style: italic;
}
.tiptap-editor .ProseMirror s {
text-decoration: line-through;
color: #8c877d;
}
/* Paragraph spacing */
.tiptap-editor .ProseMirror p {
margin-top: 0.375rem;
margin-bottom: 0.375rem;
line-height: 1.625;
}
</style>

377
src/editor/livePreview.ts Normal file
View File

@@ -0,0 +1,377 @@
import {
ViewPlugin,
ViewUpdate,
Decoration,
DecorationSet,
EditorView,
} from "@codemirror/view";
import type { Range } from "@codemirror/state";
// ── Types ──────────────────────────────────────────────────────────
interface HideRange {
/** start of whole construct (e.g. first `*` of `**bold**`) */
from: number;
/** end of whole construct */
to: number;
/** ranges of syntax chars to hide ([from, to] pairs) */
markers: [number, number][];
/** optional: range of styled content to apply[0=from, 1=to, 2=css class] */
content?: [number, number, string];
}
// ── CSS classes ────────────────────────────────────────────────────
// (no longer needed — Decoration.replace handles hiding directly)
// ── Decoration builders ────────────────────────────────────────────
const hiddenMark = Decoration.replace({});
function contentMark(cls: string) {
return Decoration.mark({ class: cls });
}
// ── Regex patterns ─────────────────────────────────────────────────
// Block-level: detect headings, blockquotes, list items, hr, fenced code
const RE_HEADING = /^(#{1,6})\s+(.*)$/;
const RE_BLOCKQUOTE = /^(>\s*)(.*)$/;
const RE_UNORDERED = /^(\s*[-*+]\s*)(.*)$/;
const RE_ORDERED = /^(\s*\d+\.\s*)(.*)$/;
const RE_HR = /^[-*_]{3,}\s*$/;
const RE_CODE_FENCE = /^```/;
// Inline: bold, italic, strikethrough, code, link, image
// These run on non-code-block content after stripping block markers
// We parse inline elements by scanning left-to-right within each line.
// Using a combined pattern that matches the FIRST occurrence:
const RE_INLINE = /(\*\*|__)(.+?)\1|(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)|(?<!_)_(?!_)(.+?)(?<!_)_(?!_)|~~(.+?)~~|`([^`]+)`|!\[([^\]]*)\]\(([^)]+)\)|\[([^\]]*)\]\(([^)]+)\)/g;
// ── Parse the document ─────────────────────────────────────────────
function parseDocument(text: string): HideRange[] {
const regions: HideRange[] = [];
const lines = text.split("\n");
let pos = 0;
let inCodeBlock = false;
let codeBlockStart = -1;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const lineStart = pos;
// ── Fenced code blocks ──
if (RE_CODE_FENCE.test(line.trimStart())) {
if (!inCodeBlock) {
inCodeBlock = true;
codeBlockStart = lineStart;
} else {
// closing fence
inCodeBlock = false;
// Hide the opening fence
const openFenceEnd = text.indexOf("\n", codeBlockStart);
const openFence = openFenceEnd === -1 ? text.substring(codeBlockStart) : text.substring(codeBlockStart, openFenceEnd);
const openMarkerLen = openFence.length;
regions.push({
from: codeBlockStart,
to: codeBlockStart + openMarkerLen + (lineStart - codeBlockStart - openMarkerLen) + line.length,
markers: [
[codeBlockStart, codeBlockStart + openMarkerLen],
[lineStart, lineStart + line.length],
],
});
}
pos += line.length + 1;
continue;
}
if (inCodeBlock) {
pos += line.length + 1;
continue;
}
// ── Horizontal rule ──
if (RE_HR.test(line.trim())) {
pos += line.length + 1;
continue; // HR is decorative, just show the line
}
// ── Headings ──
{
const m = line.match(RE_HEADING);
if (m) {
// marker = "#" marks + all whitespace before content
const contentStartInMatch = m[0].length - m[2].length;
const markerEnd = lineStart + contentStartInMatch;
regions.push({
from: lineStart,
to: lineStart + line.length,
markers: [[lineStart, markerEnd]],
content: [markerEnd, lineStart + line.length, `cm-lp-heading cm-lp-h${m[1].length}`],
});
// Parse inline within heading content
parseInline(text, markerEnd, lineStart + line.length, regions);
pos += line.length + 1;
continue;
}
}
// ── Blockquote ──
{
const m = line.match(RE_BLOCKQUOTE);
if (m) {
const markerEnd = lineStart + m[1].length;
regions.push({
from: lineStart,
to: lineStart + line.length,
markers: [[lineStart, markerEnd]],
content: [markerEnd, lineStart + line.length, "cm-lp-blockquote"],
});
parseInline(text, markerEnd, lineStart + line.length, regions);
pos += line.length + 1;
continue;
}
}
// ── Unordered list ──
{
const m = line.match(RE_UNORDERED);
if (m) {
const markerEnd = lineStart + m[1].length;
regions.push({
from: lineStart,
to: lineStart + line.length,
markers: [[lineStart, markerEnd]],
});
parseInline(text, markerEnd, lineStart + line.length, regions);
pos += line.length + 1;
continue;
}
}
// ── Ordered list ──
{
const m = line.match(RE_ORDERED);
if (m) {
const markerEnd = lineStart + m[1].length;
regions.push({
from: lineStart,
to: lineStart + line.length,
markers: [[lineStart, markerEnd]],
});
parseInline(text, markerEnd, lineStart + line.length, regions);
pos += line.length + 1;
continue;
}
}
// ── Plain paragraph ──
parseInline(text, lineStart, lineStart + line.length, regions);
pos += line.length + 1;
}
return regions;
}
function parseInline(
text: string,
lineStart: number,
lineEnd: number,
regions: HideRange[],
): void {
const slice = text.slice(lineStart, lineEnd);
RE_INLINE.lastIndex = 0;
let match: RegExpExecArray | null;
while ((match = RE_INLINE.exec(slice)) !== null) {
const absFrom = lineStart + match.index;
const absTo = lineStart + match.index + match[0].length;
// Bold: **text** or __text__
if (match[1]) {
const delimLen = match[1].length; // 2 for ** or __
const contentStart = absFrom + delimLen;
const contentEnd = absTo - delimLen;
regions.push({
from: absFrom,
to: absTo,
markers: [
[absFrom, contentStart],
[contentEnd, absTo],
],
content: [contentStart, contentEnd, "cm-lp-bold"],
});
continue;
}
// Italic: *text*
if (match[3]) {
const contentStart = absFrom + 1;
const contentEnd = absTo - 1;
regions.push({
from: absFrom,
to: absTo,
markers: [
[absFrom, contentStart],
[contentEnd, absTo],
],
content: [contentStart, contentEnd, "cm-lp-italic"],
});
continue;
}
// Italic: _text_
if (match[4]) {
const contentStart = absFrom + 1;
const contentEnd = absTo - 1;
regions.push({
from: absFrom,
to: absTo,
markers: [
[absFrom, contentStart],
[contentEnd, absTo],
],
content: [contentStart, contentEnd, "cm-lp-italic"],
});
continue;
}
// Strikethrough: ~~text~~
if (match[5]) {
const contentStart = absFrom + 2;
const contentEnd = absTo - 2;
regions.push({
from: absFrom,
to: absTo,
markers: [
[absFrom, contentStart],
[contentEnd, absTo],
],
content: [contentStart, contentEnd, "cm-lp-strikethrough"],
});
continue;
}
// Inline code: `code`
if (match[6] !== undefined) {
const contentStart = absFrom + 1;
const contentEnd = absTo - 1;
regions.push({
from: absFrom,
to: absTo,
markers: [
[absFrom, contentStart],
[contentEnd, absTo],
],
content: [contentStart, contentEnd, "cm-lp-code"],
});
continue;
}
// Image: ![alt](url)
if (match[7] !== undefined && match[8] !== undefined) {
const altText = match[7];
const altStart = absFrom + 2; // after ![
const altEnd = altStart + altText.length;
const urlPart = match[8];
const urlInText = match[0].indexOf("](") + 2;
const urlStart = absFrom + urlInText;
const urlEnd = urlStart + urlPart.length;
regions.push({
from: absFrom,
to: absTo,
markers: [
[absFrom, absFrom + 2], // ![
[altEnd, altEnd + 2], // ](
[urlEnd, absTo], // )
],
});
continue;
}
// Link: [text](url)
if (match[9] !== undefined && match[10] !== undefined) {
const linkText = match[9];
const textStart = absFrom + 1; // after [
const textEnd = textStart + linkText.length;
const urlPart = match[10];
const urlInText = match[0].indexOf("](") + 2;
const urlStart = absFrom + urlInText;
const urlEnd = urlStart + urlPart.length;
regions.push({
from: absFrom,
to: absTo,
markers: [
[absFrom, textStart], // [
[textEnd, urlStart], // ](
[urlEnd, absTo], // )
],
content: [textStart, textEnd, "cm-lp-link"],
});
continue;
}
}
}
// ── Build decoration set ───────────────────────────────────────────
function buildDecorationSet(regions: HideRange[], selFrom: number, selTo: number): DecorationSet {
const decos: Range<Decoration>[] = [];
for (const region of regions) {
// If the selection overlaps with this region, reveal it (show raw markdown)
if (selFrom < region.to && selTo > region.from) {
continue;
}
// Hide all marker ranges
for (const [mFrom, mTo] of region.markers) {
if (mFrom < mTo) {
decos.push(hiddenMark.range(mFrom, mTo));
}
}
// Apply content styling
if (region.content) {
const [cFrom, cTo, cssClass] = region.content;
if (cFrom < cTo) {
decos.push(contentMark(cssClass).range(cFrom, cTo));
}
}
}
return Decoration.set(decos, true);
}
// ── ViewPlugin ─────────────────────────────────────────────────────
class LivePreviewPlugin {
decorations: DecorationSet;
constructor(view: EditorView) {
this.decorations = this.compute(view);
}
update(update: ViewUpdate) {
if (update.docChanged || update.selectionSet) {
this.decorations = this.compute(update.view);
}
}
private compute(view: EditorView): DecorationSet {
const text = view.state.doc.toString();
const sel = view.state.selection.main;
const regions = parseDocument(text);
return buildDecorationSet(regions, sel.from, sel.to);
}
}
export function livePreview() {
return ViewPlugin.fromClass(LivePreviewPlugin, {
decorations: (v) => v.decorations,
});
}