Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
44 changes: 44 additions & 0 deletions backend/ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,3 +381,47 @@ def rate_code_efficiency(title: str, code: str, language: str = "python") -> dic
raise last_error or Exception(
"All Gemini models are currently quota-limited. Please wait a minute and try again."
)

def generate_hint(
title: str,
description: str,
difficulty: str,
hint_level: int
) -> str:

hint_prompts = {
1: "Give only a direction hint. Do not reveal algorithm or code.",
2: "Suggest useful data structures. Do not reveal solution.",
3: "Explain the intuition. No code.",
4: "Provide pseudocode only.",
5: "Provide the complete optimized solution."
}

prompt = f"""
You are a LeetCode mentor.

Problem Title:
{title}

Problem Description:
{description}

Difficulty:
{difficulty}

Task:
{hint_prompts.get(hint_level, hint_prompts[1])}

Keep response concise.
"""

api_key = os.getenv("GEMINI_API_KEY")

client = genai.Client(api_key=api_key)

response = client.models.generate_content(
model="models/gemini-2.5-flash",
contents=prompt
)

return response.text
30 changes: 30 additions & 0 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

# --- UPDATED AI PATH ---
from ai_core.blog_generator import generate_blog
from ai import generate_hint
from devto import publish_to_platforms
from models.reminder import PublishRecord
from services.reminder_scheduler import start_scheduler
Expand Down Expand Up @@ -369,6 +370,12 @@ def health_check():
# -----------------------------
# Blog Generator Endpoint
# -----------------------------
class HintRequest(BaseModel):
title: str
description: str
difficulty: str = "Unknown"
hint_level: int

@app.post("/generate-blog")
async def create_blog(
problem: Problem,
Expand Down Expand Up @@ -481,6 +488,29 @@ async def create_blog(
"social": social_results,
},
}
@app.post("/generate-hint")
async def generate_hint_api(request: HintRequest):

try:

hint = generate_hint(
request.title,
request.description,
request.difficulty,
request.hint_level
)

return {
"status": "success",
"hint": hint
}

except Exception as e:

return {
"status": "error",
"message": str(e)
}


# -----------------------------
Expand Down
2 changes: 2 additions & 0 deletions backend/tests/test_devto.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,4 +183,6 @@ async def test_empty_errors_list_does_not_raise(self, mock_hashnode_request):
"Two Sum", "# content", tags=["leetcode"], published=True
)
assert result.status == "success"



25 changes: 25 additions & 0 deletions extension/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,31 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
});
}

chrome.runtime.onMessage.addListener((request) => {

if (request.type === "GENERATE_HINT") {

fetch(`${API_BASE_URL}/generate-hint`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(request.payload)
})
.then(res => res.json())
.then(data => {

chrome.runtime.sendMessage({
type: "HINT_READY",
hint: data.hint
});

});

}

});

if (data.status === 'success' || data.status === 'partial_success') {
const platforms = data.data?.platforms || [];
const postedPlatforms = platforms
Expand Down
36 changes: 36 additions & 0 deletions extension/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,42 @@ <h3>Preview & Edit Blog</h3>
</div>
</div>

<div id="hintSection" style="margin-top:15px;">

<h3>Hint Progression Mode</h3>

<div class="button-group">
<button id="hint1Btn">💡 Hint 1</button>
<button id="hint2Btn">🧩 Hint 2</button>
</div>

<div class="button-group" style="margin-top:8px;">
<button id="hint3Btn">🧠 Hint 3</button>
<button id="hint4Btn">⚡ Hint 4</button>
</div>

<button
id="solutionBtn"
style="margin-top:8px;background:#27ae60;"
>
🖥️ Show Solution
</button>

<textarea
id="hintOutput"
style="
width:100%;
min-height:120px;
margin-top:10px;
padding:10px;
border-radius:8px;
resize:vertical;
"
placeholder="Hints will appear here..."
></textarea>

</div>

<div id="status">Ready to blog! 🚀</div>
<details id="historySection" style="margin-top:20px; text-align:left; background: var(--bg-glass); border: 1px solid var(--border-glass); border-radius: 16px; padding: 12px; backdrop-filter: blur(12px);">
<summary style="cursor:pointer; font-size:12px; font-weight:600; text-transform:uppercase; color:var(--text-muted); padding:4px 0; user-select:none; font-family: 'Outfit', sans-serif;">📋 Recent Posts</summary>
Expand Down
70 changes: 70 additions & 0 deletions extension/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -533,4 +533,74 @@ const copyBtn = document.getElementById('copyBtn');
if (copyBtn) {
copyBtn.addEventListener('click', copyBlogToClipboard);
}

async function requestHint(level) {

const tabs = await chrome.tabs.query({
active: true,
currentWindow: true
});

const tab = tabs[0];

chrome.scripting.executeScript(
{
target: { tabId: tab.id },
func: () => {

const titleElement =
document.querySelector('div[data-cy="question-title"]') ||
document.querySelector('.text-title-large');

const descriptionElement =
document.querySelector('.elfjS') ||
document.querySelector('[data-track-load="description_content"]');

return {
title: titleElement ? titleElement.innerText : "",
description: descriptionElement ? descriptionElement.innerText : "",
difficulty: "Unknown"
};
}
},
(results) => {

const problem = results[0].result;

chrome.runtime.sendMessage({
type: "GENERATE_HINT",
payload: {
...problem,
hint_level: level
}
});

}
);
}

document.getElementById("hint1Btn")?.addEventListener("click", () => requestHint(1));

document.getElementById("hint2Btn")?.addEventListener("click", () => requestHint(2));

document.getElementById("hint3Btn")?.addEventListener("click", () => requestHint(3));

document.getElementById("hint4Btn")?.addEventListener("click", () => requestHint(4));

document.getElementById("solutionBtn")?.addEventListener("click", () => requestHint(5));

chrome.runtime.onMessage.addListener((request) => {

if (request.type === "HINT_READY") {

const hintBox = document.getElementById("hintOutput");

if (hintBox) {
hintBox.value = request.hint;
}

}

});

// =================================================
Loading