Migrate to Tailwind 4 + Editor Upgrade (#8465)

* wip

* fix: roadmap editor

* fix: padding

* wip

* fix: remove editor package

* wip

* fix: update pnpm lock

* Add contribution docs

* UI changes for TW4

* Update deployment workflow

---------

Co-authored-by: Arik Chakma <arikchangma@gmail.com>
This commit is contained in:
Kamran AhmedandArik Chakma authored and GitHub committed 2025-04-07 12:53:25 +01:00
1 parent 09d8c709d4
commit 64a65fa2e9
239 files changed
+1167 -1171

No files matched your search

+2 -2
View File
@@ -72,7 +72,7 @@ export function AICourse(props: AICourseProps) {
}
return (
<section className="flex flex-grow flex-col bg-gray-100">
<section className="flex grow flex-col bg-gray-100">
<div className="container mx-auto flex max-w-3xl flex-col py-24 max-sm:py-4">
<h1 className="mb-2.5 text-center text-4xl font-bold max-sm:mb-2 max-sm:text-left max-sm:text-xl">
Learn anything with AI
@@ -107,7 +107,7 @@ export function AICourse(props: AICourseProps) {
onChange={(e) => setKeyword(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="e.g., Algebra, JavaScript, Photography"
className="w-full rounded-md border border-gray-300 bg-white p-3 pl-10 text-gray-900 focus:outline-none focus:ring-1 focus:ring-gray-500 max-sm:placeholder:text-base"
className="w-full rounded-md border border-gray-300 bg-white p-3 pl-10 text-gray-900 focus:outline-hidden focus:ring-1 focus:ring-gray-500 max-sm:placeholder:text-base"
maxLength={50}
/>
</div>
@@ -216,7 +216,7 @@ export function AICourseContent(props: AICourseContentProps) {
const isViewingLesson = viewMode === 'module';
return (
<section className="flex h-screen flex-grow flex-col overflow-hidden bg-gray-50">
<section className="flex h-screen grow flex-col overflow-hidden bg-gray-50">
{modals}
<div className="border-b border-gray-200 bg-gray-100">
@@ -246,7 +246,7 @@ export function AICourseContent(props: AICourseContentProps) {
{viewMode === 'module' && (
<button
onClick={() => setIsAIChatsOpen(!isAIChatsOpen)}
className="flex items-center justify-center text-gray-400 shadow-sm transition-colors hover:bg-gray-50 hover:text-gray-900 lg:hidden"
className="flex items-center justify-center text-gray-400 shadow-xs transition-colors hover:bg-gray-50 hover:text-gray-900 lg:hidden"
>
{isAIChatsOpen ? (
<MessageCircleOffIcon size={17} strokeWidth={3} />
@@ -258,7 +258,7 @@ export function AICourseContent(props: AICourseContentProps) {
<button
onClick={() => setSidebarOpen(!sidebarOpen)}
className="flex items-center justify-center text-gray-400 shadow-sm transition-colors hover:bg-gray-50 hover:text-gray-900 lg:hidden"
className="flex items-center justify-center text-gray-400 shadow-xs transition-colors hover:bg-gray-50 hover:text-gray-900 lg:hidden"
>
{sidebarOpen ? (
<X size={17} strokeWidth={3} />
@@ -272,7 +272,7 @@ export function AICourseContent(props: AICourseContentProps) {
<header className="flex items-center justify-between border-b border-gray-200 bg-white px-6 max-lg:py-4 lg:h-[80px]">
<div className="flex items-center">
<div className="flex flex-col">
<h1 className="text-balance text-xl font-bold !leading-tight text-gray-900 max-lg:mb-0.5 max-lg:text-lg">
<h1 className="text-balance text-xl font-bold leading-tight! text-gray-900 max-lg:mb-0.5 max-lg:text-lg">
{course.title || 'Loading Course...'}
</h1>
<div className="mt-1 flex flex-row items-center gap-2 text-sm text-gray-600 max-lg:text-xs">
@@ -354,7 +354,7 @@ export function AICourseContent(props: AICourseContentProps) {
setViewMode('outline');
}}
className={cn(
'flex items-center gap-1 rounded px-2 py-1 text-xs transition-colors',
'flex items-center gap-1 rounded-sm px-2 py-1 text-xs transition-colors',
viewMode === 'outline'
? 'bg-gray-200 text-gray-900'
: 'text-gray-600 hover:bg-gray-50',
@@ -369,7 +369,7 @@ export function AICourseContent(props: AICourseContentProps) {
setViewMode('roadmap');
}}
className={cn(
'flex items-center gap-1 rounded px-2 py-1 text-xs transition-colors',
'flex items-center gap-1 rounded-sm px-2 py-1 text-xs transition-colors',
viewMode === 'roadmap'
? 'bg-gray-200 text-gray-900'
: 'text-gray-600 hover:bg-gray-50',
@@ -475,7 +475,7 @@ export function AICourseContent(props: AICourseContentProps) {
{sidebarOpen && (
<div
className="fixed inset-0 z-10 bg-gray-900 bg-opacity-50 lg:hidden"
className="fixed inset-0 z-10 bg-gray-900/50 lg:hidden"
onClick={() => setSidebarOpen(false)}
></div>
)}
@@ -278,7 +278,7 @@ export function AICourseLesson(props: AICourseLessonProps) {
defaultSize={isAIChatsOpen ? 70 : 100}
minSize={40}
id="course-text-content"
className="h-full !overflow-y-scroll bg-white"
className="h-full overflow-y-scroll! bg-white"
order={1}
>
<div className="relative mx-auto max-w-5xl">
@@ -402,7 +402,7 @@ export function AICourseLesson(props: AICourseLessonProps) {
{!isLoggedIn() && (
<div className="mt-8 flex min-h-[152px] flex-col items-center justify-center gap-3 rounded-lg border border-gray-200 p-8">
<LockIcon className="size-7 stroke-[2] text-gray-400/90" />
<LockIcon className="size-7 stroke-2 text-gray-400/90" />
<p className="text-sm text-gray-500">
Please login to generate course content
</p>
@@ -345,7 +345,7 @@ export function AICourseLessonChat(props: AICourseLessonChatProps) {
)}
<TextareaAutosize
className={cn(
'h-full min-h-[41px] grow resize-none bg-transparent px-4 py-2 focus:outline-none',
'h-full min-h-[41px] grow resize-none bg-transparent px-4 py-2 focus:outline-hidden',
isDisabled ? 'cursor-not-allowed opacity-50' : 'cursor-auto',
)}
placeholder="Ask AI anything about the lesson..."
@@ -35,7 +35,7 @@ export function AICourseOutlineView(props: AICourseOutlineViewProps) {
const aiCourseProgress = course.done || [];
return (
<div className="mx-auto rounded-xl border border-gray-200 bg-white shadow-sm lg:max-w-5xl">
<div className="mx-auto rounded-xl border border-gray-200 bg-white shadow-xs lg:max-w-5xl">
<AICourseOutlineHeader
course={course}
isLoading={isLoading}
@@ -82,7 +82,7 @@ export function AICourseOutlineView(props: AICourseOutlineViewProps) {
{!isCompleted && (
<span
className={cn(
'flex size-6 flex-shrink-0 items-center justify-center rounded-full bg-gray-200 text-sm font-medium text-gray-800 max-lg:size-5 max-lg:text-xs',
'flex size-6 shrink-0 items-center justify-center rounded-full bg-gray-200 text-sm font-medium text-gray-800 max-lg:size-5 max-lg:text-xs',
)}
>
{lessonIdx + 1}
@@ -90,7 +90,7 @@ export function AICourseOutlineView(props: AICourseOutlineViewProps) {
)}
{isCompleted && (
<CheckIcon additionalClasses="size-6 flex-shrink-0 text-green-500" />
<CheckIcon additionalClasses="size-6 shrink-0 text-green-500" />
)}
<p className="flex-1 truncate text-base text-gray-800 max-lg:text-sm">
@@ -206,7 +206,7 @@ export function AICourseRoadmapView(props: AICourseRoadmapViewProps) {
);
return (
<div className="relative mx-auto min-h-[500px] rounded-xl border border-gray-200 bg-white shadow-sm lg:max-w-5xl">
<div className="relative mx-auto min-h-[500px] rounded-xl border border-gray-200 bg-white shadow-xs lg:max-w-5xl">
<AICourseOutlineHeader
course={course}
isLoading={isLoading}
@@ -36,7 +36,7 @@ export function AICourseSearch(props: AICourseSearchProps) {
</div>
<input
type="text"
className="block w-full rounded-md border border-gray-200 bg-white py-1.5 pl-10 pr-3 leading-5 placeholder-gray-500 focus:border-gray-300 focus:outline-none focus:ring-blue-500 disabled:opacity-70 sm:text-sm"
className="block w-full rounded-md border border-gray-200 bg-white py-1.5 pl-10 pr-3 leading-5 placeholder-gray-500 focus:border-gray-300 focus:outline-hidden focus:ring-blue-500 disabled:opacity-70 sm:text-sm"
placeholder="Search your courses..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
@@ -96,7 +96,7 @@ export function AICourseSidebarModuleList(props: AICourseModuleListProps) {
)}
>
<div className="flex min-w-0 flex-1 items-center gap-2">
<div className="flex-shrink-0">
<div className="shrink-0">
<CircularProgress
percentage={percentage}
isVisible={!isModuleCompleted}
@@ -105,7 +105,7 @@ export function AICourseSidebarModuleList(props: AICourseModuleListProps) {
>
<span
className={cn(
'flex size-[21px] flex-shrink-0 items-center justify-center rounded-full bg-gray-400/70 text-xs font-semibold text-white',
'flex size-[21px] shrink-0 items-center justify-center rounded-full bg-gray-400/70 text-xs font-semibold text-white',
{
'bg-black': isActive,
'bg-green-600': isModuleCompleted,
@@ -114,7 +114,7 @@ export function AICourseSidebarModuleList(props: AICourseModuleListProps) {
>
{!isModuleCompleted && moduleIdx + 1}
{isModuleCompleted && (
<Check className="size-3 stroke-[3] text-white" />
<Check className="size-3 stroke-3 text-white" />
)}
</span>
</CircularProgress>
@@ -125,9 +125,9 @@ export function AICourseSidebarModuleList(props: AICourseModuleListProps) {
</div>
<div className="ml-auto self-center">
{expandedModules[moduleIdx] ? (
<ChevronDownIcon size={16} className="flex-shrink-0" />
<ChevronDownIcon size={16} className="shrink-0" />
) : (
<ChevronRightIcon size={16} className="flex-shrink-0" />
<ChevronRightIcon size={16} className="shrink-0" />
)}
</div>
</button>
@@ -167,7 +167,7 @@ export function AICourseSidebarModuleList(props: AICourseModuleListProps) {
{isCompleted ? (
<CheckIcon
additionalClasses={cn(
'size-[18px] relative bg-white rounded-full top-[2px] flex-shrink-0 text-green-600',
'size-[18px] relative bg-white rounded-full top-[2px] shrink-0 text-green-600',
{
'text-black':
activeModuleIndex === moduleIdx &&
@@ -178,7 +178,7 @@ export function AICourseSidebarModuleList(props: AICourseModuleListProps) {
) : (
<span
className={cn(
'flex size-[18px] flex-shrink-0 items-center justify-center rounded-full bg-gray-400/70 text-xs font-semibold text-white',
'flex size-[18px] shrink-0 items-center justify-center rounded-full bg-gray-400/70 text-xs font-semibold text-white',
{
'bg-black':
activeModuleIndex === moduleIdx &&
@@ -15,7 +15,7 @@ export function AIRoadmapViewSwitch(props: AIRoadmapViewSwitchProps) {
return (
<div
className={cn(
'grid shrink-0 grid-cols-2 gap-0.5 rounded-md border border-gray-300 bg-white p-0.5 shadow-sm',
'grid shrink-0 grid-cols-2 gap-0.5 rounded-md border border-gray-300 bg-white p-0.5 shadow-xs',
)}
>
<SwitchButton
@@ -61,7 +61,7 @@ export function SwitchButton(props: SwitchButtonProps) {
return (
<button
className={cn(
'flex items-center justify-center gap-1.5 rounded text-sm hover:bg-gray-100 disabled:cursor-not-allowed',
'flex items-center justify-center gap-1.5 rounded-sm text-sm hover:bg-gray-100 disabled:cursor-not-allowed',
isActive && 'bg-gray-100 text-gray-800',
variant === 'text' ? 'px-2 py-1.5' : 'p-[5px]',
)}
@@ -28,7 +28,7 @@ export function CircularProgress(props: CircularProgressProps) {
const strokeDashoffset = circumference - (percentage / 100) * circumference;
return (
<div className="relative flex h-[28px] w-[28px] flex-shrink-0 items-center justify-center">
<div className="relative flex h-[28px] w-[28px] shrink-0 items-center justify-center">
{isVisible && !isLoading && (
<svg className="absolute h-full w-full -rotate-90">
<circle
@@ -19,7 +19,7 @@ function Question(props: QuestionProps) {
</label>
<textarea
placeholder={placeholder}
className="min-h-[80px] w-full resize-none px-4 py-3 text-gray-700 placeholder:text-gray-400 focus:outline-none"
className="min-h-[80px] w-full resize-none px-4 py-3 text-gray-700 placeholder:text-gray-400 focus:outline-hidden"
value={value}
onChange={(e) => onChange(e.target.value)}
autoFocus={autoFocus}
@@ -56,7 +56,7 @@ export function FineTuneCourse(props: FineTuneCourseProps) {
<div className="flex flex-col overflow-hidden rounded-lg border border-gray-200 transition-all">
<label
className={cn(
'group flex cursor-pointer select-none flex-row items-center gap-2.5 px-4 py-3 text-left text-gray-500 transition-colors hover:bg-gray-100 focus:outline-none',
'group flex cursor-pointer select-none flex-row items-center gap-2.5 px-4 py-3 text-left text-gray-500 transition-colors hover:bg-gray-100 focus:outline-hidden',
hasFineTuneData && 'bg-gray-100',
)}
>
+2 -2
View File
@@ -26,13 +26,13 @@ const ResizableHandle = ({
}) => (
<ResizablePrimitive.PanelResizeHandle
className={cn(
'relative flex w-px items-center justify-center bg-gray-200 after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90',
'relative flex w-px items-center justify-center bg-gray-200 after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90',
className,
)}
{...props}
>
{withHandle && (
<div className="z-10 flex h-[30px] w-3 items-center justify-center rounded-sm bg-gray-200 text-black hover:bg-gray-300">
<div className="z-10 flex h-[30px] w-3 items-center justify-center rounded-xs bg-gray-200 text-black hover:bg-gray-300">
<GripVertical className="size-5" />
</div>
)}
@@ -120,7 +120,7 @@ export function TestMyKnowledgeAction(props: TestMyKnowledgeActionProps) {
<div className="flex items-center gap-2">
<button
className={cn(
'group flex flex-shrink-0 items-center gap-2 rounded-xl border border-gray-200 bg-white px-6 py-3 text-sm font-medium text-gray-700 transition-all hover:border-black hover:bg-gray-50 hover:text-gray-900',
'group flex shrink-0 items-center gap-2 rounded-xl border border-gray-200 bg-white px-6 py-3 text-sm font-medium text-gray-700 transition-all hover:border-black hover:bg-gray-50 hover:text-gray-900',
{
'bg-gray-100 text-gray-900': isKnowledgeTestOpen,
},
@@ -333,7 +333,7 @@ export function QuizItem(props: QuizItemProps) {
},
)}
>
<div className="flex items-center gap-4 rounded-t-xl border-b bg-gradient-to-r from-gray-50 to-white p-5">
<div className="flex items-center gap-4 rounded-t-xl border-b bg-linear-to-r from-gray-50 to-white p-5">
<span className="text-sm font-medium text-gray-700">
Question {counter} of {totalQuestions}
</span>
@@ -406,14 +406,14 @@ export function QuizItem(props: QuizItemProps) {
<div className="flex gap-2">
<button
className="group flex h-10 items-center justify-center gap-1 rounded-lg border border-gray-200 p-2 pr-4 text-sm text-black transition-all hover:border-black hover:bg-black hover:text-white focus:outline-none max-sm:pr-2"
className="group flex h-10 items-center justify-center gap-1 rounded-lg border border-gray-200 p-2 pr-4 text-sm text-black transition-all hover:border-black hover:bg-black hover:text-white focus:outline-hidden max-sm:pr-2"
onClick={onPrevious}
>
<ChevronLeftIcon className="size-5 shrink-0 transition-transform group-hover:-translate-x-0.5" />
<span className="max-sm:hidden">Previous</span>
</button>
<button
className="group flex h-10 items-center justify-center gap-1 rounded-lg border border-gray-200 p-2 pl-4 text-sm text-black transition-all hover:border-black hover:bg-black hover:text-white focus:outline-none max-sm:pl-2"
className="group flex h-10 items-center justify-center gap-1 rounded-lg border border-gray-200 p-2 pl-4 text-sm text-black transition-all hover:border-black hover:bg-black hover:text-white focus:outline-hidden max-sm:pl-2"
onClick={onNext}
>
<span className="max-sm:hidden">Next</span>
@@ -424,7 +424,7 @@ export function QuizItem(props: QuizItemProps) {
</div>
{isLoading && (
<div className="absolute bg-white right-3 top-3 flex h-8 items-center justify-center gap-1 rounded-lg border border-gray-200 p-2 text-sm text-black hover:bg-black hover:text-white focus:outline-none">
<div className="absolute bg-white right-3 top-3 flex h-8 items-center justify-center gap-1 rounded-lg border border-gray-200 p-2 text-sm text-black hover:bg-black hover:text-white focus:outline-hidden">
<Loader2Icon className="size-5 animate-spin text-gray-400" />
</div>
)}