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:
1 parent
09d8c709d4
commit
64a65fa2e9
239 files changed
+1167
-1171
No files matched your search
@@ -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',
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
Reference in new issue
Block a user