feat: 书库导入导出/图片变体/书籍搜索/小组件运行时等

新增:
- 书库导入导出(library_import/library_export)及测试
- 图片变体生成(image_variants)与响应式图片(responsiveImage)
- 书籍搜索(bookSearch)+ BookSearch/LibrarySearchGrid 组件
- 小组件运行时(widgetRuntime)与静态检查(widgetLint)
- 上传缓存中间件(upload_cache)与字体 CSS 提取脚本

其它:
- 后端 handlers/services 全量调整
- 前端页面、组件、库函数与配置更新
This commit is contained in:
2026-10-01 03:06:05 +08:00
parent 7fbd6ba699
commit ff2ab286fb
141 changed files with 7527 additions and 1407 deletions

View File

@@ -16,11 +16,26 @@ import { userPath } from "@/lib/urlStyle";
import { apiGetUserProfile, apiOpenDirectChat, type UserProfile, type User } from "@/lib/api";
import { toast } from "@/lib/toast";
/** 计算禁言剩余时间,返回「x天x小时x分钟」格式 */
function formatMuteRemain(untilIso: string): string {
const until = new Date(untilIso).getTime();
const diff = until - Date.now();
if (diff <= 0) return "即将";
const days = Math.floor(diff / 86_400_000);
const hours = Math.floor((diff % 86_400_000) / 3_600_000);
const minutes = Math.floor((diff % 3_600_000) / 60_000);
if (days > 0) return `${days} 天`;
if (hours > 0) return `${hours} 小时`;
return `${minutes} 分钟`;
}
export type ChatUserCardTarget = {
user: User;
/** 群内身份(可从成员表查到时携带) */
roomRole?: "owner" | "admin" | "member";
muted?: boolean;
/** 禁言截止时间(ISO 字符串);null/缺省表示永久 */
mutedUntil?: string | null;
};
/**
@@ -149,6 +164,9 @@ export default function ChatUserCard({
style={{ color: "var(--ink-3)" }}
>
<VolumeX size={12} /> 禁言中
{target.mutedUntil && (
<span>· {formatMuteRemain(target.mutedUntil)}后解除</span>
)}
</p>
)}
</div>