Compare commits

..
30 changed files with 20463 additions and 2664 deletions

No files matched your search

+22 -11
View File
@@ -1,12 +1,23 @@
.vscode
crewai
.cache
__pycache__
chroma.sqlite3
test/
dist/
.env
assets/*
.idea
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.pytest_cache
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
+65 -10
View File
@@ -1,15 +1,70 @@
# Praison AI
# Getting Started with Create React App
Praison AI, leveraging both AutoGen and CrewAI or any other agent framework, represents a low-code, centralised framework designed to simplify the creation and orchestration of multi-agent systems for various LLM applications, emphasizing ease of use, customization, and human-agent interaction.
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Installation
## Available Scripts
```bash
pip install praisonai
```
In the project directory, you can run:
## Run
### `npm start`
```bash
praisonai
```
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
The page will reload when you make changes.\
You may also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
### Code Splitting
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
### Analyzing the Bundle Size
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
### Making a Progressive Web App
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
### Advanced Configuration
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
### Deployment
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
### `npm run build` fails to minify
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
-41
View File
@@ -1,41 +0,0 @@
framework: "autogen"
topic: "Artificial Intelligence"
roles:
narrative_designer:
role: "Narrative Designer"
goal: "Craft engaging storylines and narratives for {topic}"
backstory: >
You're a creative visionary skilled in weaving complex narratives and
developing engaging story worlds within {topic}. Your expertise lies in
conceptualizing the overarching story and ensuring it resonates with the audience.
tasks:
story_concept_development:
description: >
Develop a compelling story concept for {topic}, focusing on originality,
thematic depth, and audience engagement. Outline the main narrative arcs,
characters, and settings.
expected_output: >
A comprehensive document outlining the story concept, including key narrative
arcs, character bios, and settings descriptions.
scriptwriter:
role: "Scriptwriter"
goal: "Transform story concepts into detailed scripts for {topic}"
backstory: >
With a flair for dialogue and a deep understanding of pacing and structure,
you excel at bringing narratives to life through scriptwriting. Your work
turns story concepts into blueprints for compelling {topic} experiences.
tasks:
scriptwriting_task:
description: >
Based on the narrative concept, write a detailed script for {topic}.
Include dialogue, stage directions, and scene descriptions that bring
the story to life.
expected_output: >
A detailed script ready for production, including dialogue, stage
directions, and scene descriptions.
dependencies:
- task: scriptwriting_task
depends_on: story_concept_development
-41
View File
@@ -1,41 +0,0 @@
framework: "crewai"
topic: "Artificial Intelligence"
roles:
researcher:
role: "Senior Data Researcher"
goal: "Uncover cutting-edge developments in {topic}"
backstory: >
You're a seasoned researcher with a knack for uncovering the latest
developments in {topic}. Known for your ability to find the most relevant
information and present it in a clear and concise manner.
tools:
- search_tool:
engine: google
tasks:
research_task:
description: >
Conduct thorough research about {topic}, focusing on the latest
trends and technologies.
expected_output: >
A list of 10 bullet points summarizing key findings with links to
relevant sources.
reporting_analyst:
role: "Reporting Analyst"
goal: "Create detailed reports based on {topic} data analysis and research findings."
backstory: >
You're a meticulous analyst with a keen eye for detail. You're known for
your ability to turn complex data into clear and concise reports, making
it easy for others to understand and act on the information you provide.
tasks:
reporting_task:
description: >
Review the research findings and create a comprehensive report on
{topic}. Include insights, potential opportunities, and recommendations.
expected_output: >
A well-structured report in Markdown format, ready for distribution.
dependencies:
- task: reporting_task
depends_on: research_task
-41
View File
@@ -1,41 +0,0 @@
framework: "autogen"
topic: "Artificial Intelligence"
roles:
narrative_designer:
role: "Narrative Designer"
goal: "Craft engaging storylines and narratives for {topic}"
backstory: >
You're a creative visionary skilled in weaving complex narratives and
developing engaging story worlds within {topic}. Your expertise lies in
conceptualizing the overarching story and ensuring it resonates with the audience.
tasks:
story_concept_development:
description: >
Develop a compelling story concept for {topic}, focusing on originality,
thematic depth, and audience engagement. Outline the main narrative arcs,
characters, and settings.
expected_output: >
A comprehensive document outlining the story concept, including key narrative
arcs, character bios, and settings descriptions.
scriptwriter:
role: "Scriptwriter"
goal: "Transform story concepts into detailed scripts for {topic}"
backstory: >
With a flair for dialogue and a deep understanding of pacing and structure,
you excel at bringing narratives to life through scriptwriting. Your work
turns story concepts into blueprints for compelling {topic} experiences.
tasks:
scriptwriting_task:
description: >
Based on the narrative concept, write a detailed script for {topic}.
Include dialogue, stage directions, and scene descriptions that bring
the story to life.
expected_output: >
A detailed script ready for production, including dialogue, stage
directions, and scene descriptions.
dependencies:
- task: scriptwriting_task
depends_on: story_concept_development
+20043
View File
File diff suppressed because it is too large. Load diff
+42
View File
@@ -0,0 +1,42 @@
{
"name": "admin-ui",
"version": "0.1.0",
"private": true,
"dependencies": {
"@chakra-ui/react": "^2.8.2",
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"framer-motion": "^11.2.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
Generated
-2281
View File
File diff suppressed because it is too large. Load diff
-1
View File
@@ -1 +0,0 @@
from .version import __version__
-4
View File
@@ -1,4 +0,0 @@
from .cli import main
if __name__ == "__main__":
main()
-114
View File
@@ -1,114 +0,0 @@
import sys
from .version import __version__
import yaml, os
from rich import print
from dotenv import load_dotenv
from crewai import Agent, Task, Crew
load_dotenv()
import autogen
config_list = [
{
'model': os.environ.get("OPENAI_MODEL_NAME", "gpt-3.5-turbo"),
'base_url': os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1"),
}
]
def generate_crew_and_kickoff(agent_file, framework=None):
with open(agent_file, 'r') as f:
config = yaml.safe_load(f)
topic = config['topic']
framework = framework or config.get('framework')
agents = {}
tasks = []
if framework == "autogen":
# Load the LLM configuration dynamically
print(config_list)
llm_config = {"config_list": config_list}
for role, details in config['roles'].items():
agent_name = details['role'].format(topic=topic).replace("{topic}", topic)
agent_goal = details['goal'].format(topic=topic)
# Creating an AssistantAgent for each role dynamically
agents[role] = autogen.AssistantAgent(
name=agent_name,
llm_config=llm_config,
system_message=details['backstory'].format(topic=topic)+". Reply \"TERMINATE\" in the end when everything is done.",
)
# Preparing tasks for initiate_chats
for task_name, task_details in details.get('tasks', {}).items():
description_filled = task_details['description'].format(topic=topic)
expected_output_filled = task_details['expected_output'].format(topic=topic)
chat_task = {
"recipient": agents[role],
"message": description_filled,
"summary_method": "last_msg", # Customize as needed
# Additional fields like carryover can be added based on dependencies
}
tasks.append(chat_task)
# Assuming the user proxy agent is set up as per your requirements
user = autogen.UserProxyAgent(
name="User",
human_input_mode="NEVER",
is_termination_msg=lambda x: (x.get("content") or "").rstrip().endswith("TERMINATE"),
code_execution_config={
"work_dir": "coding",
"use_docker": False,
},
# additional setup for the user proxy agent
)
response = user.initiate_chats(tasks)
result = "### Output ###\n"+response[-1].summary if hasattr(response[-1], 'summary') else ""
else:
for role, details in config['roles'].items():
role_filled = details['role'].format(topic=topic)
goal_filled = details['goal'].format(topic=topic)
backstory_filled = details['backstory'].format(topic=topic)
# Assume tools are loaded and handled here as per your requirements
agent = Agent(role=role_filled, goal=goal_filled, backstory=backstory_filled)
agents[role] = agent
for task_name, task_details in details.get('tasks', {}).items():
description_filled = task_details['description'].format(topic=topic)
expected_output_filled = task_details['expected_output'].format(topic=topic)
task = Task(description=description_filled, expected_output=expected_output_filled, agent=agent)
tasks.append(task)
crew = Crew(
agents=list(agents.values()),
tasks=tasks,
verbose=2
)
result = crew.kickoff()
return result
def main(args=None):
if args is None:
args = sys.argv[1:] # Skip the script name
invocation_cmd = "praisonai"
version_string = f"PraisonAI version {__version__}"
framework = "crewai" # Default framework
if "--framework" in args:
framework_index = args.index("--framework")
framework = args[framework_index + 1]
args = args[:framework_index] + args[framework_index + 2:]
if args:
agent_file = args[-1] # Assuming the last argument is the agent file
else:
agent_file = "agents.yaml"
result = generate_crew_and_kickoff(agent_file, framework)
print(result)
if __name__ == "__main__":
main()
-93
View File
@@ -1,93 +0,0 @@
import yaml, os
from rich import print
from dotenv import load_dotenv
from crewai import Agent, Task, Crew
load_dotenv()
import autogen
config_list = [
{
'model': os.environ.get("OPENAI_MODEL_NAME", "gpt-3.5-turbo"),
'base_url': os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1"),
}
]
def generate_crew_and_kickoff(agent_file):
with open(agent_file, 'r') as f:
config = yaml.safe_load(f)
topic = config['topic']
framework = config['framework']
agents = {}
tasks = []
if framework == "autogen":
# Load the LLM configuration dynamically
print(config_list)
llm_config = {"config_list": config_list}
for role, details in config['roles'].items():
agent_name = details['role'].format(topic=topic).replace("{topic}", topic)
agent_goal = details['goal'].format(topic=topic)
# Creating an AssistantAgent for each role dynamically
agents[role] = autogen.AssistantAgent(
name=agent_name,
llm_config=llm_config,
system_message=details['backstory'].format(topic=topic)+". Reply \"TERMINATE\" in the end when everything is done.",
)
# Preparing tasks for initiate_chats
for task_name, task_details in details.get('tasks', {}).items():
description_filled = task_details['description'].format(topic=topic)
expected_output_filled = task_details['expected_output'].format(topic=topic)
chat_task = {
"recipient": agents[role],
"message": description_filled,
"summary_method": "last_msg", # Customize as needed
# Additional fields like carryover can be added based on dependencies
}
tasks.append(chat_task)
# Assuming the user proxy agent is set up as per your requirements
user = autogen.UserProxyAgent(
name="User",
human_input_mode="NEVER",
is_termination_msg=lambda x: (x.get("content") or "").rstrip().endswith("TERMINATE"),
code_execution_config={
"work_dir": "coding",
"use_docker": False,
},
# additional setup for the user proxy agent
)
response = user.initiate_chats(tasks)
result = "### Output ###\n"+response[-1].summary if hasattr(response[-1], 'summary') else ""
else:
for role, details in config['roles'].items():
role_filled = details['role'].format(topic=topic)
goal_filled = details['goal'].format(topic=topic)
backstory_filled = details['backstory'].format(topic=topic)
# Assume tools are loaded and handled here as per your requirements
agent = Agent(role=role_filled, goal=goal_filled, backstory=backstory_filled)
agents[role] = agent
for task_name, task_details in details.get('tasks', {}).items():
description_filled = task_details['description'].format(topic=topic)
expected_output_filled = task_details['expected_output'].format(topic=topic)
task = Task(description=description_filled, expected_output=expected_output_filled, agent=agent)
tasks.append(task)
crew = Crew(
agents=list(agents.values()),
tasks=tasks,
verbose=2
)
result = crew.kickoff()
return result
if __name__ == "__main__":
agent_file = "agents.yaml"
result = generate_crew_and_kickoff(agent_file)
print(result)
-1
View File
@@ -1 +0,0 @@
__version__ = "0.0.1"
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

+43
View File
@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

+25
View File
@@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
+3
View File
@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
-23
View File
@@ -1,23 +0,0 @@
[tool.poetry]
name = "PraisonAI"
version = "0.0.1"
description = "PraisonAI application combines AutoGen and CrewAI or similar frameworks into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customization, and efficient human-agent collaboration."
authors = ["Mervin Praison"]
license = ""
readme = "README.md"
[tool.poetry.urls]
Homepage = "https://github.com/mervinpraison/PraisonAI"
Repository = "https://github.com/mervinpraison/PraisonAI"
[tool.poetry.dependencies]
python = ">=3.10,<3.13"
rich = ">=13.7"
pyautogen = ">=0.2.19"
crewai = ">=0.22.5"
[tool.poetry.dev-dependencies]
pytest = "^5.2"
[tool.poetry.extras]
tools = ["crewai-tools"]
-3
View File
@@ -1,3 +0,0 @@
rich>=13.7
pyautogen>=0.2.19
crewai>=0.22.5
+38
View File
@@ -0,0 +1,38 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
+13
View File
@@ -0,0 +1,13 @@
import React from 'react';
import './App.css';
import SettingsForm from './SettingsForm'; // Import the SettingsForm component
function App() {
return (
<div className="App">
<SettingsForm /> {/* Render the SettingsForm component */}
</div>
);
}
export default App;
+8
View File
@@ -0,0 +1,8 @@
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});
+112
View File
@@ -0,0 +1,112 @@
import React, { useState } from 'react';
import {
Container,
FormControl,
FormLabel,
Input,
Button,
VStack,
} from '@chakra-ui/react';
// Mocking wpApiSettings for development purposes. This should be replaced with the actual object provided by WordPress in production.
const wpApiSettings = { nonce: 'dev-nonce' }; // This line is added to mock the wpApiSettings object
const SettingsForm = () => {
const [patreonClientId, setPatreonClientId] = useState('');
const [patreonClientSecret, setPatreonClientSecret] = useState('');
const [youtubeApiKey, setYoutubeApiKey] = useState('');
const [file, setFile] = useState(null);
const handleSubmit = (event) => {
event.preventDefault();
// Handle the submission logic here
// This will involve saving the API credentials to the WordPress database
// For now, we'll log the credentials to the console
console.log({
patreonClientId,
patreonClientSecret,
youtubeApiKey,
});
// TODO: Implement API call to save these settings in WordPress
};
const handleFileChange = (event) => {
setFile(event.target.files[0]);
};
const handleFileUpload = async (event) => {
event.preventDefault();
if (file) {
const formData = new FormData();
formData.append('file', file);
try {
const response = await fetch('/wp-json/patreon-youtube/v1/upload_media', {
method: 'POST',
headers: {
'X-WP-Nonce': wpApiSettings.nonce,
},
body: formData,
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
console.log('File uploaded successfully:', result);
// TODO: Handle the successful upload (e.g., display a success message, clear the form, etc.)
} catch (error) {
console.error('File upload error:', error);
// TODO: Handle the upload error (e.g., display an error message to the user)
}
} else {
console.error('No file selected');
// TODO: Inform the user that no file was selected
}
};
return (
<Container>
<form onSubmit={handleSubmit}>
<VStack spacing={4}>
<FormControl id="patreon-client-id">
<FormLabel>Patreon Client ID</FormLabel>
<Input
type="text"
value={patreonClientId}
onChange={(e) => setPatreonClientId(e.target.value)}
/>
</FormControl>
<FormControl id="patreon-client-secret">
<FormLabel>Patreon Client Secret</FormLabel>
<Input
type="text"
value={patreonClientSecret}
onChange={(e) => setPatreonClientSecret(e.target.value)}
/>
</FormControl>
<FormControl id="youtube-api-key">
<FormLabel>YouTube API Key</FormLabel>
<Input
type="text"
value={youtubeApiKey}
onChange={(e) => setYoutubeApiKey(e.target.value)}
/>
</FormControl>
<FormControl id="file-upload">
<FormLabel>Upload Media</FormLabel>
<Input
type="file"
onChange={handleFileChange}
/>
<Button onClick={handleFileUpload} colorScheme="blue">Upload</Button>
</FormControl>
<Button type="submit" colorScheme="blue">Save Settings</Button>
</VStack>
</form>
</Container>
);
};
export default SettingsForm;
+13
View File
@@ -0,0 +1,13 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
+17
View File
@@ -0,0 +1,17 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

+13
View File
@@ -0,0 +1,13 @@
const reportWebVitals = onPerfEntry => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;
+5
View File
@@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';