Compare commits

...
2 Commits
Author SHA1 Message Date
Godopu f34e9bad57 refactor(pages,a11y,seo): enhance subpage metadata, semantic HTML, accessibility, and E2E regression tests
- Standardize Next.js Metadata and dynamic generateMetadata across all subpages
- Fix duplicate main landmark in publications page by replacing nested main with div
- Unify Members page spacing (space-y-10 sm:space-y-14, pt-6 md:pt-8) and responsive H2s
- Add aria-label and focus ring to CategoryNav View All links for a11y
- Derive dynamic course history semester ranges in Lectures page
- Add 3 comprehensive E2E test cases (test_08, test_09, test_10) for highlights, active border, CITE brand links, and English H1 headers
2026-08-24 16:10:11 +09:00
Godopu b1640cdac4 feat(publications,standardization,ui): harmonize typography, active category border, and 5 representative highlights
- Refine Publications overview & category detail pages with clean typography and spacing
- Add active border line (border-2 border-cobalt-dark) on active CategoryNav card
- Replace 10 highlights with 5 designated representative publications
- Add Patent subtitle '(IoT Architecture & Protocols, etc ...)'
- Update Footer link label from 'College of IT Engineering' to 'CITE'
- Clean up Standardization & Lectures page header kickers and spacing
2026-08-24 15:55:59 +09:00
15 changed files with 321 additions and 208 deletions
+30
View File
@@ -80,6 +80,10 @@
#default: hermes
# MQTT_CLIENT_ID_PREFIX=hermes
# MQTT keepalive interval (seconds). Used by paho-mqtt client connections.
#default: 60
# MQTT_KEEPALIVE=60
# Log level for MAM runtime components (DEBUG, INFO, WARN, ERROR).
#default: INFO
# MAM_LOG_LEVEL=INFO
@@ -112,6 +116,32 @@
#default: <cwd>/.mam/delegate_job_logs
# DELEGATE_JOB_LOGS_DIR=/path/to/workspace/.mam/delegate_job_logs
# Max attempts to poll for pane renderer quiescence in send_keys_safe.
#default: 20
# SKS_QUIESCENT_TRIES=20
# Interval (seconds) between pane quiescence capture polls.
#default: 0.5
# SKS_QUIESCENT_INTERVAL=0.5
# Consecutive empty captures to conclude unobservable/headless mode early.
#default: 3
# SKS_EMPTY_GIVEUP=3
# Minimum columns a pane must retain after a vertical split (2xK layout engine).
#default: 60
# MAM_MIN_PANE_COLS=60
# Minimum rows a pane must retain after a horizontal split (2xK layout engine).
#default: 20
# MAM_MIN_PANE_ROWS=20
# Maximum number of columns a workspace may grow to before the engine reports
# 'overflow' (which makes lib.sh create a fresh workspace instead of splitting).
# Applies to both measured (GUI) and headless 0x0 layouts.
#default: (unset -> no column cap)
# MAM_MAX_PANE_COLS=3
# ==============================================================================
# deploy / distribution source (for forks/mirrors)
# ==============================================================================
+6 -4
View File
@@ -68,7 +68,9 @@ Strong success criteria let you loop independently. Weak criteria ("make it work
**These guidelines are working if:** fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.
Read [MULTI_AGENT_RULES.md](.agents/MULTI_AGENT_RULES.md) (or [Korean version](.agents/MULTI_AGENT_RULES.ko.md)) and [multi-agent-mux-loop/SKILL.md](.agents/skills/multi-agent-mux-loop/SKILL.md) first before working and follow the instructions for orchestration and collaboration.
<!-- BEGIN MAM ORCHESTRATION -->
# 🤖 Multi-Agent Orchestration Guidelines
Please refer to [.agents/MULTI_AGENT_RULES.md](file://./.agents/MULTI_AGENT_RULES.md) for detailed collaborative rules and state flow constraints.
<!-- END MAM ORCHESTRATION -->
## 5. Orchestrator Scope Guard (O-3)
**Normal vs. Orchestration Modes**
- **Normal Mode**: The orchestrator acts as Main Creator and may edit files directly.
- **Loop Active Mode (`/multi-agent-mux-loop`)**: Direct file mutation is intercepted by `.agents/hooks.json`. Do not seek workarounds when a tool call is denied — delegate the task immediately via `run_loop.sh`.
+43 -44
View File
@@ -1,61 +1,59 @@
import React from "react";
import type { Metadata } from "next";
import { semesters } from "../../content/lectures";
export const metadata: Metadata = {
title: "Lectures",
description: "AI Agent Networking Lab (ANL) lecture courses and curriculum history",
};
export default function LecturesPage() {
const recentSemesters = semesters.slice(0, 3);
const olderSemesters = semesters.slice(3);
const oldestSem = olderSemesters[olderSemesters.length - 1];
const newestOlderSem = olderSemesters[0];
return (
<div className="container-content space-y-12 md:space-y-16 pt-10 md:pt-16 pb-16 md:pb-24">
{/* Header */}
<section className="space-y-3">
<div className="flex items-center gap-3">
<span className="h-px w-8 bg-cobalt-dark"></span>
<span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark font-bold">
Academic Courses
</span>
</div>
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink">
<div className="container-content py-8 sm:py-12 space-y-10 sm:space-y-14">
{/* Page Header */}
<section>
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
LECTURES
</h1>
<p className="text-ink-mute text-base max-w-2xl">
2016 2026 20 / .
</p>
</section>
{/* Recent 3 Semesters */}
<section className="space-y-6 pt-6 md:pt-10 border-t border-line">
<div className="flex justify-between items-end">
<div>
<div className="font-mono text-xs text-cobalt-dark uppercase tracking-widest font-bold">
Recent Courses ({recentSemesters.length} Semesters)
</div>
<h2 className="font-serif text-3xl font-normal text-ink mt-1">
<section className="space-y-6 pt-6 md:pt-8 border-t border-line">
<div className="flex justify-between items-center">
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-cobalt-dark">
Recent Courses
</h2>
</div>
<span className="font-mono text-xs text-ink-mute hidden sm:inline">
Latest 3 Semesters
</span>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="grid grid-cols-1 md:grid-cols-3 gap-5 sm:gap-6">
{recentSemesters.map((sem, idx) => (
<div
key={idx}
className="bg-paper p-6 rounded-lg border border-line space-y-4 flex flex-col justify-between"
className="bg-paper p-5 sm:p-6 rounded-lg border border-line space-y-4 flex flex-col justify-between shadow-sm"
>
<div className="space-y-3">
<div className="flex justify-between items-center border-b border-line pb-2">
<span className="font-serif text-xl font-normal text-ink">
<div className="flex justify-between items-center border-b border-line pb-2.5">
<span className="font-serif text-lg sm:text-xl font-normal text-ink">
{sem.term} {sem.year}
</span>
<span className="font-mono text-xs text-vermillion-dark font-bold">
<span className="font-mono text-[0.7rem] uppercase tracking-wider text-vermillion-dark font-bold px-2 py-0.5 rounded bg-vermillion-dark/10">
Latest
</span>
</div>
<ul className="space-y-2 pt-1">
<ul className="space-y-2.5 pt-1">
{sem.courses.map((c, cIdx) => (
<li key={cIdx} className="space-y-0.5">
<div className="font-serif text-base text-ink">
<div className="font-serif text-sm sm:text-base text-ink leading-snug">
{c.en}
</div>
<div className="font-mono text-xs text-ink-mute">
@@ -66,8 +64,9 @@ export default function LecturesPage() {
</ul>
</div>
<div className="pt-3 border-t border-line/60 font-mono text-xs text-ink-mute">
Course Count: {sem.courses.length}
<div className="pt-3 border-t border-line/60 font-mono text-xs text-ink-mute flex justify-between items-center">
<span>Total Offerings</span>
<span className="font-semibold text-ink">{sem.courses.length} Courses</span>
</div>
</div>
))}
@@ -75,30 +74,30 @@ export default function LecturesPage() {
</section>
{/* Older Semesters (Native <details>/<summary> progressive disclosure) */}
<section className="space-y-6 pt-6 border-t border-line">
<details className="group border border-line rounded-lg bg-paper p-6 transition-all duration-200">
<section className="space-y-6 pt-6 md:pt-8 border-t border-line">
<details className="group border border-line rounded-lg bg-paper p-5 sm:p-6 transition-all duration-200">
<summary className="flex justify-between items-center cursor-pointer list-none select-none">
<div className="space-y-1">
<span className="font-serif text-xl font-normal text-ink">
({olderSemesters.length} )
<span className="font-serif text-lg sm:text-xl font-normal text-ink">
Browse all semesters
</span>
<p className="text-xs text-ink-mute font-mono">
2016 Fall ~ 2024 Spring
Course history from {oldestSem ? `${oldestSem.year} ${oldestSem.term}` : "2004 Spring"} to {newestOlderSem ? `${newestOlderSem.year} ${newestOlderSem.term}` : "2024 Fall"} ({olderSemesters.length} Semesters)
</p>
</div>
<span className="font-mono text-xs text-vermillion-dark font-bold border border-vermillion-dark px-3 py-1.5 rounded group-open:bg-vermillion-dark group-open:text-white dark:group-open:text-ivory transition-colors">
<span className="group-open:hidden"> </span>
<span className="hidden group-open:inline"> </span>
<span className="font-mono text-xs text-vermillion-dark font-bold border border-vermillion-dark px-3.5 py-1.5 rounded group-open:bg-vermillion-dark group-open:text-white dark:group-open:text-ivory transition-colors">
<span className="group-open:hidden">Open </span>
<span className="hidden group-open:inline">Close </span>
</span>
</summary>
<div className="mt-6 pt-6 border-t border-line grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div className="mt-6 pt-6 border-t border-line grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5 sm:gap-6">
{olderSemesters.map((sem, idx) => (
<div
key={idx}
className="bg-ivory p-5 rounded border border-line space-y-3"
className="bg-ivory p-5 rounded-lg border border-line space-y-3 shadow-sm hover:border-cobalt-dark/40 transition-colors"
>
<div className="font-serif text-lg font-normal text-ink border-b border-line pb-1.5 flex justify-between">
<div className="font-serif text-base sm:text-lg font-normal text-ink border-b border-line pb-2 flex justify-between items-center">
<span>
{sem.term} {sem.year}
</span>
@@ -110,7 +109,7 @@ export default function LecturesPage() {
<ul className="space-y-2">
{sem.courses.map((c, cIdx) => (
<li key={cIdx} className="space-y-0.5">
<div className="font-serif text-sm text-ink">
<div className="font-serif text-sm text-ink leading-snug">
{c.en}
</div>
<div className="font-mono text-[0.7rem] text-ink-mute">
+13 -7
View File
@@ -1,7 +1,13 @@
import React from "react";
import type { Metadata } from "next";
import { activeMembers, alumni } from "../../content/members";
import { ProfessorDetailsDialog } from "./_components/ProfessorDetailsDialog";
export const metadata: Metadata = {
title: "Members",
description: "AI Agent Networking Lab (ANL) professor, active researchers, and alumni directory",
};
const DEGREE_LABEL: Record<string, string> = {
Professor: "Professor",
PhD: "Ph. D.",
@@ -24,16 +30,16 @@ export default function MembersPage() {
);
return (
<div className="container-content py-8 sm:py-12 space-y-12 sm:space-y-16">
<div className="container-content py-8 sm:py-12 space-y-10 sm:space-y-14">
{/* Page Header */}
<section>
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink">
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
LAB MEMBERS
</h1>
</section>
{/* Professor Section */}
<section className="space-y-6 pt-6 md:pt-10 border-t border-line">
<section className="space-y-6 pt-6 md:pt-8 border-t border-line">
<div className="bg-paper p-8 rounded-lg border border-line grid grid-cols-1 lg:grid-cols-12 gap-8 items-center">
<div className="lg:col-span-8 space-y-4">
<h2 className="font-serif text-3xl font-normal text-ink">
@@ -77,10 +83,10 @@ export default function MembersPage() {
</section>
{/* Active Researchers Section */}
<section className="space-y-8 pt-6 md:pt-10 border-t border-line">
<section className="space-y-8 pt-6 md:pt-8 border-t border-line">
<div className="flex justify-between items-end">
<div>
<h2 className="font-serif text-3xl font-normal text-cobalt-dark">
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-cobalt-dark">
Active Researchers
</h2>
</div>
@@ -148,10 +154,10 @@ export default function MembersPage() {
</section>
{/* Alumni Section (Native <details>/<summary> progressive disclosure) */}
<section className="space-y-6 pt-6 md:pt-10 border-t border-line">
<section className="space-y-6 pt-6 md:pt-8 border-t border-line">
<div className="flex justify-between items-end">
<div>
<h2 className="font-serif text-3xl font-normal text-ink">
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-ink">
Graduates
</h2>
</div>
+1 -1
View File
@@ -86,7 +86,7 @@ export default function HomePage() {
</div>
<div className="font-mono text-xs text-ink-mute uppercase tracking-wider mt-1 leading-tight">
Patent<br />
<span className="text-[0.68rem] tracking-normal font-sans normal-case text-ink-mute/80">
<span className="text-[0.68rem] tracking-normal font-sans normal-case text-ink-mute">
(IoT Architecture &amp; Protocols)
</span>
</div>
@@ -1,4 +1,5 @@
import React from "react";
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import { CategoryNav } from "../_components/CategoryNav";
import { publications, patents } from "../../../content/publications";
@@ -11,6 +12,17 @@ interface PageProps {
};
}
export function generateMetadata({ params }: PageProps): Metadata {
const currentCat = PUB_CATEGORIES.find((c) => c.slug === params.category);
if (!currentCat) {
return { title: "Publications" };
}
return {
title: `${currentCat.label} | Publications`,
description: `AI Agent Networking Lab (ANL) ${currentCat.label} research inventory and records`,
};
}
// SSG static pre-rendering (● SSG) for Next.js 14
export function generateStaticParams() {
return PUB_CATEGORIES.map((cat) => ({
@@ -55,44 +67,39 @@ export default function PublicationCategoryPage({ params }: PageProps) {
}
return (
<div className="container-content space-y-8 md:space-y-12 pt-10 md:pt-16 pb-16 md:pb-24">
{/* Header */}
<section className="space-y-3">
<div className="flex items-center gap-3">
<span className="h-px w-8 bg-cobalt-dark"></span>
<span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark font-bold">
Publications Category
</span>
</div>
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink">
<div className="container-content py-8 sm:py-12 space-y-10 sm:space-y-14">
{/* Page Header */}
<section className="space-y-1">
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
{currentCat.label}
</h1>
<p className="text-ink-mute text-base max-w-2xl">
{currentCat.desc} {records.length} .
{currentCat.subtitle && (
<p className="font-mono text-sm text-ink-mute">
{currentCat.subtitle}
</p>
)}
</section>
{/* Category Nav */}
{/* Category Nav Cards */}
<section className="pt-6 md:pt-8 border-t border-line">
<CategoryNav currentCategory={slug} />
</section>
{/* Record List Section */}
<section className="space-y-6">
<section className="space-y-6 pt-6 md:pt-8 border-t border-line">
<div className="flex justify-between items-center border-b border-line pb-3">
<span className="font-mono text-xs text-ink-mute uppercase">
Showing {records.length} Pre-rendered Records
</span>
<span className="font-mono text-xs text-vermillion-dark font-bold">
SSG Static HTML
{records.length} Records
</span>
</div>
{records.length === 0 ? (
<div className="bg-paper p-8 rounded-lg border border-line text-center space-y-3">
<p className="font-serif text-lg text-ink">
0.
No records found in this category.
</p>
<p className="text-xs text-ink-mute font-mono">
(D-01 )
Records will be updated periodically.
</p>
</div>
) : (
@@ -116,13 +123,13 @@ export default function PublicationCategoryPage({ params }: PageProps) {
{rec.isPatent ? (
<div className="space-y-1 text-xs text-ink-mute font-mono">
<p className="text-ink">: {rec.inventors}</p>
<p className="text-ink">Inventors: {rec.inventors}</p>
<p>
: {rec.appNo} ({rec.country}, {rec.appDate})
App No: {rec.appNo} ({rec.country}, {rec.appDate})
</p>
{rec.regNo && (
<p className="text-cobalt-dark font-bold">
: {rec.regNo} ({rec.regDate})
Reg No: {rec.regNo} ({rec.regDate})
</p>
)}
</div>
@@ -133,7 +140,7 @@ export default function PublicationCategoryPage({ params }: PageProps) {
<span>{rec.volume}</span>
{rec.kci && (
<span className="bg-cobalt-dark text-ivory text-[0.65rem] px-1.5 py-0.5 rounded font-bold">
KCI
KCI Indexed
</span>
)}
</div>
@@ -1,6 +1,7 @@
import React from "react";
import Link from "next/link";
import { PUB_CATEGORIES } from "../../../content/categories";
import { publications, patents } from "../../../content/publications";
import { PubCategory } from "../../../content/types";
interface CategoryNavProps {
@@ -8,23 +9,94 @@ interface CategoryNavProps {
}
export function CategoryNav({ currentCategory }: CategoryNavProps) {
const getCount = (slug: string) => {
if (slug === "patent") return patents.length;
return publications.filter((p) => p.category === slug).length;
};
return (
<div className="space-y-4">
<div className="flex flex-wrap items-center gap-2 border-b border-line pb-4">
{PUB_CATEGORIES.map((cat) => {
const isActive = currentCategory === cat.slug;
return (
<div className="flex justify-between items-center">
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-cobalt-dark">
Categories
</h2>
{currentCategory && (
<Link
href="/publications"
className="font-mono text-xs text-ink-mute hover:text-ink hover:underline transition-colors"
>
&larr; Overview
</Link>
)}
</div>
<div className="grid gap-5 sm:gap-6 md:grid-cols-3">
{PUB_CATEGORIES.map((cat) => {
const count = getCount(cat.slug);
const isActive = currentCategory === cat.slug;
return (
<div
key={cat.slug}
href={`/publications/${cat.slug}`}
className={`px-4 py-2 rounded text-xs font-mono tracking-wider transition-colors ${
className={`flex flex-col justify-between bg-paper p-5 sm:p-6 rounded-lg transition-all ${
isActive
? "bg-cobalt-dark text-white dark:text-ivory font-bold"
: "bg-paper text-ink border border-line hover:border-cobalt-dark"
? "border-2 border-cobalt-dark shadow-md"
: "border border-line shadow-sm hover:border-cobalt-dark/40"
}`}
>
<div className="space-y-3">
<div className="flex justify-between items-center">
<span
className={`font-mono text-xs font-bold uppercase tracking-wider ${
isActive ? "text-cobalt-dark" : "text-ink-mute"
}`}
>
Category
</span>
{isActive && (
<span className="font-mono text-[0.65rem] font-bold px-2 py-0.5 rounded bg-cobalt-dark/10 text-cobalt-dark uppercase">
Current
</span>
)}
</div>
<h3
className={`font-serif text-xl ${
isActive ? "text-cobalt-dark font-normal" : "text-ink font-normal"
}`}
>
{cat.label}
{cat.subtitle && (
<span className="block text-xs font-mono text-ink-mute mt-1 font-normal">
{cat.subtitle}
</span>
)}
</h3>
</div>
<div className="mt-6 pt-4 border-t border-line flex items-baseline justify-between">
<span className="font-display text-3xl font-bold text-ink">
{count}
<span className="text-xs font-mono text-ink-mute ml-1 font-normal">
{cat.slug === "patent" ? "Patents" : "Papers"}
</span>
</span>
{isActive ? (
<span className="inline-flex items-center text-xs font-bold text-cobalt-dark font-mono">
Active View
</span>
) : (
<Link
href={`/publications/${cat.slug}`}
aria-label={`View all ${cat.label} records`}
className="inline-flex items-center text-xs font-bold text-vermillion-dark hover:underline focus:outline-none focus:ring-1 focus:ring-vermillion-dark rounded"
>
View All &rarr;
</Link>
)}
</div>
</div>
);
})}
</div>
+39 -86
View File
@@ -1,100 +1,53 @@
import Link from "next/link";
import type { Metadata } from "next";
import { CategoryNav } from "./_components/CategoryNav";
import { PUB_CATEGORIES } from "../../content/categories";
import { publications, patents } from "../../content/publications";
import { publications } from "../../content/publications";
export const metadata = {
title: "Publications Overview | ANL",
description: "AI 에이전트 네트워크 연구실(ANL) 연구 실적 개요 및 카테고리별 집계",
export const metadata: Metadata = {
title: "Publications",
description: "AI Agent Networking Lab (ANL) research publications and patents overview",
};
const REPRESENTATIVE_HIGHLIGHT_TITLES = [
"Globally Integrated Trust Authority (GITA) for Resource-Constrained Edge Devices in IoT and 6G",
"Application Level Trust Authority (APPLETA) for Resource-Constrained Edge Devices in IoT and 6G",
"mQUIC: Use of QUIC for Handover Support with Connection Migration in Wireless/Mobile Networks",
"Use of QUIC for CoAP Transport in IoT Networks",
"Use of QUIC for Mobile-Oriented Future Internet (Q-MOFI)",
];
export default function PublicationsIndexPage() {
const totalPubCount = publications.length + patents.length;
// Sort publication records by publishedAt descending to select top 10 highlights
const top10Highlights = [...publications]
.sort((a, b) => (b.publishedAt || "").localeCompare(a.publishedAt || ""))
.slice(0, 10);
const getCount = (slug: string) => {
if (slug === "patent") return patents.length;
return publications.filter((p) => p.category === slug).length;
};
// Selected 5 representative publications
const highlights = REPRESENTATIVE_HIGHLIGHT_TITLES.map((title) =>
publications.find(
(p) => p.title.trim().toLowerCase() === title.trim().toLowerCase()
)
).filter((p): p is (typeof publications)[0] => Boolean(p));
return (
<main className="container-content space-y-8 md:space-y-12 pt-10 md:pt-16 pb-16 md:pb-24">
{/* Header section */}
<section className="space-y-4">
<div className="flex items-center gap-3">
<span className="h-px w-8 bg-cobalt-dark" />
<span className="font-mono text-xs font-bold uppercase tracking-widest text-cobalt-dark">
BIBLIOGRAPHY &amp; INTELLECTUAL PROPERTY
</span>
</div>
<h1 className="headline-ko text-3xl sm:text-4xl text-ink font-bold">
<div className="container-content py-8 sm:py-12 space-y-10 sm:space-y-14">
{/* Page Header */}
<section>
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
PUBLICATIONS
</h1>
<p className="text-sm sm:text-base text-ink-mute max-w-3xl leading-relaxed">
AI (ANL) ({publications.length}) ({patents.length}) {totalPubCount} .
</p>
</section>
{/* Category Navigation */}
<CategoryNav />
{/* Category Summary Cards */}
<section className="grid gap-6 md:grid-cols-3">
{PUB_CATEGORIES.map((cat) => {
const count = getCount(cat.slug);
return (
<div
key={cat.slug}
className="flex flex-col justify-between border border-line bg-paper p-6 rounded-lg transition-colors hover:border-cobalt-dark group"
>
<div className="space-y-3">
<span className="font-mono text-xs text-ink-mute uppercase tracking-wider">
Category
</span>
<h2 className="headline-ko text-xl text-ink font-bold group-hover:text-cobalt-dark transition-colors">
{cat.label}
</h2>
<p className="text-xs text-ink-mute leading-relaxed">
{cat.desc}
</p>
</div>
<div className="mt-6 pt-4 border-t border-line flex items-baseline justify-between">
<span className="font-display text-3xl font-bold text-ink">
{count}
<span className="text-xs font-sans text-ink-mute ml-1 font-normal"></span>
</span>
<Link
href={`/publications/${cat.slug}`}
className="inline-flex items-center text-xs font-bold text-vermillion-dark hover:underline"
>
&rarr;
</Link>
</div>
</div>
);
})}
<section className="pt-6 md:pt-8 border-t border-line">
<CategoryNav />
</section>
{/* Top 10 Highlights Section */}
<section className="space-y-6 pt-6 border-t border-line">
<div className="flex items-center justify-between">
<h2 className="headline-ko text-2xl font-bold text-ink">
(Top 10 Highlights)
{/* Highlights Section */}
<section className="space-y-6 pt-6 md:pt-8 border-t border-line">
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-cobalt-dark">
Highlights
</h2>
<span className="font-mono text-xs text-ink-mute">
RECENT HIGHLIGHTS
</span>
</div>
<div className="divide-y divide-line border border-line bg-ivory rounded-lg overflow-hidden">
{top10Highlights.map((pub, idx) => (
<article key={`${pub.category}-${idx}`} className="p-5 hover:bg-paper/50 transition-colors space-y-2">
<div className="divide-y divide-line border border-line bg-paper rounded-lg overflow-hidden shadow-sm">
{highlights.map((pub, idx) => (
<article key={`${pub.category}-${idx}`} className="p-5 hover:bg-ivory/60 transition-colors space-y-2">
<div className="flex flex-wrap items-center gap-2 text-xs font-mono text-ink-mute">
<span className="px-2 py-0.5 rounded bg-paper border border-line text-ink font-bold">
<span className="px-2 py-0.5 rounded bg-ivory border border-line text-ink font-bold">
{pub.publishedAt?.substring(0, 7) || "Recent"}
</span>
<span>·</span>
@@ -102,11 +55,11 @@ export default function PublicationsIndexPage() {
{pub.doi && (
<>
<span>·</span>
<span className="text-vermillion">DOI: {pub.doi}</span>
<span className="text-vermillion-dark">DOI: {pub.doi}</span>
</>
)}
</div>
<h3 className="font-semibold text-base text-ink leading-snug">
<h3 className="font-serif text-base font-normal text-ink leading-snug">
{pub.title}
</h3>
{pub.authors && (
@@ -115,14 +68,14 @@ export default function PublicationsIndexPage() {
</p>
)}
{pub.venue && (
<p className="text-xs italic text-ink-soft">
{pub.venue}
<p className="text-xs italic text-ink-mute">
{pub.venue}{pub.volume ? `, ${pub.volume}` : ""}
</p>
)}
</article>
))}
</div>
</section>
</main>
</div>
);
}
+14 -23
View File
@@ -1,35 +1,26 @@
import React from "react";
import type { Metadata } from "next";
import { standardsBodies } from "../../content/standards";
export default function StandardizationPage() {
const totalDocs = standardsBodies.reduce(
(acc, body) =>
acc + body.projects.reduce((pAcc, proj) => pAcc + proj.documents.length, 0),
0
);
export const metadata: Metadata = {
title: "Standardization",
description: "AI Agent Networking Lab (ANL) international standardization research and contributions",
};
export default function StandardizationPage() {
return (
<div className="container-content space-y-12 md:space-y-16 pt-10 md:pt-16 pb-16 md:pb-24">
{/* Header */}
<section className="space-y-3">
<div className="flex items-center gap-3">
<span className="h-px w-8 bg-cobalt-dark"></span>
<span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark font-bold">
Open Standards &amp; Contributions
</span>
</div>
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink">
<div className="container-content py-8 sm:py-12 space-y-10 sm:space-y-14">
{/* Page Header */}
<section>
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
International Standardization
</h1>
<p className="text-ink-mute text-base max-w-2xl">
IEC TC100, ISO/IEC JTC1, ITU-T, TTA / {standardsBodies.length} {totalDocs} .
</p>
</section>
{/* Standards Bodies Section */}
<section className="space-y-12 pt-6 md:pt-10 border-t border-line">
<section className="space-y-8 sm:space-y-10 pt-6 md:pt-8 border-t border-line">
{standardsBodies.map((body, idx) => (
<div key={idx} className="bg-paper p-8 rounded-lg border border-line space-y-6">
<div key={idx} className="bg-paper p-6 sm:p-8 rounded-lg border border-line space-y-6 shadow-sm">
<div className="flex flex-wrap justify-between items-start gap-4 border-b border-line pb-4">
<div>
<div className="flex items-center gap-2">
@@ -103,7 +94,7 @@ export default function StandardizationPage() {
</div>
) : (
<div className="text-xs text-ink-mute font-mono italic">
Project specification in progress
</div>
)}
</div>
+1 -1
View File
@@ -101,7 +101,7 @@ export default function Footer() {
rel="noopener noreferrer"
className="inline-flex items-center gap-1 px-2.5 py-1 rounded bg-ivory border border-line text-ink-soft hover:text-cobalt-dark hover:border-cobalt/40 transition-colors"
>
<span>College of IT Engineering</span>
<span>CITE</span>
<span className="text-[0.65rem] text-ink-mute"></span>
</a>
</div>
+3 -3
View File
@@ -130,7 +130,7 @@ export default function Header() {
{/* Brand logo */}
<Link
href="/"
className="group flex items-center gap-3 shrink-0"
className="group flex items-center gap-3 min-w-0 desktop:shrink-0"
onClick={() => setOpen(false)}
>
<div className="relative flex items-center justify-center w-9 h-9 sm:w-10 sm:h-10 rounded-xl overflow-hidden shadow-sm ring-1 ring-ink/10 group-hover:ring-cobalt/40 group-hover:shadow-md group-hover:scale-105 transition-all duration-300 shrink-0">
@@ -158,11 +158,11 @@ export default function Header() {
</svg>
</div>
<div className="flex flex-col leading-none">
<div className="flex flex-col leading-none min-w-0">
<span className="headline-ko text-lg text-ink sm:text-xl font-bold tracking-tight group-hover:text-cobalt-dark transition-colors">
ANL
</span>
<span className="kicker mt-1 transition-colors group-hover:text-cobalt-dark">
<span className="kicker mt-1 transition-colors group-hover:text-cobalt-dark truncate">
AI Agent Networking LAB
</span>
</div>
+2
View File
@@ -3,6 +3,7 @@ import { PubCategory } from "./types";
export interface CategoryInfo {
slug: PubCategory;
label: string;
subtitle?: string;
desc: string;
}
@@ -21,6 +22,7 @@ export const PUB_CATEGORIES: CategoryInfo[] = [
{
slug: "patent",
label: "Patent",
subtitle: "(IoT Architecture & Protocols, etc ...)",
desc: "국내외 지식재산권 출원 및 등록 실적",
},
];
@@ -121,5 +121,56 @@ class TestANLHomepageE2E(unittest.TestCase):
# 4. Assert zero placeholder Funder labels (AC-31)
self.assertNotIn("Funder:", html)
def test_08_publications_highlights_and_category_nav(self):
"""TC-T1-F8: Verify 5 representative highlights and CategoryNav active border in Publications."""
html = self.read_html("publications.html")
# 1. Verify 5 designated representative publications
expected_highlights = [
"Globally Integrated Trust Authority (GITA) for Resource-Constrained Edge Devices in IoT and 6G",
"Application Level Trust Authority (APPLETA) for Resource-Constrained Edge Devices in IoT and 6G",
"mQUIC: Use of QUIC for Handover Support with Connection Migration in Wireless/Mobile Networks",
"Use of QUIC for CoAP Transport in IoT Networks",
"Use of QUIC for Mobile-Oriented Future Internet (Q-MOFI)",
]
for title in expected_highlights:
self.assertIn(title, html, f"Missing representative highlight publication: '{title}'")
# 2. Verify Highlights heading & section
self.assertIn("Highlights", html)
self.assertIn("Categories", html)
self.assertIn("(IoT Architecture &amp; Protocols, etc ...)", html)
# 3. Verify category detail active card border & label
patent_html = self.read_html(os.path.join("publications", "patent.html"))
self.assertIn("border-2 border-cobalt-dark", patent_html)
self.assertIn("Active View ●", patent_html)
self.assertIn("Patent", patent_html)
def test_09_footer_cite_and_brand_links(self):
"""TC-T1-F9: Verify Footer CITE abbreviation and brand/contact elements."""
html = self.read_html("index.html")
# 1. Institution link label CITE
self.assertIn("<span>CITE</span>", html)
self.assertIn("<span>KNU</span>", html)
self.assertIn("<span>CSE</span>", html)
# 2. Advisor contact info
self.assertIn("Prof. Seok-Joo Koh", html)
self.assertIn("sjkoh@knu.ac.kr", html)
def test_10_subpage_layout_and_english_headers(self):
"""TC-T1-F10: Verify English page headers across all subpages."""
subpages = [
("members.html", "LAB MEMBERS"),
("publications.html", "PUBLICATIONS"),
("lectures.html", "LECTURES"),
("standardization.html", "International Standardization"),
]
for rel_path, expected_h1 in subpages:
html = self.read_html(rel_path)
self.assertIn(expected_h1, html, f"Subpage {rel_path} missing H1 '{expected_h1}'")
if __name__ == "__main__":
unittest.main()
+2 -2
View File
@@ -7,10 +7,10 @@
# - .mam.env present --force → overwrite .mam.env from .mam.env.example (backs up to .mam.env.bak).
# - --migrate-legacy → explicitly rename existing .env to .mam.env if absent.
#
# Paths are resolved relative to this script (repo root = parent of scripts/),
# Paths are resolved relative to this script (repo root = parent of deploy/),
# so it works regardless of the caller's cwd.
#
# Usage: scripts/generate-env.sh [--force] [--migrate-legacy] [-h|--help]
# Usage: deploy/generate-env.sh [--force] [--migrate-legacy] [-h|--help]
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"