khulnasoft commited on
Commit
8e20687
·
verified ·
1 Parent(s): e24297d

Upload 24 files

Browse files
Dockerfile ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use the official Node.js image as the base image
2
+ FROM node:14
3
+
4
+ # Set the working directory inside the container
5
+ WORKDIR /app
6
+
7
+ # Copy package.json and package-lock.json to the working directory
8
+ COPY package*.json ./
9
+
10
+ # Install the dependencies
11
+ RUN npm ci
12
+
13
+ # Copy the rest of the application code to the working directory
14
+ COPY . .
15
+
16
+ # Expose the port the app will run on
17
+ EXPOSE 3001
18
+
19
+ # Start the application
20
+ CMD ["npm", "run", "dev"]
README.md CHANGED
@@ -1,11 +1,29 @@
1
- ---
2
- title: Code Translator
3
- emoji: 📚
4
- colorFrom: indigo
5
- colorTo: indigo
6
- sdk: docker
7
- pinned: false
8
- license: mit
9
- ---
10
-
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # AI Code Translator
2
+
3
+ Use AI to translate code from one language to another.
4
+
5
+ ![AI Code Translator](./public/screenshot.png)
6
+
7
+ ## Running Locally
8
+
9
+ **1. Clone Repo**
10
+
11
+ ```bash
12
+ git clone https://github.com/mckaywrigley/ai-code-translator.git
13
+ ```
14
+
15
+ **2. Install Dependencies**
16
+
17
+ ```bash
18
+ npm i
19
+ ```
20
+
21
+ **3. Run App**
22
+
23
+ ```bash
24
+ npm run dev
25
+ ```
26
+
27
+ ## Contact
28
+
29
+ If you have any questions, feel free to reach out to me on [Twitter](https://twitter.com/mckaywrigley).
components/APIKeyInput.tsx ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ interface Props {
2
+ apiKey: string;
3
+ onChange: (apiKey: string) => void;
4
+ }
5
+
6
+ export const APIKeyInput: React.FC<Props> = ({ apiKey, onChange }) => {
7
+ return (
8
+ <input
9
+ className="mt-1 h-[24px] w-[280px] rounded-md border border-gray-300 px-3 py-2 text-black shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm"
10
+ type="password"
11
+ placeholder="OpenAI API Key"
12
+ value={apiKey}
13
+ onChange={(e) => onChange(e.target.value)}
14
+ />
15
+ );
16
+ };
components/CodeBlock.tsx ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { StreamLanguage } from '@codemirror/language';
2
+ import { go } from '@codemirror/legacy-modes/mode/go';
3
+ import { tokyoNight } from '@uiw/codemirror-theme-tokyo-night';
4
+ import CodeMirror from '@uiw/react-codemirror';
5
+ import { FC, useEffect, useState } from 'react';
6
+
7
+ interface Props {
8
+ code: string;
9
+ editable?: boolean;
10
+ onChange?: (value: string) => void;
11
+ }
12
+
13
+ export const CodeBlock: FC<Props> = ({
14
+ code,
15
+ editable = false,
16
+ onChange = () => {},
17
+ }) => {
18
+ const [copyText, setCopyText] = useState<string>('Copy');
19
+
20
+ useEffect(() => {
21
+ const timeout = setTimeout(() => {
22
+ setCopyText('Copy');
23
+ }, 2000);
24
+
25
+ return () => clearTimeout(timeout);
26
+ }, [copyText]);
27
+
28
+ return (
29
+ <div className="relative">
30
+ <button
31
+ className="absolute right-0 top-0 z-10 rounded bg-[#1A1B26] p-1 text-xs text-white hover:bg-[#2D2E3A] active:bg-[#2D2E3A]"
32
+ onClick={() => {
33
+ navigator.clipboard.writeText(code);
34
+ setCopyText('Copied!');
35
+ }}
36
+ >
37
+ {copyText}
38
+ </button>
39
+
40
+ <CodeMirror
41
+ editable={editable}
42
+ value={code}
43
+ minHeight="500px"
44
+ extensions={[StreamLanguage.define(go)]}
45
+ theme={tokyoNight}
46
+ onChange={(value) => onChange(value)}
47
+ />
48
+ </div>
49
+ );
50
+ };
components/LanguageSelect.tsx ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { FC } from 'react';
2
+
3
+ interface Props {
4
+ language: string;
5
+ onChange: (language: string) => void;
6
+ }
7
+
8
+ export const LanguageSelect: FC<Props> = ({ language, onChange }) => {
9
+ const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
10
+ onChange(e.target.value);
11
+ };
12
+
13
+ return (
14
+ <select
15
+ className="w-full rounded-md bg-[#1F2937] px-4 py-2 text-neutral-200"
16
+ value={language}
17
+ onChange={handleChange}
18
+ >
19
+ {languages
20
+ .sort((a, b) => a.label.localeCompare(b.label))
21
+ .map((language) => (
22
+ <option key={language.value} value={language.value}>
23
+ {language.label}
24
+ </option>
25
+ ))}
26
+ </select>
27
+ );
28
+ };
29
+
30
+ const languages = [
31
+ { value: 'Pascal', label: 'Pascal' },
32
+ { value: 'JavaScript', label: 'JavaScript' },
33
+ { value: 'TypeScript', label: 'TypeScript' },
34
+ { value: 'Python', label: 'Python' },
35
+ { value: 'TSX', label: 'TSX' },
36
+ { value: 'JSX', label: 'JSX' },
37
+ { value: 'Vue', label: 'Vue' },
38
+ { value: 'Go', label: 'Go' },
39
+ { value: 'C', label: 'C' },
40
+ { value: 'C++', label: 'C++' },
41
+ { value: 'Java', label: 'Java' },
42
+ { value: 'C#', label: 'C#' },
43
+ { value: 'Visual Basic .NET', label: 'Visual Basic .NET' },
44
+ { value: 'SQL', label: 'SQL' },
45
+ { value: 'Assembly Language', label: 'Assembly Language' },
46
+ { value: 'PHP', label: 'PHP' },
47
+ { value: 'Ruby', label: 'Ruby' },
48
+ { value: 'Swift', label: 'Swift' },
49
+ { value: 'SwiftUI', label: 'SwiftUI' },
50
+ { value: 'Kotlin', label: 'Kotlin' },
51
+ { value: 'R', label: 'R' },
52
+ { value: 'Objective-C', label: 'Objective-C' },
53
+ { value: 'Perl', label: 'Perl' },
54
+ { value: 'SAS', label: 'SAS' },
55
+ { value: 'Scala', label: 'Scala' },
56
+ { value: 'Dart', label: 'Dart' },
57
+ { value: 'Rust', label: 'Rust' },
58
+ { value: 'Haskell', label: 'Haskell' },
59
+ { value: 'Lua', label: 'Lua' },
60
+ { value: 'Groovy', label: 'Groovy' },
61
+ { value: 'Elixir', label: 'Elixir' },
62
+ { value: 'Clojure', label: 'Clojure' },
63
+ { value: 'Lisp', label: 'Lisp' },
64
+ { value: 'Julia', label: 'Julia' },
65
+ { value: 'Matlab', label: 'Matlab' },
66
+ { value: 'Fortran', label: 'Fortran' },
67
+ { value: 'COBOL', label: 'COBOL' },
68
+ { value: 'Bash', label: 'Bash' },
69
+ { value: 'Powershell', label: 'Powershell' },
70
+ { value: 'PL/SQL', label: 'PL/SQL' },
71
+ { value: 'CSS', label: 'CSS' },
72
+ { value: 'Racket', label: 'Racket' },
73
+ { value: 'HTML', label: 'HTML' },
74
+ { value: 'NoSQL', label: 'NoSQL' },
75
+ { value: 'Natural Language', label: 'Natural Language' },
76
+ { value: 'CoffeeScript', label: 'CoffeeScript' },
77
+ ];
components/ModelSelect.tsx ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { OpenAIModel } from '@/types/types';
2
+ import { FC } from 'react';
3
+
4
+ interface Props {
5
+ model: OpenAIModel;
6
+ onChange: (model: OpenAIModel) => void;
7
+ }
8
+
9
+ export const ModelSelect: FC<Props> = ({ model, onChange }) => {
10
+ const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
11
+ onChange(e.target.value as OpenAIModel);
12
+ };
13
+
14
+ return (
15
+ <select
16
+ className="h-[40px] w-[140px] rounded-md bg-[#1F2937] px-4 py-2 text-neutral-200"
17
+ value={model}
18
+ onChange={handleChange}
19
+ >
20
+ <option value="gpt-3.5-turbo">GPT-3.5</option>
21
+ <option value="gpt-4">GPT-4</option>
22
+ </select>
23
+ );
24
+ };
components/TextBlock.tsx ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ interface Props {
2
+ text: string;
3
+ editable?: boolean;
4
+ onChange?: (value: string) => void;
5
+ }
6
+
7
+ export const TextBlock: React.FC<Props> = ({
8
+ text,
9
+ editable = false,
10
+ onChange = () => {},
11
+ }) => {
12
+ return (
13
+ <textarea
14
+ className="min-h-[500px] w-full bg-[#1A1B26] p-4 text-[15px] text-neutral-200 focus:outline-none"
15
+ style={{ resize: 'none' }}
16
+ value={text}
17
+ onChange={(e) => onChange(e.target.value)}
18
+ disabled={!editable}
19
+ />
20
+ );
21
+ };
docker-compose.yml ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ ai-code-translator:
5
+ build: .
6
+ container_name: ai_code_translator
7
+ ports:
8
+ - '3001:3000'
9
+ volumes:
10
+ - .:/app
11
+ - /app/node_modules
12
+ environment:
13
+ - NODE_ENV=development
14
+ command: npm run dev
15
+ restart: always
16
+ security_opt:
17
+ - no-new-privileges:true
next.config.js ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {
3
+ reactStrictMode: true,
4
+ }
5
+
6
+ module.exports = nextConfig
package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
package.json ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "code-translate",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "next dev",
7
+ "build": "next build",
8
+ "start": "next start",
9
+ "lint": "next lint"
10
+ },
11
+ "dependencies": {
12
+ "@codemirror/legacy-modes": "^6.3.2",
13
+ "@uiw/codemirror-theme-tokyo-night": "^4.19.11",
14
+ "@uiw/react-codemirror": "^4.19.11",
15
+ "endent": "^2.1.0",
16
+ "eventsource-parser": "^1.0.0",
17
+ "next": "13.2.4",
18
+ "react": "18.2.0",
19
+ "react-dom": "18.2.0"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "18.15.11",
23
+ "@types/react": "18.0.31",
24
+ "@types/react-dom": "18.0.11",
25
+ "autoprefixer": "^10.4.14",
26
+ "eslint": "8.37.0",
27
+ "eslint-config-next": "13.2.4",
28
+ "postcss": "^8.4.21",
29
+ "prettier-plugin-tailwindcss": "^0.2.6",
30
+ "tailwindcss": "^3.3.1",
31
+ "typescript": "5.0.3"
32
+ }
33
+ }
pages/_app.tsx ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import "@/styles/globals.css";
2
+ import type { AppProps } from "next/app";
3
+ import { Inter } from "next/font/google";
4
+
5
+ const inter = Inter({ subsets: ["latin"] });
6
+
7
+ function App({ Component, pageProps }: AppProps<{}>) {
8
+ return (
9
+ <main className={inter.className}>
10
+ <Component {...pageProps} />
11
+ </main>
12
+ );
13
+ }
14
+
15
+ export default App;
pages/_document.tsx ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Html, Head, Main, NextScript } from 'next/document'
2
+
3
+ export default function Document() {
4
+ return (
5
+ <Html lang="en">
6
+ <Head />
7
+ <body>
8
+ <Main />
9
+ <NextScript />
10
+ </body>
11
+ </Html>
12
+ )
13
+ }
pages/api/translate.ts ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { TranslateBody } from '@/types/types';
2
+ import { OpenAIStream } from '@/utils';
3
+
4
+ export const config = {
5
+ runtime: 'edge',
6
+ };
7
+
8
+ const handler = async (req: Request): Promise<Response> => {
9
+ try {
10
+ const { inputLanguage, outputLanguage, inputCode, model, apiKey } =
11
+ (await req.json()) as TranslateBody;
12
+
13
+ const stream = await OpenAIStream(
14
+ inputLanguage,
15
+ outputLanguage,
16
+ inputCode,
17
+ model,
18
+ apiKey,
19
+ );
20
+
21
+ return new Response(stream);
22
+ } catch (error) {
23
+ console.error(error);
24
+ return new Response('Error', { status: 500 });
25
+ }
26
+ };
27
+
28
+ export default handler;
pages/index.tsx ADDED
@@ -0,0 +1,225 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { APIKeyInput } from '@/components/APIKeyInput';
2
+ import { CodeBlock } from '@/components/CodeBlock';
3
+ import { LanguageSelect } from '@/components/LanguageSelect';
4
+ import { ModelSelect } from '@/components/ModelSelect';
5
+ import { TextBlock } from '@/components/TextBlock';
6
+ import { OpenAIModel, TranslateBody } from '@/types/types';
7
+ import Head from 'next/head';
8
+ import { useEffect, useState } from 'react';
9
+
10
+ export default function Home() {
11
+ const [inputLanguage, setInputLanguage] = useState<string>('JavaScript');
12
+ const [outputLanguage, setOutputLanguage] = useState<string>('Python');
13
+ const [inputCode, setInputCode] = useState<string>('');
14
+ const [outputCode, setOutputCode] = useState<string>('');
15
+ const [model, setModel] = useState<OpenAIModel>('gpt-3.5-turbo');
16
+ const [loading, setLoading] = useState<boolean>(false);
17
+ const [hasTranslated, setHasTranslated] = useState<boolean>(false);
18
+ const [apiKey, setApiKey] = useState<string>('');
19
+
20
+ const handleTranslate = async () => {
21
+ const maxCodeLength = model === 'gpt-3.5-turbo' ? 6000 : 12000;
22
+
23
+ if (!apiKey) {
24
+ alert('Please enter an API key.');
25
+ return;
26
+ }
27
+
28
+ if (inputLanguage === outputLanguage) {
29
+ alert('Please select different languages.');
30
+ return;
31
+ }
32
+
33
+ if (!inputCode) {
34
+ alert('Please enter some code.');
35
+ return;
36
+ }
37
+
38
+ if (inputCode.length > maxCodeLength) {
39
+ alert(
40
+ `Please enter code less than ${maxCodeLength} characters. You are currently at ${inputCode.length} characters.`,
41
+ );
42
+ return;
43
+ }
44
+
45
+ setLoading(true);
46
+ setOutputCode('');
47
+
48
+ const controller = new AbortController();
49
+
50
+ const body: TranslateBody = {
51
+ inputLanguage,
52
+ outputLanguage,
53
+ inputCode,
54
+ model,
55
+ apiKey,
56
+ };
57
+
58
+ const response = await fetch('/api/translate', {
59
+ method: 'POST',
60
+ headers: {
61
+ 'Content-Type': 'application/json',
62
+ },
63
+ signal: controller.signal,
64
+ body: JSON.stringify(body),
65
+ });
66
+
67
+ if (!response.ok) {
68
+ setLoading(false);
69
+ alert('Something went wrong.');
70
+ return;
71
+ }
72
+
73
+ const data = response.body;
74
+
75
+ if (!data) {
76
+ setLoading(false);
77
+ alert('Something went wrong.');
78
+ return;
79
+ }
80
+
81
+ const reader = data.getReader();
82
+ const decoder = new TextDecoder();
83
+ let done = false;
84
+ let code = '';
85
+
86
+ while (!done) {
87
+ const { value, done: doneReading } = await reader.read();
88
+ done = doneReading;
89
+ const chunkValue = decoder.decode(value);
90
+
91
+ code += chunkValue;
92
+
93
+ setOutputCode((prevCode) => prevCode + chunkValue);
94
+ }
95
+
96
+ setLoading(false);
97
+ setHasTranslated(true);
98
+ copyToClipboard(code);
99
+ };
100
+
101
+ const copyToClipboard = (text: string) => {
102
+ const el = document.createElement('textarea');
103
+ el.value = text;
104
+ document.body.appendChild(el);
105
+ el.select();
106
+ document.execCommand('copy');
107
+ document.body.removeChild(el);
108
+ };
109
+
110
+ const handleApiKeyChange = (value: string) => {
111
+ setApiKey(value);
112
+
113
+ localStorage.setItem('apiKey', value);
114
+ };
115
+
116
+ useEffect(() => {
117
+ if (hasTranslated) {
118
+ handleTranslate();
119
+ }
120
+ }, [outputLanguage]);
121
+
122
+ useEffect(() => {
123
+ const apiKey = localStorage.getItem('apiKey');
124
+
125
+ if (apiKey) {
126
+ setApiKey(apiKey);
127
+ }
128
+ }, []);
129
+
130
+ return (
131
+ <>
132
+ <Head>
133
+ <title>Code Translator</title>
134
+ <meta
135
+ name="description"
136
+ content="Use AI to translate code from one language to another."
137
+ />
138
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
139
+ <link rel="icon" href="/favicon.ico" />
140
+ </Head>
141
+ <div className="flex h-full min-h-screen flex-col items-center bg-[#0E1117] px-4 pb-20 text-neutral-200 sm:px-10">
142
+ <div className="mt-10 flex flex-col items-center justify-center sm:mt-20">
143
+ <div className="text-4xl font-bold">AI Code Translator</div>
144
+ </div>
145
+
146
+ <div className="mt-6 text-center text-sm">
147
+ <APIKeyInput apiKey={apiKey} onChange={handleApiKeyChange} />
148
+ </div>
149
+
150
+ <div className="mt-2 flex items-center space-x-2">
151
+ <ModelSelect model={model} onChange={(value) => setModel(value)} />
152
+
153
+ <button
154
+ className="w-[140px] cursor-pointer rounded-md bg-violet-500 px-4 py-2 font-bold hover:bg-violet-600 active:bg-violet-700"
155
+ onClick={() => handleTranslate()}
156
+ disabled={loading}
157
+ >
158
+ {loading ? 'Translating...' : 'Translate'}
159
+ </button>
160
+ </div>
161
+
162
+ <div className="mt-2 text-center text-xs">
163
+ {loading
164
+ ? 'Translating...'
165
+ : hasTranslated
166
+ ? 'Output copied to clipboard!'
167
+ : 'Enter some code and click "Translate"'}
168
+ </div>
169
+
170
+ <div className="mt-6 flex w-full max-w-[1200px] flex-col justify-between sm:flex-row sm:space-x-4">
171
+ <div className="h-100 flex flex-col justify-center space-y-2 sm:w-2/4">
172
+ <div className="text-center text-xl font-bold">Input</div>
173
+
174
+ <LanguageSelect
175
+ language={inputLanguage}
176
+ onChange={(value) => {
177
+ setInputLanguage(value);
178
+ setHasTranslated(false);
179
+ setInputCode('');
180
+ setOutputCode('');
181
+ }}
182
+ />
183
+
184
+ {inputLanguage === 'Natural Language' ? (
185
+ <TextBlock
186
+ text={inputCode}
187
+ editable={!loading}
188
+ onChange={(value) => {
189
+ setInputCode(value);
190
+ setHasTranslated(false);
191
+ }}
192
+ />
193
+ ) : (
194
+ <CodeBlock
195
+ code={inputCode}
196
+ editable={!loading}
197
+ onChange={(value) => {
198
+ setInputCode(value);
199
+ setHasTranslated(false);
200
+ }}
201
+ />
202
+ )}
203
+ </div>
204
+ <div className="mt-8 flex h-full flex-col justify-center space-y-2 sm:mt-0 sm:w-2/4">
205
+ <div className="text-center text-xl font-bold">Output</div>
206
+
207
+ <LanguageSelect
208
+ language={outputLanguage}
209
+ onChange={(value) => {
210
+ setOutputLanguage(value);
211
+ setOutputCode('');
212
+ }}
213
+ />
214
+
215
+ {outputLanguage === 'Natural Language' ? (
216
+ <TextBlock text={outputCode} />
217
+ ) : (
218
+ <CodeBlock code={outputCode} />
219
+ )}
220
+ </div>
221
+ </div>
222
+ </div>
223
+ </>
224
+ );
225
+ }
postcss.config.js ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ module.exports = {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ }
prettier.config.js ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ module.exports = {
2
+ trailingComma: "all",
3
+ singleQuote: true,
4
+ plugins: [require("prettier-plugin-tailwindcss")]
5
+ };
public/favicon.ico ADDED
public/screenshot.png ADDED
styles/globals.css ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
tailwind.config.js ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ /** @type {import('tailwindcss').Config} */
2
+ module.exports = {
3
+ content: ["./pages/**/*.{js,ts,jsx,tsx}", "./components/**/*.{js,ts,jsx,tsx}"],
4
+ theme: {
5
+ extend: {}
6
+ },
7
+ plugins: []
8
+ };
tsconfig.json ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "nodenext",
5
+ "lib": ["dom", "dom.iterable", "esnext"],
6
+ "allowJs": true,
7
+ "skipLibCheck": true,
8
+ "strict": true,
9
+ "forceConsistentCasingInFileNames": true,
10
+ "noEmit": true,
11
+ "esModuleInterop": true,
12
+ "moduleResolution": "node",
13
+ "resolveJsonModule": true,
14
+ "isolatedModules": true,
15
+ "jsx": "preserve",
16
+ "incremental": true,
17
+ "paths": {
18
+ "@/*": ["./*"]
19
+ }
20
+ },
21
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
22
+ "exclude": ["node_modules"]
23
+ }
types/types.ts ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export type OpenAIModel = 'gpt-3.5-turbo' | 'gpt-4';
2
+
3
+ export interface TranslateBody {
4
+ inputLanguage: string;
5
+ outputLanguage: string;
6
+ inputCode: string;
7
+ model: OpenAIModel;
8
+ apiKey: string;
9
+ }
10
+
11
+ export interface TranslateResponse {
12
+ code: string;
13
+ }
utils/index.ts ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import endent from 'endent';
2
+ import {
3
+ createParser,
4
+ ParsedEvent,
5
+ ReconnectInterval,
6
+ } from 'eventsource-parser';
7
+
8
+ const createPrompt = (
9
+ inputLanguage: string,
10
+ outputLanguage: string,
11
+ inputCode: string,
12
+ ) => {
13
+ if (inputLanguage === 'Natural Language') {
14
+ return endent`
15
+ You are an expert programmer in all programming languages. Translate the natural language to "${outputLanguage}" code. Do not include \`\`\`.
16
+
17
+ Example translating from natural language to JavaScript:
18
+
19
+ Natural language:
20
+ Print the numbers 0 to 9.
21
+
22
+ JavaScript code:
23
+ for (let i = 0; i < 10; i++) {
24
+ console.log(i);
25
+ }
26
+
27
+ Natural language:
28
+ ${inputCode}
29
+
30
+ ${outputLanguage} code (no \`\`\`):
31
+ `;
32
+ } else if (outputLanguage === 'Natural Language') {
33
+ return endent`
34
+ You are an expert programmer in all programming languages. Translate the "${inputLanguage}" code to natural language in plain English that the average adult could understand. Respond as bullet points starting with -.
35
+
36
+ Example translating from JavaScript to natural language:
37
+
38
+ JavaScript code:
39
+ for (let i = 0; i < 10; i++) {
40
+ console.log(i);
41
+ }
42
+
43
+ Natural language:
44
+ Print the numbers 0 to 9.
45
+
46
+ ${inputLanguage} code:
47
+ ${inputCode}
48
+
49
+ Natural language:
50
+ `;
51
+ } else {
52
+ return endent`
53
+ You are an expert programmer in all programming languages. Translate the "${inputLanguage}" code to "${outputLanguage}" code. Do not include \`\`\`.
54
+
55
+ Example translating from JavaScript to Python:
56
+
57
+ JavaScript code:
58
+ for (let i = 0; i < 10; i++) {
59
+ console.log(i);
60
+ }
61
+
62
+ Python code:
63
+ for i in range(10):
64
+ print(i)
65
+
66
+ ${inputLanguage} code:
67
+ ${inputCode}
68
+
69
+ ${outputLanguage} code (no \`\`\`):
70
+ `;
71
+ }
72
+ };
73
+
74
+ export const OpenAIStream = async (
75
+ inputLanguage: string,
76
+ outputLanguage: string,
77
+ inputCode: string,
78
+ model: string,
79
+ key: string,
80
+ ) => {
81
+ const prompt = createPrompt(inputLanguage, outputLanguage, inputCode);
82
+
83
+ const system = { role: 'system', content: prompt };
84
+
85
+ const res = await fetch(`https://api.openai.com/v1/chat/completions`, {
86
+ headers: {
87
+ 'Content-Type': 'application/json',
88
+ Authorization: `Bearer ${key || process.env.OPENAI_API_KEY}`,
89
+ },
90
+ method: 'POST',
91
+ body: JSON.stringify({
92
+ model,
93
+ messages: [system],
94
+ temperature: 0,
95
+ stream: true,
96
+ }),
97
+ });
98
+
99
+ const encoder = new TextEncoder();
100
+ const decoder = new TextDecoder();
101
+
102
+ if (res.status !== 200) {
103
+ const statusText = res.statusText;
104
+ const result = await res.body?.getReader().read();
105
+ throw new Error(
106
+ `OpenAI API returned an error: ${
107
+ decoder.decode(result?.value) || statusText
108
+ }`,
109
+ );
110
+ }
111
+
112
+ const stream = new ReadableStream({
113
+ async start(controller) {
114
+ const onParse = (event: ParsedEvent | ReconnectInterval) => {
115
+ if (event.type === 'event') {
116
+ const data = event.data;
117
+
118
+ if (data === '[DONE]') {
119
+ controller.close();
120
+ return;
121
+ }
122
+
123
+ try {
124
+ const json = JSON.parse(data);
125
+ const text = json.choices[0].delta.content;
126
+ const queue = encoder.encode(text);
127
+ controller.enqueue(queue);
128
+ } catch (e) {
129
+ controller.error(e);
130
+ }
131
+ }
132
+ };
133
+
134
+ const parser = createParser(onParse);
135
+
136
+ for await (const chunk of res.body as any) {
137
+ parser.feed(decoder.decode(chunk));
138
+ }
139
+ },
140
+ });
141
+
142
+ return stream;
143
+ };