864 Commits

Author SHA1 Message Date
xfy
6e5ff72a29 feat(editor): 挂载 CodeBlockNodeView + editor.storage 注入 onRunCode
CodeBlockLowlight.extend({ addNodeView }) 接入自定义 NodeView。
EditorOptions 新增 onRunCode 字段;编辑器初始化时挂到 editor.storage,
NodeView 经 storage key 读取(仿 upload-coordinator 范式)。
2026-07-06 18:00:15 +08:00
xfy
82280e3d4f feat(editor): CodeBlockNodeView 显示语言标签与运行按钮
自定义 NodeView 包裹 <pre><code>,顶部工具栏显示语言(extractLang 提取),
runnable 块额外显示运行按钮。contentDOM 指向 <code>,保证 CodeBlockLowlight
decoration(高亮)正常生效。运行走 editor.storage.__onRunCode 回调。
2026-07-06 17:59:01 +08:00
xfy
0adc6d8382 fix(editor): 暗色 type 色值修正 + extractLang 小写化
- .dark .hljs-type/.hljs-title.class_ 由 #8839ef(紫) 改为 #f9e2af(黄),
  与 spec 表、官方 @catppuccin/highlightjs、读者侧 highlight.css 三处对齐
- extractLang 加 toLowerCase,与后端 parse_fence_info(languages.rs:85)对齐,
  避免大写语言名触发 lowlight Unknown language 抛错
2026-07-06 16:13:08 +08:00
xfy
4de48cd16c style(editor): biome 修正 import 排序(highlight/index)
TDD 阶段子代理未跑 biome check --write,补齐 import 字母序以过 make lint。
2026-07-06 15:53:16 +08:00
xfy
8493e4c4c4 feat(editor): 代码块语法高亮配色(Catppuccin Latte/Mocha)
色值取自 @catppuccin/highlightjs@1.0.1,与读者侧 highlight.css 同一 palette。
亮/暗双套,.dark 前缀切换,scope 到 .tiptap-editor 避免污染全站。
2026-07-06 15:50:35 +08:00
xfy
e86fe0767d feat(editor): 用 CodeBlockLowlight 替换 StarterKit 自带 CodeBlock
禁用 StarterKit codeBlock,引入 CodeBlockLowlight + lowlight 实例。
代码块现在有实时语法高亮(ProseMirror decoration),language 属性
仍承载完整 info string(markdown 往返不变)。
2026-07-06 15:48:02 +08:00
xfy
e79e0d9ee2 feat(editor): 新增 lowlight 实例与 extractLang 语言提取
common 集(~35 种语言)注册到 lowlight;extractLang 从完整 info string
(如 'python runnable {...}')提取首 token,使 runnable 块按正确语言高亮。
2026-07-06 15:45:42 +08:00
xfy
0b4346ecd4 fix(editor): 语言下拉展开时 Enter 不误触发插入
<select> 展开时 Enter 用于确认选项,不应提交整个弹框。
HTMLSelectElement.open 浏览器存在但 TS lib.dom 未声明,用交叉类型断言。
2026-07-06 14:15:25 +08:00
xfy
9b4c0ea36e style(editor): biome 格式化 runnable 模态框代码
biome check --write 修正 import 排序 + 格式化 openRunnableModal 链式调用
(TDD 阶段子代理未跑 make fix,此处补齐以过 make lint)。
2026-07-06 14:08:06 +08:00
xfy
01ceef2694 feat(editor): 可运行代码块模态框样式 + runnable 块绿色边框
模态框复用 Catppuccin Latte/Mocha 配色;编辑器内含 runnable 标记的
代码块加左侧绿色边框(与 Code Runner 运行按钮主色一致)。
2026-07-06 14:04:52 +08:00
xfy
d6fc98d253 feat(editor): 斜杠菜单新增「可运行代码块」条目
触发 openRunnableModal,配置后插入标准 CodeBlock(language 承载
'python runnable {...}' info string)。
2026-07-06 14:02:12 +08:00
xfy
5929ab68b4 feat(editor): 新增 openRunnableModal 模态框配置可运行代码块
原生 DOM 模态框(遮罩+卡片),语言 select + 超时/内存/网络 3 项 overrides
+ 实时预览。确认插入 setCodeBlock({language: 'python runnable {...}'}),
Esc/遮罩/取消关闭不插入。
2026-07-06 13:58:51 +08:00
xfy
84b3da9db6 feat(editor): 新增 buildRunnableInfo 构造 runnable fence info string
纯函数,把语言+overrides 配置转成 'python runnable {...}' 形态。
dirty=false 省略 JSON,dirty=true 写 timeout/memory/network 三项,
字段顺序固定。
2026-07-06 13:45:11 +08:00
xfy
89bab26b90 fix(lint): themes.ts import 排序(type import 前置)
Biome organizeImports 要求 import type 排在普通 import 之前,
交换 @codemirror/view 与 @codemirror/state 顺序。make lint 全绿。
2026-07-06 11:45:14 +08:00
xfy
9231e993e4 fix(clippy): 修复两处预存 clippy 错误
- execute.rs: ExecResult/ExecStatus 仅 server 端使用,加 cfg gate
  消除 WASM 目标的 unused_imports
- system.rs: in_scope 外层闭包保留 || execute_for_editor() 包装
  (Closure::new 要求 Fn 可重复调用,直接传闭包会 use-after-move),
  加 #[allow(clippy::redundant_closure)] 并注释原因

cargo clippy 双目标 -- -D warnings 均通过。
2026-07-06 11:42:03 +08:00
xfy
1db641bca9 refactor(sql-console): SqlConsoleTab 改用 SqlResultTable 组件
删除调用方对 Vec<Vec<Value>> → Vec<Vec<String>> 的预处理(保留类型
信息),结果表格段替换为 <SqlResultTable>,空结果集分支保留。
2026-07-06 11:32:33 +08:00
xfy
59edc7c4e4 feat(sql-console): 表头 sticky + 数据行截断 + 点击展开详情 2026-07-06 11:01:11 +08:00
xfy
b0833eebf7 feat(sql-console): 单元格类型化渲染 (NULL/布尔/数字/文本) 2026-07-06 10:33:46 +08:00
xfy
6d0dd8bb6e feat(sql-console): SqlResultTable 组件骨架 + 模块挂载 2026-07-06 10:30:30 +08:00
xfy
d3c2e41fd8 fix(codemirror): 编辑器塌缩导致上下背景割裂(height:100% 失效)
上一提交(3c10f72)用 `& { height: 100% }` 让 .cm-editor 撑满容器,
但实测无效——headless chromium 抓取 computed style 显示:

  容器#ed:      h=280px  (min-height)
  .cm-editor:   h=63px   ← 塌缩!只有内容高度
  .cm-gutters:  h=63px
  .cm-content:  h=63px

根因:CSS 百分比高度要求父元素有「明确 height」(不是 min-height)。
容器只有 min-height: 280px,没有 height,`.cm-editor { height: 100% }`
解析为 auto,塌缩到内容高度(3 行 ≈ 63px)。剩余 217px 透出容器背景
(paper-entry/mantle),与 .cm-editor 的 base 色形成上下割裂。

修复:改用 flexbox 而非百分比高度——
- themes.ts:`& { flex: 1 1 0; minHeight: 0 }`(替换 height:100%)
- 容器(system.rs / runner.rs):`display: flex; flex-direction: column`

flex 子项的 `flex: 1` 不依赖父元素 height,能真正填满 min-height 撑开
的空间。headless 验证:.cm-editor 现为 280px,与容器一致,割裂消失。

诊断方法:构建自包含测试 HTML(内联 editor.js),chromium --headless
--dump-dom 抓 getComputedStyle。这是应有的反馈循环,避免了前几轮
凭代码猜测导致的反复误诊。
2026-07-06 00:20:55 +08:00
xfy
b207babdb6 refactor(admin): 统一按钮令牌,消除样式散落
将 7 个 admin 页面中复制粘贴/各自为政的按钮 class 收敛到 ui.rs 令牌:

主操作色系统一为主题绿(bg-paper-accent):
- dashboard/posts「发布文章」、write「发布/更新」+「确认」、runner 语言切换选中态
  改用 BTN_PRIMARY / BTN_PRIMARY_SM(原为深色 bg-paper-primary,现统一为绿)
- system 4 处刷新/执行/创建备份、trash 保存设置、write 发布按钮改用 LoadingButton
  统一 loading 态为 spinner 绝对居中叠加(按钮宽度不变,原 system 用文字切换、
  write 用整体变灰,现全部一致)

描边/图标/行内按钮统一:
- posts 重建、system 刷新列表 → BTN_OUTLINE
- trash 清空回收站 → BTN_DANGER_OUTLINE
- trash 步进 ± 按钮(2 处重复字符串)→ BTN_ICON
- write 关闭 × 按钮(2 处重复)→ BTN_CLOSE_ICON
- system BackupRow 恢复/删除绕过令牌 → BTN_TEXT_AMBER / BTN_TEXT_RED
  (删除色从 red-600 校正为令牌的 red-500,与其他行内删除一致)

消除约 15 处内联 class 字符串重复,system.rs 4 种主操作变体收敛为 1 种。
2026-07-06 00:17:25 +08:00
xfy
545651d304 refactor(ui): 新增 BTN_PRIMARY/BTN_OUTLINE 等按钮令牌与 LoadingButton 组件
消除 admin 页面按钮样式散落问题:
- BTN_PRIMARY / BTN_PRIMARY_SM:主题绿实心胶囊主操作按钮
- BTN_OUTLINE:描边次要按钮(relative 以承载 spinner 叠加)
- BTN_DANGER_OUTLINE:红色描边危险按钮(清空回收站)
- BTN_CLOSE_ICON / BTN_ICON:× 关闭与方形步进图标按钮
- LoadingButton 组件:封装主题绿三态(loading spinner 叠加 / disabled 灰化 / 正常),
  统一全站 loading 态为 posts.rs 的 spinner 叠加模式(按钮宽度不变,避免布局抖动)
2026-07-06 00:17:09 +08:00
xfy
3c10f72444 fix(codemirror): 编辑器未撑满容器导致上下背景割裂
现象:编辑器「有行号的上半部分」与「空白下半部分」背景色不一致。

根因:CodeMirror core 的 base theme 给 `.cm-editor`(&)只设了
position: relative + display: flex,没有 height。编辑器默认只占
内容高度(几行 ≈ 60px),不填满父容器。容器 div 设了 min-height:
280px,但 .cm-editor 不会自动撑到 280px——剩余空间透出容器的
paper-entry(mantle #e6e9ef) 背景,与 .cm-editor 的 base(#eff1f5)
形成「上半 base / 下半 mantle」的上下色差。

修复:themes.ts 的覆盖里加 `& { height: 100% }`,让 .cm-editor
撑满父容器。配合容器已有的 min-height: 280px,编辑器完整填充,
背景统一为 base 色,上下割裂消失。
2026-07-06 00:06:00 +08:00
xfy
64907401a0 fix(codemirror): 行号区背景与代码区割裂(core base theme 覆盖 catppuccin)
现象:行号列(gutter)背景是 #f5f5f5 浅灰,代码区(content)是 catppuccin
base #eff1f5 浅蓝灰,两者不一致,有明显割裂。

误诊修正:上一提交(e1b312e)以为是容器背景与 CodeMirror 层次扁平,
改了容器为 code-block(crust)。但 CodeMirror 内部 .cm-editor 和
.cm-gutters 都有不透明背景,容器色根本透不出来——所以「没效果」。
本次回退该容器改动。

真因:CodeMirror core 的内置 base theme(@codemirror/view dist index.js:6916)
给 `&light .cm-gutters` 设了 backgroundColor: "#f5f5f5" / dark "#333338",
特异性为 .cm-editor.cm-light .cm-gutters,高于 @catppuccin/codemirror
的 .cm-gutters 覆盖,catppuccin 的 base 背景被 core 默认灰压制。

修复:themes.ts 在 catppuccin extension 之后追加一个 EditorView.theme,
用 backgroundColor: transparent !important 强制 .cm-gutters 透明,
继承 .cm-editor 的 base 色,行号区与代码区完全融合。light/dark 同理。
2026-07-05 23:58:18 +08:00
xfy
e1b312e651 fix(admin): SQL 编辑器 gutter 与 content 背景割裂
现象:行号区和代码输入区背景色看起来不一致,有割裂感。

根因:三层背景色各不相同——容器用 paper-entry(mantle #e6e9ef)、
标题栏用 paper-theme(base #eff1f5)、CodeMirror 自带 base 背景。
容器与编辑器同处 base/mantle 之间,层次扁平,CodeMirror 的 base
浮不出层次,gutter 与 content 的细微差异被放大。

修复:建立 crust > mantle > base 三层明确递进——
- 容器改 paper-code-block(crust #dce0e8,最深)
- 标题栏改 paper-entry(mantle,中)
- CodeMirror 保持自带 base(最浅)

编辑器作为「浅色岛」浮在深色容器上(VS Code / Sublime 的 IDE
经典范式),gutter 与 content 同为 base 完全融合,割裂消失。
2026-07-05 23:10:56 +08:00
xfy
a7a97fc299 fix(code-runner): 去掉 nproc ulimit,修复容器启动 EAGAIN
容器启动时报 'exec /bin/sh: resource temporarily unavailable',根因是
HostConfig 里的 ulimit nproc=64 与 non-root user(1000:1000) 组合:
RLIMIT_NPROC 按 UID 计数且在容器初始 exec 阶段就生效,与容器内实际进程数无关,
导致 sh 都起不来。pids_limit=64 已在 cgroup 层兜底进程数限制,nproc 是冗余且
有害的双重约束。

复现:docker run --ulimit nproc=64 --user 1000:1000 → exec 失败
       去掉 nproc(保留 nofile + pids_limit + 其他沙箱约束)→ python/node 正常运行

cargo test infra::docker 全过。
2026-07-05 22:05:01 +08:00
xfy
2cfc64b319 fix(admin): SQL 控制台 Ctrl+Enter 触发 panic(无 dioxus scope)
现象:编辑器有焦点时点击执行(或按 Ctrl+Enter)会 panic:
  called `Option::unwrap()` on a `None` value  (runtime.rs:223)
  RefCell already borrowed                       (runtime.rs:280)

根因:Ctrl+Enter 从 CodeMirror 的 JS 事件处理中触发 onRunShortcut
回调,此时 dioxus scope stack 为空。execute_for_editor 内部调
spawn(),而 spawn() 走 Runtime::with_current_scope →
current_scope_id().unwrap(),scope stack 空时直接 panic。
第二个 panic 是 unwind 期间再次 borrow scope_stack 触发的。

修复:用 dioxus 文档推荐的 web-sys 回调模式——在组件主体捕获
scope_id(此时 scope 活跃),JS 回调里用
Runtime::current().in_scope(scope_id, ...) 重建 scope 上下文
后再执行,spawn 即可找到 origin scope。

dioxus-core 源码(global_context.rs docstring)明示此模式,
dioxus-signals/src/global/mod.rs:201 也是这样用的。

注意:按钮 onclick 走 dioxus 事件系统(scope 已设),不受影响;
仅 CodeMirror JS 回调路径需要 in_scope 兜底。
2026-07-05 21:59:17 +08:00
xfy
81648de16f fix(docker): runner-base 切换 apk 源为清华镜像
国内访问 dl-cdn.alpinelinux.org 经常超时,导致 apk add python3/nodejs 失败
('temporary error' / 'no such package')。在基础镜像里用 sed 把 apk repositories
指向 mirrors.tuna.tsinghua.edu.cn,python/node 镜像 FROM base 自动继承,无需重复修改。
2026-07-05 21:57:44 +08:00
xfy
fe6934ddd5 feat(admin): SQL 控制台接通 Ctrl+Enter 并重做视觉
接通上一提交打通的快捷键通道,同时重做编辑器/工具条/结果区视觉。

功能(接通 Ctrl+Enter):
- 把 run_sql 逻辑拆出 execute_for_editor 闭包(仅捕获 Copy 的 Signal),
  注入 CodeMirror onRunShortcut,按钮 onclick 仍走 run_sql
- 两条路径共用同一套 4 道护栏 + 资源逻辑,行为完全等价

视觉(编辑器标题栏一体化):
- 编辑器加标题栏:左 accent 状态点 + SQL 标识,右 ⌘↵ 快捷键提示
- 容器升至 rounded-2xl + 280px 最小高度,留呼吸
- 执行按钮改胶囊式(rounded-full)+ active:scale 触觉反馈 +
  SPINNER_SVG 替代纯文字「执行中...」
- 危险选项(我了解后果)用竖线分隔 + 红色语义,与普通选项分层
- 结果摘要从裸文本改徽章式(accent-soft 底 + 圆角胶囊)
- 加空结果友好态:SELECT 返回空集时显示「查询成功,无返回行」
- EXPLAIN 输出 pre 补 m-0,消除默认外边距
2026-07-05 21:31:16 +08:00
xfy
4239602f3d feat(codemirror): 支持 Ctrl/Cmd+Enter 运行快捷键
按钮一直写着「执行 (Ctrl+Enter)」但快捷键从未接线——basicSetup 不含
自定义 keymap。从底层往上打通 Mod-Enter 运行通道:

editor.ts:
- EditorOptions 加 onRunShortcut 可选回调
- constructor 条件注入 keymap.of([{ key: "Mod-Enter", ... }])
- 用 Prec.highest 包裹:@replit/codemirror-vim 的 vimPlugin 在
  ViewPlugin 层拦截按键,普通 keymap 会被吞;vim 自己也用
  Prec.highest,跟随此惯例保证 Mod-Enter 在 vim 之前命中
- 放在 vimCompartment 之前,满足「vim 必须在 keymap 最前」约束
- Prec 从 @codemirror/state 引入(不在 @codemirror/view)

codemirror_bridge.rs:
- EditorOptions extern 加 set_on_run_shortcut
- EditorHandle 加 _on_run_shortcut 字段保生命周期,new() 签名增加
  该参数(Drop 随实例释放)

调用点适配:
- CodeRunner(runner.rs)传 no-op 闭包(不用该功能但签名要满足)
- SQL 控制台的接线在后续 system.rs 改动中完成
2026-07-05 21:31:03 +08:00
xfy
1dc02561a3 style(code-runner): 重做 admin/runner 视觉,对齐项目 token 体系
页面与 CodeRunner 组件此前用的是 daisyUI 类(base-100/btn-primary/
input-bordered/bg-neutral 等),但项目根本没装 daisyUI——这些类在
编译产物里 0 匹配,导致页面渲染成裸结构,是「半成品」观感的根因。

全部换成项目真实的 --color-paper-* 语义 token,视觉与 dashboard /
posts / system 等后台页面统一:

页面壳(runner.rs):
- h1 升至 text-4xl,与其它 admin 页一致;加底部分割线
- 外层对齐 max-w-5xl space-y-8
- 语言切换改为胶囊按钮组(选中态 accent 绿实心)
- overrides 输入框套 system.rs 的表单范式,加 focus 态
- 错误/提示用 red-500 dark: red-400,解析成功时显示字段说明

CodeRunner 组件(runner.rs):
- 容器改 rounded-2xl + paper-border + paper-entry
- 顶栏加状态点(accent)+ 胶囊运行按钮(active 缩放反馈)
- 运行中用 SPINNER_SVG 替代 daisyUI loading-spinner
- 输出区改 paper-code-block 底 + 语义色文字,随主题切
- 错误区改 red-50 dark: red-900/10 柔光块
2026-07-05 14:49:24 +08:00
xfy
c83b834c8b feat(code-runner): admin 跳过代码运行速率限制
作者在 /admin/runner 沙箱调试代码块时,1 次/秒 + 50 次/天的读者限流
(RATE_LIMIT_CODE_EXEC_*)会频繁打断试运行。已登录 admin 改为跳过
check_code_exec_limit,仍受并发槽、资源钳制与源码大小校验约束。
2026-07-05 14:32:56 +08:00
xfy
575b6b533e feat(code-runner): CodeRunner 挂载 CodeMirror 编辑器,支持双向绑定
兑现组件一直承诺但无人实现的「调用方挂载编辑器」契约——此前容器 div 只
输出 id,admin/runner 沙箱与文章正文 runnable 块都只能提交写死的初始源码,
作者/读者无法编辑。

- CodeRunner 在 WASM 端按自身 container_id 调 codemirror_bridge.create
  挂载编辑器,onChange 回写内部 source_signal(编辑器内容 = 唯一真源)
- run_code 改读 source_signal 当前值,提交编辑后内容而非初始快照
- 主题切换经 use_resolved_theme 订阅,热切换编辑器主题
- use_drop 销毁 EditorHandle,释放 CodeMirror 实例与 closure 槽位
- source prop 外部变更(admin 切换语言重置示例)同步进 signal + 编辑器
- admin/runner 页面 overrides 解析挪进 use_memo,避免 render 体写状态

范式镜像 SQL 控制台(system.rs)与 Tiptap 编辑器(write.rs)。
2026-07-05 14:32:39 +08:00
xfy
58f1a981d3 feat(admin): SSR 层认证守卫,未登录直接跳登录页
此前后台鉴权完全在客户端 WASM 完成:SSR 渲染骨架屏 → 浏览器下载
/编译 WASM → hydrate → 异步 get_current_user() → 客户端 navigator.
push(Login)。整条链串行,未登录用户首屏要空白好久才跳转登录。

新增 admin_guard 中间件:/admin* 请求在进入 Dioxus SSR 渲染器之前
读 session cookie → get_user_by_token 校验 admin 角色 → 失败直接 302
到 /login。未登录用户首屏即登录页,零 SSR 渲染开销。

- 置于 app_routes layer 链最外层(CSRF/cache/SSR 之前短路)
- 复用 get_user_by_token:命中内存缓存 + session_generation 校验,
  与客户端鉴权同一套语义
- DB 错误 fail-open(避免抖动把已登录管理员踢登录页),客户端
  AdminLayout 仍作兜底
- /admin 与 /login 本就不进 SSR 缓存,302 不会被缓存
2026-07-04 22:53:28 +08:00
xfy
16bd644783 fix(admin): 仪表盘空数据时显示空状态占位
空库下 get_post_stats / list_posts 正常返回,但 list_posts 返回
Some([]) 时走有数据分支、循环 0 次,渲染出一个空表格容器,视觉
上仪表盘近期文章区域一片空白,没有空状态引导。

复用项目既有的 empty_state::EmptyState 约定(与 posts.rs / trash.rs /
comments.rs 列表页一致):Some(posts) 且 posts.is_empty() 时渲染
暂无文章插画 + 写文章CTA。空状态放在 ADMIN_TABLE_CLASS 容器
之外,避免 overflow-hidden 裁掉插画的 py-20 内边距。
2026-07-04 22:49:25 +08:00
xfy
383e6f6b43 fix(db): expand source chain for all DB error logging
The previous fix only covered migration errors. The same truncation
affected two more places, both because tokio_postgres::Error's Display
prints only "db error" while the real message lives in source():

- api/error.rs: AppError::{db_conn,query,tx} logged with {e}, so every
  runtime DB failure showed as "Query failed: db error". The
  constructors' bound is tightened from impl Display+Debug to
  impl std::error::Error so the source chain can be walked; the
  user-facing message stays sanitized (existing tests still pass).
- db/pool.rs: ensure_database formatted tokio_postgres::Error directly
  into the String returned to main.rs' exit(1) path, collapsing to
  "failed to query pg_database: db error" etc.

Adds a shared crate::db::format_with_sources helper (walks source()
with de-dup of placeholder layers) and reuses it from MigrateError's
Display, the AppError constructors, and pool.rs.
2026-07-04 21:54:54 +08:00
xfy
e9f0b032b7 fix(db): expand migration error display to walk source chain
tokio_postgres::Error's Display prints only the placeholder "db error"
for DB-side failures; the actual message (e.g. column already exists,
SQLSTATE, constraint name) lives in source() as DbError. Without
traversing the chain, startup logs showed the useless
"migration 001 failed: db error", making failures impossible to
diagnose.

MigrateError::Display now appends each source layer with ": " and
exposes source() so error-reporting layers that walk the chain
themselves also benefit.
2026-07-04 21:47:24 +08:00
xfy
f3eb93f320 docs(agents): document code runner and fix clippy/biome lint
文档:
- AGENTS.md 新增「Code Runner」架构章节(三层架构 + WASM 可见性规则 +
  governor 0.8 无 per_day 的处理 + runner 镜像契约)
- Environment 段补齐 CODE_RUNNER_* 与 RATE_LIMIT_CODE_EXEC_* 环境变量

Lint 修复(clippy --all-targets --all-features -D warnings 全绿):
- runner.rs/runner.rs: use_signal 冗余闭包 → 直接传 fn
- runner_config.rs: &*RUNNER_CONFIG 去 deref;assert_eq!(...false/true) → assert!
- markdown.rs: 局部变量 /// 改 //;Option.map().flatten() → and_then
- languages.rs: 删除从未读取的 LanguageDef.name 字段(语言名即 map key)
- docker.rs: start_container if let Err → ?;timeout match → is_err();
  嵌套 if 合并(修 task 2 遗留 + clippy 1.96 新 lint)
- pages/admin/posts.rs: use_signal 冗余闭包(预存 lint,顺带修绿)
- codemirror editor.ts: biome format 重排 setSchema 单行
2026-07-03 18:57:25 +08:00
xfy
9851f98dc0 chore(docker): setup sandboxed runtime containers and build script
- runner-base: alpine 3.18 + 非 root 用户 1000:1000 + /code 工作目录
- runner-python: 基于 base, apk add python3 + python 符号链接(对齐 run_cmd 'python')
- runner-node: 基于 base, apk add nodejs
- runner.toml: 镜像自描述(运行时实际配置由 Rust LANGUAGES 注册表 + 环境变量决定)
- build-runners.sh: 按 base→python→node 顺序构建, tag 与 languages.rs 严格对齐
2026-07-03 18:48:03 +08:00
xfy
bbd4553467 feat(admin): add dedicated code-runner sandbox page at /admin/runner
不改动 852 行的 write.rs 编辑器布局,改为独立 admin 页面(与 posts/system 同款):
- runner.rs: Runner 页面(语言切换 join 按钮 + overrides JSON 输入 + CodeRunner 沙箱)
  切换语言自动填充示例源码;overrides JSON 实时校验,畸形时提示并忽略
- mod.rs: 注册 runner 模块并导出 Runner
- router.rs: 新增 #[route("/runner")] Runner {} 嵌套在 /admin 下
- admin_layout.rs: 导航新增「试运行」入口
2026-07-03 18:41:05 +08:00
xfy
0e8b5fd7b7 feat(ui): scan and mount code-runner component client-side in reader
采用 Dioxus vdom 友好的片段拆分(避免篡改 dangerous_inner_html 产物导致 hydration 冲突):
- post_content.rs: split_content_fragments 把 content_html 拆成 Html/Runnable 片段,
  Runnable 作为 <CodeRunner> 组件穿插渲染;HTML 实体解码还原源码;6 个单测
- markdown.rs: 可运行 pre 增加 data-source(转义后的原始源码), 供阅读器无损提取
- sanitizer.rs: 放行 pre 的 data-source 属性

(任务 7 的 data-source 增量一并在此提交)
2026-07-03 18:35:57 +08:00
xfy
6b875e6ba5 feat(ui): create CodeRunner Dioxus UI component with async polling
- runner.rs: CodeRunner 组件(源码展示 + Run 按钮 + 阶段/输出/错误区)
  spawn 提交 StartExec 后用 sleep_ms(WASM 友好) 轮询 GetExecResult,MAX_POLLS 兜底
  mut 信号加 cfg_attr 放行 server 的 unused_mut(AGENTS.md 约定)
  提前克隆 source/language 给 move 闭包,避免 rsx! 二次借用 (E0382)
- execute.rs: 修正双目标可见性——server fn 模块不能 cfg-gate
  共享类型 use 双目标可见,server-only 辅助 use 单独 gate(对齐 posts 模块约定)
- mod.rs: execute 不再 gate,languages/progress 仍 gate
2026-07-03 18:28:52 +08:00
xfy
576803302d refactor(editor): support dynamic languages python/javascript in codemirror
- editor.ts: 新增 languageCompartment + buildLanguageExtension(lang, schema)
  python → lang-python, node/javascript → lang-javascript, sql/缺省 → lang-sql
  新增 setLanguage(lang) 热切换; setSchema 保留当前语言不被强制切回 sql
- package.json: +@codemirror/lang-python +lang-javascript
- codemirror_bridge.rs: EditorInstance 新增 set_language wasm-bindgen extern

(产物 public/codemirror/ 被 gitignore,本地已重建)
2026-07-03 18:22:21 +08:00
xfy
a77a2e3502 feat(markdown): support rendering and sanitizing runnable pre code-blocks
- markdown.rs: CodeBlock 处理识别 `runnable` 标记 + 受支持语言,在 <pre> 上
  挂 data-runnable/data-lang/data-overrides;overrides JSON 经 escape_html 转义
  防属性注入;非可运行围栏的 language-xxx 改取纯语言 token(首个空白前)
- sanitizer.rs: clean_html 放行 <pre> 的 data-runnable/data-lang/data-overrides
- 新增 4 个 markdown 测试 + 2 个 sanitizer 测试覆盖转义/白名单/字段顺序
2026-07-03 18:19:26 +08:00
xfy
9fe128d44d feat(api): implement StartExec and GetExecResult server functions
- rate_limit.rs: 新增 CODE_EXEC 双层限流(per-second burst + 每日总额)
  governor 0.8 无 per_day,用 with_period(24h)+allow_burst 模拟
- execute.rs: StartExec(IP 提取/语言白名单/源码大小/入队/并发信号量/clamp/容器执行)
  + GetExecResult(轮询读取 DashMap);系统异常脱敏,日志记详情
- .env.example: RATE_LIMIT_CODE_EXEC_PER_SEC 改为整数(governor 不支持小数)
2026-07-03 18:15:59 +08:00
xfy
3f3bbf284c feat(api): add language registry and markdown fence info parser 2026-07-03 18:11:43 +08:00
xfy
9baa9da62f feat(api): implement in-memory task registry with gc 2026-07-03 18:10:20 +08:00
xfy
60fd6378b6 feat(api): define shared serialized structs for code runner
Co-authored-by: plan docs/superpowers/plans/2026-07-03-code-runner.md Task 3
2026-07-03 18:09:27 +08:00
xfy
5fdc88a48c fix(runner): resolve task 2 container runner review findings 2026-07-03 18:02:56 +08:00
xfy
f6042d23f6 chore(skill): add matt-pocock engineering skills
Add 20 engineering/productivity skills from mattpocock/skills:
ask-matt, code-review, codebase-design, diagnosing-bugs,
domain-modeling, grill-me, grill-with-docs, grilling, handoff,
implement, improve-codebase-architecture, prototype, research,
setup-matt-pocock-skills, tdd, teach, to-issues, to-prd, triage,
writing-great-skills.

Update skills-lock.json with corresponding source/hash entries.
2026-07-03 18:00:16 +08:00