Skip to content

Commit dddd072

Browse files
done
1 parent d508523 commit dddd072

3 files changed

Lines changed: 165 additions & 39 deletions

File tree

index.html

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,16 @@
1010
<meta property="og:type" content="website">
1111
<meta property="og:url" content="https://parasgupta-bca.github.io/PHP-Programs/">
1212
<meta property="og:title" content="PHP Portfolio | Paras Gupta">
13-
<meta property="og:description" content="Interactive PHP Code Repository & Portfolio. Browse, run, and explore PHP programs directly in your browser.">
13+
<meta property="og:description"
14+
content="Interactive PHP Code Repository & Portfolio. Browse, run, and explore PHP programs directly in your browser.">
1415
<meta property="og:image" content="https://parasgupta-bca.github.io/PHP-Programs/og-image.png">
1516

1617
<!-- Twitter -->
1718
<meta property="twitter:card" content="summary_large_image">
1819
<meta property="twitter:url" content="https://parasgupta-bca.github.io/PHP-Programs/">
1920
<meta property="twitter:title" content="PHP Portfolio | Paras Gupta">
20-
<meta property="twitter:description" content="Interactive PHP Code Repository & Portfolio. Browse, run, and explore PHP programs directly in your browser.">
21+
<meta property="twitter:description"
22+
content="Interactive PHP Code Repository & Portfolio. Browse, run, and explore PHP programs directly in your browser.">
2123
<meta property="twitter:image" content="https://parasgupta-bca.github.io/PHP-Programs/og-image.png">
2224
<!-- Google Fonts -->
2325
<link rel="preconnect" href="https://fonts.googleapis.com">
@@ -54,16 +56,22 @@
5456
</div>
5557

5658
<div class="file-list-container">
57-
<div style="display: flex; justify-content: space-between; align-items: center; padding-right: 20px;">
58-
<h3>Explorer</h3>
59+
<div
60+
style="display: flex; justify-content: space-between; align-items: center; padding: 0 20px 10px 25px;">
61+
<h3 style="padding: 0;">Explorer</h3>
5962
<button id="refresh-files" class="icon-btn" title="Refresh File List">
6063
<ion-icon name="refresh-outline"></ion-icon>
6164
</button>
6265
</div>
6366
<!-- Search Bar -->
64-
<div class="search-container">
65-
<ion-icon name="search-outline"></ion-icon>
66-
<input type="text" id="file-search" placeholder="Search programs...">
67+
<div class="search-wrapper">
68+
<div class="search-box">
69+
<ion-icon name="search-outline"></ion-icon>
70+
<input type="text" id="file-search" placeholder="Search programs..." autocomplete="off">
71+
<button id="clear-search" class="clear-btn" title="Clear search" style="display: none;">
72+
<ion-icon name="close-circle"></ion-icon>
73+
</button>
74+
</div>
6775
</div>
6876
<ul id="file-list" class="file-list">
6977
<!-- Files will be injected here -->

script.js

Lines changed: 95 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ const outputConsole = document.getElementById('output-console');
1111
const clearConsoleBtn = document.getElementById('clear-console');
1212
const refreshFilesBtn = document.getElementById('refresh-files');
1313
const fileSearchInput = document.getElementById('file-search');
14+
const clearSearchBtn = document.getElementById('clear-search');
1415
const copyBtn = document.getElementById('copy-btn');
1516

17+
let currentSearchTerm = '';
18+
1619
const CACHE_KEY = 'php_repos_cache_v1';
1720
const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
1821

@@ -36,8 +39,23 @@ document.addEventListener('DOMContentLoaded', () => {
3639
// Search functionality
3740
if (fileSearchInput) {
3841
fileSearchInput.addEventListener('input', (e) => {
39-
const searchTerm = e.target.value.toLowerCase();
40-
filterFiles(searchTerm);
42+
currentSearchTerm = e.target.value.toLowerCase();
43+
filterFiles(currentSearchTerm);
44+
45+
// Toggle clear button
46+
if (clearSearchBtn) {
47+
clearSearchBtn.style.display = currentSearchTerm ? 'flex' : 'none';
48+
}
49+
});
50+
}
51+
52+
if (clearSearchBtn) {
53+
clearSearchBtn.addEventListener('click', () => {
54+
fileSearchInput.value = '';
55+
currentSearchTerm = '';
56+
filterFiles('');
57+
clearSearchBtn.style.display = 'none';
58+
fileSearchInput.focus();
4159
});
4260
}
4361

@@ -152,19 +170,43 @@ async function fetchFiles(isBackground = false) {
152170
li.onclick = () => loadFile(file, li);
153171
fileListEl.appendChild(li);
154172
});
173+
174+
// Re-apply search filter if active
175+
if (currentSearchTerm) {
176+
filterFiles(currentSearchTerm);
177+
}
155178
}
156179

157180
// Filter files based on search term
158181
function filterFiles(term) {
159182
const items = fileListEl.querySelectorAll('.file-item');
183+
let foundCount = 0;
184+
160185
items.forEach(item => {
161-
const fileName = item.innerText.toLowerCase();
186+
const fileName = item.innerText.trim().toLowerCase();
162187
if (fileName.includes(term)) {
163-
item.style.display = 'flex';
188+
item.style.setProperty('display', 'flex', 'important');
189+
foundCount++;
164190
} else {
165-
item.style.display = 'none';
191+
item.style.setProperty('display', 'none', 'important');
166192
}
167193
});
194+
195+
// Handle "No results" message
196+
const existingNoResults = fileListEl.querySelector('.no-results');
197+
if (foundCount === 0 && term !== '') {
198+
if (!existingNoResults) {
199+
const noRes = document.createElement('li');
200+
noRes.className = 'file-item no-results';
201+
noRes.style.cursor = 'default';
202+
noRes.style.color = 'var(--text-muted)';
203+
noRes.style.justifyContent = 'center';
204+
noRes.innerHTML = 'No matches found';
205+
fileListEl.appendChild(noRes);
206+
}
207+
} else if (existingNoResults) {
208+
existingNoResults.remove();
209+
}
168210
}
169211

170212
// Poll for updates every 5 minutes (reduced from 60s to save API calls)
@@ -202,7 +244,7 @@ async function loadFile(file, element) {
202244

203245
runBtn.disabled = false;
204246
runBtn.innerHTML = '<ion-icon name="play"></ion-icon> Run Code';
205-
247+
206248
if (copyBtn) copyBtn.disabled = false;
207249

208250
// Log to terminal
@@ -216,33 +258,70 @@ async function loadFile(file, element) {
216258
}
217259

218260
// Copy Code to Clipboard
261+
// Copy Code to Clipboard with Fallback
219262
async function copyToClipboard() {
220263
if (!currentCode) return;
221264

222-
try {
223-
await navigator.clipboard.writeText(currentCode);
224-
265+
const copySuccess = () => {
225266
// Show success tooltip
226267
const tooltip = document.createElement('div');
227268
tooltip.className = 'copy-tooltip';
228269
tooltip.innerText = 'Copied!';
229270
copyBtn.parentElement.appendChild(tooltip);
230-
271+
231272
setTimeout(() => tooltip.classList.add('show'), 10);
232273
setTimeout(() => {
233274
tooltip.classList.remove('show');
234275
setTimeout(() => tooltip.remove(), 300);
235276
}, 2000);
236277

237278
// Change icon temporarily
238-
const originalIcon = copyBtn.querySelector('ion-icon').name;
239-
copyBtn.querySelector('ion-icon').name = 'checkmark-outline';
240-
setTimeout(() => {
241-
copyBtn.querySelector('ion-icon').name = originalIcon;
242-
}, 2000);
279+
const iconEl = copyBtn.querySelector('ion-icon');
280+
if (iconEl) {
281+
const originalIcon = iconEl.name;
282+
iconEl.name = 'checkmark-outline';
283+
setTimeout(() => {
284+
iconEl.name = originalIcon;
285+
}, 2000);
286+
}
287+
};
288+
289+
// Try modern API first
290+
if (navigator.clipboard && navigator.clipboard.writeText) {
291+
try {
292+
await navigator.clipboard.writeText(currentCode);
293+
copySuccess();
294+
return;
295+
} catch (err) {
296+
console.warn('Clipboard API failed, trying fallback...', err);
297+
}
298+
}
243299

300+
// Fallback: Temporary Textarea
301+
try {
302+
const textArea = document.createElement('textarea');
303+
textArea.value = currentCode;
304+
305+
// Ensure textarea is not visible but part of DOM
306+
textArea.style.position = 'fixed';
307+
textArea.style.left = '-9999px';
308+
textArea.style.top = '0';
309+
document.body.appendChild(textArea);
310+
311+
textArea.focus();
312+
textArea.select();
313+
314+
const successful = document.execCommand('copy');
315+
document.body.removeChild(textArea);
316+
317+
if (successful) {
318+
copySuccess();
319+
} else {
320+
throw new Error('execCommand copy failed');
321+
}
244322
} catch (err) {
245-
console.error('Failed to copy: ', err);
323+
console.error('Copy fallback failed: ', err);
324+
alert('Could not copy code. Please select and copy manually.');
246325
}
247326
}
248327

style.css

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -117,38 +117,68 @@ body {
117117
padding: 15px 0;
118118
}
119119

120-
/* Search Bar */
121-
.search-container {
122-
padding: 5px 15px 15px;
120+
/* Search Bar - Modern Glassmorphism Fix */
121+
.search-wrapper {
122+
padding: 0 20px 15px;
123+
}
124+
125+
.search-box {
123126
position: relative;
124127
display: flex;
125128
align-items: center;
129+
background: rgba(255, 255, 255, 0.04);
130+
border: 1px solid var(--glass-border);
131+
border-radius: 10px;
132+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
126133
}
127134

128-
.search-container ion-icon {
135+
.search-box:focus-within {
136+
background: rgba(255, 255, 255, 0.08);
137+
border-color: var(--primary);
138+
box-shadow: 0 0 0 2px var(--primary-glow);
139+
}
140+
141+
.search-box ion-icon[name="search-outline"] {
129142
position: absolute;
130-
left: 28px;
143+
left: 12px;
131144
color: var(--text-muted);
132-
font-size: 0.9rem;
145+
font-size: 1rem;
146+
pointer-events: none;
133147
}
134148

135149
#file-search {
136150
width: 100%;
137-
background: rgba(255, 255, 255, 0.05);
138-
border: 1px solid var(--glass-border);
139-
border-radius: 8px;
140-
padding: 8px 12px 8px 35px;
151+
background: transparent;
152+
border: none;
153+
padding: 10px 35px 10px 38px;
141154
color: var(--text-main);
142155
font-family: var(--font-heading);
143-
font-size: 0.85rem;
156+
font-size: 0.9rem;
144157
outline: none;
145-
transition: all 0.2s;
146158
}
147159

148-
#file-search:focus {
149-
background: rgba(255, 255, 255, 0.08);
150-
border-color: var(--primary);
151-
box-shadow: 0 0 0 2px var(--primary-glow);
160+
.clear-btn {
161+
position: absolute;
162+
right: 8px;
163+
background: none;
164+
border: none;
165+
color: var(--text-muted);
166+
cursor: pointer;
167+
display: flex;
168+
align-items: center;
169+
justify-content: center;
170+
padding: 4px;
171+
border-radius: 50%;
172+
transition: color 0.2s;
173+
}
174+
175+
.clear-btn:hover {
176+
color: var(--text-main);
177+
}
178+
179+
.search-box input::placeholder {
180+
color: var(--text-muted);
181+
opacity: 0.6;
152182
}
153183

154184
.file-list-container h3 {
@@ -661,4 +691,13 @@ pre[class*="language-"] {
661691
min-height: 150px;
662692
max-height: 60vh;
663693
}
694+
695+
/* Mobile Search Refinements */
696+
.search-wrapper {
697+
padding: 5px 15px 15px;
698+
}
699+
700+
.search-box {
701+
background: rgba(255, 255, 255, 0.07);
702+
}
664703
}

0 commit comments

Comments
 (0)