Skip to content

Commit dc0845d

Browse files
authored
feat: articles support with post about blazctf 2024
* mdx-based article page & article listing page * responsive images * sitemap.xml, robots.txt generation * rss/atom/json feeds
1 parent ef5e38e commit dc0845d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+3026
-976
lines changed

.github/workflows/nextjs.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,13 @@ jobs:
6565
# If source files changed but packages didn't, rebuild from a prior cache.
6666
restore-keys: |
6767
${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-
68+
- name: Install remark-code-title dependencies
69+
working-directory: mdx-plugins/remark-code-title
70+
run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }}
6871
- name: Install dependencies
6972
run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }}
7073
- name: Build with Next.js
71-
run: ${{ steps.detect-package-manager.outputs.runner }} next build
74+
run: ${{ steps.detect-package-manager.outputs.runner }} next build && ${{ steps.detect-package-manager.outputs.runner }} next-export-optimize-images
7275
- name: Upload artifact
7376
uses: actions/upload-pages-artifact@v3
7477
with:

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
22

33
# dependencies
4-
/node_modules
4+
node_modules/
55
/.pnp
66
.pnp.js
77
.yarn/install-state.gz
@@ -15,6 +15,7 @@
1515

1616
# production
1717
/build
18+
/public/feed.*
1819

1920
# misc
2021
.DS_Store

.prettierrc

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
11
{
2-
"plugins": ["prettier-plugin-tailwindcss"],
3-
"tailwindStylesheet": "./app/globals.css"
2+
"plugins": [
3+
"@trivago/prettier-plugin-sort-imports",
4+
"prettier-plugin-tailwindcss"
5+
],
6+
"tailwindStylesheet": "./app/globals.css",
7+
"importOrder": [
8+
"^@/app/components/(.*)$",
9+
"^@/app/(.*)$",
10+
"^@/(.*)$",
11+
"^./",
12+
"^../",
13+
"^@/public/(.*)$"
14+
],
15+
"importOrderSeparation": true
416
}

app/(articles)/ArticlePreview.tsx

Lines changed: 46 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
"use client";
22

3-
import Context from "@/app/components/Context";
3+
import Picture from "next-export-optimize-images/image";
4+
import type { StaticImageData } from "next/image";
45
import Link from "next/link";
56
import React, { useContext, useState } from "react";
67
import { BiLinkExternal, BiSolidLock } from "react-icons/bi";
78
import { IoEyeOffOutline } from "react-icons/io5";
89

9-
export type Article = {
10+
import Context from "@/app/components/Context";
11+
12+
export type PreviewProps = {
1013
title: string;
11-
description: string;
12-
metadata: {
13-
href?: string;
14-
author: string;
15-
date: string;
16-
};
14+
summary: string;
15+
href?: string;
16+
cover: StaticImageData | string;
17+
coverAlt: string;
18+
author: string;
19+
date: string;
1720
hidden: boolean;
1821
};
1922

@@ -30,25 +33,17 @@ function MaybeLink({
3033
return <React.Fragment>{children}</React.Fragment>;
3134
}
3235

33-
export default function ArticlePreview({
34-
title,
35-
description,
36-
metadata,
37-
hidden,
38-
canvasRef,
39-
}: Article & {
40-
canvasRef: (ref: HTMLCanvasElement) => void;
41-
}) {
36+
export default function ArticlePreview(props: PreviewProps) {
4237
const { canHover } = useContext(Context);
4338
const [shaking, setShaking] = useState(false);
4439
const classShaking = shaking ? "animate-shake" : "";
4540
const [locking, setLocking] = useState(false);
4641

4742
return (
4843
<div
49-
className={`article-preview ${hidden && "article-hidden"} transition-transform duration-300 hover:scale-105 ${hidden ? "hover:cursor-not-allowed" : "hover:cursor-pointer"}`}
44+
className={`article-preview ${props.hidden && "article-hidden"} transition-transform duration-300 hover:scale-105 ${props.hidden ? "hover:cursor-not-allowed" : "hover:cursor-pointer"}`}
5045
onClick={(e) => {
51-
if (!hidden) {
46+
if (!props.hidden) {
5247
return;
5348
}
5449
if (!canHover) {
@@ -61,52 +56,63 @@ export default function ArticlePreview({
6156
e.preventDefault();
6257
}}
6358
>
64-
<MaybeLink href={metadata.href ?? ""}>
59+
<MaybeLink href={props.href ?? ""}>
6560
<div
6661
style={{
6762
backgroundColor: "oklch(from var(--element) l c h / 0.2)",
6863
opacity: locking ? 1 : undefined,
6964
}}
70-
className={`article-preview-card relative flex aspect-16/10 h-auto w-full flex-col items-end justify-between rounded-xl px-6 py-4 transition select-none ${hidden && "opacity-85 grayscale-32"}`}
71-
title={hidden ? undefined : title}
65+
className={`article-preview-card relative aspect-16/10 h-auto w-full rounded-xl px-6 py-4 transition select-none ${props.hidden ? "opacity-85 grayscale-32" : ""}`}
7266
>
73-
<canvas
74-
className="absolute inset-0 h-full w-full rounded-xl object-cover"
75-
ref={canvasRef}
67+
<Picture
68+
className="absolute inset-0 z-0 h-full w-full rounded-xl object-cover"
69+
sizes="100vw, (min-width: 768px) 50vw, (min-width: 1024px) 33vw"
70+
src={props.cover}
71+
alt={props.title}
72+
width={1600}
73+
height={1000}
7674
/>
77-
{hidden && (
75+
{props.hidden && (
7876
<IoEyeOffOutline
7977
className={`article-preview-card-lock absolute top-1/2 left-1/2 -translate-1/2 text-surface ${locking ? "opacity-100" : "opacity-0"} h-12 w-12 transition-opacity duration-300`}
8078
/>
8179
)}
82-
<span className={`font-theme-sans text-xs ${hidden && "opacity-40"}`}>
83-
[ {metadata.date} ]
84-
</span>
85-
<span className={`font-theme-sans text-xs ${hidden && "opacity-40"}`}>
86-
by [ {metadata.author} ]
87-
</span>
8880
</div>
8981
<h2
90-
className={`mt-4 mb-2 flex justify-between font-theme-sans text-xl transition-all duration-300 ${hidden && "break-words opacity-35"}`}
82+
className={`mt-4 mb-2 flex justify-between transition-all duration-300 ${props.hidden && "break-words opacity-35"}`}
9183
style={{ WebkitTextStrokeWidth: "0.01em" }}
9284
>
9385
<span
94-
className={`article-preview-title mx-3 font-theme-sans font-medium ${classShaking}`}
95-
onAnimationEnd={() => hidden && !canHover && setShaking(false)}
86+
className={`article-preview-title mx-3 font-theme-serif text-lg font-semibold lg:text-xl ${classShaking}`}
87+
onAnimationEnd={() =>
88+
props.hidden && !canHover && setShaking(false)
89+
}
9690
>
97-
{title}
91+
{props.title}
9892
</span>
99-
<span className="mx-5 mt-1 font-light">
100-
{hidden ? <BiSolidLock /> : <BiLinkExternal />}
93+
<span className="mx-5 mt-1">
94+
{props.hidden ? <BiSolidLock /> : <BiLinkExternal />}
10195
</span>
10296
</h2>
10397
</MaybeLink>
10498
<hr className="mb-2 h-px border-0 bg-element opacity-15" />
10599
<p
106-
className={`font-theme-sans text-sm font-light ${hidden && "break-words opacity-20"}`}
100+
className={`text-justify font-theme-sans text-base font-light ${props.hidden && "break-words opacity-20"}`}
107101
>
108-
{description}
102+
{props.summary}
109103
</p>
104+
<div className="mt-4 flex justify-between">
105+
<span
106+
className={`font-theme-serif text-base text-raisin-500 dark:text-stone-500 ${props.hidden ? "opacity-40" : ""}`}
107+
>
108+
By {props.author}
109+
</span>
110+
<span
111+
className={`font-theme-serif text-base text-raisin-500 dark:text-stone-500 ${props.hidden ? "opacity-40" : ""}`}
112+
>
113+
{props.date}
114+
</span>
115+
</div>
110116
</div>
111117
);
112118
}

app/(articles)/PreviewBundle.tsx

Lines changed: 0 additions & 27 deletions
This file was deleted.
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
@reference "../../../globals.css";
2+
3+
@media (prefers-color-scheme: dark) {
4+
.markdown-alert {
5+
--color-note-text: #58a6ff;
6+
--color-note-border: #1f6feb;
7+
--color-tip-text: #3fb950;
8+
--color-tip-border: #238636;
9+
--color-important-text: #a371f7;
10+
--color-important-border: #8957e5;
11+
--color-warning-text: #d29922;
12+
--color-warning-border: #9e6a03;
13+
--color-caution-text: #f85149;
14+
--color-caution-border: #da3633;
15+
}
16+
}
17+
18+
@media (prefers-color-scheme: light) {
19+
.markdown-alert {
20+
--color-note-text: #0969da;
21+
--color-note-border: #0969da;
22+
--color-tip-text: #1a7f37;
23+
--color-tip-border: #1f883d;
24+
--color-important-text: #8250df;
25+
--color-important-border: #8250df;
26+
--color-warning-text: #9a6700;
27+
--color-warning-border: #9a6700;
28+
--color-caution-text: #d1242f;
29+
--color-caution-border: #cf222e;
30+
}
31+
}
32+
33+
.markdown-alert .markdown-alert-title {
34+
align-items: center;
35+
display: flex;
36+
font-weight: 500;
37+
line-height: 1;
38+
}
39+
40+
.markdown-alert .markdown-alert-title svg.octicon {
41+
margin-right: 8px !important;
42+
margin-right: var(--base-size-8, 8px) !important;
43+
fill: currentColor;
44+
}
45+
46+
.markdown-alert.markdown-alert-note {
47+
border-left-color: var(--color-note-border);
48+
49+
.markdown-alert-title {
50+
color: var(--color-note-text);
51+
}
52+
}
53+
54+
.markdown-alert.markdown-alert-tip {
55+
border-left-color: var(--color-tip-border);
56+
57+
.markdown-alert-title {
58+
color: var(--color-tip-text);
59+
}
60+
}
61+
62+
.markdown-alert.markdown-alert-important {
63+
border-left-color: var(--color-important-border);
64+
65+
.markdown-alert-title {
66+
color: var(--color-important-text);
67+
}
68+
}
69+
70+
.markdown-alert.markdown-alert-warning {
71+
border-left-color: var(--color-warning-border);
72+
73+
.markdown-alert-title {
74+
color: var(--color-warning-text);
75+
}
76+
}
77+
78+
.markdown-alert.markdown-alert-caution {
79+
border-left-color: var(--color-caution-border);
80+
81+
.markdown-alert-title {
82+
color: var(--color-caution-text);
83+
}
84+
}

0 commit comments

Comments
 (0)