diff --git a/docs/history/history-river.md b/docs/history/history-river.md
new file mode 100644
index 0000000..cb32094
--- /dev/null
+++ b/docs/history/history-river.md
@@ -0,0 +1,107 @@
+---
+sidebar_position: 1
+---
+
+# 社区大事记
+
+import HistoryTimeline from '@site/src/components/HistoryTimeline/HistoryTimeline';
+
+`
+ copyright: `红星我的世界社区维基 © 2021 - ${new Date().getFullYear()} 由 红星我的世界社区 授权 CC BY-NC-SA 4.0 许可协议
`
},
// 深浅主题
prism: {
diff --git a/src/components/HistoryTimeline/HistoryTimeline.css b/src/components/HistoryTimeline/HistoryTimeline.css
new file mode 100644
index 0000000..bdebbef
--- /dev/null
+++ b/src/components/HistoryTimeline/HistoryTimeline.css
@@ -0,0 +1,123 @@
+/*
+ * 使用 AI 生成并人工修改。
+ */
+.rsmc-timeline {
+ position: relative;
+ max-width: 1200px;
+ margin: 2rem auto;
+ padding: 0 1rem;
+}
+
+.rsmc-timeline__toggle {
+ position: relative;
+ z-index: 10;
+ margin: 0 0 2rem auto;
+ padding: 0.5rem 1.25rem;
+ background-color: var(--balance-2);
+ color: var(--bg-container);
+ border: none;
+ border-radius: 4px;
+ font-size: 0.9rem;
+ cursor: pointer;
+ display: block;
+ /*display: none;*/
+}
+
+.rsmc-timeline__axis {
+ position: absolute;
+ left: 50%;
+ top: 0;
+ bottom: 0;
+ width: 4px;
+ background: linear-gradient(to bottom, var(--primary-light), var(--balance-2), var(--primary-dark));
+ transform: translateX(-50%);
+ z-index: 1;
+}
+
+.rsmc-timeline__item {
+ position: relative;
+ width: 45%;
+ margin-bottom: 3rem;
+ opacity: 0;
+ transform: translateY(20px);
+ transition: all 0.5s ease;
+}
+
+.rsmc-timeline__item--left { left: 0; }
+.rsmc-timeline__item--right { left: 55%; }
+.rsmc-timeline__item.visible { opacity: 1; transform: translateY(0); }
+
+.rsmc-timeline__content {
+ background-color: var(--bg-container);
+ border: 1px solid var(--border-medium);
+ border-radius: 8px;
+ padding: 1.25rem;
+ box-shadow: var(--shadow-sm);
+}
+
+.rsmc-timeline__item--left .rsmc-timeline__content { border-left: 3px solid var(--balance-2); }
+.rsmc-timeline__item--right .rsmc-timeline__content { border-left: 3px solid var(--primary); }
+
+.rsmc-timeline__date {
+ color: var(--balance-2);
+ font-weight: 600;
+ margin-bottom: 0.5rem;
+ font-size: 0.9rem;
+}
+
+.rsmc-timeline__title {
+ margin: 0 0 0.75rem 0;
+ font-size: 1.25rem;
+ font-weight: 600;
+ color: var(--balance-1);
+}
+
+.rsmc-timeline__title-link {
+ color: inherit;
+ text-decoration: none;
+ font-size: inherit;
+ font-weight: inherit;
+}
+
+.rsmc-timeline__title-link:hover {
+ color: var(--primary);
+ text-decoration: underline;
+}
+
+.rsmc-timeline__description {
+ color: var(--text-secondary);
+ font-size: 0.95rem;
+ line-height: 1.6;
+}
+
+.rsmc-timeline__dot {
+ position: absolute;
+ top: 1.5rem;
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ background-color: var(--bg-container);
+ border: 3px solid var(--balance-2);
+ z-index: 5;
+}
+
+.rsmc-timeline__item--left .rsmc-timeline__dot { right: -12px; }
+.rsmc-timeline__item--right .rsmc-timeline__dot { left: -12px; }
+
+@media (max-width: 768px) {
+ .rsmc-timeline__axis {
+ left: 30px;
+ transform: none;
+ }
+ .rsmc-timeline__item {
+ width: 100%;
+ left: 0 !important;
+ padding-left: 60px;
+ }
+ .rsmc-timeline__dot {
+ left: 30px !important;
+ right: auto !important;
+ transform: translateX(-50%);
+ }
+}
+
diff --git a/src/components/HistoryTimeline/HistoryTimeline.jsx b/src/components/HistoryTimeline/HistoryTimeline.jsx
new file mode 100644
index 0000000..0534dbe
--- /dev/null
+++ b/src/components/HistoryTimeline/HistoryTimeline.jsx
@@ -0,0 +1,84 @@
+import React, { useState, useEffect, useMemo } from 'react';
+import clsx from 'clsx';
+import './HistoryTimeline.css';
+
+const HistoryTimeline = ({ events }) => {
+ const [visibleEvents, setVisibleEvents] = useState([]);
+ const [isAscending, setIsAscending] = useState(false);
+
+ const sortedEvents = useMemo(() => {
+ return [...events].sort((a, b) => {
+ const dateA = new Date(a.date);
+ const dateB = new Date(b.date);
+ return isAscending ? dateA - dateB : dateB - dateA;
+ });
+ }, [events, isAscending]);
+
+ useEffect(() => {
+ const handleScroll = () => {
+ const newVisible = sortedEvents.map((_, index) => {
+ const element = document.getElementById(`timeline-item-${index}`);
+ if (!element) return false;
+ const rect = element.getBoundingClientRect();
+ return rect.top < window.innerHeight * 0.85;
+ });
+ setVisibleEvents(newVisible);
+ };
+
+ handleScroll();
+ window.addEventListener('scroll', handleScroll);
+ return () => window.removeEventListener('scroll', handleScroll);
+ }, [sortedEvents]);
+
+ const toggleOrder = () => {
+ setIsAscending(!isAscending);
+ setVisibleEvents([]);
+ };
+
+ return (
+