feat: 添加文稿版本管理、差异对比高亮和多项功能优化
- 修复AI重写输出过滤(过滤thinking/draft标签) - 添加文稿保存与版本管理功能 - 新增插入差异功能(差异对比模式) - 实现版本历史侧边抽屉(DocumentVersionPanel) - 添加版本对比差异高亮显示 - 调整文稿编辑区高度适应屏幕 - 新增MarkdownEditor组件(暂未启用)
This commit is contained in:
807
package-lock.json
generated
807
package-lock.json
generated
@@ -8,6 +8,10 @@
|
||||
"name": "ai-writer-workshop",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@tiptap/extension-placeholder": "^3.15.3",
|
||||
"@tiptap/pm": "^3.15.3",
|
||||
"@tiptap/starter-kit": "^3.15.3",
|
||||
"@tiptap/vue-3": "^3.15.3",
|
||||
"axios": "^1.6.0",
|
||||
"marked": "^9.1.0",
|
||||
"pinia": "^2.1.0",
|
||||
@@ -457,12 +461,44 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "1.7.3",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz",
|
||||
"integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/utils": "^0.2.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/dom": {
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz",
|
||||
"integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@floating-ui/core": "^1.7.3",
|
||||
"@floating-ui/utils": "^0.2.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/utils": {
|
||||
"version": "0.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
|
||||
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@remirror/core-constants": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-3.0.0.tgz",
|
||||
"integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz",
|
||||
@@ -813,6 +849,450 @@
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@tiptap/core": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.15.3.tgz",
|
||||
"integrity": "sha512-bmXydIHfm2rEtGju39FiQNfzkFx9CDvJe+xem1dgEZ2P6Dj7nQX9LnA1ZscW7TuzbBRkL5p3dwuBIi3f62A66A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/pm": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-blockquote": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-3.15.3.tgz",
|
||||
"integrity": "sha512-13x5UsQXtttFpoS/n1q173OeurNxppsdWgP3JfsshzyxIghhC141uL3H6SGYQLPU31AizgDs2OEzt6cSUevaZg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-bold": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-3.15.3.tgz",
|
||||
"integrity": "sha512-I8JYbkkUTNUXbHd/wCse2bR0QhQtJD7+0/lgrKOmGfv5ioLxcki079Nzuqqay3PjgYoJLIJQvm3RAGxT+4X91w==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-bubble-menu": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.15.3.tgz",
|
||||
"integrity": "sha512-e88DG1bTy6hKxrt7iPVQhJnH5/EOrnKpIyp09dfRDgWrrW88fE0Qjys7a/eT8W+sXyXM3z10Ye7zpERWsrLZDg==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.15.3",
|
||||
"@tiptap/pm": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-bullet-list": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-3.15.3.tgz",
|
||||
"integrity": "sha512-MGwEkNT7ltst6XaWf0ObNgpKQ4PvuuV3igkBrdYnQS+qaAx9IF4isygVPqUc9DvjYC306jpyKsNqNrENIXcosA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extension-list": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-code": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-3.15.3.tgz",
|
||||
"integrity": "sha512-x6LFt3Og6MFINYpsMzrJnz7vaT9Yk1t4oXkbJsJRSavdIWBEBcoRudKZ4sSe/AnsYlRJs8FY2uR76mt9e+7xAQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-code-block": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-3.15.3.tgz",
|
||||
"integrity": "sha512-q1UB9icNfdJppTqMIUWfoRKkx5SSdWIpwZoL2NeOI5Ah3E20/dQKVttIgLhsE521chyvxCYCRaHD5tMNGKfhyw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.15.3",
|
||||
"@tiptap/pm": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-document": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-3.15.3.tgz",
|
||||
"integrity": "sha512-AC72nI2gnogBuETCKbZekn+h6t5FGGcZG2abPGKbz/x9rwpb6qV2hcbAQ30t6M7H6cTOh2/Ut8bEV2MtMB15sw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-dropcursor": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-3.15.3.tgz",
|
||||
"integrity": "sha512-jGI5XZpdo8GSYQFj7HY15/oEwC2m2TqZz0/Fln5qIhY32XlZhWrsMuMI6WbUJrTH16es7xO6jmRlDsc6g+vJWg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extensions": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-floating-menu": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-3.15.3.tgz",
|
||||
"integrity": "sha512-+3DVBleKKffadEJEdLYxmYAJOjHjLSqtiSFUE3RABT4V2ka1ODy2NIpyKX0o1SvQ5N1jViYT9Q+yUbNa6zCcDw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@floating-ui/dom": "^1.0.0",
|
||||
"@tiptap/core": "^3.15.3",
|
||||
"@tiptap/pm": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-gapcursor": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-3.15.3.tgz",
|
||||
"integrity": "sha512-Kaw0sNzP0bQI/xEAMSfIpja6xhsu9WqqAK/puzOIS1RKWO47Wps/tzqdSJ9gfslPIb5uY5mKCfy8UR8Xgiia8w==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extensions": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-hard-break": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-3.15.3.tgz",
|
||||
"integrity": "sha512-8HjxmeRbBiXW+7JKemAJtZtHlmXQ9iji398CPQ0yYde68WbIvUhHXjmbJE5pxFvvQTJ/zJv1aISeEOZN2bKBaw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-heading": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-3.15.3.tgz",
|
||||
"integrity": "sha512-G1GG6iN1YXPS+75arDpo+bYRzhr3dNDw99c7D7na3aDawa9Qp7sZ/bVrzFUUcVEce0cD6h83yY7AooBxEc67hA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-horizontal-rule": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.15.3.tgz",
|
||||
"integrity": "sha512-FYkN7L6JsfwwNEntmLklCVKvgL0B0N47OXMacRk6kYKQmVQ4Nvc7q/VJLpD9sk4wh4KT1aiCBfhKEBTu5pv1fg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.15.3",
|
||||
"@tiptap/pm": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-italic": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.15.3.tgz",
|
||||
"integrity": "sha512-6XeuPjcWy7OBxpkgOV7bD6PATO5jhIxc8SEK4m8xn8nelGTBIbHGqK37evRv+QkC7E0MUryLtzwnmmiaxcKL0Q==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-link": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-3.15.3.tgz",
|
||||
"integrity": "sha512-PdDXyBF9Wco9U1x6e+b7tKBWG+kqBDXDmaYXHkFm/gYuQCQafVJ5mdrDdKgkHDWVnJzMWZXBcZjT9r57qtlLWg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"linkifyjs": "^4.3.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.15.3",
|
||||
"@tiptap/pm": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-list": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.15.3.tgz",
|
||||
"integrity": "sha512-n7y/MF9lAM5qlpuH5IR4/uq+kJPEJpe9NrEiH+NmkO/5KJ6cXzpJ6F4U17sMLf2SNCq+TWN9QK8QzoKxIn50VQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.15.3",
|
||||
"@tiptap/pm": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-list-item": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-3.15.3.tgz",
|
||||
"integrity": "sha512-CCxL5ek1p0lO5e8aqhnPzIySldXRSigBFk2fP9OLgdl5qKFLs2MGc19jFlx5+/kjXnEsdQTFbGY1Sizzt0TVDw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extension-list": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-list-keymap": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-list-keymap/-/extension-list-keymap-3.15.3.tgz",
|
||||
"integrity": "sha512-UxqnTEEAKrL+wFQeSyC9z0mgyUUVRS2WTcVFoLZCE6/Xus9F53S4bl7VKFadjmqI4GpDk5Oe2IOUc72o129jWg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extension-list": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-ordered-list": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-3.15.3.tgz",
|
||||
"integrity": "sha512-/8uhw528Iy0c9wF6tHCiIn0ToM0Ml6Ll2c/3iPRnKr4IjXwx2Lr994stUFihb+oqGZwV1J8CPcZJ4Ufpdqi4Dw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extension-list": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-paragraph": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-3.15.3.tgz",
|
||||
"integrity": "sha512-lc0Qu/1AgzcEfS67NJMj5tSHHhH6NtA6uUpvppEKGsvJwgE2wKG1onE4isrVXmcGRdxSMiCtyTDemPNMu6/ozQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-placeholder": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-3.15.3.tgz",
|
||||
"integrity": "sha512-XcHHnojT186hKIoOgcPBesXk89+caNGVUdMtc171Vcr/5s0dpnr4q5LfE+YRC+S85CpCxCRRnh84Ou+XRtOqrw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extensions": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-strike": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-3.15.3.tgz",
|
||||
"integrity": "sha512-Y1P3eGNY7RxQs2BcR6NfLo9VfEOplXXHAqkOM88oowWWOE7dMNeFFZM9H8HNxoQgXJ7H0aWW9B7ZTWM9hWli2Q==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-text": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.15.3.tgz",
|
||||
"integrity": "sha512-MhkBz8ZvrqOKtKNp+ZWISKkLUlTrDR7tbKZc2OnNcUTttL9dz0HwT+cg91GGz19fuo7ttDcfsPV6eVmflvGToA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-underline": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-3.15.3.tgz",
|
||||
"integrity": "sha512-r/IwcNN0W366jGu4Y0n2MiFq9jGa4aopOwtfWO4d+J0DyeS2m7Go3+KwoUqi0wQTiVU74yfi4DF6eRsMQ9/iHQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extensions": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.15.3.tgz",
|
||||
"integrity": "sha512-ycx/BgxR4rc9tf3ZyTdI98Z19yKLFfqM3UN+v42ChuIwkzyr9zyp7kG8dB9xN2lNqrD+5y/HyJobz/VJ7T90gA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.15.3",
|
||||
"@tiptap/pm": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/pm": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.15.3.tgz",
|
||||
"integrity": "sha512-Zm1BaU1TwFi3CQiisxjgnzzIus+q40bBKWLqXf6WEaus8Z6+vo1MT2pU52dBCMIRaW9XNDq3E5cmGtMc1AlveA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"prosemirror-changeset": "^2.3.0",
|
||||
"prosemirror-collab": "^1.3.1",
|
||||
"prosemirror-commands": "^1.6.2",
|
||||
"prosemirror-dropcursor": "^1.8.1",
|
||||
"prosemirror-gapcursor": "^1.3.2",
|
||||
"prosemirror-history": "^1.4.1",
|
||||
"prosemirror-inputrules": "^1.4.0",
|
||||
"prosemirror-keymap": "^1.2.2",
|
||||
"prosemirror-markdown": "^1.13.1",
|
||||
"prosemirror-menu": "^1.2.4",
|
||||
"prosemirror-model": "^1.24.1",
|
||||
"prosemirror-schema-basic": "^1.2.3",
|
||||
"prosemirror-schema-list": "^1.5.0",
|
||||
"prosemirror-state": "^1.4.3",
|
||||
"prosemirror-tables": "^1.6.4",
|
||||
"prosemirror-trailing-node": "^3.0.0",
|
||||
"prosemirror-transform": "^1.10.2",
|
||||
"prosemirror-view": "^1.38.1"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/starter-kit": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-3.15.3.tgz",
|
||||
"integrity": "sha512-ia+eQr9Mt1ln2UO+kK4kFTJOrZK4GhvZXFjpCCYuHtco3rhr2fZAIxEEY4cl/vo5VO5WWyPqxhkFeLcoWmNjSw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tiptap/core": "^3.15.3",
|
||||
"@tiptap/extension-blockquote": "^3.15.3",
|
||||
"@tiptap/extension-bold": "^3.15.3",
|
||||
"@tiptap/extension-bullet-list": "^3.15.3",
|
||||
"@tiptap/extension-code": "^3.15.3",
|
||||
"@tiptap/extension-code-block": "^3.15.3",
|
||||
"@tiptap/extension-document": "^3.15.3",
|
||||
"@tiptap/extension-dropcursor": "^3.15.3",
|
||||
"@tiptap/extension-gapcursor": "^3.15.3",
|
||||
"@tiptap/extension-hard-break": "^3.15.3",
|
||||
"@tiptap/extension-heading": "^3.15.3",
|
||||
"@tiptap/extension-horizontal-rule": "^3.15.3",
|
||||
"@tiptap/extension-italic": "^3.15.3",
|
||||
"@tiptap/extension-link": "^3.15.3",
|
||||
"@tiptap/extension-list": "^3.15.3",
|
||||
"@tiptap/extension-list-item": "^3.15.3",
|
||||
"@tiptap/extension-list-keymap": "^3.15.3",
|
||||
"@tiptap/extension-ordered-list": "^3.15.3",
|
||||
"@tiptap/extension-paragraph": "^3.15.3",
|
||||
"@tiptap/extension-strike": "^3.15.3",
|
||||
"@tiptap/extension-text": "^3.15.3",
|
||||
"@tiptap/extension-underline": "^3.15.3",
|
||||
"@tiptap/extensions": "^3.15.3",
|
||||
"@tiptap/pm": "^3.15.3"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/vue-3": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/vue-3/-/vue-3-3.15.3.tgz",
|
||||
"integrity": "sha512-iFmf8oLTtQztY+7O7DxxLp43ZRL5N5lP3wV7/RrZy4aFka524/8Lo04fV18t6aevJLRXlxbWokXbT7Ak2XcXBA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tiptap/extension-bubble-menu": "^3.15.3",
|
||||
"@tiptap/extension-floating-menu": "^3.15.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@floating-ui/dom": "^1.0.0",
|
||||
"@tiptap/core": "^3.15.3",
|
||||
"@tiptap/pm": "^3.15.3",
|
||||
"vue": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
||||
@@ -820,6 +1300,28 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/linkify-it": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
|
||||
"integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/markdown-it": {
|
||||
"version": "14.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz",
|
||||
"integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/linkify-it": "^5",
|
||||
"@types/mdurl": "^2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/mdurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
|
||||
"integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vitejs/plugin-vue": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.6.2.tgz",
|
||||
@@ -940,6 +1442,12 @@
|
||||
"integrity": "sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
@@ -982,6 +1490,12 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/crelt": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
|
||||
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
@@ -1120,6 +1634,18 @@
|
||||
"@esbuild/win32-x64": "0.21.5"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/estree-walker": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||
@@ -1274,6 +1800,21 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/linkify-it": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
|
||||
"integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"uc.micro": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/linkifyjs": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.2.tgz",
|
||||
"integrity": "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.21",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||
@@ -1283,6 +1824,35 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||
}
|
||||
},
|
||||
"node_modules/markdown-it": {
|
||||
"version": "14.1.0",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
|
||||
"integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^2.0.1",
|
||||
"entities": "^4.4.0",
|
||||
"linkify-it": "^5.0.0",
|
||||
"mdurl": "^2.0.0",
|
||||
"punycode.js": "^2.3.1",
|
||||
"uc.micro": "^2.1.0"
|
||||
},
|
||||
"bin": {
|
||||
"markdown-it": "bin/markdown-it.mjs"
|
||||
}
|
||||
},
|
||||
"node_modules/markdown-it/node_modules/entities": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/marked": {
|
||||
"version": "9.1.6",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-9.1.6.tgz",
|
||||
@@ -1304,6 +1874,12 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/mdurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
|
||||
"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
@@ -1343,6 +1919,12 @@
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/orderedmap": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz",
|
||||
"integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
@@ -1399,12 +1981,219 @@
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-changeset": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.3.1.tgz",
|
||||
"integrity": "sha512-j0kORIBm8ayJNl3zQvD1TTPHJX3g042et6y/KQhZhnPrruO8exkTgG8X+NRpj7kIyMMEx74Xb3DyMIBtO0IKkQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-transform": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-collab": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.1.tgz",
|
||||
"integrity": "sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-commands": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz",
|
||||
"integrity": "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.10.2"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-dropcursor": {
|
||||
"version": "1.8.2",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.2.tgz",
|
||||
"integrity": "sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.1.0",
|
||||
"prosemirror-view": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-gapcursor": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.4.0.tgz",
|
||||
"integrity": "sha512-z00qvurSdCEWUIulij/isHaqu4uLS8r/Fi61IbjdIPJEonQgggbJsLnstW7Lgdk4zQ68/yr6B6bf7sJXowIgdQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-keymap": "^1.0.0",
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-view": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-history": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.5.0.tgz",
|
||||
"integrity": "sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.2.2",
|
||||
"prosemirror-transform": "^1.0.0",
|
||||
"prosemirror-view": "^1.31.0",
|
||||
"rope-sequence": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-inputrules": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.5.1.tgz",
|
||||
"integrity": "sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-keymap": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.3.tgz",
|
||||
"integrity": "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"w3c-keyname": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-markdown": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.13.2.tgz",
|
||||
"integrity": "sha512-FPD9rHPdA9fqzNmIIDhhnYQ6WgNoSWX9StUZ8LEKapaXU9i6XgykaHKhp6XMyXlOWetmaFgGDS/nu/w9/vUc5g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/markdown-it": "^14.0.0",
|
||||
"markdown-it": "^14.0.0",
|
||||
"prosemirror-model": "^1.25.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-menu": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.2.5.tgz",
|
||||
"integrity": "sha512-qwXzynnpBIeg1D7BAtjOusR+81xCp53j7iWu/IargiRZqRjGIlQuu1f3jFi+ehrHhWMLoyOQTSRx/IWZJqOYtQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"crelt": "^1.0.0",
|
||||
"prosemirror-commands": "^1.0.0",
|
||||
"prosemirror-history": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-model": {
|
||||
"version": "1.25.4",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz",
|
||||
"integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"orderedmap": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-schema-basic": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.4.tgz",
|
||||
"integrity": "sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.25.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-schema-list": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz",
|
||||
"integrity": "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.7.3"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-state": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz",
|
||||
"integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-transform": "^1.0.0",
|
||||
"prosemirror-view": "^1.27.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-tables": {
|
||||
"version": "1.8.5",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.8.5.tgz",
|
||||
"integrity": "sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-keymap": "^1.2.3",
|
||||
"prosemirror-model": "^1.25.4",
|
||||
"prosemirror-state": "^1.4.4",
|
||||
"prosemirror-transform": "^1.10.5",
|
||||
"prosemirror-view": "^1.41.4"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-trailing-node": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-3.0.0.tgz",
|
||||
"integrity": "sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@remirror/core-constants": "3.0.0",
|
||||
"escape-string-regexp": "^4.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prosemirror-model": "^1.22.1",
|
||||
"prosemirror-state": "^1.4.2",
|
||||
"prosemirror-view": "^1.33.8"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-transform": {
|
||||
"version": "1.10.5",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.10.5.tgz",
|
||||
"integrity": "sha512-RPDQCxIDhIBb1o36xxwsaeAvivO8VLJcgBtzmOwQ64bMtsVFh5SSuJ6dWSxO1UsHTiTXPCgQm3PDJt7p6IOLbw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-view": {
|
||||
"version": "1.41.4",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.4.tgz",
|
||||
"integrity": "sha512-WkKgnyjNncri03Gjaz3IFWvCAE94XoiEgvtr0/r2Xw7R8/IjK3sKLSiDoCHWcsXSAinVaKlGRZDvMCsF1kbzjA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.20.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/punycode.js": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
|
||||
"integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.55.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.55.1.tgz",
|
||||
@@ -1450,6 +2239,12 @@
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/rope-sequence": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz",
|
||||
"integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
@@ -1465,6 +2260,12 @@
|
||||
"integrity": "sha512-RJbVP1HRDlUUXahJ7VMTcu9Rm1Nzw+EBpoPr94vnbD4LwR715F3CcxE2G2k45PewcaZ57pjetYa+LoSJLAASgA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/uc.micro": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
|
||||
"integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.4.21",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
|
||||
@@ -1573,6 +2374,12 @@
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/w3c-keyname": {
|
||||
"version": "2.2.8",
|
||||
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
|
||||
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tiptap/extension-placeholder": "^3.15.3",
|
||||
"@tiptap/pm": "^3.15.3",
|
||||
"@tiptap/starter-kit": "^3.15.3",
|
||||
"@tiptap/vue-3": "^3.15.3",
|
||||
"axios": "^1.6.0",
|
||||
"marked": "^9.1.0",
|
||||
"pinia": "^2.1.0",
|
||||
|
||||
44
src/App.vue
44
src/App.vue
@@ -8,18 +8,32 @@
|
||||
<!-- 左侧面板 -->
|
||||
<WriterPanel v-if="currentPage === 'writer'" />
|
||||
<AnalysisPanel v-else-if="currentPage === 'analysis'" />
|
||||
<DocumentsPanel v-else-if="currentPage === 'documents'" />
|
||||
<DocumentsPanel
|
||||
v-else-if="currentPage === 'documents'"
|
||||
@toggle-version-panel="toggleVersionPanel"
|
||||
@document-selected="handleDocumentSelected"
|
||||
/>
|
||||
<MaterialsPanel v-else-if="currentPage === 'materials'" />
|
||||
<SettingsPanel v-else-if="currentPage === 'settings'" />
|
||||
|
||||
<!-- 右侧主内容区 -->
|
||||
<MainContent />
|
||||
|
||||
<!-- 版本历史面板(仅文稿管理页面显示) -->
|
||||
<DocumentVersionPanel
|
||||
v-if="currentPage === 'documents'"
|
||||
:visible="showVersionPanel"
|
||||
:document-id="selectedDocumentId"
|
||||
:current-content="selectedDocumentContent"
|
||||
@close="showVersionPanel = false"
|
||||
@restore="handleVersionRestore"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { ref, computed } from 'vue'
|
||||
import { useAppStore } from './stores/app'
|
||||
import WriterPanel from './components/WriterPanel.vue'
|
||||
import AnalysisPanel from './components/AnalysisPanel.vue'
|
||||
@@ -28,7 +42,33 @@ import MaterialsPanel from './components/MaterialsPanel.vue'
|
||||
import SettingsPanel from './components/SettingsPanel.vue'
|
||||
import MainContent from './components/MainContent.vue'
|
||||
import ComparePanel from './components/ComparePanel.vue'
|
||||
import DocumentVersionPanel from './components/DocumentVersionPanel.vue'
|
||||
|
||||
const appStore = useAppStore()
|
||||
const currentPage = computed(() => appStore.currentPage)
|
||||
|
||||
// 版本历史面板状态
|
||||
const showVersionPanel = ref(false)
|
||||
const selectedDocumentId = ref(null)
|
||||
const selectedDocumentContent = ref('')
|
||||
|
||||
// 切换版本面板
|
||||
const toggleVersionPanel = () => {
|
||||
showVersionPanel.value = !showVersionPanel.value
|
||||
}
|
||||
|
||||
// 处理文稿选择
|
||||
const handleDocumentSelected = (doc) => {
|
||||
selectedDocumentId.value = doc?.id || null
|
||||
selectedDocumentContent.value = doc?.content || ''
|
||||
}
|
||||
|
||||
// 处理版本恢复
|
||||
const handleVersionRestore = (content) => {
|
||||
selectedDocumentContent.value = content
|
||||
// 刷新 MainContent 显示
|
||||
if (appStore.currentDocument) {
|
||||
appStore.currentDocument.content = content
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -153,7 +153,26 @@
|
||||
<h2 class="text-sm font-medium text-blue-400 flex items-center gap-2">
|
||||
✍️ 写作内容
|
||||
</h2>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-xs text-slate-500">{{ rightParagraphs.length }} 段</span>
|
||||
<!-- 保存按钮(仅当内容来自文稿库时显示) -->
|
||||
<button
|
||||
v-if="rightSourceType === 'document' && rightSourceDocId"
|
||||
@click="saveRightContent"
|
||||
:disabled="isSaving || !hasContentChanged"
|
||||
class="text-xs px-2 py-1 rounded flex items-center gap-1 transition"
|
||||
:class="hasContentChanged
|
||||
? 'bg-green-600 text-white hover:bg-green-500'
|
||||
: 'bg-slate-700 text-slate-400 cursor-not-allowed'"
|
||||
>
|
||||
<span v-if="isSaving" class="animate-spin">↻</span>
|
||||
<span v-else>💾</span>
|
||||
{{ isSaving ? '保存中...' : (hasContentChanged ? '保存版本' : '已保存') }}
|
||||
</button>
|
||||
<span v-if="rightSourceType === 'document' && rightSourceDocTitle" class="text-xs text-blue-400">
|
||||
📄 {{ rightSourceDocTitle }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 来源选择器 -->
|
||||
<div class="px-3 py-2 bg-slate-800/50 border-b border-slate-700 flex items-center gap-2">
|
||||
@@ -651,11 +670,18 @@
|
||||
>取消</button>
|
||||
<!-- 差异对比模式的应用选中按钮 -->
|
||||
<button
|
||||
v-if="rewriteViewMode !== 'partial' && diffSegments.length > 0 && acceptedChanges.size < reviewableChanges.length"
|
||||
v-if="rewriteViewMode === 'diff' && diffSegments.length > 0 && acceptedChanges.size < reviewableChanges.length"
|
||||
@click="applySelectedChangesToContent"
|
||||
:disabled="rewritingSuggestionIdx !== null || acceptedChanges.size === 0"
|
||||
class="px-4 py-2 text-sm bg-amber-600 text-white rounded-lg hover:bg-amber-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>应用选中 ({{ acceptedChanges.size }})</button>
|
||||
<!-- 差异对比模式的插入差异按钮 -->
|
||||
<button
|
||||
v-if="rewriteViewMode === 'diff' && diffSegments.length > 0"
|
||||
@click="insertSelectedChangesToContent"
|
||||
:disabled="rewritingSuggestionIdx !== null || acceptedChanges.size === 0"
|
||||
class="px-4 py-2 text-sm bg-cyan-600 text-white rounded-lg hover:bg-cyan-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>插入差异</button>
|
||||
<!-- 部分替换模式的应用按钮 -->
|
||||
<button
|
||||
v-if="rewriteViewMode === 'partial'"
|
||||
@@ -680,6 +706,7 @@ import { ref, computed, watch } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useAppStore } from '../stores/app'
|
||||
import { useDatabaseStore } from '../stores/database'
|
||||
import { updateDocument, saveDocumentVersion, getDocumentById } from '../db/index.js'
|
||||
import { SECTION_TYPES, getSectionTypeById, getSectionTypeClasses } from '../config/sectionTypes'
|
||||
import { getLogicParadigmById, buildLogicPrompt } from '../config/logicParadigms'
|
||||
import { computeDiff, applySelectedChanges as applyDiffChanges, getDiffStats, splitIntoSentencesWithPosition } from '../utils/textDiff'
|
||||
@@ -702,6 +729,10 @@ const showLeftDocSelector = ref(false)
|
||||
// 右侧来源相关
|
||||
const rightSourceType = ref('paste') // 'paste' | 'document'
|
||||
const showRightDocSelector = ref(false)
|
||||
const rightSourceDocId = ref(null) // 来源文稿ID
|
||||
const rightSourceDocTitle = ref('') // 来源文稿标题
|
||||
const rightOriginalContent = ref('') // 来源文稿原始内容(用于差异比对)
|
||||
const isSaving = ref(false) // 保存中状态
|
||||
|
||||
// 段落类型识别相关
|
||||
const isDetectingTypes = ref(false)
|
||||
@@ -814,6 +845,12 @@ const canCompare = computed(() => {
|
||||
return selectedLeftIdxs.value.length > 0 && selectedRightIdxs.value.length > 0
|
||||
})
|
||||
|
||||
// 右侧内容是否已修改(与原始内容对比)
|
||||
const hasContentChanged = computed(() => {
|
||||
if (rightSourceType.value !== 'document' || !rightSourceDocId.value) return false
|
||||
return rightContent.value !== rightOriginalContent.value
|
||||
})
|
||||
|
||||
// 选择段落(多选切换)
|
||||
const selectLeftParagraph = (idx) => {
|
||||
const i = selectedLeftIdxs.value.indexOf(idx)
|
||||
@@ -888,9 +925,55 @@ const handleLeftDocSelect = (doc) => {
|
||||
const handleRightDocSelect = (doc) => {
|
||||
rightContent.value = doc.content || ''
|
||||
rightSourceType.value = 'document'
|
||||
rightSourceDocId.value = doc.id // 保存文稿ID
|
||||
rightSourceDocTitle.value = doc.title // 保存文稿标题
|
||||
rightOriginalContent.value = doc.content || '' // 保存原始内容用于差异比对
|
||||
showRightDocSelector.value = false
|
||||
}
|
||||
|
||||
// 保存右侧内容到文稿
|
||||
const saveRightContent = async () => {
|
||||
if (rightSourceType.value !== 'document' || !rightSourceDocId.value) {
|
||||
alert('当前内容不是来自文稿库,无需保存')
|
||||
return
|
||||
}
|
||||
|
||||
if (!hasContentChanged.value) {
|
||||
alert('内容未修改,无需保存')
|
||||
return
|
||||
}
|
||||
|
||||
isSaving.value = true
|
||||
|
||||
try {
|
||||
// 计算差异用于生成版本说明
|
||||
const changes = computeDiff(rightOriginalContent.value, rightContent.value)
|
||||
const stats = getDiffStats(changes)
|
||||
|
||||
// 生成简短的变更说明
|
||||
let changeNote = `修改了 ${stats.modified} 处`
|
||||
if (stats.added > 0) changeNote += `,新增了 ${stats.added} 处`
|
||||
if (stats.removed > 0) changeNote += `,删除了 ${stats.removed} 处`
|
||||
|
||||
// 保存新版本
|
||||
const versionNumber = saveDocumentVersion(rightSourceDocId.value, rightContent.value, changeNote)
|
||||
|
||||
// 同时更新文稿主内容
|
||||
updateDocument(rightSourceDocId.value, { content: rightContent.value })
|
||||
|
||||
// 更新原始内容为当前内容(标记为已保存)
|
||||
rightOriginalContent.value = rightContent.value
|
||||
|
||||
console.log(`文稿保存成功,版本号: ${versionNumber},${changeNote}`)
|
||||
alert(`保存成功!新版本: v${versionNumber}\n${changeNote}`)
|
||||
} catch (error) {
|
||||
console.error('保存失败:', error)
|
||||
alert('保存失败,请重试')
|
||||
} finally {
|
||||
isSaving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 切换段落类型下拉菜单
|
||||
const toggleTypeDropdown = (idx) => {
|
||||
showTypeDropdown.value = showTypeDropdown.value === idx ? null : idx
|
||||
@@ -1042,6 +1125,14 @@ const executeRewrite = async (suggestionIdx, suggestion) => {
|
||||
finalContent = finalContent.replace(/<thinking>[\s\S]*?<\/thinking>/g, '').trim()
|
||||
// 也移除可能残留的单独标签
|
||||
finalContent = finalContent.replace(/<\/?thinking>/g, '').trim()
|
||||
// 提取 <draft>...</draft> 中的内容,或移除标签
|
||||
const draftMatch = finalContent.match(/<draft>([\s\S]*?)<\/draft>/)
|
||||
if (draftMatch) {
|
||||
finalContent = draftMatch[1].trim()
|
||||
} else {
|
||||
// 移除残留的 <draft> 或 </draft> 标签
|
||||
finalContent = finalContent.replace(/<\/?draft>/g, '').trim()
|
||||
}
|
||||
rewritePreview.value = finalContent
|
||||
|
||||
// 重写完成后自动计算差异
|
||||
@@ -1186,6 +1277,59 @@ const applySelectedChangesToContent = () => {
|
||||
resetRewriteModal()
|
||||
}
|
||||
|
||||
// 将选中的修改插入到原文(不替换原文内容)
|
||||
const insertSelectedChangesToContent = () => {
|
||||
if (diffSegments.value.length === 0) return
|
||||
|
||||
// 如果没有选中任何修改,提示用户并返回
|
||||
if (acceptedChanges.value.size === 0) {
|
||||
alert('请先选中需要插入的内容')
|
||||
return
|
||||
}
|
||||
|
||||
// 收集选中的修改片段的重写内容
|
||||
const selectedSegments = diffSegments.value.filter(seg =>
|
||||
acceptedChanges.value.has(seg.idx) &&
|
||||
(seg.type === 'modified' || seg.type === 'added')
|
||||
)
|
||||
|
||||
if (selectedSegments.length === 0) {
|
||||
alert('没有可插入的内容(请选择"修改"或"新增"类型的差异)')
|
||||
return
|
||||
}
|
||||
|
||||
// 获取要插入的内容
|
||||
const insertContent = selectedSegments.map(seg => seg.rewritten).join('')
|
||||
|
||||
// 找到第一个选中修改对应的原文位置,在其后插入
|
||||
const firstSegment = selectedSegments[0]
|
||||
let finalContent = originalContent.value
|
||||
|
||||
if (firstSegment.originalEnd !== undefined) {
|
||||
// 在原文对应位置后插入新内容
|
||||
const insertPosition = firstSegment.originalEnd
|
||||
finalContent = originalContent.value.slice(0, insertPosition) +
|
||||
insertContent +
|
||||
originalContent.value.slice(insertPosition)
|
||||
} else {
|
||||
// 如果没有位置信息,追加到末尾
|
||||
finalContent = originalContent.value + '\n\n' + insertContent
|
||||
}
|
||||
|
||||
// 防止结果为空
|
||||
if (!finalContent || finalContent.trim() === '') {
|
||||
console.error('插入操作返回空结果,保持原内容不变')
|
||||
alert('插入失败,请尝试其他方式')
|
||||
return
|
||||
}
|
||||
|
||||
// 执行更新
|
||||
updateRightContent(targetParagraphIdxs.value, finalContent)
|
||||
|
||||
// 关闭浮窗并重置状态
|
||||
resetRewriteModal()
|
||||
}
|
||||
|
||||
// 重置重写浮窗状态
|
||||
const resetRewriteModal = () => {
|
||||
showRewriteModal.value = false
|
||||
|
||||
270
src/components/DocumentVersionPanel.vue
Normal file
270
src/components/DocumentVersionPanel.vue
Normal file
@@ -0,0 +1,270 @@
|
||||
<template>
|
||||
<aside
|
||||
v-if="visible && documentId"
|
||||
class="w-80 h-screen flex flex-col border-l border-slate-700 bg-slate-800 shrink-0"
|
||||
>
|
||||
<!-- 头部 -->
|
||||
<header class="p-4 border-b border-slate-700 flex items-center justify-between">
|
||||
<h2 class="font-bold text-white flex items-center gap-2">
|
||||
<span class="text-xl">🕐</span> 版本历史
|
||||
</h2>
|
||||
<button
|
||||
@click="$emit('close')"
|
||||
class="text-slate-400 hover:text-white transition"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<div v-if="isLoading" class="flex-1 flex items-center justify-center">
|
||||
<span class="text-slate-500 animate-pulse">加载中...</span>
|
||||
</div>
|
||||
|
||||
<!-- 版本列表 -->
|
||||
<div v-else-if="versions.length > 0" class="flex-1 overflow-y-auto p-4 space-y-3">
|
||||
<div
|
||||
v-for="(version, idx) in versions"
|
||||
:key="version.id"
|
||||
:class="[
|
||||
'p-3 rounded-lg border transition',
|
||||
idx === 0
|
||||
? 'bg-blue-900/30 border-blue-500'
|
||||
: 'bg-slate-900/50 border-slate-700 hover:border-slate-500'
|
||||
]"
|
||||
>
|
||||
<!-- 版本号和时间 -->
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-bold text-white">v{{ version.version_number }}</span>
|
||||
<span v-if="idx === 0" class="text-xs px-1.5 py-0.5 rounded bg-blue-600 text-white">
|
||||
当前
|
||||
</span>
|
||||
</div>
|
||||
<span class="text-xs text-slate-500">{{ formatDate(version.created_at) }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 变更说明 -->
|
||||
<p class="text-xs text-slate-400 mb-3">
|
||||
{{ version.change_note || '无变更说明' }}
|
||||
</p>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div v-if="idx > 0" class="flex gap-2">
|
||||
<button
|
||||
@click="restoreVersion(version)"
|
||||
class="flex-1 text-xs py-1.5 rounded bg-amber-600 text-white hover:bg-amber-500 transition"
|
||||
>
|
||||
恢复
|
||||
</button>
|
||||
<button
|
||||
@click="compareVersion(version)"
|
||||
class="flex-1 text-xs py-1.5 rounded bg-slate-600 text-white hover:bg-slate-500 transition"
|
||||
>
|
||||
对比
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 无版本记录 -->
|
||||
<div v-else class="flex-1 flex flex-col items-center justify-center text-slate-500">
|
||||
<span class="text-4xl mb-2">📋</span>
|
||||
<p class="text-sm">暂无版本记录</p>
|
||||
<p class="text-xs text-slate-600 mt-1">保存文稿后将自动记录版本</p>
|
||||
</div>
|
||||
|
||||
<!-- 版本对比弹窗 -->
|
||||
<div
|
||||
v-if="showCompareModal"
|
||||
class="fixed inset-0 bg-black/70 flex items-center justify-center z-50"
|
||||
@click.self="showCompareModal = false"
|
||||
>
|
||||
<div class="bg-slate-800 rounded-lg w-[900px] max-h-[80vh] flex flex-col border border-slate-600">
|
||||
<header class="p-4 border-b border-slate-700 flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="font-bold text-white">
|
||||
版本对比:v{{ comparingVersion?.version_number }} → 当前版本
|
||||
</h3>
|
||||
<div class="text-xs text-slate-500 mt-1 flex gap-4">
|
||||
<span><span class="inline-block w-3 h-3 rounded bg-red-500/50 mr-1"></span>删除</span>
|
||||
<span><span class="inline-block w-3 h-3 rounded bg-green-500/50 mr-1"></span>新增</span>
|
||||
<span><span class="inline-block w-3 h-3 rounded bg-amber-500/50 mr-1"></span>修改</span>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
@click="showCompareModal = false"
|
||||
class="text-slate-400 hover:text-white"
|
||||
>✕</button>
|
||||
</header>
|
||||
<div class="flex-1 overflow-y-auto p-4">
|
||||
<div class="flex gap-4">
|
||||
<!-- 旧版本(带高亮) -->
|
||||
<div class="flex-1">
|
||||
<div class="text-xs text-amber-400 mb-2 font-medium">
|
||||
v{{ comparingVersion?.version_number }} 版本
|
||||
</div>
|
||||
<div class="bg-slate-900 rounded-lg p-3 border border-slate-700 text-sm text-slate-300 max-h-[400px] overflow-y-auto leading-relaxed">
|
||||
<template v-for="(segment, idx) in diffSegments" :key="'old-' + idx">
|
||||
<span
|
||||
v-if="segment.type === 'unchanged'"
|
||||
class="text-slate-300"
|
||||
>{{ segment.original }}</span>
|
||||
<span
|
||||
v-else-if="segment.type === 'modified'"
|
||||
class="bg-amber-500/30 text-amber-200 px-0.5 rounded"
|
||||
>{{ segment.original }}</span>
|
||||
<span
|
||||
v-else-if="segment.type === 'removed'"
|
||||
class="bg-red-500/30 text-red-200 px-0.5 rounded line-through"
|
||||
>{{ segment.original }}</span>
|
||||
<!-- added 类型在旧版本中不显示 -->
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 当前版本(带高亮) -->
|
||||
<div class="flex-1">
|
||||
<div class="text-xs text-blue-400 mb-2 font-medium">
|
||||
当前版本
|
||||
</div>
|
||||
<div class="bg-slate-900 rounded-lg p-3 border border-slate-700 text-sm text-slate-300 max-h-[400px] overflow-y-auto leading-relaxed">
|
||||
<template v-for="(segment, idx) in diffSegments" :key="'new-' + idx">
|
||||
<span
|
||||
v-if="segment.type === 'unchanged'"
|
||||
class="text-slate-300"
|
||||
>{{ segment.rewritten }}</span>
|
||||
<span
|
||||
v-else-if="segment.type === 'modified'"
|
||||
class="bg-amber-500/30 text-amber-200 px-0.5 rounded"
|
||||
>{{ segment.rewritten }}</span>
|
||||
<span
|
||||
v-else-if="segment.type === 'added'"
|
||||
class="bg-green-500/30 text-green-200 px-0.5 rounded"
|
||||
>{{ segment.rewritten }}</span>
|
||||
<!-- removed 类型在新版本中不显示 -->
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="p-4 border-t border-slate-700 flex justify-between items-center">
|
||||
<div class="text-xs text-slate-500">
|
||||
共 {{ diffStats.total }} 处差异
|
||||
<span v-if="diffStats.modified > 0" class="text-amber-400 ml-2">{{ diffStats.modified }} 处修改</span>
|
||||
<span v-if="diffStats.added > 0" class="text-green-400 ml-2">{{ diffStats.added }} 处新增</span>
|
||||
<span v-if="diffStats.removed > 0" class="text-red-400 ml-2">{{ diffStats.removed }} 处删除</span>
|
||||
</div>
|
||||
<button
|
||||
@click="showCompareModal = false"
|
||||
class="px-4 py-2 text-sm bg-slate-600 text-white rounded-lg hover:bg-slate-500"
|
||||
>
|
||||
关闭
|
||||
</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, computed, defineProps, defineEmits } from 'vue'
|
||||
import { getDocumentVersions, getDocumentById, saveDocumentVersion, updateDocument } from '../db/index.js'
|
||||
import { computeDiff, getDiffStats } from '../utils/textDiff.js'
|
||||
|
||||
const props = defineProps({
|
||||
visible: Boolean,
|
||||
documentId: String,
|
||||
currentContent: String
|
||||
})
|
||||
|
||||
const emit = defineEmits(['close', 'restore'])
|
||||
|
||||
// 状态
|
||||
const isLoading = ref(false)
|
||||
const versions = ref([])
|
||||
const showCompareModal = ref(false)
|
||||
const comparingVersion = ref(null)
|
||||
const diffSegments = ref([])
|
||||
|
||||
// 差异统计
|
||||
const diffStats = computed(() => {
|
||||
return getDiffStats(diffSegments.value)
|
||||
})
|
||||
|
||||
// 加载版本历史
|
||||
const loadVersions = async () => {
|
||||
if (!props.documentId) {
|
||||
versions.value = []
|
||||
return
|
||||
}
|
||||
|
||||
isLoading.value = true
|
||||
try {
|
||||
versions.value = getDocumentVersions(props.documentId)
|
||||
} catch (error) {
|
||||
console.error('加载版本历史失败:', error)
|
||||
versions.value = []
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (dateStr) => {
|
||||
if (!dateStr) return ''
|
||||
const date = new Date(dateStr)
|
||||
return date.toLocaleDateString('zh-CN', {
|
||||
month: 'numeric',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})
|
||||
}
|
||||
|
||||
// 恢复版本
|
||||
const restoreVersion = async (version) => {
|
||||
if (!confirm(`确定要恢复到 v${version.version_number} 版本吗?\n当前内容将保存为新版本。`)) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 先保存当前内容为新版本
|
||||
saveDocumentVersion(props.documentId, props.currentContent, '恢复前自动保存')
|
||||
|
||||
// 更新文稿内容为旧版本
|
||||
updateDocument(props.documentId, { content: version.content })
|
||||
|
||||
// 触发恢复事件,通知父组件刷新
|
||||
emit('restore', version.content)
|
||||
|
||||
// 重新加载版本列表
|
||||
await loadVersions()
|
||||
|
||||
alert(`已恢复到 v${version.version_number} 版本`)
|
||||
} catch (error) {
|
||||
console.error('恢复版本失败:', error)
|
||||
alert('恢复失败,请重试')
|
||||
}
|
||||
}
|
||||
|
||||
// 对比版本
|
||||
const compareVersion = (version) => {
|
||||
comparingVersion.value = version
|
||||
// 计算差异
|
||||
diffSegments.value = computeDiff(version.content || '', props.currentContent || '')
|
||||
showCompareModal.value = true
|
||||
}
|
||||
|
||||
// 监听文稿ID变化
|
||||
watch(() => props.documentId, () => {
|
||||
loadVersions()
|
||||
}, { immediate: true })
|
||||
|
||||
// 监听面板显示
|
||||
watch(() => props.visible, (newVal) => {
|
||||
if (newVal) {
|
||||
loadVersions()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -86,6 +86,12 @@
|
||||
>
|
||||
📋 复制
|
||||
</button>
|
||||
<button
|
||||
@click="toggleVersionPanel"
|
||||
class="flex-1 text-xs py-2 rounded bg-cyan-600 text-white hover:bg-cyan-500 transition"
|
||||
>
|
||||
🕰️ 版本
|
||||
</button>
|
||||
<button
|
||||
@click="confirmDelete"
|
||||
class="text-xs px-3 py-2 rounded bg-red-900/50 text-red-300 hover:bg-red-800/50 transition"
|
||||
@@ -128,6 +134,9 @@ import { storeToRefs } from 'pinia'
|
||||
const appStore = useAppStore()
|
||||
const dbStore = useDatabaseStore()
|
||||
|
||||
// 定义事件
|
||||
const emit = defineEmits(['toggle-version-panel', 'document-selected'])
|
||||
|
||||
// 状态
|
||||
const selectedDocId = ref(null)
|
||||
const currentFilter = ref('all')
|
||||
@@ -185,6 +194,14 @@ const loadDocuments = async () => {
|
||||
// 选择文稿
|
||||
const selectDocument = (doc) => {
|
||||
selectedDocId.value = selectedDocId.value === doc.id ? null : doc.id
|
||||
// 通知父组件
|
||||
const selectedDoc = selectedDocId.value ? doc : null
|
||||
emit('document-selected', selectedDoc)
|
||||
}
|
||||
|
||||
// 切换版本面板
|
||||
const toggleVersionPanel = () => {
|
||||
emit('toggle-version-panel')
|
||||
}
|
||||
|
||||
// 创建新文稿
|
||||
|
||||
@@ -175,7 +175,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 文稿内容编辑器 -->
|
||||
<div class="flex-1 min-h-[400px]">
|
||||
<div class="flex-1 min-h-[60vh]">
|
||||
<textarea
|
||||
v-model="documentContent"
|
||||
class="w-full h-full bg-slate-900/50 border border-slate-700 rounded-lg p-4 text-slate-200 outline-none focus:border-blue-500 resize-none font-mono text-sm leading-relaxed"
|
||||
|
||||
260
src/components/MarkdownEditor.vue
Normal file
260
src/components/MarkdownEditor.vue
Normal file
@@ -0,0 +1,260 @@
|
||||
<template>
|
||||
<div class="markdown-editor h-full flex flex-col">
|
||||
<!-- 工具栏 -->
|
||||
<div class="toolbar flex items-center gap-1 p-2 bg-slate-800 border border-slate-700 rounded-t-lg">
|
||||
<button
|
||||
@click="editor?.chain().focus().toggleBold().run()"
|
||||
:class="['toolbar-btn', { 'is-active': editor?.isActive('bold') }]"
|
||||
title="粗体 (Ctrl+B)"
|
||||
>
|
||||
<span class="font-bold">B</span>
|
||||
</button>
|
||||
<button
|
||||
@click="editor?.chain().focus().toggleItalic().run()"
|
||||
:class="['toolbar-btn', { 'is-active': editor?.isActive('italic') }]"
|
||||
title="斜体 (Ctrl+I)"
|
||||
>
|
||||
<span class="italic">I</span>
|
||||
</button>
|
||||
<button
|
||||
@click="editor?.chain().focus().toggleStrike().run()"
|
||||
:class="['toolbar-btn', { 'is-active': editor?.isActive('strike') }]"
|
||||
title="删除线"
|
||||
>
|
||||
<span class="line-through">S</span>
|
||||
</button>
|
||||
|
||||
<div class="h-4 w-px bg-slate-600 mx-1"></div>
|
||||
|
||||
<button
|
||||
@click="editor?.chain().focus().toggleHeading({ level: 1 }).run()"
|
||||
:class="['toolbar-btn', { 'is-active': editor?.isActive('heading', { level: 1 }) }]"
|
||||
title="一级标题"
|
||||
>
|
||||
H1
|
||||
</button>
|
||||
<button
|
||||
@click="editor?.chain().focus().toggleHeading({ level: 2 }).run()"
|
||||
:class="['toolbar-btn', { 'is-active': editor?.isActive('heading', { level: 2 }) }]"
|
||||
title="二级标题"
|
||||
>
|
||||
H2
|
||||
</button>
|
||||
<button
|
||||
@click="editor?.chain().focus().toggleHeading({ level: 3 }).run()"
|
||||
:class="['toolbar-btn', { 'is-active': editor?.isActive('heading', { level: 3 }) }]"
|
||||
title="三级标题"
|
||||
>
|
||||
H3
|
||||
</button>
|
||||
|
||||
<div class="h-4 w-px bg-slate-600 mx-1"></div>
|
||||
|
||||
<button
|
||||
@click="editor?.chain().focus().toggleBulletList().run()"
|
||||
:class="['toolbar-btn', { 'is-active': editor?.isActive('bulletList') }]"
|
||||
title="无序列表"
|
||||
>
|
||||
•
|
||||
</button>
|
||||
<button
|
||||
@click="editor?.chain().focus().toggleOrderedList().run()"
|
||||
:class="['toolbar-btn', { 'is-active': editor?.isActive('orderedList') }]"
|
||||
title="有序列表"
|
||||
>
|
||||
1.
|
||||
</button>
|
||||
|
||||
<div class="h-4 w-px bg-slate-600 mx-1"></div>
|
||||
|
||||
<button
|
||||
@click="editor?.chain().focus().toggleBlockquote().run()"
|
||||
:class="['toolbar-btn', { 'is-active': editor?.isActive('blockquote') }]"
|
||||
title="引用"
|
||||
>
|
||||
❝
|
||||
</button>
|
||||
<button
|
||||
@click="editor?.chain().focus().toggleCodeBlock().run()"
|
||||
:class="['toolbar-btn', { 'is-active': editor?.isActive('codeBlock') }]"
|
||||
title="代码块"
|
||||
>
|
||||
</>
|
||||
</button>
|
||||
<button
|
||||
@click="editor?.chain().focus().setHorizontalRule().run()"
|
||||
class="toolbar-btn"
|
||||
title="分割线"
|
||||
>
|
||||
─
|
||||
</button>
|
||||
|
||||
<div class="flex-1"></div>
|
||||
|
||||
<button
|
||||
@click="editor?.chain().focus().undo().run()"
|
||||
:disabled="!editor?.can().undo()"
|
||||
class="toolbar-btn disabled:opacity-30"
|
||||
title="撤销 (Ctrl+Z)"
|
||||
>
|
||||
↶
|
||||
</button>
|
||||
<button
|
||||
@click="editor?.chain().focus().redo().run()"
|
||||
:disabled="!editor?.can().redo()"
|
||||
class="toolbar-btn disabled:opacity-30"
|
||||
title="重做 (Ctrl+Y)"
|
||||
>
|
||||
↷
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 编辑区域 -->
|
||||
<div class="flex-1 overflow-y-auto bg-slate-900/50 border border-t-0 border-slate-700 rounded-b-lg">
|
||||
<editor-content
|
||||
:editor="editor"
|
||||
class="h-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { watch, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { useEditor, EditorContent } from '@tiptap/vue-3'
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
import Placeholder from '@tiptap/extension-placeholder'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '开始写作...'
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
// 创建编辑器实例
|
||||
const editor = useEditor({
|
||||
content: props.modelValue,
|
||||
extensions: [
|
||||
StarterKit.configure({
|
||||
heading: {
|
||||
levels: [1, 2, 3]
|
||||
}
|
||||
}),
|
||||
Placeholder.configure({
|
||||
placeholder: props.placeholder
|
||||
})
|
||||
],
|
||||
editorProps: {
|
||||
attributes: {
|
||||
class: 'prose prose-invert max-w-none p-4 h-full outline-none focus:outline-none'
|
||||
}
|
||||
},
|
||||
onUpdate: ({ editor }) => {
|
||||
emit('update:modelValue', editor.getHTML())
|
||||
}
|
||||
})
|
||||
|
||||
// 监听外部值变化
|
||||
watch(() => props.modelValue, (newValue) => {
|
||||
if (editor.value && editor.value.getHTML() !== newValue) {
|
||||
editor.value.commands.setContent(newValue || '')
|
||||
}
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
editor.value?.destroy()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.toolbar-btn {
|
||||
@apply w-8 h-8 flex items-center justify-center rounded text-sm text-slate-300 hover:bg-slate-700 hover:text-white transition;
|
||||
}
|
||||
|
||||
.toolbar-btn.is-active {
|
||||
@apply bg-blue-600 text-white;
|
||||
}
|
||||
|
||||
/* 编辑器内容样式 */
|
||||
:deep(.ProseMirror) {
|
||||
min-height: 100%;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
:deep(.ProseMirror:focus) {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
:deep(.ProseMirror p.is-editor-empty:first-child::before) {
|
||||
content: attr(data-placeholder);
|
||||
float: left;
|
||||
color: #64748b;
|
||||
pointer-events: none;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/* Prose 样式覆盖 */
|
||||
:deep(.ProseMirror h1) {
|
||||
@apply text-2xl font-bold text-white mt-6 mb-4;
|
||||
}
|
||||
|
||||
:deep(.ProseMirror h2) {
|
||||
@apply text-xl font-bold text-white mt-5 mb-3;
|
||||
}
|
||||
|
||||
:deep(.ProseMirror h3) {
|
||||
@apply text-lg font-bold text-white mt-4 mb-2;
|
||||
}
|
||||
|
||||
:deep(.ProseMirror p) {
|
||||
@apply text-slate-200 leading-relaxed mb-4;
|
||||
}
|
||||
|
||||
:deep(.ProseMirror ul),
|
||||
:deep(.ProseMirror ol) {
|
||||
@apply pl-6 mb-4;
|
||||
}
|
||||
|
||||
:deep(.ProseMirror li) {
|
||||
@apply text-slate-200 mb-1;
|
||||
}
|
||||
|
||||
:deep(.ProseMirror blockquote) {
|
||||
@apply border-l-4 border-blue-500 pl-4 italic text-slate-400 my-4;
|
||||
}
|
||||
|
||||
:deep(.ProseMirror code) {
|
||||
@apply bg-slate-700 px-1 py-0.5 rounded text-blue-300 text-sm;
|
||||
}
|
||||
|
||||
:deep(.ProseMirror pre) {
|
||||
@apply bg-slate-800 p-4 rounded-lg my-4 overflow-x-auto;
|
||||
}
|
||||
|
||||
:deep(.ProseMirror pre code) {
|
||||
@apply bg-transparent p-0;
|
||||
}
|
||||
|
||||
:deep(.ProseMirror hr) {
|
||||
@apply border-slate-700 my-6;
|
||||
}
|
||||
|
||||
:deep(.ProseMirror strong) {
|
||||
@apply font-bold text-white;
|
||||
}
|
||||
|
||||
:deep(.ProseMirror em) {
|
||||
@apply italic text-slate-300;
|
||||
}
|
||||
|
||||
:deep(.ProseMirror s) {
|
||||
@apply line-through text-slate-500;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user