feat(markdown): support yaml role=tag info string
Accept yaml role=tag alongside legacy yaml/tag so the block body gets yaml syntax highlighting, matching the CLI role= attribute convention. Copy button now emits the role=tag form.
This commit is contained in:
@@ -15,3 +15,26 @@ bleed: 1
|
|||||||
padding: 2
|
padding: 2
|
||||||
layers: name1:1,1-2,2f12 name2:4,7-5,8f12s num1:1,3-2,4f12 num2:4,5-5,6f12s
|
layers: name1:1,1-2,2f12 name2:4,7-5,8f12s num1:1,3-2,4f12 num2:4,5-5,6f12s
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 使用 yaml role=tag 语法创建 md-deck(推荐,可语法高亮)
|
||||||
|
|
||||||
|
```yaml role=tag
|
||||||
|
tag: md-deck
|
||||||
|
body: ./names.csv
|
||||||
|
data-config:
|
||||||
|
size: 54x86
|
||||||
|
grid: 5x8
|
||||||
|
layers:
|
||||||
|
- prop: name1
|
||||||
|
pos: 1,1-2,2
|
||||||
|
font: 12
|
||||||
|
- prop: num2
|
||||||
|
pos: 4,5-5,6
|
||||||
|
font: 12
|
||||||
|
orientation: s
|
||||||
|
- template: |
|
||||||
|
**{{name1}}** / {{num1}}
|
||||||
|
pos: 1,3-5,4
|
||||||
|
font: 6
|
||||||
|
align: l
|
||||||
|
```
|
||||||
|
|||||||
+15
-6
@@ -423,12 +423,12 @@ layers="字段:起始行,起始列 - 结束列,字体大小"
|
|||||||
|
|
||||||
示例:`title:1,1-5,1f8` 表示 title 字段从第 1 行开始,占据 1-5 列,8mm字体。
|
示例:`title:1,1-5,1f8` 表示 title 字段从第 1 行开始,占据 1-5 列,8mm字体。
|
||||||
|
|
||||||
**结构化配置(yaml/tag 代码块):**
|
**结构化配置(yaml 代码块):**
|
||||||
|
|
||||||
可以使用 ```yaml/tag 代码块,通过 `body` 指定 CSV 路径,`data-config` 提供结构化配置(图层可以是字段或模板):
|
推荐使用 ```yaml role=tag 代码块(`yaml` 语言可被语法高亮),通过 `body` 指定 CSV 路径,`data-config` 提供结构化配置(图层可以是字段或模板):
|
||||||
|
|
||||||
````markdown
|
````markdown
|
||||||
```yaml/tag
|
```yaml role=tag
|
||||||
tag: md-deck
|
tag: md-deck
|
||||||
body: ./cards.csv
|
body: ./cards.csv
|
||||||
data-config:
|
data-config:
|
||||||
@@ -489,13 +489,22 @@ track npc#john.dwarf.warrior[hp=4/4 ac=15 name="John"]
|
|||||||
|
|
||||||
## YAML 标签
|
## YAML 标签
|
||||||
|
|
||||||
使用 ```yaml/tag 代码块创建自定义标签:
|
使用 ```yaml role=tag 代码块创建自定义标签(推荐,`yaml` 语言可被语法高亮):
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```yaml role=tag
|
||||||
|
tag: tag-name
|
||||||
|
class: custom-class
|
||||||
|
id: my-id
|
||||||
|
body: 标签内容
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
也支持旧写法 ```yaml/tag(等价,但无语法高亮):
|
||||||
|
|
||||||
````markdown
|
````markdown
|
||||||
```yaml/tag
|
```yaml/tag
|
||||||
tag: tag-name
|
tag: tag-name
|
||||||
class: custom-class
|
|
||||||
id: my-id
|
|
||||||
body: 标签内容
|
body: 标签内容
|
||||||
```
|
```
|
||||||
````
|
````
|
||||||
|
|||||||
@@ -521,7 +521,7 @@ export function createDeckStore(initialSrc: string = ""): DeckStore {
|
|||||||
return parts.join("");
|
return parts.join("");
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Serialize the deck back to a ```yaml/tag codeblock (round-trips templates). */
|
/** Serialize the deck back to a yaml codeblock (round-trips templates). */
|
||||||
const generateYamlCode = () => {
|
const generateYamlCode = () => {
|
||||||
const toYamlLayer = (l: LayerConfig) => {
|
const toYamlLayer = (l: LayerConfig) => {
|
||||||
const out: Record<string, unknown> = {};
|
const out: Record<string, unknown> = {};
|
||||||
@@ -560,8 +560,9 @@ export function createDeckStore(initialSrc: string = ""): DeckStore {
|
|||||||
lineWidth: -1,
|
lineWidth: -1,
|
||||||
noRefs: true,
|
noRefs: true,
|
||||||
});
|
});
|
||||||
const fence = "```";
|
const fence = "```yaml role=tag";
|
||||||
return `${fence}yaml/tag\n${yamlStr}${fence}`;
|
const backtick = "`";
|
||||||
|
return `${fence}\n${yamlStr}${backtick.repeat(3)}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const copyCode = async (fallback?: (code: string) => void) => {
|
const copyCode = async (fallback?: (code: string) => void) => {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ title: 卡牌组件
|
|||||||
|
|
||||||
将 CSV 数据渲染为可打印的卡牌布局,支持自定义尺寸、网格、图层和双面排版。
|
将 CSV 数据渲染为可打印的卡牌布局,支持自定义尺寸、网格、图层和双面排版。
|
||||||
|
|
||||||
**语法:** `:md-deck[./cards.csv]{选项}` 或 ```yaml/tag 代码块
|
**语法:** `:md-deck[./cards.csv]{选项}` 或 yaml 代码块
|
||||||
|
|
||||||
**基础卡牌:**
|
**基础卡牌:**
|
||||||
:md-deck[./spells.csv]{grid="3x3"}
|
:md-deck[./spells.csv]{grid="3x3"}
|
||||||
@@ -16,12 +16,12 @@ title: 卡牌组件
|
|||||||
|
|
||||||
CSV 包含数据字段列,通过图层定义控制各字段的位置、大小和对齐。
|
CSV 包含数据字段列,通过图层定义控制各字段的位置、大小和对齐。
|
||||||
|
|
||||||
## 结构化配置 (yaml/tag)
|
## 结构化配置 (yaml 代码块)
|
||||||
|
|
||||||
```yaml/tag 代码块可以用 `body` 指定 CSV 路径,`data-config` 提供结构化配置,图层可以是字段(`prop`)或 markdown 模板(`template`,支持 `{{变量}}` 替换):
|
推荐 ```yaml role=tag 代码块(`yaml` 语言可被语法高亮),用 `body` 指定 CSV 路径,`data-config` 提供结构化配置,图层可以是字段(`prop`)或 markdown 模板(`template`,支持 `{{变量}}` 替换):
|
||||||
|
|
||||||
````markdown
|
````markdown
|
||||||
```yaml/tag
|
```yaml role=tag
|
||||||
tag: md-deck
|
tag: md-deck
|
||||||
body: ./cards.csv
|
body: ./cards.csv
|
||||||
data-config:
|
data-config:
|
||||||
@@ -40,7 +40,7 @@ data-config:
|
|||||||
```
|
```
|
||||||
````
|
````
|
||||||
|
|
||||||
`pos` 为网格位置 `x1,y1-x2,y2`(1-based,与紧凑 layers 字符串同格式)。
|
`pos` 为网格位置 `x1,y1-x2,y2`(1-based,与紧凑 layers 字符串同格式)。也支持旧写法 ```yaml/tag。
|
||||||
|
|
||||||
## 图层格式
|
## 图层格式
|
||||||
|
|
||||||
|
|||||||
@@ -28,10 +28,7 @@ describe("code-block-yaml-tag", () => {
|
|||||||
" size: 63x88",
|
" size: 63x88",
|
||||||
" layers:",
|
" layers:",
|
||||||
" - prop: title",
|
" - prop: title",
|
||||||
" x1: 1",
|
" pos: 1,1-5,1",
|
||||||
" y1: 1",
|
|
||||||
" x2: 5",
|
|
||||||
" y2: 1",
|
|
||||||
" font: 12",
|
" font: 12",
|
||||||
"```",
|
"```",
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
@@ -47,12 +44,38 @@ describe("code-block-yaml-tag", () => {
|
|||||||
expect(config.layers).toHaveLength(1);
|
expect(config.layers).toHaveLength(1);
|
||||||
expect(config.layers[0]).toMatchObject({
|
expect(config.layers[0]).toMatchObject({
|
||||||
prop: "title",
|
prop: "title",
|
||||||
x1: 1,
|
pos: "1,1-5,1",
|
||||||
y1: 1,
|
|
||||||
font: 12,
|
font: 12,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("supports yaml role=tag info string (highlight-friendly)", () => {
|
||||||
|
const html = render(
|
||||||
|
[
|
||||||
|
"```yaml role=tag",
|
||||||
|
"tag: md-deck",
|
||||||
|
"body: ./cards.csv",
|
||||||
|
"data-config:",
|
||||||
|
" grid: 5x5",
|
||||||
|
" layers:",
|
||||||
|
" - template: |",
|
||||||
|
" **{{name}}**",
|
||||||
|
" pos: 1,1-5,8",
|
||||||
|
"```",
|
||||||
|
].join("\n"),
|
||||||
|
);
|
||||||
|
expect(html).toContain("<md-deck data-config=");
|
||||||
|
expect(html).toContain("./cards.csv</md-deck>");
|
||||||
|
const m = html.match(/data-config="([^"]*)"/);
|
||||||
|
const config = JSON.parse((m![1] || "").replace(/"/g, '"'));
|
||||||
|
expect(config.layers[0].template).toBe("**{{name}}**\n");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("does not swallow plain yaml code blocks", () => {
|
||||||
|
const token = ext.tokenizer("```yaml\nsize: 54x86\n```");
|
||||||
|
expect(token).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
test("handles missing tag and body", () => {
|
test("handles missing tag and body", () => {
|
||||||
const html = render("```yaml/tag\nclass: foo\n```");
|
const html = render("```yaml/tag\nclass: foo\n```");
|
||||||
expect(html).toContain("<tag-unknown class=\"foo\"></tag-unknown>");
|
expect(html).toContain("<tag-unknown class=\"foo\"></tag-unknown>");
|
||||||
|
|||||||
@@ -8,10 +8,15 @@ export default function markedCodeBlockYamlTag(): MarkedExtension {
|
|||||||
name: "code-block-yaml-tag",
|
name: "code-block-yaml-tag",
|
||||||
level: "block",
|
level: "block",
|
||||||
start(src: string) {
|
start(src: string) {
|
||||||
return src.match(/^```yaml\/tag\s*\n/m)?.index;
|
return (
|
||||||
|
src.match(/^```yaml\/tag\s*\n/m)?.index ??
|
||||||
|
src.match(/^```yaml\s+role=tag(?:\s|\n)/m)?.index
|
||||||
|
);
|
||||||
},
|
},
|
||||||
tokenizer(src: string) {
|
tokenizer(src: string) {
|
||||||
const rule = /^```yaml\/tag\s*\n([\s\S]*?)\n```/;
|
// Both `yaml/tag` (legacy) and `yaml role=tag` (highlight-friendly)
|
||||||
|
// info strings identify a yaml-defined tag block.
|
||||||
|
const rule = /^```yaml(?:\/tag|\s+role=tag[^\n]*)\n([\s\S]*?)\n```/;
|
||||||
const match = rule.exec(src);
|
const match = rule.exec(src);
|
||||||
if (match) {
|
if (match) {
|
||||||
const yamlContent = match[1]?.trim() || "";
|
const yamlContent = match[1]?.trim() || "";
|
||||||
|
|||||||
Reference in New Issue
Block a user