Spaces:
Configuration error
Configuration error
Upload 15 files
Browse files- App.css +38 -0
- App.js +15 -0
- App.test.js +13 -0
- ExamplePrompts.css +64 -0
- ExamplePrompts.js +19 -0
- Landing.css +76 -0
- Landing.js +78 -0
- Layout.css +52 -0
- Layout.js +29 -0
- StoryGenerator.js +48 -0
- index.css +13 -0
- index.js +17 -0
- logo.svg +1 -0
- reportWebVitals.js +13 -0
- setupTests.js +5 -0
App.css
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.App {
|
2 |
+
text-align: center;
|
3 |
+
}
|
4 |
+
|
5 |
+
.App-logo {
|
6 |
+
height: 40vmin;
|
7 |
+
pointer-events: none;
|
8 |
+
}
|
9 |
+
|
10 |
+
@media (prefers-reduced-motion: no-preference) {
|
11 |
+
.App-logo {
|
12 |
+
animation: App-logo-spin infinite 20s linear;
|
13 |
+
}
|
14 |
+
}
|
15 |
+
|
16 |
+
.App-header {
|
17 |
+
background-color: #282c34;
|
18 |
+
min-height: 100vh;
|
19 |
+
display: flex;
|
20 |
+
flex-direction: column;
|
21 |
+
align-items: center;
|
22 |
+
justify-content: center;
|
23 |
+
font-size: calc(10px + 2vmin);
|
24 |
+
color: white;
|
25 |
+
}
|
26 |
+
|
27 |
+
.App-link {
|
28 |
+
color: #61dafb;
|
29 |
+
}
|
30 |
+
|
31 |
+
@keyframes App-logo-spin {
|
32 |
+
from {
|
33 |
+
transform: rotate(0deg);
|
34 |
+
}
|
35 |
+
to {
|
36 |
+
transform: rotate(360deg);
|
37 |
+
}
|
38 |
+
}
|
App.js
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { useState } from "react";
|
2 |
+
import Layout from "./Layout";
|
3 |
+
import Landing from "./Landing";
|
4 |
+
import './App.css';
|
5 |
+
|
6 |
+
export default function App() {
|
7 |
+
const [selectedPrompt, setSelectedPrompt] = useState("");
|
8 |
+
|
9 |
+
return (
|
10 |
+
<Layout onPromptSelect={setSelectedPrompt}>
|
11 |
+
<Landing initialPrompt={selectedPrompt} />
|
12 |
+
</Layout>
|
13 |
+
);
|
14 |
+
}
|
15 |
+
|
App.test.js
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { useState } from "react";
|
2 |
+
import Layout from "./Layout";
|
3 |
+
import Landing from "./Landing";
|
4 |
+
|
5 |
+
export default function App() {
|
6 |
+
const [selectedPrompt, setSelectedPrompt] = useState("");
|
7 |
+
|
8 |
+
return (
|
9 |
+
<Layout onPromptSelect={setSelectedPrompt}>
|
10 |
+
<Landing initialPrompt={selectedPrompt} />
|
11 |
+
</Layout>
|
12 |
+
);
|
13 |
+
}
|
ExamplePrompts.css
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.example-prompts-box {
|
2 |
+
background-color: #2a2c37;
|
3 |
+
border: 1.5px solid #565867;
|
4 |
+
border-radius: 8px;
|
5 |
+
padding: 15px 20px;
|
6 |
+
margin-bottom: 20px;
|
7 |
+
max-width: 700px;
|
8 |
+
color: #e5e5e5;
|
9 |
+
font-family: Arial, sans-serif;
|
10 |
+
}
|
11 |
+
|
12 |
+
.example-prompts-box h3 {
|
13 |
+
margin-top: 0;
|
14 |
+
margin-bottom: 12px;
|
15 |
+
color: #5671f5;
|
16 |
+
font-weight: 700;
|
17 |
+
font-size: 18px;
|
18 |
+
}
|
19 |
+
|
20 |
+
.example-prompts-box ul {
|
21 |
+
list-style: none;
|
22 |
+
padding-left: 0;
|
23 |
+
margin: 0;
|
24 |
+
}
|
25 |
+
|
26 |
+
.example-prompts-box li {
|
27 |
+
cursor: pointer;
|
28 |
+
padding: 8px 10px;
|
29 |
+
border-radius: 5px;
|
30 |
+
margin-bottom: 10px;
|
31 |
+
transition: background-color 0.2s ease;
|
32 |
+
font-size: 14px;
|
33 |
+
line-height: 1.4;
|
34 |
+
}
|
35 |
+
|
36 |
+
.example-prompts-box li:hover,
|
37 |
+
.example-prompts-box li:focus {
|
38 |
+
background-color: #5671f5;
|
39 |
+
color: white;
|
40 |
+
outline: none;
|
41 |
+
}
|
42 |
+
.example-prompts-box li {
|
43 |
+
cursor: pointer;
|
44 |
+
padding: 12px 14px;
|
45 |
+
border-radius: 8px;
|
46 |
+
margin-bottom: 12px;
|
47 |
+
background-color: #2f3240;
|
48 |
+
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.7);
|
49 |
+
transition: background-color 0.2s ease, box-shadow 0.3s ease;
|
50 |
+
font-size: 14px;
|
51 |
+
line-height: 1.4;
|
52 |
+
color: #e5e5e5;
|
53 |
+
}
|
54 |
+
|
55 |
+
.example-prompts-box li:hover,
|
56 |
+
.example-prompts-box li:focus {
|
57 |
+
background-color: #5671f5;
|
58 |
+
color: white;
|
59 |
+
box-shadow:
|
60 |
+
0 4px 12px rgba(86, 113, 245, 0.7),
|
61 |
+
inset 0 0 6px 2px rgba(255, 255, 255, 0.2);
|
62 |
+
outline: none;
|
63 |
+
}
|
64 |
+
|
ExamplePrompts.js
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React from "react";
|
2 |
+
import "./ExamplePrompts.css";
|
3 |
+
|
4 |
+
export default function ExamplePrompts({ prompts, onSelect }) {
|
5 |
+
return (
|
6 |
+
<div className="example-prompts-box">
|
7 |
+
<h3>Example Prompts</h3>
|
8 |
+
<ul>
|
9 |
+
{prompts.map((prompt, i) => (
|
10 |
+
<li key={i} onClick={() => onSelect(prompt)} tabIndex={0} onKeyDown={e => {
|
11 |
+
if(e.key === 'Enter' || e.key === ' ') onSelect(prompt);
|
12 |
+
}}>
|
13 |
+
{prompt}
|
14 |
+
</li>
|
15 |
+
))}
|
16 |
+
</ul>
|
17 |
+
</div>
|
18 |
+
);
|
19 |
+
}
|
Landing.css
ADDED
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.container {
|
2 |
+
width: 100vw;
|
3 |
+
height: 100vh;
|
4 |
+
padding: 40px 20px;
|
5 |
+
box-sizing: border-box;
|
6 |
+
font-family: Arial, sans-serif;
|
7 |
+
color: #e5e5e5;
|
8 |
+
background-color: #202123;
|
9 |
+
display: flex;
|
10 |
+
flex-direction: column;
|
11 |
+
align-items: center;
|
12 |
+
justify-content: flex-start;
|
13 |
+
overflow: auto;
|
14 |
+
}
|
15 |
+
|
16 |
+
.content {
|
17 |
+
max-width: 700px;
|
18 |
+
width: 100%;
|
19 |
+
}
|
20 |
+
|
21 |
+
.heading {
|
22 |
+
margin-bottom: 10px;
|
23 |
+
text-align: center;
|
24 |
+
}
|
25 |
+
|
26 |
+
.description {
|
27 |
+
margin-bottom: 20px;
|
28 |
+
line-height: 1.5;
|
29 |
+
color: #ccc;
|
30 |
+
text-align: center;
|
31 |
+
}
|
32 |
+
|
33 |
+
.textarea {
|
34 |
+
width: 100%;
|
35 |
+
padding: 12px;
|
36 |
+
border-radius: 6px;
|
37 |
+
border: 1.5px solid #565867;
|
38 |
+
background-color: #40414f;
|
39 |
+
color: #e5e5e5;
|
40 |
+
font-size: 16px;
|
41 |
+
margin-bottom: 20px;
|
42 |
+
resize: vertical;
|
43 |
+
font-family: inherit;
|
44 |
+
}
|
45 |
+
|
46 |
+
.button {
|
47 |
+
background-color: #5671f5;
|
48 |
+
border: none;
|
49 |
+
border-radius: 6px;
|
50 |
+
padding: 14px 0;
|
51 |
+
width: 100%;
|
52 |
+
color: white;
|
53 |
+
font-weight: 700;
|
54 |
+
font-size: 16px;
|
55 |
+
cursor: pointer;
|
56 |
+
margin-bottom: 20px;
|
57 |
+
transition: background-color 0.3s ease;
|
58 |
+
}
|
59 |
+
|
60 |
+
.button:hover {
|
61 |
+
background-color: #3753d4;
|
62 |
+
}
|
63 |
+
|
64 |
+
.output {
|
65 |
+
background-color: #343541;
|
66 |
+
border-radius: 6px;
|
67 |
+
padding: 18px;
|
68 |
+
height: 250px;
|
69 |
+
overflow-y: auto;
|
70 |
+
white-space: pre-wrap;
|
71 |
+
color: #e5e5e5;
|
72 |
+
border: 1.5px solid #565867;
|
73 |
+
box-shadow: inset 0 0 8px #1c1c1f;
|
74 |
+
font-family: 'Courier New', Courier, monospace;
|
75 |
+
font-size: 15px;
|
76 |
+
}
|
Landing.js
ADDED
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { useState, useEffect } from "react";
|
2 |
+
import ExamplePrompts from "./ExamplePrompts";
|
3 |
+
import "./Landing.css";
|
4 |
+
|
5 |
+
const examplePromptsList = [
|
6 |
+
"Write a romantic comedy set in New York.",
|
7 |
+
"Generate a thriller about a detective chasing a serial killer.",
|
8 |
+
"Create a sci-fi scene on a distant planet.",
|
9 |
+
"Write a dialogue between two estranged siblings.",
|
10 |
+
"Outline a fantasy adventure with dragons and magic.",
|
11 |
+
];
|
12 |
+
|
13 |
+
export default function Landing({ initialPrompt }) {
|
14 |
+
const [prompt, setPrompt] = useState(initialPrompt || "");
|
15 |
+
const [output, setOutput] = useState(
|
16 |
+
"Your screenplay output will appear here..."
|
17 |
+
);
|
18 |
+
const [loading, setLoading] = useState(false);
|
19 |
+
|
20 |
+
useEffect(() => {
|
21 |
+
if (initialPrompt) setPrompt(initialPrompt);
|
22 |
+
}, [initialPrompt]);
|
23 |
+
|
24 |
+
async function generate() {
|
25 |
+
setLoading(true);
|
26 |
+
setOutput("Generating screenplay...");
|
27 |
+
try {
|
28 |
+
const response = await fetch("http://localhost:8000/generate-story", {
|
29 |
+
method: "POST",
|
30 |
+
headers: { "Content-Type": "application/json" },
|
31 |
+
body: JSON.stringify({ user_input: prompt }),
|
32 |
+
});
|
33 |
+
if (!response.ok) {
|
34 |
+
throw new Error("Backend error: " + response.status);
|
35 |
+
}
|
36 |
+
const data = await response.json();
|
37 |
+
setOutput(
|
38 |
+
`Genre: ${data.genre || ''}\n` +
|
39 |
+
`Tone: ${data.tone || ''}\n\n` +
|
40 |
+
`Outline:\n${data.outline || ''}\n\n` +
|
41 |
+
`Scene:\n${data.scene || ''}\n\n` +
|
42 |
+
`Dialogue:\n${data.dialogue || ''}`
|
43 |
+
);
|
44 |
+
} catch (error) {
|
45 |
+
setOutput("Error generating screenplay: " + error.message);
|
46 |
+
} finally {
|
47 |
+
setLoading(false);
|
48 |
+
}
|
49 |
+
}
|
50 |
+
|
51 |
+
return (
|
52 |
+
<div className="container">
|
53 |
+
<div className="content">
|
54 |
+
<h1 className="heading">Screenplay Generator</h1>
|
55 |
+
<p className="description">
|
56 |
+
Simply provide a brief prompt, and this AI will craft a complete screenplay tailored to your idea — seamlessly weaving together the genre, plot outline, character development, and dialogue to bring your story to life.
|
57 |
+
</p>
|
58 |
+
|
59 |
+
{/* Example prompts box */}
|
60 |
+
<ExamplePrompts prompts={examplePromptsList} onSelect={setPrompt} />
|
61 |
+
|
62 |
+
<textarea
|
63 |
+
className="textarea"
|
64 |
+
value={prompt}
|
65 |
+
onChange={(e) => setPrompt(e.target.value)}
|
66 |
+
placeholder="Enter your prompt here..."
|
67 |
+
rows={6}
|
68 |
+
/>
|
69 |
+
|
70 |
+
<button className="button" onClick={generate} disabled={loading}>
|
71 |
+
{loading ? "Generating..." : "Generate Screenplay"}
|
72 |
+
</button>
|
73 |
+
|
74 |
+
<pre className="output" style={{ whiteSpace: 'pre-wrap' }}>{output}</pre>
|
75 |
+
</div>
|
76 |
+
</div>
|
77 |
+
);
|
78 |
+
}
|
Layout.css
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.layout-container {
|
2 |
+
display: flex;
|
3 |
+
height: 100vh;
|
4 |
+
background-color: #202123;
|
5 |
+
color: #e5e5e5;
|
6 |
+
font-family: Arial, sans-serif;
|
7 |
+
}
|
8 |
+
|
9 |
+
.sidebar {
|
10 |
+
width: 320px;
|
11 |
+
background-color: #2a2c37;
|
12 |
+
padding: 20px;
|
13 |
+
box-sizing: border-box;
|
14 |
+
border-right: 1px solid #565867;
|
15 |
+
overflow-y: auto;
|
16 |
+
}
|
17 |
+
|
18 |
+
.sidebar h2 {
|
19 |
+
margin-top: 0;
|
20 |
+
margin-bottom: 15px;
|
21 |
+
font-weight: 700;
|
22 |
+
font-size: 20px;
|
23 |
+
color: #5671f5;
|
24 |
+
}
|
25 |
+
|
26 |
+
.sidebar ul {
|
27 |
+
list-style: none;
|
28 |
+
padding: 0;
|
29 |
+
margin: 0;
|
30 |
+
}
|
31 |
+
|
32 |
+
.sidebar li {
|
33 |
+
cursor: pointer;
|
34 |
+
margin-bottom: 12px;
|
35 |
+
padding: 10px;
|
36 |
+
border-radius: 6px;
|
37 |
+
transition: background-color 0.2s ease;
|
38 |
+
font-size: 14px;
|
39 |
+
line-height: 1.4;
|
40 |
+
}
|
41 |
+
|
42 |
+
.sidebar li:hover {
|
43 |
+
background-color: #5671f5;
|
44 |
+
color: white;
|
45 |
+
}
|
46 |
+
|
47 |
+
.main-content {
|
48 |
+
flex-grow: 1;
|
49 |
+
padding: 40px 30px;
|
50 |
+
box-sizing: border-box;
|
51 |
+
overflow-y: auto;
|
52 |
+
}
|
Layout.js
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React from "react";
|
2 |
+
import "./Layout.css";
|
3 |
+
|
4 |
+
export default function Layout({ children, onPromptSelect }) {
|
5 |
+
const examplePrompts = [
|
6 |
+
"Provide a simple idea, and the AI will generate a full screenplay — from genre to characters and dialogue — tailored just for you.",
|
7 |
+
"Type a concept or theme, and watch as the AI builds a complete screenplay with plot, scenes, characters, and conversations.",
|
8 |
+
"Enter a short prompt, and this AI crafts an engaging screenplay by designing the story’s genre, plot, characters, and dialogue automatically.",
|
9 |
+
"Give your story idea, and the AI will write a polished screenplay including genre, storyline, character arcs, and authentic dialogue.",
|
10 |
+
"Share a brief prompt and let the AI transform it into a full screenplay, developing the plot, characters, genre, and conversations effortlessly.",
|
11 |
+
];
|
12 |
+
|
13 |
+
return (
|
14 |
+
<div className="layout-container">
|
15 |
+
<aside className="sidebar">
|
16 |
+
<h2>Example Prompts</h2>
|
17 |
+
<ul>
|
18 |
+
{examplePrompts.map((prompt, idx) => (
|
19 |
+
<li key={idx} onClick={() => onPromptSelect(prompt)}>
|
20 |
+
{prompt}
|
21 |
+
</li>
|
22 |
+
))}
|
23 |
+
</ul>
|
24 |
+
</aside>
|
25 |
+
|
26 |
+
<main className="main-content">{children}</main>
|
27 |
+
</div>
|
28 |
+
);
|
29 |
+
}
|
StoryGenerator.js
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { useState } from "react";
|
2 |
+
|
3 |
+
function StoryGenerator() {
|
4 |
+
const [input, setInput] = useState("");
|
5 |
+
const [result, setResult] = useState(null);
|
6 |
+
const [loading, setLoading] = useState(false);
|
7 |
+
|
8 |
+
const handleSubmit = async (e) => {
|
9 |
+
e.preventDefault();
|
10 |
+
setLoading(true);
|
11 |
+
const response = await fetch("http://localhost:8000/generate-story", {
|
12 |
+
method: "POST",
|
13 |
+
headers: { "Content-Type": "application/json" },
|
14 |
+
body: JSON.stringify({ user_input: input }),
|
15 |
+
});
|
16 |
+
const data = await response.json();
|
17 |
+
setResult(data);
|
18 |
+
setLoading(false);
|
19 |
+
};
|
20 |
+
|
21 |
+
return (
|
22 |
+
<div>
|
23 |
+
<form onSubmit={handleSubmit}>
|
24 |
+
<input
|
25 |
+
value={input}
|
26 |
+
onChange={(e) => setInput(e.target.value)}
|
27 |
+
placeholder="Enter your story idea"
|
28 |
+
/>
|
29 |
+
<button type="submit">Generate Story</button>
|
30 |
+
</form>
|
31 |
+
{loading && <p>Generating...</p>}
|
32 |
+
{result && (
|
33 |
+
<div>
|
34 |
+
<h3>Genre: {result.genre}</h3>
|
35 |
+
<h3>Tone: {result.tone}</h3>
|
36 |
+
<h4>Outline:</h4>
|
37 |
+
<pre>{result.outline}</pre>
|
38 |
+
<h4>Scene:</h4>
|
39 |
+
<pre>{result.scene}</pre>
|
40 |
+
<h4>Dialogue:</h4>
|
41 |
+
<pre>{result.dialogue}</pre>
|
42 |
+
</div>
|
43 |
+
)}
|
44 |
+
</div>
|
45 |
+
);
|
46 |
+
}
|
47 |
+
|
48 |
+
export default StoryGenerator;
|
index.css
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
body {
|
2 |
+
margin: 0;
|
3 |
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
4 |
+
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
5 |
+
sans-serif;
|
6 |
+
-webkit-font-smoothing: antialiased;
|
7 |
+
-moz-osx-font-smoothing: grayscale;
|
8 |
+
}
|
9 |
+
|
10 |
+
code {
|
11 |
+
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
12 |
+
monospace;
|
13 |
+
}
|
index.js
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React from 'react';
|
2 |
+
import ReactDOM from 'react-dom/client';
|
3 |
+
import './index.css';
|
4 |
+
import App from './App';
|
5 |
+
import reportWebVitals from './reportWebVitals';
|
6 |
+
|
7 |
+
const root = ReactDOM.createRoot(document.getElementById('root'));
|
8 |
+
root.render(
|
9 |
+
<React.StrictMode>
|
10 |
+
<App />
|
11 |
+
</React.StrictMode>
|
12 |
+
);
|
13 |
+
|
14 |
+
// If you want to start measuring performance in your app, pass a function
|
15 |
+
// to log results (for example: reportWebVitals(console.log))
|
16 |
+
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
17 |
+
reportWebVitals();
|
logo.svg
ADDED
|
reportWebVitals.js
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const reportWebVitals = onPerfEntry => {
|
2 |
+
if (onPerfEntry && onPerfEntry instanceof Function) {
|
3 |
+
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
4 |
+
getCLS(onPerfEntry);
|
5 |
+
getFID(onPerfEntry);
|
6 |
+
getFCP(onPerfEntry);
|
7 |
+
getLCP(onPerfEntry);
|
8 |
+
getTTFB(onPerfEntry);
|
9 |
+
});
|
10 |
+
}
|
11 |
+
};
|
12 |
+
|
13 |
+
export default reportWebVitals;
|
setupTests.js
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
2 |
+
// allows you to do things like:
|
3 |
+
// expect(element).toHaveTextContent(/react/i)
|
4 |
+
// learn more: https://github.com/testing-library/jest-dom
|
5 |
+
import '@testing-library/jest-dom';
|