feat(header): add smart sticky scroll animation and styling refinements
- Implement RAF-throttled smart sticky scroll interaction on app header - Add flow-root BFC isolation, vertical margin, and mobile menu scroll container - Update brand logo kicker hover color to primary accent (cobalt-dark) - Fix theme script hydration warning with suppressHydrationWarning - Add favicon.ico and icon.svg to resolve root /favicon.ico 404 - Add MAM orchestration guidelines, env generation script, and templates
This commit is contained in:
+12
-1
@@ -6,4 +6,15 @@
|
||||
.agents/
|
||||
|
||||
# Binary zip archives
|
||||
*.zip
|
||||
*.zip
|
||||
# >>> MAM managed block (managed by install.sh — do not edit) >>>
|
||||
/.venv/
|
||||
/.mam/
|
||||
/.mam_deploy/
|
||||
/.mam.env
|
||||
/.mam.env.*
|
||||
!/.mam.env.example
|
||||
/.cache/multi-agent-mux-monitor/
|
||||
/.mam-skill-backup.*/
|
||||
CURRENT_JOB.md
|
||||
# <<< MAM managed block <<<
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
# ---------------------------------------------------------------------------
|
||||
# .mam.env.example — committable template for the multi-agent-mux-* skills
|
||||
#
|
||||
# This file is tracked in git and contains NO secrets. To get a working local
|
||||
# config, copy it to `.mam.env` (which is git-ignored) and edit as needed:
|
||||
#
|
||||
# scripts/generate-env.sh # creates .mam.env from this template if absent
|
||||
# # or manually: cp .mam.env.example .mam.env
|
||||
#
|
||||
# Every variable below is OPTIONAL. The skills already resolve sane defaults
|
||||
# (shown after each `#default:` line), so an unset/commented variable just keeps
|
||||
# the built-in behaviour. Uncomment + edit only the ones you want to override.
|
||||
#
|
||||
# SECURITY: never put real secrets in this template. Secret-bearing vars use a
|
||||
# `replace_me` placeholder — fill them in only in your local `.mam.env`.
|
||||
# SECURITY NOTE:
|
||||
# The default MQTT broker (broker.hivemq.com) is public and unencrypted. Job event
|
||||
# payloads (including task details) sent to the default broker can be read by anyone.
|
||||
# For production or private workloads, configure a private broker with TLS and Auth below.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# ===========================================================================
|
||||
# Workspace / runtime paths
|
||||
# ===========================================================================
|
||||
|
||||
# Single source of truth for the agent session registry YAML.
|
||||
#default: <workspace>/.mam/agent-sessions.yaml
|
||||
# AGENT_SESSIONS_YAML=/path/to/workspace/.mam/agent-sessions.yaml
|
||||
|
||||
# Root directory that holds Claude Code per-project conversation logs (*.jsonl).
|
||||
#default: $HOME/.claude/projects
|
||||
# CLAUDE_PROJECT_DIR=$HOME/.claude/projects
|
||||
|
||||
# Directory scanned for per-session launcher wrappers (~/.local/bin/<session>).
|
||||
#default: $HOME/.local/bin
|
||||
# LOCAL_BIN=$HOME/.local/bin
|
||||
|
||||
# Isolated Herdr session socket name (`herdr --session <name>`).
|
||||
#default: mam-<workspace-slug>
|
||||
# HERDR_SESSION_NAME=mam-multi-agent-mux
|
||||
|
||||
# Legacy Herdr server identifier (DEPRECATED: use HERDR_SESSION_NAME instead).
|
||||
# Written into .mam.env by install.sh as an active default.
|
||||
#default: multi-agent-mux
|
||||
# HERDR_SERVER_NAME=multi-agent-mux
|
||||
|
||||
# Path to the Herdr Unix domain socket.
|
||||
#default: $HOME/.config/herdr/herdr.sock
|
||||
# HERDR_SOCKET_PATH=$HOME/.config/herdr/herdr.sock
|
||||
|
||||
# ===========================================================================
|
||||
# delegate-job / MQTT broker
|
||||
# ===========================================================================
|
||||
|
||||
# MQTT broker host the delegate-job publisher/subscriber connects to.
|
||||
#default: broker.hivemq.com
|
||||
# MQTT_BROKER=broker.hivemq.com
|
||||
|
||||
# MQTT broker port. Written into .mam.env by install.sh as an active default.
|
||||
#default: 1883
|
||||
# MQTT_PORT=1883
|
||||
|
||||
# MQTT reconnect retry interval (seconds).
|
||||
#default: 2
|
||||
# MQTT_RETRY_INTERVAL=2
|
||||
|
||||
# MQTT maximum reconnect retries.
|
||||
#default: 5
|
||||
# MQTT_MAX_RETRIES=5
|
||||
|
||||
# Broker auth username. Leave unset for anonymous brokers.
|
||||
#default: (unset → anonymous)
|
||||
# MQTT_USERNAME=replace_me
|
||||
|
||||
# Broker auth password. SECRET — fill in only in your local .mam.env, never commit.
|
||||
#default: (unset → anonymous)
|
||||
# MQTT_PASSWORD=replace_me
|
||||
|
||||
# Prefix for generated MQTT client ids (publisher/subscriber/monitor).
|
||||
#default: hermes
|
||||
# MQTT_CLIENT_ID_PREFIX=hermes
|
||||
|
||||
# Log level for MAM runtime components (DEBUG, INFO, WARN, ERROR).
|
||||
#default: INFO
|
||||
# MAM_LOG_LEVEL=INFO
|
||||
|
||||
# Retention period (days) for delegate-job event logs.
|
||||
#default: 7
|
||||
# MAM_EVENT_RETENTION_DAYS=7
|
||||
|
||||
# Garbage collection threshold (minutes) for old message buffer logs.
|
||||
#default: 60
|
||||
# MAM_BUFFER_GC_MINUTES=60
|
||||
|
||||
# Marker string used by loop delegation guard (O-3).
|
||||
#default: (built-in guard marker)
|
||||
# MAM_LOOP_GUARD_MARKER=mam_loop_active
|
||||
|
||||
# Path to a CA bundle for TLS broker verification (set MQTT_TLS=1 to use TLS).
|
||||
#default: (unset → no custom CA)
|
||||
# MQTT_CA_CERTS=/path/to/ca.crt
|
||||
|
||||
# Client certificate for mutual-TLS brokers.
|
||||
#default: (unset → no client cert)
|
||||
# MQTT_CERTFILE=/path/to/client.crt
|
||||
|
||||
# Client private key for mutual-TLS brokers. SECRET — keep the key file private.
|
||||
#default: (unset → no client key)
|
||||
# MQTT_KEYFILE=/path/to/client.key
|
||||
|
||||
# Directory for delegate-job audit logs (sits beside .mam/jobs/).
|
||||
#default: <cwd>/.mam/delegate_job_logs
|
||||
# DELEGATE_JOB_LOGS_DIR=/path/to/workspace/.mam/delegate_job_logs
|
||||
|
||||
# ==============================================================================
|
||||
# deploy / distribution source (for forks/mirrors)
|
||||
# ==============================================================================
|
||||
# Note: These variables are read from the execution environment by deployment scripts.
|
||||
# Since deploy/install.sh runs before .mam.env exists, you must pass them via export
|
||||
# or prepended variables (e.g. MAM_REPO_URL=... bash deploy/install.sh).
|
||||
# If you run a private mirror, we strongly recommend configuring all three variables.
|
||||
|
||||
# Distribution repository URL (cloned during recovery steps).
|
||||
#default: https://git.godopu.com/tmpl/multi-agent-mux.git
|
||||
# MAM_REPO_URL=https://git.godopu.com/tmpl/multi-agent-mux.git
|
||||
|
||||
# Distribution archive download URL (used for bootstrap extraction).
|
||||
#default: https://git.godopu.com/tmpl/multi-agent-mux/archive/main.tar.gz
|
||||
# MAM_ARCHIVE_URL=https://git.godopu.com/tmpl/multi-agent-mux/archive/main.tar.gz
|
||||
|
||||
# Distribution update/installer script URL.
|
||||
#default: https://git.godopu.com/tmpl/multi-agent-mux/raw/branch/main/deploy/install.sh
|
||||
# MAM_INSTALLER_URL=https://git.godopu.com/tmpl/multi-agent-mux/raw/branch/main/deploy/install.sh
|
||||
@@ -67,4 +67,8 @@ 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.
|
||||
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 -->
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
- `<p class="text-ink-mute text-base sm:text-lg leading-relaxed max-w-2xl">AI 에이전트 네트워크 연구실(ANL)은 멀티 AI 에이전트 오케스트레이션을 위해 서로 소통하고 협력하는 에이전트 네트워킹 기술과 표준 프레임워크 기술을 연구합니다.</p>`
|
||||
|
||||
- 문구 영어로 변경
|
||||
|
||||
- `<div class="pt-6 border-t border-line grid grid-cols-3 gap-4"><div><div class="font-serif text-3xl text-cobalt-dark font-normal">4</div><div class="font-mono text-xs text-ink-mute uppercase tracking-wider mt-1">Research Areas</div></div><div><div class="font-serif text-3xl text-cobalt-dark font-normal">220<!-- -->+</div><div class="font-mono text-xs text-ink-mute uppercase tracking-wider mt-1">Publications</div></div><div><div class="font-serif text-3xl text-ink font-normal">2026</div><div class="font-mono text-xs text-ink-mute uppercase tracking-wider mt-1">Issue Static</div></div></div>`
|
||||
|
||||
- 문구 수정
|
||||
- 4 RESEARCH AREAS -> {Number of International Journals & Conference} International Journals & Conference
|
||||
- 220+ PUBLICATIONS -> {Number of Patent} Patent
|
||||
- 2026 ISSUE STATIC -> {Number of Standardization} Standardization Contributions
|
||||
|
||||
- `body > main > div > section.space-y-6 > div.flex.flex-col.sm\:flex-row.sm\:items-end.justify-between.gap-4.border-b.border-line.pb-4 > div > h2`
|
||||
- 주요 연구 프로젝트 (Research Projects) -> Research Projects
|
||||
|
||||
- `"project-iot-standards`
|
||||
- IoT Standards -> AIoT & AI-RAN
|
||||
- `#project-iot-standards > div.space-y-4 > div.flex.items-start.justify-between.gap-3.border-b.border-line\/60.pb-3 > span` : 2026 ~ Present
|
||||
- AIoT 관련 특히 Agent based IoT 관련 내용으로 수정
|
||||
- `#project-iot-standards > div.space-y-3.pt-3.border-t.border-line\/60.text-xs.font-mono > div.flex.items-baseline.justify-between.gap-2 > span.text-cobalt-dark.font-bold`
|
||||
- ISO/IEC JTC1/SC41 -> 한국연구재단 (NRF) 수정
|
||||
- `#project-iot-standards > div.space-y-3.pt-3.border-t.border-line\/60.text-xs.font-mono > div.space-y-1\.5.pt-1` 부분 삭제
|
||||
|
||||
|
||||
- `#project-ccis`
|
||||
- CCIS -> MCM으로 수정
|
||||
- `#project-ccis > div.space-y-4 > div.flex.items-start.justify-between.gap-3.border-b.border-line\/60.pb-3 > span` : 2026 ~ Present
|
||||
- `#project-ccis > div.space-y-4 > p` : MCM 내용으로 수정
|
||||
- `#project-ccis > div.space-y-4 > div.flex.flex-wrap.gap-1\.5.pt-1` : MCM 관련 키워드로 수정
|
||||
- `#project-ccis > div.space-y-3.pt-3.border-t.border-line\/60.text-xs.font-mono > div.flex.items-baseline.justify-between.gap-2 > span.text-cobalt-dark.font-bold`
|
||||
- "IEC/TC 100/TA 17" -> "IEC/TC 100/WG 12 supported by KEIT"
|
||||
- `#project-ccis > div.space-y-3.pt-3.border-t.border-line\/60.text-xs.font-mono > div.space-y-1\.5.pt-1` 삭제
|
||||
|
||||
- `#project-vlc-iot`
|
||||
- VLC-IoT -> MVQM으로 수정
|
||||
- `#project-vlc-iot > div.space-y-4 > p` : MVQM 내용으로 수정
|
||||
- `#project-vlc-iot > div.space-y-4 > div.flex.flex-wrap.gap-1\.5.pt-1` : MVQM 관련 키워드로 수정
|
||||
- `#project-vlc-iot > div.space-y-3.pt-3.border-t.border-line\/60.text-xs.font-mono > div.flex.items-baseline.justify-between.gap-2 > span.text-cobalt-dark.font-bold`
|
||||
- "ITU-T SG20" -> "IEC/TC 100/WG 12 supported by KEIT"
|
||||
- `#project-vlc-iot > div.space-y-3.pt-3.border-t.border-line\/60.text-xs.font-mono > div.space-y-1\.5.pt-1` 삭제
|
||||
|
||||
- `body > main > div > section.space-y-8.pt-20.md\:pt-28.border-t.border-line > div.flex.justify-between.items-end > div:nth-child(1)`
|
||||
- 영문화 작업을 위해 "RESEARCH AREAS" 로 수정
|
||||
- `body > main > div > section.space-y-8.pt-20.md\:pt-28.border-t.border-line > div.flex.justify-between.items-end > div:nth-child(1) > div`
|
||||
- 삭제
|
||||
|
||||
- `body > main > div > section.space-y-8.pt-20.md\:pt-28.border-t.border-line > div.flex.justify-between.items-end > div.font-mono.text-xs.text-ink-mute`
|
||||
- 삭제
|
||||
|
||||
- `body > main > div > section.space-y-8.pt-20.md\:pt-28.border-t.border-line > div.grid.grid-cols-1.md\:grid-cols-2.lg\:grid-cols-2.gap-6`
|
||||
- 삭제
|
||||
|
||||
- 다음 키워드들을 각각 카드 형태로 표현
|
||||
- Quick UDP Internet Conenction (QUIC)
|
||||
- QUIC-based Mobility and Multi-Streaming Support
|
||||
- Services and Protocols for Internet-of-Things (IoT)
|
||||
- Constrained Application Protocol (CoAP)
|
||||
- In-Vehicle Infotainment (IVI)
|
||||
- Optical Wireless or Visible Light Communications (OWC/VLC)
|
||||
- Lighting Control Networks (PLASA E1.45) for LED-based VLC
|
||||
- Mobility Management: Distributed Mobility Control
|
||||
- Multicast Routing Protocols and Reliable Multicasting
|
||||
- mobile SCTP (mSCTP): Soft Handover in Transport Layer
|
||||
- Stream Control Transmission Protocol (SCTP)
|
||||
- Telecommunication Optimization: Network Planning and Management
|
||||
- TAC/TAL Configuration for Paging and Location Management
|
||||
- Tabu Search and Genetic Algorithm
|
||||
|
||||
- 새로운 섹션 추가:
|
||||
위치 : `body > main > div > section.space-y-8.pt-20.md\:pt-28.border-t.border-line > div.flex.justify-between.items-end > div:nth-child(1)` 아래
|
||||
섹션명: International Standardizations
|
||||
내용:
|
||||
|
||||
```
|
||||
Along with the research activities, we are also in pursuit of the International Standardization,
|
||||
from the market deployment perspective, in the following SDOs (Standard-Defining Organizations):
|
||||
|
||||
- IEC TC100: Multimedia Systems and Equipment
|
||||
- ISO/IEC JTC1/SC41: Internet-of-Things (IoT) Applications
|
||||
- ITU-T SG20: IoT Services
|
||||
- ITU-T SG13: Mobility Management
|
||||
- ISO/IEC JTC1/SC6: Reliable Multicasting
|
||||
|
||||
Please refer to [here](link-to-standardization-page) for details of our works on International Standardization so far.
|
||||
```
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
@@ -172,6 +172,14 @@ body {
|
||||
.draw-underline:hover {
|
||||
background-size: 100% 2px;
|
||||
}
|
||||
|
||||
/* Mobile menu scroll container for low-height / landscape viewports (F4) */
|
||||
.mobile-menu-panel {
|
||||
max-height: calc(100vh - 6rem); /* Fallback: browsers without dvh support */
|
||||
max-height: calc(100dvh - 6rem); /* Modern: dynamic viewport height */
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain; /* Prevent chaining scroll to main page */
|
||||
}
|
||||
}
|
||||
|
||||
/* ==================================================================
|
||||
@@ -217,6 +225,11 @@ body {
|
||||
[class*="animate-"] {
|
||||
animation: none !important;
|
||||
}
|
||||
/* 스마트 스티키: 모션 줄임 사용자에게는 항상 펼친 상태 유지 */
|
||||
header[data-smart-sticky] {
|
||||
transform: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
html {
|
||||
scroll-behavior: auto;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32">
|
||||
<defs>
|
||||
<linearGradient id="cobaltGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#4E77FF"/>
|
||||
<stop offset="100%" stop-color="#2B4CDE"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="32" height="32" rx="7" fill="url(#cobaltGrad)"/>
|
||||
<!-- Network Agent Nodes & Graph -->
|
||||
<circle cx="16" cy="9" r="2.5" fill="#FFFFFF"/>
|
||||
<circle cx="9" cy="22" r="2.5" fill="#FFFFFF"/>
|
||||
<circle cx="23" cy="22" r="2.5" fill="#FFFFFF"/>
|
||||
<circle cx="16" cy="17" r="2" fill="#FFD666"/>
|
||||
|
||||
<path d="M16 9 L9 22 M16 9 L23 22 M9 22 L23 22 M16 9 L16 17 M9 22 L16 17 M23 22 L16 17"
|
||||
stroke="#FFFFFF" stroke-width="1.2" stroke-opacity="0.8" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 787 B |
@@ -34,7 +34,7 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html lang="ko">
|
||||
<html lang="ko" suppressHydrationWarning>
|
||||
<head>
|
||||
<script dangerouslySetInnerHTML={{ __html: themeScript }} />
|
||||
{/* No-JS fallback for the .reveal scroll-reveal: without this, every
|
||||
|
||||
@@ -2,9 +2,14 @@
|
||||
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import ThemeToggle from "./ThemeToggle";
|
||||
|
||||
// CUSTOMIZATION HOOK — MOTION: 스마트 스티키 튜닝
|
||||
const SCROLL_DELTA = 6; // px — 방향 전환 인식 최소 이동량(지터 방지)
|
||||
const HIDE_AFTER = 140; // px — 이 지점 이전에는 숨기지 않음(실측 데스크톱 헤더 높이 122.8px + 여유)
|
||||
const NAV_SETTLE_MS = 300; // ms — 라우트 전환 직후 방향 판정 유예 구간
|
||||
|
||||
// CUSTOMIZATION HOOK: edit nav labels / routes here.
|
||||
const navItems = [
|
||||
{ href: "/", ko: "홈", en: "Home", no: "01" },
|
||||
@@ -16,8 +21,84 @@ const navItems = [
|
||||
|
||||
export default function Header() {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [hidden, setHidden] = useState(false);
|
||||
const pathname = usePathname();
|
||||
|
||||
const lastY = useRef(0);
|
||||
const ticking = useRef(false);
|
||||
const navSettling = useRef(false);
|
||||
const openRef = useRef(open);
|
||||
openRef.current = open;
|
||||
|
||||
useEffect(() => {
|
||||
// (A) 모션 줄임 사용자는 자동 숨김 자체를 비활성화 — DESIGN.md 접근성 원칙
|
||||
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
|
||||
|
||||
lastY.current = window.scrollY;
|
||||
|
||||
const update = () => {
|
||||
ticking.current = false;
|
||||
const y = window.scrollY;
|
||||
|
||||
// (B) 최상단 + iOS 러버밴드(음수) 구간 → 항상 표시
|
||||
if (y <= 0) {
|
||||
setHidden(false);
|
||||
lastY.current = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// (F-2) 라우트 전환 정착 구간: 전환이 유발한 스크롤 점프는 방향 판정에서 제외.
|
||||
// 기준점만 따라가고 hidden은 건드리지 않는다.
|
||||
if (navSettling.current) {
|
||||
lastY.current = y;
|
||||
return;
|
||||
}
|
||||
|
||||
// (C) 모바일 메뉴가 열려 있으면 숨기지 않되, lastY는 계속 추적
|
||||
// (추적하지 않으면 메뉴를 닫는 순간 헤더가 튀며 사라짐)
|
||||
if (openRef.current) {
|
||||
setHidden(false);
|
||||
lastY.current = y;
|
||||
return;
|
||||
}
|
||||
|
||||
const delta = y - lastY.current;
|
||||
|
||||
// (D) 미세 이동 무시. 이때 lastY를 갱신하지 않고 보존
|
||||
if (Math.abs(delta) < SCROLL_DELTA) return;
|
||||
|
||||
if (delta > 0 && y > HIDE_AFTER) {
|
||||
setHidden(true); // 아래로 → 숨김
|
||||
} else if (delta < 0) {
|
||||
setHidden(false); // 위로 → 복귀
|
||||
}
|
||||
|
||||
lastY.current = y;
|
||||
};
|
||||
|
||||
const onScroll = () => {
|
||||
if (ticking.current) return;
|
||||
ticking.current = true;
|
||||
requestAnimationFrame(update); // 프레임당 1회로 제한
|
||||
};
|
||||
|
||||
window.addEventListener("scroll", onScroll, { passive: true });
|
||||
return () => window.removeEventListener("scroll", onScroll);
|
||||
}, []);
|
||||
|
||||
// (F) 라우트 전환 대응 — persistent layout이라 hidden이 페이지를 넘어 생존한다.
|
||||
// 스크롤 이벤트 발화 여부와 무관하게 표시 상태를 보장한다.
|
||||
useEffect(() => {
|
||||
setHidden(false);
|
||||
lastY.current = window.scrollY; // 현재 실제 위치로 동기화
|
||||
setOpen(false); // 뒤로/앞으로 이동 시 모바일 메뉴 잔존 방지
|
||||
navSettling.current = true;
|
||||
const id = window.setTimeout(() => {
|
||||
navSettling.current = false;
|
||||
}, NAV_SETTLE_MS);
|
||||
return () => window.clearTimeout(id);
|
||||
}, [pathname]);
|
||||
|
||||
const isActive = (href: string) =>
|
||||
pathname
|
||||
? href === "/"
|
||||
@@ -26,7 +107,13 @@ export default function Header() {
|
||||
: false;
|
||||
|
||||
return (
|
||||
<header className="sticky top-0 z-50 border-b border-ink bg-ivory/85 backdrop-blur">
|
||||
<header
|
||||
data-smart-sticky
|
||||
onFocusCapture={() => setHidden(false)}
|
||||
className={`flow-root sticky top-0 z-50 border-b border-ink bg-ivory/85 backdrop-blur transition-transform duration-300 ease-out ${
|
||||
hidden ? "-translate-y-full" : "translate-y-0"
|
||||
}`}
|
||||
>
|
||||
{/* Journal issue strip — magazine masthead feel. */}
|
||||
<div className="hidden border-b border-line desktop:block">
|
||||
<div className="container-content flex items-center justify-between py-1.5">
|
||||
@@ -39,7 +126,7 @@ export default function Header() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="container-content flex h-[4.25rem] items-center justify-between gap-4">
|
||||
<div className="container-content flex h-[4.25rem] items-center justify-between gap-4 my-2 sm:my-3">
|
||||
{/* Brand logo */}
|
||||
<Link
|
||||
href="/"
|
||||
@@ -49,7 +136,7 @@ export default function Header() {
|
||||
<span className="headline-ko text-lg text-ink sm:text-xl">
|
||||
AI 에이전트 네트워크 연구실
|
||||
</span>
|
||||
<span className="kicker mt-1 group-hover:text-vermillion">
|
||||
<span className="kicker mt-1 transition-colors group-hover:text-cobalt-dark">
|
||||
AI Agent Networking Lab (ANL)
|
||||
</span>
|
||||
</Link>
|
||||
@@ -112,7 +199,7 @@ export default function Header() {
|
||||
{open && (
|
||||
<nav
|
||||
id="mobile-menu"
|
||||
className="border-t border-ink bg-ivory desktop:hidden"
|
||||
className="mobile-menu-panel border-t border-ink bg-ivory desktop:hidden"
|
||||
aria-label="모바일 메뉴"
|
||||
>
|
||||
<ul className="container-content flex flex-col py-2">
|
||||
|
||||
Executable
+67
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env bash
|
||||
# generate-env.sh — create a local .mam.env from the committed .mam.env.example template.
|
||||
#
|
||||
# Behaviour:
|
||||
# - .mam.env absent → copy .mam.env.example to .mam.env, print the path.
|
||||
# - .mam.env present → no-op (leaves your edits intact), exit 0.
|
||||
# - .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/),
|
||||
# so it works regardless of the caller's cwd.
|
||||
#
|
||||
# Usage: scripts/generate-env.sh [--force] [--migrate-legacy] [-h|--help]
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
SRC="$REPO_ROOT/.mam.env.example"
|
||||
DST="$REPO_ROOT/.mam.env"
|
||||
LEGACY_ENV="$REPO_ROOT/.env"
|
||||
|
||||
FORCE=0
|
||||
MIGRATE=0
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--force) FORCE=1; shift ;;
|
||||
--migrate-legacy) MIGRATE=1; shift ;;
|
||||
-h|--help)
|
||||
echo "Usage: $0 [--force] [--migrate-legacy]"
|
||||
echo " Create .mam.env from .mam.env.example."
|
||||
echo " --force overwrites an existing .mam.env (backs up to .mam.env.bak)."
|
||||
echo " --migrate-legacy explicitly renames an existing legacy .env to .mam.env."
|
||||
exit 0 ;;
|
||||
*) echo "ERROR: unknown arg: $1" >&2; echo "Usage: $0 [--force] [--migrate-legacy]" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$MIGRATE" = "1" ]; then
|
||||
if [ -f "$DST" ]; then
|
||||
echo "ERROR: cannot migrate: $DST already exists." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -f "$LEGACY_ENV" ]; then
|
||||
echo "ERROR: cannot migrate: legacy file $LEGACY_ENV not found." >&2
|
||||
exit 1
|
||||
fi
|
||||
mv -f "$LEGACY_ENV" "$DST"
|
||||
chmod 0600 "$DST" 2>/dev/null || true
|
||||
echo "migrated: $LEGACY_ENV -> $DST"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
[ -f "$SRC" ] || { echo "ERROR: template not found: $SRC" >&2; exit 1; }
|
||||
|
||||
if [ -f "$DST" ] && [ "$FORCE" != "1" ]; then
|
||||
echo "no-op: $DST already exists (use --force to overwrite)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -f "$DST" ] && [ "$FORCE" = "1" ]; then
|
||||
cp -p "$DST" "$DST.bak"
|
||||
echo "backed up existing .mam.env -> $DST.bak"
|
||||
fi
|
||||
|
||||
cp "$SRC" "$DST"
|
||||
chmod 0600 "$DST" 2>/dev/null || true
|
||||
echo "created: $DST"
|
||||
echo "Next: edit $DST and fill in any secrets (look for 'replace_me')."
|
||||
Reference in New Issue
Block a user