Skip to content

Commit 3f9a7b5

Browse files
committed
Added functionalities
comments section, search by tags
1 parent ae3ab1d commit 3f9a7b5

8 files changed

Lines changed: 310 additions & 59 deletions

File tree

about.html

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,9 @@
2121
<article>
2222
<h1>Hello, I'm Robin.</h1>
2323
<p>
24-
This website is a place where I share ideas, projects, thoughts on school, life, and other things that
25-
catch my interest.
26-
I don't like confining my writing to traditional labels like "blog," "journal," or "portfolio," since
27-
that feels restrictive to how and what I want to write.
24+
This site serves as a platform for me to document my thoughts, projects, and anything else I find worth
25+
sharing. Just a little bit of self intro, I'm a freshman at the NYU studying Computer Science and
26+
Mathematics.
2827
</p>
2928

3029
<h2>What You Might Find Here</h2>

api/comments.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// /api/comments.js
2+
export default async function handler(req, res) {
3+
const fs = require('fs');
4+
const path = require('path');
5+
6+
const filePath = path.join(process.cwd(), 'comments.json');
7+
8+
// Ensure file exists
9+
if (!fs.existsSync(filePath)) {
10+
fs.writeFileSync(filePath, '{}');
11+
}
12+
13+
const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
14+
15+
if (req.method === 'GET') {
16+
const { slug } = req.query;
17+
if (!slug) {
18+
return res.status(400).json({ error: "Missing slug" });
19+
}
20+
return res.status(200).json(data[slug] || []);
21+
}
22+
23+
if (req.method === 'POST') {
24+
const { slug, text } = req.body;
25+
if (!slug || !text) {
26+
return res.status(400).json({ error: "Missing slug or text" });
27+
}
28+
if (!data[slug]) {
29+
data[slug] = [];
30+
}
31+
data[slug].push({
32+
text,
33+
timestamp: Date.now(),
34+
});
35+
36+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
37+
return res.status(201).json({ text, timestamp: Date.now() });
38+
}
39+
40+
res.setHeader('Allow', ['GET', 'POST']);
41+
res.status(405).end(`Method ${req.method} Not Allowed`);
42+
}

css/post.css

Lines changed: 78 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
11
/* Shared Typography and Layout */
2+
html {
3+
box-sizing: border-box;
4+
font-size: 16px;
5+
}
6+
7+
*, *::before, *::after {
8+
box-sizing: inherit;
9+
}
10+
211
body {
312
font-family: Georgia, 'Noto Serif', 'Noto Sans SC', 'Microsoft YaHei', 'Hiragino Sans GB', serif;
413
background-color: #fff;
@@ -20,13 +29,15 @@ a:hover {
2029
}
2130

2231
/* Header */
23-
header h1, .header-title {
32+
header h1,
33+
.header-title {
2434
font-size: 2.1rem;
2535
color: #000;
2636
margin-bottom: 0.3rem;
2737
}
2838

29-
header p, .header-row p {
39+
header p,
40+
.header-row p {
3041
font-size: 1.3rem;
3142
color: #444;
3243
margin: 0;
@@ -52,7 +63,8 @@ header p, .header-row p {
5263
font-size: 1.3rem;
5364
}
5465

55-
.post-meta, .post-date {
66+
.post-meta,
67+
.post-date {
5668
font-size: 0.9rem;
5769
color: #777;
5870
}
@@ -71,7 +83,8 @@ article {
7183
margin-top: 3.4rem;
7284
}
7385

74-
article h1, article h2 {
86+
article h1,
87+
article h2 {
7588
color: #000;
7689
margin: 1.3rem 0;
7790
}
@@ -97,21 +110,74 @@ article p {
97110
color: #88aaff;
98111
}
99112

100-
.dark-mode header h1,
101-
.dark-mode article h1,
102-
.dark-mode article h2,
113+
.dark-mode header h1,
114+
.dark-mode article h1,
115+
.dark-mode article h2,
103116
.dark-mode .header-title {
104117
color: #fff;
105118
}
106119

107-
.dark-mode header p,
108-
.dark-mode .header-row p,
109-
.dark-mode .post-meta,
110-
.dark-mode .post-date,
120+
.dark-mode header p,
121+
.dark-mode .header-row p,
122+
.dark-mode .post-meta,
123+
.dark-mode .post-date,
111124
.dark-mode .back-link {
112125
color: #ccc;
113126
}
114127

128+
/*Tags*/
129+
.tags-container {
130+
margin-top: 0.5rem;
131+
}
132+
133+
.post-tag {
134+
display: inline-block;
135+
background-color: #eef;
136+
color: #336;
137+
font-size: 0.75rem;
138+
padding: 0.2rem 0.5rem;
139+
border-radius: 0.5rem;
140+
margin-right: 0.4rem;
141+
margin-top: 0.4rem;
142+
}
143+
144+
.dark-mode .post-tag {
145+
background-color: #223;
146+
color: #ccf;
147+
}
148+
149+
#tag-filter {
150+
margin-bottom: 2rem;
151+
}
152+
153+
#tag-buttons {
154+
margin-top: 0.5rem;
155+
}
156+
157+
.tag-button {
158+
background-color: #ddeeff;
159+
border: none;
160+
border-radius: 0.5rem;
161+
padding: 0.4rem 0.8rem;
162+
margin: 0.2rem;
163+
font-size: 0.85rem;
164+
cursor: pointer;
165+
}
166+
167+
.tag-button:hover {
168+
background-color: #ccddee;
169+
}
170+
171+
.dark-mode .tag-button {
172+
background-color: #334455;
173+
color: #ddeeff;
174+
}
175+
176+
.dark-mode .tag-button:hover {
177+
background-color: #445566;
178+
}
179+
180+
115181
/* Theme Toggle */
116182
#theme-toggle {
117183
background: none;
@@ -131,4 +197,4 @@ article p {
131197

132198
.dark-mode #theme-toggle:hover {
133199
background-color: rgba(255, 255, 255, 0.1);
134-
}
200+
}

index.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
<footer id="footer-placeholder"></footer>
2828

2929
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
30-
<script src="js/main.js"></script>
3130
<script src="js/theme.js"></script>
3231
<script src="js/header-footer.js"></script>
3332
</body>

js/comments.js

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,77 @@
1-
//Comment section
1+
// Vercel serverless function
2+
import { kv } from '@vercel/kv';
3+
4+
export default async function handler(req, res) {
5+
const { method } = req;
6+
7+
if (method === 'GET') {
8+
const slug = req.query.slug;
9+
if (!slug) {
10+
return res.status(400).json({ error: "Missing slug" });
11+
}
12+
const comments = await kv.get(`comments:${slug}`) || [];
13+
return res.status(200).json(comments);
14+
}
15+
16+
if (method === 'POST') {
17+
const { slug, text } = req.body;
18+
if (!slug || !text) {
19+
return res.status(400).json({ error: "Missing slug or text" });
20+
}
21+
const comments = await kv.get(`comments:${slug}`) || [];
22+
const newComment = { text, timestamp: Date.now() };
23+
comments.push(newComment);
24+
await kv.set(`comments:${slug}`, comments);
25+
return res.status(201).json(newComment);
26+
}
27+
28+
res.setHeader('Allow', ['GET', 'POST']);
29+
res.status(405).end(`Method ${method} Not Allowed`);
30+
}
31+
32+
document.addEventListener("DOMContentLoaded", () => {
33+
const slug = new URLSearchParams(window.location.search).get('id');
34+
if (!slug) return;
35+
36+
const commentsList = document.getElementById("comments-list");
37+
const form = document.getElementById("comment-form");
38+
39+
// Load existing comments
40+
fetch(`/api/comments?slug=${slug}`)
41+
.then(res => res.json())
42+
.then(comments => {
43+
comments.forEach(comment => {
44+
const p = document.createElement("p");
45+
p.textContent = comment.text;
46+
commentsList.appendChild(p);
47+
});
48+
})
49+
.catch(err => {
50+
console.error("Failed to load comments", err);
51+
});
52+
53+
// Submit new comment
54+
form.addEventListener("submit", (e) => {
55+
e.preventDefault();
56+
const text = document.getElementById("comment-text").value.trim();
57+
if (text.length === 0) return;
58+
59+
fetch('/api/comments', {
60+
method: 'POST',
61+
headers: {
62+
'Content-Type': 'application/json'
63+
},
64+
body: JSON.stringify({ slug, text })
65+
})
66+
.then(res => res.json())
67+
.then(newComment => {
68+
const p = document.createElement("p");
69+
p.textContent = newComment.text;
70+
commentsList.appendChild(p);
71+
document.getElementById("comment-text").value = "";
72+
})
73+
.catch(err => {
74+
console.error("Failed to post comment", err);
75+
});
76+
});
77+
});

0 commit comments

Comments
 (0)