Inline Syntax Specification
@Doc Inline Syntax Specification v1.5
0. Table of Contents
- 1. Design Philosophy
- 2. Lexer 行為定義
- 3. Ambiguity Resolution Rule
- 4. 完整 EBNF 語法定義
- 5. Escape Rule
- 6. Unknown Command Fallback
- 7. @mark / @color / @bordered Styles Semantics
- 8. @link URI Semantics
- 9. @raw Opaque Domain
- 10. Nested Parsing
- 11. Parser Recovery Strategy
- 12. Architecture
- 13. Core Principle
- 14. Simplified Syntax Aliases
- 15. Semantic Inline:@spoiler 與 @hex
1. Design Philosophy
@Doc 採用:
Only Known Commands Trigger Parsing
只有已知指令具有語法意義。
未知指令永遠視為普通文字。
此設計目標:
- 降低學習成本
- 避免與 Email、Mention 系統衝突
- 提高 AI 解析穩定性
- 提高編輯器容錯能力
- 保持 DSL 的可擴充性
- 建立穩定且可預測的 AST
2. Lexer 行為定義
指令解析規則
當 Lexer 掃描到 @ 時,應依照以下優先順序處理:
- 若後續為
@@- 解析為單一純文字
@
- 解析為單一純文字
- 若後續符合已註冊之指令名稱
- 進入對應語法解析流程
- 若不符合任何已知指令
- 整段視為普通文字輸出
範例
| 輸入 | 結果 |
|---|---|
@mark[hello] | 解析為 mark 節點 |
@@mark | 輸出 @mark |
[email protected] | 純文字 |
@GitHub | 純文字 |
@unknown | 純文字 |
3. Ambiguity Resolution Rule
由於 @Doc 採用:
Known Command Recognition
因此 Lexer 必須先嘗試辨識已知指令,再退回普通文字模式。
換句話說:
inline-node的優先權高於plain-text-char。
Lexer 必須遵循:
@ 開頭
↓
是否為 @@ ?
↓
是否存在於 Command Registry ?
↓
是 → Inline Node
否 → Plain Text因此:
@mark[hello]必須解析為:
InlineNode(mark)而不是:
Text('@')
Text('m')
Text('a')
Text('r')
Text('k')
...4. 完整 EBNF 語法定義
(* ==========================================================================
Entry Point
========================================================================== *)
inline-stream =
{ inline-node | plain-text-char } ;
inline-node =
mark
| color
| bordered
| bold
| italic
| underline
| del
| raw
| sup
| sub
| fn
| defn
| kbd
| link
| spoiler
| hex
| br
| escape ;
(* ==========================================================================
Inline Nodes
========================================================================== *)
mark = "@mark" , [ styles ] , content ;
color = "@color" , [ styles ] , content ;
(* @bordered shares @color's exact {styles} slot and swatch (see §7),
applied as a text border instead of a foreground color. *)
bordered = "@bordered" , [ styles ] , content ;
bold = ( "@bold" | "@b" ) , content ;
italic = ( "@italic" | "@i" ) , content ;
underline = ( "@underline" | "@u" ) , content ;
del = "@del" , content ;
raw = "@raw" , raw-content ;
sup = "@sup" , content ;
sub = "@sub" , content ;
(* Footnotes:
fn = 正文中的引用點(角標),只帶編號
defn = 腳注定義本體,帶編號與實際內容
*)
fn = "@fn" , "[" , integer , "]" ;
(* The modifier of defn is the footnote id. It must be an integer to be
referenceable at all — fn's own "[...]" only accepts an integer, so a
non-numeric id can be written but no @fn can ever point at it. The Parser
reports it (see §11 Diagnostics). *)
defn = "@defn" , modifier , content ;
kbd = "@kbd" , "[" , key , "]" ;
link = "@link" , uri , content ;
(* v0.1.2. spoiler takes ordinary nestable content; hex takes a restricted
slot (like kbd's key), NOT generic content. See §15. *)
spoiler = "@spoiler" , content ;
hex = "@hex" , "[" , hex-code , "]" ;
hex-code = "#" , ( 3 * hex-digit | 6 * hex-digit ) ;
br = "@n" ;
escape = "@@" ;
(* ==========================================================================
Shared Components
========================================================================== *)
content =
"[" ,
{ content-element } ,
"]" ;
content-element =
inline-node
| plain-text-char ;
(* raw-content 的實際終止規則是「方括號深度計數」,不是「遇到第一個未跳脫
的 ]」——balanced-bracket-group 用遞迴產生式表達「內部只要左右括號成對,
可以隨意巢狀,完全不需要跳脫」;只有真正不成對的方括號才需要跳脫。
詳見 9. @raw Opaque Domain。*)
raw-content =
"[" , { raw-unit } , "]" ;
raw-unit =
escaped-at-close-bracket (* "@@]" → 字面 "@]" *)
| escaped-at-open-bracket (* "@@[" → 字面 "@[" *)
| escaped-close-bracket (* "@]" → 字面 "]"(僅用於不成對的 ]) *)
| escaped-open-bracket (* "@[" → 字面 "["(僅用於不成對的 [) *)
| balanced-bracket-group (* 成對、可巢狀的字面方括號,內容不受限 *)
| raw-char ;
balanced-bracket-group =
"[" , { raw-unit } , "]" ;
escaped-at-close-bracket = "@@]" ;
escaped-at-open-bracket = "@@[" ;
escaped-close-bracket = "@]" ;
escaped-open-bracket = "@[" ;
raw-char =
any-unicode-char - "]" - "[" ;
uri =
"(" ,
{ text-char - ")" } ,
")" ;
modifier =
"(" ,
{ text-char - ")" } ,
")" ;
(* Lexer 額外收斂:`text-char` 本身包含換行,字面解讀等於「未閉合的 "{" 可以
一路吞到文件後面任何一個 "}"」——作者還在打字的 "{" 會把中間所有節點
(例如底下 @code 區塊裡的大括號之前的一切)整段吃進 styles,從 AST 靜默
消失。styles 實際語意是一小串逗號分隔 token,沒有任何範例跨行,編輯器的
Monarch 規則(/\{[^}]*\}/,逐行比對)本來也不支援跨行,因此 Lexer 在
"}"、行尾、"["(內容槽開始)三者中最先出現的位置停止掃描,行尾與 "[" 兩種
情況視為未閉合。見 src/Lexer.ts 的 scanStylesEnd()。 *)
styles =
"{" ,
{ text-char - "}" - newline - "[" } ,
"}" ;
key =
{ text-char - "]" } ;
(* @color's semantic constraint on its {styles} content — see §7 for the
full validation rule (must match /^#[0-9a-fA-F]{6}$/); the terminal itself
is grammar-level only, exact digit-count/case validation is semantic-level,
same split as `styles` below. *)
hex-color =
"#" , hex-digit , hex-digit , hex-digit , hex-digit , hex-digit , hex-digit ;
hex-digit =
digit
| "a" | "b" | "c" | "d" | "e" | "f"
| "A" | "B" | "C" | "D" | "E" | "F" ;
integer =
digit ,
{ digit } ;
digit =
"0" | "1" | "2" | "3" | "4"
| "5" | "6" | "7" | "8" | "9" ;
(* ==========================================================================
Character Sets
========================================================================== *)
(* Note:
plain-text-char has lower precedence than inline-node.
The lexer MUST always attempt known command recognition
before falling back to plain text.
*)
plain-text-char =
any-unicode-char ;
text-char =
any-unicode-char ;
letter =
Unicode Letter ;
symbol =
Unicode Symbol ;5. Escape Rule
語法
@@輸出
@用途
當使用者需要輸出語法關鍵字本身時使用。
此為全域轉義規則,適用於一般 inline-stream 上下文。
@raw內部有獨立的轉義規則,請參見 9. @raw Opaque Domain。
範例
輸入:
@@mark輸出:
@mark輸入:
@@bold[hello]輸出:
@bold[hello]輸入:
Email: test@@example.com輸出:
Email: [email protected]雖然此寫法合法,但由於:
example並非已知指令,因此實際上可直接寫:
Email: [email protected]而不需要跳脫。
6. Unknown Command Fallback
若 @ 後方並非合法指令名稱,解析器必須退回純文字模式。
範例:
@github輸出:
@github輸出:
@my_custom_tag輸出:
@my_custom_tag此規則能有效避免與:
- 社群帳號
- Discord Mention
- GitHub Username
- Chat Mention System
發生衝突。
7. @mark / @color / @bordered Styles Semantics
@mark 支援可選的 styles 修飾語法:
@mark{style}[content]其中:
style為以逗號分隔的樣式標記字串(style token list)。content為被標記的文字內容。styles為可選(optional)語法,省略時等同純粹的高亮標記:
@mark[重要內容]Style Token 語意
style 內容為以逗號分隔的顏色 Token(Color Token)字串,代表高亮色彩,Renderer 依語意對應到實際顏色值。支援兩種寫法,可擇一使用:
- 具名 Token(Renderer 自訂實際色值): text
yellow / red / green / blue / orange / purple / gray - 16 進位 Hex Token(
#開頭、6 位十六進位數字,大小寫皆可,Renderer MUST 直接使用指定值,不得再映射):格式不符text#ff0000 / #3366FF / #00c896/^#[0-9a-fA-F]{6}$/的 token(例如#f00、#gggggg)不視為合法 hex token, 依一般規則走 Unknown Command Fallback 的容錯精神(見下方 Renderer 行為)。
變更記錄:先前版本另定義了
underline/strikethrough/bordered三個修飾 Token,已移除。underline與@underline節點語意重複;strikethrough與@del節點語意重複;bordered已升格為獨立節點@bordered(見下方)。style現在只承載顏色語意,不再混用修飾語意。
範例
@mark[預設高亮]
@mark{yellow}[黃色高亮]
@mark{red}[紅色高亮]
@mark{#3366ff}[16 進位背景色]@color — 文字改色
@mark 改變的是背景(高亮),無法改變文字本身的顏色。@color 補上這個能力:
@color{#ff0000}[這段文字是紅色的]@color 與 @mark 共用同一個 {styles} 欄位(見上方 EBNF),本身為選填—— 省略時 Renderer 退回預設色,行為與 @mark[content] 省略 {styles} 時相同。
@color{blue}[這段文字是深藍色的]@color 接受與 @mark 相同的七個具名 color token(yellow/red/green/blue/ orange/purple/gray),也接受單一 16 進位 hex token(/^#[0-9a-fA-F]{6}$/)。 兩者語法上共用同一組 token 名稱,但對應的實際色值各自獨立:@mark 的色階是為 淺色高亮背景調校的,直接當作文字前景色會對比度不足、難以閱讀,因此 Renderer 通常會維護一份色調較深、專門給 @color 用的對照表(而不是重用 @mark 那份)。 Renderer MUST 忽略格式不符或無法識別的值並以某種預設值作為 fallback,而非拋出錯誤:
@color{not-a-color}[這段沒有指定顏色,優雅地退回預設色]@bordered — 文字外框
@bordered 為文字加上外框,與 @color 共用完全相同的 {styles} 欄位——同樣的括號、同樣選填、同樣的七個具名 token 加 hex,也共用同一份色票對照表(實作上可直接重用 @color 的 resolver),只是套用在外框而非文字色:
@bordered[預設外框]
@bordered{blue}[藍色外框]
@bordered{#3366ff}[16 進位外框色]省略 {styles} 或給出無法識別的值時,Renderer 以其預設外框樣式作為 fallback,而非拋出錯誤,呼應 §6 Unknown Command Fallback 的容錯精神。此節點取代了 @mark 先前 {styles} 中的 bordered 修飾 token,成為獨立的第一級節點,與 underline(已有 @underline)、strikethrough(已有 @del)的角色一致。
Renderer 行為
- Renderer MUST 至少支援
styles省略時的預設高亮樣式(@mark)/預設外框樣式(@bordered)。 - Renderer MAY 自行決定各具名 color token 對應的實際色值(例如深色模式與淺色模式下的
yellow可不同;@mark與@color/@bordered也可以、通常也應該各自維護不同的對照表,理由見上);16 進位 hex token 則 MUST 直接使用指定值,不得重新映射。 - Renderer MUST 忽略無法識別的 token(包含格式不符的 hex token),並 SHOULD 以某種預設值作為 fallback,而非拋出錯誤——此行為與 6. Unknown Command Fallback 中「未知指令退回純文字」的容錯精神一致,但作用範圍限定在
styles內部,@mark[content]/@color[content]/@bordered[content]本身仍會被正常解析為對應節點。fallback 的具體樣子由 Renderer 自行決定:可以是「無額外顏色」(沿用預設文字色/外框色),也可以比照@mark省略styles時的預設高亮色再利用一次(三者共用同一個欄位形狀,這麼做在視覺上是自然的)。 - Token 之間的分隔符固定為半形逗號
,,前後允許任意數量空白(Parser 應自動 trim)。此規則適用於@mark的styles;@color/@bordered的{}內只允許單一 token(color token 或 hex 值),不使用逗號分隔。
EBNF 補充說明
對應 4. 完整 EBNF 語法定義 中:
(* Lexer 額外收斂:`text-char` 本身包含換行,字面解讀等於「未閉合的 "{" 可以
一路吞到文件後面任何一個 "}"」——作者還在打字的 "{" 會把中間所有節點
(例如底下 @code 區塊裡的大括號之前的一切)整段吃進 styles,從 AST 靜默
消失。styles 實際語意是一小串逗號分隔 token,沒有任何範例跨行,編輯器的
Monarch 規則(/\{[^}]*\}/,逐行比對)本來也不支援跨行,因此 Lexer 在
"}"、行尾、"["(內容槽開始)三者中最先出現的位置停止掃描,行尾與 "[" 兩種
情況視為未閉合。見 src/Lexer.ts 的 scanStylesEnd()。 *)
styles =
"{" ,
{ text-char - "}" - newline - "[" } ,
"}" ;styles 本身在詞法層級僅定義為「花括號包裹的任意字元序列」,實際的 token 切分(以逗號分隔、辨識 color token 與 modifier token)屬於語意層級(semantic level)的處理,非 Lexer/Parser 的語法層責任,而是留給 Renderer 或後續語意分析階段完成。這樣設計可確保:
- 新增 style token(例如未來加入
italic、bold等)不需要修改 EBNF 語法定義本身。 - 不同 Renderer 可自行擴充或裁剪其支援的 token 集合,符合 1. Design Philosophy 中「保持 DSL 的可擴充性」的目標。
8. @link URI Semantics
@link 接受任何合法 URI 或 URI-like Identifier。
@link(uri)[content]其中:
uri為目標資源識別符。content為顯示文字。
Renderer URI Inference
Renderer MAY 根據 uri 的內容自動推導 URI Scheme。
例如:
| 輸入 | Renderer 實際 URI |
|---|---|
@link(example.com)[官方網站] | https://example.com |
@link([email protected])[聯絡我] | mailto:[email protected] |
@link(+886912345678)[客服電話] | tel:+886912345678 |
若 uri 已明確指定 Scheme:
@link(https://example.com)[官方網站]
@link(mailto:[email protected])[聯絡我]
@link(tel:+886912345678)[客服電話]Renderer MUST 直接使用指定值,不得進行推導或修改。
Supported URI Examples
以下皆屬合法 uri:
https://example.com
mailto:[email protected]
tel:+886912345678
ftp://example.com/file.zip
discord://channel/123
vscode://file/path
file:///tmp/test.txt@Doc 本身不限制 URI 類型。
URI 的實際支援能力由 Renderer 決定。
9. @raw Opaque Domain
@raw 屬於:
Opaque Domain
解析器進入:
@raw[之後:
- 不解析任何內部語法。
- 所有
@mark、@bold、@link等關鍵字皆視為純文字。 - 全域
@@轉義規則(5. Escape Rule)不再適用,raw 域內是獨立的一套局部規則(見下方)。
終止規則:方括號深度計數,不是「遇到第一個 ]」
@raw[...] 的實作模型是方括號深度計數,這點必須先講清楚,因為它直接決定了跳脫規則什麼時候該用、什麼時候不該用:
[讓深度 +1,]讓深度 -1;深度歸零的那個]才是真正的結尾。- 换句話說,只要方括號成對,可以直接照抄,完全不需要跳脫——
@raw[@mark[hello]]裡的@mark[hello]本身左右括號成對,Parser 會正確在最外層那個]結束,輸出@mark[hello]原樣文字。 - 跳脫規則的存在,是專門給不成對的方括號用的——例如只想寫一個單獨的字面
]、或引用一段本身括號不平衡的內容片段。如果對一個本來就成對的方括號多加跳脫(例如把@mark[hello]的結尾寫成@mark[hello@]),跳脫消耗掉的]不會讓深度計數歸零,於是前面@mark[那個[造成的深度 +1 永遠找不到對應的]抵銷,Parser 只能繼續往後找,直到把外層更多內容(甚至整份文件)都吞下去才會出錯。平衡的括號直接寫;不平衡的括號一律跳脫;跳脫字元不參與深度計數。
跳脫規則是對稱的——] 和 [ 各自都有「單字元跳脫」與「跳脫『@』本身後面接該字元」兩種,共四條,掃描時依下列優先序比對(長的、更明確的序列優先):
| 優先序 | 輸入 | 輸出 | 說明 |
|---|---|---|---|
| 1 | @@] | @] | 輸出字面 @] 兩個字元 |
| 2 | @@[ | @[ | 輸出字面 @[ 兩個字元 |
| 3 | @] | ] | 輸出不成對的字面 ](不影響深度計數) |
| 4 | @[ | [ | 輸出不成對的字面 [(不影響深度計數) |
範例
輸入:
@raw[@mark[hello]]輸出:
@mark[hello]說明:
@mark[hello]本身括號成對,深度計數會 1→2→1,最外層那個]才讓深度歸零並結束@raw。不需要任何跳脫——這是最常見的用法(在 raw 內容裡示範一段完整、括號平衡的 @Doc 語法)。
輸入:
@raw[@@]輸出:
@@說明:此處
@@後方緊接的是 raw-content 的結尾], 因此並未觸發@@]特例(@@]須為連續三字元), 應拆解為:一般字元@@(原樣輸出,因全域轉義已停用)+ 結尾]。
輸入:
@raw[今天我怕@]被偵測]輸出:
今天我怕]被偵測說明:這裡的
@]是一個不成對的字面](前面沒有對應的[),必須跳脫,否則它會被當成@raw自己的結尾,讓後面的「被偵測」跑到 raw 內容之外。
輸入:
@raw[今天我怕@@]被偵測]輸出:
今天我怕@]被偵測輸入(跳脫用在不該用的地方——反例):
@raw[這裡的 @mark[hello@] 保持原樣]渲染語意:行內程式碼
以上規則定義的是 Parser 行為(內容不解析、原樣保留)。渲染端的對應語意是行內程式碼——與 Markdown 的反引號 `code` 是同一件事:
- Renderer SHOULD 將
@raw輸出為等寬(monospace)行內元素。HTML Route 使用<code>。 - 這與
@code區分開來:@code是區塊(HTML 為<pre><code>),@raw是行內。 - 也與
@kbd區分開來:@kbd表示實體按鍵,慣例上帶邊框與鍵帽外觀;@raw是程式碼文字,只需等寬與底色。 - Renderer 不得因此解析內容——渲染語意的加入不改變 opaque domain 的解析規則。
為何是
@raw而不是另立節點:raw-escaped是全語言唯一具備正規跳脫機制(@]/@[加方括號深度計數)的內容模式,而「內容不得被解析」正是行內程式碼的定義性需求。兩者的使用情境幾乎完全重疊——本節上方每一個範例都在展示程式碼。
10. Nested Parsing
由於:
content-element =
inline-node
| plain-text-char ;因此 @Doc 支援完整遞迴嵌套。
例如:
@bold[
這是粗體,
裡面有
@mark{yellow}[重要高亮]
與
@underline[底線]
]其 AST 結構為:
Bold
├── Text
├── Mark
└── Underline11. Parser Recovery Strategy
當 Parser 遇到未閉合結構時:
@bold[hello或:
@mark{red}[hello建議提供兩種模式:
Strict Mode
直接拋出語法錯誤:
Unexpected EOF while parsing @boldDiagnostics
除了未關閉的結構之外,Parser 也會回報一類「語法上合法、但幾乎可以確定不是作者本意」的情況——這些情況過去會乾淨地解析、然後渲染出錯誤結果,卻沒有任何地方提示。每一項都以 DocDiagnostic 回報,帶有來源範圍與訊息;它們都不會中止解析,也不會改變文件其餘部分產生的 AST。
| 情況 | 嚴重程度 | 說明 |
|---|---|---|
@data 某列的寬度(含 @span)≠ @cols 欄數 | error | 回報在有問題的那一列。絕不自動補齊或截斷。見 Block Spec 第 5 節 Table |
@span(n) 的 n 不是正整數 | error | 列寬度檢查時以寬度 1 計算,因此一個根本原因只產生一則 diagnostic |
@span 出現在 @data 儲存格之外(@cols 內、段落內、與其他儲存格內容混用) | error | 與 @tabs 之外的 @tab 採相同的限定位置規則 |
@hex[...] 內容不是 #RGB/#RRGGBB | error | 具名色、4/5/8 位數色碼,或缺少 #。8 位數 #RRGGBBAA 目前刻意拒絕(第 15 節) |
@img 的 align 值無法識別 | warning | 該值被忽略(不對齊),文件仍會渲染。見 Block Spec 第 5 節 Image |
節點 {styles} 內有無法識別的 token | error | 例如 @card{blue}、@mark{bluee}、@img{radius-x}。該 token 在渲染時仍被忽略。範圍指向該 token 本身 |
@defn(id) 的 id 不是整數 | error | @fn[...] 只接受整數,這則腳注永遠無法被引用。回報在 @defn 上,也就是選定該 id 的地方 |
@color/@bordered 沒有 {styles} 值 | error | 會退回彩虹外觀/沒有有意義的外框顏色 |
吞掉內容的 @raw 轉義,或任何被消耗的轉義 | warning/info | 見第 9 節 |
「嚴重程度」是 diagnostic 上攜帶的值。error 在 Strict Mode 下是否阻斷、在 Editor Mode 下是否僅加底線,由使用端決定,與上文所述完全相同。
Editor Mode
允許編輯器自動補全缺失閉合符號:
]以提升即時編輯體驗。
12. Architecture
推薦解析流程:
Source Text
↓
Lexer
↓
Token Stream
↓
Parser
↓
AST
↓
RendererRenderer 可以自由輸出:
- HTML
- React
- DOCX
- Markdown
- Discord
- Terminal
- Custom UI
13. Core Principle
@Doc 的核心目標並非取代 Markdown。
而是建立:
Human Editable Machine Deterministic AI Friendly Cross Platform
的新一代文件中介格式。
14. Simplified Syntax Aliases
@bold/@italic/@underline 提供簡化別名 @b/@i/@u——純粹是輸入時的簡寫,Parser 會將其正規化為正典名稱後才建立 AST 節點(node.type 永遠是正典名稱),Renderer 完全不需要、也不會區分作者實際輸入的是哪一種寫法。
| Canonical | Alias |
|---|---|
@bold | @b |
@italic | @i |
@underline | @u |
(Block Syntax 的 @heading/@paragraph 別名 @h/@p 定義在 Block Syntax Specification §11。)
15. Semantic Inline:@spoiler 與 @hex
語法 v0.1.2 新增的兩個行內節點。兩者都是一般行內節點——凡是 inline-stream 能出現的地方都能使用。
@spoiler
這部電影最後@spoiler[主角其實是反派],劇情轉折非常精彩。spoiler = "@spoiler" , content ;句子層級的隱藏片段:由讀者自行選擇是否揭露的內容,不會把周圍的行文拆成獨立區塊。content 是標準的可巢狀產生式,因此 @spoiler[@bold[驚天逆轉]] 是合法的,格式在被隱藏時也會保留。
- 不是
@details的區塊版。@details是有標題的區塊層級漸進式揭露——讀者展開的是整個段落。@spoiler是句子之中的一個片段,揭露後仍然是同一個段落。刻意不設區塊層級的 spoiler 節點:區塊層級的防雷就是標題為「劇透警告」之類的@details,同一件事用兩個節點表達,會讓語言有兩種說法。 - 表格儲存格。
@spoiler不在表格儲存格允許清單上(Block Spec 第 5 節 Table)。 - 渲染(Web)。 參考 renderer 輸出
<span class="spoiler" tabindex="0">…</span>。背景與文字預設都是隱藏的,並在:hover/:focus時同時清除,不需要 script。tabindex讓該 span 可用鍵盤到達、使 focus 能觸發,同時也是讓輔助技術能讀取內容的掛鉤。 - 無法在互動時揭露的輸出目標(純文字、搜尋索引、供 LLM 使用的擷取結果、PDF):實際的替代標示由 renderer 決定,但 renderer MUST NOT 把防雷的真實文字悄悄複製進搜尋索引或自動產生的摘要。以
[防雷]之類的標記取代,或把該句從索引中排除,都可接受。完全無互動能力的目標(DOCX)可以合理地將它轉成註腳或可展開的註解。
參考 renderer 的樣式掛鉤是 .spoiler class。最小規則集:
.spoiler { background: currentColor; border-radius: 3px; cursor: pointer; outline: none; }
.spoiler:hover, .spoiler:focus { background: transparent; }(隱藏時文字顏色必須等於背景色——background: currentColor 可以順帶達成——揭露時兩者都要重設。)
@hex
主色為 @hex[#3366ff],輔助色為 @hex[#f80]。hex = "@hex" , "[" , hex-code , "]" ;
hex-code = "#" , ( 3 * hex-digit | 6 * hex-digit ) ;顯示一個色塊與其字面色碼——這個節點的主體就是顏色值本身。
- 受限內容。 與
@kbd的key相同,括號內放的是要驗證的值,不是散文:@hex[@bold[#fff]]不是粗體色碼,因為在@hex的括號內,語法不會經過inline-node。 - 接受的形式:
#RGB與#RRGGBB。8 位數的#RRGGBBAA在本版本刻意保持未定,並且會被拒絕而不是靜默接受,如此日後採用 alpha 慣例時就不會是破壞性變更。不符合語法的內容(具名色、4 或 5 位數色碼、缺少#)是 diagnostic,絕不是靜默退回。 - 表格儲存格。
@hex在表格儲存格允許清單上——色票表與設計 token 表是它最常見的用途。 - 渲染(Web)。 參考 renderer 輸出
<span class="hex-swatch"><span class="hex-swatch-color" style="background-color:#3366ff;"></span><code>#3366ff</code></span>。只有色塊的背景色是 inline 的;尺寸與間距由樣式表負責(.hex-swatch、.hex-swatch-color)。點擊複製等互動屬於 renderer,不屬於語法。 - 無法顯示顏色的輸出目標(純文字 terminal):印出色碼
#3366ff。
@hex 不是 @color
兩者很容易被互相替代使用,意思卻相反:
| 節點 | 意思 |
|---|---|
@color{#3366ff}[文字] | 「讓這段文字變成這個顏色」——hex 值是在為無關的內容上色 |
@hex[#3366ff] | 「顯示這個色碼本身」——色碼就是內容 |
列出色票的設計系統文件要用 @hex;剛好需要藍色文字的段落要用 @color。寫成 @color{#3366ff}[#3366ff] 來顯示色票,渲染出來看起來沒錯,但結構上是錯的節點:掃描「這份文件把哪些顏色當色票呈現」的工具找得到每一個 @hex,卻無法把這個意外的 @color 與一般的彩色文字區分開來。
@Doc