Loading certificate…
diff --git a/package-lock.json b/package-lock.json index f071c58..d4683a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,8 @@ "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", + "@tiptap/react": "^3.27.0", + "@tiptap/starter-kit": "^3.27.0", "chart.js": "^4.4.0", "docx": "^9.6.1", "express": "^5.2.1", @@ -529,6 +531,34 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.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/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1019,6 +1049,435 @@ "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==", "license": "MIT" }, + "node_modules/@tiptap/core": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.27.0.tgz", + "integrity": "sha512-X53TQUq2xYn21FOC526GlVIycnDkiN9XPYO/NEsg3hXS/SUs1Q6ZLtaM8y3Ox7d+bnTW6CpWKlfyvUWp3scKEw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/pm": "3.27.0" + } + }, + "node_modules/@tiptap/extension-blockquote": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-3.27.0.tgz", + "integrity": "sha512-xQvy+n1m4oxv/QLjWdg+FrrKpkyQqU1qbLjVQTdQP/kczqvaJhjzbJFBc38K0vvzMUvBKH0x6sr3GQVl4D84wg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.27.0" + } + }, + "node_modules/@tiptap/extension-bold": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-3.27.0.tgz", + "integrity": "sha512-GbnnDVbChgp9KWv+6EBuNNYEvNVbtLPYXPm809DQPi7rvb2SI/LOzLgWgVKByQqhoh498WWVBhsg/pvQ9Va9tw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.27.0" + } + }, + "node_modules/@tiptap/extension-bubble-menu": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.27.0.tgz", + "integrity": "sha512-cy52iePYfzaUUBg3S+00KN+18KMZH+UIlE4wRTEBW/c7OA6M/nkrVkxHYfKeJ3OEbZsRYv+GiRzASchDJVVTZg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.27.0", + "@tiptap/pm": "3.27.0" + } + }, + "node_modules/@tiptap/extension-bullet-list": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-3.27.0.tgz", + "integrity": "sha512-86x8y0IzhOnRLnq5P9eTE/kgs6j383fTME+h0uNfOFfkQmzR4GLph8nkdGraormzI3+GKQUQaO4k/6bE+PSVpg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "3.27.0" + } + }, + "node_modules/@tiptap/extension-code": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-3.27.0.tgz", + "integrity": "sha512-lKJqy75GRxiNncaaBs9lutsEuVQR7AKZGFfy9Sg2nRTweWLpD6eNhSHI4N04EJvTIjxZjHAe9fGU50ZhS2ONiA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.27.0" + } + }, + "node_modules/@tiptap/extension-code-block": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-3.27.0.tgz", + "integrity": "sha512-t4cQ093ZRumHG954WKwAVYoZC6bkDrZCP9uqjHWUe1wBMzhqAWQ1OuHfaHpMnzsrdtm/svDO9rB26zBTQ0yZ/Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.27.0", + "@tiptap/pm": "3.27.0" + } + }, + "node_modules/@tiptap/extension-document": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-3.27.0.tgz", + "integrity": "sha512-xE+rUAPAA+65Usxbn5OoPVh0I0FSPz5dYprj+uo1mogPgqpcPLGVNMMoRLZ4WdiZ3I451d5+U7CUynIjD/iikw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.27.0" + } + }, + "node_modules/@tiptap/extension-dropcursor": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-3.27.0.tgz", + "integrity": "sha512-unWa4Fs2p081Dh/VC9rShby6MqVuom5FdOuLa3JKio6PufsT+Arb87yP6bdsiyUHmWb8ZXXGtxzlCTEt/bm/TA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extensions": "3.27.0" + } + }, + "node_modules/@tiptap/extension-floating-menu": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-3.27.0.tgz", + "integrity": "sha512-S9NV/3f1IFkPtIUClLUsJj98AOL3PrYMhPAH/b7SFtsvvfp2QXfL+I12ykPqOcPmj4KXw0Zc40E0QKg67M3pjw==", + "license": "MIT", + "optional": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@floating-ui/dom": "^1.0.0", + "@tiptap/core": "3.27.0", + "@tiptap/pm": "3.27.0" + } + }, + "node_modules/@tiptap/extension-gapcursor": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-3.27.0.tgz", + "integrity": "sha512-tHIUQmtebBytVpd2f5oCUMAivdN5Yj8zRDpkA5uT3x38s9OdXLjLLYZHDD7b0ANQnN0r1vkxPXi9YF6+2XWZvA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extensions": "3.27.0" + } + }, + "node_modules/@tiptap/extension-hard-break": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-3.27.0.tgz", + "integrity": "sha512-/DDTnvuW1/3qGfX5VKEFwTDHV1/ks5jMFXgMPlHXb/ASo+kb4HEohzuUYcO74Y3vtU2zENWJ2UBqsJ96TkjgOA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.27.0" + } + }, + "node_modules/@tiptap/extension-heading": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-3.27.0.tgz", + "integrity": "sha512-KDN/8RyL3/VoXpSzRtNQ3hsreBUbHgMGmpqJpRvbykQ6wJ/W421uHfZR/hCsB3RNRB0kHZJdw4hDLfP73Ff6Ig==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.27.0" + } + }, + "node_modules/@tiptap/extension-horizontal-rule": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.27.0.tgz", + "integrity": "sha512-04Xga9CqIqzKb1cqDk9AV9pTbtleqF+o8X3bb3n7HDgplYHxLjHId6RCzhfSZU6U8VZZF/RJQ1jbTebeYqGSBw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.27.0", + "@tiptap/pm": "3.27.0" + } + }, + "node_modules/@tiptap/extension-italic": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.27.0.tgz", + "integrity": "sha512-z/EomYhfaf9oxtYz4+yxaIO6cw9Sa2CVw7jsnGR0jf0CZk6B+5iTbNeJDMxpSeYg+dAPOWYiO8vWOeKM0gYPFQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.27.0" + } + }, + "node_modules/@tiptap/extension-link": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-3.27.0.tgz", + "integrity": "sha512-c0fxb1cccM/1JNk7HOoJytKJ9kcpKPeGq86FNHQkqf+Bn+IzPgLb8Q3W89lOp33NlCOx2xiSrUFMBWcPX34z8A==", + "license": "MIT", + "dependencies": { + "linkifyjs": "^4.3.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.27.0", + "@tiptap/pm": "3.27.0" + } + }, + "node_modules/@tiptap/extension-list": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.27.0.tgz", + "integrity": "sha512-27L9zP3SeKxW8DBfXcJtVnlfW6lJr6xm8C/WJ/iSlts/JHv1GwWwE7FWErmpkGeX6FwLnuaZbTMbfyyTlm4Dtg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.27.0", + "@tiptap/pm": "3.27.0" + } + }, + "node_modules/@tiptap/extension-list-item": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-3.27.0.tgz", + "integrity": "sha512-e2yb7Vjh5YV2ELEwxAu3EQr4fKJPwfAWAgbZbUuEOh8FX8R+bnJyBW9oWwxnivwPsxVPtYseWuKNEJb7wW+c8w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "3.27.0" + } + }, + "node_modules/@tiptap/extension-list-keymap": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-keymap/-/extension-list-keymap-3.27.0.tgz", + "integrity": "sha512-GV8miiDOTjSduesrO4arzC7F5T78vCy0YeVmtegtA2u5w8RBjKx3sRbS45IBF08bf4GRMRGbpGeLnqe/O0rZHQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "3.27.0" + } + }, + "node_modules/@tiptap/extension-ordered-list": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-3.27.0.tgz", + "integrity": "sha512-hiI8xcAPGOgCny8cg2md708adIdjIe2C2nNh/6TROVGfpm/tAWK13jwE1sgY+eLZTsILqpBLsEUsdF9zoS0XqQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "3.27.0" + } + }, + "node_modules/@tiptap/extension-paragraph": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-3.27.0.tgz", + "integrity": "sha512-miy096NqJJwD3ulGnOleXEIYP+pOICd5A9M3Zqy1Jpc7mw0+/YMZeR5JAXqdsYV3eq/pgYpykKlF4k+Gi84RYw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.27.0" + } + }, + "node_modules/@tiptap/extension-strike": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-3.27.0.tgz", + "integrity": "sha512-srDgYCxu2OdYGB8rtJraYzrXCvj5qRmyrSG4NObVfDYNoMuVdkbNlxA8/LNJeKKZ9H8PZlrspvmxM4F91Lvb9g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.27.0" + } + }, + "node_modules/@tiptap/extension-text": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.27.0.tgz", + "integrity": "sha512-3ZiEeDDbvhvXwqSre62yxhe0Rc7t2+BGSNoVCCUFPsNUNvBL9s9bOwwnOG4fPZNBCyF38qBnTxblDwJ92E4AGw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.27.0" + } + }, + "node_modules/@tiptap/extension-underline": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-3.27.0.tgz", + "integrity": "sha512-0w3GDF5rC0oZXYh6jBt8jPS+SQ5mtJsyDjP94A0Qjh/7tsfeDzCzZMAadE4i+9g6DI1Hce4GqYDAAfPkoj4y5Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.27.0" + } + }, + "node_modules/@tiptap/extensions": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.27.0.tgz", + "integrity": "sha512-G4ufNa/F4upDI/2YWFBCVBs4skcCeQ2jvRQiFrLJCitUFcl03evP0znAvx5JKj9+nfm4ILYpPpncEFyMq4mFEg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.27.0", + "@tiptap/pm": "3.27.0" + } + }, + "node_modules/@tiptap/pm": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.27.0.tgz", + "integrity": "sha512-cWuyaY19SoTOGwdPzhNrUuFMMILuR7mq/Ec02FO+y5jUnQiJYOy9pVx4RUIjBT24LYXrvA9YRhxL1v+KQKTz3A==", + "license": "MIT", + "dependencies": { + "prosemirror-changeset": "^2.3.0", + "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.3", + "prosemirror-model": "^1.25.7", + "prosemirror-schema-list": "^1.5.0", + "prosemirror-state": "^1.4.4", + "prosemirror-tables": "^1.8.0", + "prosemirror-transform": "^1.12.0", + "prosemirror-view": "^1.41.8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, + "node_modules/@tiptap/react": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-3.27.0.tgz", + "integrity": "sha512-C7FSa5yNPKaCll/Bvm0GmlJh2VLVJXNpKxC9I/L5BJdeVWXV3Prbt56U9FFYd8hjydFZ32GF3jUYQlVYHxnwHw==", + "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.27.0", + "@tiptap/extension-floating-menu": "^3.27.0" + }, + "peerDependencies": { + "@tiptap/core": "3.27.0", + "@tiptap/pm": "3.27.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.27.0", + "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-3.27.0.tgz", + "integrity": "sha512-AFrNuIpeyr2FJsWP5sjD0uhJfPBHdEKBByrqLu4O9h/OI8qJYKaPo9PxMXA2jQlWgDq6z0r8GPrr3PbJCMe1JA==", + "license": "MIT", + "dependencies": { + "@tiptap/core": "^3.27.0", + "@tiptap/extension-blockquote": "^3.27.0", + "@tiptap/extension-bold": "^3.27.0", + "@tiptap/extension-bullet-list": "^3.27.0", + "@tiptap/extension-code": "^3.27.0", + "@tiptap/extension-code-block": "^3.27.0", + "@tiptap/extension-document": "^3.27.0", + "@tiptap/extension-dropcursor": "^3.27.0", + "@tiptap/extension-gapcursor": "^3.27.0", + "@tiptap/extension-hard-break": "^3.27.0", + "@tiptap/extension-heading": "^3.27.0", + "@tiptap/extension-horizontal-rule": "^3.27.0", + "@tiptap/extension-italic": "^3.27.0", + "@tiptap/extension-link": "^3.27.0", + "@tiptap/extension-list": "^3.27.0", + "@tiptap/extension-list-item": "^3.27.0", + "@tiptap/extension-list-keymap": "^3.27.0", + "@tiptap/extension-ordered-list": "^3.27.0", + "@tiptap/extension-paragraph": "^3.27.0", + "@tiptap/extension-strike": "^3.27.0", + "@tiptap/extension-text": "^3.27.0", + "@tiptap/extension-underline": "^3.27.0", + "@tiptap/extensions": "^3.27.0", + "@tiptap/pm": "^3.27.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", @@ -1121,6 +1580,12 @@ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "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/@typescript-eslint/eslint-plugin": { "version": "8.57.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.1.tgz", @@ -2640,6 +3105,15 @@ "dev": true, "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-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -3604,6 +4078,12 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/linkifyjs": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.3.tgz", + "integrity": "sha512-P8aEP5U/D1/IlTY2OeYsErdwh9bGuLE30NcXtKEjgdHcahveQoQwM2yZNsioQHsWFz0P7KKudisbrzCgR0sDHg==", + "license": "MIT" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -4746,6 +5226,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/otplib": { "version": "13.4.0", "resolved": "https://registry.npmjs.org/otplib/-/otplib-13.4.0.tgz", @@ -4993,6 +5479,145 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/prosemirror-changeset": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.4.1.tgz", + "integrity": "sha512-96WBLhOaYhJ+kPhLg3uW359Tz6I/MfcrQfL4EGv4SrcqKEMC1gmoGrXHecPE8eOwTVCJ4IwgfzM8fFad25wNfw==", + "license": "MIT", + "dependencies": { + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-commands": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz", + "integrity": "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.10.2" + } + }, + "node_modules/prosemirror-dropcursor": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.2.tgz", + "integrity": "sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0", + "prosemirror-view": "^1.1.0" + } + }, + "node_modules/prosemirror-gapcursor": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.4.1.tgz", + "integrity": "sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw==", + "license": "MIT", + "dependencies": { + "prosemirror-keymap": "^1.0.0", + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-view": "^1.0.0" + } + }, + "node_modules/prosemirror-history": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.5.0.tgz", + "integrity": "sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.2.2", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.31.0", + "rope-sequence": "^1.3.0" + } + }, + "node_modules/prosemirror-inputrules": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.5.1.tgz", + "integrity": "sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-keymap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.3.tgz", + "integrity": "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "w3c-keyname": "^2.2.0" + } + }, + "node_modules/prosemirror-model": { + "version": "1.25.9", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.9.tgz", + "integrity": "sha512-pRTklkDDMMRopyoAcrr9wV/8g/RYgrLHBuJAb5hlEuYZRdm5yqmPjWId83fpBwPpSFqEdja0H7Dfd7z1X/npcA==", + "license": "MIT", + "dependencies": { + "orderedmap": "^2.0.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-transform": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.12.0.tgz", + "integrity": "sha512-GxboyN4AMIsoHNtz5uf2r2Ru551i5hWeCMD6E2Ib4Eogqoub0NflniaBPVQ4MrGE5yZ8JV9tUHg9qcZTTrcN4w==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.21.0" + } + }, + "node_modules/prosemirror-view": { + "version": "1.41.9", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.9.tgz", + "integrity": "sha512-clTunTX+eaLbr87L1V1QPheRlEQJyTlL3gXe9x3jQIk3rL0RVWxviDGz8tFaydwIVm+hKhYCyr+R/zBtWr9s6A==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.25.8", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -5477,6 +6102,12 @@ "dev": true, "license": "MIT" }, + "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/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", @@ -6169,6 +6800,15 @@ "punycode": "^2.1.0" } }, + "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", @@ -6304,6 +6944,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/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index c799d19..f6964e3 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,8 @@ "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", + "@tiptap/react": "^3.27.0", + "@tiptap/starter-kit": "^3.27.0", "chart.js": "^4.4.0", "docx": "^9.6.1", "express": "^5.2.1", diff --git a/src/App.css b/src/App.css index d083707..e4bd0bc 100644 --- a/src/App.css +++ b/src/App.css @@ -386,6 +386,17 @@ transform: translateY(-1px); } +.btn-primary:disabled { + opacity: 0.55; + cursor: not-allowed; + transform: none; +} + +.btn-secondary:disabled { + opacity: 0.55; + cursor: not-allowed; +} + .btn-secondary { display: inline-flex; align-items: center; @@ -719,6 +730,78 @@ 100% { background-position: -200% 0; } } +/* Shared shimmer mixin used by multiple skeleton components */ +.episode-embed-skeleton { + width: 100%; + height: 152px; + border-radius: 10px; + background: linear-gradient(90deg, rgba(255,255,255,0.04) 25%, rgba(255,255,255,0.08) 50%, rgba(255,255,255,0.04) 75%); + background-size: 200% 100%; + animation: skeleton-shimmer 1.4s infinite; +} + +/* Q&A skeleton cards */ +.qa-skeleton-list { + display: flex; + flex-direction: column; + gap: 1rem; + margin-top: 1.5rem; +} + +.qa-skeleton-card { + padding: 1.25rem 1.5rem; + border-radius: 10px; + border: 1px solid rgba(255,255,255,0.07); + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.qa-skeleton-line { + height: 14px; + border-radius: 6px; + width: 100%; + background: linear-gradient(90deg, rgba(255,255,255,0.04) 25%, rgba(255,255,255,0.08) 50%, rgba(255,255,255,0.04) 75%); + background-size: 200% 100%; + animation: skeleton-shimmer 1.4s infinite; +} + +.qa-skeleton-line--short { width: 38%; } +.qa-skeleton-line--medium { width: 68%; } + +/* Certificate loading skeleton */ +.cert-skeleton-wrap { + display: flex; + justify-content: center; + align-items: center; + min-height: 60vh; + padding: 2rem 1rem; +} + +.cert-skeleton-card { + width: 100%; + max-width: 520px; + padding: 3rem 2.5rem; + border-radius: 16px; + border: 1px solid rgba(255,255,255,0.1); + display: flex; + flex-direction: column; + gap: 1.25rem; + align-items: center; +} + +.cert-skeleton-line { + height: 14px; + border-radius: 6px; + background: linear-gradient(90deg, rgba(255,255,255,0.04) 25%, rgba(255,255,255,0.08) 50%, rgba(255,255,255,0.04) 75%); + background-size: 200% 100%; + animation: skeleton-shimmer 1.4s infinite; +} + +.cert-skeleton-line--short { width: 40%; } +.cert-skeleton-line--medium { width: 65%; } +.cert-skeleton-line--title { width: 80%; height: 28px; border-radius: 8px; } + .platform-buttons { display: flex; gap: 1rem; @@ -1490,6 +1573,23 @@ font-weight: 500; } +.study-lesson-announcement-close { + background: none; + border: none; + cursor: pointer; + font-size: 1.3rem; + line-height: 1; + color: #3a2800; + opacity: 0.65; + padding: 0.2rem 0.4rem; + flex-shrink: 0; + transition: opacity 150ms; +} + +.study-lesson-announcement-close:hover { + opacity: 1; +} + .section-study-course-hero { padding: 6.5rem 0 3rem; background: linear-gradient(135deg, rgba(201, 168, 76, 0.08) 0%, rgba(201, 168, 76, 0.03) 40%, transparent 100%); @@ -2483,6 +2583,261 @@ gap: 0.5rem; } +/* ── Anchor note buttons (per lesson block) ────────────────────── */ +.study-class-block--anchored { + position: relative; +} + +.note-anchor-btn { + position: absolute; + top: 1rem; + right: 1rem; + background: transparent; + border: 1px solid #3a3828; + border-radius: 6px; + color: #5a5440; + padding: 0.2rem 0.55rem; + font-size: 0.9rem; + cursor: pointer; + transition: color 150ms, border-color 150ms, background 150ms; + line-height: 1; + z-index: 1; +} + +.note-anchor-btn:hover { + color: #e0c070; + border-color: rgba(224, 192, 112, 0.45); + background: rgba(224, 192, 112, 0.06); +} + +.note-anchor-btn--has-note { + color: #c9a84c; + border-color: rgba(201, 168, 76, 0.35); +} + +.note-anchor-btn--open { + color: #e0c070; + background: rgba(224, 192, 112, 0.1); + border-color: rgba(224, 192, 112, 0.5); +} + +/* ── Sidebar anchor list ─────────────────────────────────────────── */ +.note-anchor-list { + display: flex; + flex-direction: column; + gap: 0.3rem; +} + +.note-anchor-list-item { + display: flex; + align-items: center; + justify-content: space-between; + background: transparent; + border: 1px solid #27231b; + border-radius: 8px; + padding: 0.4rem 0.7rem; + color: #7a7060; + font-size: 0.83rem; + font-family: var(--brand-font-body); + cursor: pointer; + text-align: left; + transition: color 150ms, border-color 150ms, background 150ms; + width: 100%; +} + +.note-anchor-list-item:hover { + color: #f0ead8; + border-color: #3a3828; + background: rgba(255, 255, 255, 0.03); +} + +.note-anchor-list-item--has { + color: #c9a84c; + border-color: rgba(201, 168, 76, 0.3); +} + +.note-anchor-list-item--open { + color: #e0c070; + background: rgba(224, 192, 112, 0.07); + border-color: rgba(224, 192, 112, 0.45); +} + +.note-anchor-dot { + font-size: 0.45rem; + color: #c9a84c; + flex-shrink: 0; +} + +/* ── NoteCard (WYSIWYG inline editor) ────────────────────────────── */ +.note-card { + margin-top: 1.25rem; + border-radius: 14px; + border: 1px solid rgba(224, 192, 112, 0.22); + background: #14130f; + overflow: hidden; +} + +.note-card-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.6rem 1rem; + border-bottom: 1px solid #27231b; +} + +.note-card-label { + color: #c9a84c; + font-size: 0.82rem; + font-weight: 600; + letter-spacing: 0.04em; + font-family: var(--brand-font-body); +} + +.note-card-autosave { + color: #7a9c6a; + font-size: 0.78rem; + font-family: var(--brand-font-body); +} + +.note-card-close { + background: transparent; + border: none; + color: #6a6050; + font-size: 1.1rem; + cursor: pointer; + padding: 0 0.2rem; + line-height: 1; + transition: color 150ms; +} + +.note-card-close:hover { + color: #e0c070; +} + +.note-card-toolbar { + display: flex; + align-items: center; + gap: 0.2rem; + padding: 0.35rem 0.75rem; + border-bottom: 1px solid #27231b; +} + +.note-tool { + background: transparent; + border: 1px solid transparent; + border-radius: 5px; + color: #8a8070; + padding: 0.2rem 0.5rem; + font-size: 0.88rem; + font-family: var(--brand-font-body); + cursor: pointer; + transition: color 150ms, background 150ms, border-color 150ms; + min-width: 28px; + text-align: center; + line-height: 1.4; +} + +.note-tool:hover:not(:disabled) { + color: #f0ead8; + background: #1f1d19; +} + +.note-tool:disabled { + opacity: 0.3; + cursor: default; +} + +.note-tool--active { + color: #e0c070; + background: rgba(224, 192, 112, 0.12); + border-color: rgba(224, 192, 112, 0.28); +} + +.note-tool-divider { + width: 1px; + height: 18px; + background: #2a2518; + margin: 0 0.2rem; + flex-shrink: 0; +} + +.note-card-editor { + padding: 0.85rem 1rem; + min-height: 120px; +} + +.note-card-editor .tiptap { + outline: none; + color: #f0ead8; + font-family: var(--brand-font-body); + font-size: 0.95rem; + line-height: 1.65; +} + +.note-card-editor .tiptap p { + margin: 0 0 0.5em; +} + +.note-card-editor .tiptap p:last-child { + margin-bottom: 0; +} + +.note-card-editor .tiptap ul, +.note-card-editor .tiptap ol { + padding-left: 1.4rem; + margin: 0 0 0.5em; +} + +.note-card-editor .tiptap li { + margin-bottom: 0.25rem; +} + +.note-card-editor .tiptap strong { + color: #f4edd5; + font-weight: 700; +} + +.note-card-editor .tiptap em { + color: #d4c898; +} + +.note-card-editor .tiptap .is-editor-empty:first-child::before { + content: attr(data-placeholder); + float: left; + color: #4a4535; + pointer-events: none; + height: 0; +} + +/* ── Notes page rich content display ─────────────────────────────── */ +.study-note-rich-content { + color: #d8cca8; + font-size: 0.95rem; + line-height: 1.7; +} + +.study-note-rich-content p { + margin: 0 0 0.6em; +} + +.study-note-rich-content ul, +.study-note-rich-content ol { + padding-left: 1.5rem; + margin: 0 0 0.6em; +} + +.study-note-rich-content li { + margin-bottom: 0.3rem; +} + +.study-note-rich-content strong { + color: #f0ead8; +} + +.study-note-rich-content em { + color: #d4c898; +} + .study-auth-box label { font-size: 0.8rem; letter-spacing: 0.06em; @@ -2537,6 +2892,25 @@ font-size: 0.92rem; } +.study-modal-overlay { + position: fixed; + inset: 0; + background: rgba(12, 11, 10, 0.8); + display: flex; + align-items: center; + justify-content: center; + z-index: 50; + padding: 1rem; +} + +.study-modal-box { + width: min(540px, 100%); + background: #1b1a16; + border: 1px solid #3c3a31; + border-radius: 18px; + padding: 2.5rem 2rem; +} + .study-detail-nav { display: flex; align-items: stretch; @@ -2798,6 +3172,15 @@ background: var(--brand-gold-light, #e0c070); } +@keyframes buffering-pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.4; } +} + +.episode-audio-player__play--buffering { + animation: buffering-pulse 1s ease-in-out infinite; +} + .episode-audio-player__play svg { width: 14px; height: 14px; @@ -3250,6 +3633,12 @@ margin: 0.25rem 0 0 !important; } +.study-signup-field-error { + font-size: 0.82rem; + color: #e57373; + margin: 0.2rem 0 0.1rem; +} + .study-signup-success { font-size: 1.05rem !important; color: #81c784 !important; @@ -3658,6 +4047,26 @@ color: var(--brand-gold); } + .download-clear-link { + background: none; + border: none; + cursor: pointer; + font-family: var(--brand-font-body); + font-size: 0.82rem; + font-weight: 600; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--brand-muted); + padding: 0.35rem 0.5rem; + transition: color 140ms ease; + text-decoration: underline; + text-underline-offset: 3px; + } + + .download-clear-link:hover { + color: var(--brand-warm-white); + } + .download-empty-state { text-align: center; color: var(--brand-muted); @@ -5457,7 +5866,10 @@ } .header-nav { - display: none; + max-height: 0; + opacity: 0; + pointer-events: none; + overflow: hidden; position: absolute; top: 100%; left: 0; @@ -5466,13 +5878,17 @@ flex-direction: column; align-items: stretch; gap: 0; - padding: 0.5rem 1rem 1rem; + padding: 0 1rem; background: rgba(10, 10, 8, 0.98); border-bottom: 1px solid rgba(201, 168, 76, 0.2); + transition: max-height 0.28s ease, opacity 0.22s ease, padding 0.28s ease; } .header-nav--open { - display: flex; + max-height: 500px; + opacity: 1; + pointer-events: auto; + padding: 0.5rem 1rem 1rem; } .header-nav .header-nav-link { diff --git a/src/App.tsx b/src/App.tsx index d2d131d..aa12fd3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -795,6 +795,15 @@ function DownloadLibrarySection({ content }: { content: SiteContent }) { {tag} ))} + {activeTag !== null && ( + + )} )} @@ -1497,20 +1506,24 @@ function EpisodeDetailPage({ content }: { content: SiteContent }) {
{episode.summary}
} - {resolvedEmbedUrl && ( + {(episode.embedUrl || episode.url) && (Loading certificate…
You're signed in as {loggedInAs}. Your notes are ready on every lesson.
-You're signed in as {loggedInAs}.
+Head to the study hub to enroll in a track and begin your first lesson.
+{fieldErrors.email}
} setPassword(e.target.value)} + onChange={e => { setPassword(e.target.value); if (fieldErrors.password) setFieldErrors(prev => ({ ...prev, password: undefined })) }} + onBlur={() => { + if (mode === 'signup' && password && password.length < 8) { + setFieldErrors(prev => ({ ...prev, password: 'Password must be at least 8 characters.' })) + } else { + setFieldErrors(prev => ({ ...prev, password: undefined })) + } + }} placeholder="At least 8 characters" autoComplete={mode === 'signup' ? 'new-password' : 'current-password'} onKeyDown={e => e.key === 'Enter' && submit()} /> + {fieldErrors.password &&{fieldErrors.password}
} {mode === 'signup' && (Signed in as {auth.username}
-Highlight scripture, commentary, or Greek word text, right click, and add it to your notes.
- {noteLoading ?Loading notes…
: null} + My Account + {noteLoading ? ( +Loading notes…
+ ) : ( + <> + {savedNoteCount > 0 && ( ++ {savedNoteCount} note{savedNoteCount !== 1 ? 's' : ''} saved ✓ +
+ )} +Click a section above to add notes. Right-click lesson text to append a selection.
+ > + )}{authMessage || noteMessage || progressMessage}
} @@ -1930,7 +2196,7 @@ export function ColossiansStudySectionPage({ content }: Props) { }} onMouseDown={e => e.stopPropagation()} > -Add selected text to notes:
+Add to {ANCHOR_LABELS[contextNoteMenu.anchor] ?? 'Notes'}:
Save notes for this lesson and paste selections from the page.
-{active.section ? `Lesson ${getLessonNumber(study.sections, active.section.id)}` : 'Saved Note'}
{active.section ? active.section.reference : 'Lesson reference unavailable'}
-Your Note
-{active.note}
+ {(() => { + const anchorMap = parseNoteMap(active.note) + const hasContent = Object.values(anchorMap).some(v => v?.trim()) + if (!hasContent) returnNo notes saved for this lesson yet.
+ return Object.entries(anchorMap).map(([key, html]) => ( + html?.trim() ? ( +{ANCHOR_LABELS[key] ?? key}
+ +