diff --git a/package-lock.json b/package-lock.json index be3c557..f8f6af6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,22 @@ { "name": "my-weekly-todo-list", - "version": "1.11.2", + "version": "1.13.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "my-weekly-todo-list", - "version": "1.11.1", + "version": "1.13.0", "license": "MIT", "dependencies": { "@auth/prisma-adapter": "^2.11.1", "@next-auth/prisma-adapter": "^1.0.7", "@prisma/client": "^5.22.0", + "@tiptap/extension-link": "^3.20.0", + "@tiptap/extension-placeholder": "^3.20.0", + "@tiptap/extension-underline": "^3.20.0", + "@tiptap/react": "^3.20.0", + "@tiptap/starter-kit": "^3.20.0", "@types/bcryptjs": "^2.4.6", "@types/nodemailer": "^7.0.11", "bcryptjs": "^3.0.3", @@ -840,6 +845,34 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "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", + "optional": true, + "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", + "optional": true, + "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", + "optional": true + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -2290,6 +2323,12 @@ "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==", "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/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -2347,6 +2386,453 @@ "tslib": "^2.4.0" } }, + "node_modules/@tiptap/core": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.20.0.tgz", + "integrity": "sha512-aC9aROgia/SpJqhsXFiX9TsligL8d+oeoI8W3u00WI45s0VfsqjgeKQLDLF7Tu7hC+7F02teC84SAHuup003VQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/pm": "^3.20.0" + } + }, + "node_modules/@tiptap/extension-blockquote": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-3.20.0.tgz", + "integrity": "sha512-LQzn6aGtL4WXz2+rYshl/7/VnP2qJTpD7fWL96GXAzhqviPEY1bJES7poqJb3MU/gzl8VJUVzVzU1VoVfUKlbA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.0" + } + }, + "node_modules/@tiptap/extension-bold": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-3.20.0.tgz", + "integrity": "sha512-sQklEWiyf58yDjiHtm5vmkVjfIc/cBuSusmCsQ0q9vGYnEF1iOHKhGpvnCeEXNeqF3fiJQRlquzt/6ymle3Iwg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.0" + } + }, + "node_modules/@tiptap/extension-bubble-menu": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.20.0.tgz", + "integrity": "sha512-MDosUfs8Tj+nwg8RC+wTMWGkLJORXmbR6YZgbiX4hrc7G90Gopdd6kj6ht5/T8t7dLLaX7N0+DEHdUEPGED7dw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.0", + "@tiptap/pm": "^3.20.0" + } + }, + "node_modules/@tiptap/extension-bullet-list": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-3.20.0.tgz", + "integrity": "sha512-OcKMeopBbqWzhSi6o8nNz0aayogg1sfOAhto3NxJu3Ya32dwBFqmHXSYM6uW4jOphNvVPyjiq9aNRh3qTdd1dw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "^3.20.0" + } + }, + "node_modules/@tiptap/extension-code": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-3.20.0.tgz", + "integrity": "sha512-TYDWFeSQ9umiyrqsT6VecbuhL8XIHkUhO+gEk0sVvH67ZLwjFDhAIIgWIr1/dbIGPcvMZM19E7xUUhAdIaXaOQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.0" + } + }, + "node_modules/@tiptap/extension-code-block": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-3.20.0.tgz", + "integrity": "sha512-lBbmNek14aCjrHcBcq3PRqWfNLvC6bcRa2Osc6e/LtmXlcpype4f6n+Yx+WZ+f2uUh0UmDRCz7BEyUETEsDmlQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.0", + "@tiptap/pm": "^3.20.0" + } + }, + "node_modules/@tiptap/extension-document": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-3.20.0.tgz", + "integrity": "sha512-oJfLIG3vAtZo/wg29WiBcyWt22KUgddpP8wqtCE+kY5Dw8znLR9ehNmVWlSWJA5OJUMO0ntAHx4bBT+I2MBd5w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.0" + } + }, + "node_modules/@tiptap/extension-dropcursor": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-3.20.0.tgz", + "integrity": "sha512-d+cxplRlktVgZPwatnc34IArlppM0IFKS1J5wLk+ba1jidizsbMVh45tP/BTK2flhyfRqcNoB5R0TArhUpbkNQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extensions": "^3.20.0" + } + }, + "node_modules/@tiptap/extension-floating-menu": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-3.20.0.tgz", + "integrity": "sha512-rYs4Bv5pVjqZ/2vvR6oe7ammZapkAwN51As/WDbemvYDjfOGRqK58qGauUjYZiDzPOEIzI2mxGwsZ4eJhPW4Ig==", + "license": "MIT", + "optional": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@floating-ui/dom": "^1.0.0", + "@tiptap/core": "^3.20.0", + "@tiptap/pm": "^3.20.0" + } + }, + "node_modules/@tiptap/extension-gapcursor": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-3.20.0.tgz", + "integrity": "sha512-P/LasfvG9/qFq43ZAlNbAnPnXC+/RJf49buTrhtFvI9Zg0+Lbpjx1oh6oMHB19T88Y28KtrckfFZ8aTSUWDq6w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extensions": "^3.20.0" + } + }, + "node_modules/@tiptap/extension-hard-break": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-3.20.0.tgz", + "integrity": "sha512-rqvhMOw4f+XQmEthncbvDjgLH6fz8L9splnKZC7OeS0eX8b0qd7+xI1u5kyxF3KA2Z0BnigES++jjWuecqV6mA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.0" + } + }, + "node_modules/@tiptap/extension-heading": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-3.20.0.tgz", + "integrity": "sha512-JgJhurnCe3eN6a0lEsNQM/46R1bcwzwWWZEFDSb1P9dR8+t1/5v7cMZWsSInpD7R4/74iJn0+M5hcXLwCmBmYA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.0" + } + }, + "node_modules/@tiptap/extension-horizontal-rule": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.20.0.tgz", + "integrity": "sha512-6uvcutFMv+9wPZgptDkbRDjAm3YVxlibmkhWD5GuaWwS9L/yUtobpI3GycujRSUZ8D3q6Q9J7LqpmQtQRTalWA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.0", + "@tiptap/pm": "^3.20.0" + } + }, + "node_modules/@tiptap/extension-italic": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.20.0.tgz", + "integrity": "sha512-/DhnKQF8yN8RxtuL8abZ28wd5281EaGoE2Oha35zXSOF1vNYnbyt8Ymkv/7u1BcWEWTvRPgaju0YCGXisPRLYw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.0" + } + }, + "node_modules/@tiptap/extension-link": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-3.20.0.tgz", + "integrity": "sha512-qI/5A+R0ZWBxo/8HxSn1uOyr7odr3xHBZ/gzOR1GUJaZqjlJxkWFX0RtXMbLKEGEvT25o345cF7b0wFznEh8qA==", + "license": "MIT", + "dependencies": { + "linkifyjs": "^4.3.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.0", + "@tiptap/pm": "^3.20.0" + } + }, + "node_modules/@tiptap/extension-list": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.20.0.tgz", + "integrity": "sha512-+V0/gsVWAv+7vcY0MAe6D52LYTIicMSHw00wz3ISZgprSb2yQhJ4+4gurOnUrQ4Du3AnRQvxPROaofwxIQ66WQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.0", + "@tiptap/pm": "^3.20.0" + } + }, + "node_modules/@tiptap/extension-list-item": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-3.20.0.tgz", + "integrity": "sha512-qEtjaaGPuqaFB4VpLrGDoIe9RHnckxPfu6d3rc22ap6TAHCDyRv05CEyJogqccnFceG/v5WN4znUBER8RWnWHA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "^3.20.0" + } + }, + "node_modules/@tiptap/extension-list-keymap": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-keymap/-/extension-list-keymap-3.20.0.tgz", + "integrity": "sha512-Z4GvKy04Ms4cLFN+CY6wXswd36xYsT2p/YL0V89LYFMZTerOeTjFYlndzn6svqL8NV1PRT5Diw4WTTxJSmcJPA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "^3.20.0" + } + }, + "node_modules/@tiptap/extension-ordered-list": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-3.20.0.tgz", + "integrity": "sha512-jVKnJvrizLk7etwBMfyoj6H2GE4M+PD4k7Bwp6Bh1ohBWtfIA1TlngdS842Mx5i1VB2e3UWIwr8ZH46gl6cwMA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "^3.20.0" + } + }, + "node_modules/@tiptap/extension-paragraph": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-3.20.0.tgz", + "integrity": "sha512-mM99zK4+RnEXIMCv6akfNATAs0Iija6FgyFA9J9NZ6N4o8y9QiNLLa6HjLpAC+W+VoCgQIekyoF/Q9ftxmAYDQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.0" + } + }, + "node_modules/@tiptap/extension-placeholder": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-3.20.0.tgz", + "integrity": "sha512-ZhYD3L5m16ydSe2z8vqz+RdtAG/iOQaFHHedFct70tKRoLqi2ajF5kgpemu8DwpaRTcyiCN4G99J/+MqehKNjQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extensions": "^3.20.0" + } + }, + "node_modules/@tiptap/extension-strike": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-3.20.0.tgz", + "integrity": "sha512-0vcTZRRAiDfon3VM1mHBr9EFmTkkUXMhm0Xtdtn0bGe+sIqufyi+hUYTEw93EQOD9XNsPkrud6jzQNYpX2H3AQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.0" + } + }, + "node_modules/@tiptap/extension-text": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.20.0.tgz", + "integrity": "sha512-tf8bE8tSaOEWabCzPm71xwiUhyMFKqY9jkP5af3Kr1/F45jzZFIQAYZooHI/+zCHRrgJ99MQHKHe1ZNvODrKHQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.0" + } + }, + "node_modules/@tiptap/extension-underline": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-3.20.0.tgz", + "integrity": "sha512-LzNXuy2jwR/y+ymoUqC72TiGzbOCjioIjsDu0MNYpHuHqTWPK5aV9Mh0nbZcYFy/7fPlV1q0W139EbJeYBZEAQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.0" + } + }, + "node_modules/@tiptap/extensions": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.20.0.tgz", + "integrity": "sha512-HIsXX942w3nbxEQBlMAAR/aa6qiMBEP7CsSMxaxmTIVAmW35p6yUASw6GdV1u0o3lCZjXq2OSRMTskzIqi5uLg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.0", + "@tiptap/pm": "^3.20.0" + } + }, + "node_modules/@tiptap/pm": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.20.0.tgz", + "integrity": "sha512-jn+2KnQZn+b+VXr8EFOJKsnjVNaA4diAEr6FOazupMt8W8ro1hfpYtZ25JL87Kao/WbMze55sd8M8BDXLUKu1A==", + "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.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-3.20.0.tgz", + "integrity": "sha512-jFLNzkmn18zqefJwPje0PPd9VhZ7Oy28YHiSvSc7YpBnQIbuN/HIxZ2lrOsKyEHta0WjRZjfU5X1pGxlbcGwOA==", + "license": "MIT", + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "fast-equals": "^5.3.3", + "use-sync-external-store": "^1.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "optionalDependencies": { + "@tiptap/extension-bubble-menu": "^3.20.0", + "@tiptap/extension-floating-menu": "^3.20.0" + }, + "peerDependencies": { + "@tiptap/core": "^3.20.0", + "@tiptap/pm": "^3.20.0", + "@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.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-3.20.0.tgz", + "integrity": "sha512-W4+1re35pDNY/7rpXVg+OKo/Fa4Gfrn08Bq3E3fzlJw6gjE3tYU8dY9x9vC2rK9pd9NOp7Af11qCFDaWpohXkw==", + "license": "MIT", + "dependencies": { + "@tiptap/core": "^3.20.0", + "@tiptap/extension-blockquote": "^3.20.0", + "@tiptap/extension-bold": "^3.20.0", + "@tiptap/extension-bullet-list": "^3.20.0", + "@tiptap/extension-code": "^3.20.0", + "@tiptap/extension-code-block": "^3.20.0", + "@tiptap/extension-document": "^3.20.0", + "@tiptap/extension-dropcursor": "^3.20.0", + "@tiptap/extension-gapcursor": "^3.20.0", + "@tiptap/extension-hard-break": "^3.20.0", + "@tiptap/extension-heading": "^3.20.0", + "@tiptap/extension-horizontal-rule": "^3.20.0", + "@tiptap/extension-italic": "^3.20.0", + "@tiptap/extension-link": "^3.20.0", + "@tiptap/extension-list": "^3.20.0", + "@tiptap/extension-list-item": "^3.20.0", + "@tiptap/extension-list-keymap": "^3.20.0", + "@tiptap/extension-ordered-list": "^3.20.0", + "@tiptap/extension-paragraph": "^3.20.0", + "@tiptap/extension-strike": "^3.20.0", + "@tiptap/extension-text": "^3.20.0", + "@tiptap/extension-underline": "^3.20.0", + "@tiptap/extensions": "^3.20.0", + "@tiptap/pm": "^3.20.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", @@ -2472,6 +2958,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": "20.19.34", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.34.tgz", @@ -2494,14 +3002,12 @@ "version": "15.7.15", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", - "devOptional": true, "license": "MIT" }, "node_modules/@types/react": { "version": "18.3.28", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", - "devOptional": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -2512,7 +3018,6 @@ "version": "18.3.7", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", - "dev": true, "license": "MIT", "peerDependencies": { "@types/react": "^18.0.0" @@ -2525,6 +3030,12 @@ "dev": true, "license": "MIT" }, + "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/yargs": { "version": "17.0.35", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", @@ -3233,7 +3744,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-query": { @@ -4152,6 +4662,12 @@ "node": "^14.15.0 || ^16.10.0 || >=18.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/cross-fetch": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz", @@ -4212,7 +4728,6 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "devOptional": true, "license": "MIT" }, "node_modules/damerau-levenshtein": { @@ -4508,6 +5023,18 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "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/error-ex": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", @@ -4705,7 +5232,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" @@ -5252,6 +5778,15 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, + "node_modules/fast-equals": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz", + "integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -8393,6 +8928,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", @@ -8487,6 +9037,23 @@ "tmpl": "1.0.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/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -8496,6 +9063,12 @@ "node": ">= 0.4" } }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT" + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -9091,6 +9664,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/own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -9775,6 +10354,201 @@ "dev": true, "license": "MIT" }, + "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.0", + "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.4.0.tgz", + "integrity": "sha512-z00qvurSdCEWUIulij/isHaqu4uLS8r/Fi61IbjdIPJEonQgggbJsLnstW7Lgdk4zQ68/yr6B6bf7sJXowIgdQ==", + "license": "MIT", + "dependencies": { + "prosemirror-keymap": "^1.0.0", + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-view": "^1.0.0" + } + }, + "node_modules/prosemirror-history": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.5.0.tgz", + "integrity": "sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.2.2", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.31.0", + "rope-sequence": "^1.3.0" + } + }, + "node_modules/prosemirror-inputrules": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.5.1.tgz", + "integrity": "sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-keymap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.3.tgz", + "integrity": "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "w3c-keyname": "^2.2.0" + } + }, + "node_modules/prosemirror-markdown": { + "version": "1.13.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.11.0", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.11.0.tgz", + "integrity": "sha512-4I7Ce4KpygXb9bkiPS3hTEk4dSHorfRw8uI0pE8IhxlK2GXsqv5tIA7JUSxtSu7u8APVOTtbUBxTmnHIxVkIJw==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.21.0" + } + }, + "node_modules/prosemirror-view": { + "version": "1.41.6", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.6.tgz", + "integrity": "sha512-mxpcDG4hNQa/CPtzxjdlir5bJFDlm0/x5nGBbStB2BWX+XOQ9M8ekEG+ojqB5BcVu2Rc80/jssCMZzSstJuSYg==", + "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", @@ -9785,6 +10559,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/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", @@ -10116,6 +10899,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "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", @@ -11290,6 +12079,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/uglify-js": { "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", @@ -11469,6 +12264,12 @@ "node": ">=10.12.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" + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -11879,4 +12680,4 @@ } } } -} \ No newline at end of file +} diff --git a/package.json b/package.json index c3eb549..76f273d 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,11 @@ "@auth/prisma-adapter": "^2.11.1", "@next-auth/prisma-adapter": "^1.0.7", "@prisma/client": "^5.22.0", + "@tiptap/extension-link": "^3.20.0", + "@tiptap/extension-placeholder": "^3.20.0", + "@tiptap/extension-underline": "^3.20.0", + "@tiptap/react": "^3.20.0", + "@tiptap/starter-kit": "^3.20.0", "@types/bcryptjs": "^2.4.6", "@types/nodemailer": "^7.0.11", "bcryptjs": "^3.0.3", @@ -59,4 +64,4 @@ "ts-jest": "^29.0.0", "typescript": "^5.0.0" } -} \ No newline at end of file +} diff --git a/prisma/schema.prisma b/prisma/schema.prisma index a15df6f..9a20d6c 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -73,6 +73,7 @@ model User { pastDayColor String? @default("#a6a6a7") goalScope String @default("week") dateLayout String @default("right") + mobileDateLayout String @default("below") dateAlignment String @default("center") hourLabelFormat String @default("short") showSubHourSlots Boolean @default(true) @@ -158,6 +159,7 @@ model Task { recurrenceUnit String? recurrenceTime String? recurrenceEndDate DateTime? + recurrenceDays String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt externalId String? diff --git a/query.js b/query.js new file mode 100644 index 0000000..be38b9f --- /dev/null +++ b/query.js @@ -0,0 +1,9 @@ +const { PrismaClient } = require('@prisma/client'); +const prisma = new PrismaClient(); + +async function main() { + const conns = await prisma.calendarConnection.findMany({ where: { provider: 'synology' } }); + console.log(JSON.stringify(conns, null, 2)); +} + +main().catch(console.error).finally(() => prisma.$disconnect()); diff --git a/src/app/api/calendar/sync/route.ts b/src/app/api/calendar/sync/route.ts index f6fe96b..e7f8efb 100644 --- a/src/app/api/calendar/sync/route.ts +++ b/src/app/api/calendar/sync/route.ts @@ -56,7 +56,7 @@ export async function POST(request: NextRequest) { dbId: conn.id, userId: user.id, id: conn.id, - provider: conn.provider as 'google' | 'apple' | 'outlook', + provider: conn.provider as 'google' | 'apple' | 'outlook' | 'synology', accessToken: conn.accessToken, refreshToken: conn.refreshToken ?? undefined, expiresAt: conn.expiresAt ?? undefined, diff --git a/src/app/api/calendar/synology/connect/route.ts b/src/app/api/calendar/synology/connect/route.ts new file mode 100644 index 0000000..fc89f80 --- /dev/null +++ b/src/app/api/calendar/synology/connect/route.ts @@ -0,0 +1,88 @@ +import { NextResponse } from 'next/server'; +import { validateCredentials } from '@/lib/synology-calendar'; +import { getServerSession } from 'next-auth'; +import { authOptions } from "@/lib/auth"; +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient(); + +export async function POST(req: Request) { + try { + const session = await getServerSession(authOptions); + if (!session?.user?.email) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const body = await req.json(); + const { serverUrl, username, password } = body; + + if (!serverUrl || !username || !password) { + return NextResponse.json( + { error: 'Server URL, username, and password are required' }, + { status: 400 } + ); + } + + // Validate credentials and get calendars + const calendars = await validateCredentials(serverUrl, username, password); + + // Find user + const user = await prisma.user.findUnique({ + where: { email: session.user.email } + }); + + if (!user) { + return NextResponse.json({ error: 'User not found' }, { status: 404 }); + } + + // Check if connection already exists and update it, otherwise create new + const existingConnection = await prisma.calendarConnection.findFirst({ + where: { + userId: user.id, + provider: 'synology' + } + }); + + let connection; + + if (existingConnection) { + connection = await prisma.calendarConnection.update({ + where: { id: existingConnection.id }, + data: { + accessToken: `${username}:${password}`, + refreshToken: serverUrl, // Use refreshToken field to store the server URL since Basic Auth doesn't need refresh tokens + calendars: calendars.map(cal => ({ + id: cal.id, + title: cal.title, + isPrimary: cal.isPrimary, + selected: true // Default to selected + })) as any, + updatedAt: new Date() + } + }); + } else { + connection = await prisma.calendarConnection.create({ + data: { + userId: user.id, + provider: 'synology', + accessToken: `${username}:${password}`, + refreshToken: serverUrl, + calendars: calendars.map(cal => ({ + id: cal.id, + title: cal.title, + isPrimary: cal.isPrimary, + selected: true + })) as any + } + }); + } + + return NextResponse.json({ success: true, connection }); + } catch (error: any) { + console.error('Error connecting Synology Calendar:', error); + return NextResponse.json( + { error: error.message || 'Failed to connect Synology Calendar' }, + { status: 500 } + ); + } +} diff --git a/src/app/api/projects/route.ts b/src/app/api/projects/route.ts new file mode 100644 index 0000000..a21c1f0 --- /dev/null +++ b/src/app/api/projects/route.ts @@ -0,0 +1,104 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { getServerSession } from 'next-auth'; +import { authOptions } from '@/lib/auth'; +import { prisma } from '@/lib/prisma'; + +export async function GET() { + const session = await getServerSession(authOptions); + if (!session?.user?.email) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const user = await prisma.user.findUnique({ + where: { email: session.user.email }, + select: { id: true }, + }); + if (!user) return NextResponse.json({ error: 'User not found' }, { status: 404 }); + + const projects = await prisma.project.findMany({ + where: { userId: user.id }, + orderBy: { order: 'asc' }, + }); + + return NextResponse.json({ projects }); +} + +export async function POST(request: NextRequest) { + const session = await getServerSession(authOptions); + if (!session?.user?.email) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const user = await prisma.user.findUnique({ + where: { email: session.user.email }, + select: { id: true }, + }); + if (!user) return NextResponse.json({ error: 'User not found' }, { status: 404 }); + + const { name, color, icon } = await request.json(); + if (!name) { + return NextResponse.json({ error: 'Name is required' }, { status: 400 }); + } + + const project = await prisma.project.create({ + data: { name, color: color || null, icon: icon || null, userId: user.id }, + }); + + return NextResponse.json({ project }); +} + +export async function PATCH(request: NextRequest) { + const session = await getServerSession(authOptions); + if (!session?.user?.email) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const user = await prisma.user.findUnique({ + where: { email: session.user.email }, + select: { id: true }, + }); + if (!user) return NextResponse.json({ error: 'User not found' }, { status: 404 }); + + const { id, name, color, icon } = await request.json(); + if (!id) return NextResponse.json({ error: 'ID required' }, { status: 400 }); + + const existing = await prisma.project.findFirst({ where: { id, userId: user.id } }); + if (!existing) return NextResponse.json({ error: 'Not found' }, { status: 404 }); + + const project = await prisma.project.update({ + where: { id }, + data: { + ...(name !== undefined && { name }), + ...(color !== undefined && { color: color || null }), + ...(icon !== undefined && { icon: icon || null }), + }, + }); + + return NextResponse.json({ project }); +} + +export async function DELETE(request: NextRequest) { + const session = await getServerSession(authOptions); + if (!session?.user?.email) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const user = await prisma.user.findUnique({ + where: { email: session.user.email }, + select: { id: true }, + }); + if (!user) return NextResponse.json({ error: 'User not found' }, { status: 404 }); + + const { searchParams } = new URL(request.url); + const id = searchParams.get('id'); + if (!id) return NextResponse.json({ error: 'ID required' }, { status: 400 }); + + const existing = await prisma.project.findFirst({ where: { id, userId: user.id } }); + if (!existing) return NextResponse.json({ error: 'Not found' }, { status: 404 }); + + // Unlink tasks from this project + await prisma.task.updateMany({ where: { projectId: id }, data: { projectId: null } }); + await prisma.project.delete({ where: { id } }); + + return NextResponse.json({ message: 'Project deleted' }); +} diff --git a/src/app/api/tasks/import/route.ts b/src/app/api/tasks/import/route.ts index 2f12ad4..b7562f0 100644 --- a/src/app/api/tasks/import/route.ts +++ b/src/app/api/tasks/import/route.ts @@ -35,7 +35,7 @@ export async function POST(req: NextRequest) { const body = await req.json(); const { provider, sourceLists } = body; - if (!provider || !['google', 'outlook'].includes(provider)) { + if (!provider || !['google', 'outlook', 'synology'].includes(provider)) { return NextResponse.json({ error: 'Invalid provider' }, { status: 400 }); } @@ -151,6 +151,47 @@ export async function POST(req: NextRequest) { } } + if (provider === 'synology') { + const connection = await prisma.calendarConnection.findFirst({ + where: { userId: user.id, provider: 'synology' } + }); + + if (!connection || !connection.accessToken || !connection.refreshToken) { + return NextResponse.json({ error: 'Synology account not connected' }, { status: 400 }); + } + + const [username, password] = connection.accessToken.split(':'); + const serverUrl = connection.refreshToken; + + const { fetchSynologyReminderLists, fetchSynologyTasks } = await import('@/lib/synology-tasks'); + + if (targetLists.length === 0) { + const synoLists = await fetchSynologyReminderLists(serverUrl, username, password); + if (synoLists.length > 0) { + targetLists = [{ id: synoLists[0].id, title: synoLists[0].title }]; + } + } + + for (const sourceList of targetLists) { + try { + const synoTasks = await fetchSynologyTasks(serverUrl, username, password, sourceList.id); + for (const t of synoTasks) { + importedTasks.push({ + title: t.title, + description: t.notes || '', + externalId: t.id, + externalListId: sourceList.id, + dueDate: t.due ? new Date(t.due) : null, + status: t.status === 'completed' ? 'completed' : 'notStarted', + sourceListTitle: sourceList.title, + }); + } + } catch (e) { + console.error(`Error fetching Synology tasks for list ${sourceList.id}:`, e); + } + } + } + // Group tasks by source list title const tasksByList = new Map(); // Pre-initialize with all target lists to ensure they are created even if empty diff --git a/src/app/api/tasks/lists/route.ts b/src/app/api/tasks/lists/route.ts index d008ed7..0284f21 100644 --- a/src/app/api/tasks/lists/route.ts +++ b/src/app/api/tasks/lists/route.ts @@ -20,7 +20,7 @@ export async function GET(req: NextRequest) { const { searchParams } = new URL(req.url); const provider = searchParams.get('provider'); - if (!provider || !['google', 'outlook'].includes(provider)) { + if (!provider || !['google', 'outlook', 'synology'].includes(provider)) { return NextResponse.json({ error: 'Invalid provider' }, { status: 400 }); } @@ -60,6 +60,27 @@ export async function GET(req: NextRequest) { }); } + if (provider === 'synology') { + const connection = await prisma.calendarConnection.findFirst({ + where: { userId: user.id, provider: 'synology' } + }); + + if (!connection || !connection.accessToken || !connection.refreshToken) { + return NextResponse.json({ error: 'Synology account not connected' }, { status: 400 }); + } + + const [username, password] = connection.accessToken.split(':'); + const serverUrl = connection.refreshToken; + + // Re-export dynamically to avoid circular dependencies in route + const { fetchSynologyReminderLists } = await import('@/lib/synology-tasks'); + const lists = await fetchSynologyReminderLists(serverUrl, username, password); + + return NextResponse.json({ + lists: lists.map(l => ({ id: l.id, title: l.title })) + }); + } + return NextResponse.json({ error: 'Invalid provider' }, { status: 400 }); } catch (error: unknown) { diff --git a/src/app/api/tasks/route.ts b/src/app/api/tasks/route.ts index 7f09069..dda7c1b 100644 --- a/src/app/api/tasks/route.ts +++ b/src/app/api/tasks/route.ts @@ -53,19 +53,33 @@ const projectFutureTasks = (tasks: Task[], horizonDays = 90) => { const interval = latestTask.recurrenceInterval || 1; const unit = latestTask.recurrenceUnit || 'weeks'; // 'days', 'weeks', 'months', 'years' - // Advance to next slot - // We only generate UP TO horizon. - // We avoid generating duplicates if a real task already exists at that date? - // We already grouped by series, and we are projecting from the *latest* one. - // So any future dates we generate *should* be new. + // Parse recurrenceDays for weekly multi-day recurrence + let recDays: number[] | null = null; + if (latestTask.recurrenceDays) { + try { recDays = JSON.parse(latestTask.recurrenceDays); } catch { recDays = null; } + } // Safety break let iterations = 0; - while (currentDate < horizonDate && iterations < 100) { + while (currentDate < horizonDate && iterations < 500) { iterations++; // Advance date - if (unit === 'days') { + if (unit === 'weeks' && recDays && recDays.length > 0) { + // Multi-day weekly: advance day by day within each week cycle + currentDate.setDate(currentDate.getDate() + 1); + // If we've moved past the current week (interval > 1), skip ahead + const daysSinceBase = Math.floor((currentDate.getTime() - baseDate.getTime()) / 86400000); + const weeksSinceBase = Math.floor(daysSinceBase / 7); + if (weeksSinceBase % interval !== 0) { + // Skip to start of next valid week + const nextValidWeek = (Math.floor(weeksSinceBase / interval) + 1) * interval; + const daysToSkip = (nextValidWeek * 7) - daysSinceBase; + currentDate.setDate(currentDate.getDate() + daysToSkip); + } + // Only emit if current day matches one of the selected days + if (!recDays.includes(currentDate.getDay())) continue; + } else if (unit === 'days') { currentDate.setDate(currentDate.getDate() + interval); } else if (unit === 'weeks') { currentDate.setDate(currentDate.getDate() + (interval * 7)); @@ -74,7 +88,6 @@ const projectFutureTasks = (tasks: Task[], horizonDays = 90) => { } else if (unit === 'years') { currentDate.setFullYear(currentDate.getFullYear() + interval); } else { - // Default to weekly if unknown currentDate.setDate(currentDate.getDate() + 7); } @@ -120,14 +133,24 @@ export async function GET(request: NextRequest) { try { const session = await getServerSession(authOptions); - if (!session?.user) { + if (!session?.user?.email) { return NextResponse.json( { error: 'Unauthorized' }, { status: 401 } ); } - const userId = (session.user as any).id; + // Resolve user by email (session ID can be stale after DB changes) + const dbUser = await prisma.user.findUnique({ + where: { email: session.user.email }, + select: { id: true } + }); + + if (!dbUser) { + return NextResponse.json({ error: 'User not found' }, { status: 404 }); + } + + const userId = dbUser.id; const { searchParams } = new URL(request.url); const start = searchParams.get('start'); const end = searchParams.get('end'); @@ -195,10 +218,9 @@ export async function POST(request: NextRequest) { ); } - const userId = (session.user as any).id; const body = await request.json(); - const { title, description, dayOfWeek, order, markdownContent, somedayListId, somedaySlotIndex, startTime, scheduledDate, recurrenceInterval, recurrenceUnit, recurrenceTime, recurrenceEndDate, parentTaskId, projectId } = body; + const { title, description, dayOfWeek, order, markdownContent, somedayListId, somedaySlotIndex, startTime, scheduledDate, recurrenceInterval, recurrenceUnit, recurrenceTime, recurrenceEndDate, recurrenceDays, parentTaskId, projectId } = body; let { isRolling } = body; const { isRecurring } = body; @@ -209,30 +231,27 @@ export async function POST(request: NextRequest) { ); } - // If isRolling is not specified, check user preference - if (isRolling === undefined) { - const user = await prisma.user.findUnique({ - where: { email: session.user.email }, - select: { autoRolling: true } - }); - isRolling = user?.autoRolling || false; - } - - console.log('API POST - Creating task for user:', userId); - - // Safety check: ensure user exists before creation to provide better error - const userExists = await prisma.user.findUnique({ - where: { id: userId } + // Resolve user by email (session ID can be stale after DB changes) + const user = await prisma.user.findUnique({ + where: { email: session.user.email }, + select: { id: true, autoRolling: true } }); - if (!userExists) { - console.error('CRITICAL: User ID in session does not exist in database:', userId); + if (!user) { + console.error('CRITICAL: User email in session does not exist in database:', session.user.email); return NextResponse.json( { error: 'User does not exist in database. Please log out and back in.' }, { status: 400 } ); } + const userId = user.id; + + // If isRolling is not specified, check user preference + if (isRolling === undefined) { + isRolling = user.autoRolling || false; + } + const task = await prisma.task.create({ data: { title, @@ -250,6 +269,7 @@ export async function POST(request: NextRequest) { recurrenceUnit, recurrenceTime, recurrenceEndDate: recurrenceEndDate ? new Date(recurrenceEndDate) : null, + recurrenceDays: recurrenceDays ? JSON.stringify(recurrenceDays) : null, somedaySlotIndex: somedaySlotIndex !== undefined ? parseInt(somedaySlotIndex) : null, parentTaskId: parentTaskId || null, ...(projectId !== undefined && { projectId: projectId || null }), @@ -274,18 +294,28 @@ export async function PATCH(request: NextRequest) { try { const session = await getServerSession(authOptions); - if (!session?.user) { + if (!session?.user?.email) { return NextResponse.json( { error: 'Unauthorized' }, { status: 401 } ); } - const userId = (session.user as any).id; + // Resolve user by email (session ID can be stale after DB changes) + const dbUser = await prisma.user.findUnique({ + where: { email: session.user.email }, + select: { id: true } + }); + + if (!dbUser) { + return NextResponse.json({ error: 'User not found' }, { status: 404 }); + } + + const userId = dbUser.id; const body = await request.json(); const { id } = body; - const { title, description, completed, dayOfWeek, order, markdownContent, scheduledDate, startTime, somedayListId, somedaySlotIndex, isRecurring, recurrenceInterval, recurrenceUnit, recurrenceTime, recurrenceEndDate, restore, parentTaskId, projectId } = body; + const { title, description, completed, dayOfWeek, order, markdownContent, scheduledDate, startTime, somedayListId, somedaySlotIndex, isRecurring, recurrenceInterval, recurrenceUnit, recurrenceTime, recurrenceEndDate, recurrenceDays, restore, parentTaskId, projectId } = body; if (!id) { return NextResponse.json( @@ -325,6 +355,7 @@ export async function PATCH(request: NextRequest) { recurrenceUnit: originalTask.recurrenceUnit, recurrenceTime: originalTask.recurrenceTime, recurrenceEndDate: originalTask.recurrenceEndDate, + recurrenceDays: originalTask.recurrenceDays, isRolling: originalTask.isRolling, // Apply overrides from the patch body immediately completed: completed !== undefined ? completed : false, @@ -368,6 +399,7 @@ export async function PATCH(request: NextRequest) { ...(recurrenceUnit !== undefined && { recurrenceUnit }), ...(recurrenceTime !== undefined && { recurrenceTime }), ...(recurrenceEndDate !== undefined && { recurrenceEndDate: recurrenceEndDate ? new Date(recurrenceEndDate) : null }), + ...(recurrenceDays !== undefined && { recurrenceDays: recurrenceDays ? JSON.stringify(recurrenceDays) : null }), ...(restore === true && { deletedAt: null }), ...(somedaySlotIndex !== undefined && { somedaySlotIndex: somedaySlotIndex !== null ? parseInt(somedaySlotIndex) : null }), ...(parentTaskId !== undefined && { parentTaskId: parentTaskId || null }), @@ -396,14 +428,24 @@ export async function DELETE(request: NextRequest) { try { const session = await getServerSession(authOptions); - if (!session?.user) { + if (!session?.user?.email) { return NextResponse.json( { error: 'Unauthorized' }, { status: 401 } ); } - const userId = (session.user as any).id; + // Resolve user by email (session ID can be stale after DB changes) + const dbUser = await prisma.user.findUnique({ + where: { email: session.user.email }, + select: { id: true } + }); + + if (!dbUser) { + return NextResponse.json({ error: 'User not found' }, { status: 404 }); + } + + const userId = dbUser.id; const { searchParams } = new URL(request.url); const id = searchParams.get('id'); diff --git a/src/app/api/tasks/sync/route.ts b/src/app/api/tasks/sync/route.ts index a597dca..bcaf051 100644 --- a/src/app/api/tasks/sync/route.ts +++ b/src/app/api/tasks/sync/route.ts @@ -316,6 +316,106 @@ export async function GET(req: NextRequest) { } } + // --- Synology pull-sync --- + const synologySyncedLists = syncedLists.filter(l => l.externalProvider === 'synology'); + const synologyLocalTasks = allExternalTasks.filter(t => t.externalProvider === 'synology'); + const hasSynologyTasks = synologyLocalTasks.length > 0 || synologySyncedLists.length > 0; + + if (hasSynologyTasks) { + const synoConnection = await prisma.calendarConnection.findFirst({ + where: { userId: user.id, provider: 'synology' } + }); + + if (synoConnection?.accessToken && synoConnection?.refreshToken) { + const [synoUsername, synoPassword] = synoConnection.accessToken.split(':'); + const synoServerUrl = synoConnection.refreshToken; + + if (synoUsername && synoPassword && synoServerUrl) { + const { fetchSynologyTasks } = await import('@/lib/synology-tasks'); + + const synoListIds = new Set(); + const synoListIdToSomedayList = new Map(); + + for (const task of synologyLocalTasks) { + if (task.externalListId) synoListIds.add(task.externalListId); + } + for (const sl of synologySyncedLists) { + if (sl.externalId) { + synoListIds.add(sl.externalId); + synoListIdToSomedayList.set(sl.externalId, { id: sl.id, title: sl.title }); + } + } + + const synoByList = new Map(); + for (const task of synologyLocalTasks) { + if (!task.externalListId) continue; + if (!synoByList.has(task.externalListId)) synoByList.set(task.externalListId, []); + synoByList.get(task.externalListId)!.push(task); + } + + for (const listId of synoListIds) { + const localTasks = synoByList.get(listId) || []; + try { + const remoteTasks = await fetchSynologyTasks(synoServerUrl, synoUsername, synoPassword, listId); + // Remap ID: the fetchSynologyTasks prefixes ids with "synology::" + const remoteMap = new Map(remoteTasks.map(t => [t.id.replace(/^synology::/, ''), t])); + const existingExternalIds = new Set(localTasks.map(t => t.externalId?.replace(/^synology::/, ''))); + + for (const localTask of localTasks) { + const cleanExtId = localTask.externalId?.replace(/^synology::/, ''); + const remote = cleanExtId ? remoteMap.get(cleanExtId) : null; + + if (!remote) { + await prisma.task.update({ where: { id: localTask.id }, data: { deletedAt: new Date() } }); + deleted++; + continue; + } + + const updateData: any = { lastSyncedAt: new Date() }; + const remoteCompleted = remote.status === 'completed'; + if (remoteCompleted !== localTask.completed) updateData.completed = remoteCompleted; + if (remote.title && remote.title !== localTask.title) updateData.title = remote.title; + + await prisma.task.update({ where: { id: localTask.id }, data: updateData }); + if (Object.keys(updateData).length > 1) updated++; + } + + // Create new local tasks from remote + const somedayListInfo = synoListIdToSomedayList.get(listId); + if (somedayListInfo) { + const newRemoteTasks = remoteTasks.filter(rt => { + const cleanId = rt.id.replace(/^synology::/, ''); + return !existingExternalIds.has(cleanId); + }); + + for (const remote of newRemoteTasks) { + if (!remote.title?.trim()) continue; + await prisma.task.create({ + data: { + userId: user.id, + title: remote.title, + description: remote.notes || null, + completed: remote.status === 'completed', + somedayListId: somedayListInfo.id, + externalId: remote.id, + externalProvider: 'synology', + externalListId: listId, + scheduledDate: remote.due ? new Date(remote.due) : null, + lastSyncedAt: new Date(), + } + }); + existingExternalIds.add(remote.id.replace(/^synology::/, '')); + created++; + } + } + } catch (listError) { + console.error(`Error syncing Synology list ${listId}:`, listError); + } + } + } + } + } + return NextResponse.json({ success: true, updated, deleted, created }); } catch (error: unknown) { diff --git a/src/app/api/user/profile/route.ts b/src/app/api/user/profile/route.ts index 2ae492c..d32635b 100644 --- a/src/app/api/user/profile/route.ts +++ b/src/app/api/user/profile/route.ts @@ -43,6 +43,7 @@ export async function GET(request: NextRequest) { goalFontWeight: true, goalScope: true, dateLayout: true, + mobileDateLayout: true, headlineFont: true, headlineFontSize: true, headlineFontWeight: true, @@ -120,7 +121,7 @@ export async function PATCH(request: NextRequest) { weekdayColor, dateColor, taskColor, todayHighlightColor, pastDayColor, goalFallbackType, goalDefaultSentence, goalFontFamily, goalFontSize, goalFontWeight, goalScope, - dateLayout, + dateLayout, mobileDateLayout, hourLabelFormat, showSubHourSlots, allDayPosition, cwFontFamily, cwFontSize, cwFontWeight, cwColor, yearFontFamily, yearFontSize, yearFontWeight, yearColor, @@ -182,6 +183,7 @@ export async function PATCH(request: NextRequest) { ...(goalFontWeight !== undefined && { goalFontWeight }), ...(goalScope !== undefined && { goalScope }), ...(dateLayout !== undefined && { dateLayout }), + ...(mobileDateLayout !== undefined && { mobileDateLayout }), ...(hourLabelFormat !== undefined && { hourLabelFormat }), ...(showSubHourSlots !== undefined && { showSubHourSlots }), ...(allDayPosition !== undefined && { allDayPosition }), @@ -265,6 +267,7 @@ export async function PATCH(request: NextRequest) { goalFontWeight: true, goalScope: true, dateLayout: true, + mobileDateLayout: true, cwFontFamily: true, cwFontSize: true, cwFontWeight: true, diff --git a/src/app/globals.css b/src/app/globals.css index a0636e1..5f855f2 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1041,6 +1041,10 @@ h3 { font-size: 0.8125rem; color: var(--weekly-text); position: relative; + cursor: grab; +} +.subtask-item:active { + cursor: grabbing; } .subtask-item.completed .subtask-title { @@ -2750,27 +2754,27 @@ h3 { /* Class-based animation that works on ALL browsers (no View Transitions API needed) */ @keyframes navSlideInFromRight { - from { transform: translateX(40%); opacity: 0.3; } + from { transform: translateX(30%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } @keyframes navSlideInFromLeft { - from { transform: translateX(-40%); opacity: 0.3; } + from { transform: translateX(-30%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } .slide-animate-next { - animation: navSlideInFromRight 0.35s cubic-bezier(0.25, 1, 0.5, 1) both; + animation: navSlideInFromRight 0.3s cubic-bezier(0.25, 1, 0.5, 1) both; } .slide-animate-prev { - animation: navSlideInFromLeft 0.35s cubic-bezier(0.25, 1, 0.5, 1) both; + animation: navSlideInFromLeft 0.3s cubic-bezier(0.25, 1, 0.5, 1) both; } -/* Week navigation: 5x longer for a more dramatic slide */ +/* Week navigation: slightly longer slide with more offset */ .slide-animate-week-next { - animation: navSlideInFromRight 1.75s cubic-bezier(0.25, 1, 0.5, 1) both; + animation: navSlideInFromRight 0.45s cubic-bezier(0.25, 1, 0.5, 1) both; } .slide-animate-week-prev { - animation: navSlideInFromLeft 1.75s cubic-bezier(0.25, 1, 0.5, 1) both; + animation: navSlideInFromLeft 0.45s cubic-bezier(0.25, 1, 0.5, 1) both; } /* Smooth View Transitions */ diff --git a/src/components/CalendarEventModal.tsx b/src/components/CalendarEventModal.tsx index 09edcde..e53b7f8 100644 --- a/src/components/CalendarEventModal.tsx +++ b/src/components/CalendarEventModal.tsx @@ -1,4 +1,6 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, lazy, Suspense } from 'react'; + +const RichTextEditor = lazy(() => import('./RichTextEditor')); interface CalendarEventModalProps { event?: any; // Existing event if editing @@ -26,7 +28,8 @@ export default function CalendarEventModal({ provider: conn.provider, providerName: conn.provider === 'google' ? 'Google Calendar' : conn.provider === 'apple' ? 'Apple Calendar' : - 'Outlook Calendar' + conn.provider === 'synology' ? 'Synology Calendar' : + 'Outlook Calendar' }))) .filter((cal: any) => cal.editable); // Only editable calendars @@ -284,16 +287,25 @@ export default function CalendarEventModal({ ); })()} - {/* Description */} + {/* Description - Rich text */}
-