Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
cfe7452
Added bash to list of languages
size12husky Aug 3, 2025
6a25d43
Replaced bash with Visual Basic on languages dropdown in popup.html
size12husky Aug 3, 2025
f3f52d4
Merge pull request #11 from size12husky/new-branch
dineshsutihar Aug 3, 2025
8ae9e83
feat: Add workflow to welcome new contributors
dineshsutihar Aug 3, 2025
2955f0e
center translator-loading
SidoJain Aug 3, 2025
2551929
Merge pull request #22 from SidoJain/develop
dineshsutihar Aug 3, 2025
5f7f974
Added copy button to code translation panel
hemanth5055 Aug 3, 2025
f923f92
Merge pull request #23 from hemanth5055/add-copy-button
dineshsutihar Aug 3, 2025
10c922d
feat(dev-tools): Configure Prettier and Husky for automated code form…
dineshsutihar Jun 30, 2025
40a5602
Merge pull request #25 from dineshsutihar/feature/style
dineshsutihar Aug 3, 2025
80cd92f
feat: implement /v1/translate and /v1/explain routes and preserve leg…
hemanth5055 Aug 4, 2025
74d03d8
Update issue/feature templates
dineshsutihar Aug 5, 2025
728f685
Merge pull request #27 from dineshsutihar/issue-feature-temp
dineshsutihar Aug 5, 2025
531e36b
Minor fixes based on review comments
hemanth5055 Aug 5, 2025
9a76586
feat: implement /v1/translate and /v1/explain routes and preserve leg…
dineshsutihar Aug 5, 2025
f7bc97b
Added keyboard shortcut for code selection
hemanth5055 Aug 6, 2025
91a3f79
Changed keyboard shortcut
hemanth5055 Aug 6, 2025
3edcf13
Merge pull request #28 from hemanth5055/feature/keyboard-shortcut-cod…
dineshsutihar Aug 7, 2025
6df07f5
fix: fixes build fail issue of windows
dineshsutihar Aug 23, 2025
929d4f2
Merge pull request #32 from dineshsutihar/fix/build-fails
dineshsutihar Aug 23, 2025
621039f
style: change primary hover color of pop-up
Aashutosh-Mishra Aug 24, 2025
f470b0e
Merge pull request #33 from Aashutosh-Mishra/develop
dineshsutihar Aug 24, 2025
6478961
Tried to fix code window formatting on light and dark mode
hemanth5055 Aug 26, 2025
ab763fa
Fixed function arguments
hemanth5055 Aug 28, 2025
c174350
Merge pull request #36 from hemanth5055/dark-light-code-window
dineshsutihar Aug 28, 2025
32a4810
add IP-based rate limiting to Cloudflare Worker
hemanth5055 Sep 5, 2025
3c879c0
Update backend/src/index.ts
dineshsutihar Sep 7, 2025
fabe5ca
Merge pull request #37 from hemanth5055/add-rate-limiting
dineshsutihar Sep 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: bug, review
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]

**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]

**Additional context**
Add any other context about the problem here.
20 changes: 20 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[ISSUE]"
labels: feature, review
assignees: ''

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.
26 changes: 26 additions & 0 deletions .github/workflows/welcome.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Welcome First-Time Contributors

on:
pull_request_target:
types: [opened]
issues:
types: [opened]

jobs:
greet:
runs-on: ubuntu-latest
permissions:
pull-requests: write
issues: write
steps:
- uses: actions/first-interaction@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
issue-message: |
Hello @${{ github.actor }}! 👋
Thank you so much for taking the time to open your first issue with CodeTranslateAI. We appreciate you helping us make the project better!
We'll take a look and get back to you soon.
pr-message: |
Hi @${{ github.actor }}! 🎉
Welcome, and thank you for submitting your first pull request to CodeTranslateAI! We're thrilled to have your contribution.
Please make sure you've read our CONTRIBUTING.md guide. We'll review your changes shortly.
139 changes: 111 additions & 28 deletions backend/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,137 @@
import { GoogleGenerativeAI } from '@google/generative-ai';

export interface Env {
RATE_LIMIT: KVNamespace;
GEMINI_API_KEY: string;
}

export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
};
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
};

if (request.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
const MAX_REQUESTS_ALLOWED = 10;
const DURATION = 60_000;

async function checkRateLimit(ip: string, env: Env) {
const key = `ip_key:${ip}`;
const now = Date.now();
let value = await env.RATE_LIMIT.get(key);
let data = { count: 0, time: now };

if (value) {
try {
const { code, targetLanguage } = await request.json<{ code: string; targetLanguage: string }>();
data = JSON.parse(value);
} catch {
data = { count: 0, time: now };
}
}

if (!code || !targetLanguage) {
return new Response(JSON.stringify({ error: "Missing 'code' or 'targetLanguage' in request body." }), {
status: 400,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}
if (now - data.time > DURATION) {
data.count = 0;
data.time = now;
}

const genAI = new GoogleGenerativeAI(env.GEMINI_API_KEY);
data.count += 1;
await env.RATE_LIMIT.put(key, JSON.stringify(data), { expirationTtl: 65 });

const model = genAI.getGenerativeModel({ model: 'gemini-2.0-flash' });
return data.count <= MAX_REQUESTS_ALLOWED;
}
async function handleTranslate(request: Request, model: ReturnType<GoogleGenerativeAI['getGenerativeModel']>) {
const { code, targetLanguage } = await request.json<{ code: string; targetLanguage: string }>();

if (!code || !targetLanguage) {
return new Response(JSON.stringify({ error: "Missing 'code' or 'targetLanguage' in request body." }), {
status: 400,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}

const prompt = `Translate the following code snippet to ${targetLanguage}.
const prompt = `Translate the following code snippet to ${targetLanguage}.
Do not add any explanation, commentary, or markdown formatting like \`\`\` around the code.
**IMPORTANT: Preserve all original comments and their exact placement in the translated code. do not add extra spaces in between.**
**IMPORTANT: Preserve all original comments and their exact placement in the translated code. Do not add extra spaces in between.**
Only provide the raw, translated code itself.

Original Code:
${code}`;

const result = await model.generateContent(prompt);
const geminiResponse = result.response;
const translatedCode = geminiResponse.text();
const result = await model.generateContent(prompt);
const translatedCode = result.response.text();

return new Response(JSON.stringify({ translation: translatedCode }), {
status: 200,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}

async function handleExplain(request: Request, model: ReturnType<GoogleGenerativeAI['getGenerativeModel']>) {
const { code } = await request.json<{ code: string }>();

if (!code) {
return new Response(JSON.stringify({ error: "Missing 'code' in request body." }), {
status: 400,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}

const prompt = `Explain the following code snippet in detail:
1. Provide a clear breakdown of what each part (functions, variables, logic blocks) does.
2. If applicable, describe the overall purpose or intent of the code.
3. Offer a step-by-step explanation of how the code executes.
4. If the code is executable, show a sample input and the corresponding output.
5. Keep the explanation beginner-friendly but technically accurate.

Code:
${code}`;

const result = await model.generateContent(prompt);
const explanation = result.response.text();

return new Response(JSON.stringify({ explanation }), {
status: 200,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}

export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
if (request.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}

try {
const ip = request.headers.get('CF-Connecting-IP') || 'unknown';
const allowed = await checkRateLimit(ip, env);
if (!allowed) {
return new Response(JSON.stringify({ error: "Too many requests. Try again later." }), {
status: 429,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}
const url = new URL(request.url);
const path = url.pathname;
const genAI = new GoogleGenerativeAI(env.GEMINI_API_KEY);
const model = genAI.getGenerativeModel({ model: 'gemini-2.0-flash' });

if(path==="/test-rate-limit"){
return new Response(JSON.stringify("Proceed !"))
}
if (path === '/' || path === '/v1/translate') {
return await handleTranslate(request, model);
}

if (path === '/v1/explain') {
return await handleExplain(request, model);
}

return new Response(JSON.stringify({ translation: translatedCode }), {
status: 200,
return new Response(JSON.stringify({ error: 'Route not found.' }), {
status: 404,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
} catch (error) {
console.error('Error during translation:', error);
return new Response(JSON.stringify({ error: 'An error occurred while translating the code.' }), {
console.error('Error during request:', error);
return new Response(JSON.stringify({ error: 'An internal error occurred.' }), {
status: 500,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
Expand Down
1 change: 1 addition & 0 deletions backend/worker-configuration.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Runtime types generated with workerd@1.20250712.0 2025-07-15
declare namespace Cloudflare {
interface Env {
RATE_LIMIT: KVNamespace;
}
}
interface Env extends Cloudflare.Env {}
Expand Down
9 changes: 8 additions & 1 deletion backend/wrangler.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@
"compatibility_date": "2025-07-15",
"observability": {
"enabled": true
}
},
"kv_namespaces": [
{
"binding": "RATE_LIMIT",
"id": "<your_kv_id>"
}
]

/**
* Smart Placement
* Docs: https://developers.cloudflare.com/workers/configuration/smart-placement/#smart-placement
Expand Down
4 changes: 4 additions & 0 deletions frontend/.husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged
4 changes: 4 additions & 0 deletions frontend/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
dist
node_modules
packages
package-lock.json
5 changes: 5 additions & 0 deletions frontend/.prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"semi": true,
"singleQuote": false,
"trailingComma": "es5"
}
85 changes: 48 additions & 37 deletions frontend/background.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,52 @@

chrome.runtime.onMessage.addListener((request, _, sendResponse) => {
if (request.type === "TRANSLATE_CODE") {
const BACKEND_URL = process.env.BACKEND_URL;

chrome.storage.sync.get(['targetLanguage'], (result) => {
const targetLanguage = result.targetLanguage || 'Java';
fetch(BACKEND_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
code: request.code,
targetLanguage: targetLanguage,
}),
})
.then(response => {
if (!response.ok) {
throw new Error(`Network response was not ok: ${response.statusText}`);
}
return response.json();
})
.then(data => {

if (data.error) {
sendResponse({ error: data.error });
} else {
sendResponse({ translation: data.translation });
}
})
.catch(error => {
if (request.type === "TRANSLATE_CODE") {
const BACKEND_URL = process.env.BACKEND_URL;

console.error("Error calling backend:", error);
sendResponse({ error: `Failed to connect to the translation service: ${error.message}` });
});
chrome.storage.sync.get(["targetLanguage"], (result) => {
const targetLanguage = result.targetLanguage || "Java";
fetch(BACKEND_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
code: request.code,
targetLanguage: targetLanguage,
}),
})
.then((response) => {
if (!response.ok) {
throw new Error(
`Network response was not ok: ${response.statusText}`
);
}
return response.json();
})
.then((data) => {
if (data.error) {
sendResponse({ error: data.error });
} else {
sendResponse({ translation: data.translation });
}
})
.catch((error) => {
console.error("Error calling backend:", error);
sendResponse({
error: `Failed to connect to the translation service: ${error.message}`,
});
});
});

return true;
}
});
return true;
}
});
//Default commmand = Alt+T , Mac = Option+T
chrome.commands.onCommand.addListener((command) => {
if (command === "translate") {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
if (tabs.length > 0) {
chrome.tabs.sendMessage(tabs[0].id, { type: "ENABLE_PICKER" });
}
});
}
});
Loading