feat: 增加 ECharts 渲染与预览交互增强

- 新增可复用的 markdown-echarts 工作区,支持安全的 YAML 围栏、默认值、语义校验、错误占位和 SVG 渲染
- 将 ECharts 接入统一 Markdown、快速预览、精确预览与 PDF 链路,并复用 Mermaid 的不可跨页和超高图单页适配策略
- 默认示例加入 ECharts,增加源文本折叠、顶部文档状态、页码输入跳转与滚动同步
- 快速预览改用外层容器滚动,使滚动条与精确预览统一贴在预览区域右侧,并兼容显示缩放
- 完善 Docker 工作区复制、可配置 Compose 镜像、测试覆盖和进度文档
This commit is contained in:
SkyJourney
2026-07-27 16:05:14 +08:00
parent 1db21c80fc
commit f5a46f79f1
53 changed files with 3998 additions and 221 deletions
@@ -0,0 +1,81 @@
import { describe, expect, it } from "vitest";
import {
normalizeAndValidateEChartsOption
} from "../src/index.js";
describe("normalizeAndValidateEChartsOption", () => {
it("接受首版系列和内置数据转换", () => {
const normalized = normalizeAndValidateEChartsOption({
dataset: {
source: [
["name", "value"],
["A", 1]
],
transform: {
type: "sort",
config: {
dimension: "value",
order: "desc"
}
}
},
series: [
{
type: "bar",
encode: {
x: "name",
y: "value"
}
}
]
});
expect(normalized).toMatchObject({
series: [{ type: "bar" }]
});
expect(Object.getPrototypeOf(normalized)).toBe(
Object.prototype
);
expect(normalized.hasOwnProperty("series")).toBe(true);
});
it("拒绝不支持的系列", () => {
expect(() =>
normalizeAndValidateEChartsOption({
series: [{ type: "custom" }]
})
).toThrow(/ custom/u);
});
it("拒绝外部资源和危险链接", () => {
expect(() =>
normalizeAndValidateEChartsOption({
title: {
text: "示例",
subtext: "https://example.com/chart"
},
series: [{ type: "line" }]
})
).toThrow(/ URL/u);
});
it("拒绝 HTML formatter", () => {
expect(() =>
normalizeAndValidateEChartsOption({
tooltip: {
formatter: "<img src=x onerror=alert(1)>"
},
series: [{ type: "line" }]
})
).toThrow(/ HTML/u);
});
it("拒绝原型污染属性", () => {
const option = JSON.parse(
'{"series":[{"type":"line"}],"__proto__":{"polluted":true}}'
);
expect(() => normalizeAndValidateEChartsOption(option)).toThrow(
//u
);
});
});