diff --git a/package-lock.json b/package-lock.json index 11dee05..dbd747a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,10 +38,16 @@ "@radix-ui/react-tooltip": "^1.1.4", "@supabase/supabase-js": "^2.58.0", "@tanstack/react-query": "^5.56.2", + "@tiptap/extension-link": "^3.7.2", + "@tiptap/extension-text-align": "^3.7.2", + "@tiptap/react": "^3.7.2", + "@tiptap/starter-kit": "^3.7.2", + "@types/dompurify": "^3.2.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.0.0", "date-fns": "^3.6.0", + "dompurify": "^3.3.0", "embla-carousel-react": "^8.3.0", "input-otp": "^1.2.4", "lucide-react": "^0.462.0", @@ -2318,6 +2324,12 @@ "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==", "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/@remix-run/router": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.20.0.tgz", @@ -2905,6 +2917,453 @@ "react": "^18 || ^19" } }, + "node_modules/@tiptap/core": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.7.2.tgz", + "integrity": "sha512-fJwNpTx0aq4UU0HNkxPvPYfNBcTHQ/q5xBUdOB5Mgu6clwGES38jVsNNSudB8g53APUmJIS+2fJbkxl3V+0jww==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/pm": "^3.7.2" + } + }, + "node_modules/@tiptap/extension-blockquote": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-3.7.2.tgz", + "integrity": "sha512-8rNDh1E1ratex9KicvNNnjJGtF313Kpf5hXHOUcIm8FQwvA/0Tu6jq7r6VgESMyo95R3EmzRpnCYQef+zDm6OQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.7.2" + } + }, + "node_modules/@tiptap/extension-bold": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-3.7.2.tgz", + "integrity": "sha512-bwCn9lQEXnEi7LfIx3G/oaH4I0ZapAgrHzLCNJH/tNgRKVWym1H1Oa8PlkiFDbalWOdUkbgeAUqUaIB13k408Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.7.2" + } + }, + "node_modules/@tiptap/extension-bubble-menu": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.7.2.tgz", + "integrity": "sha512-rCJu/X7sZEYWkOwLO342JP06f4giVBECPzr/SzG/fQdAidPW96eilPk3L82w5j24kS9odTlxSLlFlIf6UZ2b9w==", + "license": "MIT", + "optional": true, + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.7.2", + "@tiptap/pm": "^3.7.2" + } + }, + "node_modules/@tiptap/extension-bullet-list": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-3.7.2.tgz", + "integrity": "sha512-OHYYXKjmxisLQws0tW8Dz14PcyIJmaed7eypZvIm/R3hxa/7lJY/2EM/Ti5g/w1U8WPBEH1hX3icRtiulserKw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "^3.7.2" + } + }, + "node_modules/@tiptap/extension-code": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-3.7.2.tgz", + "integrity": "sha512-J8FaCiKJJnHvQiPcbfbUtc5RNmGx/Gui/K5CDMPc17jhCiQ9JhR9idRPREV24Z2t7GujWX7LG6ZDDR82pSns+g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.7.2" + } + }, + "node_modules/@tiptap/extension-code-block": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-3.7.2.tgz", + "integrity": "sha512-TfixutvvbGCrSSCsfDK/PBm6A5FIzcPTSVDrmmsiAfqldj/Woy1T42dads+wv9SjKG06GlWDwYtDGAk2Uun8NA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.7.2", + "@tiptap/pm": "^3.7.2" + } + }, + "node_modules/@tiptap/extension-document": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-3.7.2.tgz", + "integrity": "sha512-OrHl402v2FWCUKR1Xi5MTNBAkKYQh7mtpw/WlJDFnk5z1qHLqz4UIcbGilDYzVPrNUZPhA1p3c+V5UUVUFzUfg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.7.2" + } + }, + "node_modules/@tiptap/extension-dropcursor": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-3.7.2.tgz", + "integrity": "sha512-79y6M9pJYwqcqBHIWoomfptJp0QB/TP3Y+2NOL09sMNeSdUgmz5pCVObA4H48YMkoB0EcUtux2IUOM66e4nsJA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extensions": "^3.7.2" + } + }, + "node_modules/@tiptap/extension-floating-menu": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-3.7.2.tgz", + "integrity": "sha512-g19ratrXlplYDS29VLQa1y/IM/ro0UFhSS4fQokiQKkazwnA1ZVnebjw8ERYg5lkMm/hiImqstpgdO0LtoivvQ==", + "license": "MIT", + "optional": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@floating-ui/dom": "^1.0.0", + "@tiptap/core": "^3.7.2", + "@tiptap/pm": "^3.7.2" + } + }, + "node_modules/@tiptap/extension-gapcursor": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-3.7.2.tgz", + "integrity": "sha512-vCLo2dL2SfeWjh/gJKDiu0/fz6OF7obGTJvHg/yStkoUqlAEiwKoyHP/NXeTGYJMzZzUi0kY9DtTEJdGFvphuQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extensions": "^3.7.2" + } + }, + "node_modules/@tiptap/extension-hard-break": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-3.7.2.tgz", + "integrity": "sha512-nNDo+5S1yRQ3JkBM+gwpEEVZ/Kw9qWoG/cpShyGYDHo1/y8MgO+VI0kSb/LuBTw7g+jmNXdf+ZaRRI/pXsUihg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.7.2" + } + }, + "node_modules/@tiptap/extension-heading": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-3.7.2.tgz", + "integrity": "sha512-eH/G66FIRlTQz4MhEmlNNNQgVTxhoqlkyFzgeG5aipIplYOdYa5Y6Wl0NF4xqr1jAHGLAK6LaYS4FXp3TE7LyA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.7.2" + } + }, + "node_modules/@tiptap/extension-horizontal-rule": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.7.2.tgz", + "integrity": "sha512-pN+1hJAVVP3uqtpZ5Rm7z5XUB/NGprK6wExJ04xG117E4rTVcaEb1FnMILY3J3A5XbdC3vHX+cblR8mOl1PAMw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.7.2", + "@tiptap/pm": "^3.7.2" + } + }, + "node_modules/@tiptap/extension-italic": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.7.2.tgz", + "integrity": "sha512-1tfF37LvKgA5hg09UBgOjdMLNRb1C6keIOBF0r5oHKeWPYOf4z3j5IU9PsFUoOn53XRMb1aiD/TNbGPyoT3Fyw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.7.2" + } + }, + "node_modules/@tiptap/extension-link": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-3.7.2.tgz", + "integrity": "sha512-9K54PxBiDSWAMfICqkb8jcQ6cL7vDAtjTk0zqBw4d+XuaUy0FC9QUdbx7r1Pkbf36K1/ApbvM9a7qpOirWk8Xw==", + "license": "MIT", + "dependencies": { + "linkifyjs": "^4.3.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.7.2", + "@tiptap/pm": "^3.7.2" + } + }, + "node_modules/@tiptap/extension-list": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.7.2.tgz", + "integrity": "sha512-/tYHmEkOGcVweAc9ZgnAXkzua5aJfu7TjZcKTq5fmDt6x9MY1eY1+egS7D9hVR2sUSAC10VgXmYdYPDsKF3p2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.7.2", + "@tiptap/pm": "^3.7.2" + } + }, + "node_modules/@tiptap/extension-list-item": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-3.7.2.tgz", + "integrity": "sha512-962TFsx4eF5NMyLVhGFGF/btt5j3MipPhDiUsxzBgnlW8o5OonVepb9cDrqpEDQ2/wLvheWnCKuvmG7umasldQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "^3.7.2" + } + }, + "node_modules/@tiptap/extension-list-keymap": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-keymap/-/extension-list-keymap-3.7.2.tgz", + "integrity": "sha512-1du9eo+NPIkuRT258yUn9bovhip556aJo/yDtRbswEVNScP1E8y/kFRWvw0HD7/YWcNqok1ZteoSwShWnKAXRQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "^3.7.2" + } + }, + "node_modules/@tiptap/extension-ordered-list": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-3.7.2.tgz", + "integrity": "sha512-Tu61/JXh1RRd3Kb+s7A7jmpnB+w1pqGSRfMXBtYHDHDIGyXu255ru7soX44lJfHGq/zYcTFSHGSsi8o23QONJg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "^3.7.2" + } + }, + "node_modules/@tiptap/extension-paragraph": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-3.7.2.tgz", + "integrity": "sha512-HmDuAixTcvP4A/v6OLkh/C6nB86i7/DRNswBf/Udak8TgWUIcSUK0iActxxm5+B3MZTSf3U87JzyI6IeuElLIQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.7.2" + } + }, + "node_modules/@tiptap/extension-strike": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-3.7.2.tgz", + "integrity": "sha512-I1G+4vZbCBTpAMmyVwaO8cLBJgXEf1DyEzc0B+HhTJiBa9qA9OKgRQEGFgisxu1kggjbzB6+d0+taHfjsZC1SQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.7.2" + } + }, + "node_modules/@tiptap/extension-text": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.7.2.tgz", + "integrity": "sha512-sKaeGYNP1+bAe2rvmzWLW5qH9DsSFOJlOUEOFchR0OX0rC7bbGS6/KuyAq0w6UkL+cMJnDyAbv3KeD2WEA192w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.7.2" + } + }, + "node_modules/@tiptap/extension-text-align": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-3.7.2.tgz", + "integrity": "sha512-tUdoatcxM8u16tFVfEURFZwmxvZQR33f9VLtkyR+1aXgy0Pi87cNoFC60pTjH7gNtktEuagNfPE00tGMvqIehg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.7.2" + } + }, + "node_modules/@tiptap/extension-underline": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-3.7.2.tgz", + "integrity": "sha512-GDpUZllTD7uIdHjTzYJ6i4jUgCeviW40SCpLVVv1xH0gj1t1xu0Rnxmk+bXkF2XNe8jPXkMCgYNr6DR6eO8roQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.7.2" + } + }, + "node_modules/@tiptap/extensions": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.7.2.tgz", + "integrity": "sha512-FaToSdU9fhQk2swkaXrAQNgdaE0dwLbUHcvilW5F4xTpQfZ3J535u5U2TUYf+f9KKSV5fTmD4QGNY9qxY7ihTg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.7.2", + "@tiptap/pm": "^3.7.2" + } + }, + "node_modules/@tiptap/pm": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.7.2.tgz", + "integrity": "sha512-i2fvXDapwo/TWfHM6STYEbkYyF3qyfN6KEBKPrleX/Z80G5bLxom0gB79TsjLNxTLi6mdf0vTHgAcXMG1avc2g==", + "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/react": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-3.7.2.tgz", + "integrity": "sha512-tka4ioSmsGI4TyGZ7jAUoIw8t8DVjr1It0B38vZVLqg8M/ZFgR1NkF50TJ6qAkhy8Uz12AO50so0v79tV2pmEA==", + "license": "MIT", + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "fast-deep-equal": "^3.1.3", + "use-sync-external-store": "^1.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "optionalDependencies": { + "@tiptap/extension-bubble-menu": "^3.7.2", + "@tiptap/extension-floating-menu": "^3.7.2" + }, + "peerDependencies": { + "@tiptap/core": "^3.7.2", + "@tiptap/pm": "^3.7.2", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "@types/react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@tiptap/starter-kit": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-3.7.2.tgz", + "integrity": "sha512-43GwI+2Mtc/ci7J4eJOE02wZxp5KIsDTMMb0peNksPcEGaURGdDhav9zbAW24NRjRxU7Auk/zaQu9O8+ZE0v0A==", + "license": "MIT", + "dependencies": { + "@tiptap/core": "^3.7.2", + "@tiptap/extension-blockquote": "^3.7.2", + "@tiptap/extension-bold": "^3.7.2", + "@tiptap/extension-bullet-list": "^3.7.2", + "@tiptap/extension-code": "^3.7.2", + "@tiptap/extension-code-block": "^3.7.2", + "@tiptap/extension-document": "^3.7.2", + "@tiptap/extension-dropcursor": "^3.7.2", + "@tiptap/extension-gapcursor": "^3.7.2", + "@tiptap/extension-hard-break": "^3.7.2", + "@tiptap/extension-heading": "^3.7.2", + "@tiptap/extension-horizontal-rule": "^3.7.2", + "@tiptap/extension-italic": "^3.7.2", + "@tiptap/extension-link": "^3.7.2", + "@tiptap/extension-list": "^3.7.2", + "@tiptap/extension-list-item": "^3.7.2", + "@tiptap/extension-list-keymap": "^3.7.2", + "@tiptap/extension-ordered-list": "^3.7.2", + "@tiptap/extension-paragraph": "^3.7.2", + "@tiptap/extension-strike": "^3.7.2", + "@tiptap/extension-text": "^3.7.2", + "@tiptap/extension-underline": "^3.7.2", + "@tiptap/extensions": "^3.7.2", + "@tiptap/pm": "^3.7.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, "node_modules/@types/d3-array": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", @@ -2968,6 +3427,16 @@ "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", "license": "MIT" }, + "node_modules/@types/dompurify": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.2.0.tgz", + "integrity": "sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg==", + "deprecated": "This is a stub types definition. dompurify provides its own type definitions, so you do not need this installed.", + "license": "MIT", + "dependencies": { + "dompurify": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -2982,6 +3451,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/@types/node": { "version": "22.7.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.9.tgz", @@ -3025,6 +3516,19 @@ "@types/react": "*" } }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, "node_modules/@types/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", @@ -3377,7 +3881,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, "license": "Python-2.0" }, "node_modules/aria-hidden": { @@ -4038,6 +4541,12 @@ "dev": true, "license": "MIT" }, + "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/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -4263,6 +4772,15 @@ "csstype": "^3.0.2" } }, + "node_modules/dompurify": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.0.tgz", + "integrity": "sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -4312,6 +4830,18 @@ "dev": true, "license": "MIT" }, + "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/esbuild": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", @@ -4365,7 +4895,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -4572,7 +5101,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, "license": "MIT" }, "node_modules/fast-equals": { @@ -5124,6 +5652,21 @@ "dev": true, "license": "MIT" }, + "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/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -5652,6 +6195,29 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, + "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/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/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -5818,6 +6384,12 @@ "node": ">= 0.8.0" } }, + "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/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -6131,6 +6703,201 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "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.4.1", + "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.4.1.tgz", + "integrity": "sha512-2JZD8z2JviJrboD9cPuX/Sv/1ChFng+xh2tChQ2X4bB2HeK+rra/bmJ3xGntCcjhOqIzSDG6Id7e8RJ9QPXLEQ==", + "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.3", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.3.tgz", + "integrity": "sha512-dY2HdaNXlARknJbrManZ1WyUtos+AP97AmvqdOQtWtrrC5g4mohVX5DTi9rXNFSk09eczLq9GuNTtq3EfMeMGA==", + "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.3", + "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.3.tgz", + "integrity": "sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==", + "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.1", + "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.8.1.tgz", + "integrity": "sha512-DAgDoUYHCcc6tOGpLVPSU1k84kCUWTWnfWX3UDy2Delv4ryH0KqTD6RBI6k4yi9j9I8gl3j8MkPpRD/vWPZbug==", + "license": "MIT", + "dependencies": { + "prosemirror-keymap": "^1.2.2", + "prosemirror-model": "^1.25.0", + "prosemirror-state": "^1.4.3", + "prosemirror-transform": "^1.10.3", + "prosemirror-view": "^1.39.1" + } + }, + "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.4", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.10.4.tgz", + "integrity": "sha512-pwDy22nAnGqNR1feOQKHxoFkkUtepoFAd3r2hbEDsnf4wp57kKA36hXsB3njA9FtONBEwSDnDeCiJe+ItD+ykw==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.21.0" + } + }, + "node_modules/prosemirror-view": { + "version": "1.41.3", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.3.tgz", + "integrity": "sha512-SqMiYMUQNNBP9kfPhLO8WXEk/fon47vc52FQsUiJzTBuyjKgEcoAwMyF04eQ4WZ2ArMn7+ReypYL60aKngbACQ==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.20.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -6141,6 +6908,15 @@ "node": ">=6" } }, + "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/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -6496,6 +7272,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/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -6953,6 +7735,12 @@ } } }, + "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/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", @@ -7043,6 +7831,15 @@ } } }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -7145,6 +7942,12 @@ } } }, + "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" + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/package.json b/package.json index 4f1af76..06c9870 100644 --- a/package.json +++ b/package.json @@ -41,10 +41,16 @@ "@radix-ui/react-tooltip": "^1.1.4", "@supabase/supabase-js": "^2.58.0", "@tanstack/react-query": "^5.56.2", + "@tiptap/extension-link": "^3.7.2", + "@tiptap/extension-text-align": "^3.7.2", + "@tiptap/react": "^3.7.2", + "@tiptap/starter-kit": "^3.7.2", + "@types/dompurify": "^3.2.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.0.0", "date-fns": "^3.6.0", + "dompurify": "^3.3.0", "embla-carousel-react": "^8.3.0", "input-otp": "^1.2.4", "lucide-react": "^0.462.0", diff --git a/src/components/EditableField.tsx b/src/components/EditableField.tsx index 1f71ef2..2612cc2 100644 --- a/src/components/EditableField.tsx +++ b/src/components/EditableField.tsx @@ -118,7 +118,7 @@ useEffect(() => { onChange={handleChange} onBlur={handleBlur} onKeyDown={handleKeyDown} - className={`w-full p-2 rounded border border-portfolio-blue bg-white ${className}`} + className={`w-full p-2 rounded border border-portfolio-blue bg-white !text-foreground ${className}`} placeholder={placeholder} rows={3} /> @@ -130,7 +130,7 @@ useEffect(() => { onChange={handleChange} onBlur={handleBlur} onKeyDown={handleKeyDown} - className={`w-full p-2 rounded border border-portfolio-blue bg-white ${className}`} + className={`w-full p-2 rounded border border-portfolio-blue bg-white !text-foreground ${className}`} placeholder={placeholder} /> ); diff --git a/src/components/EditableImage.tsx b/src/components/EditableImage.tsx index 03d16da..e6a79d4 100644 --- a/src/components/EditableImage.tsx +++ b/src/components/EditableImage.tsx @@ -52,7 +52,7 @@ const EditableImage: React.FC = ({ return (
setIsHovering(true)} onMouseLeave={() => setIsHovering(false)} onClick={handleClick} diff --git a/src/components/PortfolioHeader.tsx b/src/components/PortfolioHeader.tsx index 79d8def..ab2a580 100644 --- a/src/components/PortfolioHeader.tsx +++ b/src/components/PortfolioHeader.tsx @@ -370,7 +370,7 @@ const PortfolioHeader: React.FC = ({ return (
diff --git a/src/components/ProjectCard.tsx b/src/components/ProjectCard.tsx index 0d122ad..a34949c 100644 --- a/src/components/ProjectCard.tsx +++ b/src/components/ProjectCard.tsx @@ -1,12 +1,13 @@ import React, { useState, useEffect } from "react"; -import { X, Plus, Pencil, ExternalLink } from "lucide-react"; +import { X, Plus, Pencil, ExternalLink, EyeOff, Eye } from "lucide-react"; import EditableField from "./EditableField"; import EditableImage from "./EditableImage"; +import { RichTextEditor } from "./RichTextEditor"; import { LinkData, ProjectData, FeatureData } from "@/types/portfolio"; import { toast } from "sonner"; import { Button } from "./ui/button"; -import { validateAndFormatUrl } from "@/utils/securityUtils"; +import { validateAndFormatUrl, sanitizeHtml } from "@/utils/securityUtils"; interface ProjectCardProps { project: ProjectData; @@ -16,16 +17,35 @@ interface ProjectCardProps { } const ProjectCard: React.FC = ({ project, onUpdate, onDelete, isEditingMode = true }) => { -const [isEditing, setIsEditing] = useState(false); +const [isEditing, setIsEditing] = useState(() => { + const saved = localStorage.getItem(`project-edit-${project.id}`); + return saved === 'true'; +}); const [confirmDelete, setConfirmDelete] = useState(false); const [localProject, setLocalProject] = useState({ ...project, features: project.features || [], project_role: project.project_role || "", - key_learnings: project.key_learnings || [] + key_learnings: project.key_learnings || [], + show_tech_used: project.show_tech_used ?? true, + show_key_learnings: project.show_key_learnings ?? true, + show_links: project.show_links ?? true }); const [lastAddedFeatureId, setLastAddedFeatureId] = useState(null); const [lastAddedLearningIndex, setLastAddedLearningIndex] = useState(null); + +// Persist edit state to localStorage +useEffect(() => { + localStorage.setItem(`project-edit-${project.id}`, String(isEditing)); +}, [isEditing, project.id]); + +// Restore edit state when project ID changes (navigating between tabs) +useEffect(() => { + const saved = localStorage.getItem(`project-edit-${project.id}`); + if (saved !== null) { + setIsEditing(saved === 'true'); + } +}, [project.id]); // Update local state when props change useEffect(() => { @@ -33,11 +53,14 @@ const [lastAddedLearningIndex, setLastAddedLearningIndex] = useState { + const updateField = (field: keyof ProjectData, value: string | LinkData[] | FeatureData[] | string[] | boolean) => { const updatedProject = { ...localProject, [field]: value }; setLocalProject(updatedProject); onUpdate(updatedProject); @@ -59,14 +82,14 @@ const [lastAddedLearningIndex, setLastAddedLearningIndex] = useState { const newFeature: FeatureData = { id: `${localProject.id}-feature-${Date.now()}`, - title: "New Feature" + title: "New learning" }; const updatedFeatures = [...localProject.features, newFeature]; setLocalProject(prev => ({ ...prev, features: updatedFeatures })); updateField("features", updatedFeatures); setLastAddedFeatureId(newFeature.id); - toast.success("Feature added"); + toast.success("Learning added"); }; const updateLink = (linkId: string, field: keyof LinkData, value: string) => { @@ -101,16 +124,16 @@ const addFeature = () => { const updatedFeatures = localProject.features.filter(feature => feature.id !== featureId); setLocalProject(prev => ({ ...prev, features: updatedFeatures })); updateField("features", updatedFeatures); - toast.success("Feature removed"); + toast.success("Learning removed"); }; const addKeyLearning = () => { - const newLearning = "New key learning"; + const newLearning = "New tech"; const updatedLearnings = [...(localProject.key_learnings || []), newLearning]; setLocalProject(prev => ({ ...prev, key_learnings: updatedLearnings })); updateField("key_learnings", updatedLearnings); setLastAddedLearningIndex(updatedLearnings.length - 1); - toast.success("Key learning added"); + toast.success("Tech added"); }; const updateKeyLearning = (index: number, value: string) => { @@ -124,7 +147,7 @@ const addFeature = () => { const updatedLearnings = (localProject.key_learnings || []).filter((_, i) => i !== index); setLocalProject(prev => ({ ...prev, key_learnings: updatedLearnings })); updateField("key_learnings", updatedLearnings); - toast.success("Key learning removed"); + toast.success("Tech removed"); }; const handleDeleteProject = () => { @@ -184,40 +207,51 @@ const addFeature = () => { )} {localProject.description && localProject.description.trim().length > 0 && ( -
- {renderDescription(localProject.description)} -
+
)} - {/* Key Learnings */} - {localProject.key_learnings && localProject.key_learnings.length > 0 && ( + {/* Tech Used */} + {localProject.show_tech_used && localProject.key_learnings && localProject.key_learnings.length > 0 && (
-

Key Learnings

-
    +

    Tech Used

    +
    {localProject.key_learnings.map((learning, index) => ( -
  • {learning}
  • + + {learning} + ))} -
+
)} - {/* Features */} - {localProject.features.length > 0 && ( -
- {localProject.features.map((feature) => ( - - {feature.title} - - ))} + {/* Key Learnings */} + {localProject.show_key_learnings && localProject.features.length > 0 && ( +
+

Key Learnings

+
+ {localProject.features.map((feature) => ( + + {feature.title} + + ))} +
)} {/* Links */} - {localProject.links.length > 0 && ( -
+ {localProject.show_links && localProject.links.length > 0 && ( +
+

Relevant links

+
{localProject.links.map((link) => ( { ))} +
)}
@@ -311,73 +346,92 @@ const addFeature = () => {
- updateField("description", value)} - tag="p" - className="text-portfolio-muted mb-4 text-sm text-justify" - placeholder="Project Description (Use '- ' or '* ' at the start of a line for bullet points)" - multiline + placeholder="Project description" + className="mb-4" /> - {isEditing && ( -
- Tip: Use "- " or "* " at the start of a line to create bullet points -
- )} - - {/* Key Learnings Section */} + {/* Tech Used Section */}
{localProject.key_learnings && localProject.key_learnings.length > 0 && ( -
-

Key Learnings

-
- {localProject.key_learnings.map((learning, index) => ( -
- - {isEditing ? ( - <> - updateKeyLearning(index, value)} - tag="span" - className="text-sm text-portfolio-muted flex-1" - placeholder="Key learning" - autoEdit={lastAddedLearningIndex === index} - onEditingChange={(editing) => { - if (!editing && lastAddedLearningIndex === index) { - setLastAddedLearningIndex(null); - } - }} - /> - - - ) : ( - {learning} - )} -
- ))} -
+
+

Tech Used

+ {isEditing && ( + + )}
)} +
+ {localProject.key_learnings && localProject.key_learnings.map((learning, index) => ( +
+ {isEditing ? ( + <> + updateKeyLearning(index, value)} + tag="span" + className="text-sm bg-portfolio-violet text-white py-1 px-3 rounded-full font-medium" + placeholder="Technology" + autoEdit={lastAddedLearningIndex === index} + onEditingChange={(editing) => { + if (!editing && lastAddedLearningIndex === index) { + setLastAddedLearningIndex(null); + } + }} + /> + + + ) : ( + + {learning} + + )} +
+ ))} +
{isEditing && ( )}
- {/* Features Section */} + {/* Key Learnings Section */}
+ {localProject.features.length > 0 && ( +
+

Key Learnings

+ {isEditing && ( + + )} +
+ )}
{localProject.features.map((feature) => (
{ value={feature.title} onChange={(value) => updateFeature(feature.id, "title", value)} tag="span" - className="text-sm bg-gray-100 text-gray-700 py-1 px-3 rounded-full border" - placeholder="Feature name" + className="text-sm bg-portfolio-celadon text-white py-1 px-3 rounded-full font-medium" + placeholder="Learning" autoEdit={feature.id === lastAddedFeatureId} onEditingChange={(editing) => { if (!editing && lastAddedFeatureId === feature.id) { @@ -407,7 +461,7 @@ const addFeature = () => { ) : ( - + {feature.title} )} @@ -420,13 +474,27 @@ const addFeature = () => { onClick={addFeature} className="flex items-center text-gray-600 text-sm hover:text-gray-800 transition-colors" > - Add Feature + Add Learning )}
{/* Links Section */}
+ {localProject.links.length > 0 && ( +
+

Relevant links

+ {isEditing && ( + + )} +
+ )} {localProject.links.map((link) => (
void; + onBlur?: () => void; + placeholder?: string; + className?: string; +} + +export const RichTextEditor = ({ + content, + onChange, + onBlur, + placeholder = "Start typing...", + className = "" +}: RichTextEditorProps) => { + const editor = useEditor({ + extensions: [ + StarterKit, + Link.configure({ + openOnClick: false, + HTMLAttributes: { + class: 'text-primary underline', + }, + }), + TextAlign.configure({ + types: ['heading', 'paragraph'], + alignments: ['left', 'center', 'right', 'justify'], + }), + ], + content, + onUpdate: ({ editor }) => { + onChange(editor.getHTML()); + }, + onBlur: () => { + onBlur?.(); + }, + editorProps: { + attributes: { + class: 'prose prose-sm max-w-none focus:outline-none min-h-[100px] p-3', + }, + }, + }); + + if (!editor) { + return null; + } + + const addLink = () => { + const url = window.prompt('Enter URL:'); + if (url) { + editor.chain().focus().setLink({ href: url }).run(); + } + }; + + return ( +
+
+ + + + + +
+ + + + +
+ +
+ ); +}; diff --git a/src/components/SectionItem.tsx b/src/components/SectionItem.tsx index 0129038..5cd034c 100644 --- a/src/components/SectionItem.tsx +++ b/src/components/SectionItem.tsx @@ -2,8 +2,10 @@ import React, { useCallback } from "react"; import ProjectList from "./ProjectList"; import EditableField from "./EditableField"; +import { RichTextEditor } from "./RichTextEditor"; import { ArrowUp, ArrowDown, Trash2 } from "lucide-react"; import { SectionData } from "@/types/portfolio"; +import { sanitizeHtml } from "@/utils/securityUtils"; interface SectionItemProps { section: SectionData; @@ -100,19 +102,18 @@ const SectionItem: React.FC = React.memo(({
{isEditingMode ? ( - ) : ( section.description && section.description.trim().length > 0 && ( -

- {section.description} -

+
) )}
diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx index 68551b9..35fecca 100644 --- a/src/components/ui/input.tsx +++ b/src/components/ui/input.tsx @@ -8,7 +8,7 @@ const Input = React.forwardRef>( ( return (