feat: add ai course generator (#8322)

* Course landing page

* Add ai course page

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip: error handling

* wip

* wip

* wip

* wip: ai course progress

* wip

* wip

* wip

* feat: code highlighting

* feat: usage limit

* feat: follow up message

* Update UI

* wip

* Add course content

* wip: autogrow textarea & examples

* Update types

* Update

* fix: add highlight to the AI chat

* UI changes

* Refactor

* Update

* Improve outline style

* Improve spacing

* Improve spacing

* UI changes for sidebar

* Update UI for sidebar

* Improve course UI

* Mark done, undone

* Add toggle lesson done/undone

* Update forward backward UI

* wip

* Minor ui change

* Responsiveness of sidebar

* wip

* wip

* wip: billing page

* wip

* Update UI

* fix: hide upgrade if paid user

* feat: token usage

* feat: list ai courses

* fix: limit for followup

* Course content responsiveness

* Make course content responsive

* Responsiveness

* Outline button

* Responsiveness of course content

* Responsiveness of course content

* Add course upgrade button

* Update design for upgrade

* Improve logic for upgrade and limits button

* Limits and errors

* Add lesson count

* Add course card

* Improve UI for course generator

* Update course functionality

* Refactor AI course generation

* Responsiveness of screen

* Improve

* Add responsiveness

* Improve empty billing page design

* Add empty billing screen

* Update UI for billing page

* Update UI for billing page

* Update UI for billing page

* Update billing page design

* Update

* Remove sidebar

* Update

---------

Co-authored-by: Arik Chakma <arikchangma@gmail.com>
This commit is contained in:
Kamran Ahmed
2025-03-12 13:17:38 +00:00
committed by GitHub
co-authored by Arik Chakma
parent faf43f7905
commit cb64894e49
39 changed files with 3906 additions and 25 deletions
+204
View File
@@ -80,6 +80,9 @@ importers:
luxon:
specifier: ^3.5.0
version: 3.5.0
markdown-it-async:
specifier: ^2.0.0
version: 2.0.0
nanoid:
specifier: ^5.0.7
version: 5.0.9
@@ -110,6 +113,9 @@ importers:
react-dom:
specifier: ^18.3.1
version: 18.3.1(react@18.3.1)
react-textarea-autosize:
specifier: ^8.5.7
version: 8.5.7(@types/react@18.3.18)(react@18.3.1)
react-tooltip:
specifier: ^5.28.0
version: 5.28.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -137,6 +143,9 @@ importers:
sharp:
specifier: ^0.33.5
version: 0.33.5
shiki:
specifier: ^3.1.0
version: 3.1.0
slugify:
specifier: ^1.6.6
version: 1.6.6
@@ -396,6 +405,10 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/runtime@7.26.9':
resolution: {integrity: sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==}
engines: {node: '>=6.9.0'}
'@babel/template@7.25.9':
resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==}
engines: {node: '>=6.9.0'}
@@ -1179,24 +1192,45 @@ packages:
'@shikijs/core@1.29.1':
resolution: {integrity: sha512-Mo1gGGkuOYjDu5H8YwzmOuly9vNr8KDVkqj9xiKhhhFS8jisAtDSEWB9hzqRHLVQgFdA310e8XRJcW4tYhRB2A==}
'@shikijs/core@3.1.0':
resolution: {integrity: sha512-1ppAOyg3F18N8Ge9DmJjGqRVswihN33rOgPovR6gUHW17Hw1L4RlRhnmVQcsacSHh0A8IO1FIgNbtTxUFwodmg==}
'@shikijs/engine-javascript@1.29.1':
resolution: {integrity: sha512-Hpi8k9x77rCQ7F/7zxIOUruNkNidMyBnP5qAGbLFqg4kRrg1HZhkB8btib5EXbQWTtLb5gBHOdBwshk20njD7Q==}
'@shikijs/engine-javascript@3.1.0':
resolution: {integrity: sha512-/LwkhW17jYi7uPcdaaSQQDNW+xgrHXarkrxYPoC6WPzH2xW5mFMw12doHXJBqxmYvtcTbaatcv2MkH9+3PU1FA==}
'@shikijs/engine-oniguruma@1.29.1':
resolution: {integrity: sha512-gSt2WhLNgEeLstcweQOSp+C+MhOpTsgdNXRqr3zP6M+BUBZ8Md9OU2BYwUYsALBxHza7hwaIWtFHjQ/aOOychw==}
'@shikijs/engine-oniguruma@3.1.0':
resolution: {integrity: sha512-reRgy8VzDPdiDocuGDD60Rk/jLxgcgy+6H4n6jYLeN2Yw5ikasRjQQx8ERXtDM35yg2v/d6KolDBcK8hYYhcmw==}
'@shikijs/langs@1.29.1':
resolution: {integrity: sha512-iERn4HlyuT044/FgrvLOaZgKVKf3PozjKjyV/RZ5GnlyYEAZFcgwHGkYboeBv2IybQG1KVS/e7VGgiAU4JY2Gw==}
'@shikijs/langs@3.1.0':
resolution: {integrity: sha512-hAM//sExPXAXG3ZDWjrmV6Vlw4zlWFOcT1ZXNhFRBwPP27scZu/ZIdZ+TdTgy06zSvyF4KIjnF8j6+ScKGu6ww==}
'@shikijs/themes@1.29.1':
resolution: {integrity: sha512-lb11zf72Vc9uxkl+aec2oW1HVTHJ2LtgZgumb4Rr6By3y/96VmlU44bkxEb8WBWH3RUtbqAJEN0jljD9cF7H7g==}
'@shikijs/themes@3.1.0':
resolution: {integrity: sha512-A4MJmy9+ydLNbNCtkmdTp8a+ON+MMXoUe1KTkELkyu0+pHGOcbouhNuobhZoK59cL4cOST6CCz1x+kUdkp9UZA==}
'@shikijs/types@1.29.1':
resolution: {integrity: sha512-aBqAuhYRp5vSir3Pc9+QPu9WESBOjUo03ao0IHLC4TyTioSsp/SkbAZSrIH4ghYYC1T1KTEpRSBa83bas4RnPA==}
'@shikijs/types@3.1.0':
resolution: {integrity: sha512-F8e7Fy4ihtcNpJG572BZZC1ErYrBrzJ5Cbc9Zi3REgWry43gIvjJ9lFAoUnuy7Bvy4IFz7grUSxL5edfrrjFEA==}
'@shikijs/vscode-textmate@10.0.1':
resolution: {integrity: sha512-fTIQwLF+Qhuws31iw7Ncl1R3HUDtGwIipiJ9iU+UsDUwMhegFcQKQHd51nZjb7CArq0MvON8rbgCGQYWHUKAdg==}
'@shikijs/vscode-textmate@10.0.2':
resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
'@shuding/opentype.js@1.4.0-beta.0':
resolution: {integrity: sha512-3NgmNyH3l/Hv6EvsWJbsvpcpUba6R8IREQ83nH83cyakCw7uM1arZKNfHwv1Wz6jgqrF/j4x5ELvR6PnK9nTcA==}
engines: {node: '>= 8.0.0'}
@@ -2090,6 +2124,9 @@ packages:
hast-util-to-html@9.0.4:
resolution: {integrity: sha512-wxQzXtdbhiwGAUKrnQJXlOPmHnEehzphwkK7aluUPQ+lEc1xefC8pblMgpp2w5ldBTEfveRIrADcrhGIWrlTDA==}
hast-util-to-html@9.0.5:
resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==}
hast-util-to-parse5@8.0.0:
resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==}
@@ -2343,6 +2380,9 @@ packages:
resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
engines: {node: '>=8'}
markdown-it-async@2.0.0:
resolution: {integrity: sha512-jBthmQR5MwXR9Y8Y0teRoZAenaKQMdjuTfpbNARqMBSRPvyzyXCVduHZHakyyhL3ugIacCobXJrO07t277sIjw==}
markdown-it-task-lists@2.1.1:
resolution: {integrity: sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA==}
@@ -2603,6 +2643,9 @@ packages:
oniguruma-to-es@2.3.0:
resolution: {integrity: sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g==}
oniguruma-to-es@3.1.1:
resolution: {integrity: sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==}
openai@4.80.1:
resolution: {integrity: sha512-+6+bbXFwbIE88foZsBEt36bPkgZPdyFN82clAXG61gnHb2gXdZApDyRrcAHqEtpYICywpqaNo57kOm9dtnb7Cw==}
hasBin: true
@@ -2847,6 +2890,9 @@ packages:
property-information@6.5.0:
resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==}
property-information@7.0.0:
resolution: {integrity: sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg==}
prosemirror-changeset@2.2.1:
resolution: {integrity: sha512-J7msc6wbxB4ekDFj+n9gTW/jav/p53kdlivvuppHsrZXCaQdVgRghoZbSS3kwrRyAstRVQ4/+u5k7YfLgkkQvQ==}
@@ -2942,6 +2988,12 @@ packages:
resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==}
engines: {node: '>=0.10.0'}
react-textarea-autosize@8.5.7:
resolution: {integrity: sha512-2MqJ3p0Jh69yt9ktFIaZmORHXw4c4bxSIhCeWiFwmJ9EYKgLmuNII3e9c9b2UO+ijl4StnpZdqpxNIhTdHvqtQ==}
engines: {node: '>=10'}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-tooltip@5.28.0:
resolution: {integrity: sha512-R5cO3JPPXk6FRbBHMO0rI9nkUG/JKfalBSQfZedZYzmqaZQgq7GLzF8vcCWx6IhUCKg0yPqJhXIzmIO5ff15xg==}
peerDependencies:
@@ -2965,15 +3017,24 @@ packages:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
regenerator-runtime@0.14.1:
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
regex-recursion@5.1.1:
resolution: {integrity: sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w==}
regex-recursion@6.0.2:
resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==}
regex-utilities@2.3.0:
resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==}
regex@5.1.1:
resolution: {integrity: sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw==}
regex@6.0.1:
resolution: {integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==}
rehype-external-links@3.0.0:
resolution: {integrity: sha512-yp+e5N9V3C6bwBeAC4n796kc86M4gJCdlVhiMTxIrJG5UHDMh+PJANf9heqORJbt1nrCbDwIlAZKjANIaVBbvw==}
@@ -3110,6 +3171,9 @@ packages:
shiki@1.29.1:
resolution: {integrity: sha512-TghWKV9pJTd/N+IgAIVJtr0qZkB7FfFCUrrEJc0aRmZupo3D1OCVRknQWVRVA7AX/M0Ld7QfoAruPzr3CnUJuw==}
shiki@3.1.0:
resolution: {integrity: sha512-LdTNyWQlC5zdCaHdcp1zPA1OVA2ivb+KjGOOnGcy02tGaF5ja+dGibWFH7Ar8YlngUgK/scDqworK18Ys9cbYA==}
signal-exit@4.1.0:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
@@ -3348,6 +3412,33 @@ packages:
peerDependencies:
browserslist: '>= 4.21.0'
use-composed-ref@1.4.0:
resolution: {integrity: sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==}
peerDependencies:
'@types/react': '*'
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
peerDependenciesMeta:
'@types/react':
optional: true
use-isomorphic-layout-effect@1.2.0:
resolution: {integrity: sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w==}
peerDependencies:
'@types/react': '*'
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
peerDependenciesMeta:
'@types/react':
optional: true
use-latest@1.3.0:
resolution: {integrity: sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==}
peerDependencies:
'@types/react': '*'
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
peerDependenciesMeta:
'@types/react':
optional: true
use-sync-external-store@1.4.0:
resolution: {integrity: sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==}
peerDependencies:
@@ -3743,6 +3834,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@babel/runtime@7.26.9':
dependencies:
regenerator-runtime: 0.14.1
'@babel/template@7.25.9':
dependencies:
'@babel/code-frame': 7.26.2
@@ -4336,32 +4431,65 @@ snapshots:
'@types/hast': 3.0.4
hast-util-to-html: 9.0.4
'@shikijs/core@3.1.0':
dependencies:
'@shikijs/types': 3.1.0
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
hast-util-to-html: 9.0.5
'@shikijs/engine-javascript@1.29.1':
dependencies:
'@shikijs/types': 1.29.1
'@shikijs/vscode-textmate': 10.0.1
oniguruma-to-es: 2.3.0
'@shikijs/engine-javascript@3.1.0':
dependencies:
'@shikijs/types': 3.1.0
'@shikijs/vscode-textmate': 10.0.2
oniguruma-to-es: 3.1.1
'@shikijs/engine-oniguruma@1.29.1':
dependencies:
'@shikijs/types': 1.29.1
'@shikijs/vscode-textmate': 10.0.1
'@shikijs/engine-oniguruma@3.1.0':
dependencies:
'@shikijs/types': 3.1.0
'@shikijs/vscode-textmate': 10.0.2
'@shikijs/langs@1.29.1':
dependencies:
'@shikijs/types': 1.29.1
'@shikijs/langs@3.1.0':
dependencies:
'@shikijs/types': 3.1.0
'@shikijs/themes@1.29.1':
dependencies:
'@shikijs/types': 1.29.1
'@shikijs/themes@3.1.0':
dependencies:
'@shikijs/types': 3.1.0
'@shikijs/types@1.29.1':
dependencies:
'@shikijs/vscode-textmate': 10.0.1
'@types/hast': 3.0.4
'@shikijs/types@3.1.0':
dependencies:
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
'@shikijs/vscode-textmate@10.0.1': {}
'@shikijs/vscode-textmate@10.0.2': {}
'@shuding/opentype.js@1.4.0-beta.0':
dependencies:
fflate: 0.7.4
@@ -5381,6 +5509,20 @@ snapshots:
stringify-entities: 4.0.4
zwitch: 2.0.4
hast-util-to-html@9.0.5:
dependencies:
'@types/hast': 3.0.4
'@types/unist': 3.0.3
ccount: 2.0.1
comma-separated-tokens: 2.0.3
hast-util-whitespace: 3.0.0
html-void-elements: 3.0.0
mdast-util-to-hast: 13.2.0
property-information: 7.0.0
space-separated-tokens: 2.0.2
stringify-entities: 4.0.4
zwitch: 2.0.4
hast-util-to-parse5@8.0.0:
dependencies:
'@types/hast': 3.0.4
@@ -5611,6 +5753,11 @@ snapshots:
dependencies:
semver: 6.3.1
markdown-it-async@2.0.0:
dependencies:
'@types/markdown-it': 14.1.2
markdown-it: 14.1.0
markdown-it-task-lists@2.1.1: {}
markdown-it@14.1.0:
@@ -6027,6 +6174,12 @@ snapshots:
regex: 5.1.1
regex-recursion: 5.1.1
oniguruma-to-es@3.1.1:
dependencies:
emoji-regex-xs: 1.0.0
regex: 6.0.1
regex-recursion: 6.0.2
openai@4.80.1(zod@3.24.1):
dependencies:
'@types/node': 18.19.74
@@ -6214,6 +6367,8 @@ snapshots:
property-information@6.5.0: {}
property-information@7.0.0: {}
prosemirror-changeset@2.2.1:
dependencies:
prosemirror-transform: 1.10.2
@@ -6348,6 +6503,15 @@ snapshots:
react-refresh@0.14.2: {}
react-textarea-autosize@8.5.7(@types/react@18.3.18)(react@18.3.1):
dependencies:
'@babel/runtime': 7.26.9
react: 18.3.1
use-composed-ref: 1.4.0(@types/react@18.3.18)(react@18.3.1)
use-latest: 1.3.0(@types/react@18.3.18)(react@18.3.1)
transitivePeerDependencies:
- '@types/react'
react-tooltip@5.28.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@floating-ui/dom': 1.6.13
@@ -6381,17 +6545,27 @@ snapshots:
dependencies:
picomatch: 2.3.1
regenerator-runtime@0.14.1: {}
regex-recursion@5.1.1:
dependencies:
regex: 5.1.1
regex-utilities: 2.3.0
regex-recursion@6.0.2:
dependencies:
regex-utilities: 2.3.0
regex-utilities@2.3.0: {}
regex@5.1.1:
dependencies:
regex-utilities: 2.3.0
regex@6.0.1:
dependencies:
regex-utilities: 2.3.0
rehype-external-links@3.0.0:
dependencies:
'@types/hast': 3.0.4
@@ -6655,6 +6829,17 @@ snapshots:
'@shikijs/vscode-textmate': 10.0.1
'@types/hast': 3.0.4
shiki@3.1.0:
dependencies:
'@shikijs/core': 3.1.0
'@shikijs/engine-javascript': 3.1.0
'@shikijs/engine-oniguruma': 3.1.0
'@shikijs/langs': 3.1.0
'@shikijs/themes': 3.1.0
'@shikijs/types': 3.1.0
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
signal-exit@4.1.0: {}
simple-swizzle@0.2.2:
@@ -6912,6 +7097,25 @@ snapshots:
escalade: 3.2.0
picocolors: 1.1.1
use-composed-ref@1.4.0(@types/react@18.3.18)(react@18.3.1):
dependencies:
react: 18.3.1
optionalDependencies:
'@types/react': 18.3.18
use-isomorphic-layout-effect@1.2.0(@types/react@18.3.18)(react@18.3.1):
dependencies:
react: 18.3.1
optionalDependencies:
'@types/react': 18.3.18
use-latest@1.3.0(@types/react@18.3.18)(react@18.3.1):
dependencies:
react: 18.3.1
use-isomorphic-layout-effect: 1.2.0(@types/react@18.3.18)(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.18
use-sync-external-store@1.4.0(react@18.3.1):
dependencies:
react: 18.3.1