Skip to content

Commit 75b66d5

Browse files
authored
Merge pull request #8 from kiing-dom/feat/options-upgrade
V0.3.0 updates to options page
2 parents 16d631f + dcc5690 commit 75b66d5

File tree

5 files changed

+304
-34
lines changed

5 files changed

+304
-34
lines changed

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"manifest_version": 2,
33
"name": "KeepCode",
4-
"version": "0.2.2",
4+
"version": "0.3.0",
55
"description": "Prep smarter for coding interviews: KeepCode tracks your LeetCode progress so you can focus on solving.",
66
"permissions": ["storage", "tabs", "activeTab", "https://leetcode.com/*"],
77
"background": {

options/options.css

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ a:hover {
8181
gap: 12px;
8282
margin-bottom: 18px;
8383
}
84+
.problems-toolbar #searchInput {
85+
width: 240px !important;
86+
min-width: 120px !important;
87+
max-width: 320px !important;
88+
flex: 0 0 auto !important;
89+
}
8490
#searchInput {
8591
flex: 1;
8692
padding: 7px 12px;
@@ -138,3 +144,133 @@ a:hover {
138144
.section a:hover {
139145
text-decoration: underline;
140146
}
147+
148+
.about-content {
149+
background: #fafbfc;
150+
border-radius: 8px;
151+
padding: 24px 28px;
152+
margin-top: 12px;
153+
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
154+
font-size: 1.05em;
155+
color: #222;
156+
}
157+
158+
.about-content p {
159+
margin-bottom: 12px;
160+
}
161+
162+
.about-content a {
163+
color: #0074d9;
164+
text-decoration: none;
165+
transition: color 0.2s;
166+
}
167+
.about-content a:hover {
168+
color: #005fa3;
169+
text-decoration: underline;
170+
}
171+
172+
.about-version {
173+
margin: 10px 0 18px 0;
174+
font-size: 0.98em;
175+
color: #555;
176+
font-style: italic;
177+
}
178+
179+
.faq {
180+
margin-top: 10px;
181+
background: #f3f6fa;
182+
border-left: 3px solid #0074d9;
183+
padding: 12px 16px;
184+
border-radius: 5px;
185+
}
186+
187+
/* Custom Tag Dropdown Styles */
188+
.tag-dropdown,
189+
.tag-dropdown-selected,
190+
.tag-dropdown-list {
191+
position: relative;
192+
font-size: 1em;
193+
}
194+
.tag-dropdown-selected {
195+
background: #fff;
196+
border: 1px solid #d1d5db;
197+
border-radius: 5px;
198+
padding: 7px 12px;
199+
cursor: pointer;
200+
user-select: none;
201+
min-width: 120px;
202+
transition: border-color 0.15s;
203+
}
204+
.tag-dropdown-selected:focus {
205+
outline: none;
206+
border-color: #8868bd;
207+
}
208+
.tag-dropdown-list {
209+
position: absolute;
210+
top: 110%;
211+
left: 0;
212+
right: 0;
213+
background: #fff;
214+
border: 1px solid #d1d5db;
215+
border-radius: 5px;
216+
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
217+
z-index: 10;
218+
padding: 0;
219+
}
220+
.tag-dropdown-search {
221+
width: 100%;
222+
margin: 0;
223+
border-radius: 5px 5px 0 0;
224+
border-bottom: 1px solid #e5e7eb;
225+
border-top: none;
226+
border-left: none;
227+
border-right: none;
228+
box-sizing: border-box;
229+
padding: 10px 12px;
230+
font-size: 1em;
231+
background: #fafbfc;
232+
}
233+
.tag-dropdown-options {
234+
max-height: 180px;
235+
overflow-y: auto;
236+
padding-top: 4px;
237+
}
238+
.tag-dropdown-list {
239+
border-radius: 5px;
240+
overflow: hidden;
241+
}
242+
.tag-dropdown-option {
243+
padding: 7px 16px;
244+
cursor: pointer;
245+
transition: background 0.13s;
246+
color: #374151;
247+
}
248+
.tag-dropdown-option.selected,
249+
.tag-dropdown-option:hover {
250+
background: #f3f4f6;
251+
color: #8868bd;
252+
}
253+
254+
#sortDropdown {
255+
background: #fff;
256+
background-image: url('data:image/svg+xml;utf8,<svg fill="%238868bd" height="20" viewBox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg"><path d="M7.293 8.293a1 1 0 011.414 0L10 9.586l1.293-1.293a1 1 0 111.414 1.414l-2 2a1 1 0 01-1.414 0l-2-2a1 1 0 010-1.414z"/></svg>');
257+
background-repeat: no-repeat;
258+
background-position: right 10px center;
259+
background-size: 18px 18px;
260+
261+
border: 1px solid #d1d5db;
262+
border-radius: 5px;
263+
padding: 7px 32px 7px 12px;
264+
font-size: 1em;
265+
min-width: 160px;
266+
cursor: pointer;
267+
appearance: none;
268+
-webkit-appearance: none;
269+
-moz-appearance: none;
270+
transition: border-color 0.15s;
271+
position: relative;
272+
}
273+
#sortDropdown:focus {
274+
outline: none;
275+
border-color: #8868bd;
276+
}

options/options.html

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@
3030
<h2>All Solved Problems</h2>
3131
<div class="problems-toolbar">
3232
<input type="text" id="searchInput" placeholder="Search by title...">
33-
<select id="tagFilter">
34-
<option value="all">All Tags</option>
33+
<div id="tagDropdownContainer"></div>
34+
<select id="sortDropdown" style="margin-left:8px;">
35+
<option value="recent-desc">Most recently solved</option>
36+
<option value="recent-asc">Least recently solved</option>
3537
</select>
3638
</div>
3739
<div id="problemsList"></div>
@@ -42,17 +44,26 @@ <h2>Settings</h2>
4244
</section>
4345
<section id="section-about" class="section">
4446
<h2>FAQ / About</h2>
45-
<p>
46-
Made by <a href="https://linktr.ee/267dngi" target="_blank">@dngi</a>.<br>
47-
Star the project on <a href="https://github.com/kiing-dom/leetcode-tracker" target="_blank">GitHub</a>!<br>
48-
If you find this useful, consider <a href="https://www.ko-fi.com/267dngi" target="_blank"> buying me a kebab 😏</a>.<br><br>
49-
<strong>FAQ:</strong><br>
50-
<b>Q:</b> How do I use this extension?<br>
51-
<b>A:</b> Just solve problems on LeetCode as usual! Your progress is tracked automatically.<br>
52-
</p>
47+
<div class="about-version" style="margin-bottom: 18px;">
48+
<span id="extVersion"></span>
49+
</div>
50+
<hr style="margin-bottom: 18px;">
51+
<div class="about-content">
52+
<p>
53+
Made by <a href="https://linktr.ee/267dngi" target="_blank">@dngi</a>.<br>
54+
Star the project on <a href="https://github.com/kiing-dom/leetcode-tracker" target="_blank">GitHub</a>!<br>
55+
If you find this useful, consider <a href="https://www.ko-fi.com/267dngi" target="_blank"> buying me a kebab 😏</a>.
56+
</p>
57+
<div class="faq">
58+
<strong>FAQ:</strong><br>
59+
<b>Q:</b> How do I use this extension?<br>
60+
<b>A:</b> Just solve problems on LeetCode as usual! Your progress is tracked automatically.<br>
61+
</div>
62+
</div>
5363
</section>
5464
</main>
5565
</div>
66+
<script src="tagDropdown.js"></script>
5667
<script src="options.js"></script>
5768
</body>
5869
</html>

options/options.js

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// options.js
2-
// Handles navigation and basic filtering for the options page
3-
41
document.addEventListener('DOMContentLoaded', () => {
52
// Sidebar navigation
63
const navItems = document.querySelectorAll('.nav-item');
@@ -16,12 +13,15 @@ document.addEventListener('DOMContentLoaded', () => {
1613
});
1714
});
1815

16+
const sortDropdown = document.getElementById('sortDropdown');
17+
1918
// All Problems: load and render problems
2019
const problemsList = document.getElementById('problemsList');
2120
const searchInput = document.getElementById('searchInput');
22-
const tagFilter = document.getElementById('tagFilter');
21+
const tagDropdownContainer = document.getElementById('tagDropdownContainer');
22+
let tagDropdownInstance = null;
2323

24-
function renderProblems(problems, filterTag, searchTerm) {
24+
function renderProblems(problems, filterTag, searchTerm, sortOrder = 'recent-desc') {
2525
problemsList.innerHTML = '';
2626
let filtered = problems;
2727
if (filterTag && filterTag !== 'all') {
@@ -30,31 +30,33 @@ document.addEventListener('DOMContentLoaded', () => {
3030
if (searchTerm) {
3131
filtered = filtered.filter(p => p.title.toLowerCase().includes(searchTerm.toLowerCase()));
3232
}
33+
// Sorting
34+
if (sortOrder === 'recent-desc') {
35+
filtered.sort((a, b) => (b.solvedAt || 0) - (a.solvedAt || 0));
36+
} else if (sortOrder === 'recent-asc') {
37+
filtered.sort((a, b) => (a.solvedAt || 0) - (b.solvedAt || 0));
38+
}
3339
if (filtered.length === 0) {
3440
problemsList.innerHTML = '<p>No problems found.</p>';
3541
}
3642
filtered.forEach(problem => {
3743
const item = document.createElement('div');
3844
item.className = 'problem-item';
3945
const difficultyClass = problem.difficulty ? problem.difficulty.toLowerCase() : '';
40-
// Use DOM methods instead of innerHTML for safety
4146
const link = document.createElement('a');
4247
link.href = problem.url;
4348
link.target = '_blank';
4449
link.textContent = problem.title;
45-
4650
const diffSpan = document.createElement('span');
4751
diffSpan.className = `difficulty ${difficultyClass}`;
4852
diffSpan.textContent = problem.difficulty;
49-
5053
const tagsSpan = document.createElement('span');
5154
tagsSpan.style.fontSize = '0.85em';
5255
tagsSpan.style.color = problem.tags && problem.tags.length > 0 ? '#666' : '#bbb';
5356
tagsSpan.style.marginLeft = '8px';
5457
tagsSpan.textContent = problem.tags && problem.tags.length > 0
5558
? `[${problem.tags.join(', ')}]`
5659
: '[No tags]';
57-
5860
item.appendChild(link);
5961
item.appendChild(diffSpan);
6062
item.appendChild(tagsSpan);
@@ -78,21 +80,31 @@ document.addEventListener('DOMContentLoaded', () => {
7880
p.tags.forEach(tag => tagSet.add(tag));
7981
}
8082
});
81-
tagFilter.innerHTML = '<option value="all">All Tags</option>';
82-
Array.from(tagSet).forEach(tag => {
83-
const opt = document.createElement('option');
84-
opt.value = tag;
85-
opt.textContent = tag;
86-
tagFilter.appendChild(opt);
87-
});
83+
const allTags = Array.from(tagSet).sort((a, b) => a.localeCompare(b));
84+
let currentProblems = problems; // Store for re-sorting/filtering
85+
function rerender() {
86+
renderProblems(
87+
currentProblems,
88+
tagDropdownInstance ? tagDropdownInstance.selectedTag : 'all',
89+
searchInput.value,
90+
sortDropdown.value
91+
);
92+
}
93+
if (tagDropdownInstance) {
94+
tagDropdownInstance.setTags(allTags);
95+
} else {
96+
tagDropdownInstance = new window.TagDropdown(tagDropdownContainer, allTags, () => rerender());
97+
}
8898
// Initial render
89-
renderProblems(problems, tagFilter.value, searchInput.value);
99+
rerender();
90100
// Event listeners
91-
tagFilter.addEventListener('change', () => {
92-
renderProblems(problems, tagFilter.value, searchInput.value);
93-
});
94-
searchInput.addEventListener('input', () => {
95-
renderProblems(problems, tagFilter.value, searchInput.value);
96-
});
101+
searchInput.addEventListener('input', rerender);
102+
sortDropdown.addEventListener('change', rerender);
103+
104+
const extVersionElem = document.getElementById('extVersion');
105+
if (extVersionElem && browser.runtime.getManifest) {
106+
const manifest = browser.runtime.getManifest();
107+
extVersionElem.textContent = manifest.version;
108+
}
97109
});
98110
});

0 commit comments

Comments
 (0)