|
|
b98059edcc
|
fix(assets): 引用徽标加 z-10 修复被 blur-img 展示层遮盖
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
灯箱改造(051ca94)把缩略图换成 .blur-img 双层结构后,.blur-img-full
自带 z-index:1,而徽标 span 是 z-index:auto,绘制序被图片压过,
导致「孤儿 / 被 N 篇引用」徽标在 DOM 中存在但不可见。
浏览器实测:修复前 elementsFromPoint 命中 blur-img-full,
修复后命中徽标本身,琥珀色孤儿药丸恢复显示。
|
2026-07-24 18:31:45 +08:00 |
|
|
|
99d33b54d2
|
fix(lightbox): 图集切换不重算几何导致图片压扁
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
gotoIndex 换图时只替换 src,target/baseW/baseH/transform 全部沿用
第一张图的几何,宽高比不同的图被套用错误的 scale(显示宽高比 =
自身宽高比 × 首图宽高比)。同时 .lightbox-img 的 object-fit: contain
与 transformFor 依赖的「位图填满布局盒」前提冲突,素材网格的方形
缩略图容器下首图即被压扁。
修复:
- swap() 在新图加载后按 naturalWidth/Height 重算 fitCentered 目标,
布局盒设为 target 尺寸、transform 归位 scale(1,1),baseW/H 同步
供关闭飞回动画复用
- 先换 src 再判 complete,避免用旧图的 complete 误判缓存命中
- 移除 .lightbox-img 的 object-fit: contain,恢复位图填满布局盒
新增回归测试:切换后几何必须按新图宽高比重算(红绿验证通过)
|
2026-07-24 18:15:11 +08:00 |
|
|
|
051ca947e3
|
feat(assets): 素材网格接入灯箱预览
- 缩略图改为与前台正文图一致的 .blur-img 双层结构(?w=20 占位 +
?thumb=300x300 展示层 data-src),点击由全局注入的 lightbox.js 接管;
图集模式(当前页内 ←/→ 切换),灯箱加载原图(data-src 去 query)。
- 数据异步到达/筛选/翻页/刷新后由 use_effect 调 __initLightbox 重新绑定
(双保险契约同 PostContent:__lightboxSelectors + 直接调用兜底)。
- lightbox.ts 补 data-lb-bound 幂等守卫:Dioxus keyed diff 复用节点时
重复初始化会叠加 click 监听(点一次开两次);附回归测试。
- invoke_optional_global 提取为 src/utils/js.rs 共享,post_content 复用。
浏览器实测:47 张绑定齐全,原图打开/图集切换/Esc 关闭正常,
筛选重建与重建索引重取后重绑幂等(单次点击仅 append 一次 overlay)。
|
2026-07-24 17:39:34 +08:00 |
|
|
|
fa40f4bd95
|
fix(assets): uuid 参数序列化失败导致上传 500
tokio-postgres 将 $1::uuid 的参数推断为 uuid 类型,String 无法
序列化(缺类型桥接),INSERT assets 报 error serializing parameter 0,
补偿删文件后返回 500。list/delete/purge/rebuild 的 $1::uuid 与
ANY($1::uuid[]) 全是同一颗未引爆的雷。
正路:tokio-postgres 启用 with-uuid-1,SQL 侧去掉全部 ::uuid cast,
Rust 侧全程 Uuid 类型,仅在 serde DTO 边界(WASM 共享模型)转 String;
外部传入的 id 在边界 parse_str,非法 id 走业务错误而非 500。
|
2026-07-24 17:04:35 +08:00 |
|
|
|
8f75a64bfd
|
fix(assets): SUM(size_bytes) 显式转 bigint 修复列表 500
Postgres 的 SUM(bigint) 返回 NUMERIC,tokio-postgres 反序列化为
i64 时类型不匹配,row.get(3) 直接 panic(release 的 panic=abort
下会崩掉整个进程)。COALESCE(..., 0)::bigint 修正。
|
2026-07-24 16:50:30 +08:00 |
|
|
|
45d70e1dc6
|
refactor(assets): 清理 lint,对齐项目惯例
- api/assets/mod.rs 按 posts/comments 惯例加
#![allow(clippy::unused_unit, deprecated, unused_imports)]
(#[server] 宏固有噪音)
- 页面交互导入按 target_arch = wasm32 门控;组件级放行非 WASM 未用绑定
- 合并 check_upload_dimensions 进 upload_dimensions(单函数返回尺寸,
消除死代码),测试同步更名
|
2026-07-24 16:44:47 +08:00 |
|
|
|
5612f16425
|
feat(assets): 封面「从素材库选择」联动
- AssetPickerModal:素材网格(最新 60 + 搜索)单击回填封面 URL,
内嵌「上传新图」(复用 upload_image_file,成功即选中,
失败留在 modal 内提示),上传期间置位 cover_uploading 拦截保存
- CoverUploader 空态加「素材库」入口(与「输入链接」并列)
|
2026-07-24 16:32:43 +08:00 |
|
|
|
0c47e87d30
|
feat(assets): 全量重建索引
- rebuild_assets_index:扫 uploads/(跳过点目录)→ upsert assets
(xmax=0 判别新增;技术字段 IS DISTINCT FROM 才更新,保留 alt,
幂等重跑 updated=0)→ 删文件已消失的行 → 全表扫 posts 重建 refs
- 磁盘扫描与 header 尺寸读取移入 spawn_blocking
- extract_asset_paths 提升 pub(crate) 供重建复用同一提取逻辑
- 前端「重建索引」按钮(进行中禁点,结果进横幅)
|
2026-07-24 16:27:02 +08:00 |
|
|
|
799455da43
|
feat(assets): 删除保护、孤儿清理与 alt 编辑
- delete_asset:引用中业务拒绝(Ok(success:false) + 引用文章列表),
孤儿硬删除(文件 + DB 行级联 refs + 派生缓存)
- purge_orphan_assets:一键清理无引用且超过 7 天保护窗的素材,
容忍单项文件删除失败,返回数量/释放字节/失败数
- update_asset_alt:管理性 alt 编辑
- invalidate_asset_caches:IMAGE_CACHE 前缀批量失效 + 尺寸缓存失效;
磁盘派生缓存(哈希命名不可枚举)由既有清理任务回收
- 前端:卡片操作区三态(复制/alt/删除 ↔ alt 编辑 ↔ 确认删除),
顶栏「清理孤儿」两步确认(带数量与总大小),操作结果横幅
|
2026-07-24 16:23:00 +08:00 |
|
|
|
99b96e7a0c
|
feat(assets): 素材管理只读列表页
- list_assets:引用状态筛选 + filename/alt 搜索 + 时间/大小排序 +
分页(60/页),一次响应带齐 tabs 计数与可清理孤儿统计
- /admin/assets 网格页:?thumb=300x300 缩略图卡片 + 引用徽标,
单路由客户端分页(对齐管理文章模式),导航加「素材」入口
|
2026-07-24 16:15:19 +08:00 |
|
|
|
0c811efd4d
|
feat(assets): 素材注册表数据层
- migration 015:assets 元数据注册表 + asset_refs 引用关联表
- upload_image 落盘后登记 assets(失败补偿删文件);尺寸校验复用
upload_dimensions 一次解析同时返回 (w,h)
- sync_asset_refs 镜像 sync_tags:文章 create/update 事务内
解析 content_html + cover_image 的 /uploads/ 路径重建引用
- Asset/AssetDto/AssetFilter/AssetSort 共享模型,id 以 String 承载
(SQL 侧 ::uuid cast,避免 uuid crate 进 WASM 构建)
|
2026-07-24 16:07:07 +08:00 |
|
|
|
adde92e15c
|
docs(assets): 素材管理功能设计规格
grilling 访谈确认的结论落盘:assets/asset_refs 双表、
sync_tags 模式引用同步、引用中禁删 + 孤儿 7 天窗一键清理、
手动重建索引回填、封面联动(编辑器正文选择器不做)。
|
2026-07-24 15:26:56 +08:00 |
|
|
|
de4bfafb72
|
chore: release v0.6.2
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
v0.6.2
|
2026-07-24 14:02:10 +08:00 |
|
|
|
2b048b94a3
|
fix(utils): sleep_ms 的 cfg guard 对齐 feature 模型,修复 web-only 构建失败
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
sleep_ms 原用 #[cfg(not(target_arch = "wasm32"))] guard tokio::time::sleep,
但 tokio 是 server-only 的 optional 依赖。此 guard 会在「非 wasm32 主机 + 仅
web feature」组合(如 cargo build --no-default-features --features web)下
误激活,此时 tokio 未引入,导致编译失败。
该 bug 长期被 [dev-dependencies] tokio 掩盖——cargo test / 开发构建总能拿到
tokio,只有发布/生产构建(排除 dev-deps)才暴露。符合 AGENTS.md 第 1 条
dual-target gating 标准:触及 server-only 依赖的代码必须用 #[cfg(feature = "server")]
而非 target_arch 区分。
修复:tokio 版本改为 #[cfg(all(feature = "server", not(target_arch = "wasm32")))],
并为无效构建组合「非 wasm32 且非 server」补一个 panic stub(该组合不是有效部署
目标,web feature 真实构建目标即 wasm32;stub 仅保证符号可编译,运行时不会触发)。
调用点 runner.rs 的 server 占位分支(通过 sleep_ms 继承同一问题)无需改动——
它只在 server target 下编译运行,sleep_ms 修复后其调用自然可编译。
|
2026-07-24 10:52:51 +08:00 |
|
|
|
5a10b07bff
|
fix(router): 修复错误冒泡触发的 404 页「返回首页」卡死
PostDetail 对不存在的 slug 抛出 ServerFnError(404) 后,ErrorLayout 的
ErrorBoundary 捕获错误并在 fallback 渲染 NotFound。此时 ErrorBoundary 持有错误,
根据 Dioxus 0.7.9 的渲染逻辑(有错则永远渲染 fallback,不渲染 children/Outlet),
点击 Link 切换路由虽更新了 URL,但页面仍停留在 fallback,表现为「URL 变了页面不变」。
根因:ErrorBoundary 捕获错误后不会自动恢复,必须显式调用 clear_errors() 才能
重新渲染 children。原实现用 Link 导航无法干预点击时机清除错误。
修复:NotFound 与 ErrorLayout 的通用错误 fallback 中,「返回首页」改用 button,
onclick 内先 clear_errors() 清除错误边界,再 navigator().push 导航。
- NotFound 通过 try_consume_context::<ErrorContext>() 获取可能存在的错误边界:
路由匹配命中的 404 无错误(clear_errors 是 no-op),错误冒泡的 404 才需清除。
- ErrorLayout 的 handle_error 闭包参数本身就是 ErrorContext,clone 后在按钮内使用。
|
2026-07-24 10:34:18 +08:00 |
|
|
|
f9cf1f71e4
|
chore: release v0.6.1
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
mhchem panic 修复 + 非测试代码 unwrap 消除的 patch 发布。
### Changed
- 消除非测试代码中的裸 unwrap(),改写为带不变量说明的 expect()
### Fixed
- mhchem 转译器三处 panic(正则转录、多字节字符越界、re! 宏降级)
- Docker 构建缺 patches/ 目录导致 pnpm install ENOENT
v0.6.1
|
2026-07-23 18:29:54 +08:00 |
|
|
|
d80664db5e
|
docs(agents): 新增规范 #16——非测试代码禁止 unwrap
panic="abort" 让进程级 panic 无法恢复,裸 unwrap 与之冲突。新增第 16 条
明确:
- 默认走 ? / Result,经 AppError 或 Option 映射失败
- .expect("reason") 仅用于真正的不变量,消息必须解释为何不可能失败
- LazyLock 编译期常量、WASM 浏览器上下文、#[cfg(not(server))] stub、
bin 工具与 #[cfg(test)] 为豁免集
- 预留 clippy unwrap_used/expect_used lint 的 allow 范围
|
2026-07-23 18:26:24 +08:00 |
|
|
|
690e00e194
|
refactor(unwrap): 消除非测试代码中的裸 .unwrap(),改为带不变量说明的 .expect()
panic="abort" 下任何 panic 直接杀进程,与 unwrap 的「稍后处理」语义冲突。
本轮把所有可证不变量的裸 unwrap 改写为带说明的 expect,并修复 time.rs 中
u32→i32 转换在大 ms 值下的潜在溢出 panic(clamp 到 i32::MAX)。
- src/utils/time.rs: sleep_ms 的 ms.try_into().unwrap() 改为 clamp 到
i32::MAX,避免超大延时值溢出;window/set_timeout 的 expect 补充上下文说明
- src/utils/text.rs: 6 个静态正则的 unwrap 改为 expect,统一说明「编译期校验」
- src/api/{auth,comments/helpers}.rs: EMAIL_REGEX 同上
- src/api/markdown.rs: IMG_RE / TABLE_RE 同上
- src/api/rate_limit.rs: NonZeroU32::new(val.max(1)) 与 Quota::with_period
补充不变量说明
- src/api/image.rs: HeaderValue::from_str(&etag).unwrap() 改为 expect 说明
etag 仅含 ASCII hex;复用 etag_value 避免 clone
- src/middleware.rs: 静态 302 重定向响应的 unwrap 改为 expect
- src/components/post/post_content.rs、src/theme.rs: WASM 上下文 window().unwrap()
改为 expect 说明仅浏览器执行
保留的 unwrap/expect(合理不变量,已在注释中说明):
- DB_POOL LazyLock 闭包(已有详细注释 + validate_database_url 前置校验)
- csrf.rs 静态 forbidden 响应
- highlight.rs syntect 内置 Plain Text 语法
- bin/generate_highlight_css 构建工具
- comments/* 的 #[cfg(not(server))] unreachable!() WASM stub
|
2026-07-23 18:26:19 +08:00 |
|
|
|
d1013dfa08
|
refactor(mhchem): re! 宏编译失败降级为不匹配,补上 panic=abort 缺失的防御
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
re! 宏原先 LazyLock::new(|| Regex::new($pat).unwrap()),在 panic="abort" 下
任何正则转录错误(如曾经的 ". __* " InvalidClass)都会直接杀进程,且 to_tex
的 catch_unwind 在 abort 模式下无法兜住。
引入 compile_or_log(pat) -> Option<Regex>:编译失败记 tracing::error 日志
并返回 None(绝不 panic)。封装为 CompiledPat(Option<Regex>) newtype,把
「坏正则降级为不匹配」的语义集中在一处:
- captures_head(input) -> Option<Captures>:坏正则/未匹配统一 None
- is_match(input) -> bool:坏正则/未匹配统一 false
re! 宏改为产出 &CompiledPat,调用方直接 re!("...").captures_head(...) /
.is_match(...),无需再到处写 re_ref(...)? 或手写 None 回退。状态机遇到
坏模式自然退到 "else" 兜底分支产出原样字符,不影响整体渲染。
适配点:Pat::Re、pat_match_head、match_pattern 的 re_match 闭包、
pat_enumber/state_of_aggregation/amount/formula、1/2 动作、mhchem_tables.rs
的 ce_action、3 个 static LETTERS/GREEK/ONE_GREEK_RE。
回归测试 compile_or_log_degrades_bad_regex_without_panic 断言坏正则返回 None
不 panic。clippy 干净,622 测试全过。
|
2026-07-23 18:16:22 +08:00 |
|
|
|
a03fbac88c
|
fix(mhchem): 修复 find_observe_end 逐字节扫描多字节字符的 panic
find_observe_end 用 `let bytes = input.as_bytes(); let mut i = start; while i < len { ...; i += 1; }`
逐字节步进,对每个位置做 `&input[i..]` 切片与 `bytes[i] as char` 花括号判定。
化学公式若含多字节 UTF-8 字符(如中文「浓」占 3 字节),i 会落在字符内部:
- `&input[i..]` 触发 `byte index N is not a char boundary; it is inside '浓'` panic;
- 在 panic=abort 下直接 SIGABRT 杀进程;
- `bytes[i] as char` 把多字节字符首字节误判为花括号,还静默损坏输出。
触发路径:rebuild_content_html 重建含中文化学式(如 \ce{浓H2SO4})的文章时崩溃。
修复:改用 `input.char_indices().skip_while(...)` 按字符边界步进,i 始终落在
char 边界上,`a` 直接是解出的 char。start 校验对齐到边界后跳过。
新增:
- 回归测试 ce_multibyte_char_in_braces_does_not_panic:覆盖触发输入。
- 保护性测试 ce_arbitrary_multibyte_does_not_panic:穷举 emoji/日韩文/组合字符/
四字节区,覆盖花括号/方括号/\frac/$...$ 全部走 find_observe_end 的路径。
注:多字节字符的状态机转译输出质量(如 ce("浓H2SO4") 的字符重复)是独立的
既有问题,mhchemParser 本为 ASCII 化学符号设计;本提交只修 panic,不扩大范围。
|
2026-07-23 18:00:41 +08:00 |
|
|
|
feaf76bcd9
|
fix(mhchem): 修复 ". __* " 正则转录错误导致的 panic=abort 崩溃
mhchem 转译器中 ". __* " 模式的 regex 被误抄成
^([.\u{22C5}\u{00B7}\u{2022}|[*])\s*
即把 alternation 的 `|` 放进了字符类内部。fancy-regex 把 `[...|[*]`
当作单个畸形字符类,报 ParseError(20, InvalidClass);`re!` 宏的
`.unwrap()` 因此 panic。
上游 mhchemParser 4.2.2 是 `[...]|[*]`(alternation 在类外),修正为
^([.\u{22C5}\u{00B7}\u{2022}]|[*])\s*
触发路径:用户提交含 `\ce{·}`/`\ce{•}`/`\ce{.}` 等化学式的文章 →
render_markdown_enhanced → katex::render_* → expand_chem → mhchem::ce,
首次编译该 LazyLock 正则即 panic。
关键放大因素:本项目 release profile 为 panic="abort",`to_tex` 内的
`std::panic::catch_unwind` 在此设置下无法捕获 panic,进程直接 SIGABRT。
修正了 `ce`/`pu` 的文档注释,不再谎称"绝不 panic"。
新增:
- 回归测试 ce_dot_bullet_bond_inputs_dont_panic:覆盖触发输入。
- 保护性测试 all_match_patterns_compile:强制编译 match_pattern 全部分支,
任何同类转录错误在测试期而非生产期暴露。
|
2026-07-23 17:49:20 +08:00 |
|
|
|
3e9a3aa70d
|
docs(mhchem): 修复模块级文档中的 broken intra-doc links
cargo doc 在 --document-private-items 下报 7 个 unresolved link 警告,
全部源自 src/api/mhchem.rs 的模块级 //! 文档:
- 公开 API ce/pu 用简写 [`ce`] 无法解析,改用全路径链接
[`ce`](crate::api::mhchem::ce) / [`pu`](crate::api::mhchem::pu)。
- 私有项 Buffer/Parsed/Field/find_observe_groups/build_transitions 即便
写全路径也无法被 intra-doc link 解析器识别(rustdoc 对私有项的限制),
改回普通反引号代码,不再生成链接。这些是模块内部实现细节,本就无需
跨项跳转。
修复后 make doc 零警告。
|
2026-07-23 17:48:06 +08:00 |
|
|
|
089fe88a75
|
fix(docker): 复制 patches/ 目录以修复 pnpm install ENOENT
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
pnpm-workspace.yaml 声明了指向 patches/@tiptap__markdown@3.27.3.patch 的
patched 依赖,但 Docker 构建在 COPY package manifest 阶段未包含
patches/ 目录,导致 pnpm install --frozen-lockfile 因找不到 patch
文件而失败。将 libs/patches/ 加入 COPY 即可。
|
2026-07-23 16:45:00 +08:00 |
|
|
|
970ff8dc5b
|
chore: release v0.6.0
v0.6.0
|
2026-07-23 16:33:07 +08:00 |
|
|
|
f2216119ae
|
chore(markdown): 格式化测试中的长字符串字面量
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
|
2026-07-23 16:30:31 +08:00 |
|
|
|
07d737af0f
|
fix(markdown): 修复窄表格在 table-wrap 内边框被裁切
overflow-x:auto 会强制 overflow-y 也建滚动盒,裁切 table 向外 spread 的 1px
box-shadow 描边,导致不需要滚动的窄表格右下角边框断裂。
给 .table-wrap 加 padding:1px 为描边预留空间,负 margin:-1px 抵消外部尺寸
偏移,使 wrap 不撑大父容器、左上仍贴齐正文边缘。
|
2026-07-23 16:18:12 +08:00 |
|
|
|
485d4b1cc4
|
fix(markdown): 修复移动端表格无法横向滚动
宽表格在移动端被外层 <main> 的 overflow-hidden 直接裁掉,既看不到也滚不到。
根因是三层叠加:裸 table 无滚动容器、外层裁剪、th 强制 nowrap 加剧超宽。
- markdown.rs: 新增 wrap_tables,给每个 table 套 <div class=table-wrap>
滚动容器(仿 wrap_images_with_blur 模式,sanitizer 放行 div+class)
- input.css: .table-wrap 设 overflow-x:auto;table 加 min-width:100% 让宽表
撑开触发滚动;768px 下对旧文章裸 table (.md-content > div > table) 兜底
block 化,新文章因多一层 div 不受影响、圆角视觉完整
- frontend_layout.rs: main 的 overflow-hidden 改 overflow-x-clip,只裁 x
且不建滚动容器,让内部 .table-wrap 滚动生效
旧文章经 CSS 兜底立即修复;新文章用渲染器包裹视觉完整。可在 /admin/posts
点 rebuild all 让旧文章也升级到 div 包裹。
|
2026-07-23 16:07:11 +08:00 |
|
|
|
77a22bbfb1
|
feat(editor): tiptap 代码块 mermaid 实时预览
mermaid 代码块( ```mermaid )在富文本编辑器里此前只是普通源码,作者看不到
渲染效果。现在像数学公式、脚注一样在编辑器内直接预览:源码下方加预览区,
debounce 500ms 后渲染成 SVG,跟随站点 light/dark 主题切换,与前台文章页
渲染视觉完全一致。
一致性保证(核心):
- 同一份 mermaid 运行时:编辑器与前台都加载 /mermaid/mermaid.js(11.16.0)
- 同一套主题变量:经 @yggdrasil/shared 单一真相源(上一提交下沉)
- 同一份渲染配置:theme:'base' + securityLevel:'strict' + flowchart.curve:'basis'
- 同一主题切换机制:监听 THEME_CHANGE_EVENT(与 codemirror-editor 同范式)
实现:
- mermaid.ts:加载 bundle + renderMermaid(source, theme) 封装,与前台同逻辑
- code-block-view.ts:mermaid 块构造时建预览区;源码变化 debounce 重渲染;
renderToken 防竞态(快速改动只保留最新);主题事件重渲染;language 切入/切出
mermaid 时创建/移除预览区;destroy 清理 timer+监听+token
- 渲染失败显示错误态(accent 色左边框),源码仍可编辑修正
- 样式对齐前台 pre[data-mermaid-rendered]:flex 居中、SVG 等比缩放、Latte/Mocha 双主题
- 测试 mock mermaid 运行时,覆盖预览区生命周期、debounce、竞态、主题、错误、清理
服务端 markdown.rs / Dioxus.toml / Rust-JS 桥零改动。
|
2026-07-23 15:13:07 +08:00 |
|
|
|
800129e245
|
refactor(shared): 下沉 mermaid Catppuccin 主题变量到 shared 包
前台 yggdrasil-core 的 LATTE_VARS/MOCHA_VARS 是 mermaid 配色唯一真相源。
后台 tiptap 编辑器要实现 mermaid 预览且与前台视觉一致,需复用同一套变量;
若复制一份会立即产生漂移风险,而「主题一致性」正是编辑器预览的核心目标。
按 shared 包「单一真相源」的设计意图,把两套主题变量(~100 行)+ MermaidThemeVariables
类型 + mermaidThemeVarsFor(theme) 工具函数搬到 shared。yggdrasil-core 改 import
自 shared,删除本地定义——行为零变化(同一份变量,只换引用源)。
前台 37 个测试全绿回归,含 10 个 mermaid 测试验证亮/暗主题传入正确调色板。
|
2026-07-23 15:12:48 +08:00 |
|
|
|
86781d8bbc
|
feat(editor): tiptap 富文本编辑器内脚注所见即所得
脚注 [^id] 引用与 [^id]: 内容 定义此前在富文本模式下只是裸文本,
作者无法看到渲染效果。现在像数学公式一样建模为 atom 节点,在编辑器内
直接预览:引用渲染成上标编号、定义渲染成可双击编辑的卡片块,编号按
引用首次出现顺序分配,与线上文章页 render_markdown_enhanced 的 GFM
模式一致。
- footnoteRef(inline): [^label] 解析成上标节点,双击浮层预览定义内容
- footnoteDef(block): [^label]: 内容 解析成卡片块,双击进入 textarea 编辑
- FootnoteNumbering: ProseMirror plugin,文档变化后重算编号表写 storage,
并直接遍历 DOM 刷编号文本(不 dispatch transaction,避免循环);编号是
派生视图,不进文档模型
- 多行定义序列化用 4 空格缩进续行,与 pulldown-cmark GFM 续行规则对齐
- 样式复用文章页 .fn-ref/.footnote-definition 视觉语义,限定 .tiptap-editor
作用域,Latte/Mocha 双主题
脚注改走 atom 节点的 renderMarkdown 后,不再经过 escapeMarkdownSyntax 的
文本转义路径,unescapeFootnoteSyntax 后处理及其三处调用随之移除(转义问题
从根上消失)。测试从旧的"转义+还原"模型重写为验证节点解析、序列化往返、
编号实时分配。服务端 markdown.rs / input.css 零改动。
|
2026-07-23 14:33:25 +08:00 |
|
|
|
f569258633
|
fix(editor): 隐藏无语言标识代码块的空工具栏顶栏
|
2026-07-23 14:00:42 +08:00 |
|
|
|
67406f661a
|
refactor(middleware): 抽出 ssr_generation/version_headers 中间件到 middleware.rs
main.rs 的 serve() 闭包内有两个内联 async fn 中间件
(ssr_generation_middleware、version_headers_middleware),与已抽出到
middleware.rs 的 add_cache_control/admin_guard 同类却散落在入口,无法独立测试。
将其移入 middleware.rs 作为 pub(crate) 函数,serve() 闭包改用全路径
crate::middleware::xxx 引用(与现有中间件一致),行为不变。
签名归一:原内联用 axum::http::Request<Body>,迁移后改用
axum::extract::Request(与 add_cache_control/admin_guard 一致;二者是同一
类型的别名,from_fn 接受不变)。
挂载点说明(版本头置于最外层合并 router)留在 main.rs 调用处注释,描述的是
挂载决策而非函数本身。
|
2026-07-23 13:56:51 +08:00 |
|
|
|
b25abcb9d8
|
chore(deps): 更新 tokio 与 regex 依赖包版本
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
|
2026-07-23 13:38:10 +08:00 |
|
|
|
644db48d17
|
fix(lint): 修复 Biome 检查错误
- mermaid-renderer/src/index.ts: import 与后续语句间补空行(organizeImports)
- tiptap-editor footnote.test.ts: 局部 unescape 重命名为 unescapeFootnote,
避免遮蔽全局 restricted name(noShadowRestrictedNames)
|
2026-07-23 13:36:11 +08:00 |
|
|
|
db74a96de1
|
style: 格式化 Rust 与 TS 代码
|
2026-07-23 13:34:27 +08:00 |
|
|
|
17d596c512
|
refactor: 清理死代码与冗余,修复 mhchem 模块的 clippy 阻断
mhchem 机械移植引入了一个 deny-by-default 的 useless_attribute 硬错误
(mhchem_tables.rs 顶部的 outer #[allow] 挂在 use 语句上),导致
make lint / CI check job 失败。顺带清理同模块的死代码与风格项,以及仓库
内其他确认无引用的死代码。
- mhchem_tables.rs: 删除无效 outer allow、未使用的 `use super::*`、
pq_action 未用形参 buf→_buf;v.get(0)→v.first()
- mhchem.rs: 删除从未调用的 Field::empty;修正 &*RE→&RE、Default 字段
重赋值;Parsed/Out 加 large_enum_variant allow(镜像上游结构,
经 Vec 进堆,非栈热路径)
- codemirror_bridge.rs: 删除 SqlSchema/SqlTable 上过期的 dead_code
allow——get_db_schema 早已消费这两个类型
- ssr_cache.rs: 删除生产路径从未调用的 invalidate_ssr_home(首页已由
invalidate_ssr_all_public 覆盖),修正相关注释与测试
- auth.rs: 删除预留给未实现用户管理功能的 invalidate_user_sessions
验证:cargo clippy --all-targets --all-features -- -D warnings 通过;
mhchem/ssr_cache 共 20 项测试通过。
|
2026-07-23 13:32:46 +08:00 |
|
|
|
6276bab6c6
|
fix(sanitizer): 允许 KaTeX 渲染所需的 svg 与 path 标签及绘图属性
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
KaTeX 在 SSR 渲染根号(\sqrt)、矩阵竖线/括号(\begin{pmatrix})、大括号、上/下括号、箭头等复杂公式时,通过 <svg> 和 <path> 绘制线条与图标。
原先 HTML 消毒器 (sanitizer) 的标签白名单中未包含 svg / path,导致 lol_html 过滤正文与评论 HTML 时将 <svg> 和 <path> 剥离,致使根号顶线与矩阵括号丢失。
改动:
1. DEFAULT_ALLOWED_TAGS 增加 svg / path 标签放行
2. Sanitizer 属性白名单放行 svg (xmlns, width, height, viewbox, preserveaspectratio, style) 与 path (d) 绘图属性
3. 补充 sanitizer 与 markdown 单元测试防御回归
|
2026-07-23 13:27:13 +08:00 |
|
|
|
bcd13958ac
|
feat(katex): 移植 mhchem 化学公式转译器(\ce/\pu → LaTeX)
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
katex-rs 默认无 mhchem 解析器,\ce/\pu 化学公式渲染为红字(实测正确页面 648
的 137 个公式中 32 处化学公式坏掉,含 \ce×24 + \pu×8)。
从 mhchemParser 4.2.2(Apache-2.0)机械移植状态机转译器为纯 Rust:
- src/api/mhchem.rs:数据模型 + 模式匹配 + go() 主循环 + texify 输出 + 公开 API
- src/api/mhchem_tables.rs:14 个状态机转移表 + 机器局部动作(ce/pu/...)
- 模式含 lookahead (?=)/(?!),Rust regex crate 不支持,故用 fancy-regex
(已被 syntect 间接引入,此处显式声明为直接 server 依赖)
- ce/pu 包裹 catch_unwind,任何内部异常回退原样输出,绝不 panic
- katex.rs 新增 expand_chem:渲染前嵌套花括号配对扫描 \ce/\pu 预转译
- 行尾 ^ 气体符号转译为 \uparrow,消解原 mhchem 解析错误
- 8 个 mhchem 单测 + 5 个 katex 集成测试(水/反应箭头/气体箭头/单位/络离子)
|
2026-07-23 12:23:53 +08:00 |
|
|
|
c6999763c0
|
fix(editor): 修复 tiptap-markdown 过度转义导致显示损坏
@tiptap/markdown 的 escapeMarkdownSyntax 对每个非代码文本节点的 */_/>
无差别转义,导致 content_html→content\_html、>→>、字面星号被反复
转义累积,前台表格/加粗/引用块显示为裸 markdown 文本。转义幂等且自增强
(已转义的 \* 被 marked 解析为 escape token→再转义→粘住),只有源码模式
保存原文才能清除。
pnpm patch 持久化修补上游包(提交进 libs/patches/,pnpm install 自动应用):
- escapeMarkdownSyntax 改为 CommonMark 风向感知:只在真正构成强调开/闭
边界的位置转义 */_(一侧 space/punct、另一侧 other),词内位置不转义
- encodeHtmlEntities 降级为 encodeLimitedHtmlEntities:只编码 < 和 &,
移除 > (文本节点内的 > 不构成 blockquote,编码只会产生 > 损坏)
- 同步修补 src/dist ESM/dist CJS 三处
- 8 个往返单测覆盖词内下划线/行内>/字面星号/真实强调/多轮稳定
|
2026-07-23 12:04:25 +08:00 |
|
|
|
5e5583fe20
|
feat(katex): 注册 16 个物理学宏表
katex-rs 默认 Settings 无物理宏表,\vu \dv \dd \pdv \divg \curl \grad
\qty \RR \ZZ \NN \QQ \CC \bra \ket \braket \expval \abs \norm 渲染为红字
(实测正确页面 648 的 137 个公式中 48 处物理宏坏掉)。
- 新增 physics_macros() 表(MacroDefinition::StaticStr,参数自动推导)
- inline_settings/display_settings 构造后注入物理宏
- 刻意差异:\divg(散度)不复用 \div(除号),两者并存
- 覆写内置 \bra/\ket/\braket 为物理语义版本
- 6 个物理宏单测覆盖数集/微积分/Dirac 记号/向量/范数
|
2026-07-23 11:55:38 +08:00 |
|
|
|
68ac629f44
|
fix(sql-console): 写操作后全量失效相关缓存
SQL 控制台执行 UPDATE/DELETE 等 SQL 直改 DB 时绕过了 server function 的
正常缓存失效路径,导致 moka 单篇缓存(TTL 600s)与 SSR 磁盘缓存继续吐出
陈旧数据(含已删除文章),近 10 分钟的陈旧窗口。
- execute_sql 执行成功后,若含写语句则全量失效文章/搜索/评论缓存 +
SSR 公开页磁盘缓存 + bump 全局世代号
- cache.rs 新增 invalidate_all_comments(评论列表 + 待审数)
- 抽 writes_affect_cache 纯函数 + 3 个单测覆盖写/只读/混合语句
|
2026-07-23 11:49:52 +08:00 |
|
|
|
2b75ac4d28
|
fix(editor): 修复 tiptap 序列化转义脚注语法 [^id] → \[^id\] 导致失效
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
@tiptap/markdown 的 escapeMarkdownSyntax (MarkdownManager.ts:1112) 对所有
非代码文本节点做 [→\[、]→\] 转义,导致脚注引用/定义 [^id] 被破坏为 \[^id\],
pulldown-cmark 将其视为字面文本而非脚注语法,渲染出纯文本而非上标链接。
与数学公式节点同类问题(LaTeX 通过 atom Node + renderMarkdown 绕过),
但脚注是普通文本节点无法走相同路径,故在 getMarkdown 输出后做后处理:
将 \[^label\] 还原为 [^label],仅匹配脚注专属模式不影响普通链接语法。
同时修复数据库中已损坏的 markdown-syntax-test 和 markdown-quan-te-xing-
ce-shi-wen-zhang 两篇文章的 content_md,并通过 rebuild 全量重建 content_html。
|
2026-07-23 11:10:21 +08:00 |
|
|
|
c693d82bab
|
docs(agents): 重写 AGENTS.md 为完整的仓库指南
并行 scout 扫描 src/tests/configs/scripts/libs 五个区域,
综合整理为面向 AI 助手的 Repository Guidelines:架构与数据流、
关键目录、开发命令、配置与环境变量、代码约定、测试与 QA、CI。
保留原有 Workflow 与 JS 库说明,扩展为结构化文档。
|
2026-07-23 10:24:05 +08:00 |
|
|
|
68e290a9fe
|
feat(write): 新建文章默认发布状态改为直接发布
将 /admin/write 新建模式的默认发布状态从「存为草稿」改为「直接发布」。
编辑模式仍回填文章实际状态,不受影响。
|
2026-07-23 10:10:51 +08:00 |
|
|
|
f7e4d2f5ea
|
chore: release v0.5.0
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
数学公式(KaTeX)、mermaid 流程图、bun 代码运行器、文章脚注、Vue SFC 高亮等新功能;SSR 缓存失效根治、Docker 构建/部署修复、性能优化系列(mimalloc + 热点路径零拷贝/单遍扫描)。
v0.5.0
|
2026-07-22 17:41:29 +08:00 |
|
|
|
44c76b48c1
|
chore(deploy): 新增 xun 服务器全量部署脚本
scripts/xun.fish(fish + docker buildx)一键完成:
runner base / 主应用(透传 git 信息)/ 5 个 runner 子镜像的 amd64 构建、
架构校验、导出 scp 到 xun、导入去 localhost/ 前缀、滚动重启 app 容器,
并附容器/迁移/runner/healthz/外网 HTTPS 全链路校验。
|
2026-07-22 17:33:09 +08:00 |
|
|
|
856eeb758c
|
fix(build): Docker 构建透传 git 信息,修复 x-yggdrasil-git 恒为 unknown
.dockerignore 排除 .git/,容器内 build.rs 跑不了 git 命令,导致
x-yggdrasil-git / git_hash / commit_date 响应头和启动日志全是 "unknown"
(version 来自 CARGO_PKG_VERSION 不受影响)。
三级取值链(build.rs env_or_git):
YGG_BUILD_* 环境变量 → 本地 git 命令 → "unknown"
第一级 std::env::var 优先读 docker --build-arg 注入的值,第二级保留
本地 cargo build 的既有行为,兜底不变。
- Dockerfile: 主应用 server build 前加 ARG+ENV 块(ARG 仅 RUN 可见,
build.rs 由 cargo 调用非 RUN,故需 export 成 ENV 让 cargo build 继承)
- Makefile: GIT_DESCRIBE/HASH/DATE 在宿主采集,GIT_BUILD_ARGS 复用块
注入 docker/docker-amd64/docker-multiarch 三个 target
- deploy-to-linux skill: 两处 docker buildx build 命令(首装 + 更新)
加同名 --build-arg,附说明
|
2026-07-22 16:05:49 +08:00 |
|
|
|
eaabb87108
|
docs(code-runner): AGENTS.md 补充别名归一化与 bun 镜像说明
CI / check (push) Has been cancelled
CI / build (push) Has been cancelled
Code Runner API 层描述补充 normalize_lang 别名归一化机制:
- parse_fence_info 在 markdown 渲染期把别名(js→node, ts→bun 等)换成 canonical
- start_exec/start_exec_stream 在 LANGUAGES.get 前显式 normalize_lang
Runner images 段落补 yggdrasil-runner-bun,记录三个踩坑点:
- bun 不在 Alpine 3.18 仓库,用 bun.sh 脚本(自动选 musl 变体)
- bun 的 musl 二进制是 C++,需 libstdc++ + libgcc 运行时
- BUN_INSTALL 必须 export,否则 curl|bash 管道右侧读不到
|
2026-07-21 15:47:39 +08:00 |
|
|
|
ca676a6146
|
feat(docker): 新增 yggdrasil-runner-bun 沙箱镜像
bun 在 Alpine 3.18 官方仓库不可用,用 bun.sh 安装脚本(多架构、自动检测
/etc/alpine-release 选 musl 变体)。踩坑三处:
1. BUN_INSTALL 必须对管道两侧生效——「VAR=x cmd | bash」只给左侧设变量,
bash 侧读不到会 fallback 到 $HOME/.bun。改用 export。
2. bun 的 musl 二进制是 C++ 写的,依赖 libstdc++ + libgcc(musl libc 不带
C++ 运行时),缺失时报 _ZNSt18condition_variable... symbol not found。
这两个是运行时依赖,apk del 不得删除。
3. 安装路径固定到 /opt/bun + symlink /usr/local/bin/bun,避免依赖 $HOME
(runner 用户 $HOME 可能不可写)。
本地验证通过:
- bun --version → 1.3.14
- runner 用户(1000:1000) + 只读根 + tmpfs(/code mode=1777, /tmp exec) 下
成功执行含 interface/类型注解/循环的 TypeScript 代码。
build-runners.sh 加 bun 构建步骤;deploy-to-linux SKILL.md 的 build 循环、
docker save 列表、docker tag 列表、verify 循环全部补 bun(5→6 个镜像)。
|
2026-07-21 15:44:38 +08:00 |
|
|
|
52e5ee2736
|
feat(admin): 代码试运行沙箱加 bun 语言按钮
SUPPORTED_LANGS 追加 'bun'(仅 canonical key,不展示别名以免按钮拥挤),
default_source 加 bun 分支——示例用 TypeScript 类型注解(:string / :number)
体现 bun 跑 TS 的语言特性。
aliases(js/ts/rs 等)经 normalize_lang 归一即命中此处某项,按钮无需重复。
|
2026-07-21 15:24:41 +08:00 |
|