├── .firebaserc
├── .gitignore
├── .prettierrc
├── README.md
├── firebase.json
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── index.html
├── logo.png
├── manifest.json
├── ogimage.jpeg
└── robots.txt
├── src
├── App.tsx
├── common
│ ├── CopyButton.tsx
│ ├── components.ts
│ ├── github.interface.ts
│ └── models.ts
├── index.css
├── index.tsx
├── react-app-env.d.ts
├── reportWebVitals.ts
├── setupTests.ts
├── steps
│ ├── ConfigPrompt.tsx
│ ├── SelectModel.tsx
│ └── SelectRepo.tsx
└── utils
│ └── helper.ts
├── tsconfig.json
└── yarn.lock
/.firebaserc:
--------------------------------------------------------------------------------
1 | {
2 | "projects": {
3 | "default": "codeprompt-86dad"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 | /.firebase
3 |
4 | # dependencies
5 | /node_modules
6 | /.pnp
7 | .pnp.js
8 |
9 | # testing
10 | /coverage
11 |
12 | # production
13 | /build
14 |
15 | # misc
16 | .DS_Store
17 | .env.local
18 | .env.development.local
19 | .env.test.local
20 | .env.production.local
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "es5",
3 | "useTabs": false,
4 | "semi": true,
5 | "singleQuote": false
6 | }
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CodePrompt
2 |
3 | > CodePrompt is the code repository loader interface that consumes your relevant code and turns it into a GPT prompt — allowing the GPT to write more complex code for you.
4 |
5 |
6 |

7 |
8 |
9 |
10 | CodePrompt is an experiment and an attempt to let GPT write more complex code while taking into consideration of the project's context. This includes tasks such as implementing a function that evolves edits on multiple files, creating new components while reusing existing functions, etc.
11 |
12 | The inspiration and ideas for this project were primarily drawn from [mpoon/gpt-repository-loader](https://github.com/mpoon/gpt-repository-loader). However, due to its limitations in loading all files into the prompt and encountering token context limit issues, using gpt-repository-loader is currently impractical.
13 |
14 | So in CodePrompt, we add functionalities such as file selection and code editing, which enable developers to selectively choose relevant files and specific portions of code to build a GPT prompt based on their requirements.
15 |
16 | [Try it yourself](https://codeprompt.xyz), [Report a Bug / Request a Feature](https://github.com/chunrapeepat/codeprompt/issues)
17 |
18 | ## 📖 How to use:
19 |
20 | 1. Choose a GPT model from the available options, such as GPT-3.5, GPT-4, or GPT-4-32K.
21 | 2. Select a public repository that you want to work with.
22 | 3. Choose the relevant files from the repository that you want to include into the prompt.
23 | 4. Remove any irrelevant code and add specific instructions as needed.
24 | 5. Copy the prompt to your chosen GPT model to generate the desired output.
25 |
26 | ## 🛠️ Installation
27 |
28 | ### Local Development Environment
29 |
30 | 1. Clone the repository:
31 |
32 | ```bash
33 | git clone git@github.com:chunrapeepat/codeprompt.git
34 | ```
35 |
36 | 2. Install the required packages:
37 |
38 | ```bash
39 | cd codeprompt && yarn
40 | ```
41 |
42 | 3. Build the application:
43 |
44 | ```bash
45 | yarn build
46 | ```
47 |
48 | 4. Start the development server:
49 |
50 | ```bash
51 | yarn start
52 | ```
53 |
54 | ## 👥 Contributing
55 |
56 | Contributions to CodePrompt are welcome and encouraged! To contribute, please follow these steps:
57 |
58 | 1. Fork the repository
59 | 2. Create a new branch
60 | 3. Make your changes
61 | 4. Push your changes to your fork
62 | 5. Submit a pull request
63 |
64 | Or, if you have any fun ideas, go to [the issues page](https://github.com/chunrapeepat/codeprompt/issues) and post them there 🔥
65 |
66 | ---
67 |
68 | Crafted with 💖 by [@chunrapeepat](https://twitter.com/chunrapeepat)
69 |
--------------------------------------------------------------------------------
/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "hosting": {
3 | "public": "build",
4 | "ignore": [
5 | "firebase.json",
6 | "**/.*",
7 | "**/node_modules/**"
8 | ],
9 | "rewrites": [
10 | {
11 | "source": "**",
12 | "destination": "/index.html"
13 | }
14 | ]
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "codeprompt",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@ant-design/icons": "^5.0.1",
7 | "@monaco-editor/react": "^4.4.6",
8 | "@testing-library/jest-dom": "^5.16.5",
9 | "@testing-library/react": "^13.4.0",
10 | "@testing-library/user-event": "^13.5.0",
11 | "@types/jest": "^27.5.2",
12 | "@types/node": "^16.18.18",
13 | "@types/react": "^18.0.28",
14 | "@types/react-dom": "^18.0.11",
15 | "@types/styled-components": "^5.1.26",
16 | "antd": "^5.3.2",
17 | "gpt3-tokenizer": "^1.1.5",
18 | "react": "^18.2.0",
19 | "react-dom": "^18.2.0",
20 | "react-scripts": "5.0.1",
21 | "styled-components": "^5.3.9",
22 | "typescript": "^4.9.5",
23 | "web-vitals": "^2.1.4"
24 | },
25 | "scripts": {
26 | "start": "react-scripts start",
27 | "build": "react-scripts build",
28 | "test": "react-scripts test",
29 | "eject": "react-scripts eject",
30 | "deploy": "firebase deploy"
31 | },
32 | "eslintConfig": {
33 | "extends": [
34 | "react-app",
35 | "react-app/jest"
36 | ]
37 | },
38 | "browserslist": {
39 | "production": [
40 | ">0.2%",
41 | "not dead",
42 | "not op_mini all"
43 | ],
44 | "development": [
45 | "last 1 chrome version",
46 | "last 1 firefox version",
47 | "last 1 safari version"
48 | ]
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chunrapeepat/codeprompt/f69e6e6baab4f442ff5d6ee5d873ce391b46dd72/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | CodePrompt - The code repository loader interface for generating GPT
13 | prompt
14 |
15 |
19 |
20 |
21 |
25 |
29 |
30 |
31 |
32 |
33 |
34 |
38 |
42 |
43 |
44 |
45 |
46 |
50 |
55 |
56 |
57 |
58 |
59 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chunrapeepat/codeprompt/f69e6e6baab4f442ff5d6ee5d873ce391b46dd72/public/logo.png
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Codeprompt",
3 | "name": "The code repository loader UI for generating the GPT prompt.",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#333333",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/ogimage.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chunrapeepat/codeprompt/f69e6e6baab4f442ff5d6ee5d873ce391b46dd72/public/ogimage.jpeg
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Steps } from "antd";
3 | import styled from "styled-components";
4 | import SelectModel from "./steps/SelectModel";
5 | import MODELS from "./common/models";
6 | import SelectRepo from "./steps/SelectRepo";
7 | import { GithubFileObject } from "./common/github.interface";
8 | import ConfigPrompt from "./steps/ConfigPrompt";
9 | import {
10 | GithubOutlined,
11 | HeartFilled,
12 | TwitterOutlined,
13 | } from "@ant-design/icons";
14 | import MonacoEditor from "@monaco-editor/react";
15 | import { StepHeading } from "./common/components";
16 | import CopyButton from "./common/CopyButton";
17 |
18 | const Container = styled.div`
19 | width: 800px;
20 | margin: auto auto;
21 | `;
22 | const Grid = styled.div`
23 | display: grid;
24 | grid-template-columns: 200px 1fr;
25 | grid-gap: 30px;
26 | `;
27 | const Sticky = styled.div`
28 | position: sticky;
29 | top: 30px;
30 | `;
31 | const HeaderContainer = styled.div`
32 | padding: 50px 0;
33 | padding-top: 70px;
34 |
35 | & h1 {
36 | font-family: "Source Code Pro";
37 | margin: 0;
38 | color: #333;
39 | }
40 | & p {
41 | font-style: italic;
42 | font-weight: 500;
43 | }
44 | & .grid {
45 | display: flex;
46 | justify-content: space-between;
47 | align-items: center;
48 | }
49 | & div > a {
50 | border: 1px solid #ccc;
51 | padding: 8px 16px;
52 | border-radius: 5rem;
53 | text-decoration: none;
54 | color: #333;
55 | font-weight: 500;
56 | }
57 | `;
58 | const Header = () => {
59 | return (
60 |
61 |
81 |
82 | A code repository loader interface for generating GPT prompt.{" "}
83 |
88 | See The Demo
89 |
90 |
91 |
92 | );
93 | };
94 | const FooterContainer = styled.div`
95 | padding: 50px 0;
96 | text-align: center;
97 | color: #555;
98 |
99 | & p {
100 | font-style: italic;
101 | font-weight: 500;
102 | }
103 | & a {
104 | color: #555;
105 | text-decoration: none;
106 | font-weight: 700;
107 | font-family: "Source Code Pro";
108 | }
109 | `;
110 | const Footer = () => {
111 | return (
112 |
113 |
114 | Made with by{" "}
115 |
120 | @chunrapeepat
121 |
122 |
123 |
124 | );
125 | };
126 |
127 | const stepItems = [
128 | {
129 | title: "Select Model",
130 | description: "Select the GPT model",
131 | },
132 | {
133 | title: "Select Repo",
134 | description: "Select the Github repo",
135 | },
136 | {
137 | title: "Config Prompt",
138 | description: "Config the prompt",
139 | },
140 | {
141 | title: "Finish",
142 | description: "Copy the prompt to GPT",
143 | },
144 | ];
145 |
146 | function App() {
147 | const [step, setStep] = React.useState(0);
148 | const [selectedModel, setSelectedModel] = React.useState(null);
149 | const [files, setFiles] = React.useState([]);
150 | const [prompt, setPrompt] = React.useState("");
151 |
152 | return (
153 |
154 |
155 |
156 |
157 |
158 |
159 | {
164 | return {
165 | title: (
166 |
171 | {item.title}
172 |
173 | ),
174 | description: (
175 |
176 | {item.description}
177 |
178 | ),
179 | };
180 | })}
181 | />
182 |
183 |
184 |
185 |
{
187 | setSelectedModel(MODELS[modelId]);
188 |
189 | if (step > 1) return;
190 | setStep(Math.min(step + 1, 1));
191 | }}
192 | />
193 |
194 | {step >= 1 && (
195 | {
197 | setFiles(files);
198 |
199 | if (step > 2) return;
200 | setStep(Math.min(step + 1, 2));
201 | }}
202 | />
203 | )}
204 |
205 | {step >= 2 && (
206 | {
210 | setPrompt(prompt);
211 |
212 | if (step > 3) return;
213 | setStep(Math.min(step + 1, 3));
214 | }}
215 | />
216 | )}
217 |
218 | {step >= 3 && (
219 |
255 | )}
256 |
257 |
258 |
259 |
260 | );
261 | }
262 |
263 | export default App;
264 |
--------------------------------------------------------------------------------
/src/common/CopyButton.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { Button, message } from "antd";
3 | import { CopyOutlined } from "@ant-design/icons";
4 |
5 | interface CopyButtonProps {
6 | text: string;
7 | }
8 | function CopyButton(props: CopyButtonProps) {
9 | const [copied, setCopied] = useState(false);
10 |
11 | function handleCopyClick() {
12 | const el = document.createElement("textarea");
13 | el.value = props.text;
14 | document.body.appendChild(el);
15 | el.select();
16 | document.execCommand("copy");
17 | document.body.removeChild(el);
18 | setCopied(true);
19 | message.success("Text copied to clipboard");
20 | }
21 |
22 | return (
23 | }
25 | style={{ marginTop: 15 }}
26 | onClick={handleCopyClick}
27 | >
28 | {copied ? "Copied!" : "Copy"}
29 |
30 | );
31 | }
32 |
33 | export default CopyButton;
34 |
--------------------------------------------------------------------------------
/src/common/components.ts:
--------------------------------------------------------------------------------
1 | import styled from "styled-components";
2 |
3 | export const StepHeading = styled.h2`
4 | margin: 0;
5 | color: #333;
6 | margin-bottom: 15px;
7 | font-family: "Source Code Pro";
8 | `;
9 |
--------------------------------------------------------------------------------
/src/common/github.interface.ts:
--------------------------------------------------------------------------------
1 | export interface GithubObject {
2 | name: string;
3 | path: string;
4 | sha: string;
5 | size: number;
6 | url: string;
7 | html_url: string;
8 | git_url: string;
9 | download_url: string | null;
10 | type: string;
11 | _links: {
12 | self: string;
13 | git: string;
14 | html: string;
15 | };
16 | }
17 |
18 | export interface GithubFileObject {
19 | content: string;
20 | name: string;
21 | path: string;
22 | sha: string;
23 | size: number;
24 | url: string;
25 | html_url: string;
26 | git_url: string;
27 | download_url: string | null;
28 | type: string;
29 | _links: {
30 | self: string;
31 | git: string;
32 | html: string;
33 | };
34 | }
35 |
--------------------------------------------------------------------------------
/src/common/models.ts:
--------------------------------------------------------------------------------
1 | const MODELS: { [key: string]: any } = {
2 | "gpt-3.5": {
3 | name: "GPT-3.5-TURBO",
4 | description:
5 | "Most capable GPT-3.5 model and optimized for chat at 1/10th the cost of text-davinci-003. Will be updated with our latest model iteration.",
6 | maxTokens: 4096,
7 | trainingData: "Up to Sep 2021",
8 | },
9 | "gpt-4": {
10 | name: "GPT-4",
11 | description:
12 | "More capable than any GPT-3.5 model, able to do more complex tasks, and optimized for chat. Will be updated with our latest model iteration.",
13 | maxTokens: 8192,
14 | trainingData: "Up to Sep 2021",
15 | },
16 | "gpt-4-32k": {
17 | name: "GPT-4-32K",
18 | description:
19 | "Same capabilities as the base gpt-4 mode but with 4x the context length. Will be updated with our latest model iteration.",
20 | maxTokens: 32768,
21 | trainingData: "Up to Sep 2021",
22 | },
23 | };
24 |
25 | export default MODELS;
26 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: "Open Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI",
4 | "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans",
5 | "Helvetica Neue", sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | background: white;
9 | color: #3a3a3a;
10 | }
11 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
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(
8 | document.getElementById("root") as HTMLElement
9 | );
10 | root.render(
11 |
12 |
13 |
14 | );
15 |
16 | // If you want to start measuring performance in your app, pass a function
17 | // to log results (for example: reportWebVitals(console.log))
18 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
19 | reportWebVitals();
20 |
--------------------------------------------------------------------------------
/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/src/reportWebVitals.ts:
--------------------------------------------------------------------------------
1 | import { ReportHandler } from "web-vitals";
2 |
3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => {
4 | if (onPerfEntry && onPerfEntry instanceof Function) {
5 | import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
6 | getCLS(onPerfEntry);
7 | getFID(onPerfEntry);
8 | getFCP(onPerfEntry);
9 | getLCP(onPerfEntry);
10 | getTTFB(onPerfEntry);
11 | });
12 | }
13 | };
14 |
15 | export default reportWebVitals;
16 |
--------------------------------------------------------------------------------
/src/setupTests.ts:
--------------------------------------------------------------------------------
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';
6 |
--------------------------------------------------------------------------------
/src/steps/ConfigPrompt.tsx:
--------------------------------------------------------------------------------
1 | import { Button, Card, Divider, Input, Progress } from "antd";
2 | import { useEffect, useMemo, useState } from "react";
3 | import { StepHeading } from "../common/components";
4 | import { GithubFileObject } from "../common/github.interface";
5 | import {
6 | countTokens,
7 | getMonacoLanguageFromFilename,
8 | numberWithCommas,
9 | } from "../utils/helper";
10 | import MonacoEditor from "@monaco-editor/react";
11 | import { DeleteOutlined } from "@ant-design/icons";
12 |
13 | const promptTemplate = `The following text is a Git repository with code. The structure of the text is sections that begin with ----, followed by a single line containing the file path and file name, followed by a variable amount of lines containing the file contents. The text representing the repository ends when the symbols --END-- are encountered. Any further text beyond --END-- is meant to be interpreted as instructions using the aforementioned code as context.
14 | $GIT_REPO_FILES$
15 | --END--
16 | $INSTRUCTION$`;
17 | const defaultTemplateTokenUsed = countTokens(
18 | promptTemplate.replace("$INSTRUCTION$", "").replace("$GIT_REPO_FILES$", "")
19 | );
20 |
21 | interface ConfigPromptProps {
22 | model: any;
23 | files: GithubFileObject[];
24 | onSubmit: (prompt: string) => void;
25 | }
26 | const ConfigPrompt = ({ model, files, onSubmit }: ConfigPromptProps) => {
27 | const [instruction, setInstruction] = useState(
28 | `Write me a ...\nOutput the filename and code block, which contain the content of files.`
29 | );
30 | const [editedFiles, setEditedFiles] =
31 | useState<
32 | (GithubFileObject & { isExpanded?: boolean; tokenUsed?: number })[]
33 | >(files);
34 | const [timeoutId, setTimeoutId] = useState(
35 | undefined
36 | );
37 |
38 | const instructionTokenUsed = useMemo(
39 | () => countTokens(instruction),
40 | [instruction]
41 | );
42 | useEffect(() => {
43 | const newEditedFiles = files.map((file: any) => {
44 | file.tokenUsed = countTokens(file.content);
45 | return file;
46 | });
47 | setEditedFiles(newEditedFiles);
48 | }, [files]);
49 |
50 | const totalTokenUsed =
51 | editedFiles.map((file) => file.tokenUsed || 0).reduce((a, b) => a + b) +
52 | instructionTokenUsed +
53 | defaultTemplateTokenUsed;
54 | const tokenUsedPercentage = 100 * (totalTokenUsed / model.maxTokens);
55 |
56 | const handleSubmit = () => {
57 | const prompt = promptTemplate
58 | .replace("$INSTRUCTION$", instruction)
59 | .replace(
60 | "$GIT_REPO_FILES$",
61 | editedFiles
62 | .map((file) => `----\n${file.path}\n${file.content}`)
63 | .join("\n")
64 | );
65 |
66 | onSubmit(prompt);
67 | };
68 |
69 | const handleEditorChange = (index: number, value: string | undefined) => {
70 | const newFiles = [...editedFiles];
71 | if (value !== undefined) {
72 | newFiles[index].content = value;
73 | }
74 | setEditedFiles(newFiles);
75 |
76 | clearTimeout(timeoutId);
77 | const newTimeoutId = setTimeout(() => {
78 | const newFiles = [...editedFiles];
79 | newFiles[index].tokenUsed = countTokens(value || "");
80 | setEditedFiles(newFiles);
81 | }, 500);
82 | setTimeoutId(newTimeoutId);
83 | };
84 |
85 | const handleDeleteFile = (index: number) => {
86 | const newFiles = [...editedFiles];
87 | newFiles.splice(index, 1);
88 | setEditedFiles(newFiles);
89 | };
90 |
91 | return (
92 |
93 | 3. Config Prompt
94 | {editedFiles.map((file, index) => (
95 | {
101 | const newFiles = [...editedFiles];
102 | newFiles[index].isExpanded = !newFiles[index].isExpanded;
103 | setEditedFiles(newFiles);
104 | }}
105 | >
106 | {file.path}{" "}
107 |
108 | (click to {file.isExpanded ? "collapse" : "expand"})
109 |
110 |
111 | }
112 | style={{ marginBottom: 20 }}
113 | size="small"
114 | extra={
115 |
122 |
123 | Token used: {numberWithCommas(file.tokenUsed || 0)}
124 |
125 |
handleDeleteFile(index)}
128 | />
129 |
130 | }
131 | >
132 | {file.isExpanded && (
133 | handleEditorChange(index, value)}
141 | />
142 | )}
143 | {!file.isExpanded && (
144 |
152 | ...
153 |
154 | )}
155 |
156 | ))}
157 |
158 |
163 | Token used: {numberWithCommas(instructionTokenUsed)}
164 |
165 | }
166 | >
167 | setInstruction(e.target.value)}
171 | />
172 |
173 |
174 |
182 |
191 | Token used: {numberWithCommas(totalTokenUsed)} /{" "}
192 | {numberWithCommas(model.maxTokens)} ({model.name})
193 |
194 |
200 |
207 |
208 | );
209 | };
210 |
211 | export default ConfigPrompt;
212 |
--------------------------------------------------------------------------------
/src/steps/SelectModel.tsx:
--------------------------------------------------------------------------------
1 | import { Radio, Button, Select, RadioChangeEvent } from "antd";
2 | import { useState } from "react";
3 | import styled from "styled-components";
4 | import { StepHeading } from "../common/components";
5 | import MODELS from "../common/models";
6 | import { numberWithCommas } from "../utils/helper";
7 |
8 | const ModelInfo = styled.div`
9 | background: #e5f3f3;
10 | padding: 10px 20px;
11 | border-radius: 10px;
12 | margin: 15px 0;
13 |
14 | & p > span {
15 | margin-right: 15px;
16 | }
17 | `;
18 |
19 | interface SelectModelProps {
20 | onSelectionChange: (value: string) => void;
21 | }
22 | const SelectModel = ({ onSelectionChange }: SelectModelProps) => {
23 | const [selectedItem, setSelectedItem] = useState("gpt-3.5");
24 |
25 | const handleChange = (e: RadioChangeEvent) => {
26 | const value = e.target.value;
27 | setSelectedItem(value);
28 | };
29 | const handleSubmit = () => {
30 | if (!selectedItem) return;
31 | onSelectionChange(selectedItem);
32 | };
33 |
34 | return (
35 |
36 |
1. Select Model
37 |
38 |
43 | GPT-3.5
44 | GPT-4
45 | GPT-4-32K
46 |
47 |
48 | {selectedItem && (
49 | <>
50 |
51 |
52 |
53 | Model: {MODELS[selectedItem].name}
54 |
55 |
56 | Max Tokens:{" "}
57 | {numberWithCommas(MODELS[selectedItem].maxTokens)}
58 |
59 |
60 | {MODELS[selectedItem].description}
61 |
62 | Training Data: {MODELS[selectedItem].trainingData}
63 |
64 |
65 |
66 |
67 | >
68 | )}
69 |
70 | );
71 | };
72 |
73 | export default SelectModel;
74 |
--------------------------------------------------------------------------------
/src/steps/SelectRepo.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 | import { Button, Checkbox, Divider, Input, Spin, Tree } from "antd";
3 | import { Key } from "antd/es/table/interface";
4 | import { DataNode } from "antd/es/tree";
5 | import { GithubFileObject, GithubObject } from "../common/github.interface";
6 | import { StepHeading } from "../common/components";
7 | import {
8 | CloseOutlined,
9 | LoadingOutlined,
10 | StarOutlined,
11 | } from "@ant-design/icons";
12 | import {
13 | base64Decode,
14 | formatGithubURL,
15 | formatSize,
16 | isFolder,
17 | isImage,
18 | } from "../utils/helper";
19 | import styled from "styled-components";
20 |
21 | const FavoriteRepos = styled.div`
22 | margin-bottom: 20px;
23 |
24 | & > div {
25 | cursor: pointer;
26 | background: #fefefe;
27 | border: 1px solid #ddd;
28 | padding: 7px;
29 | margin-top: -1px;
30 |
31 | display: flex;
32 | justify-content: space-between;
33 | align-items: center;
34 |
35 | &:hover {
36 | border: 1px solid #bbb;
37 | background: #fafafa;
38 | }
39 | }
40 | `;
41 |
42 | async function processContent(
43 | contents: GithubObject[],
44 | accessToken: string
45 | ): Promise {
46 | const result: DataNode[] = [];
47 |
48 | for (const item of contents) {
49 | if (item.type === "file") {
50 | result.push({
51 | title: item.name + ` (size: ${formatSize(item.size)})`,
52 | key: item.path,
53 | });
54 | } else if (item.type === "dir") {
55 | const response = await fetch(
56 | item._links.self,
57 | accessToken
58 | ? {
59 | headers: {
60 | Authorization: `token ${accessToken}`,
61 | },
62 | }
63 | : {}
64 | );
65 | const subContents = await response.json();
66 | const children = await processContent(subContents, accessToken);
67 | result.push({
68 | title: item.name,
69 | key: item.path,
70 | children,
71 | });
72 | }
73 | }
74 |
75 | return result;
76 | }
77 |
78 | interface SelectRepoProps {
79 | onSubmit: (files: GithubFileObject[]) => void;
80 | }
81 | const SelectRepo = ({ onSubmit }: SelectRepoProps) => {
82 | const [isLoading, setIsLoading] = useState(false);
83 | const [repoURL, setRepoURL] = useState("");
84 | const [accessToken, setAccessToken] = useState("");
85 | const [treeData, setTreeData] = useState([]);
86 | const [checkedKeys, setCheckedKeys] = useState();
87 | const [usePrivateRepo, setUsePrivateRepo] = useState(false);
88 | const [favoriteRepos, setFavoriteRepos] = useState<
89 | { url: string; usePrivateRepo: boolean }[]
90 | >([]);
91 | const [error, setError] = useState("");
92 |
93 | useEffect(() => {
94 | const storedFavoriteRepos = localStorage.getItem("favorite_repos");
95 | if (storedFavoriteRepos) {
96 | setFavoriteRepos(JSON.parse(storedFavoriteRepos));
97 | }
98 |
99 | const storedToken = localStorage.getItem("github_access_token");
100 | if (storedToken) {
101 | setAccessToken(storedToken);
102 | }
103 | }, []);
104 |
105 | const saveAccessToken = () => {
106 | localStorage.setItem("github_access_token", accessToken);
107 | };
108 |
109 | const loadRepo = async (repoURL: string, usePrivateRepo: boolean) => {
110 | setError("");
111 | setRepoURL(repoURL);
112 | setIsLoading(true);
113 |
114 | try {
115 | const url = formatGithubURL(repoURL);
116 | const response = await fetch(
117 | `https://api.github.com/repos/${url}/contents/`,
118 | usePrivateRepo
119 | ? {
120 | headers: {
121 | Authorization: `token ${accessToken}`,
122 | },
123 | }
124 | : {}
125 | );
126 |
127 | const contents = await response.json();
128 | if (response.status !== 200) {
129 | throw new Error(contents.message);
130 | }
131 | const treeData = await processContent(contents, accessToken);
132 |
133 | setTreeData(treeData);
134 | } catch (e: any) {
135 | setError(e.message);
136 | console.error(e);
137 | }
138 |
139 | setIsLoading(false);
140 | };
141 |
142 | const handleSubmit = async () => {
143 | setIsLoading(true);
144 | const url = formatGithubURL(repoURL);
145 | const filenames =
146 | checkedKeys
147 | ?.map((f) => f.toString())
148 | .filter((f) => !isImage(f) && !isFolder(f)) || [];
149 |
150 | const files = await Promise.all(
151 | filenames.map((filename) =>
152 | fetch(
153 | `https://api.github.com/repos/${url}/contents/${filename}`,
154 | accessToken
155 | ? {
156 | headers: {
157 | Authorization: `token ${accessToken}`,
158 | },
159 | }
160 | : {}
161 | ).then((res) => {
162 | const data = res.json().then((data) => {
163 | data.content = base64Decode(data.content);
164 | return data;
165 | });
166 | return data;
167 | })
168 | )
169 | );
170 |
171 | setIsLoading(false);
172 | onSubmit(files);
173 | };
174 |
175 | const addFavoriteRepo = () => {
176 | const favoriteRepos = JSON.parse(
177 | localStorage.getItem("favorite_repos") || "[]"
178 | );
179 |
180 | const newRepo = {
181 | url: repoURL,
182 | usePrivateRepo,
183 | };
184 | if (!favoriteRepos.find((repo: any) => repo.url === newRepo.url)) {
185 | const newFavRepos = [...favoriteRepos, newRepo];
186 | setFavoriteRepos(newFavRepos);
187 | localStorage.setItem("favorite_repos", JSON.stringify(newFavRepos));
188 | }
189 | };
190 |
191 | const removeFavoriteRepo = (index: number) => {
192 | const updatedFavoriteRepos = favoriteRepos.filter((_, i) => i !== index);
193 | setFavoriteRepos(updatedFavoriteRepos);
194 | localStorage.setItem(
195 | "favorite_repos",
196 | JSON.stringify(updatedFavoriteRepos)
197 | );
198 | };
199 |
200 | const isRepoFavorited = () => {
201 | return favoriteRepos.some(
202 | (repo) => formatGithubURL(repo.url) === formatGithubURL(repoURL)
203 | );
204 | };
205 |
206 | return (
207 |
208 |
Select Repo & Files
209 |
210 | {error && (
211 |
212 | {error}
213 |
214 | )}
215 |
216 |
217 | {favoriteRepos.map((repo: any, index: number) => (
218 |
219 |
{
221 | loadRepo(repo.url, repo.usePrivateRepo);
222 | }}
223 | >
224 | {formatGithubURL(repo.url)}
225 |
226 | {repo.usePrivateRepo ? "(private)" : "(public)"}
227 |
228 |
229 |
230 | removeFavoriteRepo(index)}
232 | style={{ marginLeft: 5, cursor: "pointer" }}
233 | />
234 |
235 |
236 | ))}
237 |
238 |
239 |
loadRepo(value, usePrivateRepo)}
244 | style={{ width: "100%" }}
245 | />
246 | setUsePrivateRepo(e.target.checked)}
250 | >
251 | Use private repo
252 |
253 |
254 | {usePrivateRepo && (
255 |
256 |
setAccessToken(e.target.value)}
260 | style={{ width: "100%" }}
261 | type="password"
262 | />
263 |
264 |
272 |
289 |
290 |
291 |
292 | )}
293 |
294 |
295 |
296 | {treeData.length > 0 && (
297 |
298 | setCheckedKeys(keys as Key[])}
301 | checkedKeys={checkedKeys}
302 | treeData={treeData}
303 | />
304 |
305 | {!isRepoFavorited() && (
306 |
313 | )}
314 |
315 | )}
316 | {isLoading && (
317 |
318 | }
321 | />
322 |
323 | )}
324 | {treeData.length > 0 && (
325 |
326 |
327 |
328 | )}
329 |
330 | );
331 | };
332 |
333 | export default SelectRepo;
334 |
--------------------------------------------------------------------------------
/src/utils/helper.ts:
--------------------------------------------------------------------------------
1 | import GPT3Tokenizer from "gpt3-tokenizer";
2 |
3 | const tokenizer = new GPT3Tokenizer({ type: "gpt3" });
4 | export function countTokens(text: string): number {
5 | console.log("test/");
6 | return tokenizer.encode(text).bpe.length;
7 | }
8 |
9 | export function numberWithCommas(x: number) {
10 | return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
11 | }
12 |
13 | export function formatSize(size: number): string {
14 | if (size < 1024) {
15 | return `${size} B`;
16 | } else if (size < 1024 * 1024) {
17 | return `${(size / 1024).toFixed(2)} KB`;
18 | } else if (size < 1024 * 1024 * 1024) {
19 | return `${(size / 1024 / 1024).toFixed(2)} MB`;
20 | } else {
21 | return `${(size / 1024 / 1024 / 1024).toFixed(2)} GB`;
22 | }
23 | }
24 |
25 | export function formatGithubURL(url: string): string {
26 | // Split the URL into parts
27 | const parts = url.split("/");
28 |
29 | // Extract the owner and project name
30 | const owner = parts[3];
31 | const project = parts[4];
32 |
33 | // Combine the owner and project name and return the result
34 | return `${owner}/${project}`;
35 | }
36 |
37 | export function isImage(filename: string): boolean {
38 | return /\.(ico|gif|jpe?g|tiff?|png|webp|bmp)$/i.test(filename);
39 | }
40 |
41 | export function isFolder(filename: string): boolean {
42 | return !/\.[^/.]+$/.test(filename);
43 | }
44 |
45 | export function base64Decode(str: string): string {
46 | return atob(str);
47 | }
48 |
49 | export function getMonacoLanguageFromFilename(filename: string): string {
50 | const fileExtension = filename.substr(filename.lastIndexOf("."));
51 |
52 | let monacoLanguage = "plaintext";
53 |
54 | switch (fileExtension) {
55 | case ".js":
56 | case ".jsx":
57 | monacoLanguage = "javascript";
58 | break;
59 | case ".ts":
60 | case ".tsx":
61 | monacoLanguage = "typescript";
62 | break;
63 | case ".html":
64 | monacoLanguage = "html";
65 | break;
66 | case ".css":
67 | monacoLanguage = "css";
68 | break;
69 | case ".json":
70 | monacoLanguage = "json";
71 | break;
72 | case ".xml":
73 | monacoLanguage = "xml";
74 | break;
75 | case ".sql":
76 | monacoLanguage = "sql";
77 | break;
78 | case ".md":
79 | monacoLanguage = "markdown";
80 | break;
81 | case ".py":
82 | monacoLanguage = "python";
83 | break;
84 | case ".java":
85 | monacoLanguage = "java";
86 | break;
87 | case ".c":
88 | monacoLanguage = "c";
89 | break;
90 | case ".cpp":
91 | monacoLanguage = "cpp";
92 | break;
93 | case ".cs":
94 | monacoLanguage = "csharp";
95 | break;
96 | case ".php":
97 | monacoLanguage = "php";
98 | break;
99 | case ".go":
100 | monacoLanguage = "go";
101 | break;
102 | case ".rb":
103 | monacoLanguage = "ruby";
104 | break;
105 | // Add more file extensions and languages as needed
106 | default:
107 | monacoLanguage = "plaintext";
108 | }
109 |
110 | return monacoLanguage;
111 | }
112 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "esModuleInterop": true,
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "forceConsistentCasingInFileNames": true,
11 | "noFallthroughCasesInSwitch": true,
12 | "module": "esnext",
13 | "moduleResolution": "node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx"
18 | },
19 | "include": ["src"]
20 | }
21 |
--------------------------------------------------------------------------------