Skip to content

Commit bc53a43

Browse files
committed
feat(leetcode): single atomic commit, debounce dedup, enriched README, manual sync btn; centralize AI prompts
1 parent 31bf822 commit bc53a43

13 files changed

Lines changed: 492 additions & 370 deletions

File tree

src/core/storage.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import { storage as browserStorage } from "../lib/browser-compat.js";
77
import { CONSTANTS } from "./constants.js";
88
import { createDebugger } from "../lib/debug.js";
9+
import { normalizeAIPrompts } from "./ai-prompts.js";
910
const dbg = createDebugger("Storage");
1011

1112
/**
@@ -61,6 +62,16 @@ export const Storage = {
6162
await browserStorage.local.set(payload);
6263
},
6364

65+
async getAIPrompts() {
66+
const res = await browserStorage.local.get(CONSTANTS.SK.AI_PROMPTS);
67+
return normalizeAIPrompts(res[CONSTANTS.SK.AI_PROMPTS] || {});
68+
},
69+
70+
async setAIPrompts(prompts) {
71+
const normalized = normalizeAIPrompts(prompts || {});
72+
await browserStorage.local.set({ [CONSTANTS.SK.AI_PROMPTS]: normalized });
73+
},
74+
6475
async getAuthToken(provider) {
6576
const keys = await browserStorage.local.get(CONSTANTS.SK.AUTH_TOKENS);
6677
const tokens = keys[CONSTANTS.SK.AUTH_TOKENS] || {};

src/core/url-state.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,22 @@ export function updateQueryParams(partial, options = {}) {
3636
}
3737
window.history.pushState(null, "", next);
3838
}
39+
40+
export function buildSettingsHref({
41+
tab = "settings",
42+
settingsTab,
43+
settingsSection,
44+
settingsProvider,
45+
settingsAdvanced,
46+
q,
47+
} = {}) {
48+
const params = new URLSearchParams();
49+
if (tab) params.set("tab", tab);
50+
if (settingsTab) params.set("settingsTab", settingsTab);
51+
if (settingsSection) params.set("settingsSection", settingsSection);
52+
if (settingsProvider) params.set("settingsProvider", settingsProvider);
53+
if (settingsAdvanced)
54+
params.set("settingsAdvanced", String(settingsAdvanced));
55+
if (q) params.set("q", q);
56+
return `${window.location.pathname}?${params.toString()}`;
57+
}

src/handlers/ai/claude/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { BaseAIHandler } from "../../_base/BaseAIHandler.js";
77
import { APIKeyPool } from "../../../core/api-key-pool.js";
88
import { Storage } from "../../../core/storage.js";
99
import { CONSTANTS } from "../../../core/constants.js";
10+
import { buildReviewPrompt } from "../../../core/ai-prompts.js";
1011

1112
export class ClaudeHandler extends BaseAIHandler {
1213
constructor() {
@@ -26,7 +27,8 @@ export class ClaudeHandler extends BaseAIHandler {
2627
settings.aiEndpoint ||
2728
CONSTANTS.AI_PROVIDERS.claude.endpoint;
2829

29-
const prompt = `Review this DSA solution for "${problemContext.title}". Language: ${problemContext.language}, Difficulty: ${problemContext.difficulty}. Code: \`${code}\`. Provide Time/Space complexity, optimizations, and key patterns.`;
30+
const prompts = await Storage.getAIPrompts();
31+
const prompt = buildReviewPrompt(problemContext, code, prompts);
3032

3133
const keyCount = await this.keyPool.getKeyCount();
3234
if (!keyCount) throw new Error("No Claude API key available.");

src/handlers/ai/deepseek/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { BaseAIHandler } from "../../_base/BaseAIHandler.js";
77
import { APIKeyPool } from "../../../core/api-key-pool.js";
88
import { Storage } from "../../../core/storage.js";
99
import { CONSTANTS } from "../../../core/constants.js";
10+
import { buildReviewPrompt } from "../../../core/ai-prompts.js";
1011

1112
export class DeepSeekHandler extends BaseAIHandler {
1213
constructor() {
@@ -22,7 +23,8 @@ export class DeepSeekHandler extends BaseAIHandler {
2223
settings.aiModel ||
2324
CONSTANTS.AI_PROVIDERS.deepseek.defaultModel;
2425

25-
const prompt = `Review this DSA solution for "${problemContext.title}". Language: ${problemContext.language}, Difficulty: ${problemContext.difficulty}. Code: \`${code}\`. Provide Time/Space complexity, optimizations, and key patterns.`;
26+
const prompts = await Storage.getAIPrompts();
27+
const prompt = buildReviewPrompt(problemContext, code, prompts);
2628

2729
const endpoint =
2830
settings.deepseek_endpoint ||

src/handlers/ai/gemini/index.js

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { BaseAIHandler } from "../../_base/BaseAIHandler.js";
77
import { APIKeyPool } from "../../../core/api-key-pool.js";
88
import { Storage } from "../../../core/storage.js";
99
import { CONSTANTS } from "../../../core/constants.js";
10+
import { buildReviewPrompt } from "../../../core/ai-prompts.js";
1011

1112
export class GeminiHandler extends BaseAIHandler {
1213
constructor() {
@@ -55,22 +56,8 @@ export class GeminiHandler extends BaseAIHandler {
5556
settings.gemini_endpoint ||
5657
settings.aiEndpoint ||
5758
CONSTANTS.AI_PROVIDERS.gemini.endpoint;
58-
59-
const prompt = `
60-
Review the following DSA solution for the problem: "${problemContext.title}".
61-
Language: ${problemContext.language || problemContext.lang?.name || "Unknown"}
62-
Difficulty: ${problemContext.difficulty}
63-
64-
Code:
65-
\`\`\`
66-
${code}
67-
\`\`\`
68-
69-
Please provide a brief, professional analysis:
70-
1. Time & Space Complexity (using Big-O notation).
71-
2. Potential optimizations or cleaner approaches.
72-
3. Key take-away patterns.
73-
`;
59+
const prompts = await Storage.getAIPrompts();
60+
const prompt = buildReviewPrompt(problemContext, code, prompts);
7461

7562
const keyCount = await this.keyPool.getKeyCount();
7663
if (!keyCount) throw new Error("No Gemini API key available.");

src/handlers/ai/ollama/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import { BaseAIHandler } from "../../_base/BaseAIHandler.js";
77
import { Storage } from "../../../core/storage.js";
88
import { CONSTANTS } from "../../../core/constants.js";
9+
import { buildReviewPrompt } from "../../../core/ai-prompts.js";
910

1011
export class OllamaHandler extends BaseAIHandler {
1112
constructor() {
@@ -56,7 +57,8 @@ export class OllamaHandler extends BaseAIHandler {
5657
const endpoint =
5758
settings.ollama_endpoint || CONSTANTS.AI_PROVIDERS.ollama.endpoint;
5859

59-
const prompt = `Review this DSA solution for "${problemContext.title}". Language: ${problemContext.language}, Difficulty: ${problemContext.difficulty}. Code: \`${code}\`. Provide Time/Space complexity, optimizations, and key patterns.`;
60+
const prompts = await Storage.getAIPrompts();
61+
const prompt = buildReviewPrompt(problemContext, code, prompts);
6062

6163
try {
6264
const res = await fetch(`${endpoint}/generate`, {

src/handlers/ai/openai/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { BaseAIHandler } from "../../_base/BaseAIHandler.js";
77
import { APIKeyPool } from "../../../core/api-key-pool.js";
88
import { Storage } from "../../../core/storage.js";
99
import { CONSTANTS } from "../../../core/constants.js";
10+
import { buildReviewPrompt } from "../../../core/ai-prompts.js";
1011

1112
export class OpenAIHandler extends BaseAIHandler {
1213
constructor() {
@@ -26,7 +27,8 @@ export class OpenAIHandler extends BaseAIHandler {
2627
settings.aiEndpoint ||
2728
CONSTANTS.AI_PROVIDERS.openai.endpoint;
2829

29-
const prompt = `Review this DSA solution for "${problemContext.title}". Language: ${problemContext.language}, Difficulty: ${problemContext.difficulty}. Code: \`${code}\`. Provide Time/Space complexity, optimizations, and key patterns.`;
30+
const prompts = await Storage.getAIPrompts();
31+
const prompt = buildReviewPrompt(problemContext, code, prompts);
3032

3133
const keyCount = await this.keyPool.getKeyCount();
3234
if (!keyCount) throw new Error("No OpenAI API key available.");

src/handlers/ai/openrouter/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { BaseAIHandler } from "../../_base/BaseAIHandler.js";
77
import { APIKeyPool } from "../../../core/api-key-pool.js";
88
import { Storage } from "../../../core/storage.js";
99
import { CONSTANTS } from "../../../core/constants.js";
10+
import { buildReviewPrompt } from "../../../core/ai-prompts.js";
1011

1112
export class OpenRouterHandler extends BaseAIHandler {
1213
constructor() {
@@ -26,7 +27,8 @@ export class OpenRouterHandler extends BaseAIHandler {
2627
settings.aiEndpoint ||
2728
CONSTANTS.AI_PROVIDERS.openrouter.endpoint;
2829

29-
const prompt = `Review this DSA solution for "${problemContext.title}". Language: ${problemContext.language}, Difficulty: ${problemContext.difficulty}. Code: \`${code}\`. Provide Time/Space complexity, optimizations, and key patterns.`;
30+
const prompts = await Storage.getAIPrompts();
31+
const prompt = buildReviewPrompt(problemContext, code, prompts);
3032

3133
const keyCount = await this.keyPool.getKeyCount();
3234
if (!keyCount) throw new Error("No OpenRouter API key available.");

src/handlers/platforms/leetcode/graphql-queries.js

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
*/
55

66
export const QUERIES = {
7-
// Fetches problem details via slug
87
QUESTION: `
98
query questionData($titleSlug: String!) {
109
question(titleSlug: $titleSlug) {
@@ -17,45 +16,55 @@ export const QUERIES = {
1716
difficulty
1817
likes
1918
dislikes
19+
stats
20+
hints
2021
topicTags {
2122
name
2223
slug
2324
}
24-
stats
25-
hints
25+
similarQuestionList {
26+
difficulty
27+
titleSlug
28+
title
29+
isPaidOnly
30+
}
31+
codeSnippets {
32+
lang
33+
langSlug
34+
code
35+
}
36+
companyTagStats
37+
acRate
2638
}
2739
}
2840
`,
2941

30-
// Fetches detailed submission info including code
3142
SUBMISSION_DETAIL: `
3243
query submissionDetails($submissionId: Int!) {
3344
submissionDetails(submissionId: $submissionId) {
3445
runtime
3546
runtimeDisplay
3647
runtimePercentile
37-
runtimeDistribution
3848
memory
3949
memoryDisplay
4050
memoryPercentile
41-
memoryDistribution
4251
code
4352
timestamp
4453
statusCode
45-
statusDisplay
4654
lang {
4755
name
4856
verboseName
4957
}
5058
question {
5159
questionId
5260
titleSlug
61+
title
62+
difficulty
5363
}
5464
}
5565
}
5666
`,
5767

58-
// Fetches recent submissions list
5968
SUBMISSION_LIST: `
6069
query submissionList($offset: Int!, $limit: Int!, $lastKey: String, $questionSlug: String!, $status: Int) {
6170
questionSubmissionList(
@@ -86,36 +95,34 @@ export const QUERIES = {
8695
}
8796
`,
8897

89-
// Fetches the user's public profile and stats
90-
USER_PROFILE: `
91-
query getUserProfile($username: String!) {
92-
allQuestionsCount {
93-
difficulty
94-
count
98+
// Fetches the currently logged-in user's username
99+
GLOBAL_DATA: `
100+
query globalData {
101+
userStatus {
102+
isSignedIn
103+
username
104+
avatar
105+
isPremium
95106
}
96-
matchedUser(username: $username) {
97-
contributions {
98-
points
99-
}
100-
profile {
101-
reputation
102-
ranking
103-
}
104-
submissionCalendar
105-
submitStats {
106-
acSubmissionNum {
107-
difficulty
108-
count
109-
submissions
110-
}
111-
totalSubmissionNum {
112-
difficulty
113-
count
114-
submissions
107+
}
108+
`,
109+
110+
// Daily challenge — for QoL banner
111+
DAILY_CHALLENGE: `
112+
query questionOfToday {
113+
activeDailyCodingChallengeQuestion {
114+
date
115+
link
116+
question {
117+
questionFrontendId
118+
title
119+
titleSlug
120+
difficulty
121+
topicTags {
122+
name
115123
}
116124
}
117125
}
118126
}
119-
`
127+
`,
120128
};
121-

0 commit comments

Comments
 (0)