typst-writing-document について
Typst 文書の作成、修正、検証に使う自己完結型スキルです。文法例を内蔵し、コンパイルによる確認とテキストやレイアウトの検査手順を示します。
主な機能
- 01Typst 文法例を内蔵
- 02段階的な編集手順
- 03コンパイルによる検証
- 04HTML テキストの確認
- 05Playwright MCP による SVG レイアウト確認
活用シーン
- 01既存の構文パターンを参考に文書を作成する
- 02編集後にコンパイルして確認する
- 03文書のテキストや表示レイアウトを確認する
MCP 対応
視覚的なレイアウト確認が必要な場合、SVG 出力を Playwright MCP で調べる手順を案内します。
インストール
コマンドをコピーしてターミナルで実行します。
$
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 検査:
- Playwright は SVG の生成が成功した後にのみ使用してください。
- 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 対応