From 0abacad609f6517a2946c4a2a9e62159445f9d16 Mon Sep 17 00:00:00 2001 From: euncherry Date: Fri, 20 Dec 2024 05:07:09 +0900 Subject: [PATCH 01/27] =?UTF-8?q?fix:=20userStore=20=EC=B0=B8=EC=A1=B0=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/storeUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/storeUtils.js b/src/utils/storeUtils.js index 0e33b54..6979de4 100644 --- a/src/utils/storeUtils.js +++ b/src/utils/storeUtils.js @@ -8,7 +8,7 @@ import { export const resetAllStores = () => { // 각 store의 초기화 함수 실행 useAuthStore.getState().clearAuth(); - useUserStore.getState().clearMemberInfo(); + useUserStore.getState().clearUserInfo(); useAppModalStore.getState().reset(); useRepoStore.getState().reset(); }; From 02bbbe6653e5d4732baf7c9b2956c546c32339b0 Mon Sep 17 00:00:00 2001 From: euncherry Date: Fri, 20 Dec 2024 18:45:08 +0900 Subject: [PATCH 02/27] =?UTF-8?q?guide=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/chatbot.js | 32 ++ .../RepoContent/RepoBoard/RepoBoard.jsx | 6 +- .../organisms/RepoContent/RepoContent.jsx | 18 +- .../organisms/RepoContent/RepositoryGuide.jsx | 384 ++++++++++++++++++ .../organisms/RepoDetailContent/Chatting.jsx | 7 +- .../organisms/RepoDetailContent/Document.jsx | 30 -- .../organisms/RepoDetailContent/ReadMe.jsx | 5 +- src/constants/guides/readmeGuide.js | 146 +++++++ src/hooks/RepoDetailContent/useChatbot.js | 0 src/hooks/RepoDetailContent/useReadme.js | 7 + src/hooks/useRepoManagement.js | 24 +- src/store/repoStore.js | 9 + 12 files changed, 614 insertions(+), 54 deletions(-) create mode 100644 src/api/chatbot.js create mode 100644 src/components/organisms/RepoContent/RepositoryGuide.jsx create mode 100644 src/constants/guides/readmeGuide.js create mode 100644 src/hooks/RepoDetailContent/useChatbot.js diff --git a/src/api/chatbot.js b/src/api/chatbot.js new file mode 100644 index 0000000..3d869ad --- /dev/null +++ b/src/api/chatbot.js @@ -0,0 +1,32 @@ +// src/api/download.js +import api from './axios.js'; + +export const chatBotAPI = { + // chatbot 대화 내역 불러오기 API + getChatbotHistory: async (registeredRepoId) => { + try { + console.log('🏃 chatbot 대화 내역 불러오기 조회 API,,,', registeredRepoId); + const response = await api.get(`/api/chatbot/logs/${registeredRepoId}`); + console.log('chatbot 대화 내역 불러오기 : ', response.data); + return response.data; + } catch (error) { + console.error('Failed to postDownloadReadme :', error); + throw new Error( + error.response?.data?.message || '❌ Failed to post Download Readme', + ); + } + }, + + //AI 분석 DOCS 결과 목록 불러오기 + postDownloadDocs: async (registeredRepoId) => { + try { + console.log('🏃 AI 분석 DOCS 결과 불러오기 조회 API ,,,'); + const response = await api.post(`/api/download/docs/${registeredRepoId}`); + console.log('AI 분석 DOCS 결과 : ', response.data); + return response.data; + } catch (error) { + console.error('Failed to postDownloadDocs :', error); + throw new Error(error.response?.data?.message || '❌ Failed to post Download Docs'); + } + }, +}; diff --git a/src/components/organisms/RepoContent/RepoBoard/RepoBoard.jsx b/src/components/organisms/RepoContent/RepoBoard/RepoBoard.jsx index 10eae42..b1beda0 100644 --- a/src/components/organisms/RepoContent/RepoBoard/RepoBoard.jsx +++ b/src/components/organisms/RepoContent/RepoBoard/RepoBoard.jsx @@ -30,7 +30,7 @@ const ProjectBoard = ({ const transformedData = dataSource.map(item => ({ Action: "delete", Branch: item.branchName, - Repository: item.repositoryName, + repositoryName: item.repositoryName, Status: "Code Imported", registeredRepoId: item.registeredRepoId, projectsStatus: [ @@ -151,9 +151,9 @@ const ProjectBoard = ({ - {project.Repository.charAt(0)} + {project.repositoryName.charAt(0)} - {project.Repository} + {project.repositoryName} diff --git a/src/components/organisms/RepoContent/RepoContent.jsx b/src/components/organisms/RepoContent/RepoContent.jsx index 727694a..1fef233 100644 --- a/src/components/organisms/RepoContent/RepoContent.jsx +++ b/src/components/organisms/RepoContent/RepoContent.jsx @@ -6,7 +6,7 @@ import bgShapeFour from "../../../assets/images/bg-shape-four.png"; import { Search, Plus } from 'lucide-react'; import { useRepoStore, useRegisteredRepoStore, useUserStore } from '../../../store/store.js' import { useRepoManagement } from '../../../hooks/useRepoManagement' - +import RepositoryGuide from './RepositoryGuide.jsx'; import BoardTest from './RepoBoard/RepoBoard.jsx'; import Board from './Board.jsx'; @@ -137,19 +137,9 @@ const RepoContent = () => { - - - - - setSearchValue(e.target.value)} - placeholder="Search repositories..." - plane={true} - /> - - - + props.isExpanded ? 'auto' : '0'}; + overflow: hidden; + display : grid; + grid-template-columns: 1fr 1fr; +`; + +const StepsList = styled.div` + padding: 1rem; + display: flex; + flex-direction: column; + gap: 1rem; +`; + +const StepItem = styled.div` + padding: 1rem; + border-radius: 0.5rem; + border: 1px solid ${props => props.isActive ? 'rgba(139, 92, 246, 0.5)' : 'rgb(31, 41, 55)'}; + background-color: ${props => props.isActive ? 'rgba(31, 41, 55, 0.5)' : 'rgba(17, 24, 39, 0.5)'}; + transition: all 0.3s; + cursor: pointer; + + &:hover { + background-color: rgba(31, 41, 55, 0.3); + } +`; + +const StepContent = styled.div` + display: flex; + align-items: flex-start; + gap: 0.75rem; +`; + +const StepIconWrapper = styled.div` + margin-top: 0.25rem; +`; + +const StepInfo = styled.div` + flex: 1; +`; + +const StepTitle = styled.h3` + color: white; + font-weight: 500; + margin-bottom: 0.25rem; +`; + +const StepDescription = styled.p` + color: rgb(156, 163, 175); + font-size: 0.875rem; + margin-bottom: 0.5rem; +`; + +const StepHint = styled.div` + color: rgba(139, 92, 246, 0.8); + font-size: 0.75rem; + background-color: rgba(139, 92, 246, 0.1); + padding: 0.375rem 0.75rem; + border-radius: 9999px; + display: inline-block; +`; + +const RepositoryGuide = ({ onCardClick }) => { + // const { testRepo } = useRepoStore(); + const transformedData = { + Action: "delete", + Branch: 'main', + repositoryName: 'testRepo?.repositoryName', + Status: "Code Imported", + registeredRepoId: 'guide', + projectsStatus: [ + { + id: 1, + title: "readme", + name: "README 만들기", + status: "completed", + }, + + { + id: 2, + title: "docs", + name: "Docs 만들기", + status: 'completed', + }, + { + id: 3, + title: "chatting", + name: "Chatting 만들기", + status: 'completed', + }, + ], + completed: true, + createdAt: '2024-12-18', + // completed: testRepo?.readmeComplete && testRepo?.chatbotComplete && testRepo?.docsComplete, + // createdAt: testRepo?.createdAt + } + + const [isExpanded, setIsExpanded] = useState(false); + const [currentStep, setCurrentStep] = useState(0); + + const guideSteps = [ + { + title: "README 문서 작성", + status: "progress", + description: "AI가 Repository 분석 후 최적화된 README를 생성합니다", + hint: "Repository를 선택하고 'Create README' 버튼을 클릭하세요" + }, + { + title: "실시간 채팅 지원", + status: "waiting", + description: "코드에 대해 질문하고 실시간으로 답변을 받아보세요", + hint: "채팅 탭을 선택하고 질문을 입력하세요" + }, + { + title: "문서 자동화", + status: "waiting", + description: "코드 문서화를 자동으로 생성하고 관리합니다", + hint: "문서 탭에서 자동 생성된 문서를 확인하세요" + } + ]; + + const getStatusGuideIcon = (status) => { + switch (status) { + case 'completed': + return ; + case 'progress': + return ; + default: + return ; + } + }; + + + const getStatusIcon = (status) => { + switch (status) { + case 'readme': + return FileText; + case 'chatting': + return MessageCircle; + case 'docs': + return Book; + default: + return FileText; + } + }; + + const getStatusDisplay = (status) => { + switch (status) { + case 'waiting': + return { + icon: Clock, + text: "대기", + color: "#9ca3af", + bgColor: "#1f2937", + bdColor: "rgba(156, 163, 175, 0.1)" + }; + case 'in-progress': + return { + icon: Loader2, + text: "진행중", + color: "rgb(192, 132, 252)", + bgColor: "rgba(126, 34, 206, 0.3)", + bdColor: "rgba(192, 132, 252,0.1)" + }; + case 'completed': + return { + icon: CheckCircle2, + text: "완료", + color: "rgb(74, 222, 128)", + bgColor: "rgba(21, 128, 61, 0.3)", + bdColor: "rgba(74, 222, 128,0.1)" + }; + default: + return null; + } + }; + + const formatDate = (dateString) => { + const date = new Date(dateString); + return date.toLocaleString('en-US', { month: 'short', year: 'numeric' }); + }; + + const [expandedStates, setExpandedStates] = useState(true) + const handleToggleExpand = (e) => { + e.stopPropagation(); + setExpandedStates((prev) => !prev); + }; + + + return ( + + + setIsExpanded(!isExpanded)}> + + + How to Use Guide + + {isExpanded ? + : + + } + + + + + {guideSteps.map((step, index) => ( + setCurrentStep(index)} + > + + + {getStatusGuideIcon(step.status)} + + + {step.title} + {step.description} + 💡 {step.hint} + + + + ))} + +
+ onCardClick('guide')} + > + + + + + + + + {transformedData.repositoryName.charAt(0)} + + {transformedData.repositoryName} + + + + + {expandedStates ? + + : + + } + {transformedData.Branch} + + + + + {formatDate(transformedData.createdAt)} + + + + handleToggleExpand(e, transformedData.registeredRepoId)}> + + + {expandedStates ? ( + + ) : ( + + )} + + + + { + transformedData.completed && expandedStates ? ( + + + + + Dododocs complete ! + + + + ) : ( + transformedData.projectsStatus.map((statusList) => { + const status = getStatusDisplay(statusList.status); + const TimeLineIcon = getStatusIcon(statusList.title); + const StatusIcon = status.icon; + return ( + + + + + {statusList.name} + + + + + + + + {status.text} + + + + ); + }) + )} + + + + +
+
+ +
+
+ ); +}; + +export default RepositoryGuide; \ No newline at end of file diff --git a/src/components/organisms/RepoDetailContent/Chatting.jsx b/src/components/organisms/RepoDetailContent/Chatting.jsx index 1abeced..cf90fbe 100644 --- a/src/components/organisms/RepoDetailContent/Chatting.jsx +++ b/src/components/organisms/RepoDetailContent/Chatting.jsx @@ -200,7 +200,7 @@ const WelcomeMessage = styled.div` transform: translate(-50%, -10%); // 초기 위치를 최종 위치와 동일하게 설정 backdrop-filter: blur(10px); animation: ${WelcomeFadeIn} 0.6s ease-out; - max-width: 90%; + max-width: calc(100% - 36px - 36px - 24px); width : 60%; box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1); `; @@ -295,8 +295,8 @@ const MessageBubble = styled.div` `; const Avatar = styled.div` - width: 36px; - height: 36px; + min-width: 36px; + min-height: 36px; border-radius: 50%; display: flex; align-items: center; @@ -436,6 +436,7 @@ const ChatbotUI = () => { useEffect(() => { scrollToBottom(); + console.log(messages) }, [messages]); const handleSend = async () => { diff --git a/src/components/organisms/RepoDetailContent/Document.jsx b/src/components/organisms/RepoDetailContent/Document.jsx index 4c64ccf..4650a76 100644 --- a/src/components/organisms/RepoDetailContent/Document.jsx +++ b/src/components/organisms/RepoDetailContent/Document.jsx @@ -187,36 +187,6 @@ const Document = () => { console.log('docsData', docsData) }, [docsData]) - const [selectedDoc, setSelectedDoc] = useState('AuthController'); // 선택된 문서 상태 추가 - const [content, setContent] = useState(documentText[0]); // 현재 표시될 내용 - - // 문서 메뉴 데이터 정의 - // const controllerDocs = [ - // { id: 'AuthController', fileName: 'AuthController.md', content: documentText[0] }, - // { id: 'KeywordController', fileName: 'KeywordController.md', content: documentText[1] }, - // { id: 'LiveInformationController', fileName: 'LiveInformationController.md', content: documentText[2] }, - // { id: 'MemberController', fileName: 'MemberController.md', content: documentText[0] }, - // { id: 'MemberLiveInformationController', fileName: 'MemberLiveInformationController.md', content: documentText[0] }, - // { id: 'PlannerController', fileName: 'PlannerController.md', content: documentText[0] }, - // { id: 'RecommendController', fileName: 'RecommendController.md', content: documentText[0] }, - // { id: 'TripController', fileName: 'TripController.md', content: documentText[0] }, - // { id: 'TripScheduleController', fileName: 'TripScheduleController.md', content: documentText[0] } - // ]; - - - - - // 문서 선택 핸들러 - // const handleDocSelect = (fileName) => { - // setSelectedDoc(fileName); - // const selectedContent = controllerDocs.find(doc => doc.fileName === fileName)?.content; - // if (selectedContent) { - // setContent(selectedContent); - // } - - // }; - - // 선택된 파일명과 내용을 관리하는 상태 const [selectedFileName, setSelectedFileName] = useState(''); const [currentContent, setCurrentContent] = useState(''); diff --git a/src/components/organisms/RepoDetailContent/ReadMe.jsx b/src/components/organisms/RepoDetailContent/ReadMe.jsx index b1dc303..75fe663 100644 --- a/src/components/organisms/RepoDetailContent/ReadMe.jsx +++ b/src/components/organisms/RepoDetailContent/ReadMe.jsx @@ -9,7 +9,7 @@ import { useLocation, useNavigate } from 'react-router-dom'; import { Button } from "../../index.js" import { markdownText } from './markdownText.jsx'; import { useReadme } from '../../../hooks/RepoDetailContent/useReadme.js'; -import { useRegisteredRepoStore } from "../../../store/store.js" +import { useRegisteredRepoStore, useAppModalStore } from "../../../store/store.js" const Container = styled.div` display: flex; @@ -330,6 +330,7 @@ const NavItem = ({ const ReadMe = () => { const { activeRepositoryId } = useRegisteredRepoStore(); + const { AppRepo } = useAppModalStore(); const location = useLocation(); const navigate = useNavigate(); const mainContentRef = useRef(null); @@ -443,7 +444,7 @@ const ReadMe = () => { const initialSections = parseSections(markdownText); setSectionsReadMe(initialSections); - console.log("😂😂😂😂😂 READ ME : ", markdownText) + console.log("😂😂😂😂😂 READ ME : ", [markdownText]) }, [markdownText]); diff --git a/src/constants/guides/readmeGuide.js b/src/constants/guides/readmeGuide.js new file mode 100644 index 0000000..8276b1b --- /dev/null +++ b/src/constants/guides/readmeGuide.js @@ -0,0 +1,146 @@ +export const readmeGuideText = ` +# Project Name +moheng + +## Table of Contents +[ 📝 Overview](#📝-overview) +[ 📁 Project Structure](#📁-project-structure) +[ 🚀 Getting Started](#🚀-getting-started) +[ 💡 Motivation](#💡-motivation) +[ 🎬 Demo](#🎬-demo) +[ 🌐 Deployment](#🌐-deployment) +[ 🤝 Contributing](#🤝-contributing) +[ ❓ Troubleshooting & FAQ](#❓-troubleshooting-&-faq) +[ 📈 Performance](#📈-performance) + +## 📝 Overview +이 프로젝트는 여행 계획 및 추천 시스템을 구축하기 위한 것입니다. 사용자는 여행지에 대한 정보를 입력하고, 시스템은 사용자의 선호도에 따라 맞춤형 여행지를 추천합니다. + +### Main Purpose +- 사용자가 선호하는 여행지를 기반으로 맞춤형 추천을 제공하는 시스템입니다. +- 여행 계획을 보다 효율적으로 관리하고, 사용자 경험을 향상시키는 것을 목표로 합니다. +- 여행을 좋아하는 사용자들을 주요 대상으로 합니다. + +### Key Features +- 사용자 맞춤형 여행지 추천 +- 여행 일정 관리 +- 소셜 로그인 기능 +- 다양한 여행지 정보 제공 + +### Core Technology Stack +- Frontend: React, Vite +- Backend: Spring Boot +- Database: PostgreSQL +- Others: Python, FastAPI (AI 모델 서빙) + +## 📁 Project Structure +\`\`\` +moheng +├── 📁 ai +│ ├── 📁 model_serving +│ │ ├── 📁 application +│ │ ├── 📁 domain +│ │ ├── 📁 infra +│ │ ├── 📁 interface +│ │ └── 📁 presentation +│ └── ... +├── 📁 frontend +│ ├── 📁 src +│ │ ├── 📁 api +│ │ └── ... +│ └── ... +├── 📁 moheng +│ ├── 📁 auth +│ ├── 📁 member +│ ├── 📁 planner +│ ├── 📁 trip +│ ├── 📁 recommendtrip +│ └── ... +└── ... +\`\`\` + +## 🚀 Getting Started + +### Prerequisites +- 지원 운영체제 + * Windows, macOS, Linux +- 필수 소프트웨어 + * Node.js, Python, Java + * Node.js: 18.3.1 이상, Python: 3.11.x, Java: 22 이상 + * 패키지 관리자: npm, poetry +- 시스템 종속성 + * Docker + +### Installation +- Dockerfile을 사용하여 설치할 수 있습니다. +- 모든 설치 방법은 Dockerfile에 포함되어 있습니다. + +\`\`\`bash +# 리포지토리 클론 +git clone https://github.com/kakao-25/moheng.git +cd moheng-develop + +# 필요한 패키지 설치 +# Frontend 설치 +cd frontend +npm install + +# Backend 설치 +cd ../backend +./gradlew clean build + +# AI 설치 +cd ../ai +pip install poetry +poetry install + +# Docker 빌드 및 실행 +cd .. +docker-compose up --build +\`\`\` + +### Usage +\`\`\`bash +# 실행 방법 +# Frontend 실행 +cd frontend +npm start + +# Backend 실행 +cd backend +./gradlew bootRun + +# AI 서비스 실행 +cd ai +python3 main.py +\`\`\` + +## 💡 Motivation +이 프로젝트는 여행을 좋아하는 사람들을 위해 더 나은 여행 경험을 제공하고자 하는 열망에서 시작되었습니다. 사용자가 선호하는 여행지를 기반으로 맞춤형 추천을 제공함으로써 여행 계획을 보다 쉽게 할 수 있도록 돕고자 합니다. + +## 🎬 Demo +![Demo Video or Screenshot](path/to/demo.mp4) + +## 🌐 Deployment +- AWS, Heroku와 같은 클라우드 플랫폼을 통해 배포할 수 있습니다. +- 배포 단계는 다음과 같습니다: + 1. 서버 환경 설정 + 2. 데이터베이스 설정 + 3. 애플리케이션 빌드 및 배포 + +## 🤝 Contributing +- 기여 방법: 이 레포지토리에 기여하고 싶다면, 먼저 이슈를 생성하거나 PR을 제출하세요. +- 코딩 표준: Java, Python, JavaScript의 코딩 표준을 준수합니다. +- PR 프로세스: 변경 사항을 설명하는 명확한 커밋 메시지를 포함하여 PR을 제출하세요. +- 행동 강령: 모든 기여자는 상호 존중과 협력을 기반으로 행동해야 합니다. + +## ❓ Troubleshooting & FAQ +- **문제**: 서버가 시작되지 않음 + - **해결**: 필요한 의존성이 모두 설치되었는지 확인하세요. +- **문제**: 데이터베이스 연결 오류 + - **해결**: 데이터베이스 설정을 확인하고, 올바른 자격 증명을 사용하고 있는지 확인하세요. + +## 📈 Performance +- 성능 벤치마크 및 최적화 기술을 통해 시스템의 응답 속도를 개선할 수 있습니다. +- 확장성 고려 사항: 시스템이 증가하는 사용자 수를 처리할 수 있도록 설계되었습니다. +`; diff --git a/src/hooks/RepoDetailContent/useChatbot.js b/src/hooks/RepoDetailContent/useChatbot.js new file mode 100644 index 0000000..e69de29 diff --git a/src/hooks/RepoDetailContent/useReadme.js b/src/hooks/RepoDetailContent/useReadme.js index 5ffdb4d..1a96ee2 100644 --- a/src/hooks/RepoDetailContent/useReadme.js +++ b/src/hooks/RepoDetailContent/useReadme.js @@ -1,6 +1,7 @@ // src/hooks/RepoDetailContent/useReadme.js import { useQuery } from '@tanstack/react-query'; import { downloadAPI } from '../../api/index.js'; +import { readmeGuideText } from '../../constants/guides/readmeGuide.js'; export const useReadme = (registeredRepoId) => { console.log('useReadme hook called with ID:', registeredRepoId); @@ -11,6 +12,12 @@ export const useReadme = (registeredRepoId) => { if (!registeredRepoId) { throw new Error('Repository ID is required'); } + if (registeredRepoId === 'guide') { + console.log('Returning readme guide'); + return { + contents: readmeGuideText, + }; + } const response = await downloadAPI.postDownloadReadme(registeredRepoId); console.log('API Response:', response); return response; diff --git a/src/hooks/useRepoManagement.js b/src/hooks/useRepoManagement.js index f11a571..d003c30 100644 --- a/src/hooks/useRepoManagement.js +++ b/src/hooks/useRepoManagement.js @@ -83,8 +83,11 @@ export const useRepoManagement = () => { throw error; } }, - refetchInterval: (queryInfo) => { - const data = queryInfo?.state?.data; + refetchInterval: (query) => { + console.log('실제 fetch 횟수:', query.state.fetchCount); + console.log('refetch 횟수:', query); + + const data = query?.state?.data; if (!data) return false; // if (!pollingStartTime) { // setPollingStartTime(Date.now()); @@ -220,6 +223,23 @@ export const useRepoManagement = () => { app: { open: useCallback( (registeredRepoId) => { + console.log(registeredRepoId, registeredRepoId); + if (registeredRepoId === 'guide') { + setAppRepo({ + registeredRepoId: 'guide', + repositoryName: 'guide', + branchName: 'main', + createdAt: '2024-12-18', + readmeComplete: true, + chatbotComplete: true, + docsComplete: true, + }); + + setOpenAppModal(); + + navigate(`/repositories/${`guide`}`); + return; + } const repo = registeredRepositoriesList.find( (repo) => repo.registeredRepoId === registeredRepoId, ); diff --git a/src/store/repoStore.js b/src/store/repoStore.js index 35b7a64..9f5fd43 100644 --- a/src/store/repoStore.js +++ b/src/store/repoStore.js @@ -27,6 +27,15 @@ const initialState = { Action: 'Delete', }, ], + testRepo: { + registeredRepoId: 1, + repositoryName: 'testREpo', + branchName: 'main', + createdAt: '2024-12-18', + readmeComplete: true, + chatbotComplete: true, + docsComplete: true, + }, }; /** From f2bb8a2447df876645ff40859adb644f47873a78 Mon Sep 17 00:00:00 2001 From: euncherry Date: Sat, 21 Dec 2024 02:09:00 +0900 Subject: [PATCH 03/27] =?UTF-8?q?=EC=B1=84=ED=8C=85=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/chatbot.js | 2 +- src/api/index.js | 1 + .../organisms/RepoDetailContent/Chatting.jsx | 233 +++++++++++++----- src/hooks/RepoDetailContent/useChatbot.js | 138 +++++++++++ 4 files changed, 313 insertions(+), 61 deletions(-) diff --git a/src/api/chatbot.js b/src/api/chatbot.js index 3d869ad..5ba79bb 100644 --- a/src/api/chatbot.js +++ b/src/api/chatbot.js @@ -1,7 +1,7 @@ // src/api/download.js import api from './axios.js'; -export const chatBotAPI = { +export const chatbotAPI = { // chatbot 대화 내역 불러오기 API getChatbotHistory: async (registeredRepoId) => { try { diff --git a/src/api/index.js b/src/api/index.js index 5438013..00c6267 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -4,3 +4,4 @@ export { registerAPI } from './register'; export { memberAPI } from './member'; export { docsAPI } from './docs'; export { downloadAPI } from './download'; +export { chatbotAPI } from './chatbot'; diff --git a/src/components/organisms/RepoDetailContent/Chatting.jsx b/src/components/organisms/RepoDetailContent/Chatting.jsx index cf90fbe..4161228 100644 --- a/src/components/organisms/RepoDetailContent/Chatting.jsx +++ b/src/components/organisms/RepoDetailContent/Chatting.jsx @@ -3,8 +3,11 @@ import styled, { css, keyframes } from 'styled-components'; import { Send, User, Bot, Sparkles, RefreshCw, EllipsisVertical } from 'lucide-react'; import { Typo } from "../../index.js"; import useClickAway from '../../../hooks/useClickAway.js'; +import { useChatbot } from '../../../hooks/RepoDetailContent/useChatbot.js'; import { TypingMarkdownRenderer, MarkdownRenderer } from '../../index.js'; import { chattingText } from "./chattingText.jsx"; +import { useRegisteredRepoStore, useAuthStore } from "../../../store/store.js" + // Animations const fadeIn = keyframes` @@ -409,100 +412,207 @@ const LoadingDots = styled.div` `; const ChatbotUI = () => { - const [isMenuOpen, setIsMenuOpen] = useState(false); - const menuRef = useRef(null); // 메뉴 ref 생성 + const { activeRepositoryId } = useRegisteredRepoStore(); + const token = useAuthStore(state => state.token); + + const { + chatHistory, + isLoading: isChatLoading, + isError, + error, + sendMessage, + resetChat + } = useChatbot(activeRepositoryId); + + const [streamingResponse, setStreamingResponse] = useState(''); const [messages, setMessages] = useState([]); const [inputText, setInputText] = useState(''); - const [isLoading, setIsLoading] = useState(false); const messagesEndRef = useRef(null); + const [isMenuOpen, setIsMenuOpen] = useState(false); + const menuRef = useRef(null); // 메뉴 ref 생성 + const [isTest, setIsTest] = useState(0); - const [completedMessages, setCompletedMessages] = useState(new Set()); - const handleMessageComplete = (messageId) => { - setCompletedMessages(prev => new Set([...prev, messageId])); - }; - useClickAway(menuRef, () => { - if (isMenuOpen) { - setIsMenuOpen(false); - } - }); + // SSE 설정 + useEffect(() => { + if (!activeRepositoryId) return; - const scrollToBottom = () => { - messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); - }; + const eventSource = new EventSource( + `${process.env.REACT_APP_API_BASE_URL}api/chatbot/stream-and-save/${activeRepositoryId}`, + ); + + eventSource.onmessage = (event) => { + try { + const data = JSON.parse(event.data); + setStreamingResponse(prevResponse => prevResponse + data.answer); + } catch (error) { + console.error('스트리밍 데이터 파싱 에러:', error); + } + }; + + + eventSource.onerror = (error) => { + console.error('SSE 연결 에러:', error); + eventSource.close(); + }; + + return () => { + eventSource.close(); + }; + }, [activeRepositoryId]);// token이 변경될 때마다 재연결 + + + + // 채팅 히스토리 동기화 useEffect(() => { - scrollToBottom(); - console.log(messages) - }, [messages]); + if (chatHistory) { + const formattedMessages = chatHistory.flatMap(chat => [ + { id: `q-${chat.id}`, text: chat.question, isUser: true }, + { id: `a-${chat.id}`, text: chat.text, isUser: false } + ]); + setMessages(formattedMessages); + } + }, [chatHistory]); + // 스트리밍 응답이 업데이트될 때마다 메시지 업데이트 + useEffect(() => { + if (streamingResponse) { + console.log("스트리밍 응답이 업데이트될 때마다 메시지 업데이트", streamingResponse) + const lastMessage = messages[messages.length - 1]; + if (!lastMessage || lastMessage.isUser) { + // 새로운 AI 응답 메시지 추가 + setMessages(prev => [...prev, { + id: `stream-${Date.now()}`, + text: streamingResponse, + isUser: false + }]); + } else { + // 기존 AI 응답 메시지 업데이트 + setMessages(prev => { + const newMessages = [...prev]; + newMessages[newMessages.length - 1] = { + ...newMessages[newMessages.length - 1], + text: streamingResponse + }; + return newMessages; + }); + } + } + }, [streamingResponse]); + + // 메시지 전송 핸들러 const handleSend = async () => { - if (!inputText.trim() || isLoading) return; + if (!inputText.trim() || isChatLoading || !token || !activeRepositoryId) return; - // Add user message + const userMessageId = Date.now(); setMessages(prev => [...prev, { - id: Date.now(), + id: `q-${userMessageId}`, text: inputText, isUser: true }]); - setInputText(''); - setIsLoading(true); - try { - // Simulate API call - await new Promise(resolve => setTimeout(resolve, 2000)); - if (isTest === 0) { - setMessages(prev => [...prev, { - id: Date.now(), - text: chattingText[0], - isUser: false - }]); - setIsTest((state) => state + 1); - } - if (isTest === 1) { - setMessages(prev => [...prev, { - id: Date.now(), - text: chattingText[1], - isUser: false - }]); - setIsTest((state) => state + 1); - } + // API 요청 설정 + const myHeaders = new Headers(); + myHeaders.append("Content-Type", "application/json"); + myHeaders.append("Authorization", `Bearer ${token}`); + + const requestBody = { + question: inputText + }; + + const requestOptions = { + method: "POST", + headers: myHeaders, + body: JSON.stringify(requestBody), + redirect: "follow" + }; - if (isTest > 1) { - setMessages(prev => [...prev, { - id: Date.now(), - text: chattingText[2], - isUser: false - }]); - setIsTest((state) => state + 1); - } + setInputText(''); + setStreamingResponse(''); // 스트리밍 응답 초기화 + + try { + await fetch(`${process.env.REACT_APP_API_BASE_URL}api/chatbot/stream-and-save/${activeRepositoryId}`, + requestOptions); + + + // const response = await fetch(`${process.env.REACT_APP_API_BASE_URL}api/chatbot/stream-and-save`, requestOptions); + + // if (!response.ok) { + // throw new Error(`HTTP error! status: ${response.status}`); + // } + + // // SSE 연결 설정 + // const eventSource = new EventSource( + // `${process.env.REACT_APP_API_BASE_URL}/api/chatbot/stream?token=${token}&repositoryId=${activeRepositoryId}` + // ); + + // eventSource.onmessage = (event) => { + // try { + // const data = JSON.parse(event.data); + // setStreamingResponse(prevResponse => prevResponse + data.answer); + // } catch (error) { + // console.error('스트리밍 데이터 파싱 에러:', error); + // } + // }; + + // eventSource.onerror = (error) => { + // console.error('SSE 연결 에러:', error); + // eventSource.close(); + // }; } catch (error) { setMessages(prev => [...prev, { - id: Date.now(), + id: `error-${userMessageId}`, text: "죄송합니다. 오류가 발생했습니다. 다시 시도해 주세요.", isUser: false, isError: true }]); - } finally { - setIsLoading(false); + console.error('채팅 요청 에러:', error); } + + + + + }; - const handleReset = () => { + // 대화 초기화 핸들러 + const handleReset = async () => { + await resetChat(); setMessages([]); + setStreamingResponse(''); + + }; + + + + useClickAway(menuRef, () => { + if (isMenuOpen) { + setIsMenuOpen(false); + } + }); + + const scrollToBottom = () => { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }; + useEffect(() => { + scrollToBottom(); + console.log(messages) + }, [messages]); + + + const handleKeyPress = (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSend(); } }; - console.log(messages.length === 0) return ( @@ -522,8 +632,11 @@ const ChatbotUI = () => { + + + {/* Welcome message */} { : null } - + {/* Chat messages */} {messages.map(message => ( @@ -591,7 +704,7 @@ const ChatbotUI = () => { ))} - {isLoading && ( + {isChatLoading && ( @@ -616,8 +729,8 @@ const ChatbotUI = () => { placeholder="코드에 대해 궁금한 점을 물어보세요..." value={inputText} onChange={(e) => setInputText(e.target.value)} - onKeyPress={handleKeyPress} - disabled={isLoading} + onKeyPress={(e) => e.key === 'Enter' && !e.shiftKey && handleSend()} + disabled={isChatLoading} /> { diff --git a/src/hooks/RepoDetailContent/useChatbot.js b/src/hooks/RepoDetailContent/useChatbot.js index e69de29..204cb7c 100644 --- a/src/hooks/RepoDetailContent/useChatbot.js +++ b/src/hooks/RepoDetailContent/useChatbot.js @@ -0,0 +1,138 @@ +// src/hooks/useChatbot.js +import { useState, useCallback } from 'react'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { chatbotAPI } from '../../api/index.js'; + +export const useChatbot = (registeredRepoId) => { + const queryClient = useQueryClient(); + const [isLoading, setIsLoading] = useState(false); + + // 채팅 히스토리 조회 + const { + data: chatHistory, + isLoading: isHistoryLoading, + isError, + error, + refetch: refetchHistory, + } = useQuery({ + queryKey: ['chatHistory', registeredRepoId], + queryFn: () => chatbotAPI.getChatbotHistory(registeredRepoId), + enabled: !!registeredRepoId, + select: (data) => { + return data.findChatLogResponses.map((chat, index) => ({ + id: index, + text: chat.answer, + isUser: false, + question: chat.question, + })); + }, + }); + + // 메시지 전송 핸들러 + const handleSendMessage = useCallback( + //TODO if guide 경우 + // const handleSend = async () => { + // if (!inputText.trim() || isLoading) return; + + // // Add user message + // setMessages(prev => [...prev, { + // id: Date.now(), + // text: inputText, + // isUser: true + // }]); + // setInputText(''); + // setIsLoading(true); + + // try { + // // Simulate API call + // await new Promise(resolve => setTimeout(resolve, 2000)); + // if (isTest === 0) { + // setMessages(prev => [...prev, { + // id: Date.now(), + // text: chattingText[0], + // isUser: false + // }]); + // setIsTest((state) => state + 1); + // } + // if (isTest === 1) { + // setMessages(prev => [...prev, { + // id: Date.now(), + // text: chattingText[1], + // isUser: false + // }]); + // setIsTest((state) => state + 1); + // } + + // if (isTest > 1) { + // setMessages(prev => [...prev, { + // id: Date.now(), + // text: chattingText[2], + // isUser: false + // }]); + // setIsTest((state) => state + 1); + // } + + // } catch (error) { + // setMessages(prev => [...prev, { + // id: Date.now(), + // text: "죄송합니다. 오류가 발생했습니다. 다시 시도해 주세요.", + // isUser: false, + // isError: true + // }]); + // } finally { + // setIsLoading(false); + // } + // }; + + async (message) => { + if (!message.trim()) return null; + + setIsLoading(true); + try { + // API 호출 및 응답 처리 + const response = await chatbotAPI.postChatMessage(registeredRepoId, { + question: message, + }); + + // 채팅 히스토리 갱신 + await refetchHistory(); + + return { + success: true, + response: response.answer, + }; + } catch (error) { + console.error('Failed to send message:', error); + return { + success: false, + error: error.message, + }; + } finally { + setIsLoading(false); + } + }, + [registeredRepoId, refetchHistory], + ); + + // 채팅 초기화 + const resetChat = useCallback(async () => { + try { + // 채팅 초기화 API 호출 (필요한 경우) + // await chatBotAPI.resetChat(registeredRepoId); + + // 캐시 무효화 + await queryClient.invalidateQueries(['chatHistory', registeredRepoId]); + } catch (error) { + console.error('Failed to reset chat:', error); + } + }, [queryClient, registeredRepoId]); + + return { + chatHistory, + isLoading: isLoading || isHistoryLoading, + isError, + error, + sendMessage: handleSendMessage, + resetChat, + }; +}; From d60299def4a0af22d5d4d28b801ea90130b464df Mon Sep 17 00:00:00 2001 From: euncherry Date: Sun, 22 Dec 2024 16:56:08 +0900 Subject: [PATCH 04/27] =?UTF-8?q?fix:=20=ED=8C=8C=EC=9D=BC=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EB=8C=80=EC=86=8C=EB=AC=B8=EC=9E=90=20=EC=B6=A9?= =?UTF-8?q?=EB=8F=8C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/index.js | 1 + .../ChattingContent/ChattingContent.jsx | 366 +++++++++--------- .../organisms/ChattingContent/ForKko.jsx | 149 ++++++- .../ChattingContent/ReadMeLanding.styles.js | 206 ++++++++++ .../ChattingLanding/ChattingLanding.jsx | 193 +++++++++ .../ChattingLanding/ChattingLanding.styles.js | 206 ++++++++++ .../Landing/DocsLanding/DocsLanding.jsx | 193 +++++++++ .../Landing/DocsLanding/DocsLanding.styles.js | 206 ++++++++++ .../organisms/Landing/LandingContent.jsx | 20 + .../Landing/ReadmeLanding/ReadmeLanding.jsx | 193 +++++++++ .../ReadmeLanding/ReadmeLanding.styles.js | 206 ++++++++++ .../organisms/OAuthCallback/OAuthCallback.jsx | 2 +- .../organisms/RepoDetailContent/Chatting.jsx | 106 ++--- .../organisms/RepoDetailContent/Document.jsx | 7 +- src/constants/guides/AuthController.js | 174 +++++++++ src/constants/guides/KeywordController.js | 201 ++++++++++ .../guides/LiveInformationController.js | 0 src/constants/guides/MemberController.js | 208 ++++++++++ .../guides/MemberLiveInformationController.js | 153 ++++++++ src/constants/guides/PlannerController.js | 210 ++++++++++ .../guides/RecommendTripController.js | 152 ++++++++ src/constants/guides/TripController.js | 192 +++++++++ src/constants/guides/docsGuide.js | 55 +++ src/hooks/RepoDetailContent/useDocument.js | 6 + src/hooks/useRepoManagement.js | 17 +- src/layouts/Header/header.jsx | 6 +- src/pages/LandingPage/index.jsx | 16 + src/router/LandingRouter.jsx | 9 + src/router/index.jsx | 8 + 29 files changed, 3174 insertions(+), 287 deletions(-) create mode 100644 src/components/organisms/ChattingContent/ReadMeLanding.styles.js create mode 100644 src/components/organisms/Landing/ChattingLanding/ChattingLanding.jsx create mode 100644 src/components/organisms/Landing/ChattingLanding/ChattingLanding.styles.js create mode 100644 src/components/organisms/Landing/DocsLanding/DocsLanding.jsx create mode 100644 src/components/organisms/Landing/DocsLanding/DocsLanding.styles.js create mode 100644 src/components/organisms/Landing/LandingContent.jsx create mode 100644 src/components/organisms/Landing/ReadmeLanding/ReadmeLanding.jsx create mode 100644 src/components/organisms/Landing/ReadmeLanding/ReadmeLanding.styles.js create mode 100644 src/constants/guides/AuthController.js create mode 100644 src/constants/guides/KeywordController.js create mode 100644 src/constants/guides/LiveInformationController.js create mode 100644 src/constants/guides/MemberController.js create mode 100644 src/constants/guides/MemberLiveInformationController.js create mode 100644 src/constants/guides/PlannerController.js create mode 100644 src/constants/guides/RecommendTripController.js create mode 100644 src/constants/guides/TripController.js create mode 100644 src/constants/guides/docsGuide.js create mode 100644 src/pages/LandingPage/index.jsx create mode 100644 src/router/LandingRouter.jsx diff --git a/src/components/index.js b/src/components/index.js index 680c3b0..a88ece7 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -36,3 +36,4 @@ export { default as ChattingContent } from './organisms/ChattingContent/Chatting export { default as RepoContent } from './organisms/RepoContent/RepoContent.jsx'; export { default as RepoDetailContent } from './organisms/RepoDetailContent/RepoDetailContent.jsx'; export { default as DashboardContent } from './organisms/DashboardContent/DashboardContent.jsx'; +export { default as LandingContent } from './organisms/Landing/LandingContent.jsx'; diff --git a/src/components/organisms/ChattingContent/ChattingContent.jsx b/src/components/organisms/ChattingContent/ChattingContent.jsx index d4a2179..2d80bcf 100644 --- a/src/components/organisms/ChattingContent/ChattingContent.jsx +++ b/src/components/organisms/ChattingContent/ChattingContent.jsx @@ -1,187 +1,193 @@ -import { useState, useEffect } from 'react'; -import { Typo, Button } from "../../index.js" -import { Check } from "lucide-react" -import api from "../../../api/axios.js" -import { useParams, useNavigate } from 'react-router-dom'; -import Modal from 'react-modal'; -import { - HomeWrapper, MainSectionWrapper, HomeLayout, - TextSectionWrapper, TextContainer, MainText, SubText, - MainImageSectionWrapper, BgShape, MainImageFrame, - MainFeatureSectionWrapper, MainFeatureSection, FeatureContentText, FeatureContentImage -} from "../HomeContent/HomeContent.styles.js" -import { Link } from 'react-router-dom'; -import styled, { keyframes, css } from 'styled-components'; -import { RefreshCw, Loader2 } from 'lucide-react'; - -const ChatButton = styled.button` - padding: 8px 16px; - margin: 8px; - border: none; - border-radius: 4px; - background-color: ${({ theme }) => theme.colors.primary}; - color: white; - cursor: pointer; -`; - -const modalStyles = { - content: { - top: '50%', - left: '50%', - right: 'auto', - bottom: 'auto', - marginRight: '-50%', - transform: 'translate(-50%, -50%)', - backgroundColor: 'var(--bg-color)', // 테마 색상 사용 - border: '1px solid #fff ', - }, - overlay: { - backgroundColor: 'rgba(0, 0, 0, 0.5)' - } -}; - - -const rotate = keyframes` - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -`; - -// 애니메이션 관련 styled-components -const fade = keyframes` - 0%, 100% { opacity: 0; transform: translateY(10px); } - 20%, 80% { opacity: 1; transform: translateY(0); } -`; - -const LoadingWrapper = styled.div` - display: flex; - align-items: center; - justify-content: center; - gap: 0.75rem; - padding: 1rem; - background-color: rgba(139, 92, 246, 0.1); - border: 1px solid rgba(139, 92, 246, 0.2); - border-radius: 0.5rem; - margin-bottom: 1rem; - min-height: 3.5rem; // 높이 고정으로 텍스트 변경시 레이아웃 시프트 방지 -`; - -const LoadingText = styled.span` - color: #8b5cf6; - font-size: 0.875rem; - font-weight: 500; - min-width : 12rem; - animation: ${fade} 2s ease-in-out; -`; - -const RotatingLoader = styled(Loader2)` - ${css` - animation: ${rotate} 1s linear infinite; - `} - color: #8b5cf6; -`; - - -const DocsContent = () => { - - const [testText, setTestText] = useState([]) - - const apiHandler = async () => { - const response = await api.get('api/analyze/result', { - params: { - repositoryName: "Gatsby-Starter-Haon" - } - }); - return response.data; - } - const handleTestClick = async () => { - console.log(process.env.REACT_APP_API_BASE_URL) - const data = await apiHandler(); - console.log("api/analyze/result testApi 결과 받기 : ", data) - } - const navigate = useNavigate(); - const { chattingId } = useParams(); - const [isModalOpen, setIsModalOpen] = useState(!!chattingId); - - const openModal = (id) => { - navigate(`/chatting/${id}`); - }; - - const closeModal = () => { - navigate('/chatting'); - }; - - - const loadingMessages = [ - '레포지토리 연결 중...', - '코드를 분석하는 중입니다...', - 'AI가 코드를 이해하는 중입니다...' - ]; - - const [messageIndex, setMessageIndex] = useState(0); - - useEffect(() => { - const interval = setInterval(() => { - setMessageIndex((prev) => (prev + 1) % loadingMessages.length); - }, 2000); // 2초마다 메시지 변경 - - return () => clearInterval(interval); - }, []); +// ReadmeLanding.jsx +import React from 'react'; +import { + Book, FileEdit, Layout, Sparkles, ArrowRight, + Download, BookOpen, FileCode, GitBranch, Flag +} from 'lucide-react'; +import { + PageWrapper, + PageContainer, + HeroSection, + ContentWrapper, + HeroGrid, + GradientText, + ButtonGroup, + Button, + Section, + FeatureWrapper, + FeatureIcon, + FeatureContent, + FeatureGrid, + StepsGrid, + StepCard, + StepIcon, + CTASection, + Badge, +} from './ReadMeLanding.styles'; + + + +const Feature = ({ icon: Icon, title, description }) => ( + + + + + +

{title}

+

{description}

+
+
+); + + +const ReadmeLanding = () => { + return ( + + + + + +
+ + README Maker + +

+ Create Beautiful{' '} + Documentation +

+

+ Generate comprehensive, well-structured README files for your projects + with our intelligent documentation maker. +

+ + + + +
+
+ {/* */} +
+
+
+
+ +
+ +
+ + Features + +

+ Everything you need for perfect documentation +

+

+ Create comprehensive documentation with our intuitive tools and intelligent + suggestions, making documentation a breeze. +

+
+ + + {[ + { + icon: Layout, + title: "Smart Templates", + description: "Choose from various pre-built templates or customize your own structure." + }, + { + icon: Sparkles, + title: "AI-Powered Suggestions", + description: "Get intelligent recommendations based on your project type." + }, + { + icon: GitBranch, + title: "Version Control Integration", + description: "Seamlessly sync your documentation with your Git repository." + }, + { + icon: Flag, + title: "Custom Sections", + description: "Add, remove, or rearrange sections with our drag-and-drop interface." + } + ].map((feature, index) => ( + + ))} + +
+
+ +
+ +
+ + How It Works + +

+ Create documentation in minutes +

+

+ Our simple three-step process makes documentation creation effortless +

+
+ + + {[ + { + icon: BookOpen, + title: "1. Choose Template", + description: "Select from our collection of professional README templates" + }, + { + icon: FileEdit, + title: "2. Customize Content", + description: "Edit and customize sections with our intuitive editor" + }, + { + icon: Download, + title: "3. Export & Deploy", + description: "Export to markdown and deploy to your repository" + } + ].map((step, index) => ( + + + + +

{step.title}

+

{step.description}

+
+ ))} +
+
+
+ +
+ + + Get Started + +

+ Ready to create amazing documentation? +

+

+ Join thousands of developers who are creating better documentation + with Dododocs README Maker. +

+ +
+
+
+
+ + ); +}; +export default ReadmeLanding; - return ( - <> - - - of the 꼬 - by the 꼬 - for the 꼬 - - - - - - {loadingMessages[messageIndex]} - - -
-

채팅 목록

-
- openModal('123')}> - 채팅방 123 열기 - - openModal('456')}> - 채팅방 456 열기 - -
- - - {chattingId && ( - <> -

채팅방 {chattingId}

-

채팅 내용이 여기에 표시됩니다.

- 닫기 - - )} -
-
- - ) -} - -export default DocsContent; diff --git a/src/components/organisms/ChattingContent/ForKko.jsx b/src/components/organisms/ChattingContent/ForKko.jsx index 2041c58..d4a2179 100644 --- a/src/components/organisms/ChattingContent/ForKko.jsx +++ b/src/components/organisms/ChattingContent/ForKko.jsx @@ -1,14 +1,88 @@ - -import React, { useState } from 'react'; +import { useState, useEffect } from 'react'; import { Typo, Button } from "../../index.js" import { Check } from "lucide-react" import api from "../../../api/axios.js" +import { useParams, useNavigate } from 'react-router-dom'; +import Modal from 'react-modal'; import { HomeWrapper, MainSectionWrapper, HomeLayout, TextSectionWrapper, TextContainer, MainText, SubText, MainImageSectionWrapper, BgShape, MainImageFrame, MainFeatureSectionWrapper, MainFeatureSection, FeatureContentText, FeatureContentImage } from "../HomeContent/HomeContent.styles.js" +import { Link } from 'react-router-dom'; +import styled, { keyframes, css } from 'styled-components'; +import { RefreshCw, Loader2 } from 'lucide-react'; + +const ChatButton = styled.button` + padding: 8px 16px; + margin: 8px; + border: none; + border-radius: 4px; + background-color: ${({ theme }) => theme.colors.primary}; + color: white; + cursor: pointer; +`; + +const modalStyles = { + content: { + top: '50%', + left: '50%', + right: 'auto', + bottom: 'auto', + marginRight: '-50%', + transform: 'translate(-50%, -50%)', + backgroundColor: 'var(--bg-color)', // 테마 색상 사용 + border: '1px solid #fff ', + }, + overlay: { + backgroundColor: 'rgba(0, 0, 0, 0.5)' + } +}; + + +const rotate = keyframes` + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +`; + +// 애니메이션 관련 styled-components +const fade = keyframes` + 0%, 100% { opacity: 0; transform: translateY(10px); } + 20%, 80% { opacity: 1; transform: translateY(0); } +`; + +const LoadingWrapper = styled.div` + display: flex; + align-items: center; + justify-content: center; + gap: 0.75rem; + padding: 1rem; + background-color: rgba(139, 92, 246, 0.1); + border: 1px solid rgba(139, 92, 246, 0.2); + border-radius: 0.5rem; + margin-bottom: 1rem; + min-height: 3.5rem; // 높이 고정으로 텍스트 변경시 레이아웃 시프트 방지 +`; + +const LoadingText = styled.span` + color: #8b5cf6; + font-size: 0.875rem; + font-weight: 500; + min-width : 12rem; + animation: ${fade} 2s ease-in-out; +`; + +const RotatingLoader = styled(Loader2)` + ${css` + animation: ${rotate} 1s linear infinite; + `} + color: #8b5cf6; +`; const DocsContent = () => { @@ -16,16 +90,52 @@ const DocsContent = () => { const [testText, setTestText] = useState([]) const apiHandler = async () => { - const response = await api.get('/api/dbfind'); + const response = await api.get('api/analyze/result', { + params: { + repositoryName: "Gatsby-Starter-Haon" + } + }); return response.data; } const handleTestClick = async () => { console.log(process.env.REACT_APP_API_BASE_URL) const data = await apiHandler(); - console.log(data) + console.log("api/analyze/result testApi 결과 받기 : ", data) } + const navigate = useNavigate(); + const { chattingId } = useParams(); + const [isModalOpen, setIsModalOpen] = useState(!!chattingId); + + const openModal = (id) => { + navigate(`/chatting/${id}`); + }; + + const closeModal = () => { + navigate('/chatting'); + }; + + + const loadingMessages = [ + '레포지토리 연결 중...', + '코드를 분석하는 중입니다...', + 'AI가 코드를 이해하는 중입니다...' + ]; + + const [messageIndex, setMessageIndex] = useState(0); + + useEffect(() => { + const interval = setInterval(() => { + setMessageIndex((prev) => (prev + 1) % loadingMessages.length); + }, 2000); // 2초마다 메시지 변경 + + return () => clearInterval(interval); + }, []); + + + + return ( <> @@ -38,7 +148,38 @@ const DocsContent = () => { 시작하기 + + + + {loadingMessages[messageIndex]} + + +
+

채팅 목록

+
+ openModal('123')}> + 채팅방 123 열기 + + openModal('456')}> + 채팅방 456 열기 + +
+ + {chattingId && ( + <> +

채팅방 {chattingId}

+

채팅 내용이 여기에 표시됩니다.

+ 닫기 + + )} +
+
) } diff --git a/src/components/organisms/ChattingContent/ReadMeLanding.styles.js b/src/components/organisms/ChattingContent/ReadMeLanding.styles.js new file mode 100644 index 0000000..7704164 --- /dev/null +++ b/src/components/organisms/ChattingContent/ReadMeLanding.styles.js @@ -0,0 +1,206 @@ +// ReadmeLanding.styles.js +import styled, { css, keyframes } from 'styled-components'; + +const fadeIn = keyframes` + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +`; + +export const PageWrapper = styled.div` + width: 100%; + &::before { + content: ''; + position: absolute; + inset: 0; + background: linear-gradient( + to bottom, + rgba(147, 51, 234, 0.1), + transparent, + transparent + ); + } +`; + +export const PageContainer = styled.div` + min-height: 100vh; + /* margin-top: 11.5dvh; */ + background-color: #0f0a1f; + color: white; + + display: flex; + align-items: center; + flex-direction: column; + justify-content: center; + width: 100%; + padding: clamp(5vh, 6vh, 8vh) clamp(4vw, 6vw, 8vw); + animation: ${fadeIn} 0.5s ease-out; + + @media (max-width: 768px) { + padding: clamp(3vh, 4vh, 6vh) clamp(2vw, 3vw, 4vw); + } +`; + +export const HeroSection = styled.div` + position: relative; + overflow: hidden; +`; + +export const ContentWrapper = styled.div` + position: relative; + max-width: 80rem; + margin: 0 auto; + padding: 5rem 1rem; +`; + +export const HeroGrid = styled.div` + display: grid; + gap: 3rem; + align-items: center; + + @media (min-width: 1024px) { + grid-template-columns: repeat(2, 1fr); + } +`; + +export const GradientText = styled.span` + background: linear-gradient(to right, #a78bfa, #f472b6); + -webkit-background-clip: text; + color: transparent; +`; + +export const ButtonGroup = styled.div` + display: flex; + gap: 1rem; +`; + +export const Button = styled.button` + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.75rem 1.5rem; + border-radius: 0.5rem; + transition: all 0.2s; + + ${(props) => + props.primary && + ` + background: #9333ea; + color: white; + &:hover { + background: #7e22ce; + } + `} + + ${(props) => + props.secondary && + ` + border: 1px solid rgba(147, 51, 234, 0.2); + color: white; + &:hover { + background: rgba(147, 51, 234, 0.2); + } + `} +`; + +export const Section = styled.section` + padding: 5rem 1rem; + ${(props) => + props.darker && + ` + background: rgba(22, 17, 46, 0.5); + `} +`; + +export const FeatureWrapper = styled.div` + display: flex; + gap: 1rem; + padding: 1.5rem; + border-radius: 0.75rem; + background: #16112e; + border: 1px solid rgba(147, 51, 234, 0.2); +`; + +export const FeatureIcon = styled.div` + width: 3rem; + height: 3rem; + border-radius: 0.5rem; + background: rgba(147, 51, 234, 0.2); + display: flex; + align-items: center; + justify-content: center; + color: #e9d5ff; +`; + +export const FeatureContent = styled.div` + h3 { + font-size: 1.125rem; + font-weight: 600; + margin-bottom: 0.5rem; + color: white; + } + + p { + color: #e9d5ff; + } +`; + +export const FeatureGrid = styled.div` + display: grid; + gap: 1.5rem; + margin-top: 3rem; + + @media (min-width: 768px) { + grid-template-columns: repeat(2, 1fr); + } +`; + +export const StepsGrid = styled.div` + display: grid; + gap: 2rem; + margin-top: 3rem; + + @media (min-width: 768px) { + grid-template-columns: repeat(3, 1fr); + } +`; + +export const StepCard = styled.div` + text-align: center; + padding: 1.5rem; + background: #16112e; + border-radius: 0.75rem; + border: 1px solid rgba(147, 51, 234, 0.2); +`; + +export const StepIcon = styled.div` + width: 4rem; + height: 4rem; + border-radius: 50%; + background: rgba(147, 51, 234, 0.2); + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 1.5rem; + color: #e9d5ff; +`; + +export const CTASection = styled.div` + max-width: 64rem; + margin: 0 auto; + text-align: center; +`; + +export const Badge = styled.span` + margin-left: auto; + font-size: 0.75rem; + background: rgba(147, 51, 234, 0.2); + color: #9333ea; + padding: 0.125rem 0.5rem; + border-radius: 9999px; +`; diff --git a/src/components/organisms/Landing/ChattingLanding/ChattingLanding.jsx b/src/components/organisms/Landing/ChattingLanding/ChattingLanding.jsx new file mode 100644 index 0000000..df2da59 --- /dev/null +++ b/src/components/organisms/Landing/ChattingLanding/ChattingLanding.jsx @@ -0,0 +1,193 @@ + + +// ReadmeLanding.jsx +import React from 'react'; +import { + Book, FileEdit, Layout, Sparkles, ArrowRight, + Download, BookOpen, FileCode, GitBranch, Flag +} from 'lucide-react'; +import { + PageWrapper, + PageContainer, + HeroSection, + ContentWrapper, + HeroGrid, + GradientText, + ButtonGroup, + Button, + Section, + FeatureWrapper, + FeatureIcon, + FeatureContent, + FeatureGrid, + StepsGrid, + StepCard, + StepIcon, + CTASection, + Badge, +} from './ChattingLanding.styles'; + + + +const Feature = ({ icon: Icon, title, description }) => ( + + + + + +

{title}

+

{description}

+
+
+); + + +const ReadmeLanding = () => { + return ( + + + + + +
+ + README Maker + +

+ Create Beautiful{' '} + Documentation +

+

+ Generate comprehensive, well-structured README files for your projects + with our intelligent documentation maker. +

+ + + + +
+
+ {/* */} +
+
+
+
+ +
+ +
+ + Features + +

+ Everything you need for perfect documentation +

+

+ Create comprehensive documentation with our intuitive tools and intelligent + suggestions, making documentation a breeze. +

+
+ + + {[ + { + icon: Layout, + title: "Smart Templates", + description: "Choose from various pre-built templates or customize your own structure." + }, + { + icon: Sparkles, + title: "AI-Powered Suggestions", + description: "Get intelligent recommendations based on your project type." + }, + { + icon: GitBranch, + title: "Version Control Integration", + description: "Seamlessly sync your documentation with your Git repository." + }, + { + icon: Flag, + title: "Custom Sections", + description: "Add, remove, or rearrange sections with our drag-and-drop interface." + } + ].map((feature, index) => ( + + ))} + +
+
+ +
+ +
+ + How It Works + +

+ Create documentation in minutes +

+

+ Our simple three-step process makes documentation creation effortless +

+
+ + + {[ + { + icon: BookOpen, + title: "1. Choose Template", + description: "Select from our collection of professional README templates" + }, + { + icon: FileEdit, + title: "2. Customize Content", + description: "Edit and customize sections with our intuitive editor" + }, + { + icon: Download, + title: "3. Export & Deploy", + description: "Export to markdown and deploy to your repository" + } + ].map((step, index) => ( + + + + +

{step.title}

+

{step.description}

+
+ ))} +
+
+
+ +
+ + + Get Started + +

+ Ready to create amazing documentation? +

+

+ Join thousands of developers who are creating better documentation + with Dododocs README Maker. +

+ +
+
+
+
+ + ); +}; +export default ReadmeLanding; + + + + + diff --git a/src/components/organisms/Landing/ChattingLanding/ChattingLanding.styles.js b/src/components/organisms/Landing/ChattingLanding/ChattingLanding.styles.js new file mode 100644 index 0000000..06671d7 --- /dev/null +++ b/src/components/organisms/Landing/ChattingLanding/ChattingLanding.styles.js @@ -0,0 +1,206 @@ +// ChattingLanding.styles.js +import styled, { css, keyframes } from 'styled-components'; + +const fadeIn = keyframes` + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +`; + +export const PageWrapper = styled.div` + width: 100%; + &::before { + content: ''; + position: absolute; + inset: 0; + background: linear-gradient( + to bottom, + rgba(162, 92, 255, 0.1), + transparent, + transparent + ); + } +`; + +export const PageContainer = styled.div` + min-height: 100vh; + /* margin-top: 11.5dvh; */ + background-color: #0f0a1f; + color: white; + + display: flex; + align-items: center; + flex-direction: column; + justify-content: center; + width: 100%; + padding: clamp(5vh, 6vh, 8vh) clamp(4vw, 6vw, 8vw); + animation: ${fadeIn} 0.5s ease-out; + + @media (max-width: 768px) { + padding: clamp(3vh, 4vh, 6vh) clamp(2vw, 3vw, 4vw); + } +`; + +export const HeroSection = styled.div` + position: relative; + overflow: hidden; +`; + +export const ContentWrapper = styled.div` + position: relative; + max-width: 80rem; + margin: 0 auto; + padding: 5rem 1rem; +`; + +export const HeroGrid = styled.div` + display: grid; + gap: 3rem; + align-items: center; + + @media (min-width: 1024px) { + grid-template-columns: repeat(2, 1fr); + } +`; + +export const GradientText = styled.span` + background: linear-gradient(to right, #a78bfa, #f472b6); + -webkit-background-clip: text; + color: transparent; +`; + +export const ButtonGroup = styled.div` + display: flex; + gap: 1rem; +`; + +export const Button = styled.button` + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.75rem 1.5rem; + border-radius: 0.5rem; + transition: all 0.2s; + + ${(props) => + props.primary && + ` + background: #9333ea; + color: white; + &:hover { + background: #7e22ce; + } + `} + + ${(props) => + props.secondary && + ` + border: 1px solid rgba(147, 51, 234, 0.2); + color: white; + &:hover { + background: rgba(147, 51, 234, 0.2); + } + `} +`; + +export const Section = styled.section` + padding: 5rem 1rem; + ${(props) => + props.darker && + ` + background: rgba(22, 17, 46, 0.5); + `} +`; + +export const FeatureWrapper = styled.div` + display: flex; + gap: 1rem; + padding: 1.5rem; + border-radius: 0.75rem; + background: #16112e; + border: 1px solid rgba(147, 51, 234, 0.2); +`; + +export const FeatureIcon = styled.div` + width: 3rem; + height: 3rem; + border-radius: 0.5rem; + background: rgba(147, 51, 234, 0.2); + display: flex; + align-items: center; + justify-content: center; + color: #e9d5ff; +`; + +export const FeatureContent = styled.div` + h3 { + font-size: 1.125rem; + font-weight: 600; + margin-bottom: 0.5rem; + color: white; + } + + p { + color: #e9d5ff; + } +`; + +export const FeatureGrid = styled.div` + display: grid; + gap: 1.5rem; + margin-top: 3rem; + + @media (min-width: 768px) { + grid-template-columns: repeat(2, 1fr); + } +`; + +export const StepsGrid = styled.div` + display: grid; + gap: 2rem; + margin-top: 3rem; + + @media (min-width: 768px) { + grid-template-columns: repeat(3, 1fr); + } +`; + +export const StepCard = styled.div` + text-align: center; + padding: 1.5rem; + background: #16112e; + border-radius: 0.75rem; + border: 1px solid rgba(147, 51, 234, 0.2); +`; + +export const StepIcon = styled.div` + width: 4rem; + height: 4rem; + border-radius: 50%; + background: rgba(147, 51, 234, 0.2); + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 1.5rem; + color: #e9d5ff; +`; + +export const CTASection = styled.div` + max-width: 64rem; + margin: 0 auto; + text-align: center; +`; + +export const Badge = styled.span` + margin-left: auto; + font-size: 0.75rem; + background: rgba(147, 51, 234, 0.2); + color: #9333ea; + padding: 0.125rem 0.5rem; + border-radius: 9999px; +`; diff --git a/src/components/organisms/Landing/DocsLanding/DocsLanding.jsx b/src/components/organisms/Landing/DocsLanding/DocsLanding.jsx new file mode 100644 index 0000000..a255a0e --- /dev/null +++ b/src/components/organisms/Landing/DocsLanding/DocsLanding.jsx @@ -0,0 +1,193 @@ + + +// ReadmeLanding.jsx +import React from 'react'; +import { + Book, FileEdit, Layout, Sparkles, ArrowRight, + Download, BookOpen, FileCode, GitBranch, Flag +} from 'lucide-react'; +import { + PageWrapper, + PageContainer, + HeroSection, + ContentWrapper, + HeroGrid, + GradientText, + ButtonGroup, + Button, + Section, + FeatureWrapper, + FeatureIcon, + FeatureContent, + FeatureGrid, + StepsGrid, + StepCard, + StepIcon, + CTASection, + Badge, +} from './DocsLanding.styles'; + + + +const Feature = ({ icon: Icon, title, description }) => ( + + + + + +

{title}

+

{description}

+
+
+); + + +const DocsLanding = () => { + return ( + + + + + +
+ + Docs Maker + +

+ Create Beautiful{' '} + Documentation +

+

+ Generate comprehensive, well-structured Docs files for your projects + with our intelligent documentation maker. +

+ + + + +
+
+ {/* */} +
+
+
+
+ +
+ +
+ + Features + +

+ Everything you need for perfect documentation +

+

+ Create comprehensive documentation with our intuitive tools and intelligent + suggestions, making documentation a breeze. +

+
+ + + {[ + { + icon: Layout, + title: "Smart Templates", + description: "Choose from various pre-built templates or customize your own structure." + }, + { + icon: Sparkles, + title: "AI-Powered Suggestions", + description: "Get intelligent recommendations based on your project type." + }, + { + icon: GitBranch, + title: "Version Control Integration", + description: "Seamlessly sync your documentation with your Git repository." + }, + { + icon: Flag, + title: "Custom Sections", + description: "Add, remove, or rearrange sections with our drag-and-drop interface." + } + ].map((feature, index) => ( + + ))} + +
+
+ +
+ +
+ + How It Works + +

+ Create documentation in minutes +

+

+ Our simple three-step process makes documentation creation effortless +

+
+ + + {[ + { + icon: BookOpen, + title: "1. Choose Template", + description: "Select from our collection of professional Docs templates" + }, + { + icon: FileEdit, + title: "2. Customize Content", + description: "Edit and customize sections with our intuitive editor" + }, + { + icon: Download, + title: "3. Export & Deploy", + description: "Export to markdown and deploy to your repository" + } + ].map((step, index) => ( + + + + +

{step.title}

+

{step.description}

+
+ ))} +
+
+
+ +
+ + + Get Started + +

+ Ready to create amazing documentation? +

+

+ Join thousands of developers who are creating better documentation + with Dododocs Docs Maker. +

+ +
+
+
+
+ + ); +}; +export default DocsLanding; + + + + + diff --git a/src/components/organisms/Landing/DocsLanding/DocsLanding.styles.js b/src/components/organisms/Landing/DocsLanding/DocsLanding.styles.js new file mode 100644 index 0000000..7704164 --- /dev/null +++ b/src/components/organisms/Landing/DocsLanding/DocsLanding.styles.js @@ -0,0 +1,206 @@ +// ReadmeLanding.styles.js +import styled, { css, keyframes } from 'styled-components'; + +const fadeIn = keyframes` + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +`; + +export const PageWrapper = styled.div` + width: 100%; + &::before { + content: ''; + position: absolute; + inset: 0; + background: linear-gradient( + to bottom, + rgba(147, 51, 234, 0.1), + transparent, + transparent + ); + } +`; + +export const PageContainer = styled.div` + min-height: 100vh; + /* margin-top: 11.5dvh; */ + background-color: #0f0a1f; + color: white; + + display: flex; + align-items: center; + flex-direction: column; + justify-content: center; + width: 100%; + padding: clamp(5vh, 6vh, 8vh) clamp(4vw, 6vw, 8vw); + animation: ${fadeIn} 0.5s ease-out; + + @media (max-width: 768px) { + padding: clamp(3vh, 4vh, 6vh) clamp(2vw, 3vw, 4vw); + } +`; + +export const HeroSection = styled.div` + position: relative; + overflow: hidden; +`; + +export const ContentWrapper = styled.div` + position: relative; + max-width: 80rem; + margin: 0 auto; + padding: 5rem 1rem; +`; + +export const HeroGrid = styled.div` + display: grid; + gap: 3rem; + align-items: center; + + @media (min-width: 1024px) { + grid-template-columns: repeat(2, 1fr); + } +`; + +export const GradientText = styled.span` + background: linear-gradient(to right, #a78bfa, #f472b6); + -webkit-background-clip: text; + color: transparent; +`; + +export const ButtonGroup = styled.div` + display: flex; + gap: 1rem; +`; + +export const Button = styled.button` + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.75rem 1.5rem; + border-radius: 0.5rem; + transition: all 0.2s; + + ${(props) => + props.primary && + ` + background: #9333ea; + color: white; + &:hover { + background: #7e22ce; + } + `} + + ${(props) => + props.secondary && + ` + border: 1px solid rgba(147, 51, 234, 0.2); + color: white; + &:hover { + background: rgba(147, 51, 234, 0.2); + } + `} +`; + +export const Section = styled.section` + padding: 5rem 1rem; + ${(props) => + props.darker && + ` + background: rgba(22, 17, 46, 0.5); + `} +`; + +export const FeatureWrapper = styled.div` + display: flex; + gap: 1rem; + padding: 1.5rem; + border-radius: 0.75rem; + background: #16112e; + border: 1px solid rgba(147, 51, 234, 0.2); +`; + +export const FeatureIcon = styled.div` + width: 3rem; + height: 3rem; + border-radius: 0.5rem; + background: rgba(147, 51, 234, 0.2); + display: flex; + align-items: center; + justify-content: center; + color: #e9d5ff; +`; + +export const FeatureContent = styled.div` + h3 { + font-size: 1.125rem; + font-weight: 600; + margin-bottom: 0.5rem; + color: white; + } + + p { + color: #e9d5ff; + } +`; + +export const FeatureGrid = styled.div` + display: grid; + gap: 1.5rem; + margin-top: 3rem; + + @media (min-width: 768px) { + grid-template-columns: repeat(2, 1fr); + } +`; + +export const StepsGrid = styled.div` + display: grid; + gap: 2rem; + margin-top: 3rem; + + @media (min-width: 768px) { + grid-template-columns: repeat(3, 1fr); + } +`; + +export const StepCard = styled.div` + text-align: center; + padding: 1.5rem; + background: #16112e; + border-radius: 0.75rem; + border: 1px solid rgba(147, 51, 234, 0.2); +`; + +export const StepIcon = styled.div` + width: 4rem; + height: 4rem; + border-radius: 50%; + background: rgba(147, 51, 234, 0.2); + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 1.5rem; + color: #e9d5ff; +`; + +export const CTASection = styled.div` + max-width: 64rem; + margin: 0 auto; + text-align: center; +`; + +export const Badge = styled.span` + margin-left: auto; + font-size: 0.75rem; + background: rgba(147, 51, 234, 0.2); + color: #9333ea; + padding: 0.125rem 0.5rem; + border-radius: 9999px; +`; diff --git a/src/components/organisms/Landing/LandingContent.jsx b/src/components/organisms/Landing/LandingContent.jsx new file mode 100644 index 0000000..7b6b1ed --- /dev/null +++ b/src/components/organisms/Landing/LandingContent.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { useParams, useNavigate } from 'react-router-dom'; +import ChattingLanding from './ChattingLanding/ChattingLanding.jsx'; +import DocsLanding from './DocsLanding/DocsLanding.jsx'; +import ReadmeLanding from './ReadmeLanding/ReadmeLanding.jsx'; + +const LandingContent = () => { + const { serviceTitle } = useParams(); + + if (serviceTitle === 'chatting') { + return ; + } else if (serviceTitle === 'readme') { + return ; + } else if (serviceTitle === 'docs') { + return ; + } + +}; + +export default LandingContent; \ No newline at end of file diff --git a/src/components/organisms/Landing/ReadmeLanding/ReadmeLanding.jsx b/src/components/organisms/Landing/ReadmeLanding/ReadmeLanding.jsx new file mode 100644 index 0000000..7514997 --- /dev/null +++ b/src/components/organisms/Landing/ReadmeLanding/ReadmeLanding.jsx @@ -0,0 +1,193 @@ + + +// ReadmeLanding.jsx +import React from 'react'; +import { + Book, FileEdit, Layout, Sparkles, ArrowRight, + Download, BookOpen, FileCode, GitBranch, Flag +} from 'lucide-react'; +import { + PageWrapper, + PageContainer, + HeroSection, + ContentWrapper, + HeroGrid, + GradientText, + ButtonGroup, + Button, + Section, + FeatureWrapper, + FeatureIcon, + FeatureContent, + FeatureGrid, + StepsGrid, + StepCard, + StepIcon, + CTASection, + Badge, +} from './ReadmeLanding.styles' + + + +const Feature = ({ icon: Icon, title, description }) => ( + + + + + +

{title}

+

{description}

+
+
+); + + +const ReadmeLanding = () => { + return ( + + + + + +
+ + README Maker + +

+ Create Beautiful{' '} + Documentation +

+

+ Generate comprehensive, well-structured README files for your projects + with our intelligent documentation maker. +

+ + + + +
+
+ {/* */} +
+
+
+
+ +
+ +
+ + Features + +

+ Everything you need for perfect documentation +

+

+ Create comprehensive documentation with our intuitive tools and intelligent + suggestions, making documentation a breeze. +

+
+ + + {[ + { + icon: Layout, + title: "Smart Templates", + description: "Choose from various pre-built templates or customize your own structure." + }, + { + icon: Sparkles, + title: "AI-Powered Suggestions", + description: "Get intelligent recommendations based on your project type." + }, + { + icon: GitBranch, + title: "Version Control Integration", + description: "Seamlessly sync your documentation with your Git repository." + }, + { + icon: Flag, + title: "Custom Sections", + description: "Add, remove, or rearrange sections with our drag-and-drop interface." + } + ].map((feature, index) => ( + + ))} + +
+
+ +
+ +
+ + How It Works + +

+ Create documentation in minutes +

+

+ Our simple three-step process makes documentation creation effortless +

+
+ + + {[ + { + icon: BookOpen, + title: "1. Choose Template", + description: "Select from our collection of professional README templates" + }, + { + icon: FileEdit, + title: "2. Customize Content", + description: "Edit and customize sections with our intuitive editor" + }, + { + icon: Download, + title: "3. Export & Deploy", + description: "Export to markdown and deploy to your repository" + } + ].map((step, index) => ( + + + + +

{step.title}

+

{step.description}

+
+ ))} +
+
+
+ +
+ + + Get Started + +

+ Ready to create amazing documentation? +

+

+ Join thousands of developers who are creating better documentation + with Dododocs README Maker. +

+ +
+
+
+
+ + ); +}; +export default ReadmeLanding; + + + + + diff --git a/src/components/organisms/Landing/ReadmeLanding/ReadmeLanding.styles.js b/src/components/organisms/Landing/ReadmeLanding/ReadmeLanding.styles.js new file mode 100644 index 0000000..7704164 --- /dev/null +++ b/src/components/organisms/Landing/ReadmeLanding/ReadmeLanding.styles.js @@ -0,0 +1,206 @@ +// ReadmeLanding.styles.js +import styled, { css, keyframes } from 'styled-components'; + +const fadeIn = keyframes` + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +`; + +export const PageWrapper = styled.div` + width: 100%; + &::before { + content: ''; + position: absolute; + inset: 0; + background: linear-gradient( + to bottom, + rgba(147, 51, 234, 0.1), + transparent, + transparent + ); + } +`; + +export const PageContainer = styled.div` + min-height: 100vh; + /* margin-top: 11.5dvh; */ + background-color: #0f0a1f; + color: white; + + display: flex; + align-items: center; + flex-direction: column; + justify-content: center; + width: 100%; + padding: clamp(5vh, 6vh, 8vh) clamp(4vw, 6vw, 8vw); + animation: ${fadeIn} 0.5s ease-out; + + @media (max-width: 768px) { + padding: clamp(3vh, 4vh, 6vh) clamp(2vw, 3vw, 4vw); + } +`; + +export const HeroSection = styled.div` + position: relative; + overflow: hidden; +`; + +export const ContentWrapper = styled.div` + position: relative; + max-width: 80rem; + margin: 0 auto; + padding: 5rem 1rem; +`; + +export const HeroGrid = styled.div` + display: grid; + gap: 3rem; + align-items: center; + + @media (min-width: 1024px) { + grid-template-columns: repeat(2, 1fr); + } +`; + +export const GradientText = styled.span` + background: linear-gradient(to right, #a78bfa, #f472b6); + -webkit-background-clip: text; + color: transparent; +`; + +export const ButtonGroup = styled.div` + display: flex; + gap: 1rem; +`; + +export const Button = styled.button` + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.75rem 1.5rem; + border-radius: 0.5rem; + transition: all 0.2s; + + ${(props) => + props.primary && + ` + background: #9333ea; + color: white; + &:hover { + background: #7e22ce; + } + `} + + ${(props) => + props.secondary && + ` + border: 1px solid rgba(147, 51, 234, 0.2); + color: white; + &:hover { + background: rgba(147, 51, 234, 0.2); + } + `} +`; + +export const Section = styled.section` + padding: 5rem 1rem; + ${(props) => + props.darker && + ` + background: rgba(22, 17, 46, 0.5); + `} +`; + +export const FeatureWrapper = styled.div` + display: flex; + gap: 1rem; + padding: 1.5rem; + border-radius: 0.75rem; + background: #16112e; + border: 1px solid rgba(147, 51, 234, 0.2); +`; + +export const FeatureIcon = styled.div` + width: 3rem; + height: 3rem; + border-radius: 0.5rem; + background: rgba(147, 51, 234, 0.2); + display: flex; + align-items: center; + justify-content: center; + color: #e9d5ff; +`; + +export const FeatureContent = styled.div` + h3 { + font-size: 1.125rem; + font-weight: 600; + margin-bottom: 0.5rem; + color: white; + } + + p { + color: #e9d5ff; + } +`; + +export const FeatureGrid = styled.div` + display: grid; + gap: 1.5rem; + margin-top: 3rem; + + @media (min-width: 768px) { + grid-template-columns: repeat(2, 1fr); + } +`; + +export const StepsGrid = styled.div` + display: grid; + gap: 2rem; + margin-top: 3rem; + + @media (min-width: 768px) { + grid-template-columns: repeat(3, 1fr); + } +`; + +export const StepCard = styled.div` + text-align: center; + padding: 1.5rem; + background: #16112e; + border-radius: 0.75rem; + border: 1px solid rgba(147, 51, 234, 0.2); +`; + +export const StepIcon = styled.div` + width: 4rem; + height: 4rem; + border-radius: 50%; + background: rgba(147, 51, 234, 0.2); + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 1.5rem; + color: #e9d5ff; +`; + +export const CTASection = styled.div` + max-width: 64rem; + margin: 0 auto; + text-align: center; +`; + +export const Badge = styled.span` + margin-left: auto; + font-size: 0.75rem; + background: rgba(147, 51, 234, 0.2); + color: #9333ea; + padding: 0.125rem 0.5rem; + border-radius: 9999px; +`; diff --git a/src/components/organisms/OAuthCallback/OAuthCallback.jsx b/src/components/organisms/OAuthCallback/OAuthCallback.jsx index 522a6c1..273804f 100644 --- a/src/components/organisms/OAuthCallback/OAuthCallback.jsx +++ b/src/components/organisms/OAuthCallback/OAuthCallback.jsx @@ -26,7 +26,7 @@ const OAuthCallback = () => { if (success) { const returnUrl = localStorage.getItem('returnUrl') || '/'; localStorage.removeItem('returnUrl'); - navigate('/', { replace: true }); + // navigate('/', { replace: true }); } else { // navigate('/', { replace: true }); } diff --git a/src/components/organisms/RepoDetailContent/Chatting.jsx b/src/components/organisms/RepoDetailContent/Chatting.jsx index 4161228..9e30ed5 100644 --- a/src/components/organisms/RepoDetailContent/Chatting.jsx +++ b/src/components/organisms/RepoDetailContent/Chatting.jsx @@ -436,35 +436,6 @@ const ChatbotUI = () => { const [isTest, setIsTest] = useState(0); - // SSE 설정 - useEffect(() => { - if (!activeRepositoryId) return; - - - const eventSource = new EventSource( - `${process.env.REACT_APP_API_BASE_URL}api/chatbot/stream-and-save/${activeRepositoryId}`, - ); - - eventSource.onmessage = (event) => { - try { - const data = JSON.parse(event.data); - setStreamingResponse(prevResponse => prevResponse + data.answer); - } catch (error) { - console.error('스트리밍 데이터 파싱 에러:', error); - } - }; - - - eventSource.onerror = (error) => { - console.error('SSE 연결 에러:', error); - eventSource.close(); - }; - - return () => { - eventSource.close(); - }; - }, [activeRepositoryId]);// token이 변경될 때마다 재연결 - // 채팅 히스토리 동기화 @@ -515,55 +486,37 @@ const ChatbotUI = () => { isUser: true }]); - // API 요청 설정 - const myHeaders = new Headers(); - myHeaders.append("Content-Type", "application/json"); - myHeaders.append("Authorization", `Bearer ${token}`); - - const requestBody = { - question: inputText - }; + setInputText(''); + setStreamingResponse(''); - const requestOptions = { - method: "POST", - headers: myHeaders, - body: JSON.stringify(requestBody), - redirect: "follow" - }; + try { + const response = await fetch( + `${process.env.REACT_APP_API_BASE_URL}api/chatbot/stream-and-save/${activeRepositoryId}`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + }, + body: JSON.stringify({ + question: inputText + }) + } + ); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const result = await response.text(); + console.log(result); - setInputText(''); - setStreamingResponse(''); // 스트리밍 응답 초기화 + setMessages(prev => [...prev, { + id: `a-${userMessageId}`, + text: result, + isUser: false + }]); - try { - await fetch(`${process.env.REACT_APP_API_BASE_URL}api/chatbot/stream-and-save/${activeRepositoryId}`, - requestOptions); - - - // const response = await fetch(`${process.env.REACT_APP_API_BASE_URL}api/chatbot/stream-and-save`, requestOptions); - - // if (!response.ok) { - // throw new Error(`HTTP error! status: ${response.status}`); - // } - - // // SSE 연결 설정 - // const eventSource = new EventSource( - // `${process.env.REACT_APP_API_BASE_URL}/api/chatbot/stream?token=${token}&repositoryId=${activeRepositoryId}` - // ); - - // eventSource.onmessage = (event) => { - // try { - // const data = JSON.parse(event.data); - // setStreamingResponse(prevResponse => prevResponse + data.answer); - // } catch (error) { - // console.error('스트리밍 데이터 파싱 에러:', error); - // } - // }; - - // eventSource.onerror = (error) => { - // console.error('SSE 연결 에러:', error); - // eventSource.close(); - // }; } catch (error) { setMessages(prev => [...prev, { id: `error-${userMessageId}`, @@ -573,11 +526,6 @@ const ChatbotUI = () => { }]); console.error('채팅 요청 에러:', error); } - - - - - }; // 대화 초기화 핸들러 diff --git a/src/components/organisms/RepoDetailContent/Document.jsx b/src/components/organisms/RepoDetailContent/Document.jsx index 4650a76..0fe6825 100644 --- a/src/components/organisms/RepoDetailContent/Document.jsx +++ b/src/components/organisms/RepoDetailContent/Document.jsx @@ -198,9 +198,9 @@ const Document = () => { // docsData가 변경될 때마다 문서 목록 업데이트 useEffect(() => { if (!docsData) return; - // Controller 문서 필터링 (Test 파일 제외) const controllers = docsData.regularFiles + console.log('controllers', controllers) // Summary 문서 설정 const summaries = docsData.summaryFiles || []; @@ -209,7 +209,7 @@ const Document = () => { setSummaryDocs(summaries); // 초기 선택된 문서 설정 - if (controllers.length && !selectedFileName) { + if (controllers?.length && !selectedFileName) { setSelectedFileName(controllers[0].fileName); setCurrentContent(controllers[0].fileContents); } @@ -237,7 +237,7 @@ const Document = () => {
regular Docs - {controllerDocs.map(doc => ( + {controllerDocs?.map(doc => ( { {isError ? ( - ) : ( )} diff --git a/src/constants/guides/AuthController.js b/src/constants/guides/AuthController.js new file mode 100644 index 0000000..21f6e40 --- /dev/null +++ b/src/constants/guides/AuthController.js @@ -0,0 +1,174 @@ +export const AuthController = ` +# 시스템 아키텍처 문서 + +## 전체 구조 +\`\`\`mermaid +graph TD + A[Client] --> B[AuthController] + B --> C[AuthService] + C --> D[TokenManager] + C --> E[MemberService] + D --> F[Database] + E --> F +\`\`\` + +## 시스템 흐름 +\`\`\`mermaid +sequenceDiagram + Client->>AuthController: Request for OAuth URI + AuthController->>AuthService: generateUri(providerName) + AuthService->>OAuthProvider: getOAuthUriProvider(providerName) + OAuthProvider->>AuthService: Return OAuth URI + AuthService->>AuthController: Return OAuth URI + AuthController->>Client: Return OAuth URI + + Client->>AuthController: Login with code + AuthController->>AuthService: generateTokenWithCode(code, providerName) + AuthService->>OAuthClient: getOAuthMember(code) + OAuthClient->>AuthService: Return OAuthMember + AuthService->>MemberService: findOrCreateMember(oAuthMember, providerName) + MemberService->>AuthService: Return Member + AuthService->>TokenManager: createMemberToken(memberId) + TokenManager->>AuthService: Return MemberToken + AuthService->>AuthController: Return MemberToken + AuthController->>Client: Return Access Token and Set Refresh Token Cookie + + Client->>AuthController: Extend login with refresh token + AuthController->>AuthService: generateRenewalAccessToken(refreshToken) + AuthService->>TokenManager: generateRenewalAccessToken(refreshToken) + TokenManager->>AuthService: Return RenewalToken + AuthService->>AuthController: Return RenewalAccessTokenResponse + AuthController->>Client: Return New Access Token + + Client->>AuthController: Logout + AuthController->>AuthService: removeRefreshToken(refreshToken) + AuthService->>TokenManager: removeRefreshToken(refreshToken) + AuthController->>Client: No Content +\`\`\` + +## 주요 컴포넌트 설명 + +### AuthController +- **역할과 책임**: 클라이언트의 요청을 처리하고, AuthService와 상호작용하여 인증 관련 작업을 수행합니다. +- **주요 메서드**: + - \`generateUri\`: OAuth URI를 생성합니다. + - \`login\`: 로그인 요청을 처리하고, 액세스 토큰과 리프레시 토큰을 반환합니다. + - \`extendLogin\`: 리프레시 토큰을 사용하여 액세스 토큰을 갱신합니다. + - \`logout\`: 로그아웃 요청을 처리합니다. + +### AuthService +- **역할과 책임**: 인증 관련 비즈니스 로직을 처리합니다. OAuth 인증, 토큰 생성 및 갱신, 회원 관리 등을 담당합니다. +- **주요 메서드**: + - \`generateTokenWithCode\`: OAuth 코드를 사용하여 토큰을 생성합니다. + - \`generateUri\`: OAuth URI를 생성합니다. + - \`generateRenewalAccessToken\`: 리프레시 토큰을 사용하여 액세스 토큰을 갱신합니다. + - \`removeRefreshToken\`: 로그아웃 시 리프레시 토큰을 제거합니다. + +### TokenManager +- **역할과 책임**: 토큰 생성 및 관리 기능을 제공합니다. 액세스 토큰과 리프레시 토큰을 생성하고 검증합니다. + +### MemberService +- **역할과 책임**: 회원 관련 데이터 접근 및 관리 기능을 제공합니다. 회원의 존재 여부를 확인하고, 회원 정보를 저장합니다. + +## API 엔드포인트 + +### Generate OAuth URI +**GET** \`/api/auth/{oAuthProvider}/link\` + +#### 설명 +주어진 OAuth 공급자에 대한 인증 URI를 생성합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|--------------|--------|-----------|----------------------------| +| oAuthProvider| string | Required | OAuth 공급자의 이름 | + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "oAuthUri": "https://example.com/oauth/authorize?client_id=xxx&redirect_uri=xxx" +} +\`\`\` + +### Login +**POST** \`/api/auth/{oAuthProvider}/login\` + +#### 설명 +주어진 OAuth 공급자와 함께 로그인합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|--------------|--------|-----------|----------------------------| +| oAuthProvider| string | Required | OAuth 공급자의 이름 | + +##### Request Body +\`\`\`json +{ + "code": "authorization_code" +} +\`\`\` + +#### 응답 +##### Success Response +- Status: 201 Created +\`\`\`json +{ + "accessToken": "access_token_value" +} +\`\`\` + +### Extend Login +**POST** \`/api/auth/extend/login\` + +#### 설명 +리프레시 토큰을 사용하여 액세스 토큰을 갱신합니다. + +#### 요청 +##### Request Body +\`\`\`json +{ + "refreshToken": "refresh_token_value" +} +\`\`\` + +#### 응답 +##### Success Response +- Status: 201 Created +\`\`\`json +{ + "accessToken": "new_access_token_value" +} +\`\`\` + +### Logout +**DELETE** \`/api/auth/logout\` + +#### 설명 +사용자를 로그아웃합니다. + +#### 요청 +##### Headers +| 이름 | 필수 여부 | 설명 | +|----------------|-----------|----------------------------| +| Cookie | Required | 리프레시 토큰이 포함된 쿠키 | + +#### 응답 +##### Success Response +- Status: 204 No Content + +## SUMMARY + +### Component Overview +- **AuthController**: 클라이언트 요청을 처리하고 응답을 반환하는 역할. +- **AuthService**: 인증 및 회원 관리 비즈니스 로직을 처리. +- **TokenManager**: 토큰 생성 및 검증 기능 제공. +- **MemberService**: 회원 데이터 접근 및 관리. + +### Architecture & Implementation +- **구성 요소 상호 작용**: 클라이언트는 AuthController에 요청을 보내고, AuthController는 AuthService와 상호작용하여 비즈니스 로직을 처리합니다. AuthService는 TokenManager와 MemberService를 통해 토큰 관리 및 회원 정보를 처리합니다. +- **주요 흐름 및 프로세스**: 클라이언트의 요청은 AuthController를 통해 AuthService로 전달되고, 필요한 데이터는 TokenManager와 MemberService를 통해 처리됩니다. 최종적으로 응답은 클라이언트에게 반환됩니다. +`; diff --git a/src/constants/guides/KeywordController.js b/src/constants/guides/KeywordController.js new file mode 100644 index 0000000..4e835f2 --- /dev/null +++ b/src/constants/guides/KeywordController.js @@ -0,0 +1,201 @@ +export const KeywordController = ` +# 시스템 아키텍처 문서 + +## 전체 구조 +\`\`\`mermaid +graph TD + A[Client] --> B[KeywordController] + B --> C[KeywordService] + C --> D[KeywordRepository] + C --> D2[TripKeywordRepository] + C --> D3[TripRepository] + C --> E[TripsByStatisticsFinder] + C --> E2[RandomKeywordGeneratable] + D --> F[Database] + D2 --> F + D3 --> F +\`\`\` + +## 시스템 흐름 +\`\`\`mermaid +sequenceDiagram + Client->>KeywordController: Request + KeywordController->>KeywordService: Process + KeywordService->>KeywordRepository: Data Access + KeywordService->>TripKeywordRepository: Data Access + KeywordService->>TripRepository: Data Access + KeywordService->>TripsByStatisticsFinder: Process Statistics + KeywordService->>RandomKeywordGeneratable: Generate Random Keyword +\`\`\` + +## 주요 컴포넌트 설명 + +### KeywordController +- 역할과 책임: API 요청을 처리하고, 서비스 계층과의 상호작용을 통해 클라이언트에게 응답을 반환합니다. +- 주요 컨트롤러 목록: + - \`recommendTripsByKeywords\` + - \`findAllKeywords\` + - \`createKeyword\` + - \`createTripKeyword\` + - \`findTripsByRandomKeyword\` +- 공통 처리 로직: 모든 요청에 대해 인증을 수행합니다. + +### KeywordService +- 비즈니스 로직 구조: 키워드 관련 비즈니스 로직을 처리합니다. +- 주요 서비스 목록: + - \`findAllKeywords\` + - \`createKeyword\` + - \`findRecommendTripsByKeywords\` + - \`findRecommendTripsByRandomKeyword\` +- 트랜잭션 경계: 데이터베이스 변경이 필요한 메서드는 \`@Transactional\` 어노테이션을 사용하여 트랜잭션을 관리합니다. + +## API 엔드포인트 + +### 추천 여행지 조회 +**POST** \`/api/keyword/trip/recommend\` + +#### 설명 +주어진 키워드에 따라 추천 여행지를 조회합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| accessor | Accessor | Required | 인증 정보 | + +##### Request Body +\`\`\`json +{ + "keywordIds": [1, 2, 3] +} +\`\`\` + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "findTripResponses": [ + { + "trip": { + "id": 1, + "name": "Trip Name" + }, + "keywords": ["Keyword1", "Keyword2"] + } + ] +} +\`\`\` + +##### Error Response +- Status: 400 Bad Request +\`\`\`json +{ + "error": "일부 키워드가 존재하지 않습니다." +} +\`\`\` + +### 모든 키워드 조회 +**GET** \`/api/keyword\` + +#### 설명 +모든 키워드를 조회합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| accessor | Accessor | Required | 인증 정보 | + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "findAllKeywordResponses": [ + { + "keyword": "Keyword1" + }, + { + "keyword": "Keyword2" + } + ] +} +\`\`\` + +### 키워드 생성 +**POST** \`/api/keyword\` + +#### 설명 +새로운 키워드를 생성합니다. + +#### 요청 +##### Request Body +\`\`\`json +{ + "keyword": "New Keyword" +} +\`\`\` + +#### 응답 +##### Success Response +- Status: 204 No Content + +### 여행지 키워드 생성 +**POST** \`/api/keyword/trip\` + +#### 설명 +여행지에 키워드를 추가합니다. + +#### 요청 +##### Request Body +\`\`\`json +{ + "tripId": 1, + "keywordId": 2 +} +\`\`\` + +#### 응답 +##### Success Response +- Status: 204 No Content + +### 랜덤 키워드로 여행지 조회 +**GET** \`/api/keyword/random/trip\` + +#### 설명 +랜덤 키워드를 사용하여 추천 여행지를 조회합니다. + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "keywordName": "Random Keyword", + "findTripResponses": [ + { + "trip": { + "id": 1, + "name": "Trip Name" + }, + "keywords": ["Keyword1", "Keyword2"] + } + ] +} +\`\`\` + +## SUMMARY + +### Component Overview +- 주요 책임 및 목적: 키워드와 관련된 데이터의 생성, 조회 및 추천 기능을 제공합니다. +- 주요 기능 및 역량: 키워드 관리, 여행지 추천, 통계 기반 추천 기능을 포함합니다. + +### Architecture & Implementation +- 주요 구성 요소 및 역할: + - \`KeywordController\`: API 요청 처리 + - \`KeywordService\`: 비즈니스 로직 처리 + - \`KeywordRepository\`, \`TripKeywordRepository\`, \`TripRepository\`: 데이터 접근 + - \`TripsByStatisticsFinder\`, \`RandomKeywordGeneratable\`: 통계 및 랜덤 키워드 생성 +- 구성 요소 상호 작용 및 종속성: 컨트롤러는 서비스에 의존하고, 서비스는 여러 리포지토리에 의존합니다. +- 주요 흐름 및 프로세스: 클라이언트 요청 -> 컨트롤러 -> 서비스 -> 리포지토리 -> 데이터베이스. +`; diff --git a/src/constants/guides/LiveInformationController.js b/src/constants/guides/LiveInformationController.js new file mode 100644 index 0000000..e69de29 diff --git a/src/constants/guides/MemberController.js b/src/constants/guides/MemberController.js new file mode 100644 index 0000000..e1fd698 --- /dev/null +++ b/src/constants/guides/MemberController.js @@ -0,0 +1,208 @@ +export const MemberController = ` +# 시스템 아키텍처 문서 + +## 전체 구조 +\`\`\`mermaid +graph TD + A[Client] --> B[MemberController] + B --> C[MemberService] + C --> D[MemberRepository] + C --> E[LiveInformationRepository] + C --> F[MemberLiveInformationService] + C --> G[TripService] + C --> H[RecommendTripService] + D --> I[Database] + E --> I + F --> I + G --> I + H --> I +\`\`\` + +## 시스템 흐름 +\`\`\`mermaid +sequenceDiagram + Client->>MemberController: Request + MemberController->>MemberService: Process + MemberService->>MemberRepository: Data Access + MemberService->>LiveInformationRepository: Data Access + MemberService->>MemberLiveInformationService: Data Access + MemberService->>TripService: Data Access + MemberService->>RecommendTripService: Data Access + MemberRepository->>Database: Query + LiveInformationRepository->>Database: Query + MemberLiveInformationService->>Database: Query + TripService->>Database: Query + RecommendTripService->>Database: Query +\`\`\` + +## 주요 컴포넌트 설명 + +### MemberController +- 역할과 책임: 클라이언트의 요청을 처리하고, 적절한 서비스 메소드를 호출하여 응답을 반환합니다. +- 주요 메소드: + - \`getUserInfo()\`: 현재 사용자 정보를 조회합니다. + - \`signupProfile()\`: 프로필 정보를 등록합니다. + - \`signupLiveInfo()\`: 생활 정보를 등록합니다. + - \`signupInterestTrip()\`: 관심 여행지를 등록합니다. + - \`checkDuplicateNickname()\`: 닉네임 중복 여부를 확인합니다. + - \`updateMemberProfile()\`: 사용자 프로필을 업데이트합니다. + - \`findMemberAuthorityAndProfileImg()\`: 사용자 권한 및 프로필 이미지를 조회합니다. + +### MemberService +- 비즈니스 로직을 처리하며, 데이터베이스와의 상호작용을 관리합니다. +- 주요 메소드: + - \`findById()\`: ID로 회원을 조회합니다. + - \`signUpByProfile()\`: 프로필 정보를 등록합니다. + - \`signUpByLiveInfo()\`: 생활 정보를 등록합니다. + - \`signUpByInterestTrips()\`: 관심 여행지를 등록합니다. + - \`updateByProfile()\`: 프로필 정보를 업데이트합니다. + - \`checkIsAlreadyExistNickname()\`: 닉네임 중복 여부를 확인합니다. + - \`findMemberAuthorityAndProfileImg()\`: 사용자 권한 및 프로필 이미지를 조회합니다. + +### MemberRepository +- 회원 정보를 데이터베이스에 저장하고 조회하는 역할을 합니다. + +### LiveInformationRepository +- 생활 정보 데이터를 데이터베이스에 저장하고 조회하는 역할을 합니다. + +### MemberLiveInformationService +- 회원의 생활 정보를 관리하는 서비스입니다. + +### TripService +- 여행 관련 데이터를 관리하는 서비스입니다. + +### RecommendTripService +- 추천 여행지 관련 데이터를 관리하는 서비스입니다. + +## API 엔드포인트 + +### 사용자 정보 조회 +**GET** \`/api/member/me\` + +#### 설명 +현재 로그인한 사용자의 정보를 조회합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| - | - | - | - | + +##### Headers +| 이름 | 필수 여부 | 설명 | +|------|-----------|------| +| Authorization | Required | Bearer {token} | + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "id": 1, + "profileImageUrl": "http://example.com/image.jpg", + "nickname": "user123", + "birthday": "1990-01-01", + "genderType": "MALE" +} +\`\`\` + +##### Error Response +- Status: 404 Not Found +\`\`\`json +{ + "error": "존재하지 않는 회원입니다." +} +\`\`\` + +### 프로필 등록 +**POST** \`/api/member/signup/profile\` + +#### 설명 +사용자의 프로필 정보를 등록합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| - | - | - | - | + +##### Headers +| 이름 | 필수 여부 | 설명 | +|------|-----------|------| +| Authorization | Required | Bearer {token} | + +##### Request Body +\`\`\`json +{ + "nickname": "user123", + "birthday": "1990-01-01", + "genderType": "MALE" +} +\`\`\` + +#### 응답 +##### Success Response +- Status: 204 No Content + +##### Error Response +- Status: 400 Bad Request +\`\`\`json +{ + "error": "중복되는 닉네임이 존재합니다." +} +\`\`\` + +### 닉네임 중복 확인 +**POST** \`/api/member/check/nickname\` + +#### 설명 +닉네임의 중복 여부를 확인합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| - | - | - | - | + +##### Headers +| 이름 | 필수 여부 | 설명 | +|------|-----------|------| +| Authorization | Required | Bearer {token} | + +##### Request Body +\`\`\`json +{ + "nickname": "user123" +} +\`\`\` + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "message": "사용 가능한 닉네임입니다." +} +\`\`\` + +##### Error Response +- Status: 409 Conflict +\`\`\`json +{ + "error": "중복되는 닉네임이 존재합니다." +} +\`\`\` + +## SUMMARY + +### Component Overview +- **MemberController**: 클라이언트 요청을 처리하고, 서비스 메소드를 호출하여 응답을 반환합니다. +- **MemberService**: 비즈니스 로직을 처리하고, 데이터베이스와의 상호작용을 관리합니다. +- **Repositories**: 데이터베이스와의 CRUD 작업을 수행합니다. +- **DTOs**: 데이터 전송 객체로, 클라이언트와 서버 간의 데이터 전송을 담당합니다. + +### Architecture & Implementation +- **구성 요소 상호 작용**: 클라이언트는 \`MemberController\`에 요청을 보내고, \`MemberController\`는 \`MemberService\`를 호출하여 비즈니스 로직을 처리합니다. \`MemberService\`는 필요한 데이터를 \`MemberRepository\`, \`LiveInformationRepository\` 등에서 조회합니다. +- **주요 흐름 및 프로세스**: 클라이언트의 요청이 들어오면, 해당 요청에 맞는 메소드가 호출되고, 필요한 데이터가 처리된 후 응답이 반환됩니다. + +`; diff --git a/src/constants/guides/MemberLiveInformationController.js b/src/constants/guides/MemberLiveInformationController.js new file mode 100644 index 0000000..7aa238e --- /dev/null +++ b/src/constants/guides/MemberLiveInformationController.js @@ -0,0 +1,153 @@ +export const MemberLiveInformationController = ` +# 시스템 아키텍처 문서 + +## 전체 구조 +\`\`\`mermaid +graph TD + A[Client] --> B[Controller Layer] + B --> C[Service Layer] + C --> D[Repository Layer] + D --> E[Database] +\`\`\` + +## 시스템 흐름 +\`\`\`mermaid +sequenceDiagram + Client->>Controller: Request + Controller->>Service: Process + Service->>Repository: Data Access + Repository->>Database: Query +\`\`\` + +## 주요 컴포넌트 설명 + +### Controller Layer +- **역할과 책임**: 클라이언트의 요청을 처리하고, 서비스 레이어와 상호작용하여 비즈니스 로직을 실행합니다. +- **주요 컨트롤러 목록**: + - \`MemberLiveInformationController\`: 회원의 생활 정보를 조회하고 업데이트합니다. +- **공통 처리 로직**: 인증 정보(\`Accessor\`)를 요청 매개변수로 받아 사용합니다. + +### Service Layer +- **비즈니스 로직 구조**: 비즈니스 로직을 처리하고, 데이터베이스와의 상호작용을 관리합니다. +- **주요 서비스 목록**: + - \`MemberLiveInformationService\`: 회원의 생활 정보를 관리합니다. +- **트랜잭션 경계**: 데이터 업데이트 시 트랜잭션을 관리합니다. + +### Repository Layer +- **역할과 책임**: 데이터베이스와의 상호작용을 담당하며, CRUD 작업을 수행합니다. +- **주요 리포지토리 목록**: + - \`MemberLiveInformationRepository\` + - \`LiveInformationRepository\` + - \`MemberRepository\` + +### Database +- **역할과 책임**: 모든 데이터의 영속성을 보장합니다. + +## API 문서 + +### MemberLiveInformationController + +#### 개요 +- **컨트롤러 설명**: 회원의 생활 정보를 조회하고 업데이트하는 API를 제공합니다. +- **기본 URL 경로**: \`/api/live/info/member\` +- **공통 요청/응답 형식**: JSON 형식 + +#### API 엔드포인트 + +### 1. Find Member Live Information +**GET** \`/api/live/info/member\` + +#### 설명 +회원의 선택된 생활 정보를 조회합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|-----------|--------|-----------|---------------------| +| accessor | Accessor | Required | 인증된 회원 정보 | + +##### Headers +| 이름 | 필수 여부 | 설명 | +|---------------|-----------|--------------------------| +| Authorization | Required | Bearer {token} | + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "liveInfoResponses": [ + { + "id": 1, + "name": "Live Info 1", + "selected": true + }, + { + "id": 2, + "name": "Live Info 2", + "selected": false + } + ] +} +\`\`\` + +##### Error Response +- Status: 404 Not Found +\`\`\`json +{ + "error": "존재하지 않는 회원입니다." +} +\`\`\` + +### 2. Update Member Live Information +**PUT** \`/api/live/info/member\` + +#### 설명 +회원의 생활 정보를 업데이트합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|-----------|--------|-----------|---------------------| +| accessor | Accessor | Required | 인증된 회원 정보 | + +##### Request Body +\`\`\`json +{ + "liveInfoIds": [1, 2, 3] +} +\`\`\` + +#### 응답 +##### Success Response +- Status: 204 No Content + +##### Error Response +- Status: 400 Bad Request +\`\`\`json +{ + "error": "업데이트 할 생활정보는 비어있을 수 없습니다." +} +\`\`\` +- Status: 404 Not Found +\`\`\`json +{ + "error": "존재하지 않는 회원입니다." +} +\`\`\` + +## SUMMARY + +### Component Overview +- **주요 책임 및 목적**: 클라이언트 요청을 처리하고, 비즈니스 로직을 실행하며, 데이터베이스와의 상호작용을 관리합니다. +- **주요 기능 및 역량**: 회원의 생활 정보를 조회하고 업데이트하는 기능을 제공합니다. + +### Architecture & Implementation +- **주요 구성 요소 및 역할**: + - \`MemberLiveInformationController\`: 클라이언트 요청을 처리합니다. + - \`MemberLiveInformationService\`: 비즈니스 로직을 처리합니다. + - \`MemberLiveInformationRepository\`, \`LiveInformationRepository\`, \`MemberRepository\`: 데이터베이스와의 상호작용을 담당합니다. +- **구성 요소 상호 작용 및 종속성**: 컨트롤러는 서비스에 의존하고, 서비스는 리포지토리에 의존합니다. +- **주요 흐름 및 프로세스**: 클라이언트 요청 → 컨트롤러 → 서비스 → 리포지토리 → 데이터베이스. + +`; diff --git a/src/constants/guides/PlannerController.js b/src/constants/guides/PlannerController.js new file mode 100644 index 0000000..e13e22e --- /dev/null +++ b/src/constants/guides/PlannerController.js @@ -0,0 +1,210 @@ +export const PlannerController = ` +# 시스템 아키텍처 + +## 전체 구조 +\`\`\`mermaid +graph TD + A[Client] --> B[Controller Layer] + B --> C[Service Layer] + C --> D[Repository Layer] + D --> E[Database] +\`\`\` + +## 시스템 흐름 +\`\`\`mermaid +sequenceDiagram + Client->>Controller: Request + Controller->>Service: Process + Service->>Repository: Data Access + Repository->>Database: Query +\`\`\` + +## 주요 컴포넌트 설명 +### Controller Layer +- **역할과 책임**: 클라이언트의 요청을 처리하고, 서비스 레이어와의 상호작용을 통해 비즈니스 로직을 수행합니다. +- **주요 컨트롤러 목록**: + - \`PlannerController\`: 여행 일정 관련 API를 제공합니다. +- **공통 처리 로직**: \`@Authentication\` 어노테이션을 통해 인증된 사용자의 정보를 가져옵니다. + +### Service Layer +- **비즈니스 로직 구조**: 비즈니스 로직을 구현하며, 데이터베이스와의 상호작용을 위한 리포지토리 호출을 포함합니다. +- **주요 서비스 목록**: + - \`PlannerService\`: 여행 일정 관련 비즈니스 로직을 처리합니다. +- **트랜잭션 경계**: \`@Transactional\` 어노테이션을 사용하여 트랜잭션을 관리합니다. + +## API 문서 + +# Planner API + +## 개요 +- **컨트롤러 설명**: 여행 일정 관련 API를 제공하는 컨트롤러입니다. +- **기본 URL 경로**: \`/api/planner\` +- **공통 요청/응답 형식**: JSON 형식의 요청 및 응답을 사용합니다. + +## API 엔드포인트 + +### 최근 여행 일정 조회 +**GET** \`/api/planner/recent\` + +#### 설명 +최근 여행 일정을 조회합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| 없음 | - | - | - | + +##### Headers +| 이름 | 필수 여부 | 설명 | +|------|-----------|------| +| Authorization | Required | Bearer {token} | + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "tripSchedules": [ + { + "id": 1, + "name": "여행 일정 1", + "startDate": "2023-10-01", + "endDate": "2023-10-05" + } + ] +} +\`\`\` + +##### Error Response +- Status: 401 Unauthorized +\`\`\`json +{ + "error": "인증이 필요합니다." +} +\`\`\` + +### 여행 일정 이름으로 조회 +**GET** \`/api/planner/name\` + +#### 설명 +이름으로 여행 일정을 조회합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| 없음 | - | - | - | + +##### Headers +| 이름 | 필수 여부 | 설명 | +|------|-----------|------| +| Authorization | Required | Bearer {token} | + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "tripSchedules": [ + { + "id": 2, + "name": "여행 일정 2", + "startDate": "2023-10-10", + "endDate": "2023-10-15" + } + ] +} +\`\`\` + +##### Error Response +- Status: 404 Not Found +\`\`\`json +{ + "error": "여행 일정을 찾을 수 없습니다." +} +\`\`\` + +### 여행 일정 업데이트 +**PUT** \`/api/planner/schedule\` + +#### 설명 +여행 일정을 업데이트합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| 없음 | - | - | - | + +##### Headers +| 이름 | 필수 여부 | 설명 | +|------|-----------|------| +| Authorization | Required | Bearer {token} | + +##### Request Body +\`\`\`json +{ + "scheduleId": 1, + "scheduleName": "업데이트된 여행 일정", + "startDate": "2023-10-01", + "endDate": "2023-10-05" +} +\`\`\` + +#### 응답 +##### Success Response +- Status: 204 No Content + +##### Error Response +- Status: 400 Bad Request +\`\`\`json +{ + "error": "잘못된 요청입니다." +} +\`\`\` + +### 여행 일정 삭제 +**DELETE** \`/api/planner/schedule/{scheduleId}\` + +#### 설명 +여행 일정을 삭제합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| scheduleId | Long | Required | 삭제할 여행 일정의 ID | + +##### Headers +| 이름 | 필수 여부 | 설명 | +|------|-----------|------| +| Authorization | Required | Bearer {token} | + +#### 응답 +##### Success Response +- Status: 204 No Content + +##### Error Response +- Status: 404 Not Found +\`\`\`json +{ + "error": "존재하지 않는 여행 일정입니다." +} +\`\`\` + +## SUMMARY + +### Component Overview +- **주요 책임 및 목적**: 클라이언트의 요청을 처리하고, 비즈니스 로직을 수행하여 데이터베이스와 상호작용합니다. +- **주요 기능 및 역량**: 여행 일정의 생성, 조회, 업데이트, 삭제 기능을 제공합니다. + +### Architecture & Implementation +- **주요 구성 요소 및 역할**: + - \`PlannerController\`: API 요청을 처리합니다. + - \`PlannerService\`: 비즈니스 로직을 구현합니다. + - \`TripScheduleRepository\`: 데이터베이스와의 상호작용을 담당합니다. +- **구성 요소 상호 작용 및 종속성**: 컨트롤러는 서비스에 의존하며, 서비스는 리포지토리에 의존합니다. +- **주요 흐름 및 프로세스**: 클라이언트 요청 -> 컨트롤러 처리 -> 서비스 로직 실행 -> 데이터베이스 쿼리 -> 응답 반환. + +`; diff --git a/src/constants/guides/RecommendTripController.js b/src/constants/guides/RecommendTripController.js new file mode 100644 index 0000000..216924f --- /dev/null +++ b/src/constants/guides/RecommendTripController.js @@ -0,0 +1,152 @@ +export const RecommendTripController = ` +# 시스템 아키텍처 문서 + +## 전체 구조 +\`\`\`mermaid +graph TD + A[Client] --> B[Controller Layer] + B --> C[Service Layer] + C --> D[Repository Layer] + D --> E[Database] +\`\`\` + +## 시스템 흐름 +\`\`\`mermaid +sequenceDiagram + Client->>Controller: Request + Controller->>Service: Process + Service->>Repository: Data Access + Repository->>Database: Query +\`\`\` + +## 주요 컴포넌트 설명 + +### Controller Layer +- **역할과 책임**: 클라이언트의 요청을 처리하고, 서비스 계층과의 상호작용을 통해 비즈니스 로직을 수행합니다. +- **주요 컨트롤러 목록**: + - \`RecommendTripController\`: 추천 여행지 생성 및 조회 기능을 제공합니다. +- **공통 처리 로직**: 인증 정보를 처리하기 위해 \`@Authentication\` 어노테이션을 사용합니다. + +### Service Layer +- **비즈니스 로직 구조**: 추천 여행지 생성 및 필터링 전략을 통해 여행지를 추천하는 비즈니스 로직을 포함합니다. +- **주요 서비스 목록**: + - \`RecommendTripService\`: 추천 여행지 저장 및 생성 기능을 제공합니다. +- **트랜잭션 경계**: \`@Transactional\` 어노테이션을 사용하여 데이터베이스 트랜잭션을 관리합니다. + +### Repository Layer +- **역할과 책임**: 데이터베이스와의 상호작용을 통해 데이터를 저장하고 조회합니다. +- **주요 리포지토리 목록**: + - \`RecommendTripRepository\`: 추천 여행지 관련 데이터 접근을 담당합니다. + - \`MemberRepository\`: 회원 관련 데이터 접근을 담당합니다. + - \`TripRepository\`: 여행지 관련 데이터 접근을 담당합니다. + - \`TripKeywordRepository\`: 여행지 키워드 관련 데이터 접근을 담당합니다. + +### Domain Layer +- **역할과 책임**: 비즈니스 도메인 모델을 정의하고, 필터링 전략 및 추천 로직을 구현합니다. +- **주요 도메인 클래스**: + - \`RecommendTrip\`: 추천 여행지 정보를 담고 있는 엔티티입니다. + - \`PreferredLocationsFilterInfo\`: 선호하는 위치에 대한 필터 정보를 담고 있습니다. + - \`TripFilterStrategy\`: 여행지 필터링 전략의 인터페이스입니다. + +## API 문서 + +### 추천 여행지 생성 API +**POST** \`/api/recommend\` + +#### 설명 +추천 여행지를 생성합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| accessor | Accessor | Required | 인증된 사용자 정보 | + +##### Headers +| 이름 | 필수 여부 | 설명 | +|------|-----------|------| +| Authorization | Required | Bearer {token} | + +##### Request Body +\`\`\`json +{ + "tripId": 1 +} +\`\`\` + +#### 응답 +##### Success Response +- Status: 204 No Content + +##### Error Response +- Status: 400 Bad Request +\`\`\`json +{ + "error": "Null 일 수 없습니다." +} +\`\`\` +- Status: 404 Not Found +\`\`\`json +{ + "error": "존재하지 않는 회원입니다." +} +\`\`\` + +### 추천 여행지 조회 API +**GET** \`/api/recommend\` + +#### 설명 +사용자의 선호 위치에 따라 추천 여행지를 조회합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| accessor | Accessor | Required | 인증된 사용자 정보 | + +##### Headers +| 이름 | 필수 여부 | 설명 | +|------|-----------|------| +| Authorization | Required | Bearer {token} | + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "findTripResponses": [ + { + "trip": { + "id": 1, + "name": "여행지 이름" + }, + "keywords": ["키워드1", "키워드2"] + } + ] +} +\`\`\` + +##### Error Response +- Status: 404 Not Found +\`\`\`json +{ + "error": "추천 여행지를 찾을 수 없습니다." +} +\`\`\` + +## SUMMARY + +### Component Overview +- **주요 책임 및 목적**: 사용자 인증을 통해 추천 여행지를 생성하고 조회하는 기능을 제공합니다. +- **주요 기능 및 역량**: 추천 여행지 생성, 사용자 선호 기반 여행지 필터링 및 조회 기능을 포함합니다. + +### Architecture & Implementation +- **주요 구성 요소 및 역할**: + - \`RecommendTripController\`: 클라이언트 요청을 처리하고, 서비스 계층과 상호작용합니다. + - \`RecommendTripService\`: 비즈니스 로직을 처리합니다. + - \`TripFilterStrategyProvider\`: 필터링 전략을 제공하고 실행합니다. + - \`TripsWithKeywordProvider\`: 추천 여행지와 관련된 키워드를 조회합니다. +- **구성 요소 상호 작용 및 종속성**: 컨트롤러는 서비스에 의존하고, 서비스는 여러 리포지토리와 필터링 전략 제공자에 의존합니다. +- **주요 흐름 및 프로세스**: 클라이언트 요청 → 컨트롤러 → 서비스 → 리포지토리 → 데이터베이스 쿼리. + +`; diff --git a/src/constants/guides/TripController.js b/src/constants/guides/TripController.js new file mode 100644 index 0000000..60cdaf6 --- /dev/null +++ b/src/constants/guides/TripController.js @@ -0,0 +1,192 @@ +export const TripController = ` +# 시스템 아키텍처 문서 + +## 전체 구조 +\`\`\`mermaid +graph TD + A[Client] --> B[Controller Layer] + B --> C[Service Layer] + C --> D[Repository Layer] + D --> E[Database] +\`\`\` + +## 시스템 흐름 +\`\`\`mermaid +sequenceDiagram + Client->>Controller: Request + Controller->>Service: Process + Service->>Repository: Data Access + Repository->>Database: Query +\`\`\` + +## 주요 컴포넌트 설명 + +### Controller Layer +- **역할과 책임**: 클라이언트의 요청을 처리하고, 서비스 레이어와 상호작용하여 비즈니스 로직을 실행합니다. +- **주요 컨트롤러 목록**: + - \`TripController\`: 여행 관련 API를 처리합니다. +- **공통 처리 로직**: 요청 매핑, 응답 생성 및 상태 코드 반환. + +### Service Layer +- **비즈니스 로직 구조**: 비즈니스 로직을 처리하며, 데이터 접근을 위한 리포지토리와 상호작용합니다. +- **주요 서비스 목록**: + - \`TripService\`: 여행 관련 비즈니스 로직을 처리합니다. +- **트랜잭션 경계**: @Transactional 어노테이션을 사용하여 트랜잭션을 관리합니다. + +## API 문서 + +### Trip API + +#### 개요 +- **컨트롤러 설명**: 여행 관련 API를 제공하는 컨트롤러입니다. +- **기본 URL 경로**: \`/api/trip\` +- **공통 요청/응답 형식**: JSON 형식의 요청 및 응답을 사용합니다. + +#### API 엔드포인트 + +### 1. Create Trip +**POST** \`/api/trip\` + +#### 설명 +여행지를 생성합니다. + +#### 요청 +##### Request Body +\`\`\`json +{ + "name": "여행지 이름", + "placeName": "여행지 장소명", + "contentId": 123, + "description": "여행지 설명", + "tripImageUrl": "이미지 URL" +} +\`\`\` + +#### 응답 +##### Success Response +- Status: 204 No Content + +##### Error Response +- Status: 400 Bad Request +\`\`\`json +{ + "error": "여행지 이름은 공백일 수 없습니다." +} +\`\`\` + +### 2. Find Top Trips +**GET** \`/api/trip/find/interested\` + +#### 설명 +방문 수에 따라 상위 30개의 여행지를 조회합니다. + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "findTripResponses": [ + { + "trip": { + "id": 1, + "name": "여행지 이름", + "placeName": "여행지 장소명", + "contentId": 123, + "description": "여행지 설명", + "tripImageUrl": "이미지 URL" + }, + "keywords": ["키워드1", "키워드2"] + } + ] +} +\`\`\` + +### 3. Find Trip with Similar Trips +**GET** \`/api/trip/find/{tripId}\` + +#### 설명 +특정 여행지와 유사한 여행지를 조회합니다. + +#### 요청 +##### Path Variables +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| tripId | long | Required | 여행지 ID | + +##### Headers +| 이름 | 필수 여부 | 설명 | +|------|-----------|------| +| Authorization | Required | Bearer {token} | + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "findTripResponse": { + "trip": { + "id": 1, + "name": "여행지 이름", + "placeName": "여행지 장소명", + "contentId": 123, + "description": "여행지 설명", + "tripImageUrl": "이미지 URL" + }, + "keywords": ["키워드1", "키워드2"] + }, + "similarTripResponses": { + "trips": [ + { + "id": 2, + "name": "유사 여행지 이름", + "placeName": "유사 여행지 장소명" + } + ] + } +} +\`\`\` + +### 4. Create Member Trip +**POST** \`/api/trip/member/{tripId}\` + +#### 설명 +특정 여행지를 회원의 여행 목록에 추가합니다. + +#### 요청 +##### Path Variables +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| tripId | long | Required | 여행지 ID | + +##### Headers +| 이름 | 필수 여부 | 설명 | +|------|-----------|------| +| Authorization | Required | Bearer {token} | + +#### 응답 +##### Success Response +- Status: 204 No Content + +##### Error Response +- Status: 404 Not Found +\`\`\`json +{ + "error": "여행지가 존재하지 않습니다." +} +\`\`\` + +## SUMMARY + +### Component Overview +- **주요 책임 및 목적**: 각 레이어는 명확한 책임을 가지고 있으며, 클라이언트의 요청을 처리하고 비즈니스 로직을 실행합니다. +- **주요 기능 및 역량**: 여행지 생성, 조회, 추천 기능을 포함합니다. + +### Architecture & Implementation +- **주요 구성 요소 및 역할**: + - \`TripController\`: API 요청 처리 + - \`TripService\`: 비즈니스 로직 처리 + - \`TripRepository\`: 데이터 접근 +- **구성 요소 상호 작용 및 종속성**: 컨트롤러는 서비스에 의존하고, 서비스는 리포지토리에 의존합니다. +- **주요 흐름 및 프로세스**: 클라이언트 요청 -> 컨트롤러 -> 서비스 -> 리포지토리 -> 데이터베이스. + +`; diff --git a/src/constants/guides/docsGuide.js b/src/constants/guides/docsGuide.js new file mode 100644 index 0000000..1e1ea47 --- /dev/null +++ b/src/constants/guides/docsGuide.js @@ -0,0 +1,55 @@ +import { AuthController } from './AuthController.js'; +import { KeywordController } from './KeywordController.js'; +import { LiveInformationController } from './LiveInformationController.js'; +import { MemberController } from './MemberController.js'; +import { MemberLiveInformationController } from './MemberLiveInformationController.js'; +import { PlannerController } from './PlannerController.js'; +import { RecommendTripController } from './RecommendTripController.js'; +import { TripController } from './TripController.js'; + +export const docsGuideText = { + regularFiles: [ + { + fileName: 'AuthController.md', + fileContents: AuthController, + }, + { + fileName: 'KeywordController.md', + fileContents: KeywordController, + }, + { + fileName: 'LiveInformationController.md', + fileContents: LiveInformationController, + }, + { + fileName: 'MemberController.md', + fileContents: MemberController, + }, + { + fileName: 'MemberLiveInformationController.md', + fileContents: MemberLiveInformationController, + }, + { + fileName: 'PlannerController.md', + fileContents: PlannerController, + }, + { + fileName: 'RecommendTripController.md', + fileContents: RecommendTripController, + }, + { + fileName: 'TripController.md', + fileContents: TripController, + }, + ], + summaryFiles: [ + { + fileName: 'Controller_Summary.md', + fileContents: '전체 컨트롤러 요약 내용', + }, + { + fileName: 'Service_Summary.md', + fileContents: '전체 서비스 요약 내용', + }, + ], +}; diff --git a/src/hooks/RepoDetailContent/useDocument.js b/src/hooks/RepoDetailContent/useDocument.js index 3746ac0..ebb61a6 100644 --- a/src/hooks/RepoDetailContent/useDocument.js +++ b/src/hooks/RepoDetailContent/useDocument.js @@ -1,6 +1,7 @@ // src/hooks/RepoDetailContent/useDocument.js import { useQuery } from '@tanstack/react-query'; import { downloadAPI } from '../../api/index.js'; +import { docsGuideText } from '../../constants/guides/docsGuide.js'; export const useDocument = (registeredRepoId) => { console.log('useDocument hook called with ID:', registeredRepoId); @@ -11,6 +12,11 @@ export const useDocument = (registeredRepoId) => { if (!registeredRepoId) { throw new Error('Repository ID is required'); } + if (registeredRepoId === 'guide') { + console.log('Returning readme guide'); + return docsGuideText; + } + console.log('Fetching data from documentAPI'); const response = await downloadAPI.postDownloadDocs(registeredRepoId); console.log('API Response:', response); return response; diff --git a/src/hooks/useRepoManagement.js b/src/hooks/useRepoManagement.js index d003c30..7ba5c6a 100644 --- a/src/hooks/useRepoManagement.js +++ b/src/hooks/useRepoManagement.js @@ -200,7 +200,6 @@ export const useRepoManagement = () => { type: 'active', exact: true, }); - modalHandlers.addRepo.close(); // 폴링 상태 초기화 및 시작 setPollingStartTime(Date.now()); @@ -209,6 +208,7 @@ export const useRepoManagement = () => { console.log('✅ Repository added and list refreshed successfully:', newRepo); setExtendedLoading(false); + modalHandlers.addRepo.close(); // 모든 데이터 작업이 완료된 후 모달 닫기 } catch (error) { console.error('Failed to refresh repository list:', error); @@ -295,21 +295,6 @@ export const useRepoManagement = () => { [deleteRepoModal, setRepositoryToRemove], ), - // handleConfirmDelete: useCallback(async () => { - // try { - // const success = deleteRepo(); - // if (success) { - // // React Query 캐시 갱신 - // queryClient.invalidateQueries({ queryKey: ['repositories'] }); - // deleteRepoModal.closeModal(); - // // TODO: 성공 토스트 메시지 표시 - // console.log('Repository deleted successfully'); - // } - // } catch (error) { - // console.error('Failed to delete repository:', error); - // // TODO: 에러 토스트 메시지 표시 - // } - // }, [deleteRepoModal, deleteRepo, queryClient]), handleConfirmDelete: useCallback(async () => { console.log(repositoryToRemove); if (!repositoryToRemove) { diff --git a/src/layouts/Header/header.jsx b/src/layouts/Header/header.jsx index 4024dab..16cd3c3 100644 --- a/src/layouts/Header/header.jsx +++ b/src/layouts/Header/header.jsx @@ -104,9 +104,9 @@ const HomeHeader = ({ role }) => { navigate("/")} fontFamily={'Roboto'} weight={100} size={'32px'} cursor={"pointer"} $isGradient>Dododocs - navigate("/docs")}>AI Code document - navigate("/chatting")} >AI chatting - navigate("/chatting")} >Read Me Editor + navigate("/landing/docs")}>AI Code document + navigate("/landing/chatting")} >AI chatting + navigate("/landing/readme")} >Read Me Editor diff --git a/src/pages/LandingPage/index.jsx b/src/pages/LandingPage/index.jsx new file mode 100644 index 0000000..b5e0eef --- /dev/null +++ b/src/pages/LandingPage/index.jsx @@ -0,0 +1,16 @@ + +import React from "react"; +import { Header } from "../../layouts/index.js" +import { LandingContent } from "../../components/index.js" +const Landing = () => { + + return ( + <> +
+ + + + ); +}; + +export default Landing; diff --git a/src/router/LandingRouter.jsx b/src/router/LandingRouter.jsx new file mode 100644 index 0000000..69a2a52 --- /dev/null +++ b/src/router/LandingRouter.jsx @@ -0,0 +1,9 @@ +import React from 'react'; +import Landing from "../pages/LandingPage/index.jsx" +const LandingRouter = () => { + return ( + + ); +}; + +export default LandingRouter; diff --git a/src/router/index.jsx b/src/router/index.jsx index 98dcf78..3f30fc0 100644 --- a/src/router/index.jsx +++ b/src/router/index.jsx @@ -10,6 +10,7 @@ import Login from "./LoginRouter.jsx" import Chatting from "./ChattingRouter.jsx"; import OAuthCallback from "./OAuthCallbackRouter.jsx" import Repo from "./RepoRouter.jsx" +import Landing from "./LandingRouter.jsx" // 모달용 컴포넌트 import { RepoDetailContent } from "../components" @@ -33,11 +34,18 @@ function App() { {/* Loading...}> */} } /> + } /> + } /> } /> } /> } /> } /> } /> + {/* } + /> */} } /> } /> From e616c7adffea2b27cea7ebf908e7238d0fa3c2cf Mon Sep 17 00:00:00 2001 From: euncherry Date: Mon, 23 Dec 2024 06:00:20 +0900 Subject: [PATCH 05/27] =?UTF-8?q?=EB=9E=9C=EB=94=A9=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 6 +- src/components/atoms/Button/index.jsx | 2 +- src/components/atoms/MarkdownEditor/index.jsx | 1 + .../atoms/MarkdownRenderer/markdown-it.jsx | 1 - src/components/index.js | 1 - .../CollectionsContent/CollectionsContent.jsx | 85 -- .../CollectionsContent.styles.js | 35 - .../DashboardContent/DashboardContent.jsx | 47 -- .../DashboardContent.styles.js | 196 ----- .../DashboardContent/ParticleBackground.jsx | 242 ------ .../organisms/DashboardContent/particle.jsx | 448 ---------- .../ChattingLanding/ChattingLanding.jsx | 670 +++++++++++---- .../Landing/ReadmeLanding/ReadmeLanding.jsx | 77 +- .../ReadmeLanding/ReadmeLanding.styles.js | 111 ++- .../organisms/MainContent/MainContent.jsx | 90 -- .../MainContent/MainContent.styles.js | 97 --- .../organisms/OAuthCallback/OAuthCallback.jsx | 2 +- .../CattingDetailContent/Chatting.jsx | 322 ++++++++ .../CattingDetailContent/Chatting.styles.js | 410 ++++++++++ .../organisms/RepoDetailContent/Chatting.jsx | 705 ---------------- .../RepoDetailContent/RepoDetailContent.jsx | 2 +- .../organisms/SearchContent/index.jsx | 105 --- .../guides/LiveInformationController.js | 171 ++++ src/router/index.jsx | 1 - src/utils/jwtUtils.js | 12 +- yarn.lock | 770 ++---------------- 26 files changed, 1629 insertions(+), 2980 deletions(-) delete mode 100644 src/components/organisms/CollectionsContent/CollectionsContent.jsx delete mode 100644 src/components/organisms/CollectionsContent/CollectionsContent.styles.js delete mode 100644 src/components/organisms/DashboardContent/DashboardContent.jsx delete mode 100644 src/components/organisms/DashboardContent/DashboardContent.styles.js delete mode 100644 src/components/organisms/DashboardContent/ParticleBackground.jsx delete mode 100644 src/components/organisms/DashboardContent/particle.jsx delete mode 100644 src/components/organisms/MainContent/MainContent.jsx delete mode 100644 src/components/organisms/MainContent/MainContent.styles.js create mode 100644 src/components/organisms/RepoDetailContent/CattingDetailContent/Chatting.jsx create mode 100644 src/components/organisms/RepoDetailContent/CattingDetailContent/Chatting.styles.js delete mode 100644 src/components/organisms/RepoDetailContent/Chatting.jsx delete mode 100644 src/components/organisms/SearchContent/index.jsx diff --git a/package.json b/package.json index 69caf29..0ee1b1c 100644 --- a/package.json +++ b/package.json @@ -10,15 +10,12 @@ "@testing-library/jest-dom": "^5.14.1", "@testing-library/react": "^13.0.0", "@testing-library/user-event": "^13.2.1", - "antd": "^5.21.5", "axios": "^1.7.7", "highlight.js": "^11.10.0", "jwt-decode": "^4.0.0", "lucide-react": "^0.454.0", "markdown-it": "^14.1.0", "markdown-it-anchor": "^9.2.0", - "markdown-it-mermaid": "^0.2.5", - "markdown-it-toc-done-right": "^4.2.0", "mermaid": "^11.4.1", "normalize.css": "^8.0.1", "react": "^18.3.1", @@ -60,6 +57,7 @@ ] }, "devDependencies": { - "@babel/plugin-proposal-private-property-in-object": "^7.21.11" + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "source-map-explorer": "^2.5.3" } } diff --git a/src/components/atoms/Button/index.jsx b/src/components/atoms/Button/index.jsx index 4136452..29ff2d2 100644 --- a/src/components/atoms/Button/index.jsx +++ b/src/components/atoms/Button/index.jsx @@ -21,7 +21,7 @@ const SIZE_STYLES = { const BUTTON_VARIANTS = { gradient: css` background:${({ theme }) => theme.gradients.values.primary}; - /* background : linear-gradient(to right, #a25cff, #d923ff); */ + /* background : linear-gradient(to right, rgb(162, 92, 255), #d923ff); */ border: 0 solid transparent; border-radius: 8px; color: #FFFFFF; diff --git a/src/components/atoms/MarkdownEditor/index.jsx b/src/components/atoms/MarkdownEditor/index.jsx index c09d9a2..9413202 100644 --- a/src/components/atoms/MarkdownEditor/index.jsx +++ b/src/components/atoms/MarkdownEditor/index.jsx @@ -1,4 +1,5 @@ import React, { useState, useEffect } from 'react'; +// @ts-ignore import MarkdownIt from 'markdown-it'; import styled from 'styled-components'; import mermaid from 'mermaid'; diff --git a/src/components/atoms/MarkdownRenderer/markdown-it.jsx b/src/components/atoms/MarkdownRenderer/markdown-it.jsx index b9bd611..80b0471 100644 --- a/src/components/atoms/MarkdownRenderer/markdown-it.jsx +++ b/src/components/atoms/MarkdownRenderer/markdown-it.jsx @@ -1,6 +1,5 @@ import React, { useEffect, useRef } from "react"; import MarkdownIt from "markdown-it"; -import markdownItMermaid from "markdown-it-mermaid"; import styled from "styled-components"; import mermaid from "mermaid"; diff --git a/src/components/index.js b/src/components/index.js index a88ece7..7b43753 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -35,5 +35,4 @@ export { default as OAuthCallback } from './organisms/OAuthCallback/OAuthCallbac export { default as ChattingContent } from './organisms/ChattingContent/ChattingContent.jsx'; export { default as RepoContent } from './organisms/RepoContent/RepoContent.jsx'; export { default as RepoDetailContent } from './organisms/RepoDetailContent/RepoDetailContent.jsx'; -export { default as DashboardContent } from './organisms/DashboardContent/DashboardContent.jsx'; export { default as LandingContent } from './organisms/Landing/LandingContent.jsx'; diff --git a/src/components/organisms/CollectionsContent/CollectionsContent.jsx b/src/components/organisms/CollectionsContent/CollectionsContent.jsx deleted file mode 100644 index 4050340..0000000 --- a/src/components/organisms/CollectionsContent/CollectionsContent.jsx +++ /dev/null @@ -1,85 +0,0 @@ -import React from 'react'; -import { ContentStyle, Row, Col } from "../../../layout/index.js" -import { Image, Typo } from "../../atoms/index.js" -import { useCategories, useQueryParams } from "../../../hooks/index.js" -import { CategoriesBox, CateCol } from "./CollectionsContent.styles.js" - - -const HomeContent = ({ }) => { - console.log('Effect 실행'); - - const { categories, categoriesValues, getCategoryKorName } = useCategories(); - - // useMemo를 사용하여 값을 메모이제이션 - const memoizedCategories = React.useMemo(() => categories, [categories]); - const memoizedCategoriesValues = React.useMemo(() => categoriesValues, [categoriesValues]); - - // useEffect를 사용한 로깅 - React.useEffect(() => { - console.log(memoizedCategoriesValues.length); - console.log(memoizedCategories['SNACK']); - }, [memoizedCategoriesValues, memoizedCategories]); - - - //SECTION params로 category가져오기 - //TODO - const { getParam, getAllParams } = useQueryParams(); - - const allParams = getAllParams(); - - console.log('allParams', allParams) - //!SECTION params로 category가져오기 - - return ( - <> - - - - {/* //SECTION Title */} - - 전체 상품 - - - {/* //!SECTION Title */} - - {/* //SECTION Categories */} - - - - - { - memoizedCategoriesValues.map((category, index) => ( - // - - {getCategoryKorName(category)} - {memoizedCategories[category].map((subCategory, index) => ( - {getCategoryKorName(subCategory)} - ))} - - // - )) - - } - - - - - {/* //!SECTION Categories */} - - {/* //SECTION Content*/} - - - 등록된 상품이 없습니다. - - - {/* //!SECTION Content*/} - - - - - - ) -} - -export default React.memo(HomeContent); - diff --git a/src/components/organisms/CollectionsContent/CollectionsContent.styles.js b/src/components/organisms/CollectionsContent/CollectionsContent.styles.js deleted file mode 100644 index 303e562..0000000 --- a/src/components/organisms/CollectionsContent/CollectionsContent.styles.js +++ /dev/null @@ -1,35 +0,0 @@ -import styled from 'styled-components'; - -export const CateCol = styled.div` - border: 1px solid rgb(226, 226, 226); - padding: 2.5rem 2.8rem; - flex-basis: 83.33%; - display: flex; - box-sizing: border-box; - flex-wrap: wrap; - @media (max-width: 576px) { - padding: 1.3rem 1rem; - flex-basis: 90%; - } -`; - -export const CategoriesBox = styled.div` - display: flex; - flex: 1; - gap: 1rem 0; - flex-direction: column; - position: relative; - justify-content: center; - align-items: center; - - &:not(:last-child)::after { - content: ''; - position: absolute; - right: 0; - top: 50%; - transform: translateY(-50%); - height: 35%; /* 세로선의 길이를 조절할 수 있습니다 */ - width: 1px; - background-color: rgb(226, 226, 226); /* 세로선의 색상 */ - } -`; diff --git a/src/components/organisms/DashboardContent/DashboardContent.jsx b/src/components/organisms/DashboardContent/DashboardContent.jsx deleted file mode 100644 index 6ec9a35..0000000 --- a/src/components/organisms/DashboardContent/DashboardContent.jsx +++ /dev/null @@ -1,47 +0,0 @@ -import React, { useState } from 'react'; -import { Image, RotateTypo, Button, Typo } from "../../index.js" -import { useNavigate } from 'react-router-dom'; - -import { Row, Col } from "../../../layouts/index.js" -import styled from 'styled-components'; -import mainBannerImg from "../../../assets/images/landing-hero-min.png"; -import bgShapeFive from "../../../assets/images/bg-shape-five.png"; -import bgShapeFour from "../../../assets/images/bg-shape-four.png"; -import { Check } from "lucide-react" - - -import { - HomeWrapper, MainSectionWrapper, HomeLayout, - TextSectionWrapper, TextContainer, MainText, SubText, - MainImageSectionWrapper, BgShape, MainImageFrame, - MainFeatureSectionWrapper, MainFeatureSection, FeatureContentText, FeatureContentImage -} from "./DashboardContent.styles.js" - - -import ParticleBackground from './ParticleBackground.jsx'; -import Particle from "./particle.jsx"; -const DashboardContent = () => { - const navigate = useNavigate(); - - const handleStartClick = () => { - console.log("dkjdkd") - return navigate('/login') - } - - - return ( - <> - - - {/* */} - - {/* - - - */} - - - ) -} - -export default DashboardContent; diff --git a/src/components/organisms/DashboardContent/DashboardContent.styles.js b/src/components/organisms/DashboardContent/DashboardContent.styles.js deleted file mode 100644 index db92a91..0000000 --- a/src/components/organisms/DashboardContent/DashboardContent.styles.js +++ /dev/null @@ -1,196 +0,0 @@ -// src/components/organisms/HomeContent/HomeContent.styles.js -import styled from 'styled-components'; -import bgBannerImg from '../../../assets/images/main-banner-bg.cb7bf167.png'; -import bgSliderBgImg from '../../../assets/images/slider-main-bg.png'; -import _ from '../../../config/index.js'; - -export const HomeWrapper = styled.div` - width: calc(100dvw - (100dvw - 100%)); - height: auto; - padding: 0 0 100px 0; - - display: flex; - flex-direction: column; - gap: clamp(2rem, 5vw, 3.5rem); - justify-content: center; - align-items: center; -`; -export const MainSectionWrapper = styled.div` - width: 100%; - height: auto; - box-sizing: border-box; - display: flex; - align-items: center; - margin: auto auto; - justify-content: center; - flex-direction: column; - gap: clamp(2rem, 4vw, 3rem); - - background-image: url(${bgBannerImg}); - background-size: unset; - background-repeat: no-repeat; - background-position: 50% 70%; -`; - -export const HomeLayout = styled.div` - width: 100%; - height: auto; - - display: flex; - flex-direction: column; - gap: clamp(1.5rem, 3vw, 2.5rem); - justify-content: center; - align-items: center; - position: relative; - - padding-bottom: 4rem; - - &::before { - opacity: 0.1; - background-color: #000; - content: ''; - position: absolute; - top: 0; - left: 0; - height: 100%; - width: 100%; - z-index: 0; - } -`; - -export const TextSectionWrapper = styled.div` - margin-top: ${_.HEADER.TOTAL_DVH}dvh; - position: relative; - width: 95dvw; - height: 60dvh; - display: flex; - align-items: center; - justify-content: center; -`; - -export const TextContainer = styled.div` - display: flex; - font-size: ${({ theme }) => theme.layout.home.mainTextSize}; - flex-direction: column; - align-items: center; - color: ${({ theme }) => theme.colors.white}; - font-family: 'Roboto', sans-serif; - font-weight: bold; - gap: 1rem; - text-align: center; -`; - -export const MainText = styled.span` - background: black; - -webkit-background-clip: text; - -webkit-text-fill-color: ${({ theme }) => theme.colors.white}; - background-clip: text; - padding-bottom: 1rem; - text-fill-color: ${({ theme }) => theme.colors.white}; -`; - -export const SubText = styled.span` - background: black; - font-size: 20px; - -webkit-background-clip: text; - -webkit-text-fill-color: ${({ theme }) => theme.colors.link}; - font-family: 'Roboto', sans-serif; - - background-clip: text; - padding-top: 2rem; - text-fill-color: ${({ theme }) => theme.colors.white}; - letter-spacing: 2px; -`; - -export const MainImageSectionWrapper = styled.div` - width: 95dvw; - height: auto; - display: flex; - align-items: center; - justify-content: center; - padding: 2dvh 2dvw; -`; - -export const BgShape = styled.div` - position: static !important; -`; - -export const MainImageFrame = styled.div` - background: url(${bgSliderBgImg}); - background-position: top; - background-size: cover; - background-repeat: no-repeat; - padding: 70px 70px 42px; - z-index: 3; - overflow: hidden; - - mask-image: linear-gradient( - to bottom, - rgba(0, 0, 0, 1) 0%, - rgba(0, 0, 0, 1) 60%, - rgba(0, 0, 0, 0) 100% - ); -`; -export const MainFeatureSectionWrapper = styled.div` - display: flex; - flex-direction: row; - display: flex; - flex-direction: column; - align-items: center; - width: 95dvw; - gap: 1rem; -`; - -export const MainFeatureSection = styled.main` - margin: 1dvh 0 4rem 0; - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - overflow: hidden; - gap: 40px; - width: clamp(min(100%, 540px), calc(90vw - 2rem), 1320px); - padding: 4.125rem 2.815rem 4.125rem 4.188rem; - background: #15091d; - border-radius: 20px; - border: 1px solid #2a1454; - border-radius: 15px; - - font-family: 'Roboto', sans-serif; - - @media (max-width: 968px) { - grid-template-columns: 1fr; - } -`; - -export const FeatureContentText = styled.div` - display: flex; - flex-direction: column; - gap: 1rem; - align-items: flex-start; - justify-content: space-evenly; - font-family: 'Roboto', sans-serif; - font-weight: 400; - h2 { - color: ${({ theme }) => theme.colors.white}; - font-size: clamp(4rem, 4vw, 2.5rem); - margin-bottom: 2rem; - } - - ul { - display: flex; - flex-direction: column; - gap: 1rem; - } - - li { - display: flex; - align-items: center; - gap: 1rem; - font-size: clamp(1rem, 2vw, 1.2rem); - } -`; - -export const FeatureContentImage = styled.div` - width: 100%; - height: auto; - border-radius: 10px; -`; diff --git a/src/components/organisms/DashboardContent/ParticleBackground.jsx b/src/components/organisms/DashboardContent/ParticleBackground.jsx deleted file mode 100644 index 28beee5..0000000 --- a/src/components/organisms/DashboardContent/ParticleBackground.jsx +++ /dev/null @@ -1,242 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import styled, { createGlobalStyle, css } from 'styled-components'; -import { Bell, Search } from 'lucide-react'; -import bg_img from "../../../assets/images/bg_img.jpg" - -// Global Styles -const GlobalStyle = createGlobalStyle` - @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap'); - - :root { - --theme-bg-color: rgba(16 18 27 / 40%); - --border-color: rgba(113 119 144 / 25%); - --theme-color: #f9fafb; - --inactive-color: rgb(113 119 144 / 78%); - --body-font: "Poppins", sans-serif; - --hover-menu-bg: rgba(12 15 25 / 30%); - --content-title-color: #999ba5; - --content-bg: rgb(146 151 179 / 13%); - --button-inactive: rgb(249 250 251 / 55%); - --dropdown-bg: #21242d; - --dropdown-hover: rgb(42 46 60); - --popup-bg: rgb(22 25 37); - --search-bg: #14162b; - --overlay-bg: rgba(36, 39, 59, 0.3); - --scrollbar-bg: rgb(1 2 3 / 40%); - } - - * { - outline: none; - box-sizing: border-box; - } - - /* body { - margin: 0; - padding: 0; - font-family: var(--body-font); - background-image: url(${bg_img}); - background-size: cover; - background-position: center; - height: 100vh; - } */ -`; - -// Styled Components - -const AppWrapper = styled.div` - position: relative; - border-radius: 14px; - - &:before { - content: ""; - /* opacity: 0.1; */ - background-image: url(${bg_img}); - background-size: cover; - background-position: center; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - /* background: rgba(0, 0, 0, 0.5); 투명도 조정 (rgba 사용) */ - pointer-events: none; /* 클릭 이벤트가 본래 요소로 전달되도록 설정 */ - } -` - -const AppContainer = styled.div` - - background-color: rgba(16 18 27 / 40%); - max-width: 1250px; - max-height: 860px; - height: 90vh; - display: flex; - flex-direction: column; - overflow: hidden; - position: relative; - width: 100%; - border-radius: 14px; - backdrop-filter: blur(20px); - -webkit-backdrop-filter: blur(20px); - font-size: 15px; - font-weight: 500; - margin: 2em auto; -`; - -const Header = styled.div` - display: flex; - align-items: center; - flex-shrink: 0; - height: 58px; - width: 100%; - border-bottom: 1px solid var(--border-color); - padding: 0 30px; - white-space: nowrap; -`; - -const MenuCircle = styled.div` - width: 15px; - height: 15px; - background-color: #f96057; - border-radius: 50%; - box-shadow: 24px 0 0 0 #f8ce52, 48px 0 0 0 #5fcf65; - margin-right: 195px; - flex-shrink: 0; -`; - -const HeaderMenu = styled.div` - display: flex; - align-items: center; - - a { - padding: 20px 30px; - text-decoration: none; - color: var(--inactive-color); - border-bottom: 2px solid transparent; - transition: 0.3s; - - &.active, &:hover { - color: var(--theme-color); - border-bottom: 2px solid var(--theme-color); - } - } -`; - -const SearchBar = styled.div` - height: 40px; - display: flex; - width: 100%; - max-width: 400px; - padding-left: 16px; - border-radius: 4px; - - input { - width: 100%; - height: 100%; - border: none; - background-color: var(--search-bg); - border-radius: 4px; - font-family: var(--body-font); - font-size: 15px; - font-weight: 500; - padding: 0 20px 0 40px; - color: var(--theme-color); - - &::placeholder { - font-family: var(--body-font); - color: var(--inactive-color); - font-size: 15px; - font-weight: 500; - } - } -`; - -const HeaderProfile = styled.div` - display: flex; - align-items: center; - padding: 0 16px 0 40px; - margin-left: auto; - flex-shrink: 0; -`; - -const NotificationIcon = styled.div` - position: relative; - cursor: pointer; - - span { - position: absolute; - background-color: #3a6df0; - width: 16px; - height: 16px; - border-radius: 50%; - font-size: 10px; - display: flex; - align-items: center; - justify-content: center; - color: #fff; - right: -6px; - top: -6px; - } -`; - -const ProfileImage = styled.img` - width: 32px; - height: 32px; - border-radius: 50%; - object-fit: cover; - border: 2px solid var(--theme-color); - margin-left: 22px; -`; - -// Main App Component -const App = () => { - const [isLightMode, setIsLightMode] = useState(false); - const [activeMenu, setActiveMenu] = useState('Apps'); - - return ( - <> - - - -
- - - setActiveMenu('Apps')} - > - Apps - - setActiveMenu('Your work')} - > - Your work - - Discover - Market - - - - - - - - - 3 - - - -
- {/* Add remaining content components here */} -
-
- - ); -}; - -export default App; \ No newline at end of file diff --git a/src/components/organisms/DashboardContent/particle.jsx b/src/components/organisms/DashboardContent/particle.jsx deleted file mode 100644 index 4cee86e..0000000 --- a/src/components/organisms/DashboardContent/particle.jsx +++ /dev/null @@ -1,448 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import styled, { createGlobalStyle, css } from 'styled-components'; -import bg_img from "../../../assets/images/bg_img.jpg" -import { Bell, Search, Grid, RefreshCw, Camera, Layout, PlayCircle, Pen, Box, MessageSquare, Sun } from 'lucide-react'; - -// Global Styles -const GlobalStyle = createGlobalStyle` - @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap'); - - :root { - --theme-bg-color: rgba(16 18 27 / 40%); - --border-color: rgba(113 119 144 / 25%); - --theme-color: #f9fafb; - --inactive-color: rgb(113 119 144 / 78%); - --body-font: "Poppins", sans-serif; - --hover-menu-bg: rgba(12 15 25 / 30%); - --content-title-color: #999ba5; - --content-bg: rgb(146 151 179 / 13%); - --button-inactive: rgb(249 250 251 / 55%); - --dropdown-bg: #21242d; - --dropdown-hover: rgb(42 46 60); - --popup-bg: rgb(22 25 37); - --search-bg: #14162b; - --overlay-bg: rgba(36, 39, 59, 0.3); - --scrollbar-bg: rgb(1 2 3 / 40%); - } - - * { - outline: none; - box-sizing: border-box; - } - - /* body { - margin: 0; - padding: 0; - font-family: var(--body-font); - background-image: url(${bg_img}); - background-size: cover; - background-position: center; - height: 100vh; - } */ -`; - -// Styled Components - -const AppWrapper = styled.div` - position: relative; - border-radius: 14px; - width : 95dvw; - height : 95dvh; - &:before { - content: ""; - /* opacity: 0.5; */ - opacity: 1; - background-image: url(${bg_img}); - background-size: cover; - background-position: center; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - border-radius : inherit; - /* background: rgba(0, 0, 0, 0.5); 투명도 조정 (rgba 사용) */ - pointer-events: none; /* 클릭 이벤트가 본래 요소로 전달되도록 설정 */ - } -` - -const AppContainer = styled.div` - - background-color: rgba(16 18 27 / 40%); - height: 100%; - display: flex; - flex-direction: column; - overflow: hidden; - position: relative; - width: 100%; - border-radius: inherit; - backdrop-filter: blur(20px); - -webkit-backdrop-filter: blur(20px); - font-size: 15px; - font-weight: 500; -`; - -const Header = styled.div` - display: flex; - align-items: center; - flex-shrink: 0; - height: 58px; - width: 100%; - border-bottom: 1px solid var(--border-color); - padding: 0 30px; - white-space: nowrap; -`; - -const MenuCircle = styled.div` - width: 15px; - height: 15px; - background-color: #f96057; - border-radius: 50%; - box-shadow: 24px 0 0 0 #f8ce52, 48px 0 0 0 #5fcf65; - margin-right: 195px; - flex-shrink: 0; -`; - -const HeaderMenu = styled.div` - display: flex; - align-items: center; - - span { - padding: 20px 30px; - text-decoration: none; - color: var(--inactive-color); - border-bottom: 2px solid transparent; - transition: 0.3s; - - &.active, &:hover { - color: var(--theme-color); - border-bottom: 2px solid var(--theme-color); - } - } -`; - -const SearchBar = styled.div` - height: 40px; - display: flex; - width: 100%; - max-width: 400px; - padding-left: 16px; - border-radius: 4px; - - input { - width: 100%; - height: 100%; - border: none; - background-color: var(--search-bg); - border-radius: 4px; - font-family: var(--body-font); - font-size: 15px; - font-weight: 500; - padding: 0 20px 0 40px; - color: var(--theme-color); - - &::placeholder { - font-family: var(--body-font); - color: var(--inactive-color); - font-size: 15px; - font-weight: 500; - } - } -`; - -const HeaderProfile = styled.div` - display: flex; - align-items: center; - padding: 0 16px 0 40px; - margin-left: auto; - flex-shrink: 0; -`; - -const NotificationIcon = styled.div` - position: relative; - cursor: pointer; - - span { - position: absolute; - background-color: #3a6df0; - width: 16px; - height: 16px; - border-radius: 50%; - font-size: 10px; - display: flex; - align-items: center; - justify-content: center; - color: #fff; - right: -6px; - top: -6px; - } -`; - -const ProfileImage = styled.img` - width: 32px; - height: 32px; - border-radius: 50%; - object-fit: cover; - border: 2px solid var(--theme-color); - margin-left: 22px; -`; - - - - -const Wrapper = styled.div` - display: flex; - flex-grow: 1; - overflow: hidden; -`; - -// Sidebar Components -const LeftSide = styled.div` - flex-basis: 240px; - border-right: 1px solid var(--border-color); - padding: 26px; - overflow: auto; - flex-shrink: 0; - - @media screen and (max-width: 945px) { - display: none; - } -`; - -const SideWrapper = styled.div` - & + & { - margin-top: 20px; - } -`; - -const SideTitle = styled.div` - color: var(--inactive-color); - margin-bottom: 14px; -`; - -const SideMenu = styled.div` - display: flex; - flex-direction: column; - white-space: nowrap; - - span { - text-decoration: none; - color: var(--theme-color); - display: flex; - align-items: center; - font-weight: 400; - padding: 10px; - font-size: 14px; - border-radius: 6px; - transition: 0.3s; - - &:hover { - background-color: var(--hover-menu-bg); - } - - svg { - width: 16px; - margin-right: 8px; - } - } -`; - -// Main Content Components -const MainContainer = styled.div` - display: flex; - flex-direction: column; - flex-grow: 1; -`; - -const MainHeader = styled.div` - display: flex; - align-items: center; - border-bottom: 1px solid var(--border-color); - height: 58px; - flex-shrink: 0; - padding: 0 30px; -`; - -const ContentWrapper = styled.div` - display: flex; - flex-direction: column; - color: var(--theme-color); - padding: 20px 40px; - height: 100%; - overflow: auto; - background-color: var(--theme-bg-color); -`; - -const ContentHeader = styled.div` - display: flex; - align-items: center; - width: 100%; - justify-content: space-between; - background-image: linear-gradient( - to right top, - #cf4af3, - #e73bd7, - #f631bc, - #fd31a2, - #ff3a8b - ); - border-radius: 14px; - padding: 20px 40px; -`; - -const ContentSection = styled.div` - margin-top: 30px; - display: flex; - flex-direction: column; -`; - -const AppsCard = styled.div` - display: flex; - align-items: center; - flex-wrap: wrap; - width: calc(100% + 20px); -`; - -const AppCard = styled.div` - display: flex; - flex-direction: column; - width: calc(33.3% - 20px); - font-size: 16px; - background-color: var(--content-bg); - border-radius: 14px; - border: 1px solid var(--theme-bg-color); - padding: 20px; - cursor: pointer; - transition: 0.3s ease; - - &:hover { - transform: scale(1.02); - background-color: var(--theme-bg-color); - } - - @media screen and (max-width: 1110px) { - width: calc(50% - 20px); - } - - @media screen and (max-width: 565px) { - width: calc(100% - 20px); - } -`; - -// Main App Component -const App = () => { - const [isLightMode, setIsLightMode] = useState(false); - const [activeMenu, setActiveMenu] = useState('Apps'); - - return ( - <> - - - -
- - - - - - 3 - - - -
- {/* Add remaining content components here */} - - All Apps - - {['Desktop', 'Mobile', 'Web'].map(menu => ( - setActiveMenu(menu)} - > - {menu} - - ))} - - - - - - - - Apps - - All Apps - Updates 3 - - - - - Categories - - Photography - Graphic Design - Video - Illustrations - UI/UX - 3D/AR - - - - - - - - {['Desktop', 'Mobile', 'Web'].map(menu => ( - setActiveMenu(menu)} - > - {menu} - - ))} - - {/* - -
-

Adobe Stock

-
Grab yourself 10 free images from Adobe Stock in a 30-day free trial plan.
- -
- Adobe Stock -
- */} - - -

Installed

- - -
- - Photoshop -
-
- Edit, master and create powerful images -
-
- -
-
- - {/* Add more AppCards as needed */} -
-
-
-
-
-
-
- - ); -}; - -export default App; \ No newline at end of file diff --git a/src/components/organisms/Landing/ChattingLanding/ChattingLanding.jsx b/src/components/organisms/Landing/ChattingLanding/ChattingLanding.jsx index df2da59..ecd91d5 100644 --- a/src/components/organisms/Landing/ChattingLanding/ChattingLanding.jsx +++ b/src/components/organisms/Landing/ChattingLanding/ChattingLanding.jsx @@ -1,193 +1,503 @@ - - -// ReadmeLanding.jsx import React from 'react'; +import styled, { css, keyframes } from 'styled-components'; import { - Book, FileEdit, Layout, Sparkles, ArrowRight, - Download, BookOpen, FileCode, GitBranch, Flag + Bot, Code, Zap, ArrowRight, Github, MessagesSquare, Sparkles, + FileText, Settings, Upload + } from 'lucide-react'; -import { - PageWrapper, - PageContainer, - HeroSection, - ContentWrapper, - HeroGrid, - GradientText, - ButtonGroup, - Button, - Section, - FeatureWrapper, - FeatureIcon, - FeatureContent, - FeatureGrid, - StepsGrid, - StepCard, - StepIcon, - CTASection, - Badge, -} from './ChattingLanding.styles'; - - - -const Feature = ({ icon: Icon, title, description }) => ( - - - - - -

{title}

-

{description}

-
-
-); - - -const ReadmeLanding = () => { +import { Image, Typo } from '../../../index.js'; +import mainBannerImg from "../../../../assets/images/landing-hero-min.png" + +const fadeIn = keyframes` + from { + opacity: 0.5; + transform: translateY(10px); /* 아래에서 올라오는 효과 추가 */ + } + to { + opacity: 1; + transform: translateY(0); + } +`; +const Container = styled.div` + min-height: 100vh; + background: #18181b; + color: #e4e4e7; + + animation: ${fadeIn} 0.5s ease-out; /* 애니메이션 추가 */ + + +`; +const HeroSection = styled.div` + padding-top : 11.5dvh; + position : relative; + + + &::before { + content: ''; + position: absolute; + inset: 0; + height: 100%; + background: linear-gradient(to right, rgba(147, 51, 234, 0.1), rgba(236, 72, 153, 0.1)); + pointer-events : none; + mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 1) 60%, rgba(0, 0, 0, 0) 100%); + } +` +const HeroGrid = styled.div` + display: grid; + gap: 5rem; + align-items: center; + + + padding: clamp(5vh, 6vh, 8vh) clamp(4vw, 6vw, 8vw); + + background: ${(props) => props.bg || 'transparent'}; + + @media (min-width: 1300px) { + grid-template-columns: repeat(2, 1fr); + gap : 3rem; + } + @media (max-width: 768px) { + padding: clamp(3vh, 4vh, 6vh) clamp(2vw, 3vw, 4vw); + } +`; + +const HeroTitle = styled.div` +display : flex; +flex-direction: column; +justify-content: flex-start; +` + +const HeroImage = styled.div` +display : flex; +justify-content: center; +` + +const Section = styled.section` + padding: clamp(5vh, 6vh, 8vh) clamp(4vw, 6vw, 8vw); + + background: ${(props) => props.bg || 'transparent'}; + + + @media (max-width: 768px) { + padding: clamp(3vh, 4vh, 6vh) clamp(2vw, 3vw, 4vw); + } + + ${(props) => + props.feature && + ` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + `} + +`; + +const Header = styled.div` + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 24px; +`; + +const Title = styled.h1` + font-size: 3rem; + font-weight: bold; + margin-bottom: 24px; + span { + background: linear-gradient(to right, #9333ea, #ec4899); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + } + + @media (max-width: 1300px) { + br { + display: none; // br 태그 숨김 + } + } +`; + +const Description = styled.p` + font-size: 1.25rem; + color: #a1a1aa; + margin-bottom: 32px; +`; + +const Button = styled.button` + padding: 12px 24px; + border-radius: 8px; + border : rgb(63 63 70) 1px solid; + font-size: 1rem; + display: flex; + align-items: center; + gap: 8px; + transition: 0.2s; + ${(props) => (props.primary ? `background: #9333ea; color: white;` : `border: rgb(63 63 70) 1px solid;`)} + + &:hover { + ${(props) => (props.primary ? `background: #7e22ce;` : `background: #27272a;`)} + } +`; + +const FeatureWrapper = styled.div` + display: grid; + grid-template-columns: repeat(2, minmax(250px, 1fr)); /* 최소 250px, 최대 동일 비율 */ + gap: 24px; + align-items: stretch; /* 카드 높이를 균일하게 맞춤 */ +`; + +const FeatureCard = styled.div` +padding: 2rem; + border-radius: 12px; + background-color: rgba(39,39,42,.5); + border: 1px solid rgb(63 63 70); + display: flex; + align-items: flex-start; /* 아이콘과 내용 상단 정렬 */ + gap: 16px; + flex-grow: 1; /* 카드가 동일한 높이로 늘어남 */ +`; + +const FeatureIcon = styled.div` + width: 48px; + height: 48px; + background: #9333ea33; + display: flex; + align-items: center; + justify-content: center; + border-radius: 8px; + color: #9333ea; + flex-shrink: 0; /* 아이콘 크기가 줄어들지 않도록 고정 */ +`; + +const FeatureContent = styled.div` + h3 { + font-size: 1.25rem; + font-weight: 600; + margin-bottom: 8px; + } + + p { + color: #a1a1aa; + /* color : rgb(233 213 255); */ + } +`; + +const Badge = styled.span` +background: ${props => (props.bg) || `rgba(168, 85, 247, .2)`} ; +color: ${props => (props.color) || `rgb(216, 180, 254)`} ; +border-radius: 0.5rem; + +display: inline-block; + margin-bottom: 1rem; + padding: 0.25rem 1rem; + background: rgba(147, 51, 234, 0.2); + color: #c084fc; + font-size: 0.875rem; +`; + +const TimelineContainer = styled.div` + position: relative; +`; + +const ConnectionLine = styled.div` + position: absolute; + left: 3rem; + top: -1rem; + bottom: -0.5rem; + width: 3px; + background: linear-gradient( + to bottom, + rgba(147, 51, 234, 0.5), + rgba(59, 130, 246, 0.5), + rgba(16, 185, 129, 0.5) + ); +`; + +const StepsList = styled.div` + display: flex; + flex-direction: column; + gap: 5rem; +`; + +const StepItem = styled.div` + position: relative; + display: flex; + align-items: flex-start; + gap: 2rem; + + &:hover { + ${props => props.hoverEffects} + } +`; + +const NumberBox = styled.div` + position: relative; + z-index: 10; +`; + +const GradientBorder = styled.div` + width: 6rem; + height: 6rem; + border-radius: 1rem; + background: linear-gradient(to bottom right, ${props => props.gradient}); + padding: 1px; +`; + +const NumberContent = styled.div` + width: 100%; + height: 100%; + border-radius: 1rem; + background: rgb(24, 24, 27); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +`; + +const StepLabel = styled.span` + font-size: 0.875rem; + color: rgb(113, 113, 122); + font-family: monospace; +`; + +const StepNumber = styled.span` + font-size: 1.5rem; + font-weight: bold; + background: linear-gradient(to right, #a855f7, #9333ea); + -webkit-background-clip: text; + color: transparent; +`; + +const IconBox = styled.div` + position: absolute; + bottom: -0.5rem; + right: -0.5rem; + width: 2rem; + height: 2rem; + border-radius: 0.5rem; + background: rgb(24, 24, 27); + display: flex; + align-items: center; + justify-content: center; + color: rgb(168, 85, 247); +`; + +const ContentBox = styled.div` + flex: 1; + padding-top: 1rem; +`; + +const ContentCard = styled.div` + background: rgba(39, 39, 42, 0.5); + border-radius: 1rem; + padding: 1.5rem; + border: 1px solid rgba(147, 51, 234, 0.1); + transition: all 0.3s; + + &:hover { + border-color: rgba(147, 51, 234, 0.2); + } +`; + +const ContentTitle = styled.h3` + font-size: 1.25rem; + font-weight: 600; + color: white; + margin-bottom: 0.5rem; + display: flex; + align-items: center; + transition: color 0.3s; + + &:hover { + color: rgb(168, 85, 247); + } +`; + +const ContentDescription = styled.p` + color: rgb(161, 161, 170); +`; + +const ArrowIcon = styled(ArrowRight)` + margin-left: 0.5rem; + opacity: 0; + transition: opacity 0.3s; + + ${ContentCard}:hover & { + opacity: 1; + } +`; + + +const LandingPage = () => { + + const steps = [ + { + number: "01", + icon: FileText, + title: "Choose Template", + description: "Select from our collection of professional README templates", + gradient: "rgba(147, 51, 234, 0.2), rgba(147, 51, 234, 0.1)" + }, + { + number: "02", + icon: Settings, + title: "Customize Content", + description: "Edit and customize sections with our intuitive editor", + gradient: "rgba(59, 130, 246, 0.2), rgba(59, 130, 246, 0.1)" + }, + { + number: "03", + icon: Upload, + title: "Export & Deploy", + description: "Export to markdown and deploy to your repository", + gradient: "rgba(16, 185, 129, 0.2), rgba(16, 185, 129, 0.1)" + } + ]; + return ( - - - - - -
- - README Maker - -

- Create Beautiful{' '} - Documentation -

-

- Generate comprehensive, well-structured README files for your projects - with our intelligent documentation maker. -

- - - - -
-
- {/* */} -
-
-
-
- -
- -
- - Features - -

- Everything you need for perfect documentation -

-

- Create comprehensive documentation with our intuitive tools and intelligent - suggestions, making documentation a breeze. -

-
+ + {/* Hero Section */} + + + +
- - {[ - { - icon: Layout, - title: "Smart Templates", - description: "Choose from various pre-built templates or customize your own structure." - }, - { - icon: Sparkles, - title: "AI-Powered Suggestions", - description: "Get intelligent recommendations based on your project type." - }, - { - icon: GitBranch, - title: "Version Control Integration", - description: "Seamlessly sync your documentation with your Git repository." - }, - { - icon: Flag, - title: "Custom Sections", - description: "Add, remove, or rearrange sections with our drag-and-drop interface." - } - ].map((feature, index) => ( - - ))} - - -
- -
- -
- - How It Works - -

- Create documentation in minutes -

-

- Our simple three-step process makes documentation creation effortless -

+ + + AI-Powered Code <br></br> <span>Chat Bot</span> + + + Dododocs의 지능형 채팅 어시스턴트와 함께 코드베이스를 즉시 이해하세요.

+ 질문하고 설명을 받고 복잡한 코드를 쉽게 탐색하세요. +
+
+ +
+ + + + + + - - {[ - { - icon: BookOpen, - title: "1. Choose Template", - description: "Select from our collection of professional README templates" - }, - { - icon: FileEdit, - title: "2. Customize Content", - description: "Edit and customize sections with our intuitive editor" - }, - { - icon: Download, - title: "3. Export & Deploy", - description: "Export to markdown and deploy to your repository" - } - ].map((step, index) => ( - - - - -

{step.title}

-

{step.description}

-
- ))} -
- -
- -
- - - Get Started - -

- Ready to create amazing documentation? -

-

- Join thousands of developers who are creating better documentation - with Dododocs README Maker. -

- -
-
-
-
+ {/* Features */} +
+
+ feature +
+

코드를 이해하는 데 필요한 모든 것

+

+ AI 어시스턴트가 강력한 코드 분석과 자연어 이해를 결합하여 + 코드베이스를 쉽게 탐색하고 이해할 수 있도록 도와드립니다. +

+ + + + + + +

자연스러운 대화

+

코드베이스와 자연어로 대화하세요. 질문하고 설명을 받아 복잡한 로직을 쉽게 이해하세요.

+
+
+ + + + + +

깊이 있는 코드 분석

+

AI 기반 분석을 통해 코드 구조, 패턴 및 관계를 이해하세요.

+
+
+ + + + + +

깊이 있는 코드 분석

+

AI 기반 분석을 통해 코드 구조, 패턴 및 관계를 이해하세요.

+
+
+ + + + + +

깊이 있는 코드 분석

+

AI 기반 분석을 통해 코드 구조, 패턴 및 관계를 이해하세요.

+
+
+
+
- ); -}; -export default ReadmeLanding; +
+
+ How It Works +
+

Create documentation in minutes

+

+ Our simple three-step process makes documentation creation effortless +

+ + + + {steps.map((step) => ( + + + + + STEP + {step.number} + + + + + + + + + + {step.title} + + + {step.description} + + + + ))} + + +
+
+
+ Get Started +
+

Ready to transform how you understand code?

+

+ Join thousands of developers who are using Dododocs to understand and document their code more effectively. +

+
+ +
+
+
+ Dododocs +
+ + + ); +}; +export default LandingPage; +// linear-gradient(rgba(39, 39, 42, 0.5), rgb(24, 24, 27) \ No newline at end of file diff --git a/src/components/organisms/Landing/ReadmeLanding/ReadmeLanding.jsx b/src/components/organisms/Landing/ReadmeLanding/ReadmeLanding.jsx index 7514997..71b7bef 100644 --- a/src/components/organisms/Landing/ReadmeLanding/ReadmeLanding.jsx +++ b/src/components/organisms/Landing/ReadmeLanding/ReadmeLanding.jsx @@ -6,6 +6,7 @@ import { Book, FileEdit, Layout, Sparkles, ArrowRight, Download, BookOpen, FileCode, GitBranch, Flag } from 'lucide-react'; + import { PageWrapper, PageContainer, @@ -25,8 +26,15 @@ import { StepIcon, CTASection, Badge, -} from './ReadmeLanding.styles' + Title, + Description, + Header, + FeatureCard, + +} from './ReadMeLanding.styles' +import { Image } from '../../../index.js'; +import mainBannerImg from "../../../../assets/images/landing-hero-min.png" const Feature = ({ icon: Icon, title, description }) => ( @@ -50,17 +58,16 @@ const ReadmeLanding = () => {
- + README Maker -

- Create Beautiful{' '} - Documentation -

-

+ + Create Beautiful <span>Readme</span> + + Generate comprehensive, well-structured README files for your projects with our intelligent documentation maker. -

+
- {/* */} +
+ {/* Features */} +
+
+ feature +
+

코드를 이해하는 데 필요한 모든 것

+

+ AI 어시스턴트가 강력한 코드 분석과 자연어 이해를 결합하여 + 코드베이스를 쉽게 탐색하고 이해할 수 있도록 도와드립니다. +

+ + + + + + +

자연스러운 대화

+

코드베이스와 자연어로 대화하세요. 질문하고 설명을 받아 복잡한 로직을 쉽게 이해하세요.

+
+
+ + + + + +

깊이 있는 코드 분석

+

AI 기반 분석을 통해 코드 구조, 패턴 및 관계를 이해하세요.

+
+
+ + + + + +

깊이 있는 코드 분석

+

AI 기반 분석을 통해 코드 구조, 패턴 및 관계를 이해하세요.

+
+
+ + + + + +

깊이 있는 코드 분석

+

AI 기반 분석을 통해 코드 구조, 패턴 및 관계를 이해하세요.

+
+
+
+
+ + +
diff --git a/src/components/organisms/Landing/ReadmeLanding/ReadmeLanding.styles.js b/src/components/organisms/Landing/ReadmeLanding/ReadmeLanding.styles.js index 7704164..913ec4e 100644 --- a/src/components/organisms/Landing/ReadmeLanding/ReadmeLanding.styles.js +++ b/src/components/organisms/Landing/ReadmeLanding/ReadmeLanding.styles.js @@ -14,23 +14,34 @@ const fadeIn = keyframes` export const PageWrapper = styled.div` width: 100%; + padding-top: 11.5dvh; + position: relative; /* ::before 사용을 위해 필요 */ + /* animation: ${fadeIn} 0.5s ease-out; */ + &::before { content: ''; position: absolute; inset: 0; + height: 100%; background: linear-gradient( to bottom, - rgba(147, 51, 234, 0.1), + rgba(147, 51, 234, 0.2), transparent, transparent ); + + /* background: linear-gradient( + to right, + rgba(147, 51, 234, 0.102), + rgba(236, 72, 153, 0.102) + ); */ } `; export const PageContainer = styled.div` min-height: 100vh; /* margin-top: 11.5dvh; */ - background-color: #0f0a1f; + background-color: #0e0c15; color: white; display: flex; @@ -38,12 +49,26 @@ export const PageContainer = styled.div` flex-direction: column; justify-content: center; width: 100%; - padding: clamp(5vh, 6vh, 8vh) clamp(4vw, 6vw, 8vw); - animation: ${fadeIn} 0.5s ease-out; + /* padding: clamp(5vh, 6vh, 8vh) clamp(4vw, 6vw, 8vw); */ + padding: clamp(5vh, 6vh, 8vh) 0; + + /* animation: ${fadeIn} 0.5s ease-out; */ @media (max-width: 768px) { - padding: clamp(3vh, 4vh, 6vh) clamp(2vw, 3vw, 4vw); + /* padding: clamp(3vh, 4vh, 6vh) clamp(2vw, 3vw, 4vw); */ + padding: clamp(3vh, 4vh, 6vh) 0; } + /* &::before { + opacity: 0.1; + background-color: #000; + content: ''; + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + z-index: 0; + } */ `; export const HeroSection = styled.div` @@ -55,7 +80,7 @@ export const ContentWrapper = styled.div` position: relative; max-width: 80rem; margin: 0 auto; - padding: 5rem 1rem; + padding: 3rem clamp(4vw, 6vw, 8vw); `; export const HeroGrid = styled.div` @@ -68,12 +93,29 @@ export const HeroGrid = styled.div` } `; +export const Title = styled.h1` + font-size: 3rem; + font-weight: bold; + margin-bottom: 24px; + span { + background: linear-gradient(to right, #9333ea, #ec4899); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + } +`; + export const GradientText = styled.span` background: linear-gradient(to right, #a78bfa, #f472b6); -webkit-background-clip: text; color: transparent; `; +export const Description = styled.p` + font-size: 1.25rem; + color: #a1a1aa; + margin-bottom: 32px; +`; + export const ButtonGroup = styled.div` display: flex; gap: 1rem; @@ -109,21 +151,53 @@ export const Button = styled.button` `; export const Section = styled.section` - padding: 5rem 1rem; + padding: clamp(5vh, 6vh, 8vh) clamp(4vw, 6vw, 8vw); + + background: ${(props) => props.bg || 'transparent'}; + + @media (max-width: 768px) { + padding: clamp(3vh, 4vh, 6vh) clamp(2vw, 3vw, 4vw); + } + ${(props) => - props.darker && + props.feature && ` - background: rgba(22, 17, 46, 0.5); + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; `} `; -export const FeatureWrapper = styled.div` +export const Header = styled.div` + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 24px; +`; + +export const FeatureCard = styled.div` + padding: 3rem 2rem; + border-radius: 12px; + /* background: #27272a; */ + background-color: rgba(39, 39, 42, 0.5); + border: 1px solid rgb(63 63 70); display: flex; + gap: 16px; + align-items: center; +`; + +export const FeatureWrapper = styled.div` + /* display: flex; gap: 1rem; padding: 1.5rem; border-radius: 0.75rem; background: #16112e; - border: 1px solid rgba(147, 51, 234, 0.2); + border: 1px solid rgba(147, 51, 234, 0.2); */ + + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 24px; `; export const FeatureIcon = styled.div` @@ -142,11 +216,11 @@ export const FeatureContent = styled.div` font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; - color: white; + color: #e9d5ff; } p { - color: #e9d5ff; + color: white; } `; @@ -198,9 +272,10 @@ export const CTASection = styled.div` export const Badge = styled.span` margin-left: auto; - font-size: 0.75rem; - background: rgba(147, 51, 234, 0.2); - color: #9333ea; - padding: 0.125rem 0.5rem; - border-radius: 9999px; + font-size: 0.8rem; + /* background: rgba(147, 51, 234, 0.2); */ + background: rgba(168, 85, 247, 0.2); + color: rgb(216, 180, 254); + padding: 0.4rem 1.5rem; + border-radius: 0.5rem; `; diff --git a/src/components/organisms/MainContent/MainContent.jsx b/src/components/organisms/MainContent/MainContent.jsx deleted file mode 100644 index 720f5a3..0000000 --- a/src/components/organisms/MainContent/MainContent.jsx +++ /dev/null @@ -1,90 +0,0 @@ -import React, { useState, useEffect } from "react" -import profileImage from "../../../assets/images/man08.jpg" -import { useNavigate } from 'react-router-dom'; -import { - Wrapper, - Container, - Card, - ImageContainer, - Image, - TextContainer, - MainText, - ButtonContainer, - CategoryButton, - ActionButtonContainer, - ActionButton -} from './MainContent.styles.js'; - - -const MainContent = () => { - const navigate = useNavigate(); - - const categories = [ - { name: 'all', label: '🍽️ 전체' }, - { name: 'korean', label: '🍚 한식' }, - { name: 'chinese', label: '🍜 중식' }, - { name: 'japanese', label: '🍣 일식' }, - { name: 'western', label: '🍝 양식' }, - { name: 'others', label: '🥓 기타' } - ]; - - const [userCategories, setUserCategories] = useState([]); - - useEffect(() => { - console.log(userCategories) - }, [userCategories]) - - - const handleCategoryClick = (category) => { - if (category === "all") { - - } - setUserCategories((state) => { - if (category === "all") { - return state.length === 5 ? [] : ['korean', 'chinese', 'japanese', 'western', 'others']; - } - return state.includes(category) - ? state.filter((e) => e !== category) - : [...state, category] - }); - }; - - const handleActionButtonClick = () => { - console.log('식당 골라줘! 버튼이 클릭되었습니다.'); - navigate('/result') - }; - - return ( - <> - {/* - - - - Profile - - - 아레아레.. 못 말리는 아가씨, - 카테고리를 선택해 주세요.. - - - - {categories.map((category) => ( - handleCategoryClick(category.name)} - > - {category.label} - - ))} - - - 식당 골라줘! - - - - */} - - ) -} - -export default MainContent; \ No newline at end of file diff --git a/src/components/organisms/MainContent/MainContent.styles.js b/src/components/organisms/MainContent/MainContent.styles.js deleted file mode 100644 index 42096a9..0000000 --- a/src/components/organisms/MainContent/MainContent.styles.js +++ /dev/null @@ -1,97 +0,0 @@ -import styled from 'styled-components'; - -export const Wrapper = styled.div` - width: 100%; - height: 100%; - box-sizing: border-box; - display: flex; - align-items: center; - margin: auto auto; - justify-content: center; - flex-direction: column; - gap: 50px; -`; - -export const Container = styled.div` - display: flex; - justify-content: center; - align-items: center; - height: 100%; - width: 100%; - flex-direction: column; - max-width: 500px; - gap: 20px; -`; - -export const Card = styled.div` - background: white; - border-radius: 10px; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); - overflow: hidden; - width: 100%; - text-align: center; -`; - -export const ImageContainer = styled.div` - width: 100%; -`; - -export const Image = styled.img` - width: 100%; - height: auto; -`; - -export const TextContainer = styled.div` - padding: 20px; -`; - -export const MainText = styled.p` - font-size: 24px; - color: #333; - margin: 0; - font-family: 'grandiflora'; -`; - -export const ButtonContainer = styled.div` - display: flex; - flex-wrap: wrap; - width: 100%; - justify-content: space-between; -`; - -export const CategoryButton = styled.button` - background-color: #ffffff; - border: none; - padding: 18px 10px 19px 8px; - font-size: 1rem; - font-family: 'grandiflora'; - cursor: pointer; - border-radius: 5px; - color: #000000; - - ${(props) => - props.checked && - ` - background-color: #96C9F4; - box-shadow: inset 3px 3px 3px 0px rgba(0, 0, 0, 0.25); - `} -`; - -export const ActionButtonContainer = styled.div` - width: 100%; -`; - -export const ActionButton = styled.button` - background-color: #ffeb3b; - border: none; - padding: 15px 30px; - font-size: 1.2em; - font-family: diphylleia; - cursor: pointer; - border-radius: 5px; - box-shadow: 8px 8px 6px 0px rgba(0, 0, 0, 0.25); - width: 100%; - &:hover { - background-color: #fdd835; - } -`; diff --git a/src/components/organisms/OAuthCallback/OAuthCallback.jsx b/src/components/organisms/OAuthCallback/OAuthCallback.jsx index 273804f..522a6c1 100644 --- a/src/components/organisms/OAuthCallback/OAuthCallback.jsx +++ b/src/components/organisms/OAuthCallback/OAuthCallback.jsx @@ -26,7 +26,7 @@ const OAuthCallback = () => { if (success) { const returnUrl = localStorage.getItem('returnUrl') || '/'; localStorage.removeItem('returnUrl'); - // navigate('/', { replace: true }); + navigate('/', { replace: true }); } else { // navigate('/', { replace: true }); } diff --git a/src/components/organisms/RepoDetailContent/CattingDetailContent/Chatting.jsx b/src/components/organisms/RepoDetailContent/CattingDetailContent/Chatting.jsx new file mode 100644 index 0000000..0694d98 --- /dev/null +++ b/src/components/organisms/RepoDetailContent/CattingDetailContent/Chatting.jsx @@ -0,0 +1,322 @@ +import React, { useState, useRef, useEffect } from 'react'; +import styled, { css, keyframes } from 'styled-components'; +import { Send, User, Bot, Sparkles, RefreshCw, EllipsisVertical } from 'lucide-react'; +import { Typo } from "../../../index.js"; +import useClickAway from '../../../../hooks/useClickAway.js'; +import { useChatbot } from '../../../../hooks/RepoDetailContent/useChatbot.js'; +import { TypingMarkdownRenderer, MarkdownRenderer } from '../../../index.js'; +import { chattingText } from "../chattingText.jsx"; +import { useRegisteredRepoStore, useAuthStore } from "../../../../store/store.js" + +import { + ChatWrapper, + EllipsisMenuWrapper, EllipsisVerticalIcon, EllipsisMenu, + ChatContainer, + MessageContainer, + WelcomeTitle, WelcomeMessage, WelcomeMessageTitle, + SparklesIcon, + BotIcon, + MessageContent, + Badge, + ChatMessages, + MessageBubble, + Avatar, + Message, + InputContainer, InputWrapper, Input, + ActionButton, + LoadingDots +} from "./Chatting.styles.js" + + +const ChatbotUI = () => { + const { activeRepositoryId } = useRegisteredRepoStore(); + const token = useAuthStore(state => state.token); + + const { + chatHistory, + isLoading: isChatLoading, + isError, + error, + sendMessage, + resetChat + } = useChatbot(activeRepositoryId); + + const [streamingResponse, setStreamingResponse] = useState(''); + + const [messages, setMessages] = useState([]); + const [inputText, setInputText] = useState(''); + const messagesEndRef = useRef(null); + const [isMenuOpen, setIsMenuOpen] = useState(false); + const menuRef = useRef(null); // 메뉴 ref 생성 + + + const [isTest, setIsTest] = useState(0); + + + + + // 채팅 히스토리 동기화 + useEffect(() => { + if (chatHistory) { + const formattedMessages = chatHistory.flatMap(chat => [ + { id: `q-${chat.id}`, text: chat.question, isUser: true }, + { id: `a-${chat.id}`, text: chat.text, isUser: false } + ]); + setMessages(formattedMessages); + } + }, [chatHistory]); + + // 스트리밍 응답이 업데이트될 때마다 메시지 업데이트 + useEffect(() => { + if (streamingResponse) { + console.log("스트리밍 응답이 업데이트될 때마다 메시지 업데이트", streamingResponse) + const lastMessage = messages[messages.length - 1]; + if (!lastMessage || lastMessage.isUser) { + // 새로운 AI 응답 메시지 추가 + setMessages(prev => [...prev, { + id: `stream-${Date.now()}`, + text: streamingResponse, + isUser: false + }]); + } else { + // 기존 AI 응답 메시지 업데이트 + setMessages(prev => { + const newMessages = [...prev]; + newMessages[newMessages.length - 1] = { + ...newMessages[newMessages.length - 1], + text: streamingResponse + }; + return newMessages; + }); + } + } + }, [streamingResponse]); + + // 메시지 전송 핸들러 + const handleSend = async () => { + if (!inputText.trim() || isChatLoading || !token || !activeRepositoryId) return; + + const userMessageId = Date.now(); + setMessages(prev => [...prev, { + id: `q-${userMessageId}`, + text: inputText, + isUser: true + }]); + + setInputText(''); + setStreamingResponse(''); + + try { + const response = await fetch( + `${process.env.REACT_APP_API_BASE_URL}api/chatbot/stream-and-save/${activeRepositoryId}`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + }, + body: JSON.stringify({ + question: inputText + }) + } + ); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const result = await response.text(); + console.log(result); + + setMessages(prev => [...prev, { + id: `a-${userMessageId}`, + text: result, + isUser: false + }]); + + } catch (error) { + setMessages(prev => [...prev, { + id: `error-${userMessageId}`, + text: "죄송합니다. 오류가 발생했습니다. 다시 시도해 주세요.", + isUser: false, + isError: true + }]); + console.error('채팅 요청 에러:', error); + } + }; + + // 대화 초기화 핸들러 + const handleReset = async () => { + await resetChat(); + setMessages([]); + setStreamingResponse(''); + + }; + + + + useClickAway(menuRef, () => { + if (isMenuOpen) { + setIsMenuOpen(false); + } + }); + + const scrollToBottom = () => { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }; + + useEffect(() => { + scrollToBottom(); + console.log(messages) + }, [messages]); + + + + const handleKeyPress = (e) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + handleSend(); + } + }; + return ( + + + setIsMenuOpen(!isMenuOpen)}> + + +
    +
  • + + reset +
  • +
+
+
+
+ + + + + {/* Welcome message */} + + + + Dododocs AI Chat + + + 연결된 GitHub 레포지토리의 코드를 기반으로 답변을 제공해드립니다. + + + { + messages.length === 0 ? + + + + + + + + DODODOCS AI + {/* AI 챗봇 */} + +

+ 안녕하세요! GitHub 레포지토리의 코드에 대해 궁금하신 점을 물어보세요. + 자세한 분석과 함께 답변해드리겠습니다. +

+
+
+ +
    +
  • 코드 구조와 아키텍처 분석
  • +
  • 함수와 클래스의 역할 설명
  • +
  • 코드 개선 방안 제안
  • +
  • 버그 해결 방안 제시
  • +
+
+
+ : null + } + + {/* Chat messages */} + + {messages.map(message => ( + + + {message.isUser ? : } + + + { + message.isUser ? + message.text + : + handleMessageComplete(message.id)} + onComplete={() => console.log('타이핑 완료')} // 필요한 경우에만 사용 + + /> + + } + {/* */} + {/* {message.text} */} + + + ))} + {isChatLoading && ( + + + + + + + + + + + + + )} +
+ + + + + + setInputText(e.target.value)} + onKeyPress={(e) => e.key === 'Enter' && !e.shiftKey && handleSend()} + disabled={isChatLoading} + /> + + + + + + + + + + + ); +}; + +export default ChatbotUI; + + diff --git a/src/components/organisms/RepoDetailContent/CattingDetailContent/Chatting.styles.js b/src/components/organisms/RepoDetailContent/CattingDetailContent/Chatting.styles.js new file mode 100644 index 0000000..cf4ef26 --- /dev/null +++ b/src/components/organisms/RepoDetailContent/CattingDetailContent/Chatting.styles.js @@ -0,0 +1,410 @@ +import styled, { css, keyframes } from 'styled-components'; +import { Sparkles } from 'lucide-react'; + +// Animations +export const fadeIn = keyframes` + from { opacity: 0; transform: translateY(10px); } + to { opacity: 1; transform: translateY(0); } +`; + +export const shimmer = keyframes` + 0% { background-position: -200% 0; } + 100% { background-position: 200% 0; } +`; + +export const WelcomeFadeIn = keyframes` + from { + opacity: 0; + transform: translate(-50%, -30%); // 최종 위치와 동일하게 설정 + } + to { + opacity: 1; + transform: translate(-50%, -30%); // 최종 위치와 동일하게 설정 + } +`; + +// Styled Components +export const ChatWrapper = styled.div` + display: flex; + flex-direction: column; + align-items: flex-end; + height: 100%; + width: 100%; + background: rgba(45, 45, 58, 0.4); + backdrop-filter: blur(10px); +`; + +export const EllipsisMenuWrapper = styled.div` + width: 100%; + background: transparent; + display: flex; + justify-content: flex-end; + align-items: center; + height: 2.5rem; +`; + +export const EllipsisVerticalIcon = styled.div` + color: #ffffff; + cursor: pointer; + padding: 0.7rem; + display: flex; + justify-content: center; + align-items: center; + &:hover { + background: rgba(13, 15, 15, 0.3); + } + position: relative; + margin: 0 1rem 0 0; +`; + +export const EllipsisMenu = styled.div` + position: absolute; + cursor: default; + inset: 0px 0px auto auto; + margin: 0px; + transform: translate(0px, 2.5rem); + border-radius: 0.75rem; + padding: 1rem; + min-width: 15rem; + background-color: #232236; + z-index: 3000; + border: 1px solid rgba(63, 63, 70, 0.3); + + /* 최적화된 그림자 효과 */ + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1), 0 8px 24px rgba(0, 0, 0, 0.15); + + /* 최적화된 트랜지션 */ + transform-origin: top right; + transition: opacity 0.2s ease-out, visibility 0.2s ease-out, + transform 0.2s cubic-bezier(0.16, 1, 0.3, 1); + + /* 상태에 따른 스타일 변화 */ + pointer-events: ${(props) => (props.isOpen ? 'auto' : 'none')}; + + ${(props) => + props.isOpen + ? css` + opacity: 1; + visibility: visible; + transform: translate(0, 2.5rem) scale(1); + ` + : css` + opacity: 0; + visibility: hidden; + transform: translate(0, 2rem) scale(0.95); + `} + ul { + margin: 0; + padding-inline-start: 0; + li { + margin: 0.5rem 0; + background-color: #232236; + padding: 0.7rem 0.5rem; + color: ${(props) => + props.$disabled + ? 'rgba(113, 119, 144, 0.4)' + : '#9492A6'}; // 비활성화 상태의 색상 변경 + transition: all 0.2s ease; // 부드러운 전환 효과 추가 + cursor: pointer; // hover 시 커서 변경 + border-radius: 4px; + display: flex; + align-items: center; + font-size: 1rem; + cursor: ${(props) => (props.$disabled === true ? 'not-allowed' : 'cursor')}; + + span { + padding: 0.3rem; + background: ${(props) => (props.$disabled ? 'rgba(55, 53, 84, 0.5)' : '#373554')}; + display: flex; + transition: all 0.2s ease; + border-radius: 4px; + margin-right: 0.5rem; + } + svg { + width: 1rem; + height: 1rem; + opacity: ${(props) => (props.$disabled ? 0.4 : 1)}; // 아이콘 투명도 + color: ${(props) => (props.$disabled ? 'rgba(113, 119, 144, 0.4)' : '#d923ff')}; + } + + &:hover { + background-color: ${(props) => + props.$disabled ? '#232236' : '#373554'}; // 비활성화 상태에서는 hover 효과 제거 + color: ${(props) => (props.$disabled ? 'rgba(113, 119, 144, 0.4)' : '#ffffff')}; + + span { + background: ${(props) => + props.$disabled ? 'rgba(55, 53, 84, 0.5)' : '#a25cff'}; + } + svg { + color: ${(props) => (props.$disabled ? 'rgba(113, 119, 144, 0.4)' : '#ffffff')}; + } + } + } + } +`; + +export const ChatContainer = styled.div` + display: flex; + flex-direction: column; + /* flex : 1; */ + height: calc(100% - 2.5rem); + width: 100%; + border-radius: 16px; + /* border: 1px solid rgba(255, 255, 255, 0.1); */ +`; + +export const MessageContainer = styled.div` + flex: 1; + overflow-y: auto; + padding: 24px; + position: relative; + scroll-behavior: smooth; + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: rgba(217, 35, 255, 0.2); + border-radius: 3px; + + &:hover { + background: rgba(217, 35, 255, 0.3); + } + } +`; + +export const WelcomeTitle = styled.div` + padding: 0 0 2rem 0; + display: flex; + flex-direction: column; + gap: 0.5rem; + animation: ${fadeIn} 0.6s ease-out; +`; + +export const WelcomeMessage = styled.div` + display: flex; + flex-direction: column; + gap: 0.5rem; + align-items: flex-start; + background: rgba(105, 14, 124, 0.05); + border: 1px solid rgba(217, 35, 255, 0.1); + padding: 1.5rem; + border-radius: 1rem; + position: absolute; + bottom: 0; + left: 50%; + transform: translate(-50%, -10%); // 초기 위치를 최종 위치와 동일하게 설정 + backdrop-filter: blur(10px); + animation: ${WelcomeFadeIn} 0.6s ease-out; + max-width: calc(100% - 36px - 36px - 24px); + width: 60%; + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1); +`; + +export const WelcomeMessageTitle = styled.div` + display: flex; + flex-direction: row; + gap: 1rem; + align-items: center; + justify-content: center; +`; + +export const SparklesIcon = styled(Sparkles)` + display: inline; + margin-right: 0.5rem; + color: #d923ff; + width: 24px; + height: 24px; +`; + +export const BotIcon = styled.div` + width: 2.5rem; + height: 2.5rem; + background: linear-gradient(135deg, #d923ff, #a78bfa); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + box-shadow: 0 2px 10px rgba(217, 35, 255, 0.2); +`; + +export const MessageContent = styled.div` + color: #e4e4e7; + font-size: 0.95rem; + line-height: 1.6; + + strong { + color: white; + font-size: 1.3rem; + font-weight: 500; + display: flex; + align-items: center; + gap: 0.5rem; + /* margin-bottom: 0.5rem; */ + } + + ul { + margin-top: 0.75rem; + padding-left: 3.5rem; + li { + margin: 0.5rem 0; + padding-left: 1rem; + position: relative; + + &:before { + content: '•'; + color: #d923ff; + position: absolute; + left: 0; + } + } + } +`; + +export const Badge = styled.span` + padding: 0.125rem 0.5rem; + background-color: #d923ff; + font-size: 0.75rem; + border-radius: 9999px; + color: white; + margin-left: 0.5rem; + font-weight: 500; + margin: 0 0 0.2rem 0.2rem; +`; + +export const ChatMessages = styled.div` + display: flex; + flex-direction: column; + gap: 16px; + padding-bottom: 120px; +`; + +export const MessageBubble = styled.div` + display: flex; + align-items: flex-start; + gap: 12px; + margin: ${(props) => (props.isUser ? '0 0 0 auto' : '0 auto 0 0')}; + max-width: 70%; + animation: ${fadeIn} 0.3s ease-out; +`; + +export const Avatar = styled.div` + min-width: 36px; + min-height: 36px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + background: ${(props) => + props.isUser ? 'linear-gradient(135deg, #d923ff, #a78bfa)' : 'rgba(63, 63, 70, 0.8)'}; + color: white; + order: ${(props) => (props.isUser ? 1 : 0)}; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +`; + +export const Message = styled.div` + background: rgba(63, 63, 70, 0.8); + padding: 14px 18px; + border-radius: 16px; + color: white; + font-size: 0.95rem; + line-height: 1.5; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + backdrop-filter: blur(10px); + ${(props) => + props.isUser + ? ` + background: transparent; + border : 1px solid #a78bfa + ` + : null} +`; + +export const InputContainer = styled.div` + padding: 20px; + background: rgba(255, 255, 255, 0.03); + border-top: 1px solid rgba(255, 255, 255, 0.06); + border-radius: 0 0 16px 16px; +`; + +export const InputWrapper = styled.div` + display: flex; + gap: 12px; + align-items: center; + background: rgba(255, 255, 255, 0.07); + border-radius: 12px; + padding: 8px 16px; + transition: all 0.2s ease; + border: 1px solid rgba(255, 255, 255, 0.1); + + &:focus-within { + background: rgba(255, 255, 255, 0.09); + border-color: rgba(217, 35, 255, 0.3); + box-shadow: 0 0 0 2px rgba(217, 35, 255, 0.1); + } +`; + +export const Input = styled.input` + flex: 1; + background: transparent; + border: none; + color: white; + font-size: 0.95rem; + padding: 10px 0; + + &::placeholder { + color: rgba(255, 255, 255, 0.3); + } + + &:focus { + outline: none; + } +`; + +export const ActionButton = styled.button` + background: transparent; + border: none; + color: ${(props) => (props.disabled ? 'rgba(255, 255, 255, 0.2)' : '#d923ff')}; + cursor: ${(props) => (props.disabled ? 'not-allowed' : 'pointer')}; + padding: 8px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s; + border-radius: 8px; + + &:hover:not(:disabled) { + background: rgba(217, 35, 255, 0.1); + color: #e048ff; + } + + &:active:not(:disabled) { + transform: scale(0.95); + } +`; + +export const LoadingDots = styled.div` + display: flex; + gap: 4px; + align-items: center; + padding: 4px 8px; + + span { + width: 4px; + height: 4px; + border-radius: 50%; + background: #d923ff; + animation: ${shimmer} 1.5s infinite; + opacity: 0.7; + + &:nth-child(2) { + animation-delay: 0.2s; + } + &:nth-child(3) { + animation-delay: 0.4s; + } + } +`; diff --git a/src/components/organisms/RepoDetailContent/Chatting.jsx b/src/components/organisms/RepoDetailContent/Chatting.jsx deleted file mode 100644 index 9e30ed5..0000000 --- a/src/components/organisms/RepoDetailContent/Chatting.jsx +++ /dev/null @@ -1,705 +0,0 @@ -import React, { useState, useRef, useEffect } from 'react'; -import styled, { css, keyframes } from 'styled-components'; -import { Send, User, Bot, Sparkles, RefreshCw, EllipsisVertical } from 'lucide-react'; -import { Typo } from "../../index.js"; -import useClickAway from '../../../hooks/useClickAway.js'; -import { useChatbot } from '../../../hooks/RepoDetailContent/useChatbot.js'; -import { TypingMarkdownRenderer, MarkdownRenderer } from '../../index.js'; -import { chattingText } from "./chattingText.jsx"; -import { useRegisteredRepoStore, useAuthStore } from "../../../store/store.js" - - -// Animations -const fadeIn = keyframes` - from { opacity: 0; transform: translateY(10px); } - to { opacity: 1; transform: translateY(0); } -`; - -const shimmer = keyframes` - 0% { background-position: -200% 0; } - 100% { background-position: 200% 0; } -`; - -const WelcomeFadeIn = keyframes` - from { - opacity: 0; - transform: translate(-50%, -30%); // 최종 위치와 동일하게 설정 - } - to { - opacity: 1; - transform: translate(-50%, -30%); // 최종 위치와 동일하게 설정 - } -` - -// Styled Components -const ChatWrapper = styled.div` - display: flex; - flex-direction : column; - align-items: flex-end; - height: 100%; - width: 100%; - background: rgba(45, 45, 58, 0.4); - backdrop-filter: blur(10px); - -`; - -const EllipsisMenuWrapper = styled.div` - width : 100%; - background: transparent; - display: flex; - justify-content: flex-end; - align-items: center; - height : 2.5rem; -` - -const EllipsisVerticalIcon = styled.div` - color : #ffffff; - cursor : pointer; - padding : 0.7rem; - display : flex; - justify-content: center; - align-items: center; - &:hover { - background: rgba(13, 15, 15, 0.3); - } - position : relative; - margin : 0 1rem 0 0 ; -` - -const EllipsisMenu = styled.div` - position: absolute; - cursor: default; - inset: 0px 0px auto auto; - margin: 0px; - transform: translate(0px, 2.5rem); - border-radius: 0.75rem; - padding: 1rem; - min-width: 15rem; - background-color: #232236; - z-index: 3000; - border: 1px solid rgba(63, 63, 70, 0.3); - - /* 최적화된 그림자 효과 */ - box-shadow: - 0 4px 12px rgba(0, 0, 0, 0.1), - 0 8px 24px rgba(0, 0, 0, 0.15); - - /* 최적화된 트랜지션 */ - transform-origin: top right; - transition: - opacity 0.2s ease-out, - visibility 0.2s ease-out, - transform 0.2s cubic-bezier(0.16, 1, 0.3, 1); - - /* 상태에 따른 스타일 변화 */ - pointer-events: ${props => props.isOpen ? 'auto' : 'none'}; - - ${props => props.isOpen ? css` - opacity: 1; - visibility: visible; - transform: translate(0, 2.5rem) scale(1); - ` : css` - opacity: 0; - visibility: hidden; - transform: translate(0, 2rem) scale(0.95); - `} - ul { - margin: 0; - padding-inline-start : 0; - li { - margin: 0.5rem 0; - background-color: #232236; - padding: 0.7rem 0.5rem; - color: ${props => props.$disabled ? 'rgba(113, 119, 144, 0.4)' : '#9492A6'}; // 비활성화 상태의 색상 변경 - transition: all 0.2s ease; // 부드러운 전환 효과 추가 - cursor: pointer; // hover 시 커서 변경 - border-radius: 4px; - display : flex; - align-items : center; - font-size : 1rem; - cursor: ${props => props.$disabled === true ? 'not-allowed' : 'cursor'}; - - span{ - padding : 0.3rem; - background: ${props => props.$disabled ? 'rgba(55, 53, 84, 0.5)' : '#373554'}; - display: flex; - transition: all 0.2s ease; - border-radius : 4px; - margin-right : .5rem; - } - svg{ - width : 1rem; - height : 1rem; - opacity: ${props => props.$disabled ? 0.4 : 1}; // 아이콘 투명도 - color : ${props => props.$disabled ? 'rgba(113, 119, 144, 0.4)' : '#d923ff'} - } - - &:hover { - background-color: ${props => props.$disabled ? '#232236' : '#373554'}; // 비활성화 상태에서는 hover 효과 제거 - color: ${props => props.$disabled ? 'rgba(113, 119, 144, 0.4)' : '#ffffff'}; - - span { - background: ${props => props.$disabled ? 'rgba(55, 53, 84, 0.5)' : '#a25cff'}; - } - svg { - color : ${props => props.$disabled ? 'rgba(113, 119, 144, 0.4)' : '#ffffff'} - } - } - } - } -` - -const ChatContainer = styled.div` - display: flex; - flex-direction: column; - /* flex : 1; */ - height : calc(100% - 2.5rem); - width: 100%; - border-radius: 16px; - /* border: 1px solid rgba(255, 255, 255, 0.1); */ -`; - -const MessageContainer = styled.div` - flex: 1; - overflow-y: auto; - padding: 24px; - position: relative; - scroll-behavior: smooth; - - &::-webkit-scrollbar { - width: 6px; - } - - &::-webkit-scrollbar-thumb { - background: rgba(217, 35, 255, 0.2); - border-radius: 3px; - - &:hover { - background: rgba(217, 35, 255, 0.3); - } - } -`; - -const WelcomeTitle = styled.div` - padding: 0 0 2rem 0; - display: flex; - flex-direction: column; - gap: .5rem; - animation: ${fadeIn} 0.6s ease-out; -`; - -const WelcomeMessage = styled.div` - display: flex; - flex-direction: column; - gap: .5rem; - align-items: flex-start; - background: rgba(105, 14, 124, 0.05); - border: 1px solid rgba(217, 35, 255, 0.1); - padding: 1.5rem; - border-radius: 1rem; - position: absolute; - bottom: 0; - left: 50%; - transform: translate(-50%, -10%); // 초기 위치를 최종 위치와 동일하게 설정 - backdrop-filter: blur(10px); - animation: ${WelcomeFadeIn} 0.6s ease-out; - max-width: calc(100% - 36px - 36px - 24px); - width : 60%; - box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1); -`; - -const WelcomeMessageTitle = styled.div` -display : flex; -flex-direction: row; -gap : 1rem; -align-items: center; -justify-content: center; -` - -const SparklesIcon = styled(Sparkles)` - display: inline; - margin-right: 0.5rem; - color: #d923ff; - width: 24px; - height: 24px; -`; - -const BotIcon = styled.div` - width: 2.5rem; - height: 2.5rem; - background: linear-gradient(135deg, #d923ff, #a78bfa); - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - box-shadow: 0 2px 10px rgba(217, 35, 255, 0.2); -`; - -const MessageContent = styled.div` - color: #e4e4e7; - font-size: 0.95rem; - line-height: 1.6; - - strong { - color: white; - font-size : 1.3rem; - font-weight: 500; - display: flex; - align-items: center; - gap: 0.5rem; - /* margin-bottom: 0.5rem; */ - } - - ul { - margin-top: 0.75rem; - padding-left : 3.5rem; - li { - margin: 0.5rem 0; - padding-left: 1rem; - position: relative; - - &:before { - content: "•"; - color: #d923ff; - position: absolute; - left: 0; - } - } - } -`; - -const Badge = styled.span` -padding: 0.125rem 0.5rem; -background-color: #d923ff; -font-size: 0.75rem; -border-radius: 9999px; -color: white; -margin-left: 0.5rem; -font-weight: 500; -margin : 0 0 0.2rem 0.2rem; -`; - - -const ChatMessages = styled.div` - display: flex; - flex-direction: column; - gap: 16px; - padding-bottom: 120px; -`; - -const MessageBubble = styled.div` - display: flex; - align-items: flex-start; - gap: 12px; - margin: ${props => props.isUser ? '0 0 0 auto' : '0 auto 0 0'}; - max-width: 70%; - animation: ${fadeIn} 0.3s ease-out; -`; - -const Avatar = styled.div` - min-width: 36px; - min-height: 36px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - background: ${props => props.isUser ? - 'linear-gradient(135deg, #d923ff, #a78bfa)' : - 'rgba(63, 63, 70, 0.8)'}; - color: white; - order: ${props => props.isUser ? 1 : 0}; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); -`; - -const Message = styled.div` - background: rgba(63, 63, 70, 0.8); - padding: 14px 18px; - border-radius: 16px; - color: white; - font-size: 0.95rem; - line-height: 1.5; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); - backdrop-filter: blur(10px); - ${props => props.isUser ? ` - background: transparent; - border : 1px solid #a78bfa - `: - null - } -`; - -const InputContainer = styled.div` - padding: 20px; - background: rgba(255, 255, 255, 0.03); - border-top: 1px solid rgba(255, 255, 255, 0.06); - border-radius: 0 0 16px 16px; -`; - -const InputWrapper = styled.div` - display: flex; - gap: 12px; - align-items: center; - background: rgba(255, 255, 255, 0.07); - border-radius: 12px; - padding: 8px 16px; - transition: all 0.2s ease; - border: 1px solid rgba(255, 255, 255, 0.1); - - &:focus-within { - background: rgba(255, 255, 255, 0.09); - border-color: rgba(217, 35, 255, 0.3); - box-shadow: 0 0 0 2px rgba(217, 35, 255, 0.1); - } -`; - -const Input = styled.input` - flex: 1; - background: transparent; - border: none; - color: white; - font-size: 0.95rem; - padding: 10px 0; - - &::placeholder { - color: rgba(255, 255, 255, 0.3); - } - - &:focus { - outline: none; - } -`; - -const ActionButton = styled.button` - background: transparent; - border: none; - color: ${props => props.disabled ? 'rgba(255, 255, 255, 0.2)' : '#d923ff'}; - cursor: ${props => props.disabled ? 'not-allowed' : 'pointer'}; - padding: 8px; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.2s; - border-radius: 8px; - - &:hover:not(:disabled) { - background: rgba(217, 35, 255, 0.1); - color: #e048ff; - } - - &:active:not(:disabled) { - transform: scale(0.95); - } -`; - -const LoadingDots = styled.div` - display: flex; - gap: 4px; - align-items: center; - padding: 4px 8px; - - span { - width: 4px; - height: 4px; - border-radius: 50%; - background: #d923ff; - animation: ${shimmer} 1.5s infinite; - opacity: 0.7; - - &:nth-child(2) { animation-delay: 0.2s; } - &:nth-child(3) { animation-delay: 0.4s; } - } -`; - -const ChatbotUI = () => { - const { activeRepositoryId } = useRegisteredRepoStore(); - const token = useAuthStore(state => state.token); - - const { - chatHistory, - isLoading: isChatLoading, - isError, - error, - sendMessage, - resetChat - } = useChatbot(activeRepositoryId); - - const [streamingResponse, setStreamingResponse] = useState(''); - - const [messages, setMessages] = useState([]); - const [inputText, setInputText] = useState(''); - const messagesEndRef = useRef(null); - const [isMenuOpen, setIsMenuOpen] = useState(false); - const menuRef = useRef(null); // 메뉴 ref 생성 - - - const [isTest, setIsTest] = useState(0); - - - - - // 채팅 히스토리 동기화 - useEffect(() => { - if (chatHistory) { - const formattedMessages = chatHistory.flatMap(chat => [ - { id: `q-${chat.id}`, text: chat.question, isUser: true }, - { id: `a-${chat.id}`, text: chat.text, isUser: false } - ]); - setMessages(formattedMessages); - } - }, [chatHistory]); - - // 스트리밍 응답이 업데이트될 때마다 메시지 업데이트 - useEffect(() => { - if (streamingResponse) { - console.log("스트리밍 응답이 업데이트될 때마다 메시지 업데이트", streamingResponse) - const lastMessage = messages[messages.length - 1]; - if (!lastMessage || lastMessage.isUser) { - // 새로운 AI 응답 메시지 추가 - setMessages(prev => [...prev, { - id: `stream-${Date.now()}`, - text: streamingResponse, - isUser: false - }]); - } else { - // 기존 AI 응답 메시지 업데이트 - setMessages(prev => { - const newMessages = [...prev]; - newMessages[newMessages.length - 1] = { - ...newMessages[newMessages.length - 1], - text: streamingResponse - }; - return newMessages; - }); - } - } - }, [streamingResponse]); - - // 메시지 전송 핸들러 - const handleSend = async () => { - if (!inputText.trim() || isChatLoading || !token || !activeRepositoryId) return; - - const userMessageId = Date.now(); - setMessages(prev => [...prev, { - id: `q-${userMessageId}`, - text: inputText, - isUser: true - }]); - - setInputText(''); - setStreamingResponse(''); - - try { - const response = await fetch( - `${process.env.REACT_APP_API_BASE_URL}api/chatbot/stream-and-save/${activeRepositoryId}`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token}` - }, - body: JSON.stringify({ - question: inputText - }) - } - ); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const result = await response.text(); - console.log(result); - - setMessages(prev => [...prev, { - id: `a-${userMessageId}`, - text: result, - isUser: false - }]); - - } catch (error) { - setMessages(prev => [...prev, { - id: `error-${userMessageId}`, - text: "죄송합니다. 오류가 발생했습니다. 다시 시도해 주세요.", - isUser: false, - isError: true - }]); - console.error('채팅 요청 에러:', error); - } - }; - - // 대화 초기화 핸들러 - const handleReset = async () => { - await resetChat(); - setMessages([]); - setStreamingResponse(''); - - }; - - - - useClickAway(menuRef, () => { - if (isMenuOpen) { - setIsMenuOpen(false); - } - }); - - const scrollToBottom = () => { - messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); - }; - - useEffect(() => { - scrollToBottom(); - console.log(messages) - }, [messages]); - - - - const handleKeyPress = (e) => { - if (e.key === 'Enter' && !e.shiftKey) { - e.preventDefault(); - handleSend(); - } - }; - return ( - - - setIsMenuOpen(!isMenuOpen)}> - - -
    -
  • - - reset -
  • -
-
-
-
- - - - - {/* Welcome message */} - - - - Dododocs AI Chat - - - 연결된 GitHub 레포지토리의 코드를 기반으로 답변을 제공해드립니다. - - - { - messages.length === 0 ? - - - - - - - - DODODOCS AI - {/* AI 챗봇 */} - -

- 안녕하세요! GitHub 레포지토리의 코드에 대해 궁금하신 점을 물어보세요. - 자세한 분석과 함께 답변해드리겠습니다. -

-
-
- -
    -
  • 코드 구조와 아키텍처 분석
  • -
  • 함수와 클래스의 역할 설명
  • -
  • 코드 개선 방안 제안
  • -
  • 버그 해결 방안 제시
  • -
-
-
- : null - } - - {/* Chat messages */} - - {messages.map(message => ( - - - {message.isUser ? : } - - - { - message.isUser ? - message.text - : - handleMessageComplete(message.id)} - onComplete={() => console.log('타이핑 완료')} // 필요한 경우에만 사용 - - /> - - } - {/* */} - {/* {message.text} */} - - - ))} - {isChatLoading && ( - - - - - - - - - - - - - )} -
- - - - - - setInputText(e.target.value)} - onKeyPress={(e) => e.key === 'Enter' && !e.shiftKey && handleSend()} - disabled={isChatLoading} - /> - - - - - - - - - - - ); -}; - -export default ChatbotUI; - - diff --git a/src/components/organisms/RepoDetailContent/RepoDetailContent.jsx b/src/components/organisms/RepoDetailContent/RepoDetailContent.jsx index fd055e0..bb34ed7 100644 --- a/src/components/organisms/RepoDetailContent/RepoDetailContent.jsx +++ b/src/components/organisms/RepoDetailContent/RepoDetailContent.jsx @@ -10,7 +10,7 @@ import useAppModalStore from '../../../store/appModalStore.js'; import Modal from 'react-modal'; -import Chatting from "./Chatting.jsx" +import Chatting from "./CattingDetailContent/Chatting.jsx" import Document from "./Document.jsx" import ReadMe from "./ReadMe.jsx" diff --git a/src/components/organisms/SearchContent/index.jsx b/src/components/organisms/SearchContent/index.jsx deleted file mode 100644 index 75642ff..0000000 --- a/src/components/organisms/SearchContent/index.jsx +++ /dev/null @@ -1,105 +0,0 @@ -import React from "react"; -import { Row, Col, ContentStyle } from "../../../layout/index.js" -import { Image, Typo } from "../../atoms/index.js" -import { GoodsForm } from "../../molecules/index.js" -import { Carousel } from 'antd'; -import mainImage from "../../../assets/images/mainImage2.png" -import mainImage_2 from "../../../assets/images/mainImage3.jpg" - -import Pagination from '@mui/material/Pagination'; - -const SearchContent = ({ - pagingClick, - pagingNum, - lists, - collectionProductOnClick, - totalPageNum, - keywordValue -}) => { - const onChange = (currentSlide) => { - console.log(currentSlide); - }; - const contentStyle = { - margin: 0, - // height: 'auto', - color: '#fff', - textAlign: 'center', - background: '#364d79', - height: "300px" - }; - return ( - <> - {/* //SECTION Carousel */} - -
- -
-
- -
-
- {/* //!SECTION Carousel */} - - - - {/* //SECTION Title */} - - -

'{keywordValue}' 

검색 결과
- - {/* //!SECTION Title */} - {/* //SECTION Content */} - - - - {/* //SECTION list */} - - - - { - (lists) ? - lists.map((lists) => { - return ( - collectionProductOnClick(lists.id)} - /> - ) - }) - : - null - } - { - lists.length === 0 ? - - 등록된 상품이 없습니다. - - : null - } - - - - {/* //!SECTION list */} - {/* //SECTION Pagination */} - - - - - - - {/* //!SECTION Pagination */} - -
-
- - ) -} - -export default SearchContent; \ No newline at end of file diff --git a/src/constants/guides/LiveInformationController.js b/src/constants/guides/LiveInformationController.js index e69de29..2f8ca39 100644 --- a/src/constants/guides/LiveInformationController.js +++ b/src/constants/guides/LiveInformationController.js @@ -0,0 +1,171 @@ +export const LiveInformationController = ` +# 시스템 아키텍처 문서 + +## 전체 구조 +\`\`\`mermaid +graph TD + A[Client] --> B[LiveInformationController] + B --> C[LiveInformationService] + C --> D[LiveInformationRepository] + C --> E[TripLiveInformationRepository] + C --> F[TripRepository] + D --> G[Database] + E --> G + F --> G +\`\`\` + +## 시스템 흐름 +\`\`\`mermaid +sequenceDiagram + Client->>LiveInformationController: GET /api/live/info/all + LiveInformationController->>LiveInformationService: findAllLiveInformation() + LiveInformationService->>LiveInformationRepository: findAll() + LiveInformationRepository-->>LiveInformationService: List + LiveInformationService-->>LiveInformationController: FindAllLiveInformationResponse + LiveInformationController-->>Client: 200 OK + + Client->>LiveInformationController: POST /api/live/info + LiveInformationController->>LiveInformationService: createLiveInformation(request) + LiveInformationService->>LiveInformationRepository: save(liveInformation) + LiveInformationRepository-->>LiveInformationService: LiveInformation + LiveInformationService-->>LiveInformationController: void + LiveInformationController-->>Client: 204 No Content + + Client->>LiveInformationController: POST /api/live/info/trip/{tripId}/{liveInfoId} + LiveInformationController->>LiveInformationService: createTripLiveInformation(tripId, liveInfoId) + LiveInformationService->>TripRepository: findById(tripId) + TripRepository-->>LiveInformationService: Trip + LiveInformationService->>LiveInformationRepository: findById(liveInfoId) + LiveInformationRepository-->>LiveInformationService: LiveInformation + LiveInformationService->>TripLiveInformationRepository: save(new TripLiveInformation(liveInformation, trip)) + TripLiveInformationRepository-->>LiveInformationService: void + LiveInformationService-->>LiveInformationController: void + LiveInformationController-->>Client: 204 No Content +\`\`\` + +## 주요 컴포넌트 설명 + +### LiveInformationController +- 역할과 책임: 클라이언트의 요청을 처리하고, 서비스 계층과 상호작용하여 응답을 반환합니다. +- 주요 메서드: + - \`findAllLiveInformation()\`: 모든 생활 정보를 조회합니다. + - \`createLiveInformation()\`: 새로운 생활 정보를 생성합니다. + - \`createTripLiveInformation()\`: 여행지와 생활 정보를 연결합니다. + +### LiveInformationService +- 비즈니스 로직을 처리하며, 데이터베이스와의 상호작용을 관리합니다. +- 주요 메서드: + - \`findAllLiveInformation()\`: 모든 생활 정보를 조회하여 응답 객체를 생성합니다. + - \`createLiveInformation()\`: 새로운 생활 정보를 생성하고 저장합니다. + - \`createTripLiveInformation()\`: 여행지와 생활 정보를 연결하여 저장합니다. + - \`findByName()\`: 이름으로 생활 정보를 조회합니다. + +### LiveInformationRepository +- 데이터베이스와의 CRUD 작업을 수행합니다. + +### TripLiveInformationRepository +- 여행지와 생활 정보 간의 관계를 관리합니다. + +### TripRepository +- 여행지에 대한 CRUD 작업을 수행합니다. + +## API 엔드포인트 + +### Find All Live Information +**GET** \`/api/live/info/all\` + +#### 설명 +모든 생활 정보를 조회합니다. + +#### 요청 +- Headers: 없음 + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "liveInformationResponses": [ + { + "name": "생활정보1" + }, + { + "name": "생활정보2" + } + ] +} +\`\`\` + +##### Error Response +- Status: 500 Internal Server Error +\`\`\`json +{ + "error": "서버 오류 메시지" +} +\`\`\` + +### Create Live Information +**POST** \`/api/live/info\` + +#### 설명 +새로운 생활 정보를 생성합니다. + +#### 요청 +##### Request Body +\`\`\`json +{ + "name": "새로운 생활정보" +} +\`\`\` + +#### 응답 +##### Success Response +- Status: 204 No Content + +##### Error Response +- Status: 400 Bad Request +\`\`\`json +{ + "error": "생활정보 이름은 공백일 수 없습니다." +} +\`\`\` + +### Create Trip Live Information +**POST** \`/api/live/info/trip/{tripId}/{liveInfoId}\` + +#### 설명 +여행지와 생활 정보를 연결합니다. + +#### 요청 +- Path Variables: + - \`tripId\`: 여행지 ID + - \`liveInfoId\`: 생활 정보 ID + +#### 응답 +##### Success Response +- Status: 204 No Content + +##### Error Response +- Status: 404 Not Found +\`\`\`json +{ + "error": "존재하지 않는 여행지입니다." +} +\`\`\`json +{ + "error": "존재하지 않는 생활정보입니다." +} +\`\`\` + +## SUMMARY + +### Component Overview +- **LiveInformationController**: 클라이언트 요청을 처리하고 서비스 계층과 상호작용. +- **LiveInformationService**: 비즈니스 로직 처리 및 데이터베이스와의 상호작용. +- **Repositories**: 데이터베이스 CRUD 작업 수행. + +### Architecture & Implementation +- **구성 요소 상호 작용**: Controller는 Service를 호출하고, Service는 Repository를 통해 데이터베이스와 상호작용. +- **주요 흐름**: 클라이언트 요청 -> Controller -> Service -> Repository -> Database. + +`; diff --git a/src/router/index.jsx b/src/router/index.jsx index 3f30fc0..e89d53e 100644 --- a/src/router/index.jsx +++ b/src/router/index.jsx @@ -3,7 +3,6 @@ import React, { Suspense, lazy } from 'react'; import { Navigate, BrowserRouter as Router, Routes, Route } from 'react-router-dom'; -// import { notification } from 'antd'; import Home from './HomeRouter.jsx'; import Docs from './DocsRouter.jsx'; import Login from "./LoginRouter.jsx" diff --git a/src/utils/jwtUtils.js b/src/utils/jwtUtils.js index 7d113bd..07163cf 100644 --- a/src/utils/jwtUtils.js +++ b/src/utils/jwtUtils.js @@ -1,7 +1,5 @@ // src/utils/jwtUtils.js import { jwtDecode } from 'jwt-decode'; -import { notification } from 'antd'; - const TOKEN_KEY = 'golla-golla'; const jwtUtils = { @@ -60,11 +58,11 @@ const jwtUtils = { localStorage.removeItem(TOKEN_KEY); - notification.error({ - message: '로그인에 실패하였습니다.', - description: '다시 시도해주세요', - duration: 2, - }); + // notification.error({ + // message: '로그인에 실패하였습니다.', + // description: '다시 시도해주세요', + // duration: 2, + // }); }, }; diff --git a/yarn.lock b/yarn.lock index 71e7adf..1539668 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20,69 +20,6 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" -"@ant-design/colors@^7.0.0", "@ant-design/colors@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-7.1.0.tgz#60eadfa2e21871d8948dac5d50b9f056062f8af3" - integrity sha512-MMoDGWn1y9LdQJQSHiCC20x3uZ3CwQnv9QMz6pCmJOrqdgM9YxsoVVY0wtrdXbmfSgnV0KNk6zi09NAhMR2jvg== - dependencies: - "@ctrl/tinycolor" "^3.6.1" - -"@ant-design/cssinjs-utils@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@ant-design/cssinjs-utils/-/cssinjs-utils-1.1.1.tgz#57abb43671023f937348bd33442862c60ac8e8b2" - integrity sha512-2HAiyGGGnM0es40SxdszeQAU5iWp41wBIInq+ONTCKjlSKOrzQfnw4JDtB8IBmqE6tQaEKwmzTP2LGdt5DSwYQ== - dependencies: - "@ant-design/cssinjs" "^1.21.0" - "@babel/runtime" "^7.23.2" - rc-util "^5.38.0" - -"@ant-design/cssinjs@^1.21.0", "@ant-design/cssinjs@^1.21.1": - version "1.21.1" - resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.21.1.tgz#7320813c5f747e0cde52c388eff5198d78d57230" - integrity sha512-tyWnlK+XH7Bumd0byfbCiZNK43HEubMoCcu9VxwsAwiHdHTgWa+tMN0/yvxa+e8EzuFP1WdUNNPclRpVtD33lg== - dependencies: - "@babel/runtime" "^7.11.1" - "@emotion/hash" "^0.8.0" - "@emotion/unitless" "^0.7.5" - classnames "^2.3.1" - csstype "^3.1.3" - rc-util "^5.35.0" - stylis "^4.3.3" - -"@ant-design/fast-color@^2.0.6": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@ant-design/fast-color/-/fast-color-2.0.6.tgz#ab4d4455c1542c9017d367c2fa8ca3e4215d0ba2" - integrity sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA== - dependencies: - "@babel/runtime" "^7.24.7" - -"@ant-design/icons-svg@^4.4.0": - version "4.4.2" - resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz#ed2be7fb4d82ac7e1d45a54a5b06d6cecf8be6f6" - integrity sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA== - -"@ant-design/icons@^5.5.1": - version "5.5.1" - resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-5.5.1.tgz#4ff57b2a0d3bafae3d990c2781fd857ead36c935" - integrity sha512-0UrM02MA2iDIgvLatWrj6YTCYe0F/cwXvVE0E2SqGrL7PZireQwgEKTKBisWpZyal5eXZLvuM98kju6YtYne8w== - dependencies: - "@ant-design/colors" "^7.0.0" - "@ant-design/icons-svg" "^4.4.0" - "@babel/runtime" "^7.24.8" - classnames "^2.2.6" - rc-util "^5.31.1" - -"@ant-design/react-slick@~1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@ant-design/react-slick/-/react-slick-1.1.2.tgz#f84ce3e4d0dc941f02b16f1d1d6d7a371ffbb4f1" - integrity sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA== - dependencies: - "@babel/runtime" "^7.10.4" - classnames "^2.2.5" - json2mq "^0.2.0" - resize-observer-polyfill "^1.5.1" - throttle-debounce "^5.0.0" - "@antfu/install-pkg@^0.4.0": version "0.4.1" resolved "https://registry.yarnpkg.com/@antfu/install-pkg/-/install-pkg-0.4.1.tgz#d1d7f3be96ecdb41581629cafe8626d1748c0cf1" @@ -1245,13 +1182,6 @@ "@babel/plugin-transform-modules-commonjs" "^7.25.7" "@babel/plugin-transform-typescript" "^7.25.7" -"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.8", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.6", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1" - integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== - dependencies: - regenerator-runtime "^0.14.0" - "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": version "7.25.7" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.7.tgz#7ffb53c37a8f247c8c4d335e89cdf16a2e0d0fb6" @@ -1259,6 +1189,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.18.3", "@babel/runtime@^7.23.8", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1" + integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/template@^7.25.7", "@babel/template@^7.3.3": version "7.25.7" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.7.tgz#27f69ce382855d915b14ab0fe5fb4cbf88fa0769" @@ -1473,11 +1410,6 @@ resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz#2cbcf822bf3764c9658c4d2e568bd0c0cb748016" integrity sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw== -"@ctrl/tinycolor@^3.6.1": - version "3.6.1" - resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz#b6c75a56a1947cc916ea058772d666a2c8932f31" - integrity sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA== - "@emotion/babel-plugin@^11.12.0": version "11.12.0" resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz#7b43debb250c313101b3f885eba634f1d723fcc2" @@ -1506,11 +1438,6 @@ "@emotion/weak-memoize" "^0.4.0" stylis "4.2.0" -"@emotion/hash@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" - integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== - "@emotion/hash@^0.9.2": version "0.9.2" resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.2.tgz#ff9221b9f58b4dfe61e619a7788734bd63f6898b" @@ -1592,11 +1519,6 @@ resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.10.0.tgz#2af2f7c7e5150f497bdabd848ce7b218a27cf745" integrity sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg== -"@emotion/unitless@^0.7.5": - version "0.7.5" - resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" - integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== - "@emotion/use-insertion-effect-with-fallbacks@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz#1a818a0b2c481efba0cf34e5ab1e0cb2dcb9dfaf" @@ -2043,88 +1965,6 @@ schema-utils "^4.2.0" source-map "^0.7.3" -"@rc-component/async-validator@^5.0.3": - version "5.0.4" - resolved "https://registry.yarnpkg.com/@rc-component/async-validator/-/async-validator-5.0.4.tgz#5291ad92f00a14b6766fc81735c234277f83e948" - integrity sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg== - dependencies: - "@babel/runtime" "^7.24.4" - -"@rc-component/color-picker@~2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@rc-component/color-picker/-/color-picker-2.0.1.tgz#6b9b96152466a9d4475cbe72b40b594bfda164be" - integrity sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q== - dependencies: - "@ant-design/fast-color" "^2.0.6" - "@babel/runtime" "^7.23.6" - classnames "^2.2.6" - rc-util "^5.38.1" - -"@rc-component/context@^1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@rc-component/context/-/context-1.4.0.tgz#dc6fb021d6773546af8f016ae4ce9aea088395e8" - integrity sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w== - dependencies: - "@babel/runtime" "^7.10.1" - rc-util "^5.27.0" - -"@rc-component/mini-decimal@^1.0.1": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@rc-component/mini-decimal/-/mini-decimal-1.1.0.tgz#7b7a362b14a0a54cb5bc6fd2b82731f29f11d9b0" - integrity sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ== - dependencies: - "@babel/runtime" "^7.18.0" - -"@rc-component/mutate-observer@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@rc-component/mutate-observer/-/mutate-observer-1.1.0.tgz#ee53cc88b78aade3cd0653609215a44779386fd8" - integrity sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw== - dependencies: - "@babel/runtime" "^7.18.0" - classnames "^2.3.2" - rc-util "^5.24.4" - -"@rc-component/portal@^1.0.0-8", "@rc-component/portal@^1.0.0-9", "@rc-component/portal@^1.0.2", "@rc-component/portal@^1.1.0", "@rc-component/portal@^1.1.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@rc-component/portal/-/portal-1.1.2.tgz#55db1e51d784e034442e9700536faaa6ab63fc71" - integrity sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg== - dependencies: - "@babel/runtime" "^7.18.0" - classnames "^2.3.2" - rc-util "^5.24.4" - -"@rc-component/qrcode@~1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@rc-component/qrcode/-/qrcode-1.0.0.tgz#48a8de5eb11d0e65926f1377c4b1ef4c888997f5" - integrity sha512-L+rZ4HXP2sJ1gHMGHjsg9jlYBX/SLN2D6OxP9Zn3qgtpMWtO2vUfxVFwiogHpAIqs54FnALxraUy/BCO1yRIgg== - dependencies: - "@babel/runtime" "^7.24.7" - classnames "^2.3.2" - rc-util "^5.38.0" - -"@rc-component/tour@~1.15.1": - version "1.15.1" - resolved "https://registry.yarnpkg.com/@rc-component/tour/-/tour-1.15.1.tgz#9b79808254185fc19e964172d99e25e8c6800ded" - integrity sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ== - dependencies: - "@babel/runtime" "^7.18.0" - "@rc-component/portal" "^1.0.0-9" - "@rc-component/trigger" "^2.0.0" - classnames "^2.3.2" - rc-util "^5.24.4" - -"@rc-component/trigger@^2.0.0", "@rc-component/trigger@^2.1.1", "@rc-component/trigger@^2.2.3": - version "2.2.3" - resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-2.2.3.tgz#b47e945115e2d0a7f7e067dbb9ed76c91c1b4385" - integrity sha512-X1oFIpKoXAMXNDYCviOmTfuNuYxE4h5laBsyCqVAVMjNHxoF3/uiyA7XdegK1XbCvBbCZ6P6byWrEoDRpKL8+A== - dependencies: - "@babel/runtime" "^7.23.2" - "@rc-component/portal" "^1.1.0" - classnames "^2.3.2" - rc-motion "^2.0.0" - rc-resize-observer "^1.3.1" - rc-util "^5.38.0" - "@reduxjs/toolkit@^2.3.0": version "2.3.0" resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-2.3.0.tgz#d00134634d6c1678e8563ac50026e429e3b64420" @@ -3389,61 +3229,6 @@ ansi-styles@^6.1.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== -antd@^5.21.5: - version "5.21.5" - resolved "https://registry.yarnpkg.com/antd/-/antd-5.21.5.tgz#c82f14b36f6821490ccd30d8befaa65a11b5e782" - integrity sha512-g/c8VkdruKDCVA6di9Ow1fG6dLtYJ1IOraPo7vXaY7DoQ56A3HExaFaH0fBEwTYKC0ICeftC4iA5eAjrF6/b9w== - dependencies: - "@ant-design/colors" "^7.1.0" - "@ant-design/cssinjs" "^1.21.1" - "@ant-design/cssinjs-utils" "^1.1.1" - "@ant-design/icons" "^5.5.1" - "@ant-design/react-slick" "~1.1.2" - "@babel/runtime" "^7.25.6" - "@ctrl/tinycolor" "^3.6.1" - "@rc-component/color-picker" "~2.0.1" - "@rc-component/mutate-observer" "^1.1.0" - "@rc-component/qrcode" "~1.0.0" - "@rc-component/tour" "~1.15.1" - "@rc-component/trigger" "^2.2.3" - classnames "^2.5.1" - copy-to-clipboard "^3.3.3" - dayjs "^1.11.11" - rc-cascader "~3.28.2" - rc-checkbox "~3.3.0" - rc-collapse "~3.8.0" - rc-dialog "~9.6.0" - rc-drawer "~7.2.0" - rc-dropdown "~4.2.0" - rc-field-form "~2.4.0" - rc-image "~7.11.0" - rc-input "~1.6.3" - rc-input-number "~9.2.0" - rc-mentions "~2.16.1" - rc-menu "~9.15.1" - rc-motion "^2.9.3" - rc-notification "~5.6.2" - rc-pagination "~4.3.0" - rc-picker "~4.6.15" - rc-progress "~4.0.0" - rc-rate "~2.13.0" - rc-resize-observer "^1.4.0" - rc-segmented "~2.5.0" - rc-select "~14.15.2" - rc-slider "~11.1.7" - rc-steps "~6.0.1" - rc-switch "~4.1.0" - rc-table "~7.47.5" - rc-tabs "~15.3.0" - rc-textarea "~1.8.2" - rc-tooltip "~6.2.1" - rc-tree "~5.9.0" - rc-tree-select "~5.23.0" - rc-upload "~4.8.1" - rc-util "^5.43.0" - scroll-into-view-if-needed "^3.1.0" - throttle-debounce "^5.0.2" - any-promise@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" @@ -3511,11 +3296,6 @@ array-includes@^3.1.6, array-includes@^3.1.8: get-intrinsic "^1.2.4" is-string "^1.0.7" -array-tree-filter@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array-tree-filter/-/array-tree-filter-2.1.0.tgz#873ac00fec83749f255ac8dd083814b4f6329190" - integrity sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw== - array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" @@ -3940,6 +3720,11 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +btoa@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/btoa/-/btoa-1.2.1.tgz#01a9909f8b2c93f6bf680ba26131eb30f7fa3d73" + integrity sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g== + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -4138,11 +3923,6 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz#707413784dbb3a72aa11c2f2b042a0bef4004170" integrity sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA== -classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1, classnames@^2.3.2, classnames@^2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" - integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== - clean-css@^5.2.2: version "5.3.3" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.3.tgz#b330653cd3bd6b75009cc25c714cae7b93351ccd" @@ -4274,11 +4054,6 @@ compression@^1.7.4: safe-buffer "5.1.2" vary "~1.1.2" -compute-scroll-into-view@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-3.1.0.tgz#753f11d972596558d8fe7c6bcbc8497690ab4c87" - integrity sha512-rj8l8pD4bJ1nx+dAkMhV1xB5RuZEyVysfxJqB1pRchh1KVvwOv9b7CGB8ZfjTImVv2oF+sYMUkMZq6Na5Ftmbg== - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -4331,13 +4106,6 @@ cookie@0.7.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== -copy-to-clipboard@^3.3.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" - integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA== - dependencies: - toggle-selection "^1.0.6" - core-js-compat@^3.38.0, core-js-compat@^3.38.1: version "3.38.1" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.38.1.tgz#2bc7a298746ca5a7bcb9c164bcb120f2ebc09a09" @@ -4614,7 +4382,7 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" -csstype@3.1.3, csstype@^3.0.2, csstype@^3.1.3: +csstype@3.1.3, csstype@^3.0.2: version "3.1.3" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== @@ -4873,11 +4641,6 @@ d3-zoom@3: d3-selection "2 - 3" d3-transition "2 - 3" -d3@3.5.17: - version "3.5.17" - resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz#bc46748004378b21a360c9fc7cf5231790762fb8" - integrity sha512-yFk/2idb8OHPKkbAL8QaOaqENNoMhIaSHZerk3oQsECwkObkCpJyjYwCe+OHiq6UEdhe1m8ZGARRRO3ljFjlKg== - d3@^7.9.0: version "7.9.0" resolved "https://registry.yarnpkg.com/d3/-/d3-7.9.0.tgz#579e7acb3d749caf8860bd1741ae8d371070cd5d" @@ -4922,24 +4685,6 @@ dagre-d3-es@7.0.11: d3 "^7.9.0" lodash-es "^4.17.21" -dagre-d3-renderer@^0.4.25: - version "0.4.26" - resolved "https://registry.yarnpkg.com/dagre-d3-renderer/-/dagre-d3-renderer-0.4.26.tgz#648a491209b853ae96ddf3fea41a1f104479a5a1" - integrity sha512-vOWj1uA4/APTrfDyfHaH/xpfXhPh9rszW+HOaEwPCeA6Afl06Lobfh7OpESuVMQW2QGuY4UQ7pte/p0WhdDs7w== - dependencies: - d3 "3.5.17" - dagre-layout "^0.8.0" - graphlib "^2.1.1" - lodash "^4.17.4" - -dagre-layout@^0.8.0: - version "0.8.8" - resolved "https://registry.yarnpkg.com/dagre-layout/-/dagre-layout-0.8.8.tgz#9b6792f24229f402441c14162c1049e3f261f6d9" - integrity sha512-ZNV15T9za7X+fV8Z07IZquUKugCxm5owoiPPxfEx6OJRD331nkiIaF3vSt0JEY5FkrY0KfRQxcpQ3SpXB7pLPQ== - dependencies: - graphlibrary "^2.2.0" - lodash "^4.17.5" - damerau-levenshtein@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" @@ -4981,7 +4726,7 @@ data-view-byte-offset@^1.0.0: es-errors "^1.3.0" is-data-view "^1.0.1" -dayjs@^1.11.10, dayjs@^1.11.11: +dayjs@^1.11.10: version "1.11.13" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c" integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== @@ -5303,7 +5048,7 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -ejs@^3.1.6: +ejs@^3.1.5, ejs@^3.1.6: version "3.1.10" resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== @@ -5528,7 +5273,7 @@ escalade@^3.1.1, escalade@^3.2.0: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== -escape-html@~1.0.3: +escape-html@^1.0.3, escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== @@ -6376,20 +6121,6 @@ graphemer@^1.4.0: resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== -graphlib@^2.1.1: - version "2.1.8" - resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.8.tgz#5761d414737870084c92ec7b5dbcb0592c9d35da" - integrity sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A== - dependencies: - lodash "^4.17.15" - -graphlibrary@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/graphlibrary/-/graphlibrary-2.2.0.tgz#017a14899775228dec4497a39babfdd6bf56eac6" - integrity sha512-XTcvT55L8u4MBZrM37zXoUxsgxs/7sow7YSygd9CIwfWTVO8RVu7AYXhhCiTuFEf+APKgx6Jk4SuQbYR0vYKmQ== - dependencies: - lodash "^4.17.5" - gzip-size@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" @@ -6512,7 +6243,7 @@ hast-util-whitespace@^3.0.0: dependencies: "@types/hast" "^3.0.0" -he@^1.1.1, he@^1.2.0: +he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== @@ -7080,7 +6811,7 @@ is-weakset@^2.0.3: call-bind "^1.0.7" get-intrinsic "^1.2.4" -is-wsl@^2.2.0: +is-wsl@^2.1.1, is-wsl@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== @@ -7806,13 +7537,6 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== -json2mq@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/json2mq/-/json2mq-0.2.0.tgz#b637bd3ba9eabe122c83e9720483aeb10d2c904a" - integrity sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA== - dependencies: - string-convert "^0.2.0" - json5@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" @@ -8065,7 +7789,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== -lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.7.0: +lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -8144,18 +7868,6 @@ markdown-it-anchor@^9.2.0: resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-9.2.0.tgz#89375d9a2a79336403ab7c4fd36b1965cc45e5c8" integrity sha512-sa2ErMQ6kKOA4l31gLGYliFQrMKkqSO0ZJgGhDHKijPf0pNFM9vghjAh3gn26pS4JDRs7Iwa9S36gxm3vgZTzg== -markdown-it-mermaid@^0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/markdown-it-mermaid/-/markdown-it-mermaid-0.2.5.tgz#aa6e488a5f4ab2006b4a63e22f315683495c1877" - integrity sha512-ZUTFRX+cXEtWmn/9LMlpVklPJiDrHPWyHE/wamC2wm0Ojh1qOcuKWfWW3BqP83+7w6C59rS7M3OrGTs/u9mQTA== - dependencies: - mermaid "^7.1.2" - -markdown-it-toc-done-right@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/markdown-it-toc-done-right/-/markdown-it-toc-done-right-4.2.0.tgz#3ccdce22d5022ffae7b991d07261b1b1de5459d0" - integrity sha512-UB/IbzjWazwTlNAX0pvWNlJS8NKsOQ4syrXZQ/C72j+jirrsjVRT627lCaylrKJFBQWfRsPmIVQie8x38DEhAQ== - markdown-it@^14.1.0: version "14.1.0" resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-14.1.0.tgz#3c3c5992883c633db4714ccb4d7b5935d98b7d45" @@ -8354,18 +8066,6 @@ mermaid@^11.4.1: ts-dedent "^2.2.0" uuid "^9.0.1" -mermaid@^7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-7.1.2.tgz#6265728156c2e0891e004cba60a44022174487ad" - integrity sha512-bDLu3fQuf3/R0fNkNzB0GTaF7+6SxnZpfTs9DVQF1ougsuP23MBzvEIGfL0ML8zeyg7+J2D+0AaoLVhskW5ulw== - dependencies: - d3 "3.5.17" - dagre-d3-renderer "^0.4.25" - dagre-layout "^0.8.0" - he "^1.1.1" - lodash "^4.17.4" - moment "^2.20.1" - methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" @@ -8654,7 +8354,7 @@ minimist@^1.2.0, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== -mkdirp@~0.5.1: +mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== @@ -8671,11 +8371,6 @@ mlly@^1.7.1, mlly@^1.7.2, mlly@^1.7.3: pkg-types "^1.2.1" ufo "^1.5.4" -moment@^2.20.1: - version "2.30.1" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" - integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -8928,6 +8623,14 @@ onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +open@^7.3.1: + version "7.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" + integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + open@^8.0.9, open@^8.4.0: version "8.4.2" resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" @@ -9925,357 +9628,6 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" -rc-cascader@~3.28.2: - version "3.28.2" - resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.28.2.tgz#91720d3498261a7bff9fffc953501a8830f601fb" - integrity sha512-8f+JgM83iLTvjgdkgU7GfI4qY8icXOBP0cGZjOdx2iJAkEe8ucobxDQAVE69UD/c3ehCxZlcgEHeD5hFmypbUw== - dependencies: - "@babel/runtime" "^7.12.5" - array-tree-filter "^2.1.0" - classnames "^2.3.1" - rc-select "~14.15.0" - rc-tree "~5.9.0" - rc-util "^5.37.0" - -rc-checkbox@~3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/rc-checkbox/-/rc-checkbox-3.3.0.tgz#0ffcb65ab78c7d2fcd1a0d6554af36786516bd02" - integrity sha512-Ih3ZaAcoAiFKJjifzwsGiT/f/quIkxJoklW4yKGho14Olulwn8gN7hOBve0/WGDg5o/l/5mL0w7ff7/YGvefVw== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "^2.3.2" - rc-util "^5.25.2" - -rc-collapse@~3.8.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.8.0.tgz#02bcf81e1601aa185cd3b9fab0ceefd8dc11aefb" - integrity sha512-YVBkssrKPBG09TGfcWWGj8zJBYD9G3XuTy89t5iUmSXrIXEAnO1M+qjUxRW6b4Qi0+wNWG6MHJF/+US+nmIlzA== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "2.x" - rc-motion "^2.3.4" - rc-util "^5.27.0" - -rc-dialog@~9.6.0: - version "9.6.0" - resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-9.6.0.tgz#dc7a255c6ad1cb56021c3a61c7de86ee88c7c371" - integrity sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg== - dependencies: - "@babel/runtime" "^7.10.1" - "@rc-component/portal" "^1.0.0-8" - classnames "^2.2.6" - rc-motion "^2.3.0" - rc-util "^5.21.0" - -rc-drawer@~7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-7.2.0.tgz#8d7de2f1fd52f3ac5a25f54afbb8ac14c62e5663" - integrity sha512-9lOQ7kBekEJRdEpScHvtmEtXnAsy+NGDXiRWc2ZVC7QXAazNVbeT4EraQKYwCME8BJLa8Bxqxvs5swwyOepRwg== - dependencies: - "@babel/runtime" "^7.23.9" - "@rc-component/portal" "^1.1.1" - classnames "^2.2.6" - rc-motion "^2.6.1" - rc-util "^5.38.1" - -rc-dropdown@~4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-4.2.0.tgz#c6052fcfe9c701487b141e411cdc277dc7c6f061" - integrity sha512-odM8Ove+gSh0zU27DUj5cG1gNKg7mLWBYzB5E4nNLrLwBmYEgYP43vHKDGOVZcJSVElQBI0+jTQgjnq0NfLjng== - dependencies: - "@babel/runtime" "^7.18.3" - "@rc-component/trigger" "^2.0.0" - classnames "^2.2.6" - rc-util "^5.17.0" - -rc-field-form@~2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-2.4.0.tgz#26997160d12ae43a94c356c1290bfc011c69b3ca" - integrity sha512-XZ/lF9iqf9HXApIHQHqzJK5v2w4mkUMsVqAzOyWVzoiwwXEavY6Tpuw7HavgzIoD+huVff4JghSGcgEfX6eycg== - dependencies: - "@babel/runtime" "^7.18.0" - "@rc-component/async-validator" "^5.0.3" - rc-util "^5.32.2" - -rc-image@~7.11.0: - version "7.11.0" - resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-7.11.0.tgz#18c77ea557a6fdbe26856c688a9aace1505c0e77" - integrity sha512-aZkTEZXqeqfPZtnSdNUnKQA0N/3MbgR7nUnZ+/4MfSFWPFHZau4p5r5ShaI0KPEMnNjv4kijSCFq/9wtJpwykw== - dependencies: - "@babel/runtime" "^7.11.2" - "@rc-component/portal" "^1.0.2" - classnames "^2.2.6" - rc-dialog "~9.6.0" - rc-motion "^2.6.2" - rc-util "^5.34.1" - -rc-input-number@~9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-9.2.0.tgz#7e9344ff054421d2bfff0eebd7c1b8ef22d12220" - integrity sha512-5XZFhBCV5f9UQ62AZ2hFbEY8iZT/dm23Q1kAg0H8EvOgD3UDbYYJAayoVIkM3lQaCqYAW5gV0yV3vjw1XtzWHg== - dependencies: - "@babel/runtime" "^7.10.1" - "@rc-component/mini-decimal" "^1.0.1" - classnames "^2.2.5" - rc-input "~1.6.0" - rc-util "^5.40.1" - -rc-input@~1.6.0, rc-input@~1.6.3: - version "1.6.3" - resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-1.6.3.tgz#f1708fc3d5e68f95cb20faeb3eed1df8543cd444" - integrity sha512-wI4NzuqBS8vvKr8cljsvnTUqItMfG1QbJoxovCgL+DX4eVUcHIjVwharwevIxyy7H/jbLryh+K7ysnJr23aWIA== - dependencies: - "@babel/runtime" "^7.11.1" - classnames "^2.2.1" - rc-util "^5.18.1" - -rc-mentions@~2.16.1: - version "2.16.1" - resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-2.16.1.tgz#5e54ebe3ce6cd79838846ff1c8cfaf2e7aa15cec" - integrity sha512-GnhSTGP9Mtv6pqFFGQze44LlrtWOjHNrUUAcsdo9DnNAhN4pwVPEWy4z+2jpjkiGlJ3VoXdvMHcNDQdfI9fEaw== - dependencies: - "@babel/runtime" "^7.22.5" - "@rc-component/trigger" "^2.0.0" - classnames "^2.2.6" - rc-input "~1.6.0" - rc-menu "~9.15.1" - rc-textarea "~1.8.0" - rc-util "^5.34.1" - -rc-menu@~9.15.1: - version "9.15.1" - resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.15.1.tgz#d8b38ea534a7f596a8da063881519e7eaafca698" - integrity sha512-UKporqU6LPfHnpPmtP6hdEK4iO5Q+b7BRv/uRpxdIyDGplZy9jwUjsnpev5bs3PQKB0H0n34WAPDfjAfn3kAPA== - dependencies: - "@babel/runtime" "^7.10.1" - "@rc-component/trigger" "^2.0.0" - classnames "2.x" - rc-motion "^2.4.3" - rc-overflow "^1.3.1" - rc-util "^5.27.0" - -rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4, rc-motion@^2.6.1, rc-motion@^2.6.2, rc-motion@^2.9.0, rc-motion@^2.9.3: - version "2.9.3" - resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.9.3.tgz#b1bdaf816f1ccb3e4b3b0c531c3037a59286379e" - integrity sha512-rkW47ABVkic7WEB0EKJqzySpvDqwl60/tdkY7hWP7dYnh5pm0SzJpo54oW3TDUGXV5wfxXFmMkxrzRRbotQ0+w== - dependencies: - "@babel/runtime" "^7.11.1" - classnames "^2.2.1" - rc-util "^5.43.0" - -rc-notification@~5.6.2: - version "5.6.2" - resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-5.6.2.tgz#8525b32d49dd96ec974acae61d1d1eabde61463a" - integrity sha512-Id4IYMoii3zzrG0lB0gD6dPgJx4Iu95Xu0BQrhHIbp7ZnAZbLqdqQ73aIWH0d0UFcElxwaKjnzNovTjo7kXz7g== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "2.x" - rc-motion "^2.9.0" - rc-util "^5.20.1" - -rc-overflow@^1.3.1, rc-overflow@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.3.2.tgz#72ee49e85a1308d8d4e3bd53285dc1f3e0bcce2c" - integrity sha512-nsUm78jkYAoPygDAcGZeC2VwIg/IBGSodtOY3pMof4W3M9qRJgqaDYm03ZayHlde3I6ipliAxbN0RUcGf5KOzw== - dependencies: - "@babel/runtime" "^7.11.1" - classnames "^2.2.1" - rc-resize-observer "^1.0.0" - rc-util "^5.37.0" - -rc-pagination@~4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-4.3.0.tgz#c6022f820aa3a45fd734ae33a2915d39597dce1d" - integrity sha512-UubEWA0ShnroQ1tDa291Fzw6kj0iOeF26IsUObxYTpimgj4/qPCWVFl18RLZE+0Up1IZg0IK4pMn6nB3mjvB7g== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "^2.3.2" - rc-util "^5.38.0" - -rc-picker@~4.6.15: - version "4.6.15" - resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-4.6.15.tgz#1531c9c382a295e2d1f1f38440d6678b09cd0468" - integrity sha512-OWZ1yrMie+KN2uEUfYCfS4b2Vu6RC1FWwNI0s+qypsc3wRt7g+peuZKVIzXCTaJwyyZruo80+akPg2+GmyiJjw== - dependencies: - "@babel/runtime" "^7.24.7" - "@rc-component/trigger" "^2.0.0" - classnames "^2.2.1" - rc-overflow "^1.3.2" - rc-resize-observer "^1.4.0" - rc-util "^5.43.0" - -rc-progress@~4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-4.0.0.tgz#5382147d9add33d3a5fbd264001373df6440e126" - integrity sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "^2.2.6" - rc-util "^5.16.1" - -rc-rate@~2.13.0: - version "2.13.0" - resolved "https://registry.yarnpkg.com/rc-rate/-/rc-rate-2.13.0.tgz#642f591ccf55c3a5d84d8d212caf1f7951d203a8" - integrity sha512-oxvx1Q5k5wD30sjN5tqAyWTvJfLNNJn7Oq3IeS4HxWfAiC4BOXMITNAsw7u/fzdtO4MS8Ki8uRLOzcnEuoQiAw== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "^2.2.5" - rc-util "^5.0.1" - -rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.3.1, rc-resize-observer@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.4.0.tgz#7bba61e6b3c604834980647cce6451914750d0cc" - integrity sha512-PnMVyRid9JLxFavTjeDXEXo65HCRqbmLBw9xX9gfC4BZiSzbLXKzW3jPz+J0P71pLbD5tBMTT+mkstV5gD0c9Q== - dependencies: - "@babel/runtime" "^7.20.7" - classnames "^2.2.1" - rc-util "^5.38.0" - resize-observer-polyfill "^1.5.1" - -rc-segmented@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/rc-segmented/-/rc-segmented-2.5.0.tgz#3b5423adf57459345c77c39c7581fde786a16c11" - integrity sha512-B28Fe3J9iUFOhFJET3RoXAPFJ2u47QvLSYcZWC4tFYNGPEjug5LAxEasZlA/PpAxhdOPqGWsGbSj7ftneukJnw== - dependencies: - "@babel/runtime" "^7.11.1" - classnames "^2.2.1" - rc-motion "^2.4.4" - rc-util "^5.17.0" - -rc-select@~14.15.0, rc-select@~14.15.2: - version "14.15.2" - resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.15.2.tgz#d85fcf3a708bdf837b003feeed653347b8980ad0" - integrity sha512-oNoXlaFmpqXYcQDzcPVLrEqS2J9c+/+oJuGrlXeVVX/gVgrbHa5YcyiRUXRydFjyuA7GP3elRuLF7Y3Tfwltlw== - dependencies: - "@babel/runtime" "^7.10.1" - "@rc-component/trigger" "^2.1.1" - classnames "2.x" - rc-motion "^2.0.1" - rc-overflow "^1.3.1" - rc-util "^5.16.1" - rc-virtual-list "^3.5.2" - -rc-slider@~11.1.7: - version "11.1.7" - resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-11.1.7.tgz#3de333b1ec84d53a7bda2f816bb4779423628f09" - integrity sha512-ytYbZei81TX7otdC0QvoYD72XSlxvTihNth5OeZ6PMXyEDq/vHdWFulQmfDGyXK1NwKwSlKgpvINOa88uT5g2A== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "^2.2.5" - rc-util "^5.36.0" - -rc-steps@~6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/rc-steps/-/rc-steps-6.0.1.tgz#c2136cd0087733f6d509209a84a5c80dc29a274d" - integrity sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g== - dependencies: - "@babel/runtime" "^7.16.7" - classnames "^2.2.3" - rc-util "^5.16.1" - -rc-switch@~4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/rc-switch/-/rc-switch-4.1.0.tgz#f37d81b4e0c5afd1274fd85367b17306bf25e7d7" - integrity sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg== - dependencies: - "@babel/runtime" "^7.21.0" - classnames "^2.2.1" - rc-util "^5.30.0" - -rc-table@~7.47.5: - version "7.47.5" - resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.47.5.tgz#3c530200baa82346c7e72fe9b1dbd47d4aa15838" - integrity sha512-fzq+V9j/atbPIcvs3emuclaEoXulwQpIiJA6/7ey52j8+9cJ4P8DGmp4YzfUVDrb3qhgedcVeD6eRgUrokwVEQ== - dependencies: - "@babel/runtime" "^7.10.1" - "@rc-component/context" "^1.4.0" - classnames "^2.2.5" - rc-resize-observer "^1.1.0" - rc-util "^5.41.0" - rc-virtual-list "^3.14.2" - -rc-tabs@~15.3.0: - version "15.3.0" - resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-15.3.0.tgz#3fcc332fbb9307d5eb147e0404daca871fb92a89" - integrity sha512-lzE18r+zppT/jZWOAWS6ntdkDUKHOLJzqMi5UAij1LeKwOaQaupupAoI9Srn73GRzVpmGznkECMRrzkRusC40A== - dependencies: - "@babel/runtime" "^7.11.2" - classnames "2.x" - rc-dropdown "~4.2.0" - rc-menu "~9.15.1" - rc-motion "^2.6.2" - rc-resize-observer "^1.0.0" - rc-util "^5.34.1" - -rc-textarea@~1.8.0, rc-textarea@~1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-1.8.2.tgz#57a6847304551c1883fc3fb0c5076d587f70bf7f" - integrity sha512-UFAezAqltyR00a8Lf0IPAyTd29Jj9ee8wt8DqXyDMal7r/Cg/nDt3e1OOv3Th4W6mKaZijjgwuPXhAfVNTN8sw== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "^2.2.1" - rc-input "~1.6.0" - rc-resize-observer "^1.0.0" - rc-util "^5.27.0" - -rc-tooltip@~6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-6.2.1.tgz#9a8f0335c86443a0c20c2557933205f645a381b7" - integrity sha512-rws0duD/3sHHsD905Nex7FvoUGy2UBQRhTkKxeEvr2FB+r21HsOxcDJI0TzyO8NHhnAA8ILr8pfbSBg5Jj5KBg== - dependencies: - "@babel/runtime" "^7.11.2" - "@rc-component/trigger" "^2.0.0" - classnames "^2.3.1" - -rc-tree-select@~5.23.0: - version "5.23.0" - resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.23.0.tgz#e56da0923c7c11dea98d4e14bb76969283c94468" - integrity sha512-aQGi2tFSRw1WbXv0UVXPzHm09E0cSvUVZMLxQtMv3rnZZpNmdRXWrnd9QkLNlVH31F+X5rgghmdSFF3yZW0N9A== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "2.x" - rc-select "~14.15.0" - rc-tree "~5.9.0" - rc-util "^5.16.1" - -rc-tree@~5.9.0: - version "5.9.0" - resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.9.0.tgz#1835b2bef36cfeb4ec15d62e0319fc503aa485f1" - integrity sha512-CPrgOvm9d/9E+izTONKSngNzQdIEjMox2PBufWjS1wf7vxtvmCWzK1SlpHbRY6IaBfJIeZ+88RkcIevf729cRg== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "2.x" - rc-motion "^2.0.1" - rc-util "^5.16.1" - rc-virtual-list "^3.5.1" - -rc-upload@~4.8.1: - version "4.8.1" - resolved "https://registry.yarnpkg.com/rc-upload/-/rc-upload-4.8.1.tgz#ac55f2bc101b95b52a6e47f3c18f0f55b54e16d2" - integrity sha512-toEAhwl4hjLAI1u8/CgKWt30BR06ulPa4iGQSMvSXoHzO88gPCslxqV/mnn4gJU7PDoltGIC9Eh+wkeudqgHyw== - dependencies: - "@babel/runtime" "^7.18.3" - classnames "^2.2.5" - rc-util "^5.2.0" - -rc-util@^5.0.1, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.2.0, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.24.4, rc-util@^5.25.2, rc-util@^5.27.0, rc-util@^5.30.0, rc-util@^5.31.1, rc-util@^5.32.2, rc-util@^5.34.1, rc-util@^5.35.0, rc-util@^5.36.0, rc-util@^5.37.0, rc-util@^5.38.0, rc-util@^5.38.1, rc-util@^5.40.1, rc-util@^5.41.0, rc-util@^5.43.0: - version "5.43.0" - resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.43.0.tgz#bba91fbef2c3e30ea2c236893746f3e9b05ecc4c" - integrity sha512-AzC7KKOXFqAdIBqdGWepL9Xn7cm3vnAmjlHqUnoQaTMZYhM4VlXGLkkHHxj/BZ7Td0+SOPKB4RGPboBVKT9htw== - dependencies: - "@babel/runtime" "^7.18.3" - react-is "^18.2.0" - -rc-virtual-list@^3.14.2, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2: - version "3.14.8" - resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.14.8.tgz#abf6e8809b7f5c955aa7f59c2a9d57443e9942fd" - integrity sha512-8D0KfzpRYi6YZvlOWIxiOm9BGt4Wf2hQyEaM6RXlDDiY2NhLheuYI+RA+7ZaZj1lq+XQqy3KHlaeeXQfzI5fGg== - dependencies: - "@babel/runtime" "^7.20.0" - classnames "^2.2.6" - rc-resize-observer "^1.0.0" - rc-util "^5.36.0" - react-app-polyfill@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz#95221e0a9bd259e5ca6b177c7bb1cb6768f68fd7" @@ -10341,7 +9693,7 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-is@^18.0.0, react-is@^18.2.0: +react-is@^18.0.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== @@ -10691,11 +10043,6 @@ reselect@^5.1.0: resolved "https://registry.yarnpkg.com/reselect/-/reselect-5.1.1.tgz#c766b1eb5d558291e5e550298adb0becc24bb72e" integrity sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w== -resize-observer-polyfill@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" - integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== - resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" @@ -10764,6 +10111,13 @@ rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" +rimraf@~2.6.2: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + robust-predicates@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-3.0.2.tgz#d5b28528c4824d20fc48df1928d41d9efa1ad771" @@ -10911,13 +10265,6 @@ schema-utils@^4.0.0, schema-utils@^4.2.0: ajv-formats "^2.1.1" ajv-keywords "^5.1.0" -scroll-into-view-if-needed@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz#fa9524518c799b45a2ef6bbffb92bcad0296d01f" - integrity sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ== - dependencies: - compute-scroll-into-view "^3.0.2" - select-hose@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" @@ -11100,6 +10447,24 @@ source-list-map@^2.0.0, source-list-map@^2.0.1: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== +source-map-explorer@^2.5.3: + version "2.5.3" + resolved "https://registry.yarnpkg.com/source-map-explorer/-/source-map-explorer-2.5.3.tgz#33551b51e33b70f56d15e79083cdd4c43e583b69" + integrity sha512-qfUGs7UHsOBE5p/lGfQdaAj/5U/GWYBw2imEpD6UQNkqElYonkow8t+HBL1qqIl3CuGZx7n8/CQo4x1HwSHhsg== + dependencies: + btoa "^1.2.1" + chalk "^4.1.0" + convert-source-map "^1.7.0" + ejs "^3.1.5" + escape-html "^1.0.3" + glob "^7.1.6" + gzip-size "^6.0.0" + lodash "^4.17.20" + open "^7.3.1" + source-map "^0.7.4" + temp "^0.9.4" + yargs "^16.2.0" + source-map-js@^1.0.1, source-map-js@^1.2.0, source-map-js@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" @@ -11132,7 +10497,7 @@ source-map@^0.5.7: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== -source-map@^0.7.3: +source-map@^0.7.3, source-map@^0.7.4: version "0.7.4" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== @@ -11223,11 +10588,6 @@ stop-iteration-iterator@^1.0.0: dependencies: internal-slot "^1.0.4" -string-convert@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/string-convert/-/string-convert-0.2.1.tgz#6982cc3049fbb4cd85f8b24568b9d9bf39eeff97" - integrity sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A== - string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -11457,7 +10817,7 @@ stylis@4.3.2: resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.2.tgz#8f76b70777dd53eb669c6f58c997bf0a9972e444" integrity sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg== -stylis@^4.3.1, stylis@^4.3.3: +stylis@^4.3.1: version "4.3.4" resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.4.tgz#ca5c6c4a35c4784e4e93a2a24dc4e9fa075250a4" integrity sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now== @@ -11594,6 +10954,14 @@ temp-dir@^2.0.0: resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-2.0.0.tgz#bde92b05bdfeb1516e804c9c00ad45177f31321e" integrity sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg== +temp@^0.9.4: + version "0.9.4" + resolved "https://registry.yarnpkg.com/temp/-/temp-0.9.4.tgz#cd20a8580cb63635d0e4e9d4bd989d44286e7620" + integrity sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA== + dependencies: + mkdirp "^0.5.1" + rimraf "~2.6.2" + tempy@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tempy/-/tempy-0.6.0.tgz#65e2c35abc06f1124a97f387b08303442bde59f3" @@ -11666,11 +11034,6 @@ throat@^6.0.1: resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.2.tgz#51a3fbb5e11ae72e2cf74861ed5c8020f89f29fe" integrity sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ== -throttle-debounce@^5.0.0, throttle-debounce@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-5.0.2.tgz#ec5549d84e053f043c9fd0f2a6dd892ff84456b1" - integrity sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A== - thunky@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" @@ -11698,11 +11061,6 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -toggle-selection@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" - integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ== - toidentifier@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" From f53343941abb1de2aebde79e10003b06880de4f8 Mon Sep 17 00:00:00 2001 From: euncherry Date: Mon, 23 Dec 2024 06:38:34 +0900 Subject: [PATCH 06/27] =?UTF-8?q?=EB=A7=88=ED=81=AC=EB=8B=A4=EC=9A=B4=20?= =?UTF-8?q?=EB=9E=9C=EB=8D=94=EB=A7=81=20=ED=95=84=EC=9A=94=ED=95=9C=20?= =?UTF-8?q?=EC=96=B8=EC=96=B4=EB=A7=8C=20=EB=93=B1=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 3 +- src/App.js | 1 + .../MarkdownRenderer.styles.js | 246 +++++++ .../atoms/MarkdownRenderer/index.jsx | 410 +++-------- .../atoms/MarkdownRenderer/init.jsx | 225 ------ .../atoms/MarkdownRenderer/markdown-it.jsx | 185 ----- src/components/index.js | 2 - .../ChattingContent/ChattingContent.jsx | 193 ----- .../organisms/ChattingContent/ForKko.jsx | 187 ----- .../ChattingContent/ReadMeLanding.styles.js | 206 ------ .../organisms/DocsContent/DocsContent.jsx | 148 ---- .../DocsContent/DocsContent.styles.js | 93 --- .../organisms/DocsContent/FlexSplitter.jsx | 90 --- .../DocsContent/FlexSplitter.styles.js | 74 -- src/components/organisms/DocsContent/test.md | 252 ------- .../ChattingLanding/ChattingLanding.jsx | 3 +- .../organisms/Landing/LandingContent.jsx | 2 +- .../LandingReadme.jsx} | 2 +- .../ReadMeLanding.styles.js} | 0 .../CattingDetailContent/Chatting.jsx | 1 - src/pages/ChattingPage/index.jsx | 16 - src/pages/DocsPage/index.jsx | 17 - src/router/ChattingRouter.jsx | 9 - src/router/DocsRouter.jsx | 9 - src/router/index.jsx | 6 - yarn.lock | 688 +----------------- 26 files changed, 360 insertions(+), 2708 deletions(-) create mode 100644 src/components/atoms/MarkdownRenderer/MarkdownRenderer.styles.js delete mode 100644 src/components/atoms/MarkdownRenderer/init.jsx delete mode 100644 src/components/atoms/MarkdownRenderer/markdown-it.jsx delete mode 100644 src/components/organisms/ChattingContent/ChattingContent.jsx delete mode 100644 src/components/organisms/ChattingContent/ForKko.jsx delete mode 100644 src/components/organisms/ChattingContent/ReadMeLanding.styles.js delete mode 100644 src/components/organisms/DocsContent/DocsContent.jsx delete mode 100644 src/components/organisms/DocsContent/DocsContent.styles.js delete mode 100644 src/components/organisms/DocsContent/FlexSplitter.jsx delete mode 100644 src/components/organisms/DocsContent/FlexSplitter.styles.js delete mode 100644 src/components/organisms/DocsContent/test.md rename src/components/organisms/Landing/{ReadmeLanding/ReadmeLanding.jsx => LandingReadme/LandingReadme.jsx} (99%) rename src/components/organisms/Landing/{ReadmeLanding/ReadmeLanding.styles.js => LandingReadme/ReadMeLanding.styles.js} (100%) delete mode 100644 src/pages/ChattingPage/index.jsx delete mode 100644 src/pages/DocsPage/index.jsx delete mode 100644 src/router/ChattingRouter.jsx delete mode 100644 src/router/DocsRouter.jsx diff --git a/package.json b/package.json index 0ee1b1c..98e0f35 100644 --- a/package.json +++ b/package.json @@ -18,15 +18,14 @@ "markdown-it-anchor": "^9.2.0", "mermaid": "^11.4.1", "normalize.css": "^8.0.1", + "prismjs": "^1.29.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-markdown": "^9.0.1", "react-modal": "^3.16.1", "react-query": "^3.39.3", "react-redux": "^9.1.2", "react-router-dom": "^6.27.0", "react-scripts": "5.0.1", - "remark-html": "^16.0.1", "styled-components": "^6.1.13", "styled-reset": "^4.5.2", "web-vitals": "^2.1.0", diff --git a/src/App.js b/src/App.js index 00edf86..9e5a7cd 100644 --- a/src/App.js +++ b/src/App.js @@ -3,6 +3,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import './App.css'; import DododocsRouter from './router/index.jsx'; import AppInitializer from './components/core/AppInitalizer.jsx'; +import 'prismjs/themes/prism-tomorrow.css'; // 코드 하이라이팅 테마 const queryClient = new QueryClient({ defaultOptions: { queries: { diff --git a/src/components/atoms/MarkdownRenderer/MarkdownRenderer.styles.js b/src/components/atoms/MarkdownRenderer/MarkdownRenderer.styles.js new file mode 100644 index 0000000..09b5dfc --- /dev/null +++ b/src/components/atoms/MarkdownRenderer/MarkdownRenderer.styles.js @@ -0,0 +1,246 @@ +import styled from 'styled-components'; + +export const MarkdownContainer = styled.div` + color: #e4e4e7; + font-size: 0.95rem; + line-height: 1.7; + padding: 0rem 1.5rem 0rem 2rem; + + h1, + h2, + h3 { + color: white; + font-weight: 500; + margin: 2rem 0 1rem; + position: relative; // 부모에 relative 추가 + } + + h1 { + font-size: 2rem; + border-bottom: 1px solid rgba(63, 63, 70, 0.7); + } + h2 { + font-size: 1.5rem; + border-bottom: 1px solid rgba(63, 63, 70, 0.7); + } + h3 { + font-size: 1.25rem; + } + + p { + margin: 1rem 0; + } + + code { + padding: 0.2rem 0.4rem; + border-radius: 0.25rem; + font-family: 'JetBrains Mono', monospace; + font-size: 0.9em; + } + + pre { + background: rgba(63, 63, 70, 0.5); + padding: 1rem; + border-radius: 0.5rem; + overflow-x: auto; + border: 1px solid rgba(255, 255, 255, 0.1); + margin: 1.5rem 0; + } + + ul, + ol { + list-style: none; /* 기본 목록 스타일 제거 */ + padding-left: 1.5rem; + margin: 0.8rem 0; + + li { + position: relative; + margin: 0.6rem 0; + padding-left: 0.2rem; + transition: all 0.2s ease; + + &::before { + position: absolute; + left: -1.2rem; /* 불릿 또는 숫자 위치 */ + opacity: 0.9; + font-size: 1.1em; + transition: all 0.2s ease; + transform: translate(0, -0.05rem); + } + } + + /* ul 리스트의 불릿 스타일 */ + &:not(ol) > li::before { + content: '•'; /* 기본 불릿 기호 */ + color: #d923ff; + } + + /* ol 리스트의 숫자 스타일 */ + &[type='1'], + & { + counter-reset: list-counter; /* 리스트 초기화 */ + + > li { + counter-increment: list-counter; /* 숫자 증가 */ + &::before { + content: counter(list-counter) '.'; /* 숫자 형식 */ + color: rgba(255, 255, 255, 0.8); + } + } + } + + /* 중첩된 ul 리스트 스타일 */ + ul { + margin: 0.4rem 0; + padding-left: 1.6rem; + border-left: 1px dashed rgba(161, 161, 170, 0.2); + + > li::before { + content: '–'; /* 중첩된 리스트 불릿 */ + color: #e980ff; + font-size: 1em; + left: -1rem; + } + } + + /* 중첩된 ol 리스트 스타일 */ + ol { + counter-reset: nested-counter; /* 중첩 리스트 초기화 */ + + > li { + counter-increment: nested-counter; /* 중첩 숫자 증가 */ + &::before { + content: counter(nested-counter) '.'; /* 숫자 형식 */ + color: rgba(255, 255, 255, 0.8); + } + } + + /* 3단계 중첩 리스트 */ + ul, + ol { + margin: 0.4rem 0; + padding-left: 1.6rem; + border-left: 1px dashed rgba(161, 161, 170, 0.2); + + &:not(ol) > li::before { + content: '∘'; /* 중첩된 리스트 불릿 */ + color: #e980ff; + font-size: 1.2em; + } + + &[type='1'] > li::before { + content: counter(list-counter) '.' counter(nested-counter) '.' + counter(subnested-counter); /* 3단계 숫자 */ + counter-increment: subnested-counter; + color: #e980ff; + } + } + } + } + + blockquote { + border-left: 3px solid #d923ff; + padding-left: 1rem; + margin: 1.5rem 0; + color: rgba(255, 255, 255, 0.8); + background: rgba(217, 35, 255, 0.05); + padding: 1rem; + border-radius: 0 0.5rem 0.5rem 0; + } + + table { + width: 100%; + border-collapse: collapse; + margin: 1.5rem 0; + + th, + td { + border: 1px solid rgba(255, 255, 255, 0.1); + padding: 0.75rem; + text-align: left; + } + + th { + background: rgba(63, 63, 70, 0.8); + color: white; + } + + tr:nth-child(even) { + background: rgba(63, 63, 70, 0.3); + } + } + + a { + color: #d923ff; + text-decoration: none; + transition: all 0.2s; + + &:hover { + color: #e980ff; + text-decoration: underline; + } + } + + .mermaid { + background: rgba(63, 63, 70, 0.3); + padding: 1.5rem; + border-radius: 0.5rem; + margin: 1.5rem 0; + display: flex; + align-items: center; + justify-content: center; + + svg { + max-width: 100%; + height: auto; + } + } + + .toc-list { + list-style: none; + padding: 0; + margin: 0; + } + + .toc-item { + margin: 0.5rem 0; + padding-left: 1rem; + + &::before { + display: none; // 기존 bullet point 제거 + } + } + + .toc-link { + color: #e4e4e7; + text-decoration: none; + transition: color 0.2s; + + &:hover { + color: #d923ff; + } + } + + .header-anchor { + position: absolute; + font-size: 0.8rem; + left: -1.3rem; + top: 50%; + transform: translateY(calc(-50%)); + opacity: 0; + transition: opacity 0.2s; + text-decoration: none; + color: #d923ff; + + &:hover { + opacity: 1; + color: #e980ff; + } + } + + h1:hover .header-anchor, + h2:hover .header-anchor, + h3:hover .header-anchor { + opacity: 0.5; + } +`; diff --git a/src/components/atoms/MarkdownRenderer/index.jsx b/src/components/atoms/MarkdownRenderer/index.jsx index 28b3936..6e61d1e 100644 --- a/src/components/atoms/MarkdownRenderer/index.jsx +++ b/src/components/atoms/MarkdownRenderer/index.jsx @@ -1,283 +1,39 @@ -import React, { useEffect } from "react"; +import React, { useEffect, useMemo, useCallback } from "react"; +import { MarkdownContainer } from "./MarkdownRenderer.styles"; + import MarkdownIt from "markdown-it"; import markdownItAnchor from 'markdown-it-anchor'; -import markdownItTocDoneRight from 'markdown-it-toc-done-right'; -import styled from "styled-components"; import mermaid from "mermaid"; -import hljs from 'highlight.js'; - - -const MarkdownContainer = styled.div` - color: #e4e4e7; - font-size: 0.95rem; - line-height: 1.7; - padding: 0rem 1.5rem 0rem 2rem; - - h1, - h2, - h3 { - color: white; - font-weight: 500; - margin: 2rem 0 1rem; - position: relative; // 부모에 relative 추가 - } - - h1 { - font-size: 2rem; - border-bottom : 1px solid rgba(63, 63, 70, 0.7); - } - h2 { - font-size: 1.5rem; - border-bottom : 1px solid rgba(63, 63, 70, 0.7); - - } - h3 { - font-size: 1.25rem; - } - - p { - margin: 1rem 0; - } - - code { - padding: 0.2rem 0.4rem; - border-radius: 0.25rem; - font-family: "JetBrains Mono", monospace; - font-size: 0.9em; - } - - pre { - background: rgba(63, 63, 70, 0.5); - padding: 1rem; - border-radius: 0.5rem; - overflow-x: auto; - border: 1px solid rgba(255, 255, 255, 0.1); - margin: 1.5rem 0; - } - - ul,ol { - list-style: none; /* 기본 목록 스타일 제거 */ - padding-left: 1.5rem; - margin: 0.8rem 0; - - li { - position: relative; - margin: 0.6rem 0; - padding-left: 0.2rem; - transition: all 0.2s ease; - - &::before { - position: absolute; - left: -1.2rem; /* 불릿 또는 숫자 위치 */ - opacity: 0.9; - font-size: 1.1em; - transition: all 0.2s ease; - transform: translate(0,-0.05rem); - } - } - - /* ul 리스트의 불릿 스타일 */ - &:not(ol) > li::before { - content: "•"; /* 기본 불릿 기호 */ - color: #d923ff; - } - - /* ol 리스트의 숫자 스타일 */ - &[type="1"], - & { - counter-reset: list-counter; /* 리스트 초기화 */ - - > li { - counter-increment: list-counter; /* 숫자 증가 */ - &::before { - content: counter(list-counter) "."; /* 숫자 형식 */ - color: rgba(255, 255, 255, 0.8); - } - } - } +import hljs from 'highlight.js/lib/core'; - /* 중첩된 ul 리스트 스타일 */ - ul { - margin: 0.4rem 0; - padding-left: 1.6rem; - border-left: 1px dashed rgba(161, 161, 170, 0.2); +// highlight.js 필요한 언어만 등록 +import javascript from 'highlight.js/lib/languages/javascript'; +import python from 'highlight.js/lib/languages/python'; +import bash from 'highlight.js/lib/languages/bash'; +import java from 'highlight.js/lib/languages/java'; +import typescript from 'highlight.js/lib/languages/typescript'; - > li::before { - content: "–"; /* 중첩된 리스트 불릿 */ - color: #e980ff; - font-size: 1em; - left: -1rem; - } - } - /* 중첩된 ol 리스트 스타일 */ - ol { - counter-reset: nested-counter; /* 중첩 리스트 초기화 */ +// highlight.js 필요한 언어만 등록 +hljs.registerLanguage('javascript', javascript); +hljs.registerLanguage('python', python); +hljs.registerLanguage('bash', bash); +hljs.registerLanguage('java', java); +hljs.registerLanguage('typescript', typescript); - > li { - counter-increment: nested-counter; /* 중첩 숫자 증가 */ - &::before { - content: counter(nested-counter) "."; /* 숫자 형식 */ - color: rgba(255, 255, 255, 0.8); - } - } - /* 3단계 중첩 리스트 */ - ul, - ol { - margin: 0.4rem 0; - padding-left: 1.6rem; - border-left: 1px dashed rgba(161, 161, 170, 0.2); - - &:not(ol) > li::before { - content: "∘"; /* 중첩된 리스트 불릿 */ - color: #e980ff; - font-size: 1.2em; - } - - &[type="1"] > li::before { - content: counter(list-counter) "." counter(nested-counter) "." counter(subnested-counter); /* 3단계 숫자 */ - counter-increment: subnested-counter; - color: #e980ff; - } - } - } -} - - - - - blockquote { - border-left: 3px solid #d923ff; - padding-left: 1rem; - margin: 1.5rem 0; - color: rgba(255, 255, 255, 0.8); - background: rgba(217, 35, 255, 0.05); - padding: 1rem; - border-radius: 0 0.5rem 0.5rem 0; - } - - table { - width: 100%; - border-collapse: collapse; - margin: 1.5rem 0; - - th, - td { - border: 1px solid rgba(255, 255, 255, 0.1); - padding: 0.75rem; - text-align: left; - } - - th { - background: rgba(63, 63, 70, 0.8); - color: white; - } - - tr:nth-child(even) { - background: rgba(63, 63, 70, 0.3); - } - } - - a { - color: #d923ff; - text-decoration: none; - transition: all 0.2s; - - &:hover { - color: #e980ff; - text-decoration: underline; - } - } - - .mermaid { - background: rgba(63, 63, 70, 0.3); - padding: 1.5rem; - border-radius: 0.5rem; - margin: 1.5rem 0; - display: flex; - align-items: center; - justify-content: center; - - svg { - max-width: 100%; - height: auto; - } - } - - .toc-list { - list-style: none; - padding: 0; - margin: 0; - } - - .toc-item { - margin: 0.5rem 0; - padding-left: 1rem; - - &::before { - display: none; // 기존 bullet point 제거 - } - } - - .toc-link { - color: #e4e4e7; - text-decoration: none; - transition: color 0.2s; - - &:hover { - color: #d923ff; - } - } - - .header-anchor { - position: absolute; - font-size : 0.8rem; - left: -1.3rem; - top: 50%; - transform: translateY(calc(-50%)); - opacity: 0; - transition: opacity 0.2s; - text-decoration: none; - color: #d923ff; - - &:hover { - opacity: 1; - color: #e980ff; - } - } - - h1:hover .header-anchor, - h2:hover .header-anchor, - h3:hover .header-anchor { - opacity: 0.5; - } -`; -// // 커스텀 Mermaid 플러그인 정의 -// const mermaidPlugin = (md) => { -// const defaultFence = md.renderer.rules.fence.bind(md.renderer.rules); - -// md.renderer.rules.fence = (tokens, idx, options, env, self) => { -// const token = tokens[idx]; -// if (token.info === 'mermaid') { -// return `
${token.content}
`; -// } -// return defaultFence(tokens, idx, options, env, self); -// }; -// }; - - -// slug 생성 함수 +// URL에 사용할 수 있는 형태로 문자열 변환 const slugify = (s) => { return encodeURIComponent(String(s).trim().toLowerCase().replace(/\s+/g, '-')); }; -// 커스텀 Mermaid 플러그인 정의 +// Mermaid 다이어그램 플러그인 설정 const mermaidPlugin = (md) => { const defaultFence = md.renderer.rules.fence.bind(md.renderer.rules); md.renderer.rules.fence = (tokens, idx, options, env, self) => { const token = tokens[idx]; + // mermaid 코드 블록 처리 if (token.info === 'mermaid') { return `
${token.content}
`; } @@ -285,9 +41,8 @@ const mermaidPlugin = (md) => { }; }; - -// Mermaid 초기화 -mermaid.initialize({ +// Mermaid 초기설정 +const mermaidConfig = { startOnLoad: true, theme: 'dark', themeVariables: { @@ -300,33 +55,41 @@ mermaid.initialize({ }, fontFamily: 'JetBrains Mono, monospace', securityLevel: 'loose', -}); - + flowchart: { + useMaxWidth: true, + htmlLabels: true, + } +}; -// markdown-it 인스턴스 생성 및 설정 -const md = new MarkdownIt({ +// markdown-it 설정 +const markdownConfig = { html: true, linkify: true, typographer: true, breaks: true, indent: true, - // 중첩 목록 처리를 위한 설정 추가 - highlight: function (str, lang) { if (lang && hljs.getLanguage(lang)) { try { - return hljs.highlight(str, { language: lang }).value; - } catch (__) { } + return hljs.highlight(str, { // highlight -> hljs.highlight + language: lang, + ignoreIllegals: true + }).value; + } catch (err) { + console.error('Highlight error:', err); + } } - return ''; // 언어가 지정되지 않은 경우 기본 처리 + return '
' + md.utils.escapeHtml(str) + '
'; } -}) +}; +// markdown-it 인스턴스 생성 및 플러그인 설정 +const md = new MarkdownIt(markdownConfig) .use(markdownItAnchor, { permalink: true, permalinkBefore: true, + // 링크 아이콘 SVG permalinkSymbol: '', - - level: [1, 2], // h2 태그만 대상으로 지정 + level: [1, 2], // h1, h2 태그만 앵커 적용 slugify: slugify, // Table of Contents 섹션에만 앵커 표시 permalinkFilter: (slug, state) => { @@ -339,52 +102,81 @@ const md = new MarkdownIt({ const contentIndex = title.map[0] + 1; const content = state.tokens[contentIndex].content; - return content.includes('Table of Contents'); } }) - .use(mermaidPlugin) - + .use(mermaidPlugin); -const MarkdownViewer = ({ content }) => { +// 메인 컴포넌트 +const MarkdownViewer = React.memo(({ content }) => { + // Mermaid 초기화 useEffect(() => { - mermaid.contentLoaded(); + try { + mermaid.initialize(mermaidConfig); + mermaid.contentLoaded(); + } catch (error) { + console.error('Mermaid initialization error:', error); + } }, [content]); - const processMarkdown = (markdown) => { - const lines = markdown.split('\n'); - - // 모든 줄의 시작 공백 길이 확인 - const leadingSpaces = lines - .filter(line => line.trim()) // 빈 줄 제외 - .map(line => { - const match = line.match(/^\s+/); - return match ? match[0].length : 0; - }); - - // 가장 작은 공통 공백 길이 찾기 - const minCommonSpace = leadingSpaces.length > 0 - ? Math.min(...leadingSpaces) - : 0; + // 마크다운 처리 로직 + const processMarkdown = useCallback((markdown) => { + try { + const lines = markdown.split('\n'); + + // 공통 들여쓰기 공백 찾기 + const leadingSpaces = lines + .filter(line => line.trim()) + .map(line => { + const match = line.match(/^\s+/); + return match ? match[0].length : 0; + }); + + const minCommonSpace = leadingSpaces.length > 0 + ? Math.min(...leadingSpaces) + : 0; + + // 공통 들여쓰기 제거 + if (minCommonSpace > 0) { + const cleanedContent = lines + .map(line => (line.startsWith(' '.repeat(minCommonSpace)) ? line.slice(minCommonSpace) : line)) + .join('\n'); + return md.render(cleanedContent); + } - // 공통 공백이 있는 경우에만 제거 - if (minCommonSpace > 0) { - const cleanedContent = lines - .map(line => (line.startsWith(' '.repeat(minCommonSpace)) ? line.slice(minCommonSpace) : line)) - .join('\n'); - return md.render(cleanedContent); + return md.render(markdown); + } catch (err) { + console.error('Markdown processing error:', err); + return 'Error processing markdown content'; } + }, []); - // 공통 공백이 없으면 원본 그대로 렌더링 - return md.render(markdown); - }; + // 컨텐츠 메모이제이션 + const renderedContent = useMemo(() => + processMarkdown(content), + [content, processMarkdown] + ); + + // 성능 모니터링 + useEffect(() => { + performance.mark('markdown-render-start'); + return () => { + performance.mark('markdown-render-end'); + performance.measure( + 'markdown-render', + 'markdown-render-start', + 'markdown-render-end' + ); + }; + }, [content]); return ( ); -}; +}); +MarkdownViewer.displayName = 'MarkdownViewer'; export default MarkdownViewer; \ No newline at end of file diff --git a/src/components/atoms/MarkdownRenderer/init.jsx b/src/components/atoms/MarkdownRenderer/init.jsx deleted file mode 100644 index 1a5a6cb..0000000 --- a/src/components/atoms/MarkdownRenderer/init.jsx +++ /dev/null @@ -1,225 +0,0 @@ -import React, { useEffect, useRef } from 'react'; -import ReactMarkdown from 'react-markdown'; -import mermaid from 'mermaid'; -import styled from 'styled-components'; - -const MarkdownContainer = styled.div` - color: #e4e4e7; - font-size: 0.95rem; - line-height: 1.7; - padding: 1.5rem; - - h1, - h2, - h3 { - color: white; - font-weight: 500; - margin: 2rem 0 1rem; - } - - h1 { - font-size: 2rem; - } - h2 { - font-size: 1.5rem; - } - h3 { - font-size: 1.25rem; - } - - p { - margin: 1rem 0; - } - - code { - background: rgba(63, 63, 70, 0.5); - padding: 0.2rem 0.4rem; - border-radius: 0.25rem; - font-family: 'JetBrains Mono', monospace; - font-size: 0.9em; - } - - pre { - background: rgba(63, 63, 70, 0.5); - padding: 1rem; - border-radius: 0.5rem; - overflow-x: auto; - border: 1px solid rgba(255, 255, 255, 0.1); - margin: 1.5rem 0; - } - - ul, - ol { - padding-left: 1.5rem; - margin: 1rem 0; - - li { - margin: 0.5rem 0; - position: relative; - &::before { - content: '•'; - color: #d923ff; - position: absolute; - left: -1rem; - } - } - } - - blockquote { - border-left: 3px solid #d923ff; - padding-left: 1rem; - margin: 1.5rem 0; - color: rgba(255, 255, 255, 0.8); - background: rgba(217, 35, 255, 0.05); - padding: 1rem; - border-radius: 0 0.5rem 0.5rem 0; - } - - table { - width: 100%; - border-collapse: collapse; - margin: 1.5rem 0; - - th, - td { - border: 1px solid rgba(255, 255, 255, 0.1); - padding: 0.75rem; - text-align: left; - } - - th { - background: rgba(63, 63, 70, 0.8); - color: white; - } - - tr:nth-child(even) { - background: rgba(63, 63, 70, 0.3); - } - } - - a { - color: #d923ff; - text-decoration: none; - transition: all 0.2s; - - &:hover { - color: #e980ff; - text-decoration: underline; - } - } - - .mermaid { - background: rgba(63, 63, 70, 0.3); - padding: 1.5rem; - border-radius: 0.5rem; - margin: 1.5rem 0; - display: flex; - align-items: center; - justify-content: center; -} - svg { - max-width: 100%; - height: auto; - } - -`; - - -const MermaidRenderer = ({ content }) => { - const elementRef = useRef(); - - useEffect(() => { - let mounted = true; - - const renderDiagram = async () => { - if (!elementRef.current || !mounted) return; - - try { - const id = `mermaid-${Math.random().toString(36).slice(2)}`; - elementRef.current.innerHTML = ''; - const { svg } = await mermaid.render(id, content); - if (mounted && elementRef.current) { - elementRef.current.innerHTML = svg; - } - } catch (error) { - console.error('Mermaid rendering failed:', error); - if (mounted && elementRef.current) { - elementRef.current.innerHTML = 'Failed to render diagram'; - } - } - }; - - renderDiagram(); - - return () => { - mounted = false; - }; - }, [content]); - - return
; -}; - - -const MarkdownRenderer = ({ content }) => { - useEffect(() => { - mermaid.initialize({ - startOnLoad: false, - theme: 'dark', - themeVariables: { - primaryColor: '#d923ff', - primaryTextColor: '#e4e4e7', - primaryBorderColor: 'rgba(255, 255, 255, 0.1)', - lineColor: '#e4e4e7', - secondaryColor: 'rgba(63, 63, 70, 0.5)', - tertiaryColor: 'rgba(63, 63, 70, 0.3)', - }, - fontFamily: 'JetBrains Mono, monospace', - securityLevel: 'loose', - }); - }, []); - - const processMarkdown = (markdown) => { - // 1. 줄 단위로 분리하고 각 줄 양 끝의 공백 제거 - let lines = markdown.split('\n').map((line) => line.trim()); - - // 2. 빈 줄 제거 - lines = lines.filter((line) => line !== ''); - - // 3. 특정 키워드 대체 (예: `[TODO]` → `**TODO**`) - // lines = lines.map((line) => - // line.replace(/\[TODO\]/g, '**TODO**') // 원하는 키워드 대체 - // ); - - // 4. Mermaid 블록 감지 및 처리 - - - // 5. 최종적으로 다시 문자열로 합치기 - - return lines.join('\n') - // return markdown.split('\n').map((v) => v.trim()).join('\n') - - }; - - const components = { - - code({ inline, children, className }) { - const match = /language-mermaid/.test(className || ""); - return !inline && match ? ( - - ) : ( - {children} - ); - }, - }; - - return ( - - - {processMarkdown(content)} - {/* {content.split('\n').map((v) => v.trim()).join('\n')} */} - - - ); -}; - -export default MarkdownRenderer; \ No newline at end of file diff --git a/src/components/atoms/MarkdownRenderer/markdown-it.jsx b/src/components/atoms/MarkdownRenderer/markdown-it.jsx deleted file mode 100644 index 80b0471..0000000 --- a/src/components/atoms/MarkdownRenderer/markdown-it.jsx +++ /dev/null @@ -1,185 +0,0 @@ -import React, { useEffect, useRef } from "react"; -import MarkdownIt from "markdown-it"; -import styled from "styled-components"; -import mermaid from "mermaid"; - -const MarkdownContainer = styled.div` - color: #e4e4e7; - font-size: 0.95rem; - line-height: 1.7; - padding: 1.5rem; - - h1, - h2, - h3 { - color: white; - font-weight: 500; - margin: 2rem 0 1rem; - } - - h1 { - font-size: 2rem; - } - h2 { - font-size: 1.5rem; - } - h3 { - font-size: 1.25rem; - } - - p { - margin: 1rem 0; - } - - code { - background: rgba(63, 63, 70, 0.5); - padding: 0.2rem 0.4rem; - border-radius: 0.25rem; - font-family: "JetBrains Mono", monospace; - font-size: 0.9em; - } - - pre { - background: rgba(63, 63, 70, 0.5); - padding: 1rem; - border-radius: 0.5rem; - overflow-x: auto; - border: 1px solid rgba(255, 255, 255, 0.1); - margin: 1.5rem 0; - } - - ul, - ol { - padding-left: 1.5rem; - margin: 1rem 0; - - li { - margin: 0.5rem 0; - position: relative; - &::before { - content: "•"; - color: #d923ff; - position: absolute; - left: -1rem; - } - } - } - - blockquote { - border-left: 3px solid #d923ff; - padding-left: 1rem; - margin: 1.5rem 0; - color: rgba(255, 255, 255, 0.8); - background: rgba(217, 35, 255, 0.05); - padding: 1rem; - border-radius: 0 0.5rem 0.5rem 0; - } - - table { - width: 100%; - border-collapse: collapse; - margin: 1.5rem 0; - - th, - td { - border: 1px solid rgba(255, 255, 255, 0.1); - padding: 0.75rem; - text-align: left; - } - - th { - background: rgba(63, 63, 70, 0.8); - color: white; - } - - tr:nth-child(even) { - background: rgba(63, 63, 70, 0.3); - } - } - - a { - color: #d923ff; - text-decoration: none; - transition: all 0.2s; - - &:hover { - color: #e980ff; - text-decoration: underline; - } - } - - .mermaid { - background: rgba(63, 63, 70, 0.3); - padding: 1.5rem; - border-radius: 0.5rem; - margin: 1.5rem 0; - display: flex; - align-items: center; - justify-content: center; - - svg { - max-width: 100%; - height: auto; - } - } -`; -// 커스텀 Mermaid 플러그인 정의 -const mermaidPlugin = (md) => { - const defaultFence = md.renderer.rules.fence.bind(md.renderer.rules); - - md.renderer.rules.fence = (tokens, idx, options, env, self) => { - const token = tokens[idx]; - if (token.info === 'mermaid') { - return `
${token.content}
`; - } - return defaultFence(tokens, idx, options, env, self); - }; -}; - - -// Mermaid 초기화 -mermaid.initialize({ - startOnLoad: true, - theme: 'dark', - themeVariables: { - primaryColor: '#d923ff', - primaryTextColor: '#e4e4e7', - primaryBorderColor: 'rgba(255, 255, 255, 0.1)', - lineColor: '#e4e4e7', - secondaryColor: 'rgba(63, 63, 70, 0.5)', - tertiaryColor: 'rgba(63, 63, 70, 0.3)', - }, - fontFamily: 'JetBrains Mono, monospace', - securityLevel: 'loose', -}); - -// markdown-it 인스턴스 생성 및 설정 -const md = new MarkdownIt({ - html: true, - linkify: true, - typographer: true, - breaks: true -}).use(mermaidPlugin); - -const MarkdownViewer = ({ content }) => { - useEffect(() => { - // 마운트 시 Mermaid 다이어그램 렌더링 - mermaid.contentLoaded(); - }, [content]); - - const processMarkdown = (markdown) => { - const trimmedContent = markdown - .split('\n') - .map(line => line.trim()) - .join('\n'); - return md.render(trimmedContent); - }; - - return ( - - ); -}; - -export default MarkdownViewer; \ No newline at end of file diff --git a/src/components/index.js b/src/components/index.js index 7b43753..ce0dcc7 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -29,10 +29,8 @@ export { //ORGANiSM export { default as HomeContent } from './organisms/HomeContent/HomeContent.jsx'; -export { default as DocsContent } from './organisms//DocsContent/DocsContent.jsx'; export { default as LoginContent } from './organisms/LoginContent/LoginContent.jsx'; export { default as OAuthCallback } from './organisms/OAuthCallback/OAuthCallback.jsx'; -export { default as ChattingContent } from './organisms/ChattingContent/ChattingContent.jsx'; export { default as RepoContent } from './organisms/RepoContent/RepoContent.jsx'; export { default as RepoDetailContent } from './organisms/RepoDetailContent/RepoDetailContent.jsx'; export { default as LandingContent } from './organisms/Landing/LandingContent.jsx'; diff --git a/src/components/organisms/ChattingContent/ChattingContent.jsx b/src/components/organisms/ChattingContent/ChattingContent.jsx deleted file mode 100644 index 2d80bcf..0000000 --- a/src/components/organisms/ChattingContent/ChattingContent.jsx +++ /dev/null @@ -1,193 +0,0 @@ - - -// ReadmeLanding.jsx -import React from 'react'; -import { - Book, FileEdit, Layout, Sparkles, ArrowRight, - Download, BookOpen, FileCode, GitBranch, Flag -} from 'lucide-react'; -import { - PageWrapper, - PageContainer, - HeroSection, - ContentWrapper, - HeroGrid, - GradientText, - ButtonGroup, - Button, - Section, - FeatureWrapper, - FeatureIcon, - FeatureContent, - FeatureGrid, - StepsGrid, - StepCard, - StepIcon, - CTASection, - Badge, -} from './ReadMeLanding.styles'; - - - -const Feature = ({ icon: Icon, title, description }) => ( - - - - - -

{title}

-

{description}

-
-
-); - - -const ReadmeLanding = () => { - return ( - - - - - -
- - README Maker - -

- Create Beautiful{' '} - Documentation -

-

- Generate comprehensive, well-structured README files for your projects - with our intelligent documentation maker. -

- - - - -
-
- {/* */} -
-
-
-
- -
- -
- - Features - -

- Everything you need for perfect documentation -

-

- Create comprehensive documentation with our intuitive tools and intelligent - suggestions, making documentation a breeze. -

-
- - - {[ - { - icon: Layout, - title: "Smart Templates", - description: "Choose from various pre-built templates or customize your own structure." - }, - { - icon: Sparkles, - title: "AI-Powered Suggestions", - description: "Get intelligent recommendations based on your project type." - }, - { - icon: GitBranch, - title: "Version Control Integration", - description: "Seamlessly sync your documentation with your Git repository." - }, - { - icon: Flag, - title: "Custom Sections", - description: "Add, remove, or rearrange sections with our drag-and-drop interface." - } - ].map((feature, index) => ( - - ))} - -
-
- -
- -
- - How It Works - -

- Create documentation in minutes -

-

- Our simple three-step process makes documentation creation effortless -

-
- - - {[ - { - icon: BookOpen, - title: "1. Choose Template", - description: "Select from our collection of professional README templates" - }, - { - icon: FileEdit, - title: "2. Customize Content", - description: "Edit and customize sections with our intuitive editor" - }, - { - icon: Download, - title: "3. Export & Deploy", - description: "Export to markdown and deploy to your repository" - } - ].map((step, index) => ( - - - - -

{step.title}

-

{step.description}

-
- ))} -
-
-
- -
- - - Get Started - -

- Ready to create amazing documentation? -

-

- Join thousands of developers who are creating better documentation - with Dododocs README Maker. -

- -
-
-
-
- - ); -}; -export default ReadmeLanding; - - - - - diff --git a/src/components/organisms/ChattingContent/ForKko.jsx b/src/components/organisms/ChattingContent/ForKko.jsx deleted file mode 100644 index d4a2179..0000000 --- a/src/components/organisms/ChattingContent/ForKko.jsx +++ /dev/null @@ -1,187 +0,0 @@ -import { useState, useEffect } from 'react'; -import { Typo, Button } from "../../index.js" -import { Check } from "lucide-react" -import api from "../../../api/axios.js" -import { useParams, useNavigate } from 'react-router-dom'; -import Modal from 'react-modal'; -import { - HomeWrapper, MainSectionWrapper, HomeLayout, - TextSectionWrapper, TextContainer, MainText, SubText, - MainImageSectionWrapper, BgShape, MainImageFrame, - MainFeatureSectionWrapper, MainFeatureSection, FeatureContentText, FeatureContentImage -} from "../HomeContent/HomeContent.styles.js" -import { Link } from 'react-router-dom'; -import styled, { keyframes, css } from 'styled-components'; -import { RefreshCw, Loader2 } from 'lucide-react'; - -const ChatButton = styled.button` - padding: 8px 16px; - margin: 8px; - border: none; - border-radius: 4px; - background-color: ${({ theme }) => theme.colors.primary}; - color: white; - cursor: pointer; -`; - -const modalStyles = { - content: { - top: '50%', - left: '50%', - right: 'auto', - bottom: 'auto', - marginRight: '-50%', - transform: 'translate(-50%, -50%)', - backgroundColor: 'var(--bg-color)', // 테마 색상 사용 - border: '1px solid #fff ', - }, - overlay: { - backgroundColor: 'rgba(0, 0, 0, 0.5)' - } -}; - - -const rotate = keyframes` - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -`; - -// 애니메이션 관련 styled-components -const fade = keyframes` - 0%, 100% { opacity: 0; transform: translateY(10px); } - 20%, 80% { opacity: 1; transform: translateY(0); } -`; - -const LoadingWrapper = styled.div` - display: flex; - align-items: center; - justify-content: center; - gap: 0.75rem; - padding: 1rem; - background-color: rgba(139, 92, 246, 0.1); - border: 1px solid rgba(139, 92, 246, 0.2); - border-radius: 0.5rem; - margin-bottom: 1rem; - min-height: 3.5rem; // 높이 고정으로 텍스트 변경시 레이아웃 시프트 방지 -`; - -const LoadingText = styled.span` - color: #8b5cf6; - font-size: 0.875rem; - font-weight: 500; - min-width : 12rem; - animation: ${fade} 2s ease-in-out; -`; - -const RotatingLoader = styled(Loader2)` - ${css` - animation: ${rotate} 1s linear infinite; - `} - color: #8b5cf6; -`; - - -const DocsContent = () => { - - const [testText, setTestText] = useState([]) - - const apiHandler = async () => { - const response = await api.get('api/analyze/result', { - params: { - repositoryName: "Gatsby-Starter-Haon" - } - }); - return response.data; - } - - const handleTestClick = async () => { - console.log(process.env.REACT_APP_API_BASE_URL) - const data = await apiHandler(); - console.log("api/analyze/result testApi 결과 받기 : ", data) - } - - const navigate = useNavigate(); - const { chattingId } = useParams(); - const [isModalOpen, setIsModalOpen] = useState(!!chattingId); - - const openModal = (id) => { - navigate(`/chatting/${id}`); - }; - - const closeModal = () => { - navigate('/chatting'); - }; - - - const loadingMessages = [ - '레포지토리 연결 중...', - '코드를 분석하는 중입니다...', - 'AI가 코드를 이해하는 중입니다...' - ]; - - const [messageIndex, setMessageIndex] = useState(0); - - useEffect(() => { - const interval = setInterval(() => { - setMessageIndex((prev) => (prev + 1) % loadingMessages.length); - }, 2000); // 2초마다 메시지 변경 - - return () => clearInterval(interval); - }, []); - - - - - - return ( - <> - - - of the 꼬 - by the 꼬 - for the 꼬 - - - - - - {loadingMessages[messageIndex]} - - -
-

채팅 목록

-
- openModal('123')}> - 채팅방 123 열기 - - openModal('456')}> - 채팅방 456 열기 - -
- - - {chattingId && ( - <> -

채팅방 {chattingId}

-

채팅 내용이 여기에 표시됩니다.

- 닫기 - - )} -
-
- - ) -} - -export default DocsContent; diff --git a/src/components/organisms/ChattingContent/ReadMeLanding.styles.js b/src/components/organisms/ChattingContent/ReadMeLanding.styles.js deleted file mode 100644 index 7704164..0000000 --- a/src/components/organisms/ChattingContent/ReadMeLanding.styles.js +++ /dev/null @@ -1,206 +0,0 @@ -// ReadmeLanding.styles.js -import styled, { css, keyframes } from 'styled-components'; - -const fadeIn = keyframes` - from { - opacity: 0; - transform: translateY(10px); - } - to { - opacity: 1; - transform: translateY(0); - } -`; - -export const PageWrapper = styled.div` - width: 100%; - &::before { - content: ''; - position: absolute; - inset: 0; - background: linear-gradient( - to bottom, - rgba(147, 51, 234, 0.1), - transparent, - transparent - ); - } -`; - -export const PageContainer = styled.div` - min-height: 100vh; - /* margin-top: 11.5dvh; */ - background-color: #0f0a1f; - color: white; - - display: flex; - align-items: center; - flex-direction: column; - justify-content: center; - width: 100%; - padding: clamp(5vh, 6vh, 8vh) clamp(4vw, 6vw, 8vw); - animation: ${fadeIn} 0.5s ease-out; - - @media (max-width: 768px) { - padding: clamp(3vh, 4vh, 6vh) clamp(2vw, 3vw, 4vw); - } -`; - -export const HeroSection = styled.div` - position: relative; - overflow: hidden; -`; - -export const ContentWrapper = styled.div` - position: relative; - max-width: 80rem; - margin: 0 auto; - padding: 5rem 1rem; -`; - -export const HeroGrid = styled.div` - display: grid; - gap: 3rem; - align-items: center; - - @media (min-width: 1024px) { - grid-template-columns: repeat(2, 1fr); - } -`; - -export const GradientText = styled.span` - background: linear-gradient(to right, #a78bfa, #f472b6); - -webkit-background-clip: text; - color: transparent; -`; - -export const ButtonGroup = styled.div` - display: flex; - gap: 1rem; -`; - -export const Button = styled.button` - display: flex; - align-items: center; - gap: 0.5rem; - padding: 0.75rem 1.5rem; - border-radius: 0.5rem; - transition: all 0.2s; - - ${(props) => - props.primary && - ` - background: #9333ea; - color: white; - &:hover { - background: #7e22ce; - } - `} - - ${(props) => - props.secondary && - ` - border: 1px solid rgba(147, 51, 234, 0.2); - color: white; - &:hover { - background: rgba(147, 51, 234, 0.2); - } - `} -`; - -export const Section = styled.section` - padding: 5rem 1rem; - ${(props) => - props.darker && - ` - background: rgba(22, 17, 46, 0.5); - `} -`; - -export const FeatureWrapper = styled.div` - display: flex; - gap: 1rem; - padding: 1.5rem; - border-radius: 0.75rem; - background: #16112e; - border: 1px solid rgba(147, 51, 234, 0.2); -`; - -export const FeatureIcon = styled.div` - width: 3rem; - height: 3rem; - border-radius: 0.5rem; - background: rgba(147, 51, 234, 0.2); - display: flex; - align-items: center; - justify-content: center; - color: #e9d5ff; -`; - -export const FeatureContent = styled.div` - h3 { - font-size: 1.125rem; - font-weight: 600; - margin-bottom: 0.5rem; - color: white; - } - - p { - color: #e9d5ff; - } -`; - -export const FeatureGrid = styled.div` - display: grid; - gap: 1.5rem; - margin-top: 3rem; - - @media (min-width: 768px) { - grid-template-columns: repeat(2, 1fr); - } -`; - -export const StepsGrid = styled.div` - display: grid; - gap: 2rem; - margin-top: 3rem; - - @media (min-width: 768px) { - grid-template-columns: repeat(3, 1fr); - } -`; - -export const StepCard = styled.div` - text-align: center; - padding: 1.5rem; - background: #16112e; - border-radius: 0.75rem; - border: 1px solid rgba(147, 51, 234, 0.2); -`; - -export const StepIcon = styled.div` - width: 4rem; - height: 4rem; - border-radius: 50%; - background: rgba(147, 51, 234, 0.2); - display: flex; - align-items: center; - justify-content: center; - margin: 0 auto 1.5rem; - color: #e9d5ff; -`; - -export const CTASection = styled.div` - max-width: 64rem; - margin: 0 auto; - text-align: center; -`; - -export const Badge = styled.span` - margin-left: auto; - font-size: 0.75rem; - background: rgba(147, 51, 234, 0.2); - color: #9333ea; - padding: 0.125rem 0.5rem; - border-radius: 9999px; -`; diff --git a/src/components/organisms/DocsContent/DocsContent.jsx b/src/components/organisms/DocsContent/DocsContent.jsx deleted file mode 100644 index a99e79f..0000000 --- a/src/components/organisms/DocsContent/DocsContent.jsx +++ /dev/null @@ -1,148 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { Typo, Button } from "../../index.js" -import FlexSplitter from './FlexSplitter.jsx'; -import MarkdownFile from "./test.md" -import { - ContentStyle, - LeftPanelContent, RightPanelContent, - Item, - Title, - Content, - RightTitle, RightContent, RightContentWrapper -} from "./DocsContent.styles.js" - -import ReactMarkdown from 'react-markdown' - - -const markdownText = ` -## 2. Strategy Pattern Implementation - -### Strategy Pattern Overview - -The strategy pattern is implemented through the use of interfaces for OAuth clients and URI providers, allowing for different implementations based on the OAuth provider. - -### Strategy Interface and Concrete Classes - -- **OAuthClient**: Interface for OAuth clients that defines methods for retrieving OAuth member information. -- **OAuthMember**: Interface representing an OAuth member with methods to get social login ID, email, and profile image URL. -- **OAuthProvider**: Interface that provides methods to get specific OAuth clients and URI providers based on the provider name. -- **OAuthUriProvider**: Interface for generating OAuth URIs. - -### Context Class - -- **AuthService**: Acts as the context that uses the strategy interfaces to perform authentication tasks. - -### Class Diagram - -\`\`\`mermaid -classDiagram - class OAuthClient { - +getOAuthMember(code: String): OAuthMember - +isSame(oAuthProviderName: String): boolean - } - - class OAuthMember { - +getSocialLoginId(): String - +getEmail(): String - +getProfileImageUrl(): String - } - - class OAuthProvider { - +getOauthClient(providerName: String): OAuthClient - +getOAuthUriProvider(providerName: String): OAuthUriProvider - +getSocialType(provider: String): SocialType - } - - class OAuthUriProvider { - +generateUri(): String - +isSame(provider: String): boolean - } - - class AuthService { - +generateTokenWithCode(code: String, providerName: String): MemberToken - +generateUri(providerName: String): String - } - - AuthService --> OAuthProvider - OAuthProvider --> OAuthClient - OAuthProvider --> OAuthUriProvider - OAuthClient --> OAuthMember -\`\`\` -`; - - - - -const DocsContent = () => { - - return ( - <> - -
- - - 왼쪽 패널 - - - {Array.from({ length: 25 }).map((_, i) => ( - - 항목 {i + 1} - - ))} - - - - - - 오른쪽 패널 - - - {/* {Array.from({ length: 25 }).map((_, i) => ( - - 콘텐츠 {i + 1} - - ))} */} - - {markdownText} - - - - - - - - {/* 오른쪽 splitter */} - - - 왼쪽 패널 - - - {Array.from({ length: 25 }).map((_, i) => ( - - 항목 {i + 1} - - ))} - - - - - - 오른쪽 패널 - - - {Array.from({ length: 25 }).map((_, i) => ( - - 콘텐츠 {i + 1} - - ))} - - - - -
-
- - ) -} - -export default DocsContent; diff --git a/src/components/organisms/DocsContent/DocsContent.styles.js b/src/components/organisms/DocsContent/DocsContent.styles.js deleted file mode 100644 index 3301595..0000000 --- a/src/components/organisms/DocsContent/DocsContent.styles.js +++ /dev/null @@ -1,93 +0,0 @@ -import styled from 'styled-components'; -import _ from '../../../config/index.js'; -export const ContentStyle = styled.div` - padding-top: ${_.HEADER.TOTAL_DVH}dvh; - width: 100dvw; - height: 100dvh; - display: flex; - justify-content: center; -`; - -export const PanelContent = styled.div` - max-height: 100dvh; - display: flex; - flex-direction: column; - height: 100%; -`; - -export const LeftPanelContent = styled(PanelContent)` - background-color: transparent; -`; - -export const RightPanelContent = styled(PanelContent)` - background-color: rgb(39 39 42); - border-radius: 0.75rem; -`; - -export const RightTitle = styled.div` - height: 4dvh; - display: flex; - padding: 1rem; - border-bottom: 1px solid #3f3f46; - gap: 0.5rem; - align-items: center; - justify-content: space-between; - font-size: 1.2rem; -`; - -export const RightContentWrapper = styled.div` - padding: 0 1rem; - overflow: hidden; - max-height: 100dvh; -`; - -export const RightContent = styled.div` - padding: 0.75rem 1rem; - width: 100%; - outline: none; - /* Firefox */ - scrollbar-width: thin; - scrollbar-color: rgba(255, 255, 255, 0.3) transparent; - position: relative; - overflow: hidden; - overflow-y: scroll; - height: 100%; - /* Chrome, Safari, Edge */ - &::-webkit-scrollbar { - width: 6px; - } - - &::-webkit-scrollbar-track { - background: transparent; - } - - &::-webkit-scrollbar-thumb { - background-color: rgba(255, 255, 255, 0.3); - border-radius: 3px; - - &:hover { - background-color: rgba(255, 255, 255, 0.5); - } - } -`; - -export const Title = styled.h2` - font-size: 1.125rem; - font-weight: 600; - margin-bottom: 1rem; -`; - -export const Item = styled.div` - padding: 0.75rem; - background-color: white; - border-radius: 0.375rem; - box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); - margin-bottom: 0.5rem; -`; - -export const Content = styled.div` - padding: 1rem; - background-color: #f9fafb38; - border-radius: 0.375rem; - margin-bottom: 1rem; -`; diff --git a/src/components/organisms/DocsContent/FlexSplitter.jsx b/src/components/organisms/DocsContent/FlexSplitter.jsx deleted file mode 100644 index 95563d4..0000000 --- a/src/components/organisms/DocsContent/FlexSplitter.jsx +++ /dev/null @@ -1,90 +0,0 @@ -import React, { useState, useEffect, useCallback } from 'react'; -import { - Container, LeftPanel, - Divider, DividerLine, - RightPanel, - NarrowPanel, WidePanel -} from './FlexSplitter.styles.js' - - -const FlexSplitter = ({ - splitterWidth = '100%', - initialLeftWidth = 250, - minWidth = 150, - maxWidth = 800, - position = 'left', - children -}) => { - const [isDragging, setIsDragging] = useState(false); - const [leftWidth, setLeftWidth] = useState(initialLeftWidth); - const [startX, setStartX] = useState(0); - const [startWidth, setStartWidth] = useState(0); - - const handleMouseDown = useCallback((e) => { - setIsDragging(true); - setStartX(e.clientX); - setStartWidth(leftWidth); - e.preventDefault(); - }, [leftWidth]); - - const handleMouseMove = useCallback((e) => { - if (!isDragging) return; - - const deltaX = e.clientX - startX; - // position에 따라 deltaX의 부호를 반대로 적용 - const adjustedDelta = position === 'left' ? deltaX : -deltaX; - const newWidth = Math.min(Math.max(startWidth + adjustedDelta, minWidth), maxWidth); - setLeftWidth(newWidth); - }, [isDragging, startX, startWidth, minWidth, maxWidth, position]); - - const handleMouseUp = useCallback(() => { - setIsDragging(false); - }, []); - - useEffect(() => { - if (isDragging) { - window.addEventListener('mousemove', handleMouseMove); - window.addEventListener('mouseup', handleMouseUp); - - return () => { - window.removeEventListener('mousemove', handleMouseMove); - window.removeEventListener('mouseup', handleMouseUp); - }; - } - }, [isDragging, handleMouseMove, handleMouseUp]); - - return ( - - { - position === 'left' ? - <> - - {children[0]} - - - - - - {children[1]} - - - : - <> - - {children[0]} - - - - - - {children[1]} - - - } - - - - ); -}; - -export default FlexSplitter; \ No newline at end of file diff --git a/src/components/organisms/DocsContent/FlexSplitter.styles.js b/src/components/organisms/DocsContent/FlexSplitter.styles.js deleted file mode 100644 index f701c4e..0000000 --- a/src/components/organisms/DocsContent/FlexSplitter.styles.js +++ /dev/null @@ -1,74 +0,0 @@ -import styled from 'styled-components'; -// Divider에서 Container 참조 - -export const Container = styled.div` - display: flex; - height: 100%; - width: ${(props) => props.splitterWidth}; - /* width: 50%; */ - background-color: rgb(39 39 42); - border-radius: 0.75rem; - transition: background-color 0.2s ease; -`; - -export const Panel = styled.div` - height: 100%; - overflow: hidden; - background-color: rgb(39 39 42); - border-radius: 0.75rem; -`; - -export const NarrowPanel = styled(Panel)` - flex: 0 0 ${(props) => props.width}px; - width: ${(props) => props.width}px; - transition: ${(props) => (props.isDragging ? 'none' : 'all 0.1s ease')}; -`; - -export const WidePanel = styled(Panel)` - flex: 1; -`; - -export const Divider = styled.div` - flex: 0 0 clamp(10px, auto, 1rem); - display: flex; - align-items: center; - justify-content: center; - cursor: col-resize; - user-select: none; - background-color: rgb(39 39 42); - transition: background-color 0.2s ease; - - border-left: 1px solid rgb(63 63 70 / 70%); - border-right: 1px solid rgb(63 63 70 / 70%); - - &:hover { - color: white; - background-color: #0e0c15; - } -`; - -export const DividerLine = styled.div` - /* width: 2px; - height: 32px; - background-color: #3f3f46; - border-radius: 9999px; */ - width: 10px; - height: 100%; - background: transparent; - display: flex; - align-items: center; - justify-content: center; - cursor: col-resize; - - &::after { - content: '︙'; - color: ${(props) => (props.isDragging ? '#fff' : '#adb5bd')}; - font-size: 12px; - font-weight: bold; - } - &:hover { - &::after { - color: #fff; - } - } -`; diff --git a/src/components/organisms/DocsContent/test.md b/src/components/organisms/DocsContent/test.md deleted file mode 100644 index ca4ed89..0000000 --- a/src/components/organisms/DocsContent/test.md +++ /dev/null @@ -1,252 +0,0 @@ -# Comprehensive Documentation for the Auth Service Code - -## 1. Overall Structure - -### High-Level Overview - -The `AuthService` codebase is part of an authentication module that integrates with various OAuth providers to manage user authentication and token generation. It interacts with member services to create or retrieve user accounts based on OAuth member information. - -### Purpose and Function - -The primary purpose of the `AuthService` is to handle authentication processes, including: - -- Generating tokens using OAuth codes. -- Creating or retrieving members based on OAuth member data. -- Generating OAuth URIs for login. -- Renewing access tokens and managing refresh tokens. - -### Interaction Between Components - -- **OAuthProvider**: Provides the necessary OAuth client and URI provider based on the provider name. -- **MemberService**: Manages member-related operations, such as saving new members and checking for existing members. -- **TokenManager**: Handles the creation and management of access and refresh tokens. - -### Mermaid Diagram - -```mermaid -classDiagram - class AuthService { - +generateTokenWithCode(code: String, providerName: String): MemberToken - +generateUri(providerName: String): String - +generateRenewalAccessToken(renewalAccessTokenRequest: RenewalAccessTokenRequest): RenewalAccessTokenResponse - +removeRefreshToken(logoutRequest: LogoutRequest): void - +extractMemberId(accessToken: String): Long - } - - class OAuthProvider { - +getOauthClient(providerName: String): OAuthClient - +getOAuthUriProvider(providerName: String): OAuthUriProvider - +getSocialType(provider: String): SocialType - } - - class MemberService { - +existsByEmail(email: String): boolean - +save(member: Member): void - +findByEmail(email: String): Member - } - - class TokenManager { - +createMemberToken(memberId: Long): MemberToken - +generateRenewalAccessToken(refreshToken: String): RenewalToken - +getMemberId(accessToken: String): Long - +removeRefreshToken(refreshToken: String): void - } - - AuthService --> OAuthProvider - AuthService --> MemberService - AuthService --> TokenManager -``` - -## 2. Strategy Pattern Implementation - -### Strategy Pattern Overview - -The strategy pattern is implemented through the use of interfaces for OAuth clients and URI providers, allowing for different implementations based on the OAuth provider. - -### Strategy Interface and Concrete Classes - -- **OAuthClient**: Interface for OAuth clients that defines methods for retrieving OAuth member information. -- **OAuthMember**: Interface representing an OAuth member with methods to get social login ID, email, and profile image URL. -- **OAuthProvider**: Interface that provides methods to get specific OAuth clients and URI providers based on the provider name. -- **OAuthUriProvider**: Interface for generating OAuth URIs. - -### Context Class - -- **AuthService**: Acts as the context that uses the strategy interfaces to perform authentication tasks. - -### Class Diagram - -```mermaid -classDiagram - class OAuthClient { - +getOAuthMember(code: String): OAuthMember - +isSame(oAuthProviderName: String): boolean - } - - class OAuthMember { - +getSocialLoginId(): String - +getEmail(): String - +getProfileImageUrl(): String - } - - class OAuthProvider { - +getOauthClient(providerName: String): OAuthClient - +getOAuthUriProvider(providerName: String): OAuthUriProvider - +getSocialType(provider: String): SocialType - } - - class OAuthUriProvider { - +generateUri(): String - +isSame(provider: String): boolean - } - - class AuthService { - +generateTokenWithCode(code: String, providerName: String): MemberToken - +generateUri(providerName: String): String - } - - AuthService --> OAuthProvider - OAuthProvider --> OAuthClient - OAuthProvider --> OAuthUriProvider - OAuthClient --> OAuthMember -``` - -## 3. Detailed Component Documentation - -### a. Classes - -#### 1. `AuthService` - -- **Purpose**: Manages authentication processes using OAuth providers. -- **Attributes**: - - `OAuthProvider oAuthProvider`: Interface for obtaining OAuth clients and URIs. - - `MemberService memberService`: Service for managing member data. - - `TokenManager tokenManager`: Service for managing tokens. -- **Role**: Acts as the main service for authentication, coordinating between OAuth providers and member services. - -#### 2. `OAuthClient` - -- **Purpose**: Interface for OAuth clients. -- **Methods**: - - `OAuthMember getOAuthMember(final String code)`: Retrieves an OAuth member based on the provided code. - - `boolean isSame(final String oAuthProviderName)`: Checks if the client is the same as the given provider name. - -#### 3. `OAuthMember` - -- **Purpose**: Represents an OAuth member. -- **Methods**: - - `String getSocialLoginId()`: Returns the social login ID. - - `String getEmail()`: Returns the email of the member. - - `String getProfileImageUrl()`: Returns the profile image URL. - -#### 4. `OAuthProvider` - -- **Purpose**: Provides OAuth clients and URI providers. -- **Methods**: - - `OAuthClient getOauthClient(final String providerName)`: Returns the OAuth client for the specified provider. - - `OAuthUriProvider getOAuthUriProvider(final String providerName)`: Returns the URI provider for the specified provider. - - `SocialType getSocialType(final String provider)`: Returns the social type for the specified provider. - -#### 5. `OAuthUriProvider` - -- **Purpose**: Generates OAuth URIs. -- **Methods**: - - `String generateUri()`: Generates the OAuth URI. - - `boolean isSame(final String provider)`: Checks if the provider matches. - -### b. Methods and Functions - -#### 1. `generateTokenWithCode` - -- **Purpose**: Generates a member token using an OAuth code. -- **Parameters**: - - `String code`: The OAuth code received from the provider. - - `String providerName`: The name of the OAuth provider. -- **Return Value**: `MemberToken`: The generated member token. -- **Code Example**: - -```java -MemberToken token = authService.generateTokenWithCode("oauth_code", "google"); -``` - -#### 2. `generateUri` - -- **Purpose**: Generates an OAuth URI for login. -- **Parameters**: - - `String providerName`: The name of the OAuth provider. -- **Return Value**: `String`: The generated OAuth URI. -- **Code Example**: - -```java -String uri = authService.generateUri("google"); -``` - -#### 3. `generateRenewalAccessToken` - -- **Purpose**: Generates a new access token using a refresh token. -- **Parameters**: - - `RenewalAccessTokenRequest renewalAccessTokenRequest`: The request containing the refresh token. -- **Return Value**: `RenewalAccessTokenResponse`: The response containing the new access token. -- **Code Example**: - -```java -RenewalAccessTokenResponse response = authService.generateRenewalAccessToken(new RenewalAccessTokenRequest("refresh_token")); -``` - -#### 4. `removeRefreshToken` - -- **Purpose**: Removes a refresh token. -- **Parameters**: - - `LogoutRequest logoutRequest`: The request containing the refresh token to be removed. -- **Return Value**: `void` -- **Code Example**: - -```java -authService.removeRefreshToken(new LogoutRequest("refresh_token")); -``` - -#### 5. `extractMemberId` - -- **Purpose**: Extracts the member ID from an access token. -- **Parameters**: - - `String accessToken`: The access token from which to extract the member ID. -- **Return Value**: `Long`: The extracted member ID. -- **Code Example**: - -```java -Long memberId = authService.extractMemberId("access_token"); -``` - -## 4. Implementation Flow - -### Sequence Diagram - -```mermaid -sequenceDiagram - participant Client - participant AuthService - participant OAuthProvider - participant MemberService - participant TokenManager - - Client->>AuthService: generateTokenWithCode(code, providerName) - AuthService->>OAuthProvider: getOauthClient(providerName) - OAuthProvider->>AuthService: OAuthClient - AuthService->>OAuthClient: getOAuthMember(code) - OAuthClient->>AuthService: OAuthMember - AuthService->>MemberService: findOrCreateMember(oAuthMember, providerName) - MemberService->>AuthService: Member - AuthService->>TokenManager: createMemberToken(memberId) - TokenManager->>AuthService: MemberToken - AuthService->>Client: MemberToken -``` - -### Explanation of Flow - -1. The client requests a token by calling `generateTokenWithCode` on the `AuthService`. -2. The `AuthService` retrieves the appropriate `OAuthClient` from the `OAuthProvider`. -3. The `AuthService` uses the `OAuthClient` to get the `OAuthMember` using the provided code. -4. The `AuthService` checks if the member exists or creates a new member using the `MemberService`. -5. Finally, the `AuthService` generates a member token using the `TokenManager` and returns it to the client. - -This documentation provides a comprehensive overview of the `AuthService` codebase, detailing its structure, strategy pattern implementation, component documentation, and implementation flow. It serves as a guide for both new and experienced developers to understand and work with the code effectively. diff --git a/src/components/organisms/Landing/ChattingLanding/ChattingLanding.jsx b/src/components/organisms/Landing/ChattingLanding/ChattingLanding.jsx index ecd91d5..163ad16 100644 --- a/src/components/organisms/Landing/ChattingLanding/ChattingLanding.jsx +++ b/src/components/organisms/Landing/ChattingLanding/ChattingLanding.jsx @@ -194,7 +194,6 @@ display: inline-block; color: #c084fc; font-size: 0.875rem; `; - const TimelineContainer = styled.div` position: relative; `; @@ -204,7 +203,7 @@ const ConnectionLine = styled.div` left: 3rem; top: -1rem; bottom: -0.5rem; - width: 3px; + width: 2csmopx; background: linear-gradient( to bottom, rgba(147, 51, 234, 0.5), diff --git a/src/components/organisms/Landing/LandingContent.jsx b/src/components/organisms/Landing/LandingContent.jsx index 7b6b1ed..1c46d59 100644 --- a/src/components/organisms/Landing/LandingContent.jsx +++ b/src/components/organisms/Landing/LandingContent.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import ChattingLanding from './ChattingLanding/ChattingLanding.jsx'; import DocsLanding from './DocsLanding/DocsLanding.jsx'; -import ReadmeLanding from './ReadmeLanding/ReadmeLanding.jsx'; +import ReadmeLanding from './LandingReadme/LandingReadme.jsx'; const LandingContent = () => { const { serviceTitle } = useParams(); diff --git a/src/components/organisms/Landing/ReadmeLanding/ReadmeLanding.jsx b/src/components/organisms/Landing/LandingReadme/LandingReadme.jsx similarity index 99% rename from src/components/organisms/Landing/ReadmeLanding/ReadmeLanding.jsx rename to src/components/organisms/Landing/LandingReadme/LandingReadme.jsx index 71b7bef..e4fe692 100644 --- a/src/components/organisms/Landing/ReadmeLanding/ReadmeLanding.jsx +++ b/src/components/organisms/Landing/LandingReadme/LandingReadme.jsx @@ -32,7 +32,7 @@ import { FeatureCard, -} from './ReadMeLanding.styles' +} from './ReadMeLanding.styles.js' import { Image } from '../../../index.js'; import mainBannerImg from "../../../../assets/images/landing-hero-min.png" diff --git a/src/components/organisms/Landing/ReadmeLanding/ReadmeLanding.styles.js b/src/components/organisms/Landing/LandingReadme/ReadMeLanding.styles.js similarity index 100% rename from src/components/organisms/Landing/ReadmeLanding/ReadmeLanding.styles.js rename to src/components/organisms/Landing/LandingReadme/ReadMeLanding.styles.js diff --git a/src/components/organisms/RepoDetailContent/CattingDetailContent/Chatting.jsx b/src/components/organisms/RepoDetailContent/CattingDetailContent/Chatting.jsx index 0694d98..7ed5393 100644 --- a/src/components/organisms/RepoDetailContent/CattingDetailContent/Chatting.jsx +++ b/src/components/organisms/RepoDetailContent/CattingDetailContent/Chatting.jsx @@ -1,5 +1,4 @@ import React, { useState, useRef, useEffect } from 'react'; -import styled, { css, keyframes } from 'styled-components'; import { Send, User, Bot, Sparkles, RefreshCw, EllipsisVertical } from 'lucide-react'; import { Typo } from "../../../index.js"; import useClickAway from '../../../../hooks/useClickAway.js'; diff --git a/src/pages/ChattingPage/index.jsx b/src/pages/ChattingPage/index.jsx deleted file mode 100644 index 0120c84..0000000 --- a/src/pages/ChattingPage/index.jsx +++ /dev/null @@ -1,16 +0,0 @@ - -import React, { useState, useRef, useEffect } from "react"; -import { Header } from "../../layouts/index.js" -import { ChattingContent } from "../../components/index.js" -const Home = () => { - - return ( - <> -
- - - - ); -}; - -export default Home; diff --git a/src/pages/DocsPage/index.jsx b/src/pages/DocsPage/index.jsx deleted file mode 100644 index 29bb40b..0000000 --- a/src/pages/DocsPage/index.jsx +++ /dev/null @@ -1,17 +0,0 @@ - -import React, { useState, useRef, useEffect } from "react"; -import { Header } from "../../layouts/index.js" -import { DocsContent } from "../../components/index.js" -import _ from '../../config/index.js'; -const Home = () => { - - return ( - <> -
- - - - ); -}; - -export default Home; diff --git a/src/router/ChattingRouter.jsx b/src/router/ChattingRouter.jsx deleted file mode 100644 index 3f518a1..0000000 --- a/src/router/ChattingRouter.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; -import Chatting from "../pages/ChattingPage/index.jsx" -const ChattingRouter = () => { - return ( - - ); -}; - -export default ChattingRouter; diff --git a/src/router/DocsRouter.jsx b/src/router/DocsRouter.jsx deleted file mode 100644 index d0f60b2..0000000 --- a/src/router/DocsRouter.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; -import Docs from "../pages/DocsPage/index.jsx" -const DocsRouter = () => { - return ( - - ); -}; - -export default DocsRouter; diff --git a/src/router/index.jsx b/src/router/index.jsx index e89d53e..9c24885 100644 --- a/src/router/index.jsx +++ b/src/router/index.jsx @@ -4,9 +4,7 @@ import React, { Suspense, lazy } from 'react'; import { Navigate, BrowserRouter as Router, Routes, Route } from 'react-router-dom'; import Home from './HomeRouter.jsx'; -import Docs from './DocsRouter.jsx'; import Login from "./LoginRouter.jsx" -import Chatting from "./ChattingRouter.jsx"; import OAuthCallback from "./OAuthCallbackRouter.jsx" import Repo from "./RepoRouter.jsx" import Landing from "./LandingRouter.jsx" @@ -35,7 +33,6 @@ function App() { } /> } /> } /> - } /> } /> } /> } /> @@ -46,9 +43,6 @@ function App() { element={} /> */} - } /> - } /> - {/* diff --git a/yarn.lock b/yarn.lock index 1539668..07c1f9e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2509,13 +2509,6 @@ "@types/d3-transition" "*" "@types/d3-zoom" "*" -"@types/debug@^4.0.0": - version "4.1.12" - resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" - integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ== - dependencies: - "@types/ms" "*" - "@types/eslint@^7.29.0 || ^8.4.1": version "8.56.12" resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.12.tgz#1657c814ffeba4d2f84c0d4ba0f44ca7ea1ca53a" @@ -2524,14 +2517,7 @@ "@types/estree" "*" "@types/json-schema" "*" -"@types/estree-jsx@^1.0.0": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@types/estree-jsx/-/estree-jsx-1.0.5.tgz#858a88ea20f34fe65111f005a689fa1ebf70dc18" - integrity sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg== - dependencies: - "@types/estree" "*" - -"@types/estree@*", "@types/estree@^1.0.0", "@types/estree@^1.0.5": +"@types/estree@*", "@types/estree@^1.0.5": version "1.0.6" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== @@ -2593,13 +2579,6 @@ dependencies: "@types/node" "*" -"@types/hast@^3.0.0": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa" - integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== - dependencies: - "@types/unist" "*" - "@types/html-minifier-terser@^6.0.0": version "6.1.0" resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" @@ -2654,23 +2633,11 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/mdast@^4.0.0": - version "4.0.4" - resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-4.0.4.tgz#7ccf72edd2f1aa7dd3437e180c64373585804dd6" - integrity sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA== - dependencies: - "@types/unist" "*" - "@types/mime@^1": version "1.3.5" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== -"@types/ms@*": - version "0.7.34" - resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433" - integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== - "@types/node-forge@^1.3.0": version "1.3.11" resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.11.tgz#0972ea538ddb0f4d9c2fa0ec5db5724773a604da" @@ -2800,16 +2767,6 @@ resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== -"@types/unist@*", "@types/unist@^3.0.0": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c" - integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== - -"@types/unist@^2.0.0": - version "2.0.11" - resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.11.tgz#11af57b127e32487774841f7a4e54eab166d03c4" - integrity sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA== - "@types/use-sync-external-store@^0.0.3": version "0.0.3" resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43" @@ -2932,7 +2889,7 @@ "@typescript-eslint/types" "5.62.0" eslint-visitor-keys "^3.3.0" -"@ungap/structured-clone@^1.0.0", "@ungap/structured-clone@^1.2.0": +"@ungap/structured-clone@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== @@ -3585,11 +3542,6 @@ babel-preset-react-app@^10.0.1: babel-plugin-macros "^3.1.0" babel-plugin-transform-react-remove-prop-types "^0.4.24" -bail@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d" - integrity sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw== - balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -3809,11 +3761,6 @@ case-sensitive-paths-webpack-plugin@^2.4.0: resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4" integrity sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw== -ccount@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" - integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== - chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -3849,26 +3796,6 @@ char-regex@^2.0.0: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-2.0.1.tgz#6dafdb25f9d3349914079f010ba8d0e6ff9cd01e" integrity sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw== -character-entities-html4@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz#1f1adb940c971a4b22ba39ddca6b618dc6e56b2b" - integrity sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA== - -character-entities-legacy@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b" - integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== - -character-entities@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22" - integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ== - -character-reference-invalid@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9" - integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw== - check-types@^11.2.3: version "11.2.3" resolved "https://registry.yarnpkg.com/check-types/-/check-types-11.2.3.tgz#1ffdf68faae4e941fce252840b1787b8edc93b71" @@ -3999,11 +3926,6 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" -comma-separated-tokens@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" - integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== - commander@7, commander@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" @@ -4738,7 +4660,7 @@ debug@2.6.9, debug@^2.6.0: dependencies: ms "2.0.0" -debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.6: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.6: version "4.3.7" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== @@ -4757,13 +4679,6 @@ decimal.js@^10.2.1: resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== -decode-named-character-reference@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz#daabac9690874c394c81e4162a0304b35d824f0e" - integrity sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg== - dependencies: - character-entities "^2.0.0" - dedent@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" @@ -4855,11 +4770,6 @@ depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== -dequal@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" - integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== - destroy@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" @@ -4883,13 +4793,6 @@ detect-port-alt@^1.1.6: address "^1.0.1" debug "^2.6.0" -devlop@^1.0.0, devlop@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/devlop/-/devlop-1.1.0.tgz#4db7c2ca4dc6e0e834c30be70c94bbc976dc7018" - integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA== - dependencies: - dequal "^2.0.0" - didyoumean@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" @@ -5574,11 +5477,6 @@ estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== -estree-util-is-identifier-name@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz#0b5ef4c4ff13508b34dcd01ecfa945f61fce5dbd" - integrity sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg== - estree-walker@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" @@ -5687,11 +5585,6 @@ express@^4.17.3: utils-merge "1.0.1" vary "~1.1.2" -extend@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -6189,60 +6082,6 @@ hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: dependencies: function-bind "^1.1.2" -hast-util-sanitize@^5.0.0: - version "5.0.2" - resolved "https://registry.yarnpkg.com/hast-util-sanitize/-/hast-util-sanitize-5.0.2.tgz#edb260d94e5bba2030eb9375790a8753e5bf391f" - integrity sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg== - dependencies: - "@types/hast" "^3.0.0" - "@ungap/structured-clone" "^1.0.0" - unist-util-position "^5.0.0" - -hast-util-to-html@^9.0.0: - version "9.0.3" - resolved "https://registry.yarnpkg.com/hast-util-to-html/-/hast-util-to-html-9.0.3.tgz#a9999a0ba6b4919576a9105129fead85d37f302b" - integrity sha512-M17uBDzMJ9RPCqLMO92gNNUDuBSq10a25SDBI08iCCxmorf4Yy6sYHK57n9WAbRAAaU+DuR4W6GN9K4DFZesYg== - dependencies: - "@types/hast" "^3.0.0" - "@types/unist" "^3.0.0" - ccount "^2.0.0" - comma-separated-tokens "^2.0.0" - hast-util-whitespace "^3.0.0" - html-void-elements "^3.0.0" - mdast-util-to-hast "^13.0.0" - property-information "^6.0.0" - space-separated-tokens "^2.0.0" - stringify-entities "^4.0.0" - zwitch "^2.0.4" - -hast-util-to-jsx-runtime@^2.0.0: - version "2.3.2" - resolved "https://registry.yarnpkg.com/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.2.tgz#6d11b027473e69adeaa00ca4cfb5bb68e3d282fa" - integrity sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg== - dependencies: - "@types/estree" "^1.0.0" - "@types/hast" "^3.0.0" - "@types/unist" "^3.0.0" - comma-separated-tokens "^2.0.0" - devlop "^1.0.0" - estree-util-is-identifier-name "^3.0.0" - hast-util-whitespace "^3.0.0" - mdast-util-mdx-expression "^2.0.0" - mdast-util-mdx-jsx "^3.0.0" - mdast-util-mdxjs-esm "^2.0.0" - property-information "^6.0.0" - space-separated-tokens "^2.0.0" - style-to-object "^1.0.0" - unist-util-position "^5.0.0" - vfile-message "^4.0.0" - -hast-util-whitespace@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz#7778ed9d3c92dd9e8c5c8f648a49c21fc51cb621" - integrity sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw== - dependencies: - "@types/hast" "^3.0.0" - he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -6305,16 +6144,6 @@ html-minifier-terser@^6.0.2: relateurl "^0.2.7" terser "^5.10.0" -html-url-attributes@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/html-url-attributes/-/html-url-attributes-3.0.1.tgz#83b052cd5e437071b756cd74ae70f708870c2d87" - integrity sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ== - -html-void-elements@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-3.0.0.tgz#fc9dbd84af9e747249034d4d62602def6517f1d7" - integrity sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg== - html-webpack-plugin@^5.5.0: version "5.6.2" resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.6.2.tgz#174a67c8e55aa3fa2ba94c8e8e42894bfe4978ea" @@ -6504,11 +6333,6 @@ ini@^1.3.5: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== -inline-style-parser@0.2.4: - version "0.2.4" - resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.2.4.tgz#f4af5fe72e612839fcd453d989a586566d695f22" - integrity sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q== - internal-slot@^1.0.4, internal-slot@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" @@ -6538,19 +6362,6 @@ ipaddr.js@^2.0.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== -is-alphabetical@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz#01072053ea7c1036df3c7d19a6daaec7f19e789b" - integrity sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ== - -is-alphanumerical@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz#7c03fbe96e3e931113e57f964b0a368cc2dfd875" - integrity sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw== - dependencies: - is-alphabetical "^2.0.0" - is-decimal "^2.0.0" - is-arguments@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" @@ -6627,11 +6438,6 @@ is-date-object@^1.0.1, is-date-object@^1.0.5: dependencies: has-tostringtag "^1.0.0" -is-decimal@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-2.0.1.tgz#9469d2dc190d0214fd87d78b78caecc0cc14eef7" - integrity sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A== - is-docker@^2.0.0, is-docker@^2.1.1: version "2.2.1" resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" @@ -6673,11 +6479,6 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" -is-hexadecimal@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz#86b5bf668fca307498d319dfc03289d781a90027" - integrity sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg== - is-map@^2.0.2, is-map@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" @@ -6720,11 +6521,6 @@ is-plain-obj@^3.0.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== -is-plain-obj@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" - integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== - is-potential-custom-element-name@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" @@ -7794,11 +7590,6 @@ lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -longest-streak@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4" - integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g== - loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -7893,111 +7684,6 @@ match-sorter@^6.0.2: "@babel/runtime" "^7.23.8" remove-accents "0.5.0" -mdast-util-from-markdown@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz#4850390ca7cf17413a9b9a0fbefcd1bc0eb4160a" - integrity sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA== - dependencies: - "@types/mdast" "^4.0.0" - "@types/unist" "^3.0.0" - decode-named-character-reference "^1.0.0" - devlop "^1.0.0" - mdast-util-to-string "^4.0.0" - micromark "^4.0.0" - micromark-util-decode-numeric-character-reference "^2.0.0" - micromark-util-decode-string "^2.0.0" - micromark-util-normalize-identifier "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - unist-util-stringify-position "^4.0.0" - -mdast-util-mdx-expression@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz#43f0abac9adc756e2086f63822a38c8d3c3a5096" - integrity sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ== - dependencies: - "@types/estree-jsx" "^1.0.0" - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - devlop "^1.0.0" - mdast-util-from-markdown "^2.0.0" - mdast-util-to-markdown "^2.0.0" - -mdast-util-mdx-jsx@^3.0.0: - version "3.1.3" - resolved "https://registry.yarnpkg.com/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.3.tgz#76b957b3da18ebcfd0de3a9b4451dcd6fdec2320" - integrity sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ== - dependencies: - "@types/estree-jsx" "^1.0.0" - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - "@types/unist" "^3.0.0" - ccount "^2.0.0" - devlop "^1.1.0" - mdast-util-from-markdown "^2.0.0" - mdast-util-to-markdown "^2.0.0" - parse-entities "^4.0.0" - stringify-entities "^4.0.0" - unist-util-stringify-position "^4.0.0" - vfile-message "^4.0.0" - -mdast-util-mdxjs-esm@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz#019cfbe757ad62dd557db35a695e7314bcc9fa97" - integrity sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg== - dependencies: - "@types/estree-jsx" "^1.0.0" - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - devlop "^1.0.0" - mdast-util-from-markdown "^2.0.0" - mdast-util-to-markdown "^2.0.0" - -mdast-util-phrasing@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz#7cc0a8dec30eaf04b7b1a9661a92adb3382aa6e3" - integrity sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w== - dependencies: - "@types/mdast" "^4.0.0" - unist-util-is "^6.0.0" - -mdast-util-to-hast@^13.0.0: - version "13.2.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz#5ca58e5b921cc0a3ded1bc02eed79a4fe4fe41f4" - integrity sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA== - dependencies: - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - "@ungap/structured-clone" "^1.0.0" - devlop "^1.0.0" - micromark-util-sanitize-uri "^2.0.0" - trim-lines "^3.0.0" - unist-util-position "^5.0.0" - unist-util-visit "^5.0.0" - vfile "^6.0.0" - -mdast-util-to-markdown@^2.0.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz#f910ffe60897f04bb4b7e7ee434486f76288361b" - integrity sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA== - dependencies: - "@types/mdast" "^4.0.0" - "@types/unist" "^3.0.0" - longest-streak "^3.0.0" - mdast-util-phrasing "^4.0.0" - mdast-util-to-string "^4.0.0" - micromark-util-classify-character "^2.0.0" - micromark-util-decode-string "^2.0.0" - unist-util-visit "^5.0.0" - zwitch "^2.0.0" - -mdast-util-to-string@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz#7a5121475556a04e7eddeb67b264aae79d312814" - integrity sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg== - dependencies: - "@types/mdast" "^4.0.0" - mdn-data@2.0.14: version "2.0.14" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" @@ -8071,200 +7757,6 @@ methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== -micromark-core-commonmark@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.1.tgz#9a45510557d068605c6e9a80f282b2bb8581e43d" - integrity sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA== - dependencies: - decode-named-character-reference "^1.0.0" - devlop "^1.0.0" - micromark-factory-destination "^2.0.0" - micromark-factory-label "^2.0.0" - micromark-factory-space "^2.0.0" - micromark-factory-title "^2.0.0" - micromark-factory-whitespace "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-chunked "^2.0.0" - micromark-util-classify-character "^2.0.0" - micromark-util-html-tag-name "^2.0.0" - micromark-util-normalize-identifier "^2.0.0" - micromark-util-resolve-all "^2.0.0" - micromark-util-subtokenize "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-destination@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz#857c94debd2c873cba34e0445ab26b74f6a6ec07" - integrity sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-label@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz#17c5c2e66ce39ad6f4fc4cbf40d972f9096f726a" - integrity sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw== - dependencies: - devlop "^1.0.0" - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-space@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz#5e7afd5929c23b96566d0e1ae018ae4fcf81d030" - integrity sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-title@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz#726140fc77892af524705d689e1cf06c8a83ea95" - integrity sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A== - dependencies: - micromark-factory-space "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-whitespace@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz#9e92eb0f5468083381f923d9653632b3cfb5f763" - integrity sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA== - dependencies: - micromark-factory-space "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-character@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.1.0.tgz#31320ace16b4644316f6bf057531689c71e2aee1" - integrity sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ== - dependencies: - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-chunked@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz#e51f4db85fb203a79dbfef23fd41b2f03dc2ef89" - integrity sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg== - dependencies: - micromark-util-symbol "^2.0.0" - -micromark-util-classify-character@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz#8c7537c20d0750b12df31f86e976d1d951165f34" - integrity sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-combine-extensions@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz#75d6ab65c58b7403616db8d6b31315013bfb7ee5" - integrity sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ== - dependencies: - micromark-util-chunked "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-decode-numeric-character-reference@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz#2698bbb38f2a9ba6310e359f99fcb2b35a0d2bd5" - integrity sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ== - dependencies: - micromark-util-symbol "^2.0.0" - -micromark-util-decode-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz#7dfa3a63c45aecaa17824e656bcdb01f9737154a" - integrity sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA== - dependencies: - decode-named-character-reference "^1.0.0" - micromark-util-character "^2.0.0" - micromark-util-decode-numeric-character-reference "^2.0.0" - micromark-util-symbol "^2.0.0" - -micromark-util-encode@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz#0921ac7953dc3f1fd281e3d1932decfdb9382ab1" - integrity sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA== - -micromark-util-html-tag-name@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz#ae34b01cbe063363847670284c6255bb12138ec4" - integrity sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw== - -micromark-util-normalize-identifier@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz#91f9a4e65fe66cc80c53b35b0254ad67aa431d8b" - integrity sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w== - dependencies: - micromark-util-symbol "^2.0.0" - -micromark-util-resolve-all@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz#189656e7e1a53d0c86a38a652b284a252389f364" - integrity sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA== - dependencies: - micromark-util-types "^2.0.0" - -micromark-util-sanitize-uri@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz#ec8fbf0258e9e6d8f13d9e4770f9be64342673de" - integrity sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-encode "^2.0.0" - micromark-util-symbol "^2.0.0" - -micromark-util-subtokenize@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.1.tgz#76129c49ac65da6e479c09d0ec4b5f29ec6eace5" - integrity sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q== - dependencies: - devlop "^1.0.0" - micromark-util-chunked "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-symbol@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz#12225c8f95edf8b17254e47080ce0862d5db8044" - integrity sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw== - -micromark-util-types@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.0.tgz#63b4b7ffeb35d3ecf50d1ca20e68fc7caa36d95e" - integrity sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w== - -micromark@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/micromark/-/micromark-4.0.0.tgz#84746a249ebd904d9658cfabc1e8e5f32cbc6249" - integrity sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ== - dependencies: - "@types/debug" "^4.0.0" - debug "^4.0.0" - decode-named-character-reference "^1.0.0" - devlop "^1.0.0" - micromark-core-commonmark "^2.0.0" - micromark-factory-space "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-chunked "^2.0.0" - micromark-util-combine-extensions "^2.0.0" - micromark-util-decode-numeric-character-reference "^2.0.0" - micromark-util-encode "^2.0.0" - micromark-util-normalize-identifier "^2.0.0" - micromark-util-resolve-all "^2.0.0" - micromark-util-sanitize-uri "^2.0.0" - micromark-util-subtokenize "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: version "4.0.8" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" @@ -8737,20 +8229,6 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse-entities@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-4.0.1.tgz#4e2a01111fb1c986549b944af39eeda258fc9e4e" - integrity sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w== - dependencies: - "@types/unist" "^2.0.0" - character-entities "^2.0.0" - character-entities-legacy "^3.0.0" - character-reference-invalid "^2.0.0" - decode-named-character-reference "^1.0.0" - is-alphanumerical "^2.0.0" - is-decimal "^2.0.0" - is-hexadecimal "^2.0.0" - parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" @@ -9515,6 +8993,11 @@ pretty-format@^29.0.0, pretty-format@^29.7.0: ansi-styles "^5.0.0" react-is "^18.0.0" +prismjs@^1.29.0: + version "1.29.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12" + integrity sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q== + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -9544,11 +9027,6 @@ prop-types@^15.7.2, prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" -property-information@^6.0.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.5.0.tgz#6212fbb52ba757e92ef4fb9d657563b933b7ffec" - integrity sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig== - proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" @@ -9703,22 +9181,6 @@ react-lifecycles-compat@^3.0.0: resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== -react-markdown@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-9.0.1.tgz#c05ddbff67fd3b3f839f8c648e6fb35d022397d1" - integrity sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg== - dependencies: - "@types/hast" "^3.0.0" - devlop "^1.0.0" - hast-util-to-jsx-runtime "^2.0.0" - html-url-attributes "^3.0.0" - mdast-util-to-hast "^13.0.0" - remark-parse "^11.0.0" - remark-rehype "^11.0.0" - unified "^11.0.0" - unist-util-visit "^5.0.0" - vfile "^6.0.0" - react-modal@^3.16.1: version "3.16.1" resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.16.1.tgz#34018528fc206561b1a5467fc3beeaddafb39b2b" @@ -9975,38 +9437,6 @@ relateurl@^0.2.7: resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== -remark-html@^16.0.1: - version "16.0.1" - resolved "https://registry.yarnpkg.com/remark-html/-/remark-html-16.0.1.tgz#9246d0cf22254c208a86531cbeb26203ae2dd34c" - integrity sha512-B9JqA5i0qZe0Nsf49q3OXyGvyXuZFDzAP2iOFLEumymuYJITVpiH1IgsTEwTpdptDmZlMDMWeDmSawdaJIGCXQ== - dependencies: - "@types/mdast" "^4.0.0" - hast-util-sanitize "^5.0.0" - hast-util-to-html "^9.0.0" - mdast-util-to-hast "^13.0.0" - unified "^11.0.0" - -remark-parse@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-11.0.0.tgz#aa60743fcb37ebf6b069204eb4da304e40db45a1" - integrity sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA== - dependencies: - "@types/mdast" "^4.0.0" - mdast-util-from-markdown "^2.0.0" - micromark-util-types "^2.0.0" - unified "^11.0.0" - -remark-rehype@^11.0.0: - version "11.1.1" - resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-11.1.1.tgz#f864dd2947889a11997c0a2667cd6b38f685bca7" - integrity sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ== - dependencies: - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - mdast-util-to-hast "^13.0.0" - unified "^11.0.0" - vfile "^6.0.0" - remove-accents@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.5.0.tgz#77991f37ba212afba162e375b627631315bed687" @@ -10514,11 +9944,6 @@ sourcemap-codec@^1.4.8: resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== -space-separated-tokens@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f" - integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== - spdy-transport@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" @@ -10704,14 +10129,6 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -stringify-entities@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-4.0.4.tgz#b3b79ef5f277cc4ac73caeb0236c5ba939b3a4f3" - integrity sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg== - dependencies: - character-entities-html4 "^2.0.0" - character-entities-legacy "^3.0.0" - stringify-object@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" @@ -10772,13 +10189,6 @@ style-loader@^3.3.1: resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.4.tgz#f30f786c36db03a45cbd55b6a70d930c479090e7" integrity sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w== -style-to-object@^1.0.0: - version "1.0.8" - resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-1.0.8.tgz#67a29bca47eaa587db18118d68f9d95955e81292" - integrity sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g== - dependencies: - inline-style-parser "0.2.4" - styled-components@^6.1.13: version "6.1.13" resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-6.1.13.tgz#2d777750b773b31469bd79df754a32479e9f475e" @@ -11090,16 +10500,6 @@ tr46@^2.1.0: dependencies: punycode "^2.1.1" -trim-lines@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338" - integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== - -trough@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/trough/-/trough-2.2.0.tgz#94a60bd6bd375c152c1df911a4b11d5b0256f50f" - integrity sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw== - tryer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" @@ -11293,19 +10693,6 @@ unicode-property-aliases-ecmascript@^2.0.0: resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== -unified@^11.0.0: - version "11.0.5" - resolved "https://registry.yarnpkg.com/unified/-/unified-11.0.5.tgz#f66677610a5c0a9ee90cab2b8d4d66037026d9e1" - integrity sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA== - dependencies: - "@types/unist" "^3.0.0" - bail "^2.0.0" - devlop "^1.0.0" - extend "^3.0.0" - is-plain-obj "^4.0.0" - trough "^2.0.0" - vfile "^6.0.0" - unique-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" @@ -11313,44 +10700,6 @@ unique-string@^2.0.0: dependencies: crypto-random-string "^2.0.0" -unist-util-is@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-6.0.0.tgz#b775956486aff107a9ded971d996c173374be424" - integrity sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw== - dependencies: - "@types/unist" "^3.0.0" - -unist-util-position@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-5.0.0.tgz#678f20ab5ca1207a97d7ea8a388373c9cf896be4" - integrity sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA== - dependencies: - "@types/unist" "^3.0.0" - -unist-util-stringify-position@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz#449c6e21a880e0855bf5aabadeb3a740314abac2" - integrity sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ== - dependencies: - "@types/unist" "^3.0.0" - -unist-util-visit-parents@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz#4d5f85755c3b8f0dc69e21eca5d6d82d22162815" - integrity sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw== - dependencies: - "@types/unist" "^3.0.0" - unist-util-is "^6.0.0" - -unist-util-visit@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-5.0.0.tgz#a7de1f31f72ffd3519ea71814cccf5fd6a9217d6" - integrity sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg== - dependencies: - "@types/unist" "^3.0.0" - unist-util-is "^6.0.0" - unist-util-visit-parents "^6.0.0" - universalify@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" @@ -11461,22 +10810,6 @@ vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== -vfile-message@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-4.0.2.tgz#c883c9f677c72c166362fd635f21fc165a7d1181" - integrity sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw== - dependencies: - "@types/unist" "^3.0.0" - unist-util-stringify-position "^4.0.0" - -vfile@^6.0.0: - version "6.0.3" - resolved "https://registry.yarnpkg.com/vfile/-/vfile-6.0.3.tgz#3652ab1c496531852bf55a6bac57af981ebc38ab" - integrity sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q== - dependencies: - "@types/unist" "^3.0.0" - vfile-message "^4.0.0" - vscode-jsonrpc@8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz#f43dfa35fb51e763d17cd94dcca0c9458f35abf9" @@ -12067,8 +11400,3 @@ zustand@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/zustand/-/zustand-5.0.1.tgz#2bdca5e4be172539558ce3974fe783174a48fdcf" integrity sha512-pRET7Lao2z+n5R/HduXMio35TncTlSW68WsYBq2Lg1ASspsNGjpwLAsij3RpouyV6+kHMwwwzP0bZPD70/Jx/w== - -zwitch@^2.0.0, zwitch@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7" - integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A== From f63231220ac15feacfc333230b89a50a692abe86 Mon Sep 17 00:00:00 2001 From: euncherry Date: Mon, 23 Dec 2024 06:44:59 +0900 Subject: [PATCH 07/27] =?UTF-8?q?=EB=A7=88=ED=81=AC=EB=8B=A4=EC=9A=B4=20?= =?UTF-8?q?=EB=9E=9C=EB=8D=94=EB=A7=81=20=EC=96=B8=EC=96=B4=20=EA=B0=95?= =?UTF-8?q?=EC=A1=B0=20=EB=9D=BC=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../atoms/MarkdownRenderer/index.jsx | 36 ++++++++----------- .../organisms/RepoDetailContent/ReadMe.jsx | 8 +---- 2 files changed, 15 insertions(+), 29 deletions(-) diff --git a/src/components/atoms/MarkdownRenderer/index.jsx b/src/components/atoms/MarkdownRenderer/index.jsx index 6e61d1e..47c5247 100644 --- a/src/components/atoms/MarkdownRenderer/index.jsx +++ b/src/components/atoms/MarkdownRenderer/index.jsx @@ -4,22 +4,14 @@ import { MarkdownContainer } from "./MarkdownRenderer.styles"; import MarkdownIt from "markdown-it"; import markdownItAnchor from 'markdown-it-anchor'; import mermaid from "mermaid"; -import hljs from 'highlight.js/lib/core'; +import Prism from 'prismjs'; -// highlight.js 필요한 언어만 등록 -import javascript from 'highlight.js/lib/languages/javascript'; -import python from 'highlight.js/lib/languages/python'; -import bash from 'highlight.js/lib/languages/bash'; -import java from 'highlight.js/lib/languages/java'; -import typescript from 'highlight.js/lib/languages/typescript'; - - -// highlight.js 필요한 언어만 등록 -hljs.registerLanguage('javascript', javascript); -hljs.registerLanguage('python', python); -hljs.registerLanguage('bash', bash); -hljs.registerLanguage('java', java); -hljs.registerLanguage('typescript', typescript); +// 필요한 언어 import +import 'prismjs/components/prism-javascript'; +import 'prismjs/components/prism-python'; +import 'prismjs/components/prism-bash'; +import 'prismjs/components/prism-java'; +import 'prismjs/components/prism-typescript'; // URL에 사용할 수 있는 형태로 문자열 변환 @@ -69,17 +61,15 @@ const markdownConfig = { breaks: true, indent: true, highlight: function (str, lang) { - if (lang && hljs.getLanguage(lang)) { + if (lang && Prism.languages[lang]) { try { - return hljs.highlight(str, { // highlight -> hljs.highlight - language: lang, - ignoreIllegals: true - }).value; + const html = Prism.highlight(str, Prism.languages[lang], lang); + return `
${html}
`; } catch (err) { - console.error('Highlight error:', err); + console.error('Prism highlight error:', err); } } - return '
' + md.utils.escapeHtml(str) + '
'; + return `
${md.utils.escapeHtml(str)}
`; } }; // markdown-it 인스턴스 생성 및 플러그인 설정 @@ -114,6 +104,8 @@ const MarkdownViewer = React.memo(({ content }) => { try { mermaid.initialize(mermaidConfig); mermaid.contentLoaded(); + // Prism 수동 하이라이팅 실행 + Prism.highlightAll(); } catch (error) { console.error('Mermaid initialization error:', error); } diff --git a/src/components/organisms/RepoDetailContent/ReadMe.jsx b/src/components/organisms/RepoDetailContent/ReadMe.jsx index 75fe663..e811d32 100644 --- a/src/components/organisms/RepoDetailContent/ReadMe.jsx +++ b/src/components/organisms/RepoDetailContent/ReadMe.jsx @@ -4,7 +4,7 @@ import styled from 'styled-components'; import { Camera, Pencil, Video, Palette, Layout, Box, MoreVertical, GripVertical, Check, X, Plus } from 'lucide-react'; import api from "../../../api/axios.js"; import { Splitter } from "../../index.js" -import { MarkdownRenderer, LoadingSpinner, MarkdownEditor } from '../../index.js'; +import { MarkdownRenderer, LoadingSpinner } from '../../index.js'; import { useLocation, useNavigate } from 'react-router-dom'; import { Button } from "../../index.js" import { markdownText } from './markdownText.jsx'; @@ -823,12 +823,6 @@ const ReadMe = () => { }) } -
- {/* */} -
From 5aa7f389350ff02d46389e0cd2d6f289c0505099 Mon Sep 17 00:00:00 2001 From: euncherry Date: Mon, 23 Dec 2024 11:30:19 +0900 Subject: [PATCH 08/27] =?UTF-8?q?=EB=9E=9C=EB=8D=94=EB=A7=81=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 - src/components/atoms/MarkdownEditor/index.jsx | 168 ------------------ src/components/index.js | 1 - .../ChattingLanding/ChattingLanding.jsx | 15 +- yarn.lock | 35 ---- 5 files changed, 8 insertions(+), 213 deletions(-) delete mode 100644 src/components/atoms/MarkdownEditor/index.jsx diff --git a/package.json b/package.json index 98e0f35..08c103a 100644 --- a/package.json +++ b/package.json @@ -5,13 +5,11 @@ "dependencies": { "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", - "@reduxjs/toolkit": "^2.3.0", "@tanstack/react-query": "^5.59.20", "@testing-library/jest-dom": "^5.14.1", "@testing-library/react": "^13.0.0", "@testing-library/user-event": "^13.2.1", "axios": "^1.7.7", - "highlight.js": "^11.10.0", "jwt-decode": "^4.0.0", "lucide-react": "^0.454.0", "markdown-it": "^14.1.0", diff --git a/src/components/atoms/MarkdownEditor/index.jsx b/src/components/atoms/MarkdownEditor/index.jsx deleted file mode 100644 index 9413202..0000000 --- a/src/components/atoms/MarkdownEditor/index.jsx +++ /dev/null @@ -1,168 +0,0 @@ -import React, { useState, useEffect } from 'react'; -// @ts-ignore -import MarkdownIt from 'markdown-it'; -import styled from 'styled-components'; -import mermaid from 'mermaid'; -import hljs from 'highlight.js'; -// 원하는 스타일 테마 import (dark 테마 예시) -import 'highlight.js/styles/atom-one-dark.css'; -// 에디터 컨테이너 스타일 -const EditorContainer = styled.div` - display: flex; - gap: 20px; - height: 100%; -`; - -const EditorSection = styled.div` - flex: 1; - display: flex; - flex-direction: column; -`; - -const TextArea = styled.textarea` - width: 100%; - height: 100%; - min-height: 500px; - padding: 16px; - background: rgba(63, 63, 70, 0.5); - border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: 4px; - color: #e4e4e7; - font-family: 'JetBrains Mono', monospace; - font-size: 14px; - line-height: 1.6; - resize: none; - - &:focus { - outline: none; - border-color: #d923ff; - } -`; - -// 프리뷰 스타일 (기존 MarkdownContainer와 동일) -const PreviewContainer = styled.div` - color: #e4e4e7; - font-size: 0.95rem; - line-height: 1.7; - padding: 1.5rem; - background: rgba(63, 63, 70, 0.5); - border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: 4px; - overflow-y: auto; - - - code { - font-family: "JetBrains Mono", monospace; - font-size: 0.9em; - } - - :not(pre) > code { - background: rgba(63, 63, 70, 0.5); - padding: 0.2rem 0.4rem; - border-radius: 0.25rem; - } - - pre { - margin: 1.5rem 0; - border-radius: 0.5rem; - overflow-x: auto; - border: 1px solid rgba(255, 255, 255, 0.1); - - code { - // highlight.js 스타일이 적용될 수 있도록 배경색 제거 - background: transparent; - padding: 1rem; - display: block; - } - } - - // highlight.js 테마 커스터마이징 - .hljs { - background: #282c34; - color: #abb2bf; - } -`; - -// 커스텀 Mermaid 플러그인 -const mermaidPlugin = (md) => { - const defaultFence = md.renderer.rules.fence.bind(md.renderer.rules); - - md.renderer.rules.fence = (tokens, idx, options, env, self) => { - const token = tokens[idx]; - if (token.info === 'mermaid') { - return `
${token.content}
`; - } - return defaultFence(tokens, idx, options, env, self); - }; -}; - -// Markdown-it 초기화 -const md = new MarkdownIt({ - html: true, - linkify: true, - typographer: true, - breaks: true, - highlight: function (str, lang) { - if (lang && hljs.getLanguage(lang)) { - try { - return hljs.highlight(str, { language: lang }).value; - } catch (__) { } - } - return ''; // 언어가 지정되지 않은 경우 기본 처리 - } -}).use(mermaidPlugin); - -// Mermaid 초기화 -mermaid.initialize({ - startOnLoad: true, - theme: 'dark', - themeVariables: { - primaryColor: '#d923ff', - primaryTextColor: '#e4e4e7', - primaryBorderColor: 'rgba(255, 255, 255, 0.1)', - lineColor: '#e4e4e7', - secondaryColor: 'rgba(63, 63, 70, 0.5)', - tertiaryColor: 'rgba(63, 63, 70, 0.3)', - }, - fontFamily: 'JetBrains Mono, monospace', - securityLevel: 'loose', -}); - -const MarkdownEditor = ({ initialValue = '', onChange }) => { - const [markdown, setMarkdown] = useState(initialValue); - - useEffect(() => { - mermaid.contentLoaded(); - }, [markdown]); - - const handleChange = (e) => { - const newValue = e.target.value; - setMarkdown(newValue); - if (onChange) { - onChange(newValue); - } - }; - - const processMarkdown = (content) => { - return md.render(content); - }; - - return ( - - -