feat: 添加站点卡片悬停提示功能
- 为所有站点卡片添加 data-tooltip 属性,包含完整的标题和描述信息 - tooltip 显示逻辑: * 鼠标悬停在整个卡片上即可触发(触发区域大,操作自然) * 跟随鼠标移动,实时更新位置 * 智能边界检测,避免 tooltip 超出视口范围 * 鼠标移出时自动隐藏 - 解决文本截断问题,用户可通过悬停查看完整内容 实现: - 模板层:在 site-card.hbs 中为卡片添加 data-tooltip 属性 - 交互层:在 script.js 中实现 tooltip 的创建、显示、移动和隐藏逻辑 - 样式层:通过 CSS 类控制 tooltip 的可见性和位置 Issue: #31
This commit is contained in:
291
assets/style.css
291
assets/style.css
@@ -92,8 +92,10 @@ body.light-theme {
|
||||
|
||||
/* 预加载主题 - 在JS完全加载前显示正确的主题 */
|
||||
html.theme-preload body {
|
||||
background-color: #e0e0d8; /* 明亮主题背景色 */
|
||||
color: #333333; /* 明亮主题文本色 */
|
||||
background-color: #e0e0d8;
|
||||
/* 明亮主题背景色 */
|
||||
color: #333333;
|
||||
/* 明亮主题文本色 */
|
||||
}
|
||||
|
||||
/* 预加载侧边栏状态 - 在JS完全加载前显示正确的侧边栏宽度 */
|
||||
@@ -169,13 +171,16 @@ html.preload * {
|
||||
|
||||
/* 通用滚动条样式 */
|
||||
.custom-scrollbar {
|
||||
scrollbar-width: thin; /* Firefox */
|
||||
scrollbar-color: var(--scrollbar-color) transparent; /* Firefox */
|
||||
scrollbar-width: thin;
|
||||
/* Firefox */
|
||||
scrollbar-color: var(--scrollbar-color) transparent;
|
||||
/* Firefox */
|
||||
}
|
||||
|
||||
/* Webkit滚动条样式(Chrome, Safari, Edge等) */
|
||||
.custom-scrollbar::-webkit-scrollbar {
|
||||
width: 7px; /* 统一滚动条宽度 */
|
||||
width: 7px;
|
||||
/* 统一滚动条宽度 */
|
||||
}
|
||||
|
||||
.custom-scrollbar::-webkit-scrollbar-track {
|
||||
@@ -183,18 +188,22 @@ html.preload * {
|
||||
}
|
||||
|
||||
.custom-scrollbar::-webkit-scrollbar-thumb {
|
||||
background-color: var(--scrollbar-color); /* 使用变量 */
|
||||
background-color: var(--scrollbar-color);
|
||||
/* 使用变量 */
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
||||
background-color: var(--scrollbar-hover-color); /* 使用变量 */
|
||||
background-color: var(--scrollbar-hover-color);
|
||||
/* 使用变量 */
|
||||
}
|
||||
|
||||
/* 防止滚动条导致的布局偏移 */
|
||||
html {
|
||||
overflow-y: hidden; /* 改为hidden,移除强制显示的滚动条 */
|
||||
scrollbar-width: thin; /* Firefox */
|
||||
overflow-y: hidden;
|
||||
/* 改为hidden,移除强制显示的滚动条 */
|
||||
scrollbar-width: thin;
|
||||
/* Firefox */
|
||||
/* 明确 rem 基准字号:便于用 rem 统一管理字号(1rem = 16px) */
|
||||
font-size: 16px;
|
||||
}
|
||||
@@ -216,8 +225,10 @@ body {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
min-height: var(--app-height, 100vh);
|
||||
overflow: hidden; /* 防止body滚动 */
|
||||
padding-right: 0 !important; /* 防止滚动条导致的布局偏移 */
|
||||
overflow: hidden;
|
||||
/* 防止body滚动 */
|
||||
padding-right: 0 !important;
|
||||
/* 防止滚动条导致的布局偏移 */
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
@@ -227,7 +238,8 @@ body {
|
||||
min-height: var(--app-height, 100vh);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
overflow: hidden; /* 防止layout滚动 */
|
||||
overflow: hidden;
|
||||
/* 防止layout滚动 */
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
@@ -291,7 +303,8 @@ body.loaded .layout {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
z-index: 950; /* 调整遮罩层z-index,处于按钮与弹出面板之间 */
|
||||
z-index: 950;
|
||||
/* 调整遮罩层z-index,处于按钮与弹出面板之间 */
|
||||
}
|
||||
|
||||
.overlay.active {
|
||||
@@ -338,14 +351,17 @@ body.loaded .layout {
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 3.75rem; /* 确保与展开状态高度一致 */
|
||||
margin-bottom: 0.8rem; /* 收起态同样拉开与按钮的间距 */
|
||||
height: 3.75rem;
|
||||
/* 确保与展开状态高度一致 */
|
||||
margin-bottom: 0.8rem;
|
||||
/* 收起态同样拉开与按钮的间距 */
|
||||
}
|
||||
|
||||
/* 折叠状态下的侧边栏内容区域调整 */
|
||||
.sidebar.collapsed .sidebar-content {
|
||||
padding: 0;
|
||||
scrollbar-width: none; /* 隐藏滚动条 */
|
||||
scrollbar-width: none;
|
||||
/* 隐藏滚动条 */
|
||||
}
|
||||
|
||||
/* 调整折叠侧边栏的部分元素间距 */
|
||||
@@ -364,20 +380,27 @@ body.loaded .layout {
|
||||
}
|
||||
|
||||
.sidebar.collapsed .sidebar-content::-webkit-scrollbar {
|
||||
display: none; /* 隐藏WebKit浏览器的滚动条 */
|
||||
display: none;
|
||||
/* 隐藏WebKit浏览器的滚动条 */
|
||||
}
|
||||
|
||||
/* 侧边栏头部区域 */
|
||||
.sidebar .logo {
|
||||
grid-area: header;
|
||||
padding: 1.2rem 1.2rem 0.6rem; /* 调整上下padding更紧凑 */
|
||||
padding: 1.2rem 1.2rem 0.6rem;
|
||||
/* 调整上下padding更紧凑 */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden; /* 防止内容溢出 */
|
||||
position: relative; /* 添加相对定位,作为按钮的参考 */
|
||||
height: 3.75rem; /* 固定高度 60px */
|
||||
margin-bottom: 0.8rem; /* 与下方按钮区域拉开间距 */
|
||||
transition: padding 0.3s ease; /* 添加padding过渡,避免突变 */
|
||||
overflow: hidden;
|
||||
/* 防止内容溢出 */
|
||||
position: relative;
|
||||
/* 添加相对定位,作为按钮的参考 */
|
||||
height: 3.75rem;
|
||||
/* 固定高度 60px */
|
||||
margin-bottom: 0.8rem;
|
||||
/* 与下方按钮区域拉开间距 */
|
||||
transition: padding 0.3s ease;
|
||||
/* 添加padding过渡,避免突变 */
|
||||
}
|
||||
|
||||
.logo-brand {
|
||||
@@ -386,7 +409,8 @@ body.loaded .layout {
|
||||
gap: 0.6rem;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
padding-right: 2.2rem; /* 预留右侧折叠按钮空间 */
|
||||
padding-right: 2.2rem;
|
||||
/* 预留右侧折叠按钮空间 */
|
||||
}
|
||||
|
||||
.logo-brand h1 {
|
||||
@@ -428,13 +452,18 @@ body.loaded .layout {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s ease; /* 只过渡背景色,移除all避免位置过渡 */
|
||||
transition: background 0.3s ease;
|
||||
/* 只过渡背景色,移除all避免位置过渡 */
|
||||
padding: 0;
|
||||
flex-shrink: 0; /* 防止按钮被压缩 */
|
||||
position: absolute; /* 在两种状态下都使用绝对定位 */
|
||||
right: 1.2rem; /* 展开状态下固定在右侧 */
|
||||
flex-shrink: 0;
|
||||
/* 防止按钮被压缩 */
|
||||
position: absolute;
|
||||
/* 在两种状态下都使用绝对定位 */
|
||||
right: 1.2rem;
|
||||
/* 展开状态下固定在右侧 */
|
||||
top: 60%;
|
||||
transform: translateY(-50%); /* 垂直居中 */
|
||||
transform: translateY(-50%);
|
||||
/* 垂直居中 */
|
||||
}
|
||||
|
||||
.sidebar-toggle .toggle-icon {
|
||||
@@ -457,9 +486,12 @@ body.loaded .layout {
|
||||
|
||||
/* 收起状态下按钮居中 */
|
||||
.sidebar.collapsed .sidebar-toggle {
|
||||
left: 50%; /* 水平居中 */
|
||||
right: auto; /* 移除右侧定位 */
|
||||
transform: translate(-50%, -50%); /* 同时水平和垂直居中 */
|
||||
left: 50%;
|
||||
/* 水平居中 */
|
||||
right: auto;
|
||||
/* 移除右侧定位 */
|
||||
transform: translate(-50%, -50%);
|
||||
/* 同时水平和垂直居中 */
|
||||
}
|
||||
|
||||
.sidebar.collapsed .sidebar-toggle:hover {
|
||||
@@ -478,28 +510,35 @@ body.loaded .layout {
|
||||
/* 侧边栏内容区域 - 可滚动 */
|
||||
.sidebar-content {
|
||||
grid-area: content;
|
||||
min-height: 0; /* 允许在 CSS Grid 内正确收缩与滚动,避免把 footer 挤出可视区域 */
|
||||
overflow-y: auto; /* 只有内容区域可滚动 */
|
||||
min-height: 0;
|
||||
/* 允许在 CSS Grid 内正确收缩与滚动,避免把 footer 挤出可视区域 */
|
||||
overflow-y: auto;
|
||||
/* 只有内容区域可滚动 */
|
||||
padding: 0 1.2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.6rem; /* 从1rem减小到0.6rem */
|
||||
gap: 0.6rem;
|
||||
/* 从1rem减小到0.6rem */
|
||||
/* 隐藏滚动条但保持滚动功能 */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
scrollbar-width: none;
|
||||
/* Firefox */
|
||||
}
|
||||
|
||||
.sidebar-content::-webkit-scrollbar {
|
||||
display: none; /* Webkit browsers */
|
||||
display: none;
|
||||
/* Webkit browsers */
|
||||
}
|
||||
|
||||
/* 折叠状态下的内容区域调整 */
|
||||
.sidebar.collapsed .sidebar-content {
|
||||
padding: 0 0.5rem;
|
||||
scrollbar-width: none; /* 隐藏滚动条 */
|
||||
scrollbar-width: none;
|
||||
/* 隐藏滚动条 */
|
||||
}
|
||||
|
||||
.sidebar.collapsed .sidebar-content::-webkit-scrollbar {
|
||||
display: none; /* 隐藏WebKit浏览器的滚动条 */
|
||||
display: none;
|
||||
/* 隐藏WebKit浏览器的滚动条 */
|
||||
}
|
||||
|
||||
/* 折叠状态下的Logo文本 */
|
||||
@@ -507,22 +546,27 @@ body.loaded .layout {
|
||||
opacity: 0;
|
||||
transform: translateX(-20px);
|
||||
width: 0;
|
||||
visibility: hidden; /* 确保完全隐藏,防止干扰布局 */
|
||||
pointer-events: none; /* 禁用交互,避免影响布局 */
|
||||
visibility: hidden;
|
||||
/* 确保完全隐藏,防止干扰布局 */
|
||||
pointer-events: none;
|
||||
/* 禁用交互,避免影响布局 */
|
||||
}
|
||||
|
||||
/* 导航区域样式 */
|
||||
.nav-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem; /* 增大按钮间距 */
|
||||
gap: 0.4rem;
|
||||
/* 增大按钮间距 */
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 1rem;
|
||||
color: var(--accent-color);
|
||||
padding: 0.4rem 0.5rem; /* 减小上下padding */
|
||||
margin-bottom: 0.2rem; /* 增大与下方按钮组的间距 */
|
||||
padding: 0.4rem 0.5rem;
|
||||
/* 减小上下padding */
|
||||
margin-bottom: 0.2rem;
|
||||
/* 增大与下方按钮组的间距 */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
@@ -539,7 +583,8 @@ body.loaded .layout {
|
||||
/* 统一与展开态的垂直间距 */
|
||||
padding: 0.4rem 0;
|
||||
text-align: center;
|
||||
margin-bottom: 0.2rem; /* 与展开态保持一致且更大 */
|
||||
margin-bottom: 0.2rem;
|
||||
/* 与展开态保持一致且更大 */
|
||||
}
|
||||
|
||||
.sidebar.collapsed .section-title i {
|
||||
@@ -556,12 +601,15 @@ body.loaded .layout {
|
||||
.sidebar.collapsed .nav-item {
|
||||
padding: 0;
|
||||
justify-content: center;
|
||||
width: 2.75rem; /* 增大按钮方块尺寸 44px */
|
||||
height: 2.75rem; /* 增大按钮方块尺寸 44px */
|
||||
width: 2.75rem;
|
||||
/* 增大按钮方块尺寸 44px */
|
||||
height: 2.75rem;
|
||||
/* 增大按钮方块尺寸 44px */
|
||||
text-align: center;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border-radius: var(--radius-md); /* 略增圆角 */
|
||||
border-radius: var(--radius-md);
|
||||
/* 略增圆角 */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
@@ -589,7 +637,8 @@ body.loaded .layout {
|
||||
opacity: 0;
|
||||
transform: translateX(-10px);
|
||||
width: 0;
|
||||
display: none; /* 完全移除,防止干扰布局 */
|
||||
display: none;
|
||||
/* 完全移除,防止干扰布局 */
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
@@ -662,19 +711,23 @@ body.loaded .layout {
|
||||
padding: 2rem 1.5rem;
|
||||
background-color: var(--bg-color);
|
||||
position: relative;
|
||||
height: var(--app-height, 100vh); /* 固定高度(移动端避免 100vh 问题) */
|
||||
overflow-y: auto; /* 使用auto替代scroll,只在需要时显示滚动条 */
|
||||
height: var(--app-height, 100vh);
|
||||
/* 固定高度(移动端避免 100vh 问题) */
|
||||
overflow-y: auto;
|
||||
/* 使用auto替代scroll,只在需要时显示滚动条 */
|
||||
overflow-x: hidden;
|
||||
width: calc(100vw - var(--sidebar-width));
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
/* 隐藏滚动条但保持滚动功能 */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
scrollbar-width: none;
|
||||
/* Firefox */
|
||||
}
|
||||
|
||||
.content::-webkit-scrollbar {
|
||||
display: none; /* Webkit browsers */
|
||||
display: none;
|
||||
/* Webkit browsers */
|
||||
}
|
||||
|
||||
/* 优化内容区域在侧边栏折叠状态下的边距 */
|
||||
@@ -1097,12 +1150,11 @@ body .content.expanded {
|
||||
|
||||
@keyframes glow {
|
||||
from {
|
||||
filter: drop-shadow(0 0 2px rgba(118, 148, 185, 0.2))
|
||||
drop-shadow(0 0 4px rgba(168, 85, 247, 0.2));
|
||||
filter: drop-shadow(0 0 2px rgba(118, 148, 185, 0.2)) drop-shadow(0 0 4px rgba(168, 85, 247, 0.2));
|
||||
}
|
||||
|
||||
to {
|
||||
filter: drop-shadow(0 0 4px rgba(118, 148, 185, 0.4))
|
||||
drop-shadow(0 0 8px rgba(168, 85, 247, 0.4));
|
||||
filter: drop-shadow(0 0 4px rgba(118, 148, 185, 0.4)) drop-shadow(0 0 8px rgba(168, 85, 247, 0.4));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1240,7 +1292,8 @@ body .content.expanded {
|
||||
}
|
||||
|
||||
/* 层级3: 分组 */
|
||||
.group-level-3, .category-level-3 {
|
||||
.group-level-3,
|
||||
.category-level-3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
padding-left: 0.5rem;
|
||||
@@ -1271,7 +1324,8 @@ body .content.expanded {
|
||||
}
|
||||
|
||||
/* 层级4: 子分组 */
|
||||
.group-level-4, .category-level-4 {
|
||||
.group-level-4,
|
||||
.category-level-4 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
padding-left: 0.5rem;
|
||||
@@ -1358,6 +1412,7 @@ body .content.expanded {
|
||||
|
||||
/* 分类/分组折叠图标:桌面端默认隐藏,悬停/收起时显示,避免按钮过多 */
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
|
||||
.category-header .toggle-icon,
|
||||
.group-header .toggle-icon {
|
||||
opacity: 0;
|
||||
@@ -1373,7 +1428,8 @@ body .content.expanded {
|
||||
}
|
||||
|
||||
/* 展开/折叠动画 */
|
||||
.category-content, .group-content {
|
||||
.category-content,
|
||||
.group-content {
|
||||
overflow: visible;
|
||||
transition: max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
@@ -1464,11 +1520,13 @@ body .content.expanded {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.group-level-3, .category-level-3 {
|
||||
.group-level-3,
|
||||
.category-level-3 {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.group-level-4, .category-level-4 {
|
||||
.group-level-4,
|
||||
.category-level-4 {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
@@ -1503,13 +1561,16 @@ body .content.expanded {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.category-level-2, .group-level-3, .category-level-3 {
|
||||
.category-level-2,
|
||||
.group-level-3,
|
||||
.category-level-3 {
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.group-level-4, .category-level-4 {
|
||||
.group-level-4,
|
||||
.category-level-4 {
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
width: 100%;
|
||||
@@ -1611,6 +1672,8 @@ body .content.expanded {
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
/* Ensure tooltip positioning context */
|
||||
}
|
||||
|
||||
.site-card.site-card-repo .repo-stats {
|
||||
@@ -1928,6 +1991,43 @@ body .content.expanded {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
/* Ensure tooltip positioning context */
|
||||
}
|
||||
|
||||
/* Tooltip styles */
|
||||
/* Tooltip styles */
|
||||
.site-card p[data-tooltip],
|
||||
.site-card .repo-desc[data-tooltip] {
|
||||
cursor: default;
|
||||
/* Indicate interactivity */
|
||||
}
|
||||
|
||||
.custom-tooltip {
|
||||
position: fixed;
|
||||
background: rgba(47, 48, 53, 0.95);
|
||||
/* Fallback dark */
|
||||
background: rgba(var(--card-bg-rgb), 0.95);
|
||||
color: var(--text-bright);
|
||||
padding: 0.5rem 0.8rem;
|
||||
border-radius: var(--radius-md);
|
||||
box-shadow: 0 4px 12px var(--shadow-color);
|
||||
border: 1px solid var(--border-color);
|
||||
font-size: 0.85rem;
|
||||
white-space: normal;
|
||||
line-height: 1.4;
|
||||
z-index: 9999;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease-out;
|
||||
backdrop-filter: blur(4px);
|
||||
-webkit-backdrop-filter: blur(4px);
|
||||
max-width: 300px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.custom-tooltip.visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* 添加编辑按钮 */
|
||||
@@ -1945,7 +2045,8 @@ body .content.expanded {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.edit-btn, .delete-btn {
|
||||
.edit-btn,
|
||||
.delete-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
@@ -1955,7 +2056,8 @@ body .content.expanded {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.edit-btn:hover, .delete-btn:hover {
|
||||
.edit-btn:hover,
|
||||
.delete-btn:hover {
|
||||
color: var(--text-bright);
|
||||
background-color: var(--secondary-bg);
|
||||
}
|
||||
@@ -2080,7 +2182,8 @@ body .content.expanded {
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.form-group input, .form-group select {
|
||||
.form-group input,
|
||||
.form-group select {
|
||||
background-color: var(--secondary-bg);
|
||||
border: 1px solid transparent;
|
||||
border-radius: var(--radius-md);
|
||||
@@ -2091,7 +2194,8 @@ body .content.expanded {
|
||||
box-shadow: 0 2px 6px var(--shadow-color);
|
||||
}
|
||||
|
||||
.form-group input:focus, .form-group select:focus {
|
||||
.form-group input:focus,
|
||||
.form-group select:focus {
|
||||
outline: none;
|
||||
background-color: var(--secondary-bg);
|
||||
border-color: var(--accent-color);
|
||||
@@ -2191,7 +2295,8 @@ body .content.expanded {
|
||||
.sidebar.active {
|
||||
transform: translateX(0);
|
||||
box-shadow: 2px 0 10px var(--shadow-color);
|
||||
z-index: 1000; /* 增加侧边栏激活时的z-index,确保显示在按钮之上 */
|
||||
z-index: 1000;
|
||||
/* 增加侧边栏激活时的z-index,确保显示在按钮之上 */
|
||||
}
|
||||
|
||||
/* 重置移动端下的侧边栏展开状态 */
|
||||
@@ -2243,7 +2348,8 @@ body .content.expanded {
|
||||
|
||||
.search-container.active {
|
||||
transform: translateY(0);
|
||||
z-index: 1000; /* 增加搜索容器激活时的z-index,确保显示在按钮之上 */
|
||||
z-index: 1000;
|
||||
/* 增加搜索容器激活时的z-index,确保显示在按钮之上 */
|
||||
}
|
||||
|
||||
.search-box {
|
||||
@@ -2284,7 +2390,8 @@ body .content.expanded {
|
||||
/* 欢迎区域样式 */
|
||||
.welcome-section {
|
||||
padding: 0 1rem;
|
||||
margin-top: 1rem; /* 增加顶部间距 */
|
||||
margin-top: 1rem;
|
||||
/* 增加顶部间距 */
|
||||
}
|
||||
|
||||
.page {
|
||||
@@ -2383,7 +2490,8 @@ body .content.expanded {
|
||||
|
||||
/* 移动端滚动进度条调整 */
|
||||
.scroll-progress {
|
||||
height: var(--radius-sm); /* 移动端略粗一些 */
|
||||
height: var(--radius-sm);
|
||||
/* 移动端略粗一些 */
|
||||
}
|
||||
|
||||
.sidebar .submenu {
|
||||
@@ -2538,6 +2646,7 @@ body .content.expanded {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
@@ -2552,8 +2661,10 @@ body .content.expanded {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
z-index: 15;
|
||||
transform: none !important; /* 确保没有变换 */
|
||||
min-height: 400px; /* 确保最小高度,防止内容过少时的布局跳动 */
|
||||
transform: none !important;
|
||||
/* 确保没有变换 */
|
||||
min-height: 400px;
|
||||
/* 确保最小高度,防止内容过少时的布局跳动 */
|
||||
}
|
||||
|
||||
#search-results.active {
|
||||
@@ -2620,6 +2731,7 @@ body .content.expanded {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -2630,6 +2742,7 @@ body .content.expanded {
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
@@ -2689,7 +2802,8 @@ body .content.expanded {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.85rem;
|
||||
border-top: 1px solid var(--border-color);
|
||||
background-color: var(--sidebar-bg); /* 使用变量 */
|
||||
background-color: var(--sidebar-bg);
|
||||
/* 使用变量 */
|
||||
transition: background-color 0.3s ease, color 0.3s ease, opacity 0.3s ease;
|
||||
}
|
||||
|
||||
@@ -2747,11 +2861,14 @@ body .content.expanded {
|
||||
|
||||
/* 子菜单展开状态 */
|
||||
.nav-item-wrapper.expanded .submenu {
|
||||
max-height: 300px; /* 设置合理的最大高度 */
|
||||
overflow-y: scroll; /* 改为scroll确保始终能滚动 */
|
||||
max-height: 300px;
|
||||
/* 设置合理的最大高度 */
|
||||
overflow-y: scroll;
|
||||
/* 改为scroll确保始终能滚动 */
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
scrollbar-width: none; /* Firefox隐藏滚动条 */
|
||||
scrollbar-width: none;
|
||||
/* Firefox隐藏滚动条 */
|
||||
}
|
||||
|
||||
/* 为WebKit浏览器隐藏滚动条 */
|
||||
@@ -2800,7 +2917,8 @@ body .content.expanded {
|
||||
使用display: none确保完全隐藏,避免任何可能的视觉问题 */
|
||||
.sidebar.collapsed .submenu {
|
||||
position: absolute;
|
||||
left: var(--sidebar-collapsed-width); /* 使用变量确保与侧边栏宽度一致 */
|
||||
left: var(--sidebar-collapsed-width);
|
||||
/* 使用变量确保与侧边栏宽度一致 */
|
||||
top: 0;
|
||||
background-color: var(--sidebar-bg);
|
||||
border-radius: 0 8px 8px 0;
|
||||
@@ -2814,7 +2932,8 @@ body .content.expanded {
|
||||
z-index: 200;
|
||||
pointer-events: none;
|
||||
transition: all 0.3s ease;
|
||||
display: none; /* 添加 display: none 确保完全隐藏 */
|
||||
display: none;
|
||||
/* 添加 display: none 确保完全隐藏 */
|
||||
}
|
||||
|
||||
/* 确保子菜单项在悬停时不会漏出
|
||||
@@ -2831,7 +2950,8 @@ body .content.expanded {
|
||||
使用static定位是为了让子菜单相对于侧边栏定位,而不是相对于nav-item-wrapper,
|
||||
这样可以避免子菜单在折叠状态下漏出的问题 */
|
||||
.sidebar.collapsed .nav-item-wrapper {
|
||||
position: static; /* 改为static,防止子菜单定位问题 */
|
||||
position: static;
|
||||
/* 改为static,防止子菜单定位问题 */
|
||||
}
|
||||
|
||||
/* 修改子菜单在悬停时的显示位置
|
||||
@@ -2845,9 +2965,12 @@ body .content.expanded {
|
||||
scrollbar-width: none;
|
||||
pointer-events: auto;
|
||||
display: block;
|
||||
left: var(--sidebar-collapsed-width); /* 确保与侧边栏宽度一致 */
|
||||
top: 0; /* 确保从顶部开始 */
|
||||
position: absolute; /* 使用绝对定位,更符合文档流 */
|
||||
left: var(--sidebar-collapsed-width);
|
||||
/* 确保与侧边栏宽度一致 */
|
||||
top: 0;
|
||||
/* 确保从顶部开始 */
|
||||
position: absolute;
|
||||
/* 使用绝对定位,更符合文档流 */
|
||||
}
|
||||
|
||||
/* 为WebKit浏览器隐藏滚动条 */
|
||||
|
||||
@@ -274,6 +274,7 @@ window.MeNav = {
|
||||
|
||||
newSite.href = siteUrl;
|
||||
newSite.title = siteName + (siteDescription ? ' - ' + siteDescription : '');
|
||||
newSite.setAttribute('data-tooltip', siteName + (siteDescription ? ' - ' + siteDescription : '')); // 添加自定义 tooltip
|
||||
if (/^https?:\/\//i.test(siteUrl)) {
|
||||
newSite.target = '_blank';
|
||||
newSite.rel = 'noopener';
|
||||
@@ -1909,3 +1910,73 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Tooltip functionality for truncated text
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Create tooltip element
|
||||
const tooltip = document.createElement('div');
|
||||
tooltip.className = 'custom-tooltip';
|
||||
document.body.appendChild(tooltip);
|
||||
|
||||
let activeElement = null;
|
||||
|
||||
// Show tooltip on hover
|
||||
document.addEventListener('mouseover', (e) => {
|
||||
const target = e.target.closest('[data-tooltip]');
|
||||
if (target) {
|
||||
const tooltipText = target.getAttribute('data-tooltip');
|
||||
if (tooltipText) {
|
||||
activeElement = target;
|
||||
tooltip.textContent = tooltipText;
|
||||
tooltip.classList.add('visible');
|
||||
updateTooltipPosition(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Move tooltip with cursor
|
||||
document.addEventListener('mousemove', (e) => {
|
||||
if (activeElement) {
|
||||
updateTooltipPosition(e);
|
||||
}
|
||||
});
|
||||
|
||||
// Hide tooltip on mouse out
|
||||
document.addEventListener('mouseout', (e) => {
|
||||
const target = e.target.closest('[data-tooltip]');
|
||||
if (target && target === activeElement) {
|
||||
// Check if we really left the element (not just went to a child)
|
||||
if (!target.contains(e.relatedTarget)) {
|
||||
activeElement = null;
|
||||
tooltip.classList.remove('visible');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function updateTooltipPosition(e) {
|
||||
// Position tooltip 15px below/right of cursor
|
||||
const x = e.clientX + 15;
|
||||
const y = e.clientY + 15;
|
||||
|
||||
// Boundary checks to keep inside viewport
|
||||
const rect = tooltip.getBoundingClientRect();
|
||||
const winWidth = window.innerWidth;
|
||||
const winHeight = window.innerHeight;
|
||||
|
||||
let finalX = x;
|
||||
let finalY = y;
|
||||
|
||||
// If tooltip goes off right edge
|
||||
if (x + rect.width > winWidth) {
|
||||
finalX = e.clientX - rect.width - 10;
|
||||
}
|
||||
|
||||
// If tooltip goes off bottom edge
|
||||
if (y + rect.height > winHeight) {
|
||||
finalY = e.clientY - rect.height - 10;
|
||||
}
|
||||
|
||||
tooltip.style.left = finalX + 'px';
|
||||
tooltip.style.top = finalY + 'px';
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
{{#if url}}
|
||||
<a href="{{url}}" class="site-card{{#if style}} site-card-{{style}}{{/if}}"
|
||||
{{#if external}}target="_blank" rel="noopener"{{/if}}
|
||||
data-type="{{#if type}}{{type}}{{else}}site{{/if}}"
|
||||
data-name="{{name}}"
|
||||
data-url="{{url}}"
|
||||
data-icon="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}}"
|
||||
{{#if faviconUrl}}data-favicon-url="{{faviconUrl}}"{{/if}}
|
||||
{{#if forceIconMode}}data-force-icon-mode="{{forceIconMode}}"{{/if}}
|
||||
<a href="{{url}}" class="site-card{{#if style}} site-card-{{style}}{{/if}}" {{#if external}}target="_blank"
|
||||
rel="noopener" {{/if}} data-type="{{#if type}}{{type}}{{else}}site{{/if}}" data-name="{{name}}" data-url="{{url}}"
|
||||
data-icon="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}}" {{#if faviconUrl}}data-favicon-url="{{faviconUrl}}"
|
||||
{{/if}} {{#if forceIconMode}}data-force-icon-mode="{{forceIconMode}}" {{/if}}
|
||||
data-description="{{#if description}}{{description}}{{else}}{{extractDomain url}}{{/if}}"
|
||||
{{#if publishedAt}}data-published-at="{{publishedAt}}"{{/if}}
|
||||
{{#if source}}data-source="{{source}}"{{/if}}>
|
||||
data-tooltip="{{#if name}}{{name}}{{else}}未命名站点{{/if}}{{#if description}} - {{description}}{{else}} - {{extractDomain url}}{{/if}}"
|
||||
{{#if publishedAt}}data-published-at="{{publishedAt}}" {{/if}} {{#if source}}data-source="{{source}}" {{/if}}>
|
||||
{{!-- articles:首行图标+标题;下方“时间/来源 + 简介”全宽对齐,不被图标列缩进 --}}
|
||||
{{#ifEquals type "article"}}
|
||||
<div class="article-card-header">
|
||||
@@ -18,14 +14,9 @@
|
||||
{{#if faviconUrl}}
|
||||
<div class="icon-container">
|
||||
<i class="fas fa-circle-notch fa-spin icon-placeholder" aria-hidden="true"></i>
|
||||
<img
|
||||
class="favicon-icon"
|
||||
src="{{faviconUrl}}"
|
||||
alt="{{name}} favicon"
|
||||
loading="lazy"
|
||||
<img class="favicon-icon" src="{{faviconUrl}}" alt="{{name}} favicon" loading="lazy"
|
||||
onload="this.classList.add('loaded'); this.previousElementSibling.classList.add('hidden');"
|
||||
onerror="this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');"
|
||||
/>
|
||||
onerror="this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');" />
|
||||
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} icon-fallback" aria-hidden="true"></i>
|
||||
</div>
|
||||
{{else}}
|
||||
@@ -36,14 +27,9 @@
|
||||
{{#ifHttpUrl url}}
|
||||
<div class="icon-container">
|
||||
<i class="fas fa-circle-notch fa-spin icon-placeholder" aria-hidden="true"></i>
|
||||
<img
|
||||
class="favicon-icon"
|
||||
src="{{faviconUrl url}}"
|
||||
alt="{{name}} favicon"
|
||||
loading="lazy"
|
||||
<img class="favicon-icon" src="{{faviconUrl url}}" alt="{{name}} favicon" loading="lazy"
|
||||
onload="this.classList.add('loaded'); this.previousElementSibling.classList.add('hidden');"
|
||||
onerror="if (!this.dataset.faviconFallbackTried) { this.dataset.faviconFallbackTried = '1'; this.src = '{{faviconFallbackUrl url}}'; return; } this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');"
|
||||
/>
|
||||
onerror="if (!this.dataset.faviconFallbackTried) { this.dataset.faviconFallbackTried = '1'; this.src = '{{faviconFallbackUrl url}}'; return; } this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');" />
|
||||
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} icon-fallback" aria-hidden="true"></i>
|
||||
</div>
|
||||
{{else}}
|
||||
@@ -54,14 +40,9 @@
|
||||
{{#ifHttpUrl url}}
|
||||
<div class="icon-container">
|
||||
<i class="fas fa-circle-notch fa-spin icon-placeholder" aria-hidden="true"></i>
|
||||
<img
|
||||
class="favicon-icon"
|
||||
src="{{faviconUrl url}}"
|
||||
alt="{{name}} favicon"
|
||||
loading="lazy"
|
||||
<img class="favicon-icon" src="{{faviconUrl url}}" alt="{{name}} favicon" loading="lazy"
|
||||
onload="this.classList.add('loaded'); this.previousElementSibling.classList.add('hidden');"
|
||||
onerror="if (!this.dataset.faviconFallbackTried) { this.dataset.faviconFallbackTried = '1'; this.src = '{{faviconFallbackUrl url}}'; return; } this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');"
|
||||
/>
|
||||
onerror="if (!this.dataset.faviconFallbackTried) { this.dataset.faviconFallbackTried = '1'; this.src = '{{faviconFallbackUrl url}}'; return; } this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');" />
|
||||
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} icon-fallback" aria-hidden="true"></i>
|
||||
</div>
|
||||
{{else}}
|
||||
@@ -93,7 +74,8 @@
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/ifCond}}
|
||||
<p>{{#if description}}{{description}}{{else}}{{extractDomain url}}{{/if}}</p>
|
||||
<p>{{#if
|
||||
description}}{{description}}{{else}}{{extractDomain url}}{{/if}}</p>
|
||||
</div>
|
||||
{{else}}
|
||||
{{!-- projects:代码仓库风格卡片(保留 data-* 结构,便于扩展识别与写回) --}}
|
||||
@@ -103,13 +85,15 @@
|
||||
<div class="repo-title">{{#if name}}{{name}}{{else}}未命名项目{{/if}}</div>
|
||||
</div>
|
||||
|
||||
<div class="repo-desc">{{#if description}}{{description}}{{else}}{{extractDomain url}}{{/if}}</div>
|
||||
<div class="repo-desc">{{#if
|
||||
description}}{{description}}{{else}}{{extractDomain url}}{{/if}}</div>
|
||||
|
||||
{{#ifCond language '||' stars}}
|
||||
<div class="repo-stats">
|
||||
{{#if language}}
|
||||
<div class="stat-item">
|
||||
<span class="lang-dot" style="background-color: {{#if languageColor}}{{languageColor}}{{else}}#909296{{/if}};"></span>
|
||||
<span class="lang-dot"
|
||||
style="background-color: {{#if languageColor}}{{languageColor}}{{else}}#909296{{/if}};"></span>
|
||||
{{language}}
|
||||
</div>
|
||||
{{/if}}
|
||||
@@ -136,14 +120,9 @@
|
||||
{{#if faviconUrl}}
|
||||
<div class="icon-container">
|
||||
<i class="fas fa-circle-notch fa-spin icon-placeholder" aria-hidden="true"></i>
|
||||
<img
|
||||
class="favicon-icon"
|
||||
src="{{faviconUrl}}"
|
||||
alt="{{name}} favicon"
|
||||
loading="lazy"
|
||||
<img class="favicon-icon" src="{{faviconUrl}}" alt="{{name}} favicon" loading="lazy"
|
||||
onload="this.classList.add('loaded'); this.previousElementSibling.classList.add('hidden');"
|
||||
onerror="this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');"
|
||||
/>
|
||||
onerror="this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');" />
|
||||
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} icon-fallback" aria-hidden="true"></i>
|
||||
</div>
|
||||
{{else}}
|
||||
@@ -154,14 +133,9 @@
|
||||
{{#ifHttpUrl url}}
|
||||
<div class="icon-container">
|
||||
<i class="fas fa-circle-notch fa-spin icon-placeholder" aria-hidden="true"></i>
|
||||
<img
|
||||
class="favicon-icon"
|
||||
src="{{faviconUrl url}}"
|
||||
alt="{{name}} favicon"
|
||||
loading="lazy"
|
||||
<img class="favicon-icon" src="{{faviconUrl url}}" alt="{{name}} favicon" loading="lazy"
|
||||
onload="this.classList.add('loaded'); this.previousElementSibling.classList.add('hidden');"
|
||||
onerror="if (!this.dataset.faviconFallbackTried) { this.dataset.faviconFallbackTried = '1'; this.src = '{{faviconFallbackUrl url}}'; return; } this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');"
|
||||
/>
|
||||
onerror="if (!this.dataset.faviconFallbackTried) { this.dataset.faviconFallbackTried = '1'; this.src = '{{faviconFallbackUrl url}}'; return; } this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');" />
|
||||
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} icon-fallback" aria-hidden="true"></i>
|
||||
</div>
|
||||
{{else}}
|
||||
@@ -172,14 +146,9 @@
|
||||
{{#ifHttpUrl url}}
|
||||
<div class="icon-container">
|
||||
<i class="fas fa-circle-notch fa-spin icon-placeholder" aria-hidden="true"></i>
|
||||
<img
|
||||
class="favicon-icon"
|
||||
src="{{faviconUrl url}}"
|
||||
alt="{{name}} favicon"
|
||||
loading="lazy"
|
||||
<img class="favicon-icon" src="{{faviconUrl url}}" alt="{{name}} favicon" loading="lazy"
|
||||
onload="this.classList.add('loaded'); this.previousElementSibling.classList.add('hidden');"
|
||||
onerror="if (!this.dataset.faviconFallbackTried) { this.dataset.faviconFallbackTried = '1'; this.src = '{{faviconFallbackUrl url}}'; return; } this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');"
|
||||
/>
|
||||
onerror="if (!this.dataset.faviconFallbackTried) { this.dataset.faviconFallbackTried = '1'; this.src = '{{faviconFallbackUrl url}}'; return; } this.classList.add('error'); this.previousElementSibling.classList.add('hidden'); this.nextElementSibling.classList.add('visible');" />
|
||||
<i class="{{#if icon}}{{icon}}{{else}}fas fa-link{{/if}} icon-fallback" aria-hidden="true"></i>
|
||||
</div>
|
||||
{{else}}
|
||||
@@ -195,7 +164,8 @@
|
||||
|
||||
<div class="site-card-content">
|
||||
<h3>{{#if name}}{{name}}{{else}}未命名站点{{/if}}</h3>
|
||||
<p>{{#if description}}{{description}}{{else}}{{extractDomain url}}{{/if}}</p>
|
||||
<p>{{#if
|
||||
description}}{{description}}{{else}}{{extractDomain url}}{{/if}}</p>
|
||||
</div>
|
||||
{{/ifEquals}}
|
||||
{{/ifEquals}}
|
||||
|
||||
Reference in New Issue
Block a user