@@ -3,7 +3,7 @@ import { configs } from '../data/loadConfigs';
33import { Icon , LinkButton } from ' @astrojs/starlight/components' ;
44
55// Simple helper functions
6- function getFeedUrl(config : any , instanceUrl : string , params : Record <string , string > = {}) {
6+ function getFeedUrl(config : { domain : string ; name : string ; url_parameters ? : Record < string , any > } , instanceUrl : string , params : Record <string , string > = {}) {
77 const baseUrl = instanceUrl .endsWith (" /" ) ? instanceUrl : ` ${instanceUrl }/ ` ;
88 let url = ` ${baseUrl }${config .domain }/${config .name }.rss ` ;
99
@@ -17,26 +17,24 @@ function getFeedUrl(config: any, instanceUrl: string, params: Record<string, str
1717 return url ;
1818}
1919
20- // Generate static feed URLs
21- const defaultInstanceUrl = atob (" aHR0cHM6Ly8xLmgyci53b3JrZXJzLmRldi8=" );
20+ // Don't generate static URLs to avoid exposing instance URL in build
2221const staticFeedUrls = configs .map (config => ({
2322 ... config ,
24- staticFeedUrl: getFeedUrl ( config , defaultInstanceUrl )
23+ staticFeedUrl: " # " // Placeholder that will be updated by JavaScript
2524}));
2625---
2726
2827<div class =" feed-directory-container" >
2928 <!-- Progressive Enhancement: Works without JavaScript -->
3029 <div class =" feed-directory__filters" >
31- <label for =" instance-url" >
30+ <label for =" instance-url-input " >
3231 Instance URL
3332 </label >
3433 <input
3534 type =" text"
3635 name =" instance"
37- id =" instance-url"
36+ id =" instance-url-input "
3837 class =" feed-directory__input"
39- value ={ defaultInstanceUrl }
4038 aria-label =" Instance URL"
4139 placeholder =" https://your-instance.com"
4240 />
@@ -73,7 +71,7 @@ const staticFeedUrls = configs.map(config => ({
7371
7472 { config .channel ?.url && (
7573 <div class = " feed-directory__item-source" >
76- <a href = { config .channel .url } target = " _blank" rel = " noopener noreferrer" class = " feed-directory__item-link" >
74+ <a href = { config .channel .url } target = " _blank" rel = " noopener noreferrer nofollow " class = " feed-directory__item-link" >
7775 { config .channel .url }
7876 </a >
7977 </div >
@@ -101,6 +99,7 @@ const staticFeedUrls = configs.map(config => ({
10199 <LinkButton
102100 href = { config .staticFeedUrl }
103101 target = " _blank"
102+ rel = " noopener noreferrer nofollow"
104103 data-feed-url
105104 size = " sm"
106105 class = " feed-directory__action-btn feed-directory__action-btn--rss"
@@ -114,6 +113,7 @@ const staticFeedUrls = configs.map(config => ({
114113 <LinkButton
115114 href = { ` https://github.com/html2rss/html2rss-configs/blob/master/lib/html2rss/configs/${encodeURIComponent (config .domain )}/${encodeURIComponent (config .name )}.yml ` }
116115 target = " _blank"
116+ rel = " noopener noreferrer nofollow"
117117 variant = " secondary"
118118 size = " sm"
119119 class = " feed-directory__action-btn feed-directory__action-btn--edit"
@@ -168,7 +168,7 @@ const staticFeedUrls = configs.map(config => ({
168168</div >
169169
170170<!-- Enhanced functionality with vanilla JavaScript -->
171- <script >
171+ <script is:inline >
172172 // Simple debounce helper
173173 function debounce(func, wait) {
174174 let timeout;
@@ -196,92 +196,112 @@ const staticFeedUrls = configs.map(config => ({
196196 return queryIndex === lowerQuery.length;
197197 }
198198
199- // Main functionality
200- let instanceUrl = document.getElementById('instance-url')?.value || atob("aHR0cHM6Ly8xLmgyci53b3JrZXJzLmRldi8=");
201- const searchInput = document.getElementById('search-input');
202- const feedItems = document.querySelectorAll('[data-domain]');
199+ // Main functionality - wait for DOM to be ready
200+ function initializeFeedDirectory() {
201+ const defaultInstanceUrl = atob("aHR0cHM6Ly8xLmgyci53b3JrZXJzLmRldi8=");
202+ let instanceUrl = defaultInstanceUrl;
203+ const searchInput = document.getElementById('search-input');
204+ const feedItems = document.querySelectorAll('[data-domain]');
203205
204- // Search functionality
205- if (searchInput) {
206- searchInput.addEventListener('input', debounce((e) => {
207- const query = e.target.value.toLowerCase();
208- feedItems.forEach(item => {
209- const searchableText = item.dataset.searchable?.toLowerCase() || '';
210- item.style.display = fuzzyMatch(searchableText, query) ? 'flex' : 'none';
211- });
212- }, 150));
213- }
206+ // Initialize instance URL field with default value
207+ const instanceInput = document.getElementById('instance-url-input');
214208
215- // Instance URL updates
216- const instanceInput = document.getElementById('instance-url');
217- if (instanceInput) {
218- instanceInput.addEventListener('input', debounce((e) => {
219- instanceUrl = e.target.value || atob("aHR0cHM6Ly8xLmgyci53b3JrZXJzLmRldi8=");
220- updateFeedUrls();
221- }, 300));
222- }
209+ if (instanceInput) {
210+ instanceInput.value = defaultInstanceUrl;
211+ instanceUrl = defaultInstanceUrl;
212+ }
213+
214+ // Search functionality
215+ if (searchInput) {
216+ searchInput.addEventListener('input', debounce((e) => {
217+ const query = e.target.value.toLowerCase();
218+ feedItems.forEach(item => {
219+ const searchableText = item.dataset.searchable?.toLowerCase() || '';
220+ item.style.display = fuzzyMatch(searchableText, query) ? 'flex' : 'none';
221+ });
222+ }, 150));
223+ }
224+
225+ // Instance URL updates
226+ if (instanceInput) {
227+ instanceInput.addEventListener('input', debounce((e) => {
228+ instanceUrl = e.target.value || defaultInstanceUrl;
229+ updateFeedUrls();
230+ }, 300));
231+ }
223232
224- // Parameter forms
225- document.querySelectorAll('.feed-directory__configure-btn').forEach(button => {
226- button.addEventListener('click', (e) => {
227- const targetId = e.target.closest('button').dataset.target;
228- const form = document.getElementById(targetId);
229- if (!form) return;
233+ // Parameter forms
234+ document.querySelectorAll('.feed-directory__configure-btn').forEach(button => {
235+ button.addEventListener('click', (e) => {
236+ const targetId = e.target.closest('button').dataset.target;
237+ const form = document.getElementById(targetId);
238+ if (!form) return;
230239
231- const isExpanded = !form.hidden;
232- form.hidden = isExpanded;
233- e.target.closest('button').setAttribute('aria-expanded', !isExpanded);
234- e.target.closest('button').querySelector('span').textContent = isExpanded ? 'Configure' : 'Hide';
240+ const isExpanded = !form.hidden;
241+ form.hidden = isExpanded;
242+ e.target.closest('button').setAttribute('aria-expanded', !isExpanded);
243+ e.target.closest('button').querySelector('span').textContent = isExpanded ? 'Configure' : 'Hide';
235244
236- if (!isExpanded) updateFeedUrls();
245+ if (!isExpanded) updateFeedUrls();
246+ });
237247 });
238- });
239248
240- // Close forms
241- document.querySelectorAll('[data-close-form]').forEach(button => {
242- button.addEventListener('click', (e) => {
243- const form = e.target.closest('.feed-directory__item-params');
244- const toggle = document.querySelector(`[data-target="${form.id}"]`);
245- if (!form || !toggle) return;
249+ // Close forms
250+ document.querySelectorAll('[data-close-form]').forEach(button => {
251+ button.addEventListener('click', (e) => {
252+ const form = e.target.closest('.feed-directory__item-params');
253+ const toggle = document.querySelector(`[data-target="${form.id}"]`);
254+ if (!form || !toggle) return;
246255
247- form.hidden = true;
248- toggle.setAttribute('aria-expanded', 'false');
249- toggle.querySelector('span').textContent = 'Configure';
256+ form.hidden = true;
257+ toggle.setAttribute('aria-expanded', 'false');
258+ toggle.querySelector('span').textContent = 'Configure';
259+ });
250260 });
251- });
252261
253- // Parameter input updates
254- document.querySelectorAll('.feed-directory__param-input').forEach(input => {
255- input.addEventListener('input', debounce(updateFeedUrls, 200));
256- });
262+ // Parameter input updates
263+ document.querySelectorAll('.feed-directory__param-input').forEach(input => {
264+ input.addEventListener('input', debounce(updateFeedUrls, 200));
265+ });
257266
258- // Update feed URLs
259- function updateFeedUrls() {
260- document.querySelectorAll('[data-feed-url]').forEach(link => {
261- const item = link.closest('[data-domain]');
262- if (!item) return;
267+ // Initialize feed URLs on page load
268+ updateFeedUrls();
263269
264- const domain = item.dataset.domain;
265- const name = item.dataset.name;
266- if (!domain || !name) return;
270+ // Update feed URLs
271+ function updateFeedUrls() {
272+ document.querySelectorAll('[data-feed-url]').forEach(link => {
273+ const item = link.closest('[data-domain]');
274+ if (!item) return;
267275
268- // Get parameters
269- const params = {};
270- item.querySelectorAll('[data-param-key]').forEach(input => {
271- if (input.value) params[input.dataset.paramKey] = input.value;
272- });
276+ const domain = item.dataset.domain;
277+ const name = item.dataset.name;
278+ if (!domain || !name) return;
273279
274- // Build URL
275- let url = instanceUrl.endsWith("/") ? instanceUrl : `${instanceUrl}/`;
276- url += `${domain}/${name}.rss`;
280+ // Get parameters
281+ const params = {};
282+ item.querySelectorAll('[data-param-key]').forEach(input => {
283+ if (input.value) params[input.dataset.paramKey] = input.value;
284+ });
277285
278- const queryParams = new URLSearchParams();
279- Object.entries(params).forEach(([key, value]) => queryParams.append(key, value));
280- const queryString = queryParams.toString();
281- if (queryString) url += `?${queryString}`;
286+ // Build URL
287+ let url = instanceUrl.endsWith("/") ? instanceUrl : `${instanceUrl}/`;
288+ url += `${domain}/${name}.rss`;
282289
283- link.href = url;
284- });
290+ const queryParams = new URLSearchParams();
291+ Object.entries(params).forEach(([key, value]) => queryParams.append(key, value));
292+ const queryString = queryParams.toString();
293+ if (queryString) url += `?${queryString}`;
294+
295+ link.href = url;
296+ });
297+ }
298+ }
299+
300+ // Initialize when DOM is ready
301+ if (document.readyState === 'loading') {
302+ document.addEventListener('DOMContentLoaded', initializeFeedDirectory);
303+ } else {
304+ initializeFeedDirectory();
285305 }
286306</script >
287307
@@ -293,18 +313,16 @@ const staticFeedUrls = configs.map(config => ({
293313
294314 /* Filters Section */
295315 .feed-directory__filters {
296- display: grid ;
297- grid-template-columns: 1fr 1fr ;
298- gap: 1.5rem ;
299- margin-bottom: 2rem ;
300- padding: 1.5rem ;
316+ display: flex ;
317+ flex-direction: column ;
318+ gap: 1rem ;
319+ margin-bottom: 1.5rem ;
320+ padding: 1rem ;
301321 background: hsl(var(--sl-color-gray-2));
302322 border: 1px solid hsl(var(--sl-color-gray-4));
303- border-radius: 0.75rem ;
323+ border-radius: 0.5rem ;
304324 }
305325
306-
307-
308326 .feed-directory__input {
309327 width: 100%;
310328 }
@@ -439,17 +457,6 @@ const staticFeedUrls = configs.map(config => ({
439457 border-color: hsl(var(--sl-color-accent));
440458 }
441459
442- /* Mobile-First Design */
443- .feed-directory__filters {
444- display: flex;
445- flex-direction: column;
446- gap: 1rem;
447- margin-bottom: 1.5rem;
448- padding: 1rem;
449- background: hsl(var(--sl-color-gray-2));
450- border: 1px solid hsl(var(--sl-color-gray-4));
451- border-radius: 0.5rem;
452- }
453460
454461 .feed-directory__item {
455462 display: flex;
0 commit comments