Merge branch 'feat/chat_editing' into feat/presenton-chat
This commit is contained in:
commit
47e16cd786
5 changed files with 197 additions and 74 deletions
|
|
@ -0,0 +1,122 @@
|
|||
import { Plus, Send } from 'lucide-react'
|
||||
import React from 'react'
|
||||
const suggestions = [
|
||||
{
|
||||
id: 1,
|
||||
icon: <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" fill="none">
|
||||
<g clip-path="url(#clip0_5237_2275)">
|
||||
<path d="M10.82 1.82039L10.18 1.18039C10.1238 1.12355 10.0568 1.07842 9.98299 1.04763C9.90918 1.01683 9.83 1.00098 9.75002 1.00098C9.67005 1.00098 9.59087 1.01683 9.51706 1.04763C9.44325 1.07842 9.37628 1.12355 9.32002 1.18039L1.18002 9.32039C1.12318 9.37665 1.07806 9.44362 1.04726 9.51743C1.01647 9.59123 1.00061 9.67041 1.00061 9.75039C1.00061 9.83036 1.01647 9.90954 1.04726 9.98335C1.07806 10.0572 1.12318 10.1241 1.18002 10.1804L1.82002 10.8204C1.87593 10.8778 1.94279 10.9235 2.01664 10.9547C2.0905 10.9859 2.16985 11.0019 2.25002 11.0019C2.33019 11.0019 2.40955 10.9859 2.4834 10.9547C2.55726 10.9235 2.62411 10.8778 2.68002 10.8204L10.82 2.68039C10.8775 2.62448 10.9231 2.55762 10.9543 2.48377C10.9855 2.40991 11.0016 2.33056 11.0016 2.25039C11.0016 2.17022 10.9855 2.09087 10.9543 2.01701C10.9231 1.94316 10.8775 1.8763 10.82 1.82039Z" stroke="#7F22FE" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M7 3.5L8.5 5" stroke="#7F22FE" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M2.5 3V5" stroke="#7F22FE" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M9.5 7V9" stroke="#7F22FE" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M5 1V2" stroke="#7F22FE" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M3.5 4H1.5" stroke="#7F22FE" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M10.5 8H8.5" stroke="#7F22FE" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M5.5 1.5H4.5" stroke="#7F22FE" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_5237_2275">
|
||||
<rect width="12" height="12" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>,
|
||||
suggestion: 'Generate a full presentation from my topic',
|
||||
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
icon: <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" fill="none">
|
||||
<g clip-path="url(#clip0_5237_2289)">
|
||||
<path d="M4.96847 7.75012C4.92383 7.57709 4.83364 7.41918 4.70728 7.29282C4.58092 7.16646 4.42301 7.07626 4.24997 7.03162L1.18247 6.24062C1.13014 6.22577 1.08407 6.19425 1.05128 6.15085C1.01848 6.10744 1.00073 6.05453 1.00073 6.00012C1.00073 5.94572 1.01848 5.89281 1.05128 5.8494C1.08407 5.806 1.13014 5.77448 1.18247 5.75962L4.24997 4.96812C4.42294 4.92353 4.58082 4.83341 4.70717 4.70714C4.83353 4.58088 4.92375 4.42307 4.96847 4.25012L5.75947 1.18262C5.77417 1.13008 5.80566 1.0838 5.84913 1.05082C5.8926 1.01785 5.94566 1 6.00022 1C6.05478 1 6.10784 1.01785 6.15131 1.05082C6.19478 1.0838 6.22627 1.13008 6.24097 1.18262L7.03147 4.25012C7.07611 4.42316 7.1663 4.58107 7.29266 4.70743C7.41902 4.83379 7.57693 4.92399 7.74997 4.96862L10.8175 5.75912C10.8702 5.77367 10.9167 5.80513 10.9499 5.84866C10.983 5.8922 11.001 5.94541 11.001 6.00012C11.001 6.05484 10.983 6.10805 10.9499 6.15159C10.9167 6.19512 10.8702 6.22657 10.8175 6.24112L7.74997 7.03162C7.57693 7.07626 7.41902 7.16646 7.29266 7.29282C7.1663 7.41918 7.07611 7.57709 7.03147 7.75012L6.24047 10.8176C6.22577 10.8702 6.19428 10.9165 6.15081 10.9494C6.10734 10.9824 6.05428 11.0002 5.99972 11.0002C5.94516 11.0002 5.8921 10.9824 5.84863 10.9494C5.80516 10.9165 5.77367 10.8702 5.75897 10.8176L4.96847 7.75012Z" stroke="#155DFC" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M10 1.5V3.5" stroke="#155DFC" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M11 2.5H9" stroke="#155DFC" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M2 8.5V9.5" stroke="#155DFC" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M2.5 9H1.5" stroke="#155DFC" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_5237_2289">
|
||||
<rect width="12" height="12" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>,
|
||||
suggestion: 'Improve this slide content',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
icon: <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" fill="none">
|
||||
<path d="M6 10H10.5" stroke="#009966" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M8.18799 1.81087C8.38703 1.61182 8.657 1.5 8.93849 1.5C9.21998 1.5 9.48994 1.61182 9.68899 1.81087C9.88803 2.00991 9.99986 2.27988 9.99986 2.56137C9.99986 2.84286 9.88803 3.11282 9.68899 3.31187L3.68399 9.31737C3.56504 9.43632 3.418 9.52333 3.25649 9.57037L1.82049 9.98937C1.77746 10.0019 1.73186 10.0027 1.68844 9.99155C1.64503 9.98042 1.6054 9.95783 1.57371 9.92614C1.54202 9.89445 1.51943 9.85483 1.50831 9.81141C1.49719 9.768 1.49794 9.72239 1.51049 9.67937L1.92949 8.24337C1.9766 8.08203 2.06361 7.93518 2.18249 7.81637L8.18799 1.81087Z" stroke="#009966" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>,
|
||||
suggestion: 'Rewrite this content professionally',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
icon: <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" fill="none">
|
||||
<path d="M1.5 1.5V9.5C1.5 9.76522 1.60536 10.0196 1.79289 10.2071C1.98043 10.3946 2.23478 10.5 2.5 10.5H10.5" stroke="#E17100" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M9 8.5V4.5" stroke="#E17100" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M6.5 8.5V2.5" stroke="#E17100" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M4 8.5V7" stroke="#E17100" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>,
|
||||
suggestion: 'Add speaker notes to this slide',
|
||||
}
|
||||
]
|
||||
const quickPrompts = ['Expand each section', 'Reorder for storytelling', 'Add missing sections', 'Convert to pitch flow']
|
||||
const Chat = () => {
|
||||
return (
|
||||
<div className='w-full h-full px-4 pt-8 bg-white overflow-y-auto hide-scrollbar'>
|
||||
<h4 className='flex items-center gap-2 text-sm font-semibold text-[#101828]'><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M19.1407 9.46542C16.5537 9.21616 14.5067 7.17009 14.2577 4.58528L13.8376 0.220703L13.4175 4.58528C13.1685 7.17053 11.1215 9.2166 8.53451 9.46542L4.1731 9.88521L8.53451 10.305C11.1215 10.5543 13.1685 12.6003 13.4175 15.1852L13.8376 19.5497L14.2577 15.1852C14.5067 12.5999 16.5537 10.5538 19.1407 10.305L23.5021 9.88521L19.1407 9.46542Z" fill="#7A5AF8" />
|
||||
<path d="M9.07681 16.8431C7.62808 16.7035 6.48175 15.5577 6.34232 14.1102L6.10707 11.666L5.87183 14.1102C5.7324 15.5579 4.58606 16.7037 3.13734 16.8431L0.694946 17.0781L3.13734 17.3132C4.58606 17.4528 5.7324 18.5986 5.87183 20.0461L6.10707 22.4903L6.34232 20.0461C6.48175 18.5984 7.62808 17.4526 9.07681 17.3132L11.5192 17.0781L9.07681 16.8431Z" fill="#7A5AF8" />
|
||||
</svg> AI Assistant</h4>
|
||||
<div className='pt-8'>
|
||||
<h4 className='text-[10px] text-[#99A1AF] font-normal leading-[15px] tracking-[0.367px] mb-2 '>SUGGESTIONS</h4>
|
||||
<div className='flex flex-col gap-1.5'>
|
||||
|
||||
{
|
||||
suggestions.map((suggestion) => (
|
||||
<div key={suggestion.id} className='flex items-center gap-3 px-3 py-2 rounded-[10px] border border-[#F4F4F4] cursor-pointer'>
|
||||
{suggestion.icon}
|
||||
<p className='text-xs text-[#364153] font-normal leading-[15px] tracking-[0.367px] '>{suggestion.suggestion}</p>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className='mt-12'>
|
||||
<h4 className='text-[10px] text-[#99A1AF] font-normal leading-[15px] tracking-[0.367px] mb-2 '>QUICK PROMPTS</h4>
|
||||
<div className='flex flex-wrap gap-2'>
|
||||
{
|
||||
quickPrompts.map((prompt) => (
|
||||
<button key={prompt} className='px-2.5 py-1 rounded-[10px] border border-[#F4F4F4] cursor-pointer'>
|
||||
<p className='text-xs text-[#364153] font-normal leading-[15px] tracking-[0.367px] '>{prompt}</p>
|
||||
</button>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className='relative bg-white border border-[#F4F4F4] rounded-[8px] py-3 px-2.5 mt-[100px]'
|
||||
style={{
|
||||
boxShadow: '0 4px 14px 0 rgba(0, 0, 0, 0.04)'
|
||||
}}
|
||||
>
|
||||
<textarea
|
||||
name='chat-input'
|
||||
id='chat-input'
|
||||
className=' h-full w-full bg-transparent focus:outline-none focus:ring-0 '
|
||||
rows={4}
|
||||
placeholder='Improve your slides...'
|
||||
/>
|
||||
<button className='absolute bottom-3 h-[28px] left-3 bg-white border border-[#EDEEEF] rounded-[64px] px-3 py-1'><Plus className='w-3 h-3 text-black' /></button>
|
||||
<button className='absolute bottom-3 flex items-center gap-1.5 px-3 py-2 right-3'
|
||||
style={{
|
||||
background: 'linear-gradient(270deg, #D5CAFC 2.4%, #E3D2EB 27.88%, #F4DCD3 69.23%, #FDE4C2 100%)',
|
||||
borderRadius: '34px',
|
||||
}}
|
||||
>
|
||||
<Send className='w-3 h-3 text-[#191919]' /> Send</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Chat
|
||||
|
|
@ -416,12 +416,18 @@ const PresentationHeader = ({
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className="py-7 sticky top-0 bg-white z-50 mb-[17px] font-syne flex justify-between items-center gap-4">
|
||||
{presentationData && !isStreaming && !isEditingTitle ? (
|
||||
<ToolTip content="Rename presentation">{titleBlock}</ToolTip>
|
||||
) : (
|
||||
titleBlock
|
||||
)}
|
||||
<div className="py-[18px] px-4 sticky top-0 bg-white z-50 mb-[17px] font-syne flex justify-between items-center gap-4">
|
||||
<div className="flex items-center gap-3">
|
||||
|
||||
<img onClick={() => {
|
||||
router.push("/dashboard");
|
||||
}} src="/logo-with-bg.png" alt="" className="w-10 h-10 cursor-pointer object-contain" />
|
||||
{presentationData && !isStreaming && !isEditingTitle ? (
|
||||
<ToolTip content="Rename presentation">{titleBlock}</ToolTip>
|
||||
) : (
|
||||
titleBlock
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2.5">
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import { applyPresentationThemeToElement } from "../utils/applyPresentationTheme
|
|||
|
||||
import { usePresentationUndoRedo } from "../hooks/PresentationUndoRedo";
|
||||
import PresentationHeader from "./PresentationHeader";
|
||||
import Chat from "./Chat";
|
||||
|
||||
const PresentationPage: React.FC<PresentationPageProps> = ({
|
||||
presentation_id,
|
||||
|
|
@ -141,66 +142,63 @@ const PresentationPage: React.FC<PresentationPageProps> = ({
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="h-screen overflow-hidden font-syne ">
|
||||
<div className="h-screen overflow-hidden font-syne">
|
||||
<div
|
||||
style={{
|
||||
background: "#ffffff",
|
||||
background: "#EDEEEF",
|
||||
}}
|
||||
id="presentation-slides-wrapper"
|
||||
className="flex gap-6 relative "
|
||||
className="relative flex h-full flex-col overflow-hidden"
|
||||
>
|
||||
<div className="w-[200px]">
|
||||
<SidePanel
|
||||
selectedSlide={selectedSlide}
|
||||
onSlideClick={handleSlideClick}
|
||||
presentationId={presentation_id}
|
||||
loading={loading}
|
||||
/>
|
||||
</div>
|
||||
<div className=" w-full h-[calc(100vh-20px)] pr-[25px] overflow-y-auto">
|
||||
<PresentationHeader presentation_id={presentation_id} isPresentationSaving={isSaving} currentSlide={selectedSlide} />
|
||||
<div
|
||||
|
||||
style={{
|
||||
background: "rgba(255, 255, 255, 0.10)",
|
||||
boxShadow: "0 0 20.01px 0 rgba(122, 90, 248, 0.16) inset",
|
||||
}}
|
||||
className="p-6 rounded-[20px] font-inter flex flex-col items-center overflow-hidden justify-center border border-[#EDECEC] "
|
||||
>
|
||||
<div className="w-full max-w-[1280px] h-full">
|
||||
|
||||
{!presentationData ||
|
||||
loading ||
|
||||
!presentationData?.slides ||
|
||||
presentationData?.slides.length === 0 ? (
|
||||
<div className="relative w-full h-[calc(100vh-120px)] mx-auto">
|
||||
<div className="">
|
||||
{Array.from({ length: 2 }).map((_, index) => (
|
||||
<Skeleton
|
||||
key={index}
|
||||
className="aspect-video bg-gray-400 my-4 w-full mx-auto "
|
||||
/>
|
||||
))}
|
||||
<PresentationHeader presentation_id={presentation_id} isPresentationSaving={isSaving} currentSlide={selectedSlide} />
|
||||
<div className="flex flex-1 min-h-0 gap-6">
|
||||
<div className="w-[120px] h-full shrink-0 self-start sticky top-0">
|
||||
<SidePanel
|
||||
selectedSlide={selectedSlide}
|
||||
onSlideClick={handleSlideClick}
|
||||
presentationId={presentation_id}
|
||||
loading={loading}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full min-w-0 h-full flex-1 ">
|
||||
<div className="font-inter h-full overflow-y-auto hide-scrollbar">
|
||||
<div className="w-full max-w-[1280px] min-h-full mx-auto flex flex-col items-center pb-8">
|
||||
{!presentationData ||
|
||||
loading ||
|
||||
!presentationData?.slides ||
|
||||
presentationData?.slides.length === 0 ? (
|
||||
<div className="relative w-full h-[calc(100vh-120px)] mx-auto hide-scrollbar">
|
||||
<div className="">
|
||||
{Array.from({ length: 2 }).map((_, index) => (
|
||||
<Skeleton
|
||||
key={index}
|
||||
className="aspect-video bg-gray-400 my-4 w-full mx-auto "
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{stream && <LoadingState />}
|
||||
</div>
|
||||
{stream && <LoadingState />}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{presentationData &&
|
||||
presentationData.slides &&
|
||||
presentationData.slides.length > 0 &&
|
||||
presentationData.slides.map((slide: any, index: number) => (
|
||||
<SlideContent
|
||||
key={`${slide.type}-${index}-${slide.index}`}
|
||||
slide={slide}
|
||||
index={index}
|
||||
presentationId={presentation_id}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
) : (
|
||||
<>
|
||||
{presentationData &&
|
||||
presentationData.slides &&
|
||||
presentationData.slides.length > 0 &&
|
||||
presentationData.slides.map((slide: any, index: number) => (
|
||||
<SlideContent
|
||||
key={`${slide.type}-${index}-${slide.index}`}
|
||||
slide={slide}
|
||||
index={index}
|
||||
presentationId={presentation_id}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full max-w-[370px] h-full shrink-0 self-start sticky top-0">
|
||||
<Chat />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import { setPresentationData } from "@/store/slices/presentationGeneration";
|
|||
import { SortableSlide } from "./SortableSlide";
|
||||
import SlideScale from "../../components/PresentationRender";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { usePathname } from "next/navigation";
|
||||
import NewSlide from "./NewSlide";
|
||||
import { trackEvent, MixpanelEvent } from "@/utils/mixpanel";
|
||||
|
||||
|
|
@ -40,8 +40,6 @@ const SidePanel = ({
|
|||
|
||||
loading,
|
||||
}: SidePanelProps) => {
|
||||
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const [showNewSlideSelection, setShowNewSlideSelection] = useState(false);
|
||||
|
||||
|
|
@ -132,32 +130,26 @@ const SidePanel = ({
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="bg-[#F6F6F9] pt-8 px-4 w-[200px]">
|
||||
<div className="px-4 w-[120px] h-full">
|
||||
|
||||
<img onClick={() => {
|
||||
router.push("/dashboard");
|
||||
}} src="/logo-with-bg.png" alt="" className="w-10 h-10 cursor-pointer object-contain" />
|
||||
|
||||
<Separator orientation="horizontal" className="my-6 " />
|
||||
<div
|
||||
className={`
|
||||
relative bg-[#F6F6F9] h-full z-50 xl:z-auto
|
||||
relative h-full z-50 xl:z-auto
|
||||
transition-all duration-300 ease-in-out
|
||||
`}
|
||||
>
|
||||
<div
|
||||
|
||||
className="w-full h-[calc(100vh-120px)] hide-scrollbar overflow-hidden slide-theme "
|
||||
className="w-full h-full hide-scrollbar overflow-hidden slide-theme flex flex-col"
|
||||
>
|
||||
|
||||
<p className="text-xl font-normal font-syne pb-3.5 text-[#000000]">Slides ({presentationData?.slides?.length})</p>
|
||||
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
<div className=" overflow-y-auto w-full hide-scrollbar h-[calc(100%-140px)] space-y-3.5">
|
||||
<div className="overflow-y-auto w-full hide-scrollbar min-h-0 flex-1 space-y-3.5">
|
||||
{isStreaming ? (
|
||||
presentationData &&
|
||||
presentationData?.slides.map((slide: any, index: number) => (
|
||||
|
|
@ -203,7 +195,7 @@ const SidePanel = ({
|
|||
<button
|
||||
type="button"
|
||||
onClick={handleAddSlideClick}
|
||||
className="pt-6 gap-2 flex flex-col py-2 duration-300 items-center justify-center rounded-lg cursor-pointer mx-auto"
|
||||
className="py-4 gap-2 flex flex-col duration-300 items-center justify-center rounded-lg cursor-pointer mx-auto"
|
||||
>
|
||||
<Plus className="w-3.5 h-3.5" />
|
||||
<span className="text-[11px] font-normal text-[#000000]">Add Slide</span>
|
||||
|
|
|
|||
|
|
@ -3,14 +3,13 @@ import { CSS } from '@dnd-kit/utilities';
|
|||
import { Slide } from '../../types/slide';
|
||||
import { useRef } from 'react';
|
||||
import { V1ContentRender } from '../../components/V1ContentRender';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
interface SortableSlideProps {
|
||||
slide: Slide;
|
||||
index: number;
|
||||
selectedSlide: number;
|
||||
onSlideClick: (index: any) => void;
|
||||
}
|
||||
const SCALE = 0.125;
|
||||
const SCALE = 0.0625;
|
||||
|
||||
export function SortableSlide({ slide, index, selectedSlide, onSlideClick }: SortableSlideProps) {
|
||||
const lastClickTime = useRef(0);
|
||||
|
|
@ -56,11 +55,17 @@ export function SortableSlide({ slide, index, selectedSlide, onSlideClick }: Sor
|
|||
className={` cursor-pointer border relative p-1 rounded-[12px] transition-all duration-200 ${selectedSlide === index ? ' border-[#BDB4FE]' : 'border-[#EDEEEF]'
|
||||
}`}
|
||||
>
|
||||
<p className='absolute top-1/2 translate-y-1/2 -left-3 bg-white border border-[#EDEEEF] rounded-[40px] text-[#191919] text-[10px] font-medium px-1 z-50
|
||||
'>
|
||||
{index + 1}
|
||||
</p>
|
||||
|
||||
|
||||
<div
|
||||
className="relative"
|
||||
style={{ height: `${720 * SCALE}px`, overflow: "hidden" }}
|
||||
>
|
||||
|
||||
<div
|
||||
className="absolute top-0 left-0 pointer-events-none"
|
||||
style={{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue