Skip to content

Commit ea242cf

Browse files
update
1 parent 7a6677a commit ea242cf

File tree

4 files changed

+175
-150
lines changed

4 files changed

+175
-150
lines changed

assets/css/style.css

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
*{
2+
margin: 0;
3+
padding: 0;
4+
box-sizing: border-box;
5+
}
6+
7+
body{
8+
font-family:'Inter',sans-serif;
9+
background:#0f1720;
10+
color:#e6eef8;
11+
line-height:1.6;
12+
}
13+
14+
.wrap{
15+
max-width:1000px;
16+
margin:32px auto;
17+
padding:20px;
18+
}
19+
20+
h1{
21+
text-align:center;
22+
margin-bottom:32px;
23+
}
24+
25+
.articles-grid{
26+
display:grid;
27+
grid-template-columns:repeat(auto-fill,minmax(280px,1fr));
28+
gap:20px;
29+
}
30+
31+
.article-card{background:rgba(255,255,255,0.02);border-radius:14px;padding:20px;border:1px solid rgba(255,255,255,0.03);cursor:pointer;transition:0.2s;}
32+
.article-card:hover{transform:translateY(-4px);box-shadow:0 12px 24px rgba(0,0,0,0.3);}
33+
.article-card h2{margin-top:0;color:#7c5cff;}
34+
.article-card p{color:#9aa4b2;}
35+
.article-card .meta{font-size:0.85rem;color:#9aa4b2;margin-top:10px;}
36+
.article-card .tags{margin-top:8px;font-size:0.8rem;color:#7c5cff;}
37+
38+
/* Modal */
39+
#article-modal{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(15,23,32,0.95);color:#e6eef8;overflow:auto;display:none;flex-direction:column;padding:20px;z-index:999;}
40+
#article-modal .close{align-self:flex-end;font-size:28px;cursor:pointer;margin-bottom:12px;}
41+
#article-modal h1{margin-top:0;}
42+
#article-modal .article-meta{display:flex;gap:12px;flex-wrap:wrap;margin-bottom:16px;color:#9aa4b2;font-size:0.9rem;}
43+
#article-modal img.cover{max-width:100%;border-radius:12px;margin:16px 0;}
44+
.article-body h1,h2,h3,h4,h5,h6{color:#e6eef8;margin-top:1.2rem;margin-bottom:0.6rem;}
45+
.article-body p{margin:0.6rem 0;}
46+
.article-body a{color:#7c5cff;}
47+
.article-body pre{background:#1e293b;padding:12px;border-radius:8px;overflow:auto;}
48+
49+
/* Navigation buttons */
50+
.nav-buttons{display:flex;justify-content:space-between;margin-top:12px;}
51+
.nav-buttons button{background:#7c5cff;color:#fff;border:none;padding:8px 16px;border-radius:8px;cursor:pointer;transition:0.2s;}
52+
.nav-buttons button:hover{background:#5a3edc;}
53+

assets/images/cover.webp

Loading

assets/js/main.js

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// --- GitHub Config ---
2+
const GITHUB_USER = 'codebysushil';
3+
const GITHUB_REPO = "codebysushil.github.io";
4+
const ARTICLES_PATH = 'articles';
5+
const BRANCH = 'main';
6+
7+
// --- Helpers ---
8+
function parseFrontMatter(md){
9+
const fmRegex=/^---\s*([\s\S]*?)\s*---\s*/;
10+
const match=md.match(fmRegex);
11+
if(!match) return {fm:{}, content:md};
12+
const yaml=match[1];
13+
const content=md.slice(match[0].length);
14+
const lines=yaml.split(/\r?\n/).map(l=>l.trim()).filter(Boolean);
15+
const fm={};
16+
for(const line of lines){
17+
const m=line.match(/^([A-Za-z0-9\-_]+)\s*:\s*(.*)$/);
18+
if(m){fm[m[1].toLowerCase()]=m[2].replace(/^["'](.*)["']$/,'$1');}
19+
}
20+
return {fm, content};
21+
}
22+
function formatDate(iso){try{const d=new Date(iso);return isNaN(d)?iso:d.toLocaleDateString(undefined,{year:'numeric',month:'short',day:'numeric'});}catch(e){return iso}}
23+
function estimateReadTime(text){const words=text.trim().split(/\s+/).length;return Math.max(1,Math.round(words/200))+' min read'}
24+
async function incAndGetViews(key){
25+
try{
26+
const res=await fetch(`https://api.countapi.xyz/hit/${GITHUB_USER}-${GITHUB_REPO}/${key}`);
27+
const json=await res.json();
28+
return json.value||0;
29+
}catch(e){return '—'}
30+
}
31+
32+
// --- DOM Elements ---
33+
const grid=document.getElementById('articles-grid');
34+
const modal=document.getElementById('article-modal');
35+
const modalClose=document.getElementById('modal-close');
36+
const modalTitle=document.getElementById('modal-title');
37+
const modalAuthor=document.getElementById('modal-author');
38+
const modalDate=document.getElementById('modal-date');
39+
const modalReadtime=document.getElementById('modal-readtime');
40+
const modalTags=document.getElementById('modal-tags');
41+
const modalViews=document.getElementById('modal-views');
42+
const modalCover=document.getElementById('modal-cover');
43+
const articleContent=document.getElementById('article-content');
44+
const prevBtn=document.getElementById('prev-article');
45+
const nextBtn=document.getElementById('next-article');
46+
47+
let articlesData=[]; // store all articles
48+
let currentIndex=0; // current article in modal
49+
50+
// --- Load articles dynamically ---
51+
(async function(){
52+
try{
53+
const apiUrl = `https://api.github.com/repos/${GITHUB_USER}/${GITHUB_REPO}/contents/${ARTICLES_PATH}?ref=${BRANCH}`;
54+
const res = await fetch(apiUrl);
55+
if(!res.ok) throw new Error('Failed to fetch articles list');
56+
const files = await res.json();
57+
const mdFiles = files.filter(f=>f.name.endsWith('.md'));
58+
59+
for(const file of mdFiles){
60+
try{
61+
const rawUrl = file.download_url;
62+
const resp = await fetch(rawUrl);
63+
if(!resp.ok) continue;
64+
const mdText = await resp.text();
65+
const {fm, content} = parseFrontMatter(mdText);
66+
67+
const article = {
68+
fileName: file.name,
69+
title: fm.title||file.name,
70+
description: fm.description||'',
71+
author: fm.author||'Unknown',
72+
date: fm.date?formatDate(fm.date):'',
73+
tags: fm.tags||'',
74+
cover: fm.cover||'',
75+
content
76+
};
77+
articlesData.push(article);
78+
79+
// create card
80+
const card = document.createElement('div');
81+
card.className = 'article-card';
82+
card.innerHTML=`
83+
<h2>${article.title}</h2>
84+
<p>${article.description}</p>
85+
<div class="meta">By ${article.author} | ${article.date}</div>
86+
<div class="tags">${article.tags}</div>
87+
`;
88+
89+
card.addEventListener('click',()=>openModal(articlesData.indexOf(article)));
90+
grid.appendChild(card);
91+
92+
}catch(err){console.error('Error loading file', file.name, err);}
93+
}
94+
}catch(e){console.error('Failed to fetch articles list', e);}
95+
})();
96+
97+
// --- Modal functions ---
98+
async function openModal(index){
99+
currentIndex=index;
100+
const article=articlesData[index];
101+
modalTitle.textContent=article.title;
102+
modalAuthor.textContent='By '+article.author;
103+
modalDate.textContent=article.date;
104+
modalReadtime.textContent=estimateReadTime(article.content);
105+
modalTags.textContent=article.tags;
106+
if(article.cover){modalCover.src=article.cover; modalCover.style.display='block';} else modalCover.style.display='none';
107+
articleContent.innerHTML=marked.parse(article.content);
108+
Prism.highlightAll();
109+
modalViews.textContent='Views: ' + await incAndGetViews(article.fileName.replace(/\.[^/.]+$/,""));
110+
modal.style.display='flex';
111+
}
112+
113+
// Next / Previous
114+
prevBtn.addEventListener('click',()=>openModal((currentIndex-1+articlesData.length)%articlesData.length));
115+
nextBtn.addEventListener('click',()=>openModal((currentIndex+1)%articlesData.length));
116+
117+
// Close modal
118+
modalClose.addEventListener('click',()=>modal.style.display='none');
119+
window.addEventListener('click',e=>{if(e.target==modal) modal.style.display='none'});
120+

index.html

Lines changed: 2 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -7,35 +7,7 @@
77

88
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap" rel="stylesheet">
99
<link href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism.min.css" rel="stylesheet">
10-
11-
<style>
12-
body{margin:0;font-family:'Inter',sans-serif;background:#0f1720;color:#e6eef8;line-height:1.6;}
13-
.wrap{max-width:1000px;margin:32px auto;padding:20px;}
14-
h1{text-align:center;margin-bottom:32px;}
15-
.articles-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:20px;}
16-
.article-card{background:rgba(255,255,255,0.02);border-radius:14px;padding:20px;border:1px solid rgba(255,255,255,0.03);cursor:pointer;transition:0.2s;}
17-
.article-card:hover{transform:translateY(-4px);box-shadow:0 12px 24px rgba(0,0,0,0.3);}
18-
.article-card h2{margin-top:0;color:#7c5cff;}
19-
.article-card p{color:#9aa4b2;}
20-
.article-card .meta{font-size:0.85rem;color:#9aa4b2;margin-top:10px;}
21-
.article-card .tags{margin-top:8px;font-size:0.8rem;color:#7c5cff;}
22-
23-
/* Modal */
24-
#article-modal{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(15,23,32,0.95);color:#e6eef8;overflow:auto;display:none;flex-direction:column;padding:20px;z-index:999;}
25-
#article-modal .close{align-self:flex-end;font-size:28px;cursor:pointer;margin-bottom:12px;}
26-
#article-modal h1{margin-top:0;}
27-
#article-modal .article-meta{display:flex;gap:12px;flex-wrap:wrap;margin-bottom:16px;color:#9aa4b2;font-size:0.9rem;}
28-
#article-modal img.cover{max-width:100%;border-radius:12px;margin:16px 0;}
29-
.article-body h1,h2,h3,h4,h5,h6{color:#e6eef8;margin-top:1.2rem;margin-bottom:0.6rem;}
30-
.article-body p{margin:0.6rem 0;}
31-
.article-body a{color:#7c5cff;}
32-
.article-body pre{background:#1e293b;padding:12px;border-radius:8px;overflow:auto;}
33-
34-
/* Navigation buttons */
35-
.nav-buttons{display:flex;justify-content:space-between;margin-top:12px;}
36-
.nav-buttons button{background:#7c5cff;color:#fff;border:none;padding:8px 16px;border-radius:8px;cursor:pointer;transition:0.2s;}
37-
.nav-buttons button:hover{background:#5a3edc;}
38-
</style>
10+
<link rel="stylesheet" href="./assets/css/style.css"/>
3911
</head>
4012
<body>
4113
<div class="wrap">
@@ -65,126 +37,6 @@ <h1 id="modal-title"></h1>
6537
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
6638
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/prism.min.js"></script>
6739

68-
<script>
69-
// --- GitHub Config ---
70-
const GITHUB_USER = 'codebysushil';
71-
const GITHUB_REPO = "codebysushil.github.io";
72-
const ARTICLES_PATH = 'articles';
73-
const BRANCH = 'main';
74-
75-
// --- Helpers ---
76-
function parseFrontMatter(md){
77-
const fmRegex=/^---\s*([\s\S]*?)\s*---\s*/;
78-
const match=md.match(fmRegex);
79-
if(!match) return {fm:{}, content:md};
80-
const yaml=match[1];
81-
const content=md.slice(match[0].length);
82-
const lines=yaml.split(/\r?\n/).map(l=>l.trim()).filter(Boolean);
83-
const fm={};
84-
for(const line of lines){
85-
const m=line.match(/^([A-Za-z0-9\-_]+)\s*:\s*(.*)$/);
86-
if(m){fm[m[1].toLowerCase()]=m[2].replace(/^["'](.*)["']$/,'$1');}
87-
}
88-
return {fm, content};
89-
}
90-
function formatDate(iso){try{const d=new Date(iso);return isNaN(d)?iso:d.toLocaleDateString(undefined,{year:'numeric',month:'short',day:'numeric'});}catch(e){return iso}}
91-
function estimateReadTime(text){const words=text.trim().split(/\s+/).length;return Math.max(1,Math.round(words/200))+' min read'}
92-
async function incAndGetViews(key){
93-
try{
94-
const res=await fetch(`https://api.countapi.xyz/hit/${GITHUB_USER}-${GITHUB_REPO}/${key}`);
95-
const json=await res.json();
96-
return json.value||0;
97-
}catch(e){return '—'}
98-
}
99-
100-
// --- DOM Elements ---
101-
const grid=document.getElementById('articles-grid');
102-
const modal=document.getElementById('article-modal');
103-
const modalClose=document.getElementById('modal-close');
104-
const modalTitle=document.getElementById('modal-title');
105-
const modalAuthor=document.getElementById('modal-author');
106-
const modalDate=document.getElementById('modal-date');
107-
const modalReadtime=document.getElementById('modal-readtime');
108-
const modalTags=document.getElementById('modal-tags');
109-
const modalViews=document.getElementById('modal-views');
110-
const modalCover=document.getElementById('modal-cover');
111-
const articleContent=document.getElementById('article-content');
112-
const prevBtn=document.getElementById('prev-article');
113-
const nextBtn=document.getElementById('next-article');
114-
115-
let articlesData=[]; // store all articles
116-
let currentIndex=0; // current article in modal
117-
118-
// --- Load articles dynamically ---
119-
(async function(){
120-
try{
121-
const apiUrl = `https://api.github.com/repos/${GITHUB_USER}/${GITHUB_REPO}/contents/${ARTICLES_PATH}?ref=${BRANCH}`;
122-
const res = await fetch(apiUrl);
123-
if(!res.ok) throw new Error('Failed to fetch articles list');
124-
const files = await res.json();
125-
const mdFiles = files.filter(f=>f.name.endsWith('.md'));
126-
127-
for(const file of mdFiles){
128-
try{
129-
const rawUrl = file.download_url;
130-
const resp = await fetch(rawUrl);
131-
if(!resp.ok) continue;
132-
const mdText = await resp.text();
133-
const {fm, content} = parseFrontMatter(mdText);
134-
135-
const article = {
136-
fileName: file.name,
137-
title: fm.title||file.name,
138-
description: fm.description||'',
139-
author: fm.author||'Unknown',
140-
date: fm.date?formatDate(fm.date):'',
141-
tags: fm.tags||'',
142-
cover: fm.cover||'',
143-
content
144-
};
145-
articlesData.push(article);
146-
147-
// create card
148-
const card = document.createElement('div');
149-
card.className = 'article-card';
150-
card.innerHTML=`
151-
<h2>${article.title}</h2>
152-
<p>${article.description}</p>
153-
<div class="meta">By ${article.author} | ${article.date}</div>
154-
<div class="tags">${article.tags}</div>
155-
`;
156-
157-
card.addEventListener('click',()=>openModal(articlesData.indexOf(article)));
158-
grid.appendChild(card);
159-
160-
}catch(err){console.error('Error loading file', file.name, err);}
161-
}
162-
}catch(e){console.error('Failed to fetch articles list', e);}
163-
})();
164-
165-
// --- Modal functions ---
166-
async function openModal(index){
167-
currentIndex=index;
168-
const article=articlesData[index];
169-
modalTitle.textContent=article.title;
170-
modalAuthor.textContent='By '+article.author;
171-
modalDate.textContent=article.date;
172-
modalReadtime.textContent=estimateReadTime(article.content);
173-
modalTags.textContent=article.tags;
174-
if(article.cover){modalCover.src=article.cover; modalCover.style.display='block';} else modalCover.style.display='none';
175-
articleContent.innerHTML=marked.parse(article.content);
176-
Prism.highlightAll();
177-
modalViews.textContent='Views: ' + await incAndGetViews(article.fileName.replace(/\.[^/.]+$/,""));
178-
modal.style.display='flex';
179-
}
180-
181-
// Next / Previous
182-
prevBtn.addEventListener('click',()=>openModal((currentIndex-1+articlesData.length)%articlesData.length));
183-
nextBtn.addEventListener('click',()=>openModal((currentIndex+1)%articlesData.length));
184-
185-
// Close modal
186-
modalClose.addEventListener('click',()=>modal.style.display='none');
187-
window.addEventListener('click',e=>{if(e.target==modal) modal.style.display='none'});
188-
</script>
40+
<script src="./assets/js/main.js"></script>
18941
</body>
19042
</html>

0 commit comments

Comments
 (0)