Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ creates a missing post, updates a different post, and leaves an identical post u
- `articles/`: Zenn-compatible Japanese articles
- `article_config.json`: non-secret publishing destination settings
- `images/`: images shared by Zenn, esa exports, and the website
- `content/news/`: standalone bilingual news records
- `content/publications/`: bilingual publication records and their optional News announcements
- `content/activities/`: standalone bilingual activity records
- `content/publications/`: bilingual publication records and their optional activity announcements
- `src/data/`: typed profile, research, education, and content transformation code
- `public/`: files copied directly into the production build

Expand Down
7 changes: 7 additions & 0 deletions content/activities/fukuoka_mitou_2023.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
date: "2023-11-01"
endDate: "2024-01-31"
title:
ja: "福岡未踏 Solve に採択され,LLM-chatbot をチームで開発しました."
en: "Selected for Fukuoka Mitou (Solve) and built LLM-chatbot with a team."
url: "https://note.com/mitou_fukuoka/n/n397fe44895c6"
type: "program"
7 changes: 7 additions & 0 deletions content/activities/q2pec_2024.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
date: "2024-08-16"
endDate: "2024-09-23"
title:
ja: "九州大学工学部の短期留学プログラム Q²PEC で,クイーンズランド大学(オーストラリア)に滞在しました."
en: "Studied at the University of Queensland, Australia, through Kyushu University's Q²PEC short-term program."
url: "https://www.eng.kyushu-u.ac.jp/programs/program-q2pec/"
type: "study-abroad"
2 changes: 1 addition & 1 deletion content/publications/ss2026_27.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ year: 2026
type: "technical-report"
peerReviewed: false
url: "https://ken.ieice.org/ken/paper/202607247cwN/"
news:
activity:
date: "2026-07-24"
title:
ja: "SIGSS 2026年7月研究会で研究発表を行いました."
Expand Down
39 changes: 39 additions & 0 deletions src/components/activities_section.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
import type { Lang } from "@i18n/ui";
import { useTranslations } from "@i18n/utils";
import { getActivities } from "@data/activities";
import Section from "./section.astro";
import ActivityEntry from "./activity_entry.astro";

interface Props {
lang: Lang;
}

const { lang } = Astro.props;
const t = useTranslations(lang);
const activities = await getActivities();
---

<Section id="activities" title={t("section.activities")}>
{
activities.length > 0 ? (
<ul class="activity-list">
{activities.map((item) => (
<ActivityEntry item={item} lang={lang} />
))}
</ul>
) : (
<p class="empty">{t("activities.empty")}</p>
)
}
</Section>

<style>
.activity-list {
list-style: none;
}

.empty {
color: var(--color-text-secondary);
}
</style>
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
---
import type { Lang } from "@i18n/ui";
import type { NewsItem } from "@data/news";
import { pickLocale } from "@i18n/utils";
import { formatActivityPeriod, type Activity } from "@data/activities";
import { pickLocale, useTranslations } from "@i18n/utils";
import OptionalLink from "./optional_link.astro";

interface Props {
item: NewsItem;
item: Activity;
lang: Lang;
}

const { item, lang } = Astro.props;
const t = useTranslations(lang);
const title = pickLocale(item.title, lang);
---

<li class="news-entry">
<time datetime={item.date}>{item.date}</time>
<span class={`badge badge-${item.type}`}>{item.type}</span>
<li class="activity-entry">
<time datetime={item.date}>{formatActivityPeriod(item)}</time>
<span class={`badge badge-${item.type}`}>{t(`activity.type.${item.type}`)}</span>
<OptionalLink href={item.url}>{title}</OptionalLink>
</li>

<style>
.news-entry {
.activity-entry {
display: flex;
align-items: baseline;
gap: 0.75rem;
Expand Down Expand Up @@ -59,7 +60,7 @@ const title = pickLocale(item.title, lang);
}

@media (width < 640px) {
.news-entry {
.activity-entry {
flex-wrap: wrap;
}
}
Expand Down
19 changes: 1 addition & 18 deletions src/components/publications_section.astro
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
---
import type { Lang } from "@i18n/ui";
import { useTranslations } from "@i18n/utils";
import { getNews } from "@data/news";
import { getPublications } from "@data/publications";
import Section from "./section.astro";
import NewsEntry from "./news_entry.astro";
import PublicationEntry from "./publication_entry.astro";

interface Props {
Expand All @@ -14,7 +12,6 @@ interface Props {
const { lang } = Astro.props;
const t = useTranslations(lang);
const publications = await getPublications(lang);
const news = await getNews();
---

<Section id="publications" title={t("section.publications")}>
Expand All @@ -29,27 +26,13 @@ const news = await getNews();
<p class="empty">{t("publications.empty")}</p>
)
}
{
news.length > 0 && (
<ul class="news-list">
{news.map((item) => (
<NewsEntry item={item} lang={lang} />
))}
</ul>
)
}
</Section>

<style>
.pub-list,
.news-list {
.pub-list {
list-style: none;
}

.news-list {
margin-top: 1.5rem;
}

.empty {
color: var(--color-text-secondary);
}
Expand Down
12 changes: 12 additions & 0 deletions src/data/activities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { getCollection } from "astro:content";

import { toActivities } from "./site_records";
import type { Activity } from "./site_records";

export { formatActivityPeriod } from "./site_records";
export type { Activity } from "./site_records";

export async function getActivities(): Promise<Activity[]> {
const entries = await getCollection("siteRecords");
return toActivities(entries.map(({ data }) => data));
}
11 changes: 0 additions & 11 deletions src/data/news.ts

This file was deleted.

1 change: 1 addition & 0 deletions src/data/sections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export const HOME_SECTIONS = [
"works",
"research",
"publications",
"activities",
"blog",
"contact",
] as const;
Expand Down
28 changes: 25 additions & 3 deletions src/data/site_record_schema.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect, test } from "vitest";

import { newsSchema, publicationSchema } from "./site_record_schema";
import { activitySchema, publicationSchema } from "./site_record_schema";

test("rejects unknown publication fields", () => {
const result = publicationSchema.safeParse({
Expand All @@ -19,12 +19,34 @@ test("rejects unknown publication fields", () => {
);
});

test("rejects news dates outside the ISO date format", () => {
const result = newsSchema.safeParse({
test("rejects activity dates outside the ISO date format", () => {
const result = activitySchema.safeParse({
date: "August 12, 2026",
title: { ja: "ニュース", en: "News" },
type: "general",
});

expect(result.success).toBe(false);
});

test("accepts a program activity with an end date", () => {
const result = activitySchema.safeParse({
date: "2024-08-16",
endDate: "2024-09-23",
title: { ja: "短期留学", en: "Study abroad" },
type: "study-abroad",
});

expect(result.success).toBe(true);
});

test("rejects an activity that ends before it starts", () => {
const result = activitySchema.safeParse({
date: "2024-01-31",
endDate: "2023-11-01",
title: { ja: "プログラム", en: "Program" },
type: "program",
});

expect(result.success).toBe(false);
});
17 changes: 11 additions & 6 deletions src/data/site_record_schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@ const localizedTextSchema = z
})
.strict();

export const newsSchema = z
export const activitySchema = z
.object({
date: z.iso.date(),
endDate: z.iso.date().optional(),
title: localizedTextSchema,
url: z.url().optional(),
type: z.enum(["paper", "talk", "award", "general"]),
type: z.enum(["paper", "talk", "award", "program", "study-abroad", "general"]),
})
.strict();
.strict()
.refine(({ date, endDate }) => endDate === undefined || date <= endDate, {
message: "endDate must not precede date",
path: ["endDate"],
});

export const publicationSchema = z
.object({
Expand All @@ -29,12 +34,12 @@ export const publicationSchema = z
pdfUrl: z.url().optional(),
slidesUrl: z.url().optional(),
bibtex: z.string().min(1).optional(),
news: newsSchema.optional(),
activity: activitySchema.optional(),
})
.strict();

export type NewsRecord = z.infer<typeof newsSchema>;
export type ActivityRecord = z.infer<typeof activitySchema>;
export type PublicationRecord = z.infer<typeof publicationSchema>;

export const siteRecordSchema = z.union([publicationSchema, newsSchema]);
export const siteRecordSchema = z.union([publicationSchema, activitySchema]);
export type SiteRecord = z.infer<typeof siteRecordSchema>;
28 changes: 19 additions & 9 deletions src/data/site_records.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { expect, test } from "vitest";

import type { PublicationRecord } from "./site_record_schema";
import { toNewsItems, toPublications } from "./site_records";
import { formatActivityPeriod, toActivities, toPublications } from "./site_records";

test("uses Japanese publication metadata on Japanese pages", () => {
const items = toPublications(
Expand All @@ -25,11 +25,11 @@ test("uses Japanese publication metadata on Japanese pages", () => {
});
});

test("projects a publication announcement into news with the publication URL", () => {
const items = toNewsItems([
test("projects a publication announcement into activities with the publication URL", () => {
const items = toActivities([
publication({
url: "https://example.com/publication",
news: {
activity: {
date: "2026-07-24",
title: {
ja: "研究発表を行いました.",
Expand All @@ -54,10 +54,10 @@ test("projects a publication announcement into news with the publication URL", (
});

test("links a publication announcement to its own event page when given", () => {
const items = toNewsItems([
const items = toActivities([
publication({
url: "https://example.com/publication",
news: {
activity: {
date: "2026-07-24",
title: { ja: "研究発表を行いました.", en: "Presented our work." },
url: "https://example.com/event",
Expand All @@ -69,10 +69,10 @@ test("links a publication announcement to its own event page when given", () =>
expect(items[0]?.url).toBe("https://example.com/event");
});

test("orders projected and standalone news from newest to oldest", () => {
const items = toNewsItems([
test("orders projected and standalone activities from newest to oldest", () => {
const items = toActivities([
publication({
news: {
activity: {
date: "2026-03-15",
title: { ja: "新しい発表", en: "Newer announcement" },
type: "paper",
Expand All @@ -88,6 +88,16 @@ test("orders projected and standalone news from newest to oldest", () => {
expect(items.map(({ date }) => date)).toEqual(["2026-03-15", "2025-08-01"]);
});

test("shows a single-day activity as its month", () => {
expect(formatActivityPeriod({ date: "2026-07-24" })).toBe("2026.07");
});

test("shows an activity period from its start month to its end month", () => {
expect(formatActivityPeriod({ date: "2023-11-01", endDate: "2024-01-31" })).toBe(
"2023.11–2024.01",
);
});

test("formats a publication author list for display", () => {
const items = toPublications(
[
Expand Down
Loading
Loading