feat: 品牌裁剪弹窗/Markdown 行内短代码与详情视频块等

新增:
- BrandCropModal 品牌图裁剪弹窗 + cropImage 工具
- Markdown 行内短代码(inlineShortcodes + remark 插件)
- MdDetails 折叠块、MdVideo 视频块组件
- directiveBlocks/inlineShortcodes 测试

其它:
- 聊天后端(handler/service chat)调整
- Markdown 渲染链路、编辑器、全局样式与品牌相关面板更新
This commit is contained in:
2026-10-02 01:40:06 +08:00
parent 3e98052c16
commit 99c0f0319d
23 changed files with 2012 additions and 119 deletions

View File

@@ -0,0 +1,58 @@
import type { Root as MdastRoot } from "mdast";
import type { Text } from "mdast";
import { visit, SKIP } from "unist-util-visit";
import { splitInlineShortcodes } from "@/lib/inlineShortcodes";
const QUICK_RE = /==|\[(?:kbd|color|bg)[=\]]/i;
type MdastParent = { children: Array<Record<string, unknown>>; type: string };
/** 行内短代码 → mdast 节点(mdast-util-to-hast 支持 data.hName / data.hProperties) */
function segToNodes(
seg: Exclude<ReturnType<typeof splitInlineShortcodes>, null>[number]
): Record<string, unknown>[] {
if (seg.type === "text") return [{ type: "text", value: seg.value }];
if (seg.type === "mark" || seg.type === "kbd") {
return [
{
type: "emphasis",
data: { hName: seg.type },
children: [{ type: "text", value: seg.value }],
},
];
}
const style = [
seg.fg ? `color:${seg.fg}` : null,
seg.bg ? `background-color:${seg.bg}` : null,
]
.filter(Boolean)
.join(";");
return [
{
type: "emphasis",
data: { hName: "span", hProperties: { style } },
children: [{ type: "text", value: seg.value }],
},
];
}
/**
* remark 插件:把文本节点中的 ==高亮== / [kbd] / [color] / [bg] 展开为
* mark/kbd/span 节点。code/inlineCode 是字面节点没有子文本,天然免疫;
* 未命中短代码的文本原样保留。
*/
export function remarkInlineShortcodes() {
return (tree: MdastRoot) => {
visit(tree, "text", (node: Text, index, parent) => {
const p = parent as unknown as MdastParent | null;
if (!p || index === undefined) return;
const value = node.value;
if (!QUICK_RE.test(value)) return;
const segs = splitInlineShortcodes(value);
if (!segs) return;
const nodes = segs.flatMap(segToNodes);
p.children.splice(index, 1, ...nodes);
return [SKIP, index + nodes.length];
});
};
}