feat: 登录设备管理与编辑器图片上传

- 新增登录设备列表与会话下线(ListLoginDevices/RevokeLoginDevice),按设备指纹识别当前会话
- Markdown 编辑器新增图片插入弹窗与图片上传(ImageInsertModal)
- 实时总线、账号守卫与用户事件适配
This commit is contained in:
2026-09-26 01:10:10 +08:00
parent 5270fee2b7
commit 31f5360c26
12 changed files with 706 additions and 165 deletions

View File

@@ -202,7 +202,10 @@ export default function MdCodeBlock({ children, className, ...rest }: MdCodeBloc
const host = hostRef.current;
const pre = host?.querySelector("pre") ?? null;
setLineCount(countLines(pre, plainText));
setLanguage(readLanguage(pre, className, dataLanguage));
// clientHtml 替换后的 shiki pre 不带 data-language / language- class,
// 读不到语言时保留原值,避免标签从真实语言回退成 "text"
const next = readLanguage(pre, className, dataLanguage);
setLanguage((prev) => next || prev);
setFoldReady(true);
}, [plainText, className, dataLanguage, clientHtml]);
@@ -222,7 +225,11 @@ export default function MdCodeBlock({ children, className, ...rest }: MdCodeBloc
// SSR 未真正着色(如自定义语言 loadLanguage 失败回退)时,客户端补双主题
useEffect(() => {
if (serverHighlighted || !plainText.trim()) return;
const lang = readLanguage(null, className, dataLanguage) || "plaintext";
// react-markdown 把 language-xxx 放在子 code 上,el=null 读不到,
// 必须从挂载后的 DOM 取语言,否则永远按 plaintext 补色(代码块无色)
const lang =
readLanguage(hostRef.current?.querySelector("pre") ?? null, className, dataLanguage) ||
"plaintext";
let cancelled = false;
(async () => {
try {