Binary files /dev/null and b/public/voice-clone/add-voice.png differ
Binary files /dev/null and b/public/voice-clone/message-input-mobile.png differ
Binary files /dev/null and b/public/voice-clone/message-preview-mobile.png differ
Binary files /dev/null and b/public/voice-clone/my-generations.png differ
Binary files /dev/null and b/public/voice-clone/parameters-panel.png differ
Binary files /dev/null and b/public/voice-clone/step-1.png differ
Binary files /dev/null and b/public/voice-clone/step-2.png differ
Binary files /dev/null and b/public/voice-clone/voice-selector-mobile.png differ
@@ -62,12 +62,6 @@
margin-right: 8px;
background-color: #7F7DF31A;
gap: 4px;
-
- @media screen and (max-width: 400px) {
- padding: 0;
- gap: 2px;
- background-color: transparent;
- }
}
.generationIcon {
@@ -0,0 +1,6 @@
+import { VoiceClonePage } from '#/views/voice-clone'
+import { getDefaultLayout } from '#/widgets/layouts'
+
+VoiceClonePage.getLayout = getDefaultLayout({ titlePage: 'Войсклон' })
+
+export default VoiceClonePage
@@ -0,0 +1,3 @@
+.card {
+ background: linear-gradient(to top left, rgba(42, 40, 188, 0.2), transparent 52%), #1a1a1e;
+}
@@ -0,0 +1,27 @@
+import * as React from 'react'
+import Image from 'next/image'
+
+import shell from './voice-clone-card-shell.module.scss'
+import fig from './voice-clone-figure.module.scss'
+import styles from './add-voices-card.module.scss'
+
+export function AddVoicesCard() {
+ return (
+
+
+ Добавляйте новые голоса
+
+
+
+
+
+
+
+ )
+}
@@ -0,0 +1,96 @@
+.grid {
+ display: grid;
+ grid-template-columns: 203px 78px 405px;
+ grid-template-rows: auto auto;
+ row-gap: 16px;
+ align-items: start;
+}
+
+.cellFigure1 {
+ grid-column: 1;
+ grid-row: 1;
+ align-self: center;
+}
+
+.cellArrow {
+ grid-column: 2;
+ grid-row: 1;
+ place-self: center;
+}
+
+.cellFigure2 {
+ grid-column: 3;
+ grid-row: 1;
+ align-self: center;
+}
+
+.cellText1 {
+ grid-column: 1;
+ grid-row: 2;
+ min-width: 0;
+}
+
+.cellText2 {
+ grid-column: 3;
+ grid-row: 2;
+ min-width: 0;
+}
+
+.stepFigure {
+ margin: 0;
+ border-radius: 12px;
+ overflow: hidden;
+ line-height: 0;
+ background: rgba(0, 0, 0, 0.2);
+}
+
+.stepFigureNarrow {
+ width: 203px;
+ height: 146px;
+}
+
+.stepFigureWide {
+ width: 405px;
+ height: 148px;
+}
+
+.stepImage {
+ display: block;
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+}
+
+.arrowWrap {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: #a4aab5;
+
+ svg {
+ display: block;
+ }
+}
+
+.stepTitle {
+ margin: 0;
+ font-size: 20px;
+ font-weight: 700;
+ line-height: 1.2;
+ letter-spacing: -0.01em;
+ color: #fff;
+}
+
+.stepText {
+ margin: 8px 0 0;
+ max-width: 220px;
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 1.35;
+ letter-spacing: -0.01em;
+ color: #a4aab5;
+}
+
+.cellText2 .stepText {
+ max-width: 405px;
+}
@@ -0,0 +1,71 @@
+import * as React from 'react'
+import Image from 'next/image'
+
+import shell from './voice-clone-card-shell.module.scss'
+import styles from './how-to-model-card.module.scss'
+
+function StepArrow() {
+ return (
+
+ )
+}
+
+export function HowToModelCard() {
+ return (
+
+
+ Как работать с моделью
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Шаг 1.
+
Выберите один из шаблонов голоса или создайте свой
+
+
+
+
Шаг 2.
+
+ Введите текст, который нужно озвучить и нажмите на кнопку – запустится процесс генерации
+
+
+
+
+
+ )
+}
@@ -0,0 +1,4 @@
+export { HowToModelCard } from './how-to-model-card'
+export { SoundParamsCard } from './sound-params-card'
+export { AddVoicesCard } from './add-voices-card'
+export { ReuseQueryCard } from './reuse-query-card'
@@ -0,0 +1,3 @@
+.card {
+ background: linear-gradient(to top right, #2b2b42, #18181b);
+}
@@ -0,0 +1,27 @@
+import * as React from 'react'
+import Image from 'next/image'
+
+import shell from './voice-clone-card-shell.module.scss'
+import fig from './voice-clone-figure.module.scss'
+import styles from './reuse-query-card.module.scss'
+
+export function ReuseQueryCard() {
+ return (
+
+
+ Переиспользуйте запрос по клику
+
+
+
+
+
+
+
+ )
+}
@@ -0,0 +1,3 @@
+.card {
+ background: linear-gradient(to bottom right, #2b2b42, #18181b);
+}
@@ -0,0 +1,30 @@
+import * as React from 'react'
+import Image from 'next/image'
+
+import shell from './voice-clone-card-shell.module.scss'
+import fig from './voice-clone-figure.module.scss'
+import styles from './sound-params-card.module.scss'
+
+export function SoundParamsCard() {
+ return (
+
+
+ Меняйте параметры звучания
+
+
+ Экспериментируйте с настройками, чтобы добиться идеального звучания
+
+
+
+
+
+
+
+ )
+}
@@ -0,0 +1,79 @@
+$purple-fade: rgba(42, 40, 188, 0.9) 0%, rgba(42, 40, 188, 0.4) 32%, rgba(42, 40, 188, 0.1) 55%, transparent 72%;
+
+.shell {
+ position: relative;
+ overflow: hidden;
+ border-radius: 16px;
+ padding: 25px;
+ width: 100%;
+ background: #1d1d21;
+}
+
+.shellFill {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+.shellFill .body {
+ flex: 1 1 auto;
+ display: flex;
+ flex-direction: column;
+ min-height: 0;
+}
+
+.shellCornerGlow::before,
+.shellCornerGlow::after {
+ content: '';
+ position: absolute;
+ z-index: 0;
+ pointer-events: none;
+ width: min(100%, 520px);
+ height: 75%;
+ min-height: 200px;
+ filter: blur(48px);
+ opacity: 0.72;
+}
+
+.shellCornerGlow::before {
+ top: -12%;
+ right: -8%;
+ background: radial-gradient(ellipse 80% 70% at 100% 0%, $purple-fade);
+}
+
+.shellCornerGlow::after {
+ bottom: -12%;
+ left: -8%;
+ background: radial-gradient(ellipse 80% 70% at 0% 100%, $purple-fade);
+}
+
+.title {
+ position: relative;
+ z-index: 1;
+ margin: 0 0 20px;
+ font-size: 28px;
+ font-weight: 600;
+ line-height: 1;
+ letter-spacing: -0.01em;
+ color: #fff;
+}
+
+.shell:has(.description) .title {
+ margin-bottom: 12px;
+}
+
+.description {
+ position: relative;
+ z-index: 1;
+ margin: 0 0 20px;
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 1.35;
+ letter-spacing: -0.01em;
+ color: #a4aab5;
+}
+
+.body {
+ position: relative;
+ z-index: 1;
+}
@@ -0,0 +1,17 @@
+.figure {
+ margin: 0;
+ border-radius: 12px;
+ overflow: hidden;
+ line-height: 0;
+ background: rgba(0, 0, 0, 0.2);
+}
+
+.figureFull {
+ max-width: 100%;
+}
+
+.image {
+ display: block;
+ width: 100%;
+ height: auto;
+}
@@ -0,0 +1,3 @@
+export { default as VoiceClonePage } from './voice-clone-page'
+export { VoiceCloneCardsGrid } from './voice-clone-cards-grid'
+export { HowToModelCard, SoundParamsCard, AddVoicesCard, ReuseQueryCard } from './cards'
@@ -0,0 +1,50 @@
+.root {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ width: 100%;
+ max-width: 1130px;
+ overflow-x: auto;
+}
+
+.row {
+ display: flex;
+ flex-wrap: nowrap;
+ align-items: stretch;
+ gap: 20px;
+ min-width: 0;
+}
+
+.mainSlot {
+ flex: 1 1 0;
+ min-width: 0;
+ max-width: 736px;
+ display: flex;
+}
+
+.sideSlot {
+ flex: 0 1 374px;
+ width: 374px;
+ max-width: 100%;
+ min-width: 280px;
+ display: flex;
+}
+
+.rowBottom .mainSlot {
+ flex: 502 1 0;
+ max-width: none;
+}
+
+.rowBottom .sideSlot {
+ flex: 608 1 0;
+ width: auto;
+ min-width: 0;
+}
+
+.mainSlot > *,
+.sideSlot > * {
+ flex: 1;
+ width: 100%;
+ min-width: 0;
+ min-height: 0;
+}
@@ -0,0 +1,28 @@
+import * as React from 'react'
+
+import { AddVoicesCard, HowToModelCard, ReuseQueryCard, SoundParamsCard } from './cards'
+
+import styles from './voice-clone-cards-grid.module.scss'
+
+export function VoiceCloneCardsGrid() {
+ return (
+
+ )
+}
@@ -0,0 +1,63 @@
+.root {
+ width: 100%;
+}
+
+.chatFrame {
+ overflow: hidden;
+ padding: 25px 10px 10px;
+}
+
+.title {
+ margin: 0 0 25px 15px;
+ font-weight: 700;
+ font-size: 18.27px;
+ line-height: 100%;
+ letter-spacing: -0.01em;
+ color: #fff;
+}
+
+.steps {
+ display: flex;
+ flex-direction: column;
+ gap: 25px;
+ margin-top: 25px;
+}
+
+.step {
+ position: relative;
+}
+
+.stepBadge {
+ position: absolute;
+ z-index: 1;
+ top: -8px;
+ left: 5px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ height: 29px;
+ padding: 7px 10px;
+ border-radius: 95px;
+ font-weight: 800;
+ font-size: 12.79px;
+ line-height: 1;
+ letter-spacing: -0.01em;
+ color: #fff;
+ background: #8280ff;
+ box-shadow: 0 4px 4px rgba(0, 0, 0, 0.25);
+}
+
+.stepImageWrap {
+ margin: 0 10px;
+ line-height: 0;
+}
+
+.stepImage {
+ display: block;
+ height: auto;
+ width: 100%;
+}
+
+.footerInputWrap {
+ margin-top: 51px;
+}
@@ -0,0 +1,51 @@
+import * as React from 'react'
+import { Box } from '@mui/material'
+import Image from 'next/image'
+
+import styles from './voice-clone-mobile.module.scss'
+import { ModelInput } from '#/features/model-input'
+
+export function VoiceCloneMobile() {
+ const steps = [
+ { label: 'Шаг 1', src: '/voice-clone/message-input-mobile.png', alt: 'Ввод сообщения' },
+ { label: 'Шаг 2', src: '/voice-clone/voice-selector-mobile.png', alt: 'Выбор голоса' },
+ { label: 'Шаг 3', src: '/voice-clone/message-preview-mobile.png', alt: 'Предпросмотр сообщения' },
+ ]
+ const inputTypes = [{ type: 'text', required: true, versions: [] }]
+
+ return (
+
+
+
+ Добавляйте свой голос
+
+ или выбирайте из пресетов
+
+
+
+ {steps.map((step) => (
+
+ ))}
+
+
+
+ false}
+ styles='chats'
+ viewMobileSettings={() => {}}
+ currentVersion=''
+ input_types={inputTypes}
+ />
+
+
+
+
+ )
+}
@@ -0,0 +1,33 @@
+import * as React from 'react'
+import { Box, Typography } from '@mui/material'
+
+import { NextPageWithLayout } from '#/pages/_app'
+import { getDeviceType } from '#/shared/lib/helpers'
+
+import { VoiceCloneCardsGrid } from './voice-clone-cards-grid'
+import { VoiceCloneMobile } from './voice-clone-mobile'
+
+export const VoiceClonePage: NextPageWithLayout = () => {
+ const device = getDeviceType()
+ const desktop = device === 'desktop'
+
+ return (
+
+ {desktop && (
+ <>
+
+ Войсклон
+
+
+
+
+
+ >
+ )}
+
+ {!desktop && }
+
+ )
+}
+
+export default VoiceClonePage
@@ -0,0 +1 @@
+export * from './ui'