mirror of
https://github.com/placeholder-soft/storytime.git
synced 2026-01-12 15:24:45 +08:00
feat: adding images
This commit is contained in:
@@ -1,10 +1,28 @@
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { Container, Heading } from "@radix-ui/themes";
|
||||
import styled from "styled-components";
|
||||
import { toScene } from "../modules/story/actions";
|
||||
import { storySelector } from "../modules/story/selectors";
|
||||
|
||||
const BackgroundImageContainer = styled.div<{ backgroundImageUrl: string }>`
|
||||
background-image: url(${(props) => props.backgroundImageUrl});
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
height: 100vh; // Full height of the viewport
|
||||
width: 100%; // Full width of the viewport
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center; // Center the content vertically and horizontally
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const Heading = styled.h1`
|
||||
font-size: 4rem; /* Large font size */
|
||||
color: white; /* White text color */
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); /* Text shadow for better readability */
|
||||
`;
|
||||
|
||||
const Cover = () => {
|
||||
const { title, currentSceneIndex } = useSelector(storySelector);
|
||||
const { title, coverImage, currentSceneIndex } = useSelector(storySelector);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const next = () => {
|
||||
@@ -13,10 +31,13 @@ const Cover = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Container onClick={next}>
|
||||
<BackgroundImageContainer backgroundImageUrl={coverImage} onClick={next}>
|
||||
<Heading>{title}</Heading>
|
||||
<Heading as="h4">image here</Heading>
|
||||
</Container>
|
||||
</BackgroundImageContainer>
|
||||
// <Container onClick={next}>
|
||||
// <Heading>{title}</Heading>
|
||||
// <img src={coverImage} />
|
||||
// </Container>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,15 +1,88 @@
|
||||
import { useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { Container, Heading } from "@radix-ui/themes";
|
||||
import {
|
||||
storySelector,
|
||||
currentSceneSelector,
|
||||
} from "../modules/story/selectors";
|
||||
import { updateStory } from "../modules/story/actions";
|
||||
import styled from "styled-components";
|
||||
import { currentSceneSelector } from "../modules/story/selectors";
|
||||
import { updateStory, toScene } from "../modules/story/actions";
|
||||
import { StoryProgressPromptRole } from "../types/story";
|
||||
|
||||
const Cover = () => {
|
||||
// const { currentSceneIndex } = useSelector(storySelector);
|
||||
const { sceneTitle, sceneDescription, optionPrompt, options } =
|
||||
const BackgroundImageContainer = styled.div<{ backgroundImageUrl: string }>`
|
||||
background-image: url(${(props) => props.backgroundImageUrl});
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
height: 100vh; // Full height of the viewport
|
||||
width: 100%; // Full width of the viewport
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center; // Center the content vertically and horizontally
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const QuoteContainer = styled.div`
|
||||
background: linear-gradient(
|
||||
0deg,
|
||||
rgba(24, 24, 24, 0.8),
|
||||
rgba(24, 24, 24, 0.8)
|
||||
),
|
||||
linear-gradient(0deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.1));
|
||||
font-size: 24px; /* Adjust as needed */
|
||||
width: 90%; /* Adjust as needed */
|
||||
position: absolute;
|
||||
bottom: 100px; /* Adjust as needed */
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
padding: 28px;
|
||||
`;
|
||||
|
||||
const QuoteText = styled.p`
|
||||
color: #ffffff;
|
||||
font-size: 24px; /* Adjust as needed */
|
||||
`;
|
||||
|
||||
const ButtonContainer = styled.div`
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const Button = styled.button`
|
||||
background-color: #000000; /* Adjust as needed */
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
padding: 10px 20px; /* Adjust as needed */
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const OptionButtonContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
gap: 10px; /* Space between options */
|
||||
`;
|
||||
|
||||
const OptionButton = styled.button`
|
||||
background-color: rgba(0, 0, 0, 0.7); /* Semi-transparent black */
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 20px; /* Rounded corners */
|
||||
padding: 10px 30px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); /* Box shadow for a 3D effect */
|
||||
text-transform: uppercase; /* Uppercase text */
|
||||
width: 200px; /* Fixed width, adjust as needed */
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.8); /* Darken button on hover */
|
||||
}
|
||||
`;
|
||||
|
||||
const Scene = () => {
|
||||
const [step, setStep] = useState(0); // note: 0 is description, 1 is options
|
||||
const { sceneNumber, sceneDescription, sceneImage, optionPrompt, options } =
|
||||
useSelector(currentSceneSelector);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
@@ -22,21 +95,50 @@ const Cover = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Heading>{sceneTitle}</Heading>
|
||||
<Heading as="h4">{sceneDescription}</Heading>
|
||||
<Heading as="h4">{optionPrompt}</Heading>
|
||||
{options && options.length && (
|
||||
<ul>
|
||||
{options.map((option, oidx) => (
|
||||
<li key={oidx} onClick={() => onOptionClick(option)}>
|
||||
{option}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<BackgroundImageContainer backgroundImageUrl={sceneImage}>
|
||||
{step === 0 && (
|
||||
<QuoteContainer>
|
||||
<QuoteText>{sceneDescription}</QuoteText>
|
||||
<ButtonContainer>
|
||||
<Button
|
||||
onClick={() => {
|
||||
dispatch(toScene({ index: sceneNumber - 1 }));
|
||||
}}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
{options && options.length && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
setStep(1);
|
||||
}}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
)}
|
||||
</ButtonContainer>
|
||||
</QuoteContainer>
|
||||
)}
|
||||
</Container>
|
||||
{step === 1 && (
|
||||
<>
|
||||
{options && options.length && (
|
||||
<OptionButtonContainer>
|
||||
{options.map((opt) => (
|
||||
<OptionButton key={opt} onClick={() => onOptionClick(opt)}>
|
||||
{opt}
|
||||
</OptionButton>
|
||||
))}
|
||||
</OptionButtonContainer>
|
||||
)}
|
||||
{optionPrompt && (
|
||||
<QuoteContainer>
|
||||
<QuoteText>{optionPrompt}</QuoteText>
|
||||
</QuoteContainer>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</BackgroundImageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Cover;
|
||||
export default Scene;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import * as ReactDOM from "react-dom/client";
|
||||
import "@mysten/dapp-kit/dist/index.css";
|
||||
import "@radix-ui/themes/styles.css";
|
||||
import "./index.css";
|
||||
// import "./index.css";
|
||||
import { getFullnodeUrl } from "@mysten/sui.js/client";
|
||||
import {
|
||||
createNetworkConfig,
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -37,19 +37,22 @@ function* initStory(action: InitStoryAction) {
|
||||
);
|
||||
const progress = data.choices?.[0].message as StoryProgress;
|
||||
const { introduction, scene } = parseStoryGuideline(progress.content);
|
||||
const coverImage = (yield call(
|
||||
const { image_url } = (yield call(
|
||||
callFunction,
|
||||
"generateImage",
|
||||
introduction,
|
||||
)) as string;
|
||||
console.log(coverImage);
|
||||
const scene1Image = (yield call(
|
||||
)) as { image_url: string; revised_prompt: string };
|
||||
const { image_url: scene1Image } = (yield call(
|
||||
callFunction,
|
||||
"generateImage",
|
||||
scene.sceneDescription,
|
||||
)) as string;
|
||||
)) as { image_url: string; revised_prompt: string };
|
||||
yield put(
|
||||
initStorySuccess({ progress, coverImage, sceneImage: scene1Image }),
|
||||
initStorySuccess({
|
||||
progress,
|
||||
coverImage: image_url,
|
||||
sceneImage: scene1Image,
|
||||
}),
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
@@ -82,12 +85,12 @@ function* updateStory(action: InitStoryAction) {
|
||||
);
|
||||
const progress = data.choices?.[0].message as StoryProgress;
|
||||
const { sceneDescription } = parseScene(progress.content);
|
||||
const sceneImage = (yield call(
|
||||
const { image_url } = (yield call(
|
||||
callFunction,
|
||||
"generateImage",
|
||||
sceneDescription,
|
||||
)) as string;
|
||||
yield put(updateStorySuccess({ progress, sceneImage }));
|
||||
)) as { image_url: string; revised_prompt: string };
|
||||
yield put(updateStorySuccess({ progress, sceneImage: image_url }));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
|
||||
@@ -44,7 +44,8 @@ export const parseStoryGuideline = (
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let cont: any = {};
|
||||
try {
|
||||
cont = JSON.parse(content) as RawScene;
|
||||
const markdownStripped = stripMarkdown(content);
|
||||
cont = JSON.parse(markdownStripped) as RawScene;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
3
model/model.d.ts
vendored
3
model/model.d.ts
vendored
@@ -29,7 +29,8 @@ export type Project = {
|
||||
createdAt: number;
|
||||
minted: boolean;
|
||||
title: string;
|
||||
image: string;
|
||||
introduction: string;
|
||||
coverImage: string;
|
||||
rawPrompts: RawPrompt[];
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user