Skip to content

Commit 12703aa

Browse files
authored
Merge pull request #20 from vin-im/main
feat: Add permalink anchors for direct section linking
2 parents ee62ea1 + 35389bc commit 12703aa

File tree

2 files changed

+88
-13
lines changed

2 files changed

+88
-13
lines changed

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ A simple web application for looking up WHOIS, IP, and ASN information using fre
2020
- 📋 Clear source attribution for all lookups
2121
- 🔍 DNS resolution for domain IP addresses
2222
- 🔗 URL query parameter support for direct lookups
23+
- 🔖 Permalink anchors for sharing specific result sections
2324

2425
## APIs Used
2526

@@ -114,6 +115,14 @@ You can also perform direct lookups by using the `lookup` query parameter in the
114115
- IP lookup: `http://localhost:3000/?lookup=8.8.8.8`
115116
- ASN lookup: `http://localhost:3000/?lookup=AS13335`
116117

118+
### Permalink Anchors
119+
120+
Each section of the results now has a permalink anchor that allows direct linking to specific information:
121+
- Click the link icon (🔗) next to any section header to copy a direct link to that section
122+
- Share links to specific sections, like nameservers or IP addresses: `http://localhost:3000/?lookup=google.com#nameservers`
123+
- When following a permalink, the page will automatically scroll to the relevant section
124+
- Sections are briefly highlighted when accessed via permalink for better visibility
125+
117126
## Example Queries
118127

119128
- **Domain Lookup**: `google.com`, `europa.eu`, `bbc.co.uk`
@@ -134,4 +143,4 @@ The application automatically handles rate limits by falling back to alternative
134143

135144
## Contributing
136145

137-
Contributions are welcome! Please feel free to submit a Pull Request.
146+
Contributions are welcome! Please feel free to submit a Pull Request.

public/index.html

Lines changed: 78 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,26 @@
115115
margin: 1cm;
116116
}
117117
}
118+
119+
/* Add CSS for permalink icons */
120+
.permalink svg {
121+
transition: opacity 0.2s ease;
122+
}
123+
124+
/* Handle anchor scrolling with fixed header offset */
125+
html {
126+
scroll-padding-top: 2rem;
127+
}
128+
129+
/* Highlight the target section when linked */
130+
:target {
131+
animation: highlight 2s ease;
132+
}
133+
134+
@keyframes highlight {
135+
0% { background-color: rgba(255, 255, 0, 0.2); }
136+
100% { background-color: transparent; }
137+
}
118138
</style>
119139
</head>
120140
<body class="bg-gray-100 min-h-screen dark:bg-gray-900">
@@ -197,8 +217,15 @@ <h1 class="text-4xl font-bold text-gray-800">DumbWhois</h1>
197217

198218
// Domain Info
199219
html += `
200-
<div class="bg-blue-50 p-4 rounded-lg">
201-
<h2 class="text-lg font-bold text-blue-800 mb-2">Domain Information</h2>
220+
<div id="domain-info" class="bg-blue-50 p-4 rounded-lg relative">
221+
<h2 class="text-lg font-bold text-blue-800 mb-2">
222+
Domain Information
223+
<a href="#domain-info" class="permalink" title="Permalink to this section">
224+
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 inline-block ml-1 opacity-50 hover:opacity-100" viewBox="0 0 20 20" fill="currentColor">
225+
<path fill-rule="evenodd" d="M12.586 4.586a2 2 0 112.828 2.828l-3 3a2 2 0 01-2.828 0 1 1 0 00-1.414 1.414 4 4 0 005.656 0l3-3a4 4 0 00-5.656-5.656l-1.5 1.5a1 1 0 101.414 1.414l1.5-1.5zm-5 5a2 2 0 012.828 0 1 1 0 101.414-1.414 4 4 0 00-5.656 0l-3 3a4 4 0 105.656 5.656l1.5-1.5a1 1 0 10-1.414-1.414l-1.5 1.5a2 2 0 11-2.828-2.828l3-3z" clip-rule="evenodd" />
226+
</svg>
227+
</a>
228+
</h2>
202229
<div class="grid grid-cols-1 md:grid-cols-2 gap-2">
203230
<div class="data-row p-2">
204231
<span class="font-semibold">Domain:</span> ${data.ldhName}
@@ -212,8 +239,15 @@ <h2 class="text-lg font-bold text-blue-800 mb-2">Domain Information</h2>
212239
// Status
213240
if (data.status && data.status.length > 0) {
214241
html += `
215-
<div class="bg-green-50 p-4 rounded-lg">
216-
<h2 class="text-lg font-bold text-green-800 mb-2">Domain Status</h2>
242+
<div id="domain-status" class="bg-green-50 p-4 rounded-lg relative">
243+
<h2 class="text-lg font-bold text-green-800 mb-2">
244+
Domain Status
245+
<a href="#domain-status" class="permalink" title="Permalink to this section">
246+
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 inline-block ml-1 opacity-50 hover:opacity-100" viewBox="0 0 20 20" fill="currentColor">
247+
<path fill-rule="evenodd" d="M12.586 4.586a2 2 0 112.828 2.828l-3 3a2 2 0 01-2.828 0 1 1 0 00-1.414 1.414 4 4 0 005.656 0l3-3a4 4 0 00-5.656-5.656l-1.5 1.5a1 1 0 101.414 1.414l1.5-1.5zm-5 5a2 2 0 012.828 0 1 1 0 101.414-1.414 4 4 0 00-5.656 0l-3 3a4 4 0 105.656 5.656l1.5-1.5a1 1 0 10-1.414-1.414l-1.5 1.5a2 2 0 11-2.828-2.828l3-3z" clip-rule="evenodd" />
248+
</svg>
249+
</a>
250+
</h2>
217251
<div class="flex flex-wrap gap-2">
218252
${data.status.map(status =>
219253
`<span class="bg-green-100 text-green-800 px-2 py-1 rounded-full text-sm">${status}</span>`
@@ -225,8 +259,15 @@ <h2 class="text-lg font-bold text-green-800 mb-2">Domain Status</h2>
225259
// IP Addresses
226260
if ((data.ipAddresses?.v4?.length > 0) || (data.ipAddresses?.v6?.length > 0)) {
227261
html += `
228-
<div class="bg-red-50 dark:bg-red-900/20 p-4 rounded-lg">
229-
<h2 class="text-lg font-bold text-red-800 dark:text-red-400 mb-2">IP Addresses</h2>`;
262+
<div id="ip-addresses" class="bg-red-50 dark:bg-red-900/20 p-4 rounded-lg relative">
263+
<h2 class="text-lg font-bold text-red-800 dark:text-red-400 mb-2">
264+
IP Addresses
265+
<a href="#ip-addresses" class="permalink" title="Permalink to this section">
266+
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 inline-block ml-1 opacity-50 hover:opacity-100" viewBox="0 0 20 20" fill="currentColor">
267+
<path fill-rule="evenodd" d="M12.586 4.586a2 2 0 112.828 2.828l-3 3a2 2 0 01-2.828 0 1 1 0 00-1.414 1.414 4 4 0 005.656 0l3-3a4 4 0 00-5.656-5.656l-1.5 1.5a1 1 0 101.414 1.414l1.5-1.5zm-5 5a2 2 0 012.828 0 1 1 0 101.414-1.414 4 4 0 00-5.656 0l-3 3a4 4 0 105.656 5.656l1.5-1.5a1 1 0 10-1.414-1.414l-1.5 1.5a2 2 0 11-2.828-2.828l3-3z" clip-rule="evenodd" />
268+
</svg>
269+
</a>
270+
</h2>`;
230271

231272
if (data.ipAddresses.v4?.length > 0) {
232273
html += `
@@ -264,8 +305,15 @@ <h3 class="text-sm font-semibold text-red-700 dark:text-red-300 mb-2">IPv6</h3>
264305
// Important Dates
265306
if (data.events && data.events.length > 0) {
266307
html += `
267-
<div class="bg-purple-50 p-4 rounded-lg">
268-
<h2 class="text-lg font-bold text-purple-800 mb-2">Important Dates</h2>
308+
<div id="important-dates" class="bg-purple-50 p-4 rounded-lg relative">
309+
<h2 class="text-lg font-bold text-purple-800 mb-2">
310+
Important Dates
311+
<a href="#important-dates" class="permalink" title="Permalink to this section">
312+
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 inline-block ml-1 opacity-50 hover:opacity-100" viewBox="0 0 20 20" fill="currentColor">
313+
<path fill-rule="evenodd" d="M12.586 4.586a2 2 0 112.828 2.828l-3 3a2 2 0 01-2.828 0 1 1 0 00-1.414 1.414 4 4 0 005.656 0l3-3a4 4 0 00-5.656-5.656l-1.5 1.5a1 1 0 101.414 1.414l1.5-1.5zm-5 5a2 2 0 012.828 0 1 1 0 101.414-1.414 4 4 0 00-5.656 0l-3 3a4 4 0 105.656 5.656l1.5-1.5a1 1 0 10-1.414-1.414l-1.5 1.5a2 2 0 11-2.828-2.828l3-3z" clip-rule="evenodd" />
314+
</svg>
315+
</a>
316+
</h2>
269317
<div class="grid grid-cols-1 md:grid-cols-2 gap-2">
270318
${data.events.map(event => `
271319
<div class="data-row p-2">
@@ -280,8 +328,15 @@ <h2 class="text-lg font-bold text-purple-800 mb-2">Important Dates</h2>
280328
// Nameservers
281329
if (data.nameservers && data.nameservers.length > 0) {
282330
html += `
283-
<div class="bg-yellow-50 p-4 rounded-lg">
284-
<h2 class="text-lg font-bold text-yellow-800 mb-2">Nameservers</h2>
331+
<div id="nameservers" class="bg-yellow-50 p-4 rounded-lg relative">
332+
<h2 class="text-lg font-bold text-yellow-800 mb-2">
333+
Nameservers
334+
<a href="#nameservers" class="permalink" title="Permalink to this section">
335+
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 inline-block ml-1 opacity-50 hover:opacity-100" viewBox="0 0 20 20" fill="currentColor">
336+
<path fill-rule="evenodd" d="M12.586 4.586a2 2 0 112.828 2.828l-3 3a2 2 0 01-2.828 0 1 1 0 00-1.414 1.414 4 4 0 005.656 0l3-3a4 4 0 00-5.656-5.656l-1.5 1.5a1 1 0 101.414 1.414l1.5-1.5zm-5 5a2 2 0 012.828 0 1 1 0 101.414-1.414 4 4 0 00-5.656 0l-3 3a4 4 0 105.656 5.656l1.5-1.5a1 1 0 10-1.414-1.414l-1.5 1.5a2 2 0 11-2.828-2.828l3-3z" clip-rule="evenodd" />
337+
</svg>
338+
</a>
339+
</h2>
285340
<div class="grid grid-cols-1 md:grid-cols-2 gap-2">
286341
${data.nameservers.map(ns => `
287342
<div class="data-row p-2">${ns.ldhName}</div>
@@ -556,13 +611,24 @@ <h2 class="text-lg font-bold text-purple-800 mb-2">Registry Information</h2>
556611
}
557612
});
558613

559-
// Check for lookup query parameter on page load
614+
// Check for lookup query parameter and hash on page load
560615
window.addEventListener('load', () => {
561616
const urlParams = new URLSearchParams(window.location.search);
562617
const lookupQuery = urlParams.get('lookup');
563618
if (lookupQuery) {
564619
document.getElementById('queryInput').value = lookupQuery;
565-
performLookup();
620+
performLookup().then(() => {
621+
// After lookup completes, check if there's a hash to scroll to
622+
if (window.location.hash) {
623+
// Small delay to ensure content is rendered
624+
setTimeout(() => {
625+
const targetElement = document.querySelector(window.location.hash);
626+
if (targetElement) {
627+
targetElement.scrollIntoView();
628+
}
629+
}, 500);
630+
}
631+
});
566632
}
567633
});
568634
</script>

0 commit comments

Comments
 (0)