关于 typst-writing-document
这项自包含技能用于撰写、修复和验证 Typst 文档,内置语法示例,并指导通过编译检查文本内容或视觉布局。
主要功能
- 01内置 Typst 语法示例
- 02渐进式编辑流程
- 03通过编译验证
- 04检查 HTML 文本
- 05结合 Playwright MCP 检查 SVG 布局
适用场景
- 01参考现有语法模式撰写 Typst 文档
- 02每次修改后通过编译检查
- 03检查文档文本或渲染布局
MCP 相关
需要检查视觉布局时,技能建议使用 Playwright MCP 查看 SVG 输出。
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ myriad-dreamin/ tinymist --skill typst-writing-document安装后在 Claude Code 中输入/typst-writing-document确认已加载。
详细介绍
编写和验证 Typst 文档
当用户需要起草、修复或验证 Typst 文档时,使用此技能。本文件包含语法查询所需的一切内容,因此可以复制到其他代码库中,无需附带同级脚本或参考文件。
工作流
- 从本文件的语法查询部分开始,选择最接近的现有模式,再考虑发明新语法。
- 复制最匹配的最小示例,然后逐步调整。
- 每次完成重要编辑后,运行
typst compile。任何非零退出代码都表示阻塞性失败。 - 编译成功后,如果措辞或内容顺序很重要,请使用 HTML 输出检查渲染后的文本和文档结构。
- 如果需要检查视觉布局、间距、编号、换行或强调效果,请使用 SVG 输出和 Playwright MCP。
验证
编译验证:
typst compile --root . path/to/document.typ target/typst-grammar-authoring-check/document.pdf
通过 HTML 验证文本:
typst compile --root . --features html path/to/document.typ target/typst-grammar-authoring-check/document.html
rg "expected text" target/typst-grammar-authoring-check/document.html
通过 SVG 进行视觉验证:
typst compile --root . path/to/document.typ target/typst-grammar-authoring-check/document.svg
typst compile --root . path/to/document.typ target/typst-grammar-authoring-check/document-{0p}.svg
Playwright 检查:
- 仅在成功生成 SVG 后使用 Playwright。
- 如果 MCP 服务支持本地文件,请直接打开 SVG。
- 否则,使用一个嵌入 SVG 的简易本地 HTML 包装页,然后截取屏幕截图并检查布局、间距、编号、换行和强调效果。
防护规则
- 所有由此技能编写的说明和指令都必须使用英文。
- 规范语法示例可以保留其源示例中的非英文文本字面量。
- 不要将 Tinymist 或编辑器诊断视为事实依据。
- 在其他地方使用此技能时,不要假定存在同级更新脚本、参考文件或代码库本地元数据。
- 多页 SVG 输出路径中使用
{p}或{0p}。 - 将 HTML 导出视为验证辅助工具,而非生产环境契约。
- 命令示例应使用正斜杠或占位符路径,以确保跨平台兼容。
语法查询
本节有意嵌入于此,以确保技能自包含。示例改编自非官方教程中的语法样例,并经过精简,以便在单个文件中使用。
基础元素
paragraph:writing-markupheading:= Heading;== Headingstrong:*Strong*emph:_emphasis_;*_emphasis_*list:
+ List 1
+ List 2
continue-list:
4. List 1
+ List 2
emum:
- Enum 1
- Enum 2
mix-list-emum:
- Enum 1
+ Item 1
- Enum 2
raw:
`code`
long-raw:
``` code```
lang-raw:
```rs trait World```
blocky-raw:
```typ
= Heading
```
image:#image("/assets/files/香風とうふ店.jpg", width: 50pt)image-stretch:#image("/assets/files/香風とうふ店.jpg", width: 50pt, height: 50pt, fit: "stretch")image-inline:在一段话中插入一个#box(baseline: 0.15em, image("/assets/files/info-icon.svg", width: 1em))图片。figure:
#figure(```typ
#image("/assets/files/香風とうふ店.jpg")
```, caption: [用于加载香風とうふ店送外卖的宝贵影像的代码])
link:#link("https://zh.wikipedia.org")[维基百科]http-link:https://zh.wikipedia.orginternal-link:
== 某个标题 <ref-internal-link>
#link(<ref-internal-link>)[链接到某个标题]
table:#table(columns: 2, [111], [2], [3])table-align:#table(columns: 2, align: center, [111], [2], [3])inline-math:$sum_x$display-math:$ sum_x $escape-sequences:>\_<unicode-escape-sequences:\u{9999}newline-by-space:A \ Bnewline:
A \
B
shorthand:北京--上海shorthand-space:A~Binline-comment:// 行内注释cross-line-comment:
/* 行间注释
*/
box:在一段话中插入一个#box(baseline: 0.15em, image("/assets/files/info-icon.svg", width: 1em))图片。
文本样式
highlight:#highlight[高亮一段内容]underline:#underline[Language]underline-evade:
#underline(
evade: false)[ጿኈቼዽ]
overline:#overline[ጿኈቼዽ]strike:#strike[ጿኈቼዽ]subscript:威严满满#sub[抱头蹲防]superscript:香風とうふ店#super[TM]text-size:#text(size: 24pt)[一斤鸭梨]text-fill:#text(fill: blue)[蓝色鸭梨]text-font:#text(font: "Microsoft YaHei")[板正鸭梨]
Script Declarations
enter-script:#1code-block:#{"a"; "b"}content-block:#[内容块]none-literal:#nonefalse-literal:#falsetrue-literal:#trueinteger-literal:#(-1), #(0), #(1)n-adecimal-literal:#(-0xdeadbeef), #(-0o644), #(-0b1001)float-literal:#(0.001), #(.1), #(2.)exp-repr-float:#(1e2), #(1.926e3), #(-1e-3)string-literal:#"Hello world!!"str-escape-sequences:#"\""str-unicode-escape-sequences:#"\u{9999}"array-literal:#(1, "OvO", [一段内容])dict-literal:#(neko-mimi: 2, "utterance": "喵喵喵")empty-array:#()empty-dict:#(:)paren-empty-array:#(())single-member-array:#(1,)var-decl:#let x = 1func-decl:#let f(x) = x * 2closure:#let f = (x, y) => x + ynamed-param:#let g(named: none) = namedvariadic-param:#let g(..args) = args.pos().join([、])destruct-array:#let (one, hello-world) = (1, "Hello, World")destruct-array-eliminate:#let (_, second, ..) = (1, "Hello, World", []); #seconddestruct-dict:#let (neko-mimi: mimi) = (neko-mimi: 2); #mimiarray-remapping:
#let (a, b, c) = (1, 2, 3)
#let (b, c, a) = (a, b, c)
#a, #b, #c
array-swap:
#let (a, b) = (1, 2)
#((a, b) = (b, a))
#a, #b
placeholder:
#let last-two(t) = {
let _ = t.pop()
t.pop()
}
#last-two((1, 2, 3, 4))
Script Statements
if:
#if true { 1 },
#if false { 1 } else { 0 }
if-if:
#if false { 0 } else if true { 1 },
#if false { 2 } else if false { 1 } else { 0 }
while:
#{
let i = 0;
while i < 10 {
(i * 2, )
i += 1;
}
}
for:
#for i in range(10) {
(i * 2, )
}
for-destruct:#for (特色, 这个) in (neko-mimi: 2) [猫猫的 #特色 是 #这个\ ]break:#for i in range(10) { (i, ); (i + 1926, ); break }continue:
#for i in range(10) {
if calc.even(i) { continue }
(i, )
}
return:
#let never(..args) = return
#type(never(1, 2))
include:#include "other-file.typ"
Script Styling
set:
#set text(size: 24pt)
四斤鸭梨
scope:
两只#[兔#set text(fill: rgb("#ffd1dc").darken(15%))
#[兔白#set text(fill: orange)
又白],真可爱
]
set-if:
#let is-dark-theme = true
#set rect(fill: black) if is-dark-theme
#set text(fill: white) if is-dark-theme
#rect([wink!])
show-set:
#show: set text(fill: blue)
wink!
show:
#show raw: it => it.lines.at(1)
获取代码片段第二行内容:```typ
#{
set text(fill: true)
}
```
text-selector:
#show "cpp": strong(emph(box("C++")))
在古代,cpp是一门常用语言。
regex-selector:
#show regex("[”。]+"): it => {
set text(font: "KaiTi")
highlight(it, fill: yellow)
}
“无名,万物之始也;有名,万物之母也。”
label-selector:
#show <一整段话>: set text(fill: blue)
#[$lambda$语言是世界上最好的语言。] <一整段话>
另一段话。
selector-exp:
#show heading.where(level: 2): set text(fill: blue)
= 一级标题
== 二级标题
here:#context here().position()here-calc:#context [ 页码是偶数:#calc.even(here().page()) ]query:#context query(<ref-internal-link>).at(0).bodystate:#state("my-state", 1)
Script Expressions
func-call:#calc.pow(4, 3)content-param:#emph[emphasis]member-exp:
#`OvO`.text
method-exp:#"Hello World".split(" ")dict-member-exp:
#let cat = (neko-mimi: 2)
#cat.neko-mimi
content-member-exp:
#`OvO`.text
repr:#repr[ 一段文本 ]type:#type[一段文本]eval:#type(eval("1"))eval-markup-mode:#eval("== 一个标题", mode: "markup")array-in:
#let pol = (1, "OvO", [])
#(1 in pol)
array-not-in:
#let pol = (1, "OvO", [])
#([另一段内容] not in pol)
dict-in:
#let cat = (neko-mimi: 2)
#("neko-mimi" in cat)
logical-cmp-exp:
#(1 < 0), #(1 >= 2),
#(1 == 2), #(1 != 2)
logical-calc-exp:#(not false), #(false or true), #(true and false)plus-exp:#(+1), #(+0), #(1), #(++1)minus-exp:
#(-1), #(-0), #(--1),
#(-+-1)
arith-exp:
#(1 + 1), #(1 + -1),
#(1 - 1), #(1 - -1)
assign-exp:#let a = 1; #repr(a = 10), #a, #repr(a += 2), #astring-concat-exp:#("a" + "b")string-mul-exp:#("a" * 4), #(4 * "ab")string-cmp-exp:#("a" == "b"), #("a" != "b"), #("a" < "ab"), #("a" >= "a")int-to-float:#float(1), #(type(float(1)))bool-to-int:#int(true), #(type(int(true)))float-to-int:#int(1), #(type(int(1)))dec-str-to-int:#int("1"), #(type(int("1")))nadec-str-to-int:
#let safe-to-int(x) = {
let res = eval(x)
assert(type(res) == int, message: "should be integer")
res
}
#safe-to-int("0xf"), #(type(safe-to-int("0xf"))) \
#safe-to-int("0o755"), #(type(safe-to-int("0o755"))) \
#safe-to-int("0b1011"), #(type(safe-to-int("0b1011"))) \
num-to-str:
#repr(str(1)),
#repr(str(.5))
int-to-nadec-str:#str(501, base:16), #str(0xdeadbeef, base:36)bool-to-str:#repr(false)int-to-bool:
#let to-bool(x) = x != 0
#repr(to-bool(0)),
#repr(to-bool(1))
评论
支持 Markdown