Skip to content

Commit b7633bd

Browse files
authored
Merge pull request #42 from iamtatsuki05/develop
Sync: Develop to Main
2 parents 83088eb + 7053122 commit b7633bd

Some content is hidden

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

59 files changed

+1210
-341
lines changed

.github/workflows/ci-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,4 @@ jobs:
5555
- name: Install Cypress binary
5656
run: bun run e2e:install
5757
- name: Run E2E tests
58-
run: bunx start-server-and-test "bun run dev:test" http://127.0.0.1:3000 "bunx cypress run --e2e --browser ${{ matrix.browser }}"
58+
run: bunx start-server-and-test "bun run e2e:dev" http://127.0.0.1:3000 "bunx cypress run --e2e --browser ${{ matrix.browser }}"

bun.lockb

-1.76 KB
Binary file not shown.

next-sitemap.config.mts

Lines changed: 0 additions & 97 deletions
This file was deleted.

package.json

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,24 @@
55
"packageManager": "[email protected]",
66
"scripts": {
77
"dev": "bun scripts/ensure-favicon.ts && next dev",
8-
"dev:test": "bun scripts/ensure-favicon.ts && next dev --port 3000",
9-
"build": "bun scripts/ensure-favicon.ts && next build && next-image-export-optimizer && bun run gen:sitemap && bun scripts/generate-rss.ts",
8+
"build": "bun scripts/ensure-favicon.ts && next build && next-image-export-optimizer && bun scripts/generate-rss.ts",
109
"start": "serve out -p 3000",
1110
"lint": "eslint .",
12-
"gen:sitemap": "bun build next-sitemap.config.mts --target node --outfile .next-sitemap.config.mjs && next-sitemap --config .next-sitemap.config.mjs && rm .next-sitemap.config.mjs",
13-
"storybook": "storybook dev -p 6006",
14-
"storybook:build": "storybook build",
11+
"test": "bun run vitest:run && bun run e2e:run",
1512
"vitest:run": "vitest run --pool threads --poolOptions.threads.singleThread",
1613
"vitest:watch": "vitest",
17-
"e2e:run": "bun run e2e:install && bunx start-server-and-test \"bun run dev:test\" http://127.0.0.1:3000 \"bunx cypress run --e2e --browser chrome\"",
18-
"e2e:open": "bun run e2e:install && bunx start-server-and-test \"bun run dev:test\" http://127.0.0.1:3000 \"bunx cypress open --e2e\"",
14+
"e2e:dev": "bun scripts/ensure-favicon.ts && next dev --port 3000",
15+
"e2e:install": "bunx cypress install",
16+
"e2e:run": "bun run e2e:install && bunx start-server-and-test \"bun run e2e:dev\" http://127.0.0.1:3000 \"bunx cypress run --e2e --browser chrome\"",
17+
"e2e:open": "bun run e2e:install && bunx start-server-and-test \"bun run e2e:dev\" http://127.0.0.1:3000 \"bunx cypress open --e2e\"",
1918
"e2e:chrome": "bunx cypress run --e2e --browser chrome",
2019
"e2e:edge": "bunx cypress run --e2e --browser edge",
2120
"e2e:electron": "bunx cypress run --e2e --browser electron",
2221
"e2e:firefox": "bunx cypress run --e2e --browser firefox",
23-
"e2e:install": "bunx cypress install",
2422
"lhci:mobile": "lhci autorun",
2523
"lhci:desktop": "lhci autorun --collect.settings.preset=desktop",
26-
"test": "bun run vitest:run && bun run e2e:run"
24+
"storybook": "storybook dev -p 6006",
25+
"storybook:build": "storybook build"
2726
},
2827
"dependencies": {
2928
"babel-plugin-react-compiler": "^1.0.0",
@@ -73,7 +72,6 @@
7372
"eslint": "9.35.0",
7473
"eslint-config-next": "15.5.2",
7574
"jsdom": "26.1.0",
76-
"next-sitemap": "^4.2.3",
7775
"postcss": "8.5.6",
7876
"serve": "14.2.5",
7977
"start-server-and-test": "2.0.5",

src/app/(site)/_components/LinksPage.tsx

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,35 @@ import { getLinks } from '@/lib/data/links';
22
import type { Locale } from '@/lib/i18n';
33
import { linksPageCopy } from '@/app/(site)/_config/pageCopy';
44
import { LinkGrid } from '@/components/links/LinkGrid';
5+
import { SectionShell } from '@/components/home/SectionShell';
6+
import { SectionHeader } from '@/components/home/sections/SectionHeader';
57

68
type LinkItem = Awaited<ReturnType<typeof getLinks>>[number];
79

810
export async function LinksPage({ locale }: { locale: Locale }) {
911
const copy = linksPageCopy[locale];
1012
const links = await getLinks();
1113
const groups = groupBy(links, (l) => l.category || copy.groupFallback);
12-
const mobileLimit = 3;
14+
const tones: Array<'blue' | 'lilac' | 'amber' | 'teal'> = ['blue', 'lilac', 'amber', 'teal'];
1315

1416
return (
1517
<div className="space-y-6">
1618
<div className="text-sm opacity-70">{copy.breadcrumb}</div>
1719
<h1 className="text-3xl font-bold">{copy.heading}</h1>
18-
{Object.entries(groups).map(([cat, items]) => (
19-
<section key={cat} className="space-y-2">
20-
<h2 className="text-xl font-semibold">{cat}</h2>
21-
<LinkGrid
22-
items={items}
23-
mobileLimit={mobileLimit}
24-
showDescription
25-
moreLabel={copy.moreLabel}
26-
iconSize={48}
27-
gridClassName="grid grid-cols-2 sm:grid-cols-3 gap-4"
28-
/>
29-
</section>
30-
))}
20+
{Object.entries(groups).map(([cat, items], idx) => {
21+
const tone = tones[idx % tones.length] as 'blue' | 'lilac' | 'amber' | 'teal';
22+
return (
23+
<SectionShell key={cat} tone={tone}>
24+
<SectionHeader title={cat} tone={tone} />
25+
<LinkGrid
26+
items={items}
27+
showDescription
28+
iconSize={48}
29+
gridClassName="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4"
30+
/>
31+
</SectionShell>
32+
);
33+
})}
3134
</div>
3235
);
3336
}

src/app/blogs/[slug]/page.tsx

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
import type { Metadata } from 'next';
21
import { notFound } from 'next/navigation';
2+
import type { Metadata } from 'next';
3+
import Image from 'next/image';
34
import { getAllPosts, getPostBySlug } from '@/lib/content/blog';
45
import { formatDate } from '@/lib/date';
56
import { withBasePath } from '@/lib/url';
7+
import { absoluteUrl } from '@/lib/seo';
68
import { CodeCopyClient } from '@/components/site/CodeCopyClient';
79
import { EmbedsClient } from '@/components/site/EmbedsClient';
810
import { buildArticleJsonLd, buildPageMetadata } from '@/lib/seo';
11+
import { ShareButtons } from '@/components/blogs/ShareButtons';
12+
import { BlogToc } from '@/components/blogs/BlogToc';
913

1014
type Params = { slug: string };
1115

@@ -49,6 +53,7 @@ export default async function BlogPostPage({ params }: { params: Promise<Params>
4953
if (!post) return notFound();
5054

5155
const { title, date, updated, html, summary, headerImage, headerAlt, thumbnail, tags } = post;
56+
const shareUrl = absoluteUrl(`/blogs/${slug}/`);
5257
const images = [headerImage, thumbnail].filter((src): src is string => Boolean(src));
5358
const articleJsonLd = buildArticleJsonLd({
5459
title,
@@ -61,28 +66,35 @@ export default async function BlogPostPage({ params }: { params: Promise<Params>
6166
});
6267

6368
return (
64-
<article className="prose dark:prose-invert max-w-none">
65-
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(articleJsonLd) }} />
66-
{headerImage ? (
67-
<img
68-
src={withBasePath(headerImage)}
69-
alt={headerAlt || title}
70-
className="mb-4 w-full h-auto rounded-sm border border-gray-200 dark:border-gray-700"
71-
loading="eager"
72-
decoding="async"
73-
fetchPriority="high"
74-
referrerPolicy="no-referrer"
75-
/>
76-
) : null}
77-
<h1>{title}</h1>
78-
<p className="mt-0! text-sm opacity-70">
79-
{formatDate(date, 'ja')}
80-
{updated ? `(更新: ${formatDate(updated, 'ja')})` : ''}
81-
</p>
82-
<div dangerouslySetInnerHTML={{ __html: html || '' }} />
83-
<CodeCopyClient />
84-
<EmbedsClient />
85-
</article>
69+
<div className="lg:grid lg:grid-cols-[minmax(0,3fr)_minmax(240px,1fr)] lg:gap-8">
70+
<article id="blog-article" className="prose dark:prose-invert max-w-none">
71+
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(articleJsonLd) }} />
72+
{headerImage ? (
73+
<div className="relative mb-4 aspect-[16/9] w-full overflow-hidden rounded-sm border border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-900">
74+
<Image
75+
src={withBasePath(headerImage)!}
76+
alt={headerAlt || title}
77+
fill
78+
className="object-cover"
79+
sizes="(max-width: 640px) 100vw, 720px"
80+
loading="lazy"
81+
priority={false}
82+
referrerPolicy="no-referrer"
83+
/>
84+
</div>
85+
) : null}
86+
<h1>{title}</h1>
87+
<p className="mt-0! text-sm opacity-70">
88+
{formatDate(date, 'ja')}
89+
{updated ? `(更新: ${formatDate(updated, 'ja')})` : ''}
90+
</p>
91+
<ShareButtons url={shareUrl} title={title} className="my-4" />
92+
<div dangerouslySetInnerHTML={{ __html: html || '' }} />
93+
<CodeCopyClient />
94+
<EmbedsClient />
95+
</article>
96+
<BlogToc containerId="blog-article" />
97+
</div>
8698
);
8799
}
88100

src/app/blogs/page.tsx

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Suspense } from 'react';
33
import type { Locale } from '@/lib/i18n';
44
import { getAllPosts } from '@/lib/content/blog';
55
import { BlogsClient } from './sections/BlogsClient';
6-
import { buildPageMetadata, defaultLanguageAlternates } from '@/lib/seo';
6+
import { buildPageMetadata, buildCollectionPageJsonLd, buildBreadcrumbJsonLd, defaultLanguageAlternates, absoluteUrl } from '@/lib/seo';
77
import { blogsPageCopy } from '@/app/(site)/_config/pageCopy';
88
import { getPageMeta } from '@/lib/seo/metaConfig';
99

@@ -21,13 +21,36 @@ export const metadata: Metadata = buildPageMetadata({
2121

2222
export default async function BlogIndex() {
2323
const posts = await getAllPosts();
24+
const collectionJsonLd = buildCollectionPageJsonLd({
25+
path: '/blogs/',
26+
name: 'Blog',
27+
description: '技術ブログと記事',
28+
itemCount: posts.length,
29+
});
30+
const breadcrumbJsonLd = buildBreadcrumbJsonLd({
31+
items: [
32+
{ name: 'Home', url: absoluteUrl('/') },
33+
{ name: 'Blog', url: absoluteUrl('/blogs/') },
34+
],
35+
});
36+
2437
return (
25-
<div className="space-y-4">
26-
<div className="text-sm opacity-70">{copy.breadcrumb}</div>
27-
<h1 className="text-3xl font-bold">{copy.heading}</h1>
28-
<Suspense fallback={null}>
29-
<BlogsClient posts={posts} locale={DEFAULT_LOCALE} />
30-
</Suspense>
31-
</div>
38+
<>
39+
<script
40+
type="application/ld+json"
41+
dangerouslySetInnerHTML={{ __html: JSON.stringify(collectionJsonLd) }}
42+
/>
43+
<script
44+
type="application/ld+json"
45+
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbJsonLd) }}
46+
/>
47+
<div className="space-y-4">
48+
<div className="text-sm opacity-70">{copy.breadcrumb}</div>
49+
<h1 className="text-3xl font-bold">{copy.heading}</h1>
50+
<Suspense fallback={null}>
51+
<BlogsClient posts={posts} locale={DEFAULT_LOCALE} />
52+
</Suspense>
53+
</div>
54+
</>
3255
);
3356
}

0 commit comments

Comments
 (0)