From 5f0ef04356447c40a8a65997168a4a661b12fafd Mon Sep 17 00:00:00 2001 From: furedea <132188853+furedea@users.noreply.github.com> Date: Mon, 19 Oct 2026 14:04:42 +0900 Subject: [PATCH] feat: list works under their own nav item, starting with Effort or Luck MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a works section after research, with a 作品 / Works nav item and a card per work from src/data/works.ts. The first card links to the browser game served at /effort-or-luck/ by its own deployment, so the internal-link check skips the paths of same-origin works. ADR-0015 records the choice. Co-Authored-By: Claude Opus 5.5 --- .../0015-list-separately-deployed-works.md | 12 ++++ scripts/verify_build.mjs | 2 + src/components/works_section.astro | 69 +++++++++++++++++++ src/data/sections.ts | 1 + src/data/works.ts | 20 ++++++ src/i18n/ui.ts | 4 ++ src/pages/[lang]/index.astro | 2 + tests/e2e/internal_links.spec.ts | 10 +++ tests/e2e/site.spec.ts | 14 ++++ 9 files changed, 134 insertions(+) create mode 100644 docs/adr/0015-list-separately-deployed-works.md create mode 100644 src/components/works_section.astro create mode 100644 src/data/works.ts diff --git a/docs/adr/0015-list-separately-deployed-works.md b/docs/adr/0015-list-separately-deployed-works.md new file mode 100644 index 0000000..501933e --- /dev/null +++ b/docs/adr/0015-list-separately-deployed-works.md @@ -0,0 +1,12 @@ +# ADR-0015: List separately deployed works under furedea.com paths + +- Status: Accepted +- Date: 2026-10-19 + +In the context of a works section that links to projects served under this domain, such as +`/effort-or-luck/`, by their own deployments rather than by this site's build, facing an +internal-link check that resolves every same-origin link against the local preview, we decided +for listing works in `src/data/works.ts` and skipping the paths of same-origin works in that +check, and against copying their builds into this site or serving them from subdomains, to keep +each project deploying from its own repository while linking from the home page, accepting that +those links are checked only after the projects deploy. diff --git a/scripts/verify_build.mjs b/scripts/verify_build.mjs index 928677d..2412a23 100644 --- a/scripts/verify_build.mjs +++ b/scripts/verify_build.mjs @@ -42,6 +42,7 @@ const requiredHtml = { "執行 凱斗", "ソフトウェア工学", "https://furedea.com/ja/", + "https://furedea.com/effort-or-luck/", "/en/", "https://github.com/", "https://x.com/", @@ -55,6 +56,7 @@ const requiredHtml = { "Kaito Shigyo", "Software Engineering", "https://furedea.com/en/", + "https://furedea.com/effort-or-luck/", "/ja/", "https://github.com/", "https://x.com/", diff --git a/src/components/works_section.astro b/src/components/works_section.astro new file mode 100644 index 0000000..25dccd5 --- /dev/null +++ b/src/components/works_section.astro @@ -0,0 +1,69 @@ +--- +import type { Lang } from "@i18n/ui"; +import { useTranslations, pickLocale } from "@i18n/utils"; +import { works } from "@data/works"; +import Section from "./section.astro"; +import TagList from "./tag_list.astro"; + +interface Props { + lang: Lang; +} + +const { lang } = Astro.props; +const t = useTranslations(lang); +--- + +
+ +
+ + diff --git a/src/data/sections.ts b/src/data/sections.ts index 9159cf5..22b4c56 100644 --- a/src/data/sections.ts +++ b/src/data/sections.ts @@ -1,6 +1,7 @@ export const HOME_SECTIONS = [ "about", "research", + "works", "publications", "news", "blog", diff --git a/src/data/works.ts b/src/data/works.ts new file mode 100644 index 0000000..69d5e64 --- /dev/null +++ b/src/data/works.ts @@ -0,0 +1,20 @@ +import type { LocalizedText } from "@i18n/ui"; + +interface Work { + title: LocalizedText; + description: LocalizedText; + url: string; + tags: string[]; +} + +export const works: Work[] = [ + { + title: { ja: "努力か,運か", en: "Effort or Luck" }, + description: { + ja: "同じ選択,違う運.100人のあなたが就活の1年を生きるブラウザゲーム.", + en: "Same choice, different luck: 100 versions of you live one job-hunting year. A browser game.", + }, + url: "https://furedea.com/effort-or-luck/", + tags: ["Astro", "React", "TypeScript"], + }, +]; diff --git a/src/i18n/ui.ts b/src/i18n/ui.ts index bd0b817..2a84ef8 100644 --- a/src/i18n/ui.ts +++ b/src/i18n/ui.ts @@ -14,12 +14,14 @@ export const ui = { "nav.home": "ホーム", "nav.about": "概要", "nav.research": "研究", + "nav.works": "作品", "nav.publications": "業績", "nav.news": "ニュース", "nav.blog": "ブログ", "nav.contact": "連絡先", "section.about": "About", "section.research": "Research", + "section.works": "Works", "section.publications": "Publications", "section.news": "News", "section.blog": "Blog", @@ -42,12 +44,14 @@ export const ui = { "nav.home": "Home", "nav.about": "About", "nav.research": "Research", + "nav.works": "Works", "nav.publications": "Publications", "nav.news": "News", "nav.blog": "Blog", "nav.contact": "Contact", "section.about": "About", "section.research": "Research", + "section.works": "Works", "section.publications": "Publications", "section.news": "News", "section.blog": "Blog", diff --git a/src/pages/[lang]/index.astro b/src/pages/[lang]/index.astro index 3c3eba7..6c7e981 100644 --- a/src/pages/[lang]/index.astro +++ b/src/pages/[lang]/index.astro @@ -5,6 +5,7 @@ import BaseLayout from "@layouts/base_layout.astro"; import HeroSection from "@components/hero_section.astro"; import AboutSection from "@components/about_section.astro"; import ResearchSection from "@components/research_section.astro"; +import WorksSection from "@components/works_section.astro"; import PublicationsSection from "@components/publications_section.astro"; import NewsSection from "@components/news_section.astro"; import BlogSection from "@components/blog_section.astro"; @@ -19,6 +20,7 @@ type SectionComponent = (typeof sectionComponents)[HomeSectionId]; const sectionComponents = { about: AboutSection, research: ResearchSection, + works: WorksSection, publications: PublicationsSection, news: NewsSection, blog: BlogSection, diff --git a/tests/e2e/internal_links.spec.ts b/tests/e2e/internal_links.spec.ts index 4f8e437..ef28289 100644 --- a/tests/e2e/internal_links.spec.ts +++ b/tests/e2e/internal_links.spec.ts @@ -3,8 +3,15 @@ import { join } from "node:path"; import { expect, test, type APIRequestContext, type Page } from "@playwright/test"; +import { works } from "../../src/data/works"; + const DIST_DIR = "dist"; const SITE_ORIGIN = "https://furedea.com"; +// Works served under furedea.com by their own deployments, outside this site's build. +const SEPARATELY_DEPLOYED_PATHS = works + .map((work) => new URL(work.url)) + .filter((url) => url.origin === SITE_ORIGIN) + .map((url) => url.pathname); const REFERENCE_SELECTOR = [ "a[href]", "area[href]", @@ -121,6 +128,9 @@ function toInternalReference(sourcePath: string, rawTarget: string): InternalRef if (target.origin !== SITE_ORIGIN) { return undefined; } + if (SEPARATELY_DEPLOYED_PATHS.some((path) => target.pathname.startsWith(path))) { + return undefined; + } return { sourcePath, rawTarget, diff --git a/tests/e2e/site.spec.ts b/tests/e2e/site.spec.ts index 9fbf012..36fc86a 100644 --- a/tests/e2e/site.spec.ts +++ b/tests/e2e/site.spec.ts @@ -2,6 +2,7 @@ import { expect, test } from "@playwright/test"; import { profile } from "../../src/data/profile"; import { site } from "../../src/data/site"; +import { works } from "../../src/data/works"; const linkByLabel = Object.fromEntries( profile.socialLinks.map((link) => [link.platform, link.url]), @@ -57,6 +58,19 @@ test("publishes the profile email address", async ({ page }) => { ); }); +test("lists works under their own nav item, each card linking to the work", async ({ page }) => { + for (const lang of ["ja", "en"] as const) { + await page.goto(`/${lang}/`); + + await expect(page.locator('.site-nav a[href$="#works"]')).toHaveCount(1); + for (const work of works) { + const card = page.locator(".work-card", { hasText: work.title[lang] }); + await expect(card).toHaveAttribute("href", work.url); + await expect(card).toContainText(work.description[lang]); + } + } +}); + test("renders news collection records", async ({ page }) => { await page.goto("/ja/");