feat(frontend): add TipTap editor and Reasoning panel
Phase 2 implementation:
- Add TipTap editor with bold, italic, headings, lists, code blocks
- Add Reasoning panel with editable reasoning_type, confidence, steps
- Add Markdown to TipTap conversion on document load
- Add PUT /documents/{id}/content endpoint integration
- Add PATCH /documents/{id}/reasoning endpoint integration
- New types: ReasoningStep, ReasoningPanelData, TipTapContentResponse
- New store methods: updateReasoning, addReasoningStep, deleteReasoningStep
- New components: TipTapEditor.vue, ReasoningPanel.vue
This commit is contained in:
864
package-lock.json
generated
864
package-lock.json
generated
@@ -8,6 +8,10 @@
|
||||
"name": "claudia-docs-frontend",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@tiptap/extension-code-block-lowlight": "^3.21.0",
|
||||
"@tiptap/starter-kit": "^3.21.0",
|
||||
"@tiptap/vue-3": "^3.21.0",
|
||||
"lowlight": "^3.3.0",
|
||||
"pinia": "^2.1.7",
|
||||
"vue": "^3.4.21",
|
||||
"vue-router": "^4.3.0"
|
||||
@@ -456,12 +460,43 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "1.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz",
|
||||
"integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/utils": "^0.2.11"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/dom": {
|
||||
"version": "1.7.6",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz",
|
||||
"integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/core": "^1.7.5",
|
||||
"@floating-ui/utils": "^0.2.11"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/utils": {
|
||||
"version": "0.2.11",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz",
|
||||
"integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==",
|
||||
"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.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz",
|
||||
@@ -851,6 +886,450 @@
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@tiptap/core": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.21.0.tgz",
|
||||
"integrity": "sha512-IfnQiuEeabDSPr1C/zHFTbnvlTf5z0DE/d/xz4C6bkL4ZBDJ3rr99h2qsaV0l8F+kbNswZMlQdM8rxNlMy95fQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/pm": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-blockquote": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-3.21.0.tgz",
|
||||
"integrity": "sha512-JDM/RR6rM0dMCZ1UnEf7eqmN6pAdIa2llhN+E24HdTGNJCklMFhLAGE/OT8/1r7M0WWA9GVO7/PTe4EdGh6+lQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-bold": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-3.21.0.tgz",
|
||||
"integrity": "sha512-iyEJRzG7XTCPlHwEDzUw3HnuYYCfL7lNpcCHmxcpYMrIUA8rv7EUxerIwApT6xY8hQ/07ljuJKgOyPvnJOOzuA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-bubble-menu": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.21.0.tgz",
|
||||
"integrity": "sha512-/fabRRhhf8i4LAx9e8xz9ppqN5KgdJk3TxMuxAD5vAWGsejvhSoPa8O8H/QwwyntXm1Vue8aQiMHsUk48b2hGQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.21.0",
|
||||
"@tiptap/pm": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-bullet-list": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-3.21.0.tgz",
|
||||
"integrity": "sha512-PWNF+xwxgOeXYGD88sCQLKL0eBoQqjUnZNALxBjN3Y7x4llalh42rHOp2Nt2t6UbQgqTBtBzU/uFcussTpxreQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extension-list": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-code": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-3.21.0.tgz",
|
||||
"integrity": "sha512-D7wA9jp+4X2r1f3FIoga73s6Rn4rmZY57Jes6a4rK3HY+3yHk1r057pPIZSY8Drfs97jxHQVFdfUYUomLSFYBA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-code-block": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-3.21.0.tgz",
|
||||
"integrity": "sha512-zrVOcOzDCjHQ8NJcC+qHmZZKiwnP/NMSb3qVJlSMN8TzuHept1MZCDa2Mbo70O6I0txo456SGuXB9sqV1vHmGg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.21.0",
|
||||
"@tiptap/pm": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-code-block-lowlight": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-3.21.0.tgz",
|
||||
"integrity": "sha512-79sS0tqoGVX6wq30ejzohpUVLeGOkTTUn5hCqjsniyYPTEtrn4tHyBnS4Du2TbrDV4SqcwgisWExhuB8pfEdYQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.21.0",
|
||||
"@tiptap/extension-code-block": "^3.21.0",
|
||||
"@tiptap/pm": "^3.21.0",
|
||||
"highlight.js": "^11",
|
||||
"lowlight": "^2 || ^3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-document": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-3.21.0.tgz",
|
||||
"integrity": "sha512-7oCyzXI9ChvJQUlr23AURdfVar4OIsrYUvqdhEwo3bjcI/Q/j0KJiXfuh6ZzL5eVaINSailH53sZaGg4THQtUg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-dropcursor": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-3.21.0.tgz",
|
||||
"integrity": "sha512-6fsDSVAM2iz7eElvT6iivMrGBGjIP/oPigVZ/SPm6f31phaYhz6TIOEgV/Lr2jaPIOgyK4U0cU4Yd4KUBCmhzQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extensions": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-floating-menu": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-3.21.0.tgz",
|
||||
"integrity": "sha512-n2HzTB+I/5rAl8R/1sKMv92JiY1oDK1hroXizxEKYa6dskJcAMW0CfYyPcPOZWQQEe7qoeOvQISr2ooLAKW+Mw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@floating-ui/dom": "^1.0.0",
|
||||
"@tiptap/core": "^3.21.0",
|
||||
"@tiptap/pm": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-gapcursor": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-3.21.0.tgz",
|
||||
"integrity": "sha512-wGjgAoYBTvPAe9QYMI5px355XcNeMkaUrMY9IHbMqgqdmHcDxqooxM4H6sYVX2CRcHwXy4I8NQAoOhSYrQJDMg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extensions": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-hard-break": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-3.21.0.tgz",
|
||||
"integrity": "sha512-6JFVSAOQ1qhQHi9mVcdn2/XO8YIMgYV8zjarzNUzP6Sf2waeE5BLXjlg6rIH/945sY1J+FndTojLru6gQ07a5A==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-heading": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-3.21.0.tgz",
|
||||
"integrity": "sha512-ji6VJmoRnDzAHYflEYEZohMHRi77UGLW1o3ua7UhI32iJ9nuYssbPNuzEeE4SvENMQwZRszad5+a+dKAa+NC7g==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-horizontal-rule": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.21.0.tgz",
|
||||
"integrity": "sha512-vNBnOfFEY62CoJPGo4nonRM7RiOvhII1vhoO+WFr1GxDqCAfmEFjToflt7JT1UJdo6lMVcD+aaaAgOiuSz5p6g==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.21.0",
|
||||
"@tiptap/pm": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-italic": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.21.0.tgz",
|
||||
"integrity": "sha512-2I8oPvwyXhRn1k8lbDFIutzvhtLEjoO5mmQCNX4TnT4PdxxaSrK9+ihYg12VeqhUeO7dg1MKiFqws0HVBrwzWg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-link": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-3.21.0.tgz",
|
||||
"integrity": "sha512-oMU7Yve1sbgBsaFAUc2R0GPf4d3ZPVJeMUFC6b6X9rJIvx/IhEUEn9toQcSBGfp02uWK9NdQyIFYFdWlVXH++w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"linkifyjs": "^4.3.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.21.0",
|
||||
"@tiptap/pm": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-list": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.21.0.tgz",
|
||||
"integrity": "sha512-KeBlEtLrGce2d3dgL89hmwWEtREuzlW4XY5bYWpKNvCbFqvdSb3n7vkdkw32YclZmMWxAcABgW6ucCStkE0rsQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.21.0",
|
||||
"@tiptap/pm": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-list-item": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-3.21.0.tgz",
|
||||
"integrity": "sha512-1ZymZmlQVbAoC4q5x3cro0v5+3I6l+BHqbhIMQLjQFlAOJfcE0pvqRzAFW7PduxUj41tXEtsYqp2NREvO9F5Fg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extension-list": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-list-keymap": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-list-keymap/-/extension-list-keymap-3.21.0.tgz",
|
||||
"integrity": "sha512-EzrfW3ASNFPWKhR8sNOq7Kqw4hvaTAOn4dlI7chB8HIANSrlyPOUn+eKAnO6HQgsUgsbjg2GbTUrGrxcoLykUg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extension-list": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-ordered-list": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-3.21.0.tgz",
|
||||
"integrity": "sha512-+d+0orokMfqaBfvr9tUBgGvo2ZCV+fR3JzsJTmnLBWOkhBSJN7H4pnfXPTue0qwspUwRmkLJxdIlU+J7HkMrng==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/extension-list": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-paragraph": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-3.21.0.tgz",
|
||||
"integrity": "sha512-cMPG/jCoZ9NmLZ5ctFziILaxJGfDtMTb5OLBhifMFZeMVwF1pEJIygDEfnX/HSruv507weZSQG4pERO2tRszMg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-strike": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-3.21.0.tgz",
|
||||
"integrity": "sha512-easnVaN11Wl+5fOtfvzJ10J762S9TRXZaMj5rLBGavgf82DCYHqhGhBqpLQrJ41r4nPABGlYvTRoxfvBLB74Lg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-text": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.21.0.tgz",
|
||||
"integrity": "sha512-Zx8QdB8a5iBuE4uO21c3BjmpBfaJEr2Jd1QFnsdgx11fm6P7dGgZaGko1FaINhfOPRGTN6O/kiF02cDMdOHa/w==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-underline": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-3.21.0.tgz",
|
||||
"integrity": "sha512-gGmBEymbWnr8AIS8bI/bPw5rcwo7wAFcBw/TsLd1nAanu1dDqSRNDBrit3m02Ru+D88u2SfNvmbOPI1pz+1f5w==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extensions": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.21.0.tgz",
|
||||
"integrity": "sha512-MN1uh5PmHT1F2BNsbc21MIS0AMFFA73oODlp/4ckpBR4o5AxRwV+8f43Cd52UL4MgMkKj/A+QfZ7iK9IDb0h5A==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.21.0",
|
||||
"@tiptap/pm": "^3.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/pm": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.21.0.tgz",
|
||||
"integrity": "sha512-I3sNo7oMMsR6FFz1ecvPb9uCF0VQuS2WV67j8Io2M7DJicRWCE/GM5DaiYjTeWBbnByk6BuG0txoJATAqPVliQ==",
|
||||
"license": "MIT",
|
||||
"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.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-3.21.0.tgz",
|
||||
"integrity": "sha512-w7fWxglDtqXFBgRYH+LforJyUboSAQllnWQbGVSTyX4rsICqZjkb3f6CTSUWpGoGKmlmbb2ZpEuoik7tur9d8Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tiptap/core": "^3.21.0",
|
||||
"@tiptap/extension-blockquote": "^3.21.0",
|
||||
"@tiptap/extension-bold": "^3.21.0",
|
||||
"@tiptap/extension-bullet-list": "^3.21.0",
|
||||
"@tiptap/extension-code": "^3.21.0",
|
||||
"@tiptap/extension-code-block": "^3.21.0",
|
||||
"@tiptap/extension-document": "^3.21.0",
|
||||
"@tiptap/extension-dropcursor": "^3.21.0",
|
||||
"@tiptap/extension-gapcursor": "^3.21.0",
|
||||
"@tiptap/extension-hard-break": "^3.21.0",
|
||||
"@tiptap/extension-heading": "^3.21.0",
|
||||
"@tiptap/extension-horizontal-rule": "^3.21.0",
|
||||
"@tiptap/extension-italic": "^3.21.0",
|
||||
"@tiptap/extension-link": "^3.21.0",
|
||||
"@tiptap/extension-list": "^3.21.0",
|
||||
"@tiptap/extension-list-item": "^3.21.0",
|
||||
"@tiptap/extension-list-keymap": "^3.21.0",
|
||||
"@tiptap/extension-ordered-list": "^3.21.0",
|
||||
"@tiptap/extension-paragraph": "^3.21.0",
|
||||
"@tiptap/extension-strike": "^3.21.0",
|
||||
"@tiptap/extension-text": "^3.21.0",
|
||||
"@tiptap/extension-underline": "^3.21.0",
|
||||
"@tiptap/extensions": "^3.21.0",
|
||||
"@tiptap/pm": "^3.21.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/vue-3": {
|
||||
"version": "3.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/vue-3/-/vue-3-3.21.0.tgz",
|
||||
"integrity": "sha512-dfjxBwxg9+GNvsgkCbxLnj/vmG+YZMdcD/qF7bKM710bANWfqzimRUhH5W2KZcxqlYzqpz0u/P0zi7dUMR5IZA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tiptap/extension-bubble-menu": "^3.21.0",
|
||||
"@tiptap/extension-floating-menu": "^3.21.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@floating-ui/dom": "^1.0.0",
|
||||
"@tiptap/core": "^3.21.0",
|
||||
"@tiptap/pm": "^3.21.0",
|
||||
"vue": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
||||
@@ -858,6 +1337,43 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/hast": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
|
||||
"integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/unist": "*"
|
||||
}
|
||||
},
|
||||
"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/@types/unist": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
|
||||
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vitejs/plugin-vue": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz",
|
||||
@@ -1050,6 +1566,12 @@
|
||||
"dev": true,
|
||||
"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/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
@@ -1067,6 +1589,12 @@
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"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",
|
||||
@@ -1080,6 +1608,28 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dequal": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
||||
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/devlop": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
|
||||
"integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dequal": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
|
||||
@@ -1131,6 +1681,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",
|
||||
@@ -1162,6 +1724,45 @@
|
||||
"he": "bin/he"
|
||||
}
|
||||
},
|
||||
"node_modules/highlight.js": {
|
||||
"version": "11.11.1",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz",
|
||||
"integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"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/lowlight": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lowlight/-/lowlight-3.3.0.tgz",
|
||||
"integrity": "sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/hast": "^3.0.0",
|
||||
"devlop": "^1.0.0",
|
||||
"highlight.js": "~11.11.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.21",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||
@@ -1171,6 +1772,41 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||
}
|
||||
},
|
||||
"node_modules/markdown-it": {
|
||||
"version": "14.1.1",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz",
|
||||
"integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==",
|
||||
"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/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/minimatch": {
|
||||
"version": "9.0.9",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
|
||||
@@ -1212,6 +1848,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/path-browserify": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
|
||||
@@ -1275,6 +1917,210 @@
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-changeset": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.4.0.tgz",
|
||||
"integrity": "sha512-LvqH2v7Q2SF6yxatuPP2e8vSUKS/L+xAU7dPDC4RMyHMhZoGDfBC74mYuyYF4gLqOEG758wajtyhNnsTkuhvng==",
|
||||
"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.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.4.1.tgz",
|
||||
"integrity": "sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw==",
|
||||
"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.4",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.13.4.tgz",
|
||||
"integrity": "sha512-D98dm4cQ3Hs6EmjK500TdAOew4Z03EV71ajEFiWra3Upr7diytJsjF4mPV2dW+eK5uNectiRj0xFxYI9NLXDbw==",
|
||||
"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.3.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.3.0.tgz",
|
||||
"integrity": "sha512-TImyPXCHPcDsSka2/lwJ6WjTASr4re/qWq1yoTTuLOqfXucwF6VcRa2LWCkM/EyTD1UO3CUwiH8qURJoWJRxwg==",
|
||||
"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",
|
||||
"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",
|
||||
"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.12.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.12.0.tgz",
|
||||
"integrity": "sha512-GxboyN4AMIsoHNtz5uf2r2Ru551i5hWeCMD6E2Ib4Eogqoub0NflniaBPVQ4MrGE5yZ8JV9tUHg9qcZTTrcN4w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-view": {
|
||||
"version": "1.41.7",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.7.tgz",
|
||||
"integrity": "sha512-jUwKNCEIGiqdvhlS91/2QAg21e4dfU5bH2iwmSDQeosXJgKF7smG0YSplOWK0cjSNgIqXe7VXqo7EIfUFJdt3w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.20.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"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.60.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz",
|
||||
@@ -1320,6 +2166,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",
|
||||
@@ -1343,6 +2195,12 @@
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"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",
|
||||
@@ -1488,6 +2346,12 @@
|
||||
"peerDependencies": {
|
||||
"typescript": ">=5.0.0"
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tiptap/extension-code-block-lowlight": "^3.21.0",
|
||||
"@tiptap/starter-kit": "^3.21.0",
|
||||
"@tiptap/vue-3": "^3.21.0",
|
||||
"lowlight": "^3.3.0",
|
||||
"pinia": "^2.1.7",
|
||||
"vue": "^3.4.21",
|
||||
"vue-router": "^4.3.0"
|
||||
|
||||
514
src/components/editor/ReasoningPanel.vue
Normal file
514
src/components/editor/ReasoningPanel.vue
Normal file
@@ -0,0 +1,514 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import type { DocumentReasoning, ReasoningStep } from '@/types'
|
||||
import Button from '@/components/common/Button.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
documentId: string
|
||||
reasoning: DocumentReasoning | null
|
||||
editable?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update': [reasoning: DocumentReasoning]
|
||||
'add-step': [thought: string, conclusion: string | null]
|
||||
'delete-step': [step: number]
|
||||
}>()
|
||||
|
||||
const reasoningTypes = [
|
||||
{ value: 'chain', label: 'Chain' },
|
||||
{ value: 'idea', label: 'Idea' },
|
||||
{ value: 'context', label: 'Context' },
|
||||
{ value: 'reflection', label: 'Reflection' },
|
||||
] as const
|
||||
|
||||
const localReasoning = ref<DocumentReasoning | null>(null)
|
||||
const showAddStep = ref(false)
|
||||
const newStepThought = ref('')
|
||||
const newStepConclusion = ref('')
|
||||
|
||||
watch(() => props.reasoning, (newVal) => {
|
||||
localReasoning.value = newVal ? JSON.parse(JSON.stringify(newVal)) : null
|
||||
}, { immediate: true, deep: true })
|
||||
|
||||
const hasReasoning = computed(() => !!localReasoning.value && localReasoning.value.reasoning_type !== null)
|
||||
|
||||
function updateReasoningType(type: string) {
|
||||
if (!localReasoning.value) return
|
||||
localReasoning.value.reasoning_type = type as DocumentReasoning['reasoning_type']
|
||||
emitUpdate()
|
||||
}
|
||||
|
||||
function updateConfidence(value: string) {
|
||||
if (!localReasoning.value) return
|
||||
const num = parseFloat(value)
|
||||
if (!isNaN(num) && num >= 0 && num <= 1) {
|
||||
localReasoning.value.confidence = num
|
||||
emitUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
function updateStep(index: number, field: 'thought' | 'conclusion', value: string) {
|
||||
if (!localReasoning.value) return
|
||||
localReasoning.value.reasoning_steps[index][field] = value || null
|
||||
emitUpdate()
|
||||
}
|
||||
|
||||
function emitUpdate() {
|
||||
if (!localReasoning.value) return
|
||||
emit('update', { ...localReasoning.value })
|
||||
}
|
||||
|
||||
function openAddStep() {
|
||||
newStepThought.value = ''
|
||||
newStepConclusion.value = ''
|
||||
showAddStep.value = true
|
||||
}
|
||||
|
||||
function submitAddStep() {
|
||||
if (!newStepThought.value.trim()) return
|
||||
emit('add-step', newStepThought.value.trim(), newStepConclusion.value.trim() || null)
|
||||
showAddStep.value = false
|
||||
}
|
||||
|
||||
function removeStep(step: number) {
|
||||
emit('delete-step', step)
|
||||
}
|
||||
|
||||
function getConfidenceColor(confidence: number | null): string {
|
||||
if (confidence === null) return 'var(--text-secondary)'
|
||||
if (confidence >= 0.8) return '#22c55e'
|
||||
if (confidence >= 0.5) return '#eab308'
|
||||
return '#ef4444'
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<aside class="reasoning-panel">
|
||||
<div class="reasoning-panel__header">
|
||||
<h3 class="reasoning-panel__title">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M12 2a8 8 0 0 1 8 8c0 3.5-2 6-4 8l-2 2-2-2c-2-2-4-4.5-4-8a8 8 0 0 1 8-8z"/>
|
||||
<circle cx="12" cy="10" r="3"/>
|
||||
</svg>
|
||||
Reasoning
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div v-if="!hasReasoning" class="reasoning-panel__empty">
|
||||
<p>No reasoning data for this document.</p>
|
||||
<Button variant="secondary" size="small" @click="openAddStep">
|
||||
Add Reasoning
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div v-else class="reasoning-panel__content">
|
||||
<!-- Reasoning Type -->
|
||||
<div class="reasoning-panel__field">
|
||||
<label class="reasoning-panel__label">Type</label>
|
||||
<select
|
||||
v-if="editable"
|
||||
:value="localReasoning?.reasoning_type"
|
||||
class="reasoning-panel__select"
|
||||
@change="updateReasoningType(($event.target as HTMLSelectElement).value)"
|
||||
>
|
||||
<option v-for="rt in reasoningTypes" :key="rt.value" :value="rt.value">
|
||||
{{ rt.label }}
|
||||
</option>
|
||||
</select>
|
||||
<span v-else class="reasoning-panel__value">
|
||||
{{ reasoningTypes.find(r => r.value === localReasoning?.reasoning_type)?.label }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Confidence -->
|
||||
<div class="reasoning-panel__field">
|
||||
<label class="reasoning-panel__label">Confidence</label>
|
||||
<div class="reasoning-panel__confidence">
|
||||
<input
|
||||
v-if="editable"
|
||||
type="number"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.01"
|
||||
:value="localReasoning?.confidence"
|
||||
class="reasoning-panel__input"
|
||||
@change="updateConfidence(($event.target as HTMLInputElement).value)"
|
||||
/>
|
||||
<span
|
||||
v-else
|
||||
class="reasoning-panel__value"
|
||||
:style="{ color: getConfidenceColor(localReasoning?.confidence ?? null) }"
|
||||
>
|
||||
{{ localReasoning?.confidence !== null ? (localReasoning!.confidence * 100).toFixed(0) + '%' : 'N/A' }}
|
||||
</span>
|
||||
<div
|
||||
v-if="localReasoning?.confidence !== null"
|
||||
class="reasoning-panel__confidence-bar"
|
||||
>
|
||||
<div
|
||||
class="reasoning-panel__confidence-fill"
|
||||
:style="{
|
||||
width: ((localReasoning?.confidence ?? 0) * 100) + '%',
|
||||
backgroundColor: getConfidenceColor(localReasoning?.confidence ?? null)
|
||||
}"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Model Source -->
|
||||
<div v-if="localReasoning?.model_source" class="reasoning-panel__field">
|
||||
<label class="reasoning-panel__label">Model</label>
|
||||
<span class="reasoning-panel__value">{{ localReasoning.model_source }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Reasoning Steps -->
|
||||
<div class="reasoning-panel__steps">
|
||||
<div class="reasoning-panel__steps-header">
|
||||
<label class="reasoning-panel__label">Steps</label>
|
||||
<Button v-if="editable" variant="secondary" size="small" @click="openAddStep">
|
||||
+ Add
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="(step, index) in localReasoning?.reasoning_steps"
|
||||
:key="step.step"
|
||||
class="reasoning-panel__step"
|
||||
>
|
||||
<div class="reasoning-panel__step-header">
|
||||
<span class="reasoning-panel__step-number">Step {{ step.step }}</span>
|
||||
<button
|
||||
v-if="editable"
|
||||
class="reasoning-panel__step-delete"
|
||||
@click="removeStep(step.step)"
|
||||
title="Delete step"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
<div class="reasoning-panel__step-body">
|
||||
<textarea
|
||||
v-if="editable"
|
||||
:value="step.thought"
|
||||
class="reasoning-panel__textarea"
|
||||
placeholder="Thought..."
|
||||
rows="2"
|
||||
@input="updateStep(index, 'thought', ($event.target as HTMLTextAreaElement).value)"
|
||||
></textarea>
|
||||
<p v-else class="reasoning-panel__step-text">{{ step.thought }}</p>
|
||||
|
||||
<div v-if="step.conclusion !== undefined" class="reasoning-panel__step-conclusion">
|
||||
<label class="reasoning-panel__sublabel">Conclusion</label>
|
||||
<textarea
|
||||
v-if="editable"
|
||||
:value="step.conclusion ?? ''"
|
||||
class="reasoning-panel__textarea"
|
||||
placeholder="Conclusion (optional)..."
|
||||
rows="2"
|
||||
@input="updateStep(index, 'conclusion', ($event.target as HTMLTextAreaElement).value)"
|
||||
></textarea>
|
||||
<p v-else-if="step.conclusion" class="reasoning-panel__step-text reasoning-panel__step-text--conclusion">
|
||||
{{ step.conclusion }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p v-if="!localReasoning?.reasoning_steps?.length" class="reasoning-panel__no-steps">
|
||||
No steps yet.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add Step Modal -->
|
||||
<div v-if="showAddStep" class="reasoning-panel__modal-overlay" @click.self="showAddStep = false">
|
||||
<div class="reasoning-panel__modal">
|
||||
<h4 class="reasoning-panel__modal-title">Add Reasoning Step</h4>
|
||||
<div class="reasoning-panel__field">
|
||||
<label class="reasoning-panel__label">Thought</label>
|
||||
<textarea
|
||||
v-model="newStepThought"
|
||||
class="reasoning-panel__textarea"
|
||||
placeholder="Describe the reasoning thought..."
|
||||
rows="3"
|
||||
autofocus
|
||||
></textarea>
|
||||
</div>
|
||||
<div class="reasoning-panel__field">
|
||||
<label class="reasoning-panel__label">Conclusion (optional)</label>
|
||||
<textarea
|
||||
v-model="newStepConclusion"
|
||||
class="reasoning-panel__textarea"
|
||||
placeholder="Optional conclusion reached..."
|
||||
rows="2"
|
||||
></textarea>
|
||||
</div>
|
||||
<div class="reasoning-panel__modal-actions">
|
||||
<Button variant="secondary" size="small" @click="showAddStep = false">Cancel</Button>
|
||||
<Button variant="primary" size="small" @click="submitAddStep">Add Step</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.reasoning-panel {
|
||||
width: 320px;
|
||||
min-width: 320px;
|
||||
height: 100%;
|
||||
background: var(--bg-secondary);
|
||||
border-left: 1px solid var(--border);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.reasoning-panel__header {
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.reasoning-panel__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.reasoning-panel__empty {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
padding: 2rem;
|
||||
color: var(--text-secondary);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.reasoning-panel__content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.reasoning-panel__field {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.reasoning-panel__label {
|
||||
display: block;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 0.375rem;
|
||||
}
|
||||
|
||||
.reasoning-panel__sublabel {
|
||||
display: block;
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.reasoning-panel__value {
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.reasoning-panel__select {
|
||||
width: 100%;
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.reasoning-panel__select:focus {
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.reasoning-panel__input {
|
||||
width: 100%;
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-primary);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.reasoning-panel__input:focus {
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.reasoning-panel__confidence {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.reasoning-panel__confidence-bar {
|
||||
height: 4px;
|
||||
background: var(--border);
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.reasoning-panel__confidence-fill {
|
||||
height: 100%;
|
||||
border-radius: 2px;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.reasoning-panel__steps {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.reasoning-panel__steps-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.reasoning-panel__step {
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 0.75rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.reasoning-panel__step-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: var(--bg-secondary);
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.reasoning-panel__step-number {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.reasoning-panel__step-delete {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
padding: 0;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 1.25rem;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.reasoning-panel__step-delete:hover {
|
||||
background: #ef444420;
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.reasoning-panel__step-body {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.reasoning-panel__textarea {
|
||||
width: 100%;
|
||||
padding: 0.5rem 0.625rem;
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
font-size: 0.8125rem;
|
||||
font-family: inherit;
|
||||
color: var(--text-primary);
|
||||
resize: vertical;
|
||||
outline: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.reasoning-panel__textarea:focus {
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.reasoning-panel__step-text {
|
||||
font-size: 0.8125rem;
|
||||
line-height: 1.5;
|
||||
color: var(--text-primary);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.reasoning-panel__step-text--conclusion {
|
||||
color: var(--text-secondary);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.reasoning-panel__step-conclusion {
|
||||
margin-top: 0.5rem;
|
||||
padding-top: 0.5rem;
|
||||
border-top: 1px dashed var(--border);
|
||||
}
|
||||
|
||||
.reasoning-panel__no-steps {
|
||||
font-size: 0.8125rem;
|
||||
color: var(--text-secondary);
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
/* Modal */
|
||||
.reasoning-panel__modal-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.reasoning-panel__modal {
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
padding: 1.25rem;
|
||||
width: 90%;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.reasoning-panel__modal-title {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin: 0 0 1rem 0;
|
||||
}
|
||||
|
||||
.reasoning-panel__modal-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 0.5rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
</style>
|
||||
337
src/components/editor/TipTapEditor.vue
Normal file
337
src/components/editor/TipTapEditor.vue
Normal file
@@ -0,0 +1,337 @@
|
||||
<script setup lang="ts">
|
||||
import { watch, onBeforeUnmount } from 'vue'
|
||||
import { useEditor, EditorContent } from '@tiptap/vue-3'
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight'
|
||||
import { common, createLowlight } from 'lowlight'
|
||||
|
||||
const lowlight = createLowlight(common)
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: Record<string, unknown> | null
|
||||
placeholder?: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: Record<string, unknown>]
|
||||
'ready': []
|
||||
}>()
|
||||
|
||||
const editor = useEditor({
|
||||
extensions: [
|
||||
StarterKit.configure({
|
||||
codeBlock: false,
|
||||
}),
|
||||
CodeBlockLowlight.configure({
|
||||
lowlight,
|
||||
}),
|
||||
],
|
||||
content: props.modelValue || '',
|
||||
editorProps: {
|
||||
attributes: {
|
||||
class: 'tiptap-editor',
|
||||
},
|
||||
},
|
||||
onUpdate: ({ editor }) => {
|
||||
emit('update:modelValue', editor.getJSON())
|
||||
},
|
||||
onCreate: () => {
|
||||
emit('ready')
|
||||
},
|
||||
})
|
||||
|
||||
watch(() => props.modelValue, (newValue) => {
|
||||
if (!editor.value) return
|
||||
if (JSON.stringify(editor.value.getJSON()) !== JSON.stringify(newValue)) {
|
||||
editor.value.commands.setContent(newValue || '', false)
|
||||
}
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
editor.value?.destroy()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="tiptap-wrapper">
|
||||
<div v-if="editor" class="tiptap-toolbar">
|
||||
<button
|
||||
type="button"
|
||||
class="tiptap-btn"
|
||||
:class="{ active: editor.isActive('bold') }"
|
||||
@click="editor.chain().focus().toggleBold().run()"
|
||||
title="Bold"
|
||||
>
|
||||
<strong>B</strong>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="tiptap-btn"
|
||||
:class="{ active: editor.isActive('italic') }"
|
||||
@click="editor.chain().focus().toggleItalic().run()"
|
||||
title="Italic"
|
||||
>
|
||||
<em>I</em>
|
||||
</button>
|
||||
<span class="tiptap-separator"></span>
|
||||
<button
|
||||
type="button"
|
||||
class="tiptap-btn"
|
||||
:class="{ active: editor.isActive('heading', { level: 1 }) }"
|
||||
@click="editor.chain().focus().toggleHeading({ level: 1 }).run()"
|
||||
title="Heading 1"
|
||||
>
|
||||
H1
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="tiptap-btn"
|
||||
:class="{ active: editor.isActive('heading', { level: 2 }) }"
|
||||
@click="editor.chain().focus().toggleHeading({ level: 2 }).run()"
|
||||
title="Heading 2"
|
||||
>
|
||||
H2
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="tiptap-btn"
|
||||
:class="{ active: editor.isActive('heading', { level: 3 }) }"
|
||||
@click="editor.chain().focus().toggleHeading({ level: 3 }).run()"
|
||||
title="Heading 3"
|
||||
>
|
||||
H3
|
||||
</button>
|
||||
<span class="tiptap-separator"></span>
|
||||
<button
|
||||
type="button"
|
||||
class="tiptap-btn"
|
||||
:class="{ active: editor.isActive('bulletList') }"
|
||||
@click="editor.chain().focus().toggleBulletList().run()"
|
||||
title="Bullet List"
|
||||
>
|
||||
•
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="tiptap-btn"
|
||||
:class="{ active: editor.isActive('orderedList') }"
|
||||
@click="editor.chain().focus().toggleOrderedList().run()"
|
||||
title="Ordered List"
|
||||
>
|
||||
1.
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="tiptap-btn"
|
||||
:class="{ active: editor.isActive('codeBlock') }"
|
||||
@click="editor.chain().focus().toggleCodeBlock().run()"
|
||||
title="Code Block"
|
||||
>
|
||||
</>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="tiptap-btn"
|
||||
:class="{ active: editor.isActive('blockquote') }"
|
||||
@click="editor.chain().focus().toggleBlockquote().run()"
|
||||
title="Blockquote"
|
||||
>
|
||||
“
|
||||
</button>
|
||||
</div>
|
||||
<EditorContent :editor="editor" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.tiptap-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tiptap-toolbar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
padding: 0.5rem;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border);
|
||||
border-bottom: none;
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
|
||||
.tiptap-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 32px;
|
||||
height: 32px;
|
||||
padding: 0 0.5rem;
|
||||
background: none;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.tiptap-btn:hover {
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.tiptap-btn.active {
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.tiptap-separator {
|
||||
width: 1px;
|
||||
height: 20px;
|
||||
background: var(--border);
|
||||
margin: 0 0.25rem;
|
||||
}
|
||||
|
||||
.tiptap-editor {
|
||||
flex: 1;
|
||||
padding: 1rem;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 0 0 8px 8px;
|
||||
font-family: 'JetBrains Mono', 'Fira Code', monospace;
|
||||
font-size: 0.9375rem;
|
||||
line-height: 1.7;
|
||||
color: var(--text-primary);
|
||||
outline: none;
|
||||
overflow-y: auto;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.tiptap-editor:focus {
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.tiptap-editor p {
|
||||
margin: 0 0 1rem 0;
|
||||
}
|
||||
|
||||
.tiptap-editor h1,
|
||||
.tiptap-editor h2,
|
||||
.tiptap-editor h3 {
|
||||
font-weight: 600;
|
||||
margin: 1.5rem 0 0.75rem 0;
|
||||
}
|
||||
|
||||
.tiptap-editor h1 {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
|
||||
.tiptap-editor h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.tiptap-editor h3 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.tiptap-editor ul,
|
||||
.tiptap-editor ol {
|
||||
padding-left: 1.5rem;
|
||||
margin: 0 0 1rem 0;
|
||||
}
|
||||
|
||||
.tiptap-editor li {
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
|
||||
.tiptap-editor code {
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
padding: 0.125rem 0.375rem;
|
||||
font-family: inherit;
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
.tiptap-editor pre {
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
padding: 1rem;
|
||||
margin: 0 0 1rem 0;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.tiptap-editor pre code {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.tiptap-editor blockquote {
|
||||
border-left: 3px solid var(--accent);
|
||||
padding-left: 1rem;
|
||||
margin: 0 0 1rem 0;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.tiptap-editor p.is-editor-empty:first-child::before {
|
||||
content: attr(data-placeholder);
|
||||
float: left;
|
||||
color: var(--text-secondary);
|
||||
pointer-events: none;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/* Syntax highlighting */
|
||||
.tiptap-editor .hljs-comment,
|
||||
.tiptap-editor .hljs-quote {
|
||||
color: #6a737d;
|
||||
}
|
||||
|
||||
.tiptap-editor .hljs-variable,
|
||||
.tiptap-editor .hljs-template-variable,
|
||||
.tiptap-editor .hljs-tag,
|
||||
.tiptap-editor .hljs-name,
|
||||
.tiptap-editor .hljs-selector-id,
|
||||
.tiptap-editor .hljs-selector-class,
|
||||
.tiptap-editor .hljs-regexp,
|
||||
.tiptap-editor .hljs-deletion {
|
||||
color: #d73a49;
|
||||
}
|
||||
|
||||
.tiptap-editor .hljs-number,
|
||||
.tiptap-editor .hljs-built_in,
|
||||
.tiptap-editor .hljs-literal,
|
||||
.tiptap-editor .hljs-type,
|
||||
.tiptap-editor .hljs-params,
|
||||
.tiptap-editor .hljs-meta,
|
||||
.tiptap-editor .hljs-link {
|
||||
color: #005cc5;
|
||||
}
|
||||
|
||||
.tiptap-editor .hljs-attribute {
|
||||
color: #e36209;
|
||||
}
|
||||
|
||||
.tiptap-editor .hljs-string,
|
||||
.tiptap-editor .hljs-symbol,
|
||||
.tiptap-editor .hljs-bullet,
|
||||
.tiptap-editor .hljs-addition {
|
||||
color: #22863a;
|
||||
}
|
||||
|
||||
.tiptap-editor .hljs-title,
|
||||
.tiptap-editor .hljs-section {
|
||||
color: #6f42c1;
|
||||
}
|
||||
|
||||
.tiptap-editor .hljs-keyword,
|
||||
.tiptap-editor .hljs-selector-tag {
|
||||
color: #d73a49;
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,6 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import type { Document, Tag } from '@/types'
|
||||
import type { Document, Tag, DocumentReasoning, ReasoningStep, ReasoningPanelData, TipTapContentResponse } from '@/types'
|
||||
import { useApi } from '@/composables/useApi'
|
||||
|
||||
export const useDocumentsStore = defineStore('documents', () => {
|
||||
@@ -95,6 +95,58 @@ export const useDocumentsStore = defineStore('documents', () => {
|
||||
}
|
||||
}
|
||||
|
||||
async function updateDocumentTipTapContent(id: string, tiptapContent: Record<string, unknown>) {
|
||||
saving.value = true
|
||||
try {
|
||||
await api.put(`/documents/${id}/content`, {
|
||||
content: tiptapContent,
|
||||
format: 'tiptap'
|
||||
})
|
||||
if (currentDocument.value?.id === id) {
|
||||
currentDocument.value.tiptap_content = tiptapContent
|
||||
}
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchTipTapContent(id: string): Promise<Record<string, unknown> | null> {
|
||||
try {
|
||||
const response = await api.get<TipTapContentResponse>(`/documents/${id}/content?format=tiptap`)
|
||||
return response.content
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchReasoningPanel(id: string): Promise<ReasoningPanelData | null> {
|
||||
try {
|
||||
return await api.get<ReasoningPanelData>(`/documents/${id}/reasoning-panel`)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async function updateReasoning(id: string, reasoning: Partial<DocumentReasoning>) {
|
||||
const updated = await api.patch<DocumentReasoning>(`/documents/${id}/reasoning`, reasoning)
|
||||
if (currentDocument.value?.id === id) {
|
||||
currentDocument.value.reasoning = updated
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
async function addReasoningStep(documentId: string, thought: string, conclusion: string | null): Promise<ReasoningStep | null> {
|
||||
try {
|
||||
return await api.post<ReasoningStep>(`/documents/${documentId}/reasoning-steps`, { thought, conclusion })
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteReasoningStep(documentId: string, step: number) {
|
||||
await api.delete(`/documents/${documentId}/reasoning-steps/${step}`)
|
||||
}
|
||||
|
||||
return {
|
||||
currentDocument,
|
||||
tags,
|
||||
@@ -105,6 +157,12 @@ export const useDocumentsStore = defineStore('documents', () => {
|
||||
createDocument,
|
||||
updateDocument,
|
||||
updateDocumentContent,
|
||||
updateDocumentTipTapContent,
|
||||
fetchTipTapContent,
|
||||
fetchReasoningPanel,
|
||||
updateReasoning,
|
||||
addReasoningStep,
|
||||
deleteReasoningStep,
|
||||
deleteDocument,
|
||||
fetchTags,
|
||||
createTag,
|
||||
|
||||
@@ -40,17 +40,36 @@ export interface Tag {
|
||||
color: string
|
||||
}
|
||||
|
||||
export interface ReasoningStep {
|
||||
step: number
|
||||
thought: string
|
||||
conclusion: string | null
|
||||
}
|
||||
|
||||
export interface DocumentReasoning {
|
||||
reasoning_type: 'chain' | 'idea' | 'context' | 'reflection'
|
||||
reasoning_type: 'chain' | 'idea' | 'context' | 'reflection' | null
|
||||
confidence: number | null
|
||||
reasoning_steps: Array<{
|
||||
step: number
|
||||
thought: string
|
||||
conclusion: string | null
|
||||
}>
|
||||
reasoning_steps: ReasoningStep[]
|
||||
model_source: string | null
|
||||
}
|
||||
|
||||
export interface ReasoningPanelData {
|
||||
document_id: string
|
||||
has_reasoning: boolean
|
||||
reasoning: DocumentReasoning | null
|
||||
editable: boolean
|
||||
}
|
||||
|
||||
export interface TipTapContentResponse {
|
||||
content: Record<string, unknown>
|
||||
format: 'tiptap'
|
||||
}
|
||||
|
||||
export interface ContentUpdateRequest {
|
||||
content: Record<string, unknown>
|
||||
format: 'tiptap' | 'markdown'
|
||||
}
|
||||
|
||||
export interface Document {
|
||||
id: string
|
||||
title: string
|
||||
@@ -60,6 +79,7 @@ export interface Document {
|
||||
path: string
|
||||
tags: Tag[]
|
||||
reasoning: DocumentReasoning | null
|
||||
tiptap_content: Record<string, unknown> | null
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
@@ -1,39 +1,60 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import { ref, onMounted, watch, computed } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useDocumentsStore } from '@/stores/documents'
|
||||
import type { DocumentReasoning } from '@/types'
|
||||
import Header from '@/components/layout/Header.vue'
|
||||
import Button from '@/components/common/Button.vue'
|
||||
import Modal from '@/components/common/Modal.vue'
|
||||
import TipTapEditor from '@/components/editor/TipTapEditor.vue'
|
||||
import ReasoningPanel from '@/components/editor/ReasoningPanel.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const documentsStore = useDocumentsStore()
|
||||
|
||||
const editTitle = ref('')
|
||||
const editContent = ref('')
|
||||
const tiptapContent = ref<Record<string, unknown> | null>(null)
|
||||
const showTagModal = ref(false)
|
||||
const newTagName = ref('')
|
||||
const newTagColor = ref('#6366f1')
|
||||
const showReasoningPanel = ref(true)
|
||||
const saveTimeout = ref<ReturnType<typeof setTimeout> | null>(null)
|
||||
const isReady = ref(false)
|
||||
|
||||
onMounted(async () => {
|
||||
const docId = route.params.id as string
|
||||
await documentsStore.fetchDocument(docId)
|
||||
await documentsStore.fetchTags()
|
||||
|
||||
|
||||
if (documentsStore.currentDocument) {
|
||||
editTitle.value = documentsStore.currentDocument.title
|
||||
editContent.value = documentsStore.currentDocument.content
|
||||
|
||||
// Try to load TipTap content first, fall back to markdown conversion
|
||||
const savedTipTap = await documentsStore.fetchTipTapContent(docId)
|
||||
if (savedTipTap) {
|
||||
tiptapContent.value = savedTipTap
|
||||
} else if (documentsStore.currentDocument.content) {
|
||||
// Convert markdown to TipTap JSON (basic conversion)
|
||||
tiptapContent.value = markdownToTiptap(documentsStore.currentDocument.content)
|
||||
} else {
|
||||
tiptapContent.value = { type: 'doc', content: [{ type: 'paragraph' }] }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => route.params.id, async (newId) => {
|
||||
if (newId) {
|
||||
isReady.value = false
|
||||
await documentsStore.fetchDocument(newId as string)
|
||||
if (documentsStore.currentDocument) {
|
||||
editTitle.value = documentsStore.currentDocument.title
|
||||
editContent.value = documentsStore.currentDocument.content
|
||||
const savedTipTap = await documentsStore.fetchTipTapContent(newId as string)
|
||||
if (savedTipTap) {
|
||||
tiptapContent.value = savedTipTap
|
||||
} else {
|
||||
tiptapContent.value = markdownToTiptap(documentsStore.currentDocument.content)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -41,12 +62,80 @@ watch(() => route.params.id, async (newId) => {
|
||||
watch(() => documentsStore.currentDocument, (doc) => {
|
||||
if (doc) {
|
||||
editTitle.value = doc.title
|
||||
editContent.value = doc.content
|
||||
}
|
||||
})
|
||||
|
||||
function handleContentChange() {
|
||||
// Auto-save with debounce
|
||||
// Convert markdown string to basic TipTap JSON
|
||||
function markdownToTiptap(markdown: string): Record<string, unknown> {
|
||||
if (!markdown) {
|
||||
return { type: 'doc', content: [{ type: 'paragraph' }] }
|
||||
}
|
||||
|
||||
const lines = markdown.split('\n')
|
||||
const content: unknown[] = []
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('### ')) {
|
||||
content.push({
|
||||
type: 'heading',
|
||||
attrs: { level: 3 },
|
||||
content: [{ type: 'text', text: line.substring(4) }]
|
||||
})
|
||||
} else if (line.startsWith('## ')) {
|
||||
content.push({
|
||||
type: 'heading',
|
||||
attrs: { level: 2 },
|
||||
content: [{ type: 'text', text: line.substring(3) }]
|
||||
})
|
||||
} else if (line.startsWith('# ')) {
|
||||
content.push({
|
||||
type: 'heading',
|
||||
attrs: { level: 1 },
|
||||
content: [{ type: 'text', text: line.substring(2) }]
|
||||
})
|
||||
} else if (line.startsWith('- ') || line.startsWith('* ')) {
|
||||
content.push({
|
||||
type: 'bulletList',
|
||||
content: [{
|
||||
type: 'listItem',
|
||||
content: [{
|
||||
type: 'paragraph',
|
||||
content: [{ type: 'text', text: line.substring(2) }]
|
||||
}]
|
||||
}]
|
||||
})
|
||||
} else if (/^\d+\.\s/.test(line)) {
|
||||
content.push({
|
||||
type: 'orderedList',
|
||||
content: [{
|
||||
type: 'listItem',
|
||||
content: [{
|
||||
type: 'paragraph',
|
||||
content: [{ type: 'text', text: line.replace(/^\d+\.\s/, '') }]
|
||||
}]
|
||||
}]
|
||||
})
|
||||
} else if (line.startsWith('```')) {
|
||||
// Skip code block markers for now (would need more complex handling)
|
||||
content.push({
|
||||
type: 'paragraph',
|
||||
content: [{ type: 'text', text: line }]
|
||||
})
|
||||
} else if (line.trim()) {
|
||||
content.push({
|
||||
type: 'paragraph',
|
||||
content: [{ type: 'text', text: line }]
|
||||
})
|
||||
} else {
|
||||
content.push({ type: 'paragraph' })
|
||||
}
|
||||
}
|
||||
|
||||
return { type: 'doc', content }
|
||||
}
|
||||
|
||||
function handleContentChange(content: Record<string, unknown>) {
|
||||
tiptapContent.value = content
|
||||
if (saveTimeout.value) {
|
||||
clearTimeout(saveTimeout.value)
|
||||
}
|
||||
@@ -55,11 +144,15 @@ function handleContentChange() {
|
||||
}, 1500)
|
||||
}
|
||||
|
||||
function handleEditorReady() {
|
||||
isReady.value = true
|
||||
}
|
||||
|
||||
async function saveContent() {
|
||||
if (!documentsStore.currentDocument) return
|
||||
await documentsStore.updateDocumentContent(
|
||||
if (!documentsStore.currentDocument || !tiptapContent.value) return
|
||||
await documentsStore.updateDocumentTipTapContent(
|
||||
documentsStore.currentDocument.id,
|
||||
editContent.value
|
||||
tiptapContent.value
|
||||
)
|
||||
}
|
||||
|
||||
@@ -78,11 +171,11 @@ async function removeTag(tagId: string) {
|
||||
|
||||
async function createAndAssignTag() {
|
||||
if (!newTagName.value.trim() || !documentsStore.currentDocument) return
|
||||
|
||||
|
||||
const tag = await documentsStore.createTag(newTagName.value.trim(), newTagColor.value)
|
||||
const currentTagIds = documentsStore.currentDocument.tags.map(t => t.id)
|
||||
await documentsStore.assignTags(documentsStore.currentDocument.id, [...currentTagIds, tag.id])
|
||||
|
||||
|
||||
showTagModal.value = false
|
||||
newTagName.value = ''
|
||||
newTagColor.value = '#6366f1'
|
||||
@@ -96,6 +189,27 @@ async function goBack() {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleReasoningUpdate(reasoning: DocumentReasoning) {
|
||||
if (!documentsStore.currentDocument) return
|
||||
await documentsStore.updateReasoning(documentsStore.currentDocument.id, reasoning)
|
||||
}
|
||||
|
||||
async function handleAddStep(thought: string, conclusion: string | null) {
|
||||
if (!documentsStore.currentDocument) return
|
||||
const step = await documentsStore.addReasoningStep(documentsStore.currentDocument.id, thought, conclusion)
|
||||
if (step && documentsStore.currentDocument.reasoning) {
|
||||
documentsStore.currentDocument.reasoning.reasoning_steps.push(step)
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDeleteStep(step: number) {
|
||||
if (!documentsStore.currentDocument) return
|
||||
await documentsStore.deleteReasoningStep(documentsStore.currentDocument.id, step)
|
||||
if (documentsStore.currentDocument.reasoning) {
|
||||
documentsStore.currentDocument.reasoning.reasoning_steps = documentsStore.currentDocument.reasoning.reasoning_steps.filter(s => s.step !== step)
|
||||
}
|
||||
}
|
||||
|
||||
const tagColors = [
|
||||
'#6366f1', '#8b5cf6', '#ec4899', '#ef4444', '#f97316',
|
||||
'#eab308', '#22c55e', '#10b981', '#14b8a6', '#06b6d4'
|
||||
@@ -106,7 +220,7 @@ const tagColors = [
|
||||
<div class="layout">
|
||||
<Header />
|
||||
<div class="layout__body">
|
||||
<main class="layout__content">
|
||||
<main class="layout__content" :class="{ 'layout__content--with-panel': showReasoningPanel }">
|
||||
<div v-if="documentsStore.loading" class="loading">
|
||||
<div class="loading__spinner"></div>
|
||||
<p>Loading document...</p>
|
||||
@@ -119,22 +233,38 @@ const tagColors = [
|
||||
|
||||
<div v-else-if="documentsStore.currentDocument" class="doc-view">
|
||||
<div class="doc-view__toolbar">
|
||||
<button class="doc-view__back" @click="goBack">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="m15 18-6-6 6-6"/>
|
||||
</svg>
|
||||
Back
|
||||
</button>
|
||||
<div class="doc-view__toolbar-left">
|
||||
<button class="doc-view__back" @click="goBack">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="m15 18-6-6 6-6"/>
|
||||
</svg>
|
||||
Back
|
||||
</button>
|
||||
|
||||
<div class="doc-view__saving" v-if="documentsStore.saving">
|
||||
<div class="doc-view__saving-dot"></div>
|
||||
Saving...
|
||||
<button
|
||||
class="doc-view__panel-toggle"
|
||||
:class="{ active: showReasoningPanel }"
|
||||
@click="showReasoningPanel = !showReasoningPanel"
|
||||
title="Toggle Reasoning Panel"
|
||||
>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M12 2a8 8 0 0 1 8 8c0 3.5-2 6-4 8l-2 2-2-2c-2-2-4-4.5-4-8a8 8 0 0 1 8-8z"/>
|
||||
<circle cx="12" cy="10" r="3"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="doc-view__saved" v-else>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="20 6 9 17 4 12"/>
|
||||
</svg>
|
||||
Saved
|
||||
|
||||
<div class="doc-view__toolbar-right">
|
||||
<div class="doc-view__saving" v-if="documentsStore.saving">
|
||||
<div class="doc-view__saving-dot"></div>
|
||||
Saving...
|
||||
</div>
|
||||
<div class="doc-view__saved" v-else-if="isReady">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="20 6 9 17 4 12"/>
|
||||
</svg>
|
||||
Saved
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -169,15 +299,27 @@ const tagColors = [
|
||||
</div>
|
||||
|
||||
<div class="doc-view__editor">
|
||||
<textarea
|
||||
v-model="editContent"
|
||||
class="doc-view__textarea"
|
||||
placeholder="Start writing in Markdown..."
|
||||
@input="handleContentChange"
|
||||
></textarea>
|
||||
<TipTapEditor
|
||||
v-if="tiptapContent"
|
||||
:model-value="tiptapContent"
|
||||
placeholder="Start writing..."
|
||||
@update:model-value="handleContentChange"
|
||||
@ready="handleEditorReady"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Reasoning Panel Sidebar -->
|
||||
<ReasoningPanel
|
||||
v-if="showReasoningPanel && documentsStore.currentDocument"
|
||||
:document-id="documentsStore.currentDocument.id"
|
||||
:reasoning="documentsStore.currentDocument.reasoning"
|
||||
:editable="true"
|
||||
@update="handleReasoningUpdate"
|
||||
@add-step="handleAddStep"
|
||||
@delete-step="handleDeleteStep"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Tag Modal -->
|
||||
@@ -232,6 +374,10 @@ const tagColors = [
|
||||
background: var(--bg-primary);
|
||||
}
|
||||
|
||||
.layout__content--with-panel {
|
||||
/* Content takes remaining space when panel is visible */
|
||||
}
|
||||
|
||||
.loading,
|
||||
.error {
|
||||
display: flex;
|
||||
@@ -259,7 +405,7 @@ const tagColors = [
|
||||
}
|
||||
|
||||
.doc-view {
|
||||
max-width: 800px;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
@@ -271,6 +417,17 @@ const tagColors = [
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.doc-view__toolbar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.doc-view__toolbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.doc-view__back {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -290,6 +447,31 @@ const tagColors = [
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.doc-view__panel-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
background: none;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.doc-view__panel-toggle:hover {
|
||||
background: var(--bg-secondary);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.doc-view__panel-toggle.active {
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.doc-view__saving {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -403,31 +585,8 @@ const tagColors = [
|
||||
|
||||
.doc-view__editor {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.doc-view__textarea {
|
||||
width: 100%;
|
||||
min-height: 400px;
|
||||
padding: 1rem;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
font-family: 'JetBrains Mono', 'Fira Code', monospace;
|
||||
font-size: 0.9375rem;
|
||||
line-height: 1.7;
|
||||
color: var(--text-primary);
|
||||
resize: vertical;
|
||||
outline: none;
|
||||
transition: border-color 0.15s;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.doc-view__textarea::placeholder {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.doc-view__textarea:focus {
|
||||
border-color: var(--accent);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Form styles */
|
||||
|
||||
Reference in New Issue
Block a user