Dark Mode

Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 5772b87

Browse files
authored andcommitted
Add typeahead search and fix mobile layout for Wikipedia wikitext tool
- Implement typeahead search using Wikipedia prefixsearch API with debouncing - Show dropdown with article suggestions as user types - Support keyboard navigation (arrow keys, Enter, Escape) in dropdown - Auto-fetch wikitext when selecting a suggestion - Fix mobile layout: button now stacks below input on narrow screens
1 parent 7d768cc commit 5772b87

File tree

1 file changed

+181
-3
lines changed
  • wikipedia-wikitext.html

1 file changed

+181
-3
lines changed

wikipedia-wikitext.html

Lines changed: 181 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,66 @@
4242
margin-bottom: 20px;
4343
}
4444

45-
input[type="text"] {
45+
.input-wrapper {
4646
flex: 1;
47+
position: relative;
48+
}
49+
50+
.autocomplete-dropdown {
51+
position: absolute;
52+
top: 100%;
53+
left: 0;
54+
right: 0;
55+
background: white;
56+
border: 2px solid #0066cc;
57+
border-top: none;
58+
border-radius: 0 0 8px 8px;
59+
max-height: 300px;
60+
overflow-y: auto;
61+
z-index: 100;
62+
display: none;
63+
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
64+
}
65+
66+
.autocomplete-dropdown.visible {
67+
display: block;
68+
}
69+
70+
.autocomplete-item {
71+
padding: 10px 16px;
72+
cursor: pointer;
73+
border-bottom: 1px solid #eee;
74+
font-size: 14px;
75+
color: #333;
76+
}
77+
78+
.autocomplete-item:last-child {
79+
border-bottom: none;
80+
}
81+
82+
.autocomplete-item:hover,
83+
.autocomplete-item.selected {
84+
background: #f0f7ff;
85+
}
86+
87+
.autocomplete-item .page-ns {
88+
color: #666;
89+
font-size: 12px;
90+
margin-left: 8px;
91+
}
92+
93+
@media (max-width: 500px) {
94+
.input-group {
95+
flex-direction: column;
96+
}
97+
98+
button {
99+
width: 100%;
100+
}
101+
}
102+
103+
input[type="text"] {
104+
width: 100%;
47105
padding: 12px 16px;
48106
font-size: 16px;
49107
font-family: Helvetica, Arial, sans-serif;
@@ -170,10 +228,13 @@
170228
<body>
171229
<div class="container">
172230
<h1>Wikipedia wikitext fetcherh1>
173-
<p class="subtitle">Paste a Wikipedia URL to fetch its raw wikitext sourcep>
231+
<p class="subtitle">Search for a Wikipedia article or paste a URL to fetch its raw wikitext sourcep>
174232

175233
<div class="input-group">
176-
<input type="text" id="urlInput" placeholder="https://en.wikipedia.org/wiki/Albert_Einstein">
234+
<div class="input-wrapper">
235+
<input type="text" id="urlInput" placeholder="Search Wikipedia or paste a URL..." autocomplete="off">
236+
<div class="autocomplete-dropdown" id="autocompleteDropdown">div>
237+
div>
177238
<button id="fetchBtn">Fetch wikitextbutton>
178239
div>
179240

@@ -204,6 +265,123 @@

Wikipedia wikitext fetcher

204265
const pageTitle = document.getElementById('pageTitle');
205266
const wikitextOutput = document.getElementById('wikitextOutput');
206267
const copyBtn = document.getElementById('copyBtn');
268+
const autocompleteDropdown = document.getElementById('autocompleteDropdown');
269+
270+
let debounceTimer = null;
271+
let selectedIndex = -1;
272+
let currentSuggestions = [];
273+
274+
function isUrl(text) {
275+
return text.startsWith('http://') || text.startsWith('https://') || text.includes('wikipedia.org');
276+
}
277+
278+
async function fetchSuggestions(query) {
279+
if (!query || query.length < 2 || isUrl(query)) {
280+
hideAutocomplete();
281+
return;
282+
}
283+
284+
try {
285+
const apiUrl = `https://en.wikipedia.org/w/api.php?action=query&list=prefixsearch&pssearch=${encodeURIComponent(query)}&pslimit=10&format=json&origin=*`;
286+
const response = await fetch(apiUrl);
287+
const data = await response.json();
288+
289+
if (data.query && data.query.prefixsearch) {
290+
currentSuggestions = data.query.prefixsearch;
291+
showSuggestions(currentSuggestions);
292+
} else {
293+
hideAutocomplete();
294+
}
295+
} catch (err) {
296+
hideAutocomplete();
297+
}
298+
}
299+
300+
function showSuggestions(suggestions) {
301+
if (suggestions.length === 0) {
302+
hideAutocomplete();
303+
return;
304+
}
305+
306+
selectedIndex = -1;
307+
autocompleteDropdown.innerHTML = suggestions.map((item, index) =>
308+
`
${item.title}
`
309+
).join('');
310+
autocompleteDropdown.classList.add('visible');
311+
}
312+
313+
function hideAutocomplete() {
314+
autocompleteDropdown.classList.remove('visible');
315+
autocompleteDropdown.innerHTML = '';
316+
selectedIndex = -1;
317+
currentSuggestions = [];
318+
}
319+
320+
function selectSuggestion(title) {
321+
const wikiUrl = `https://en.wikipedia.org/wiki/${encodeURIComponent(title.replace(/ /g, '_'))}`;
322+
urlInput.value = wikiUrl;
323+
hideAutocomplete();
324+
fetchWikitext();
325+
}
326+
327+
function updateSelection() {
328+
const items = autocompleteDropdown.querySelectorAll('.autocomplete-item');
329+
items.forEach((item, index) => {
330+
item.classList.toggle('selected', index === selectedIndex);
331+
});
332+
if (selectedIndex >= 0 && items[selectedIndex]) {
333+
items[selectedIndex].scrollIntoView({ block: 'nearest' });
334+
}
335+
}
336+
337+
urlInput.addEventListener('input', () => {
338+
clearTimeout(debounceTimer);
339+
const query = urlInput.value.trim();
340+
341+
debounceTimer = setTimeout(() => {
342+
fetchSuggestions(query);
343+
}, 200);
344+
});
345+
346+
urlInput.addEventListener('keydown', (e) => {
347+
const items = autocompleteDropdown.querySelectorAll('.autocomplete-item');
348+
349+
if (!autocompleteDropdown.classList.contains('visible')) {
350+
return;
351+
}
352+
353+
if (e.key === 'ArrowDown') {
354+
e.preventDefault();
355+
selectedIndex = Math.min(selectedIndex + 1, items.length - 1);
356+
updateSelection();
357+
} else if (e.key === 'ArrowUp') {
358+
e.preventDefault();
359+
selectedIndex = Math.max(selectedIndex - 1, -1);
360+
updateSelection();
361+
} else if (e.key === 'Enter' && selectedIndex >= 0) {
362+
e.preventDefault();
363+
const title = currentSuggestions[selectedIndex]?.title;
364+
if (title) {
365+
selectSuggestion(title);
366+
}
367+
} else if (e.key === 'Escape') {
368+
hideAutocomplete();
369+
}
370+
});
371+
372+
autocompleteDropdown.addEventListener('click', (e) => {
373+
const item = e.target.closest('.autocomplete-item');
374+
if (item) {
375+
const title = item.dataset.title;
376+
selectSuggestion(title);
377+
}
378+
});
379+
380+
document.addEventListener('click', (e) => {
381+
if (!e.target.closest('.input-wrapper')) {
382+
hideAutocomplete();
383+
}
384+
});
207385

208386
function extractPageName(url) {
209387
try {

0 commit comments

Comments
(0)