장르 선택 로직 개선
parent
888ac4fa5e
commit
6c499ae061
|
|
@ -507,7 +507,6 @@ const App: React.FC = () => {
|
||||||
onAnalyze={handleStartAnalysis}
|
onAnalyze={handleStartAnalysis}
|
||||||
onAutocomplete={handleAutocomplete}
|
onAutocomplete={handleAutocomplete}
|
||||||
onManualInput={handleManualInput}
|
onManualInput={handleManualInput}
|
||||||
onTestData={handleTestData}
|
|
||||||
onNext={() => scrollToSection(1)}
|
onNext={() => scrollToSection(1)}
|
||||||
error={error}
|
error={error}
|
||||||
scrollProgress={scrollProgress}
|
scrollProgress={scrollProgress}
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ const VideoDetailContent: React.FC<VideoDetailContentProps> = ({ videoId, isModa
|
||||||
|
|
||||||
const formatDate = (dateString: string) => {
|
const formatDate = (dateString: string) => {
|
||||||
const date = new Date(dateString);
|
const date = new Date(dateString);
|
||||||
return `${date.getFullYear()}년 ${date.getMonth() + 1}월 ${date.getDate()}일`;
|
return t('videoDetail.dateFormat', { year: date.getFullYear(), month: date.getMonth() + 1, day: date.getDate() });
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatCommentDate = (dateString: string) => {
|
const formatCommentDate = (dateString: string) => {
|
||||||
|
|
@ -120,12 +120,12 @@ const VideoDetailContent: React.FC<VideoDetailContentProps> = ({ videoId, isModa
|
||||||
kakao.Share.sendDefault({
|
kakao.Share.sendDefault({
|
||||||
objectType: 'feed',
|
objectType: 'feed',
|
||||||
content: {
|
content: {
|
||||||
title: video?.store_name ?? 'ADO2 영상',
|
title: video?.store_name ?? t('videoDetail.kakaoDefaultTitle'),
|
||||||
description: `${video?.region ?? ''} · ADO2 AI 마케팅 영상`,
|
description: t('videoDetail.kakaoDescription', { region: video?.region ?? '' }),
|
||||||
imageUrl: 'https://demo.castad.net/favicon_48.svg',
|
imageUrl: 'https://demo.castad.net/favicon_48.svg',
|
||||||
link: { mobileWebUrl: shareUrl, webUrl: shareUrl },
|
link: { mobileWebUrl: shareUrl, webUrl: shareUrl },
|
||||||
},
|
},
|
||||||
buttons: [{ title: '영상 보기', link: { mobileWebUrl: shareUrl, webUrl: shareUrl } }],
|
buttons: [{ title: t('videoDetail.kakaoButtonTitle'), link: { mobileWebUrl: shareUrl, webUrl: shareUrl } }],
|
||||||
});
|
});
|
||||||
} else if (navigator.share) {
|
} else if (navigator.share) {
|
||||||
navigator.share({ url: shareUrl }).catch(() => {});
|
navigator.share({ url: shareUrl }).catch(() => {});
|
||||||
|
|
@ -222,7 +222,7 @@ const VideoDetailContent: React.FC<VideoDetailContentProps> = ({ videoId, isModa
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderCommentContent = (content: string | null, isDeleted: boolean) => {
|
const renderCommentContent = (content: string | null, isDeleted: boolean) => {
|
||||||
if (isDeleted) return <span style={{ color: '#6B9EA0', fontStyle: 'italic' }}>(삭제된 댓글입니다.)</span>;
|
if (isDeleted) return <span style={{ color: '#6B9EA0', fontStyle: 'italic' }}>{t('videoDetail.deletedComment')}</span>;
|
||||||
return content;
|
return content;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -231,7 +231,7 @@ const VideoDetailContent: React.FC<VideoDetailContentProps> = ({ videoId, isModa
|
||||||
{/* 헤더 */}
|
{/* 헤더 */}
|
||||||
<div className="video-detail-header">
|
<div className="video-detail-header">
|
||||||
{isModal ? (
|
{isModal ? (
|
||||||
<button className="video-detail-close-btn" onClick={handleHeaderAction} aria-label="닫기">
|
<button className="video-detail-close-btn" onClick={handleHeaderAction} aria-label={t('videoDetail.closeAriaLabel')}>
|
||||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
@ -293,7 +293,7 @@ const VideoDetailContent: React.FC<VideoDetailContentProps> = ({ videoId, isModa
|
||||||
<circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/>
|
<circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/>
|
||||||
<line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/>
|
<line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/>
|
||||||
</svg>
|
</svg>
|
||||||
{copied ? '복사됨!' : '공유하기'}
|
{copied ? t('videoDetail.copied') : t('videoDetail.share')}
|
||||||
</button>
|
</button>
|
||||||
{shareMenuOpen && (
|
{shareMenuOpen && (
|
||||||
<div className="video-detail-share-menu">
|
<div className="video-detail-share-menu">
|
||||||
|
|
@ -303,21 +303,21 @@ const VideoDetailContent: React.FC<VideoDetailContentProps> = ({ videoId, isModa
|
||||||
<rect width="20" height="20" rx="4" fill="#FEE500"/>
|
<rect width="20" height="20" rx="4" fill="#FEE500"/>
|
||||||
<path fillRule="evenodd" clipRule="evenodd" d="M10 3.5C6.134 3.5 3 6.01 3 9.1c0 1.98 1.2 3.72 3.01 4.76l-.74 2.75a.19.19 0 0 0 .28.21l3.37-2.23c.34.04.69.06 1.06.06 3.866 0 7-2.51 7-5.6S13.866 3.5 10 3.5z" fill="#3C1E1E"/>
|
<path fillRule="evenodd" clipRule="evenodd" d="M10 3.5C6.134 3.5 3 6.01 3 9.1c0 1.98 1.2 3.72 3.01 4.76l-.74 2.75a.19.19 0 0 0 .28.21l3.37-2.23c.34.04.69.06 1.06.06 3.866 0 7-2.51 7-5.6S13.866 3.5 10 3.5z" fill="#3C1E1E"/>
|
||||||
</svg>
|
</svg>
|
||||||
카카오톡
|
{t('videoDetail.shareKakao')}
|
||||||
</button>
|
</button>
|
||||||
{/* 페이스북 */}
|
{/* 페이스북 */}
|
||||||
<button className="video-detail-share-item" onClick={handleFacebookShare}>
|
<button className="video-detail-share-item" onClick={handleFacebookShare}>
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="#1877F2">
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="#1877F2">
|
||||||
<path d="M24 12.073C24 5.405 18.627 0 12 0S0 5.405 0 12.073C0 18.1 4.388 23.094 10.125 24v-8.437H7.078v-3.49h3.047V9.41c0-3.025 1.792-4.697 4.533-4.697 1.312 0 2.686.235 2.686.235v2.97h-1.513c-1.491 0-1.956.93-1.956 1.887v2.268h3.328l-.532 3.49h-2.796V24C19.612 23.094 24 18.1 24 12.073z"/>
|
<path d="M24 12.073C24 5.405 18.627 0 12 0S0 5.405 0 12.073C0 18.1 4.388 23.094 10.125 24v-8.437H7.078v-3.49h3.047V9.41c0-3.025 1.792-4.697 4.533-4.697 1.312 0 2.686.235 2.686.235v2.97h-1.513c-1.491 0-1.956.93-1.956 1.887v2.268h3.328l-.532 3.49h-2.796V24C19.612 23.094 24 18.1 24 12.073z"/>
|
||||||
</svg>
|
</svg>
|
||||||
페이스북
|
{t('videoDetail.shareFacebook')}
|
||||||
</button>
|
</button>
|
||||||
{/* X (트위터) */}
|
{/* X (트위터) */}
|
||||||
<button className="video-detail-share-item" onClick={handleTwitterShare}>
|
<button className="video-detail-share-item" onClick={handleTwitterShare}>
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
|
||||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
|
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
|
||||||
</svg>
|
</svg>
|
||||||
X (트위터)
|
{t('videoDetail.shareTwitter')}
|
||||||
</button>
|
</button>
|
||||||
{/* URL 복사 */}
|
{/* URL 복사 */}
|
||||||
<button className="video-detail-share-item" onClick={() => { handleCopyLink(); setShareMenuOpen(false); }}>
|
<button className="video-detail-share-item" onClick={() => { handleCopyLink(); setShareMenuOpen(false); }}>
|
||||||
|
|
@ -325,7 +325,7 @@ const VideoDetailContent: React.FC<VideoDetailContentProps> = ({ videoId, isModa
|
||||||
<rect x="9" y="9" width="13" height="13" rx="2"/>
|
<rect x="9" y="9" width="13" height="13" rx="2"/>
|
||||||
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
|
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
|
||||||
</svg>
|
</svg>
|
||||||
{copied ? '복사됨!' : 'URL 복사'}
|
{copied ? t('videoDetail.copied') : t('videoDetail.copyUrl')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -335,7 +335,7 @@ const VideoDetailContent: React.FC<VideoDetailContentProps> = ({ videoId, isModa
|
||||||
{/* 댓글 섹션 */}
|
{/* 댓글 섹션 */}
|
||||||
<div className="video-detail-comments">
|
<div className="video-detail-comments">
|
||||||
<div className="video-detail-comments-header">
|
<div className="video-detail-comments-header">
|
||||||
<h3 className="video-detail-comments-title">댓글</h3>
|
<h3 className="video-detail-comments-title">{t('videoDetail.commentsTitle')}</h3>
|
||||||
<span className="video-detail-comments-count">{commentsTotal}</span>
|
<span className="video-detail-comments-count">{commentsTotal}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -344,16 +344,16 @@ const VideoDetailContent: React.FC<VideoDetailContentProps> = ({ videoId, isModa
|
||||||
<div className="video-detail-comment-profile">
|
<div className="video-detail-comment-profile">
|
||||||
<img
|
<img
|
||||||
src={`https://api.dicebear.com/9.x/pixel-art/svg?seed=${commentAvatarSeed}`}
|
src={`https://api.dicebear.com/9.x/pixel-art/svg?seed=${commentAvatarSeed}`}
|
||||||
alt="아바타 변경"
|
alt={t('videoDetail.changeAvatarTitle')}
|
||||||
className="video-detail-comment-avatar"
|
className="video-detail-comment-avatar"
|
||||||
onClick={handleChangeAvatar}
|
onClick={handleChangeAvatar}
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
title="클릭하여 아바타 변경"
|
title={t('videoDetail.changeAvatarTitle')}
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
className="video-detail-nickname-input"
|
className="video-detail-nickname-input"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="작성자 이름"
|
placeholder={t('videoDetail.nicknamePlaceholder')}
|
||||||
value={commentNickname}
|
value={commentNickname}
|
||||||
onChange={(e) => setCommentNickname(e.target.value)}
|
onChange={(e) => setCommentNickname(e.target.value)}
|
||||||
maxLength={20}
|
maxLength={20}
|
||||||
|
|
@ -365,7 +365,7 @@ const VideoDetailContent: React.FC<VideoDetailContentProps> = ({ videoId, isModa
|
||||||
<textarea
|
<textarea
|
||||||
ref={commentTextareaRef}
|
ref={commentTextareaRef}
|
||||||
className="video-detail-comment-input"
|
className="video-detail-comment-input"
|
||||||
placeholder={authed ? '댓글을 입력하세요...' : '로그인 후 댓글을 작성할 수 있습니다'}
|
placeholder={authed ? t('videoDetail.commentPlaceholder') : t('videoDetail.commentLoginRequired')}
|
||||||
maxLength={500}
|
maxLength={500}
|
||||||
rows={1}
|
rows={1}
|
||||||
value={commentInput}
|
value={commentInput}
|
||||||
|
|
@ -388,12 +388,12 @@ const VideoDetailContent: React.FC<VideoDetailContentProps> = ({ videoId, isModa
|
||||||
onClick={handleCommentSubmit}
|
onClick={handleCommentSubmit}
|
||||||
disabled={!authed || !commentInput.trim() || commentSubmitting}
|
disabled={!authed || !commentInput.trim() || commentSubmitting}
|
||||||
>
|
>
|
||||||
{commentSubmitting ? '작성 중' : '작성'}
|
{commentSubmitting ? t('videoDetail.commentSubmitting') : t('videoDetail.commentSubmit')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{comments.length === 0 && !commentsLoading ? (
|
{comments.length === 0 && !commentsLoading ? (
|
||||||
<p className="video-detail-comments-empty">아직 댓글이 없습니다.</p>
|
<p className="video-detail-comments-empty">{t('videoDetail.noComments')}</p>
|
||||||
) : (
|
) : (
|
||||||
<ul className="video-detail-comment-list">
|
<ul className="video-detail-comment-list">
|
||||||
{comments.map((c) => (
|
{comments.map((c) => (
|
||||||
|
|
@ -405,7 +405,7 @@ const VideoDetailContent: React.FC<VideoDetailContentProps> = ({ videoId, isModa
|
||||||
/>
|
/>
|
||||||
<div className="video-detail-comment-body">
|
<div className="video-detail-comment-body">
|
||||||
<span className="video-detail-comment-nickname">
|
<span className="video-detail-comment-nickname">
|
||||||
{c.nickname || '익명'}
|
{c.nickname || t('videoDetail.anonymous')}
|
||||||
</span>
|
</span>
|
||||||
<p className="video-detail-comment-text">
|
<p className="video-detail-comment-text">
|
||||||
{renderCommentContent(c.content, c.is_deleted)}
|
{renderCommentContent(c.content, c.is_deleted)}
|
||||||
|
|
@ -417,7 +417,7 @@ const VideoDetailContent: React.FC<VideoDetailContentProps> = ({ videoId, isModa
|
||||||
className="video-detail-comment-delete"
|
className="video-detail-comment-delete"
|
||||||
onClick={() => handleDeleteComment(c.id)}
|
onClick={() => handleDeleteComment(c.id)}
|
||||||
>
|
>
|
||||||
삭제
|
{t('videoDetail.deleteComment')}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -434,7 +434,7 @@ const VideoDetailContent: React.FC<VideoDetailContentProps> = ({ videoId, isModa
|
||||||
/>
|
/>
|
||||||
<div className="video-detail-comment-body">
|
<div className="video-detail-comment-body">
|
||||||
<span className="video-detail-comment-nickname">
|
<span className="video-detail-comment-nickname">
|
||||||
{r.nickname || '익명'}
|
{r.nickname || t('videoDetail.anonymous')}
|
||||||
</span>
|
</span>
|
||||||
<p className="video-detail-comment-text">
|
<p className="video-detail-comment-text">
|
||||||
{renderCommentContent(r.content, r.is_deleted)}
|
{renderCommentContent(r.content, r.is_deleted)}
|
||||||
|
|
@ -446,7 +446,7 @@ const VideoDetailContent: React.FC<VideoDetailContentProps> = ({ videoId, isModa
|
||||||
className="video-detail-comment-delete"
|
className="video-detail-comment-delete"
|
||||||
onClick={() => handleDeleteComment(r.id)}
|
onClick={() => handleDeleteComment(r.id)}
|
||||||
>
|
>
|
||||||
삭제
|
{t('videoDetail.deleteComment')}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -467,7 +467,7 @@ const VideoDetailContent: React.FC<VideoDetailContentProps> = ({ videoId, isModa
|
||||||
onClick={() => fetchComments(commentsPage + 1, true)}
|
onClick={() => fetchComments(commentsPage + 1, true)}
|
||||||
disabled={commentsLoading}
|
disabled={commentsLoading}
|
||||||
>
|
>
|
||||||
{commentsLoading ? '불러오는 중...' : '댓글 더 보기'}
|
{commentsLoading ? t('videoDetail.loadingComments') : t('videoDetail.loadMoreComments')}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ const VideoDetailModal: React.FC<VideoDetailModalProps> = ({ videoId, onClose })
|
||||||
background: '#01282A',
|
background: '#01282A',
|
||||||
borderRadius: '16px',
|
borderRadius: '16px',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
maxWidth: '900px',
|
maxWidth: '800px',
|
||||||
margin: 'auto',
|
margin: 'auto',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@
|
||||||
"video": { "title": "Generate Video", "desc": "Click the button to start generating your video." }
|
"video": { "title": "Generate Video", "desc": "Click the button to start generating your video." }
|
||||||
},
|
},
|
||||||
"completion": {
|
"completion": {
|
||||||
"contentInfo": { "title": "Content Info", "desc": "Check the title, genre, resolution, and lyrics of the generated content." },
|
"contentInfo": { "title": "Content Info", "desc": "Check the file name, genre, resolution, and lyrics of the generated content." },
|
||||||
"generating": { "title": "Generating Video", "desc": "AI is creating your video.\nPlease wait a moment." },
|
"generating": { "title": "Generating Video", "desc": "AI is creating your video.\nPlease wait a moment." },
|
||||||
"completion": { "title": "Video Complete!", "desc": "Your video is ready. Want to take a look?" },
|
"completion": { "title": "Video Complete!", "desc": "Your video is ready. Want to take a look?" },
|
||||||
"myInfo": { "title": "Connect Social Account", "desc": "To upload your video to YouTube, connect your social account in My Info. Click to go there." }
|
"myInfo": { "title": "Connect Social Account", "desc": "To upload your video to YouTube, connect your social account in My Info. Click to go there." }
|
||||||
|
|
@ -188,16 +188,16 @@
|
||||||
"hero": {
|
"hero": {
|
||||||
"searchTypeLabel": "Business Name | URL",
|
"searchTypeLabel": "Business Name | URL",
|
||||||
"placeholderCombined": "Enter a business name or Naver Maps/Place URL",
|
"placeholderCombined": "Enter a business name or Naver Maps/Place URL",
|
||||||
"guideCombined": "Enter a business name, or paste a Naver Maps share URL (naver.me/...) to automatically retrieve information.",
|
"guideCombined": "Paste a Naver Maps share URL, or enter a business name to automatically retrieve information.",
|
||||||
"errorInputRequired": "Please enter a business name or URL.",
|
"errorInputRequired": "Please enter a business name or URL.",
|
||||||
"errorInvalidUrl": "Invalid URL format. (e.g., https://naver.me/abcdef)",
|
"errorInvalidUrl": "Invalid URL format. (e.g., https://naver.me/abcdef)",
|
||||||
"analyzeButton": "Brand Analysis",
|
"analyzeButton": "Brand Analysis",
|
||||||
"scrollMore": "Scroll to see more",
|
"scrollMore": "Scroll to see more",
|
||||||
"searching": "Searching...",
|
"searching": "Searching...",
|
||||||
"searchTypeManual": "Not on Naver? Enter manually",
|
"searchTypeManual": "Not on Naver? Enter manually",
|
||||||
"manualButtonDesc": "Even businesses not listed on Naver Maps/Place can get\na full brand analysis, lyrics, music, and video\njust by entering the name, region, and address.",
|
"manualButtonDesc": "Even businesses not listed on Naver Maps/Place\ncan get lyrics, music, and video production\njust by entering the name, region, and address.",
|
||||||
"manualModalTitle": "Enter Business Info Directly",
|
"manualModalTitle": "Enter Business Info Directly",
|
||||||
"manualModalSubtitle": "Even businesses not on Naver can start brand analysis by filling in the details below.",
|
"manualModalSubtitle": "Even businesses not on Naver can get lyrics, music, and video production by filling in the details below.",
|
||||||
"manualAnalyzeButton": "Get Started",
|
"manualAnalyzeButton": "Get Started",
|
||||||
"manualLabelName": "Business Name",
|
"manualLabelName": "Business Name",
|
||||||
"manualLabelAddress": "Address",
|
"manualLabelAddress": "Address",
|
||||||
|
|
@ -278,7 +278,7 @@
|
||||||
"genreLabel": "Select Genre",
|
"genreLabel": "Select Genre",
|
||||||
"genreAuto": "Auto Select",
|
"genreAuto": "Auto Select",
|
||||||
"genreBallad": "Ballad",
|
"genreBallad": "Ballad",
|
||||||
"languageLabel": "Language",
|
"languageLabel": "Select Language",
|
||||||
"languageKorean": "Korean",
|
"languageKorean": "Korean",
|
||||||
"lyricsColumn": "Lyrics",
|
"lyricsColumn": "Lyrics",
|
||||||
"lyricsHint": "Select the lyrics area to edit",
|
"lyricsHint": "Select the lyrics area to edit",
|
||||||
|
|
@ -307,7 +307,7 @@
|
||||||
"songRegenerationError": "An error occurred during music regeneration.",
|
"songRegenerationError": "An error occurred during music regeneration.",
|
||||||
"creditsRemaining": "{{count}} left",
|
"creditsRemaining": "{{count}} left",
|
||||||
"creditsExhausted": "Not enough credits.",
|
"creditsExhausted": "Not enough credits.",
|
||||||
"chargeCredits": "Purchase credits"
|
"chargeCredits": "Top Up Credits"
|
||||||
},
|
},
|
||||||
"completion": {
|
"completion": {
|
||||||
"back": "Go Back",
|
"back": "Go Back",
|
||||||
|
|
@ -549,9 +549,9 @@
|
||||||
"marketPositioning": "Market Positioning",
|
"marketPositioning": "Market Positioning",
|
||||||
"coreValue": "Core Value",
|
"coreValue": "Core Value",
|
||||||
"categoryDefinition": "Category Definition",
|
"categoryDefinition": "Category Definition",
|
||||||
"targetPersona": "Target Persona",
|
"targetPersona": "Key Customer Type",
|
||||||
"ageSuffix": "years old",
|
"ageSuffix": "years old",
|
||||||
"sellingPoints": "Unique Selling Points (USP)",
|
"sellingPoints": "Key Selling Points",
|
||||||
"recommendedKeywords": "Recommended Target Keywords",
|
"recommendedKeywords": "Recommended Target Keywords",
|
||||||
"generateContent": "Generate Content",
|
"generateContent": "Generate Content",
|
||||||
"pageDescBefore": " reveals ",
|
"pageDescBefore": " reveals ",
|
||||||
|
|
@ -592,7 +592,7 @@
|
||||||
"loading": "Loading...",
|
"loading": "Loading...",
|
||||||
"noResults": "No recent results",
|
"noResults": "No recent results",
|
||||||
"noResultsDesc": "Upload your content to social channels",
|
"noResultsDesc": "Upload your content to social channels",
|
||||||
"ado2Contents": "ADO2 Contents",
|
"myContents": "My Contents",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"retry": "Retry",
|
"retry": "Retry",
|
||||||
"cancelConfirm": "Are you sure you want to cancel this reservation?",
|
"cancelConfirm": "Are you sure you want to cancel this reservation?",
|
||||||
|
|
@ -604,7 +604,33 @@
|
||||||
},
|
},
|
||||||
"loginPrompt": {
|
"loginPrompt": {
|
||||||
"title": "Login Required",
|
"title": "Login Required",
|
||||||
"loginBtn": "Login with Kakao"
|
"loginBtn": "Login"
|
||||||
|
},
|
||||||
|
"videoDetail": {
|
||||||
|
"dateFormat": "{{month}}/{{day}}/{{year}}",
|
||||||
|
"kakaoDefaultTitle": "ADO2 Video",
|
||||||
|
"kakaoDescription": "{{region}} · ADO2 AI Marketing Video",
|
||||||
|
"kakaoButtonTitle": "Watch Video",
|
||||||
|
"deletedComment": "(This comment has been deleted.)",
|
||||||
|
"closeAriaLabel": "Close",
|
||||||
|
"share": "Share",
|
||||||
|
"copied": "Copied!",
|
||||||
|
"shareKakao": "KakaoTalk",
|
||||||
|
"shareFacebook": "Facebook",
|
||||||
|
"shareTwitter": "X (Twitter)",
|
||||||
|
"copyUrl": "Copy URL",
|
||||||
|
"commentsTitle": "Comments",
|
||||||
|
"changeAvatarTitle": "Click to change avatar",
|
||||||
|
"nicknamePlaceholder": "Author name",
|
||||||
|
"commentPlaceholder": "Write a comment...",
|
||||||
|
"commentLoginRequired": "Please log in to write a comment",
|
||||||
|
"commentSubmitting": "Submitting",
|
||||||
|
"commentSubmit": "Submit",
|
||||||
|
"noComments": "No comments yet.",
|
||||||
|
"anonymous": "Anonymous",
|
||||||
|
"deleteComment": "Delete",
|
||||||
|
"loadMoreComments": "Load more comments",
|
||||||
|
"loadingComments": "Loading..."
|
||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
"loginProcessing": "Processing login...",
|
"loginProcessing": "Processing login...",
|
||||||
|
|
|
||||||
|
|
@ -591,7 +591,7 @@
|
||||||
"loading": "불러오는 중...",
|
"loading": "불러오는 중...",
|
||||||
"noResults": "최근 결과 없음",
|
"noResults": "최근 결과 없음",
|
||||||
"noResultsDesc": "제작한 콘텐츠를 소셜 채널에 업로드해 보세요",
|
"noResultsDesc": "제작한 콘텐츠를 소셜 채널에 업로드해 보세요",
|
||||||
"ado2Contents": "ADO2 콘텐츠",
|
"myContents": "내 콘텐츠",
|
||||||
"cancel": "취소",
|
"cancel": "취소",
|
||||||
"retry": "재시도",
|
"retry": "재시도",
|
||||||
"cancelConfirm": "예약을 취소하시겠습니까?",
|
"cancelConfirm": "예약을 취소하시겠습니까?",
|
||||||
|
|
@ -605,6 +605,32 @@
|
||||||
"title": "로그인이 필요합니다.",
|
"title": "로그인이 필요합니다.",
|
||||||
"loginBtn": "로그인"
|
"loginBtn": "로그인"
|
||||||
},
|
},
|
||||||
|
"videoDetail": {
|
||||||
|
"dateFormat": "{{year}}년 {{month}}월 {{day}}일",
|
||||||
|
"kakaoDefaultTitle": "ADO2 영상",
|
||||||
|
"kakaoDescription": "{{region}} · ADO2 AI 마케팅 영상",
|
||||||
|
"kakaoButtonTitle": "영상 보기",
|
||||||
|
"deletedComment": "(삭제된 댓글입니다.)",
|
||||||
|
"closeAriaLabel": "닫기",
|
||||||
|
"share": "공유하기",
|
||||||
|
"copied": "복사됨!",
|
||||||
|
"shareKakao": "카카오톡",
|
||||||
|
"shareFacebook": "페이스북",
|
||||||
|
"shareTwitter": "X (트위터)",
|
||||||
|
"copyUrl": "URL 복사",
|
||||||
|
"commentsTitle": "댓글",
|
||||||
|
"changeAvatarTitle": "클릭하여 아바타 변경",
|
||||||
|
"nicknamePlaceholder": "작성자 이름",
|
||||||
|
"commentPlaceholder": "댓글을 입력하세요...",
|
||||||
|
"commentLoginRequired": "로그인 후 댓글을 작성할 수 있습니다",
|
||||||
|
"commentSubmitting": "작성 중",
|
||||||
|
"commentSubmit": "작성",
|
||||||
|
"noComments": "아직 댓글이 없습니다.",
|
||||||
|
"anonymous": "익명",
|
||||||
|
"deleteComment": "삭제",
|
||||||
|
"loadMoreComments": "댓글 더 보기",
|
||||||
|
"loadingComments": "불러오는 중..."
|
||||||
|
},
|
||||||
"app": {
|
"app": {
|
||||||
"loginProcessing": "로그인 처리 중...",
|
"loginProcessing": "로그인 처리 중...",
|
||||||
"loginFailed": "로그인 처리에 실패했습니다. 다시 시도해주세요.",
|
"loginFailed": "로그인 처리에 실패했습니다. 다시 시도해주세요.",
|
||||||
|
|
|
||||||
|
|
@ -476,11 +476,11 @@ const CompletionContent: React.FC<CompletionContentProps> = ({
|
||||||
<div className="comp2-info-header">
|
<div className="comp2-info-header">
|
||||||
<span className="comp2-info-label">{t('completion.contentInfo')}</span>
|
<span className="comp2-info-label">{t('completion.contentInfo')}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="comp2-info-content">
|
|
||||||
<div className="comp2-file-info">
|
<div className="comp2-file-info">
|
||||||
<h3 className="comp2-filename">{getFileName()}</h3>
|
<h3 className="comp2-filename">{getFileName()}</h3>
|
||||||
{/* <p className="comp2-filesize">19.6MB</p> */}
|
{/* <p className="comp2-filesize">19.6MB</p> */}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="comp2-info-content">
|
||||||
<div className="comp2-meta-grid">
|
<div className="comp2-meta-grid">
|
||||||
<div className="comp2-meta-item">
|
<div className="comp2-meta-item">
|
||||||
<span className="comp2-meta-label">{t('completion.genre')} : {songCompletionData?.genre || 'K-POP'}</span>
|
<span className="comp2-meta-label">{t('completion.genre')} : {songCompletionData?.genre || 'K-POP'}</span>
|
||||||
|
|
|
||||||
|
|
@ -645,7 +645,7 @@ const ContentCalendarContent: React.FC<ContentCalendarContentProps> = ({ onNavig
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => onNavigate?.('ADO2 콘텐츠')}
|
onClick={() => onNavigate?.('내 콘텐츠')}
|
||||||
style={{
|
style={{
|
||||||
height: 34, padding: '0 10px', borderRadius: 8, border: 'none',
|
height: 34, padding: '0 10px', borderRadius: 8, border: 'none',
|
||||||
backgroundColor: '#a65eff', cursor: 'pointer',
|
backgroundColor: '#a65eff', cursor: 'pointer',
|
||||||
|
|
@ -653,7 +653,7 @@ const ContentCalendarContent: React.FC<ContentCalendarContentProps> = ({ onNavig
|
||||||
color: '#ffffff',
|
color: '#ffffff',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('contentCalendar.ado2Contents')}
|
{t('contentCalendar.myContents')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -717,7 +717,7 @@ const ContentCalendarContent: React.FC<ContentCalendarContentProps> = ({ onNavig
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 바텀시트 */}
|
{/* 바텀시트 */}
|
||||||
<div style={{ position: 'fixed', bottom: 0, left: 0, right: 0, zIndex: 100, display: 'flex', flexDirection: 'column' }}>
|
<div style={{ position: 'fixed', bottom: 0, left: 0, right: 0, zIndex: 10, display: 'flex', flexDirection: 'column' }}>
|
||||||
<div style={{
|
<div style={{
|
||||||
backgroundColor: '#01393b',
|
backgroundColor: '#01393b',
|
||||||
borderTop: '1px solid #046266',
|
borderTop: '1px solid #046266',
|
||||||
|
|
|
||||||
|
|
@ -301,7 +301,7 @@ const GenerationFlow: React.FC<GenerationFlowProps> = ({
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then((data: CrawlingResponse) => {
|
.then((data: CrawlingResponse) => {
|
||||||
// 언어 변경 시에는 기존 m_id 유지
|
// 언어 변경 시에는 기존 m_id 유지
|
||||||
const tagged = { ...data, m_id: parsed.m_id || generateRandomMId(), _isTestData: true };
|
const tagged = { ...data, m_id: parsed.m_id, _isTestData: true };
|
||||||
setAnalysisData(tagged);
|
setAnalysisData(tagged);
|
||||||
localStorage.setItem(ANALYSIS_DATA_KEY, JSON.stringify(tagged));
|
localStorage.setItem(ANALYSIS_DATA_KEY, JSON.stringify(tagged));
|
||||||
})
|
})
|
||||||
|
|
@ -646,7 +646,7 @@ const GenerationFlow: React.FC<GenerationFlowProps> = ({
|
||||||
<Sidebar activeItem={activeItem} onNavigate={handleNavigate} onHome={handleHome} userInfo={userInfo} onLogout={handleLogout} credits={credits} tutorialAvailable={!!getCurrentTutorialKey()} tutorialEnabled={tutorial.isEnabled} onToggleTutorial={() => tutorial.toggleTutorial(getCurrentTutorialKey())} />
|
<Sidebar activeItem={activeItem} onNavigate={handleNavigate} onHome={handleHome} userInfo={userInfo} onLogout={handleLogout} credits={credits} tutorialAvailable={!!getCurrentTutorialKey()} tutorialEnabled={tutorial.isEnabled} onToggleTutorial={() => tutorial.toggleTutorial(getCurrentTutorialKey())} />
|
||||||
)}
|
)}
|
||||||
{tutorialUI}
|
{tutorialUI}
|
||||||
<div className={`flex-1 relative pl-0 md:pl-0 ${needsScroll ? 'overflow-auto' : 'h-full overflow-hidden'}`}>
|
<div className={`flex-1 relative pl-0 md:pl-0 bg-[#001a1c] ${needsScroll ? 'overflow-auto' : 'h-full overflow-hidden'}`}>
|
||||||
{renderContent()}
|
{renderContent()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,27 @@ const MAX_RETRY_COUNT = 3;
|
||||||
// GenerationFlow의 clearAllProjectStorage가 이 키를 포함해 정리함
|
// GenerationFlow의 clearAllProjectStorage가 이 키를 포함해 정리함
|
||||||
const SONG_GENERATION_KEY = 'castad_song_generation';
|
const SONG_GENERATION_KEY = 'castad_song_generation';
|
||||||
|
|
||||||
|
const GENRE_VALUES = ['kpop', 'pop', 'ballad', 'hip-hop', 'rnb', 'edm', 'jazz', 'rock'];
|
||||||
|
|
||||||
|
const genreMap: Record<string, string> = {
|
||||||
|
'자동 선택': '', // 런타임에 랜덤 결정
|
||||||
|
'K-POP': 'kpop',
|
||||||
|
'POP': 'pop',
|
||||||
|
'발라드': 'ballad',
|
||||||
|
'Hip-Hop': 'hip-hop',
|
||||||
|
'R&B': 'rnb',
|
||||||
|
'EDM': 'edm',
|
||||||
|
'JAZZ': 'jazz',
|
||||||
|
'ROCK': 'rock',
|
||||||
|
};
|
||||||
|
|
||||||
|
const getEffectiveGenre = (selectedGenre: string): string => {
|
||||||
|
if (selectedGenre === '자동 선택') {
|
||||||
|
return GENRE_VALUES[Math.floor(Math.random() * GENRE_VALUES.length)];
|
||||||
|
}
|
||||||
|
return genreMap[selectedGenre] || 'pop';
|
||||||
|
};
|
||||||
|
|
||||||
const sanitizeError = (message: string, fallback: string): string => {
|
const sanitizeError = (message: string, fallback: string): string => {
|
||||||
if (
|
if (
|
||||||
message.includes('Traceback') ||
|
message.includes('Traceback') ||
|
||||||
|
|
@ -69,6 +90,7 @@ const SoundStudioContent: React.FC<SoundStudioContentProps> = ({
|
||||||
const [selectedType, setSelectedType] = useState('보컬');
|
const [selectedType, setSelectedType] = useState('보컬');
|
||||||
const [selectedLang, setSelectedLang] = useState('한국어');
|
const [selectedLang, setSelectedLang] = useState('한국어');
|
||||||
const [selectedGenre, setSelectedGenre] = useState('자동 선택');
|
const [selectedGenre, setSelectedGenre] = useState('자동 선택');
|
||||||
|
const [usedGenre, setUsedGenre] = useState('');
|
||||||
const [progress, setProgress] = useState(0);
|
const [progress, setProgress] = useState(0);
|
||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
const [status, setStatus] = useState<GenerationStatus>('idle');
|
const [status, setStatus] = useState<GenerationStatus>('idle');
|
||||||
|
|
@ -88,7 +110,7 @@ const SoundStudioContent: React.FC<SoundStudioContentProps> = ({
|
||||||
const saveSongCompletionData = () => {
|
const saveSongCompletionData = () => {
|
||||||
const completionData = {
|
const completionData = {
|
||||||
businessName: businessInfo?.customer_name || '알 수 없음',
|
businessName: businessInfo?.customer_name || '알 수 없음',
|
||||||
genre: selectedGenre === '자동 선택' ? 'K-POP' : selectedGenre,
|
genre: selectedGenre,
|
||||||
lyrics: lyrics,
|
lyrics: lyrics,
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
};
|
};
|
||||||
|
|
@ -211,20 +233,13 @@ const SoundStudioContent: React.FC<SoundStudioContentProps> = ({
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const language = LANGUAGE_MAP[selectedLang] || 'Korean';
|
const language = LANGUAGE_MAP[selectedLang] || 'Korean';
|
||||||
const genreMap: Record<string, string> = {
|
|
||||||
'자동 선택': 'pop',
|
|
||||||
'K-POP': 'kpop',
|
|
||||||
'발라드': 'ballad',
|
|
||||||
'Hip-Hop': 'hip-hop',
|
|
||||||
'R&B': 'rnb',
|
|
||||||
'EDM': 'edm',
|
|
||||||
'JAZZ': 'jazz',
|
|
||||||
'ROCK': 'rock',
|
|
||||||
};
|
|
||||||
|
|
||||||
const isInstrumental = selectedType === '배경음악';
|
const isInstrumental = selectedType === '배경음악';
|
||||||
|
const effectiveGenre = getEffectiveGenre(selectedGenre);
|
||||||
|
setUsedGenre(effectiveGenre);
|
||||||
|
|
||||||
const songResponse = await generateSong(imageTaskId, {
|
const songResponse = await generateSong(imageTaskId, {
|
||||||
genre: genreMap[selectedGenre] || 'pop',
|
genre: effectiveGenre,
|
||||||
...(isInstrumental ? { instrumental: true, lyrics: '' } : { language, lyrics: currentLyrics }),
|
...(isInstrumental ? { instrumental: true, lyrics: '' } : { language, lyrics: currentLyrics }),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -371,17 +386,6 @@ const SoundStudioContent: React.FC<SoundStudioContentProps> = ({
|
||||||
throw new Error(lyricResponse.error_message || t('soundStudio.lyricGenerationFailed'));
|
throw new Error(lyricResponse.error_message || t('soundStudio.lyricGenerationFailed'));
|
||||||
}
|
}
|
||||||
|
|
||||||
const genreMap: Record<string, string> = {
|
|
||||||
'자동 선택': 'pop',
|
|
||||||
'K-POP': 'kpop',
|
|
||||||
'발라드': 'ballad',
|
|
||||||
'Hip-Hop': 'hip-hop',
|
|
||||||
'R&B': 'rnb',
|
|
||||||
'EDM': 'edm',
|
|
||||||
'JAZZ': 'jazz',
|
|
||||||
'ROCK': 'rock',
|
|
||||||
};
|
|
||||||
|
|
||||||
let songLyrics: string | undefined;
|
let songLyrics: string | undefined;
|
||||||
|
|
||||||
if (!isInstrumental) {
|
if (!isInstrumental) {
|
||||||
|
|
@ -412,8 +416,11 @@ const SoundStudioContent: React.FC<SoundStudioContentProps> = ({
|
||||||
setStatus('generating_song');
|
setStatus('generating_song');
|
||||||
setStatusMessage(t('soundStudio.generatingSong'));
|
setStatusMessage(t('soundStudio.generatingSong'));
|
||||||
|
|
||||||
|
const effectiveGenre = getEffectiveGenre(selectedGenre);
|
||||||
|
setUsedGenre(effectiveGenre);
|
||||||
|
|
||||||
const songResponse = await generateSong(imageTaskId, {
|
const songResponse = await generateSong(imageTaskId, {
|
||||||
genre: genreMap[selectedGenre] || 'pop',
|
genre: effectiveGenre,
|
||||||
...(isInstrumental ? { instrumental: true, lyrics: '' } : { language, lyrics: songLyrics }),
|
...(isInstrumental ? { instrumental: true, lyrics: '' } : { language, lyrics: songLyrics }),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -552,7 +559,7 @@ const SoundStudioContent: React.FC<SoundStudioContentProps> = ({
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="genre-row">
|
<div className="genre-row">
|
||||||
{['JAZZ', 'ROCK'].map(genre => (
|
{['JAZZ', 'ROCK', 'POP'].map(genre => (
|
||||||
<button
|
<button
|
||||||
key={genre}
|
key={genre}
|
||||||
onClick={() => setSelectedGenre(genre)}
|
onClick={() => setSelectedGenre(genre)}
|
||||||
|
|
@ -562,7 +569,6 @@ const SoundStudioContent: React.FC<SoundStudioContentProps> = ({
|
||||||
{genre}
|
{genre}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
<div className="genre-btn-placeholder"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -310,6 +310,7 @@
|
||||||
color: #379599;
|
color: #379599;
|
||||||
letter-spacing: -0.006em;
|
letter-spacing: -0.006em;
|
||||||
line-height: 1.19;
|
line-height: 1.19;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-links {
|
.footer-links {
|
||||||
|
|
|
||||||
|
|
@ -626,7 +626,7 @@
|
||||||
border: none;
|
border: none;
|
||||||
color: #9BCACC;
|
color: #9BCACC;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 4px;
|
padding-bottom: 8px;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
transition: color 0.2s;
|
transition: color 0.2s;
|
||||||
}
|
}
|
||||||
|
|
@ -647,7 +647,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-detail-header {
|
.video-detail-header {
|
||||||
margin-bottom: 24px;
|
/* margin-bottom: 24px; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-detail-back-btn {
|
.video-detail-back-btn {
|
||||||
|
|
@ -685,7 +685,7 @@
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
max-width: 450px;
|
max-width: 360px;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
padding: 0.75rem;
|
padding: 0.75rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background-color: var(--color-bg-dark);
|
background-color: var(--color-bg-darker);
|
||||||
color: var(--color-text-white);
|
color: var(--color-text-white);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -563,7 +563,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.comp2-title-row {
|
.comp2-title-row {
|
||||||
padding: 16px;
|
padding: 10px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
@ -2389,7 +2389,7 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background-color: #002224;
|
background-color: var(--color-bg-darker);
|
||||||
color: var(--color-text-white);
|
color: var(--color-text-white);
|
||||||
padding: 80px 1rem 100px;
|
padding: 80px 1rem 100px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
@ -2405,6 +2405,7 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-top: 50px;
|
margin-top: 50px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
max-width: 375px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.url-input-icon {
|
.url-input-icon {
|
||||||
|
|
@ -2418,9 +2419,28 @@
|
||||||
|
|
||||||
.url-input-logo img {
|
.url-input-logo img {
|
||||||
width: 350px;
|
width: 350px;
|
||||||
|
max-width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (min-width: 640px) {
|
||||||
|
.url-input-logo img {
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.url-input-logo img {
|
||||||
|
width: 500px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.url-input-logo img {
|
||||||
|
width: 554px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.url-input-title {
|
.url-input-title {
|
||||||
font-family: 'Pretendard', sans-serif;
|
font-family: 'Pretendard', sans-serif;
|
||||||
font-size: 40px;
|
font-size: 40px;
|
||||||
|
|
|
||||||
|
|
@ -63,10 +63,11 @@
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-top: 50px;
|
margin-top: 50px;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-logo {
|
.hero-logo {
|
||||||
width: 280px;
|
width: 350px;
|
||||||
height: auto;
|
height: auto;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
@ -396,7 +397,7 @@
|
||||||
|
|
||||||
.hero-manual-card {
|
.hero-manual-card {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 16px 20px;
|
padding: 10px 16px;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
border: 1.5px solid var(--color-mint);
|
border: 1.5px solid var(--color-mint);
|
||||||
background: rgba(15, 60, 60, 0.5);
|
background: rgba(15, 60, 60, 0.5);
|
||||||
|
|
@ -451,7 +452,7 @@
|
||||||
/* Scroll Indicator */
|
/* Scroll Indicator */
|
||||||
.scroll-indicator {
|
.scroll-indicator {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 2rem;
|
bottom: 1rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
@ -466,7 +467,7 @@
|
||||||
|
|
||||||
@media (min-width: 640px) {
|
@media (min-width: 640px) {
|
||||||
.scroll-indicator {
|
.scroll-indicator {
|
||||||
bottom: 3rem;
|
bottom: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -779,7 +780,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.feature-card-description {
|
.feature-card-description {
|
||||||
font-size: 12px;
|
font-size: 14px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
color: #E5F1F2;
|
color: #E5F1F2;
|
||||||
letter-spacing: -0.006em;
|
letter-spacing: -0.006em;
|
||||||
|
|
@ -1486,24 +1487,12 @@
|
||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.loading-content {
|
|
||||||
gap: 4rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Loading Logo */
|
/* Loading Logo */
|
||||||
.loading-logo {
|
.loading-logo {
|
||||||
width: 200px;
|
width: 300px;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.loading-logo {
|
|
||||||
width: 308px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-logo img {
|
.loading-logo img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
|
@ -1520,7 +1509,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-title {
|
.loading-title {
|
||||||
font-size: 1.5rem;
|
font-size: 2rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
letter-spacing: -0.006em;
|
letter-spacing: -0.006em;
|
||||||
line-height: 1.19;
|
line-height: 1.19;
|
||||||
|
|
@ -1528,12 +1517,6 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.loading-title {
|
|
||||||
font-size: 2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Loading Progress Bar */
|
/* Loading Progress Bar */
|
||||||
.loading-progress-wrapper {
|
.loading-progress-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -1567,19 +1550,13 @@
|
||||||
|
|
||||||
/* Loading Spinner Wrapper */
|
/* Loading Spinner Wrapper */
|
||||||
.loading-spinner-wrapper {
|
.loading-spinner-wrapper {
|
||||||
width: 120px;
|
width: 200px;
|
||||||
height: 120px;
|
height: 200px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.loading-spinner-wrapper {
|
|
||||||
width: 200px;
|
|
||||||
height: 200px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-spinner-icon {
|
.loading-spinner-icon {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
||||||
|
|
@ -1315,7 +1315,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.asset-scroll-area {
|
.asset-scroll-area {
|
||||||
padding: 80px 1rem 120px;
|
padding: 1rem 1rem 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.asset-image-grid {
|
.asset-image-grid {
|
||||||
|
|
@ -1330,7 +1330,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.asset-scroll-area {
|
.asset-scroll-area {
|
||||||
padding: 80px 1.5rem 120px;
|
padding: 1.5rem 1.5rem 120px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue