@riebeckite/plugin-shortcodes 0.0.19 → 0.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,13 +6,15 @@ A generic shortcode system for Markdown, built on `remark-directive`.
6
6
 
7
7
  ## Overview
8
8
 
9
- `shortcodes()` turns `remark-directive` syntax into HTML at Markdown time.
10
- It supports leaf shortcodes (`::name[label]{key=value}`) and container
11
- shortcodes (`:::name[label]{attrs}` … `:::`), and ships an extensible registry
12
- so projects can add their own renderers on top of the built-ins.
9
+ `shortcodes()` turns `remark-directive` syntax into HTML at Markdown time. It
10
+ supports inline shortcodes (`:name[label]{key=value}`), block leaf shortcodes
11
+ (`::name[label]{key=value}`), and container shortcodes
12
+ (`:::name[label]{attrs}` … `:::`), and ships an extensible registry so projects
13
+ can add their own renderers on top of the built-ins.
13
14
 
14
- Output is rendered into `mdast` `html` nodes and wrapped in a `span` (leaf) or
15
- `div` (container) carrying `rb-shortcode rb-shortcode--<name>`.
15
+ Inline shortcodes render as a `span` inside the surrounding paragraph; block and
16
+ container shortcodes render as a `div`. Every wrapper carries
17
+ `rb-shortcode rb-shortcode--<name>`.
16
18
 
17
19
  ## Usage
18
20
 
@@ -31,6 +33,8 @@ Styles ship in `style.css`.
31
33
  ## Syntax
32
34
 
33
35
  ```
36
+ :badge[New]{variant=success}
37
+
34
38
  ::kbd[Ctrl+S]
35
39
 
36
40
  ::youtube[id=dQw4w9WgXcQ]
@@ -40,30 +44,206 @@ Container bodies support **Markdown**.
40
44
  :::
41
45
  ```
42
46
 
43
- - `::name[label]{key=value}` — leaf shortcode, rendered inline.
47
+ - `:name[label]{key=value}` — inline shortcode, rendered as a `span` inside the
48
+ current paragraph. Only names in `inlineShortcodes` can be used inline;
49
+ built-ins opt in with `badge`, `kbd`, `link-card`, and `file`.
50
+ - `::name[label]{key=value}` — block leaf shortcode, rendered as a `div`.
44
51
  - `:::name[label]{key=value}` … `:::` — container shortcode; the body is
45
52
  rendered by the normal Markdown pipeline.
46
53
 
47
- Unknown shortcodes emit a `shortcodes-unknown` diagnostic and stay on the page
48
- as escaped text. Malformed attributes emit `shortcodes-invalid` and are
49
- ignored.
54
+ A shortcode that is only defined as a block emits a
55
+ `shortcodes-inline-unsupported` diagnostic when written with `:` and stays on
56
+ the page as escaped text. Unknown shortcodes emit a `shortcodes-unknown`
57
+ diagnostic, and malformed attributes emit `shortcodes-invalid` and are ignored.
50
58
 
51
59
  ## Built-in shortcodes
52
60
 
53
- | Name | Kind | Attributes | Output |
54
- | ---- | ---- | ---------- | ------ |
55
- | `figure` | leaf / container | `src`/`url`/`image`, `alt`, `caption`, `width`, `height` | `<figure>` with image and caption |
56
- | `youtube` | leaf | `id`/`video` or `url`/`src`, `title` | Privacy-friendly `youtube-nocookie.com` embed |
57
- | `vimeo` | leaf | `id`/`video` or `url`/`src`, `title` | `player.vimeo.com` embed (`dnt=1`) |
58
- | `gist` | leaf | `user` + `id`, or `url`, `file` | GitHub Gist embed with `<noscript>` link |
59
- | `kbd` | leaf | label or `keys` | `<kbd>` elements split on `+` |
60
- | `badge` | leaf | label or `text`, `variant`/`type`/`color`, `title` | Inline badge |
61
- | `details` | container | label or `summary`, `open` | `<details>` disclosure |
62
- | `spoiler` | container | label or `summary`, `open` | `details` alias with a different class |
63
- | `note` | leaf / container | label or `title`, `type`/`variant` | Callout box |
64
- | `callout` | leaf / container | label or `title`, `type`/`variant` | `note` alias with a different class |
65
- | `link-card` | leaf | `url`/`href`, `title`, `description`, `image`/`icon` | Link preview card |
66
- | `file` | leaf | `url`/`src`/`path`, `name`/`label`, `size` | Download link |
61
+ | Name | Kind | Inline | Attributes | Output |
62
+ | ---- | ---- | ------ | ---------- | ------ |
63
+ | `figure` | leaf / container | – | `src`/`url`/`image`, `alt`, `caption`, `width`, `height` | `<figure>` with image and caption |
64
+ | `youtube` | leaf | – | `id`/`video` or `url`/`src`, `title` | Privacy-friendly `youtube-nocookie.com` embed |
65
+ | `vimeo` | leaf | – | `id`/`video` or `url`/`src`, `title` | `player.vimeo.com` embed (`dnt=1`) |
66
+ | `gist` | leaf | – | `user` + `id`, or `url`, `file` | GitHub Gist embed with `<noscript>` link |
67
+ | `kbd` | leaf | yes | label or `keys` | `<kbd>` elements split on `+` |
68
+ | `badge` | leaf | yes | label or `text`, `variant`/`type`/`color`, `title` | Inline badge |
69
+ | `details` | container | – | label or `summary`, `open` | `<details>` disclosure |
70
+ | `spoiler` | container | – | label or `summary`, `open` | `details` alias with a different class |
71
+ | `note` | leaf / container | – | label or `title`, `type`/`variant` | Callout box |
72
+ | `callout` | leaf / container | – | label or `title`, `type`/`variant` | `note` alias with a different class |
73
+ | `link-card` | leaf | yes | `url`/`href`, `title`, `description`, `image`/`icon` | Link preview card |
74
+ | `file` | leaf | yes | `url`/`src`/`path`, `name`/`label`, `size` | Download link |
75
+
76
+ ## Actual Rendering
77
+
78
+ The following examples demonstrate all 12 built-in shortcodes. Each example shows the Markdown syntax followed by the shortcode itself, allowing you to see how it renders when the Shortcodes plugin is enabled.
79
+
80
+ ### Badge
81
+
82
+ Display status indicators or labels inline with text.
83
+
84
+ ```md
85
+ This feature is :badge[New]{variant=success}.
86
+
87
+ Current status: :badge[Beta]{variant=warning}
88
+ ```
89
+
90
+ This feature is :badge[New]{variant=success}.
91
+
92
+ Current status: :badge[Beta]{variant=warning}
93
+
94
+ ### Keyboard (kbd)
95
+
96
+ Display keyboard shortcuts using semantic `<kbd>` elements.
97
+
98
+ ```md
99
+ Press :kbd[Ctrl+S] to save your changes.
100
+
101
+ Use :kbd[Ctrl+K] to open search.
102
+ ```
103
+
104
+ Press :kbd[Ctrl+S] to save your changes.
105
+
106
+ Use :kbd[Ctrl+K] to open search.
107
+
108
+ ### Note
109
+
110
+ Highlight useful information, tips, and other supplementary content.
111
+
112
+ ```md
113
+ :::note[Tip]{type=info}
114
+ You can use **Markdown** inside this container.
115
+
116
+ - Lists
117
+ - **Bold text**
118
+ - [Links](https://example.com)
119
+ :::
120
+ ```
121
+
122
+ :::note[Tip]{type=info}
123
+ You can use **Markdown** inside this container.
124
+
125
+ - Lists
126
+ - **Bold text**
127
+ - [Links](https://example.com)
128
+ :::
129
+
130
+ ### Callout
131
+
132
+ Display important notices or warnings. `callout` is an alias of `note` with a different CSS class.
133
+
134
+ ```md
135
+ :::callout[Warning]{type=warning}
136
+ Remember to rebuild your site after changing the configuration file.
137
+ :::
138
+ ```
139
+
140
+ :::callout[Warning]{type=warning}
141
+ Remember to rebuild your site after changing the configuration file.
142
+ :::
143
+
144
+ ### Details
145
+
146
+ Create collapsible sections that users can expand to reveal additional content.
147
+
148
+ ```md
149
+ :::details[Show details]
150
+ This content is hidden until the section is expanded.
151
+
152
+ You can include **Markdown** and lists:
153
+
154
+ - Item A
155
+ - Item B
156
+ :::
157
+ ```
158
+
159
+ :::details[Show details]
160
+ This content is hidden until the section is expanded.
161
+
162
+ You can include **Markdown** and lists:
163
+
164
+ - Item A
165
+ - Item B
166
+ :::
167
+
168
+ ### Spoiler
169
+
170
+ Hide content behind an expandable section. `spoiler` works like `details` but uses a different CSS class.
171
+
172
+ ```md
173
+ :::spoiler[Reveal spoiler]
174
+ The hidden content appears when the user expands this section.
175
+
176
+ **Markdown** is supported here too.
177
+ :::
178
+ ```
179
+
180
+ :::spoiler[Reveal spoiler]
181
+ The hidden content appears when the user expands this section.
182
+
183
+ **Markdown** is supported here too.
184
+ :::
185
+
186
+ ### Figure
187
+
188
+ Display images with optional captions and dimensions using semantic `<figure>` markup.
189
+
190
+ ```md
191
+ ::figure[Sample image]{src="https://placehold.co/800x400/png" alt="Sample placeholder image" caption="Figure 1: Example image"}
192
+ ```
193
+
194
+ ::figure[Sample image]{src="https://placehold.co/800x400/png" alt="Sample placeholder image" caption="Figure 1: Example image"}
195
+
196
+ ### YouTube
197
+
198
+ Embed YouTube videos using the privacy-enhanced `youtube-nocookie.com` domain.
199
+
200
+ ```md
201
+ ::youtube[id=dQw4w9WgXcQ]
202
+ ```
203
+
204
+ ::youtube[id=dQw4w9WgXcQ]
205
+
206
+ ### Vimeo
207
+
208
+ Embed Vimeo videos with the `dnt=1` privacy parameter.
209
+
210
+ ```md
211
+ ::vimeo[id=76979871]
212
+ ```
213
+
214
+ ::vimeo[id=76979871]
215
+
216
+ ### GitHub Gist
217
+
218
+ Embed GitHub Gist code snippets directly into a page. A fallback link is provided for environments where JavaScript is disabled.
219
+
220
+ ```md
221
+ ::gist{user=octocat id=aa5a315d61ae9438b18d}
222
+ ```
223
+
224
+ ::gist{user=octocat id=aa5a315d61ae9438b18d}
225
+
226
+ ### Link Card
227
+
228
+ Display external links as preview cards with titles and descriptions.
229
+
230
+ ```md
231
+ ::link-card[GitHub]{url="https://github.com" title="GitHub" description="A platform for hosting code and collaborating on software projects"}
232
+ ```
233
+
234
+ ::link-card[GitHub]{url="https://github.com" title="GitHub" description="A platform for hosting code and collaborating on software projects"}
235
+
236
+ ### File
237
+
238
+ Create downloadable file links with optional filenames and file sizes.
239
+
240
+ ```md
241
+ ::file[Sample PDF]{url="https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf" name="sample.pdf" size="13 KB"}
242
+ ```
243
+
244
+ ::file[Sample PDF]{url="https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf" name="sample.pdf" size="13 KB"}
245
+
246
+ The code blocks above show the original Markdown syntax. The shortcode expressions immediately below them serve as live rendering examples when processed by Riebeckite with the Shortcodes plugin enabled.
67
247
 
68
248
  ## Options
69
249
 
@@ -73,6 +253,7 @@ ignored.
73
253
  | `language` | `string` | – | BCP-47 tag applied to wrappers as `lang` |
74
254
  | `builtins` | `boolean` | `true` | Register the built-in renderers |
75
255
  | `shortcodes` | `Record<string, ShortcodeRenderer>` | `{}` | Custom renderers, merged over the built-ins |
256
+ | `inlineShortcodes` | `readonly string[]` | `[]` | Extra names allowed in the inline `:` form |
76
257
 
77
258
  ## Custom renderers
78
259
 
@@ -83,16 +264,19 @@ shortcodes({
83
264
  shortcodes: {
84
265
  mark: ({ label, attributes }) => `<mark>${label}</mark>`,
85
266
  },
267
+ inlineShortcodes: ["mark"],
86
268
  });
87
269
  ```
88
270
 
89
271
  A `ShortcodeRenderer` receives `{ name, label, attributes, childrenHtml,
90
- context, container }` and returns a string. For container shortcodes the plugin
91
- wraps the result in `<div class="rb-shortcode rb-shortcode--<name>">` and
92
- replaces `childrenHtml` with the rendered body, so container renderers must echo
93
- `input.childrenHtml` where the body belongs. Renderers are synchronous and must
94
- escape any user input themselves — `escapeHtml` and `escapeHtmlAttribute` are
95
- re-exported from `@riebeckite/core`.
272
+ context, container }` and returns a string. A custom renderer can be used with
273
+ the inline `:` form only when its name is listed in `inlineShortcodes`. For
274
+ container shortcodes the plugin wraps the result in
275
+ `<div class="rb-shortcode rb-shortcode--<name>">` and replaces `childrenHtml`
276
+ with the rendered body, so container renderers must echo `input.childrenHtml`
277
+ where the body belongs. Renderers are synchronous and must escape any user
278
+ input themselves — `escapeHtml` and `escapeHtmlAttribute` are re-exported from
279
+ `@riebeckite/core`.
96
280
 
97
281
  ## Exports
98
282
 
@@ -101,11 +285,11 @@ re-exported from `@riebeckite/core`.
101
285
  - `renderShortcode(request, options)` — render a single shortcode
102
286
  - `resolveShortcodeOptions(options?)` — normalize options
103
287
  - `builtinShortcodes`, `builtinShortcodeNames` — the built-in registry
288
+ - `builtinInlineShortcodes` — built-in names allowed in the inline `:` form
104
289
  - Types: `ShortcodeRenderer`, `ShortcodeOptions`, `ResolvedShortcodeOptions`,
105
290
  `ShortcodeRenderInput`, `ShortcodeRenderRequest`, `ShortcodeAttributes`,
106
291
  `RemarkShortcodesOptions`
107
292
 
108
293
  ## See also
109
294
 
110
- - [Plugin guide](../../../docs/docs/reference/plugin-api.en.md)
111
-
295
+ - [Plugin guide](../../../docs/docs/reference/plugin-api.md)
package/README_ja.md CHANGED
@@ -7,12 +7,13 @@
7
7
  ## 概要
8
8
 
9
9
  `shortcodes()` は `remark-directive` の記法を Markdown 処理時に HTML へ変換
10
- します。リーフ型(`::name[label]{key=value}`)とコンテナ型
11
- (`:::name[label]{attrs}` … `:::`)の両方に対応し、組み込みレンダラーに
12
- 独自のレンダラーを重ねられる拡張可能なレジストリを備えます。
10
+ します。インライン型(`:name[label]{key=value}`)、ブロックのリーフ型
11
+ (`::name[label]{key=value}`)、コンテナ型(`:::name[label]{attrs}` … `:::`)
12
+ に対応し、組み込みレンダラーに独自のレンダラーを重ねられる拡張可能な
13
+ レジストリを備えます。
13
14
 
14
- 出力は `mdast` の `html` ノードとして書き出され、リーフは `span`、コンテナは
15
- `div` で `rb-shortcode rb-shortcode--<name>` を付けてラップされます。
15
+ インライン型は段落の中の `span` として、リーフ型とコンテナ型は `div` として
16
+ 描画されます。ラッパーには `rb-shortcode rb-shortcode--<name>` が付きます。
16
17
 
17
18
  ## 使い方
18
19
 
@@ -31,6 +32,8 @@ export default defineConfig({
31
32
  ## 記法
32
33
 
33
34
  ```
35
+ :badge[新着]{variant=success}
36
+
34
37
  ::kbd[Ctrl+S]
35
38
 
36
39
  ::youtube[id=dQw4w9WgXcQ]
@@ -40,30 +43,206 @@ export default defineConfig({
40
43
  :::
41
44
  ```
42
45
 
43
- - `::name[label]{key=value}` — リーフ型。インラインで描画されます。
46
+ - `:name[label]{key=value}` — インライン型。現在の段落の中の `span` として
47
+ 描画されます。インラインで使えるのは `inlineShortcodes` に含まれる名前だけ
48
+ で、組み込みでは `badge`、`kbd`、`link-card`、`file` が該当します。
49
+ - `::name[label]{key=value}` — ブロックのリーフ型。`div` として描画されます。
44
50
  - `:::name[label]{key=value}` … `:::` — コンテナ型。本文は通常の Markdown
45
51
  パイプラインで描画されます。
46
52
 
47
- 未知のショートコードは `shortcodes-unknown` 診断を出し、エスケープした
48
- テキストとしてページに残します。不正な属性は `shortcodes-invalid` を出して
49
- 無視します。
53
+ ブロック専用のショートコードを `:` で書くと
54
+ `shortcodes-inline-unsupported` 診断を出し、エスケープしたテキストとして
55
+ ページに残します。未知のショートコードは `shortcodes-unknown` 診断を出し、
56
+ 不正な属性は `shortcodes-invalid` を出して無視します。
50
57
 
51
58
  ## 組み込みショートコード
52
59
 
53
- | 名前 | 種類 | 属性 | 出力 |
54
- | ---- | ---- | ---- | ---- |
55
- | `figure` | リーフ / コンテナ | `src`/`url`/`image`, `alt`, `caption`, `width`, `height` | 画像とキャプションの `<figure>` |
56
- | `youtube` | リーフ | `id`/`video` または `url`/`src`, `title` | `youtube-nocookie.com` のプライバシー配慮埋め込み |
57
- | `vimeo` | リーフ | `id`/`video` または `url`/`src`, `title` | `player.vimeo.com` 埋め込み(`dnt=1`) |
58
- | `gist` | リーフ | `user` + `id`、または `url`, `file` | `<noscript>` リンク付きの Gist 埋め込み |
59
- | `kbd` | リーフ | ラベルまたは `keys` | `+` で分割した `<kbd>` |
60
- | `badge` | リーフ | ラベルまたは `text`, `variant`/`type`/`color`, `title` | インラインバッジ |
61
- | `details` | コンテナ | ラベルまたは `summary`, `open` | `<details>` 折りたたみ |
62
- | `spoiler` | コンテナ | ラベルまたは `summary`, `open` | クラス違いの `details` エイリアス |
63
- | `note` | リーフ / コンテナ | ラベルまたは `title`, `type`/`variant` | コールアウト |
64
- | `callout` | リーフ / コンテナ | ラベルまたは `title`, `type`/`variant` | クラス違いの `note` エイリアス |
65
- | `link-card` | リーフ | `url`/`href`, `title`, `description`, `image`/`icon` | リンクプレビューカード |
66
- | `file` | リーフ | `url`/`src`/`path`, `name`/`label`, `size` | ダウンロードリンク |
60
+ | 名前 | 種類 | インライン | 属性 | 出力 |
61
+ | ---- | ---- | ---------- | ---- | ---- |
62
+ | `figure` | リーフ / コンテナ | – | `src`/`url`/`image`, `alt`, `caption`, `width`, `height` | 画像とキャプションの `<figure>` |
63
+ | `youtube` | リーフ | – | `id`/`video` または `url`/`src`, `title` | `youtube-nocookie.com` のプライバシー配慮埋め込み |
64
+ | `vimeo` | リーフ | – | `id`/`video` または `url`/`src`, `title` | `player.vimeo.com` 埋め込み(`dnt=1`) |
65
+ | `gist` | リーフ | – | `user` + `id`、または `url`, `file` | `<noscript>` リンク付きの Gist 埋め込み |
66
+ | `kbd` | リーフ | 可 | ラベルまたは `keys` | `+` で分割した `<kbd>` |
67
+ | `badge` | リーフ | 可 | ラベルまたは `text`, `variant`/`type`/`color`, `title` | インラインバッジ |
68
+ | `details` | コンテナ | – | ラベルまたは `summary`, `open` | `<details>` 折りたたみ |
69
+ | `spoiler` | コンテナ | – | ラベルまたは `summary`, `open` | クラス違いの `details` エイリアス |
70
+ | `note` | リーフ / コンテナ | – | ラベルまたは `title`, `type`/`variant` | コールアウト |
71
+ | `callout` | リーフ / コンテナ | – | ラベルまたは `title`, `type`/`variant` | クラス違いの `note` エイリアス |
72
+ | `link-card` | リーフ | 可 | `url`/`href`, `title`, `description`, `image`/`icon` | リンクプレビューカード |
73
+ | `file` | リーフ | 可 | `url`/`src`/`path`, `name`/`label`, `size` | ダウンロードリンク |
74
+
75
+ ## 実際のレンダリング
76
+
77
+ 以下の例では、組み込みショートコード全12種類の使い方を紹介します。各例では Markdown の記法と、その直下にショートコード自体を記載しています。Shortcodes プラグインが有効な場合、実際のレンダリング結果を確認できます。
78
+
79
+ ### バッジ(badge)
80
+
81
+ 文章の途中にステータスやラベルを表示できます。
82
+
83
+ ```md
84
+ この機能は :badge[新着]{variant=success} です。
85
+
86
+ 現在の状態: :badge[ベータ版]{variant=warning}
87
+ ```
88
+
89
+ この機能は :badge[新着]{variant=success} です。
90
+
91
+ 現在の状態: :badge[ベータ版]{variant=warning}
92
+
93
+ ### キーボード(kbd)
94
+
95
+ キーボードショートカットを意味的に適切な `<kbd>` 要素で表示できます。
96
+
97
+ ```md
98
+ 変更を保存するには :kbd[Ctrl+S] を押してください。
99
+
100
+ 検索を開くには :kbd[Ctrl+K] を使用します。
101
+ ```
102
+
103
+ 変更を保存するには :kbd[Ctrl+S] を押してください。
104
+
105
+ 検索を開くには :kbd[Ctrl+K] を使用します。
106
+
107
+ ### ノート(note)
108
+
109
+ ヒントや補足情報などを目立たせて表示できます。
110
+
111
+ ```md
112
+ :::note[ヒント]{type=info}
113
+ このコンテナ内では **Markdown** を使用できます。
114
+
115
+ - リスト
116
+ - **太字**
117
+ - [リンク](https://example.com)
118
+ :::
119
+ ```
120
+
121
+ :::note[ヒント]{type=info}
122
+ このコンテナ内では **Markdown** を使用できます。
123
+
124
+ - リスト
125
+ - **太字**
126
+ - [リンク](https://example.com)
127
+ :::
128
+
129
+ ### コールアウト(callout)
130
+
131
+ 重要な通知や警告を表示できます。`callout` は `note` のエイリアスで、異なる CSS クラスが適用されます。
132
+
133
+ ```md
134
+ :::callout[注意]{type=warning}
135
+ 設定ファイルを変更した場合は、サイトを再ビルドしてください。
136
+ :::
137
+ ```
138
+
139
+ :::callout[注意]{type=warning}
140
+ 設定ファイルを変更した場合は、サイトを再ビルドしてください。
141
+ :::
142
+
143
+ ### 折りたたみ(details)
144
+
145
+ クリックすると追加のコンテンツを展開できるセクションを作成できます。
146
+
147
+ ```md
148
+ :::details[詳細を表示]
149
+ このコンテンツは、セクションを展開するまで非表示になります。
150
+
151
+ **Markdown** やリストも使用できます。
152
+
153
+ - 項目 A
154
+ - 項目 B
155
+ :::
156
+ ```
157
+
158
+ :::details[詳細を表示]
159
+ このコンテンツは、セクションを展開するまで非表示になります。
160
+
161
+ **Markdown** やリストも使用できます。
162
+
163
+ - 項目 A
164
+ - 項目 B
165
+ :::
166
+
167
+ ### スポイラー(spoiler)
168
+
169
+ コンテンツを折りたたみセクション内に隠せます。`spoiler` は `details` と同様に動作しますが、異なる CSS クラスが適用されます。
170
+
171
+ ```md
172
+ :::spoiler[ネタバレを表示]
173
+ ユーザーがこのセクションを展開すると、隠されたコンテンツが表示されます。
174
+
175
+ ここでも **Markdown** を使用できます。
176
+ :::
177
+ ```
178
+
179
+ :::spoiler[ネタバレを表示]
180
+ ユーザーがこのセクションを展開すると、隠されたコンテンツが表示されます。
181
+
182
+ ここでも **Markdown** を使用できます。
183
+ :::
184
+
185
+ ### 画像(figure)
186
+
187
+ 意味的に適切な `<figure>` 要素を使用して、キャプションやサイズを指定した画像を表示できます。
188
+
189
+ ```md
190
+ ::figure[サンプル画像]{src="https://placehold.co/800x400/png" alt="サンプルのプレースホルダー画像" caption="Figure 1: 画像の表示例"}
191
+ ```
192
+
193
+ ::figure[サンプル画像]{src="https://placehold.co/800x400/png" alt="サンプルのプレースホルダー画像" caption="Figure 1: 画像の表示例"}
194
+
195
+ ### YouTube
196
+
197
+ プライバシーに配慮した `youtube-nocookie.com` ドメインを使用して、YouTube 動画を埋め込めます。
198
+
199
+ ```md
200
+ ::youtube[id=dQw4w9WgXcQ]
201
+ ```
202
+
203
+ ::youtube[id=dQw4w9WgXcQ]
204
+
205
+ ### Vimeo
206
+
207
+ `dnt=1` プライバシーパラメータを使用して、Vimeo 動画を埋め込めます。
208
+
209
+ ```md
210
+ ::vimeo[id=76979871]
211
+ ```
212
+
213
+ ::vimeo[id=76979871]
214
+
215
+ ### GitHub Gist
216
+
217
+ GitHub Gist のコードスニペットをページ内に直接埋め込めます。JavaScript が無効な環境では、代替リンクが提供されます。
218
+
219
+ ```md
220
+ ::gist{user=octocat id=aa5a315d61ae9438b18d}
221
+ ```
222
+
223
+ ::gist{user=octocat id=aa5a315d61ae9438b18d}
224
+
225
+ ### リンクカード(link-card)
226
+
227
+ 外部リンクをタイトルや説明付きのプレビューカードとして表示できます。
228
+
229
+ ```md
230
+ ::link-card[GitHub]{url="https://github.com" title="GitHub" description="コードのホスティングとソフトウェア開発の共同作業を行うためのプラットフォーム"}
231
+ ```
232
+
233
+ ::link-card[GitHub]{url="https://github.com" title="GitHub" description="コードのホスティングとソフトウェア開発の共同作業を行うためのプラットフォーム"}
234
+
235
+ ### ファイル(file)
236
+
237
+ ファイル名やサイズを指定したダウンロードリンクを作成できます。
238
+
239
+ ```md
240
+ ::file[サンプルPDF]{url="https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf" name="sample.pdf" size="13 KB"}
241
+ ```
242
+
243
+ ::file[サンプルPDF]{url="https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf" name="sample.pdf" size="13 KB"}
244
+
245
+ 上記のコードブロックは元の Markdown 記法を示しています。その直下のショートコードは、Shortcodes プラグインを有効にした Riebeckite で処理された際の、実際のレンダリングを確認するための例です。
67
246
 
68
247
  ## オプション
69
248
 
@@ -73,6 +252,7 @@ export default defineConfig({
73
252
  | `language` | `string` | – | ラッパーの `lang` に付与する BCP-47 タグ |
74
253
  | `builtins` | `boolean` | `true` | 組み込みレンダラーを登録する |
75
254
  | `shortcodes` | `Record<string, ShortcodeRenderer>` | `{}` | 組み込みに重ねる独自レンダラー |
255
+ | `inlineShortcodes` | `readonly string[]` | `[]` | インラインの `:` 形式で使える名前に追加する |
76
256
 
77
257
  ## 独自レンダラー
78
258
 
@@ -83,12 +263,14 @@ shortcodes({
83
263
  shortcodes: {
84
264
  mark: ({ label, attributes }) => `<mark>${label}</mark>`,
85
265
  },
266
+ inlineShortcodes: ["mark"],
86
267
  });
87
268
  ```
88
269
 
89
270
  `ShortcodeRenderer` は `{ name, label, attributes, childrenHtml, context,
90
- container }` を受け取り、文字列を返します。コンテナ型では結果が
91
- `<div class="rb-shortcode rb-shortcode--<name>">` でラップされ、
271
+ container }` を受け取り、文字列を返します。独自レンダラーをインラインの `:`
272
+ 形式で使うには、その名前を `inlineShortcodes` に追加します。コンテナ型では
273
+ 結果が `<div class="rb-shortcode rb-shortcode--<name>">` でラップされ、
92
274
  `childrenHtml` の位置に本文が差し込まれます。コンテナ用レンダラーは本文を
93
275
  置きたい位置で `input.childrenHtml` をそのまま出力してください。レンダラーは
94
276
  同期関数で、ユーザー入力のエスケープは各自で行う必要があります
@@ -102,11 +284,11 @@ container }` を受け取り、文字列を返します。コンテナ型では
102
284
  - `renderShortcode(request, options)` — 単一ショートコードの描画
103
285
  - `resolveShortcodeOptions(options?)` — オプションの正規化
104
286
  - `builtinShortcodes`, `builtinShortcodeNames` — 組み込みレジストリ
287
+ - `builtinInlineShortcodes` — インラインの `:` 形式で使える組み込み名
105
288
  - 型: `ShortcodeRenderer`, `ShortcodeOptions`, `ResolvedShortcodeOptions`,
106
289
  `ShortcodeRenderInput`, `ShortcodeRenderRequest`, `ShortcodeAttributes`,
107
290
  `RemarkShortcodesOptions`
108
291
 
109
292
  ## 関連
110
293
 
111
- - [プラグインガイド](../../../docs/docs/reference/plugin-api.md)
112
-
294
+ - [プラグインガイド](../../../docs/docs/reference/plugin-api.ja.md)
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { builtinShortcodeNames, builtinShortcodes } from "./src/builtins.js";
2
- import { DIAGNOSTIC_INVALID, DIAGNOSTIC_UNKNOWN, remarkShortcodes, SHORTCODES_SOURCE } from "./src/remark.js";
3
- import { createShortcodeRenderContext, DEFAULT_SHORTCODE_CLASS_NAME, renderShortcode, resolveShortcodeOptions, SHORTCODE_CHILDREN_MARKER } from "./src/render.js";
1
+ import { builtinInlineShortcodes, builtinShortcodeNames, builtinShortcodes } from "./src/builtins.js";
2
+ import { DIAGNOSTIC_INLINE_UNSUPPORTED, DIAGNOSTIC_INVALID, DIAGNOSTIC_UNKNOWN, remarkShortcodes, SHORTCODES_SOURCE } from "./src/remark.js";
3
+ import { createShortcodeRenderContext, DEFAULT_SHORTCODE_CLASS_NAME, isInlineShortcode, renderShortcode, resolveShortcodeOptions, SHORTCODE_CHILDREN_MARKER } from "./src/render.js";
4
4
  import type { ShortcodeOptions } from "./src/types.js";
5
5
  export type { DirectiveNode, RemarkShortcodesOptions, ResolvedShortcodeOptions, ShortcodeAttributes, ShortcodeOptions, ShortcodeRenderer, ShortcodeRenderInput, ShortcodeRenderRequest, } from "./src/types.js";
6
- export { builtinShortcodeNames, builtinShortcodes, createShortcodeRenderContext, DEFAULT_SHORTCODE_CLASS_NAME, DIAGNOSTIC_INVALID, DIAGNOSTIC_UNKNOWN, remarkShortcodes, renderShortcode, resolveShortcodeOptions, SHORTCODE_CHILDREN_MARKER, SHORTCODES_SOURCE, };
6
+ export { builtinInlineShortcodes, builtinShortcodeNames, builtinShortcodes, createShortcodeRenderContext, DEFAULT_SHORTCODE_CLASS_NAME, DIAGNOSTIC_INLINE_UNSUPPORTED, DIAGNOSTIC_INVALID, DIAGNOSTIC_UNKNOWN, isInlineShortcode, remarkShortcodes, renderShortcode, resolveShortcodeOptions, SHORTCODE_CHILDREN_MARKER, SHORTCODES_SOURCE, };
7
7
  export declare function shortcodes(options?: ShortcodeOptions): import("@riebeckite/core").RiebeckitePlugin<ShortcodeOptions>;
8
8
  export declare const shortcodesPlugin: typeof shortcodes;
package/dist/index.js CHANGED
@@ -206,6 +206,12 @@ var builtinShortcodes = {
206
206
  "link-card": renderLinkCard,
207
207
  file: renderFile
208
208
  };
209
+ var builtinInlineShortcodes = [
210
+ "badge",
211
+ "kbd",
212
+ "link-card",
213
+ "file"
214
+ ];
209
215
  var builtinShortcodeNames = Object.keys(builtinShortcodes);
210
216
 
211
217
  // src/remark.ts
@@ -236,14 +242,24 @@ function resolveShortcodeOptions(options = {}) {
236
242
  const builtins = options.builtins !== false;
237
243
  const custom = options.shortcodes ?? {};
238
244
  const shortcodes2 = builtins ? { ...builtinShortcodes, ...custom } : { ...custom };
245
+ const inlineShortcodes = new Set(
246
+ builtins ? builtinInlineShortcodes : []
247
+ );
248
+ for (const name of options.inlineShortcodes ?? []) {
249
+ inlineShortcodes.add(name);
250
+ }
239
251
  const resolved = {
240
252
  className: options.className ?? DEFAULT_SHORTCODE_CLASS_NAME,
241
253
  builtins,
242
- shortcodes: shortcodes2
254
+ shortcodes: shortcodes2,
255
+ inlineShortcodes
243
256
  };
244
257
  if (options.language !== void 0) resolved.language = options.language;
245
258
  return resolved;
246
259
  }
260
+ function isInlineShortcode(name, options) {
261
+ return options.inlineShortcodes.has(name);
262
+ }
247
263
  function createShortcodeRenderContext(input) {
248
264
  return {
249
265
  contentIndex: input.contentIndex ?? /* @__PURE__ */ new Map(),
@@ -272,6 +288,7 @@ function renderShortcode(request, options) {
272
288
  url: firstUrl(attributes)
273
289
  });
274
290
  const container = request.container ?? false;
291
+ const block = request.block ?? container;
275
292
  const input = {
276
293
  name: request.name,
277
294
  label: request.label ?? "",
@@ -283,10 +300,10 @@ function renderShortcode(request, options) {
283
300
  input.childrenHtml = request.childrenHtml;
284
301
  }
285
302
  const body = renderer(input);
286
- return wrapShortcode(body, request.name, container, options);
303
+ return wrapShortcode(body, request.name, block, options);
287
304
  }
288
- function wrapShortcode(body, name, container, options) {
289
- const tag = container ? "div" : "span";
305
+ function wrapShortcode(body, name, block, options) {
306
+ const tag = block ? "div" : "span";
290
307
  const classes = `${options.className} ${options.className}--${name}`;
291
308
  const languageAttribute = options.language === void 0 ? "" : ` lang="${escapeHtmlAttribute2(options.language)}"`;
292
309
  return `<${tag} class="${escapeHtmlAttribute2(
@@ -305,6 +322,7 @@ function firstUrl(attributes) {
305
322
  var SHORTCODES_SOURCE = "@riebeckite/plugin-shortcodes";
306
323
  var DIAGNOSTIC_UNKNOWN = "shortcodes-unknown";
307
324
  var DIAGNOSTIC_INVALID = "shortcodes-invalid";
325
+ var DIAGNOSTIC_INLINE_UNSUPPORTED = "shortcodes-inline-unsupported";
308
326
  var ATTRIBUTE_NAME_PATTERN = /^[A-Za-z_][\w-]*$/;
309
327
  function remarkShortcodes(options = {}) {
310
328
  const resolved = resolveShortcodeOptions(options);
@@ -312,7 +330,7 @@ function remarkShortcodes(options = {}) {
312
330
  const targets = [];
313
331
  visit(
314
332
  tree,
315
- ["containerDirective", "leafDirective"],
333
+ ["containerDirective", "leafDirective", "textDirective"],
316
334
  (node, index, parent) => {
317
335
  if (parent === void 0 || index === void 0) return;
318
336
  targets.push({ node, index, parent });
@@ -326,10 +344,37 @@ function remarkShortcodes(options = {}) {
326
344
  function transformDirective(target, resolved, options, file) {
327
345
  const { node, index, parent } = target;
328
346
  const name = node.name;
347
+ const inline = node.type === "textDirective";
329
348
  const container = node.type === "containerDirective";
330
349
  const { label, children } = extractLabel(node);
331
350
  const attributes = normalizeAttributes(node, file);
332
351
  const childrenHtml = container ? SHORTCODE_CHILDREN_MARKER : void 0;
352
+ if (inline && !isInlineShortcode(name, resolved)) {
353
+ const known = Object.hasOwn(resolved.shortcodes, name);
354
+ report(
355
+ file,
356
+ known ? DIAGNOSTIC_INLINE_UNSUPPORTED : DIAGNOSTIC_UNKNOWN,
357
+ known ? `Shortcode ":${name}" is block-only and cannot be used inline.` : `Unknown shortcode ":${name}" was left as escaped text.`,
358
+ node
359
+ );
360
+ parent.children.splice(
361
+ index,
362
+ 1,
363
+ ...createFallback(
364
+ resolved.className,
365
+ name,
366
+ label,
367
+ attributes,
368
+ {
369
+ container: false,
370
+ block: false,
371
+ modifier: known ? "inline-unsupported" : "unknown"
372
+ },
373
+ children
374
+ )
375
+ );
376
+ return;
377
+ }
333
378
  const html = renderShortcode(
334
379
  {
335
380
  name,
@@ -337,6 +382,7 @@ function transformDirective(target, resolved, options, file) {
337
382
  attributes,
338
383
  childrenHtml,
339
384
  container,
385
+ block: !inline,
340
386
  context: createShortcodeRenderContext({
341
387
  name,
342
388
  label,
@@ -363,7 +409,7 @@ function transformDirective(target, resolved, options, file) {
363
409
  name,
364
410
  label,
365
411
  attributes,
366
- container,
412
+ { container, block: !inline, modifier: "unknown" },
367
413
  children
368
414
  )
369
415
  );
@@ -431,11 +477,14 @@ function normalizeAttributes(node, file) {
431
477
  }
432
478
  return attributes;
433
479
  }
434
- function createFallback(className, name, label, attributes, container, children) {
435
- const classes = `${className} ${className}--unknown`;
480
+ function createFallback(className, name, label, attributes, shape, children) {
481
+ const classes = `${className} ${className}--${shape.modifier}`;
436
482
  const dataName = ` data-shortcode-name="${escapeHtmlAttribute3(name)}"`;
437
- const directive = escapeHtml2(sourceText(name, label, attributes, container));
438
- if (container) {
483
+ const inline = !shape.container && !shape.block;
484
+ const directive = escapeHtml2(
485
+ sourceText(name, label, attributes, shape.container, inline)
486
+ );
487
+ if (shape.container) {
439
488
  return [
440
489
  createHtml(
441
490
  `<div class="${escapeHtmlAttribute3(classes)}"${dataName}>${directive}`
@@ -444,14 +493,15 @@ function createFallback(className, name, label, attributes, container, children)
444
493
  createHtml("</div>")
445
494
  ];
446
495
  }
496
+ const tag = shape.block ? "div" : "span";
447
497
  return [
448
498
  createHtml(
449
- `<span class="${escapeHtmlAttribute3(classes)}"${dataName}>${directive}</span>`
499
+ `<${tag} class="${escapeHtmlAttribute3(classes)}"${dataName}>${directive}</${tag}>`
450
500
  )
451
501
  ];
452
502
  }
453
- function sourceText(name, label, attributes, container) {
454
- const fence = container ? ":::" : "::";
503
+ function sourceText(name, label, attributes, container, inline) {
504
+ const fence = container ? ":::" : inline ? ":" : "::";
455
505
  const labelPart = label ? `[${label}]` : "";
456
506
  return `${fence}${name}${labelPart}${attributesText(attributes)}`;
457
507
  }
@@ -503,13 +553,16 @@ function shortcodes(options = {}) {
503
553
  var shortcodesPlugin = shortcodes;
504
554
  export {
505
555
  DEFAULT_SHORTCODE_CLASS_NAME,
556
+ DIAGNOSTIC_INLINE_UNSUPPORTED,
506
557
  DIAGNOSTIC_INVALID,
507
558
  DIAGNOSTIC_UNKNOWN,
508
559
  SHORTCODES_SOURCE,
509
560
  SHORTCODE_CHILDREN_MARKER,
561
+ builtinInlineShortcodes,
510
562
  builtinShortcodeNames,
511
563
  builtinShortcodes,
512
564
  createShortcodeRenderContext,
565
+ isInlineShortcode,
513
566
  remarkShortcodes,
514
567
  renderShortcode,
515
568
  resolveShortcodeOptions,
@@ -1,4 +1,5 @@
1
1
  import type { ShortcodeRenderInput } from "./types.js";
2
2
  /** Built-in renderers keyed by directive name. */
3
3
  export declare const builtinShortcodes: Record<string, (input: ShortcodeRenderInput) => string>;
4
+ export declare const builtinInlineShortcodes: readonly string[];
4
5
  export declare const builtinShortcodeNames: readonly string[];
@@ -3,4 +3,5 @@ import type { RemarkShortcodesOptions } from "./types.js";
3
3
  export declare const SHORTCODES_SOURCE = "@riebeckite/plugin-shortcodes";
4
4
  export declare const DIAGNOSTIC_UNKNOWN = "shortcodes-unknown";
5
5
  export declare const DIAGNOSTIC_INVALID = "shortcodes-invalid";
6
+ export declare const DIAGNOSTIC_INLINE_UNSUPPORTED = "shortcodes-inline-unsupported";
6
7
  export declare function remarkShortcodes(options?: RemarkShortcodesOptions): (tree: Root, file: unknown) => void;
@@ -8,6 +8,7 @@ export declare const DEFAULT_SHORTCODE_CLASS_NAME = "rb-shortcode";
8
8
  */
9
9
  export declare const SHORTCODE_CHILDREN_MARKER = "\0rb-shortcodes:children\0";
10
10
  export declare function resolveShortcodeOptions(options?: ShortcodeOptions): ResolvedShortcodeOptions;
11
+ export declare function isInlineShortcode(name: string, options: ResolvedShortcodeOptions): boolean;
11
12
  export declare function createShortcodeRenderContext(input: {
12
13
  name: string;
13
14
  label?: string;
@@ -31,12 +31,14 @@ export type ShortcodeOptions = {
31
31
  builtins?: boolean;
32
32
  /** Custom renderers, merged on top of the built-ins by name. */
33
33
  shortcodes?: Record<string, ShortcodeRenderer>;
34
+ inlineShortcodes?: readonly string[];
34
35
  };
35
36
  export type ResolvedShortcodeOptions = {
36
37
  className: string;
37
38
  language?: string;
38
39
  builtins: boolean;
39
40
  shortcodes: Record<string, ShortcodeRenderer>;
41
+ inlineShortcodes: ReadonlySet<string>;
40
42
  };
41
43
  /** Input accepted by {@link renderShortcode}. */
42
44
  export type ShortcodeRenderRequest = {
@@ -45,6 +47,7 @@ export type ShortcodeRenderRequest = {
45
47
  attributes?: ShortcodeAttributes;
46
48
  childrenHtml?: string;
47
49
  container?: boolean;
50
+ block?: boolean;
48
51
  context?: PluginRenderContext;
49
52
  };
50
53
  /**
@@ -56,7 +59,7 @@ export type RemarkShortcodesOptions = ShortcodeOptions & {
56
59
  contentIndex?: Map<string, string>;
57
60
  contentSource?: ContentSource;
58
61
  };
59
- export type DirectiveName = "leafDirective" | "containerDirective";
62
+ export type DirectiveName = "textDirective" | "leafDirective" | "containerDirective";
60
63
  export type DirectiveNode = Parent & {
61
64
  type: DirectiveName;
62
65
  name: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riebeckite/plugin-shortcodes",
3
- "version": "0.0.19",
3
+ "version": "0.0.21",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -17,11 +17,11 @@
17
17
  "dependencies": {
18
18
  "@types/mdast": "^4.0.4",
19
19
  "unist-util-visit": "^5.1.0",
20
- "@riebeckite/core": "0.0.19"
20
+ "@riebeckite/core": "0.0.21"
21
21
  },
22
22
  "devDependencies": {
23
23
  "tsx": "^4.22.4",
24
- "@riebeckite/test": "0.0.19"
24
+ "@riebeckite/test": "0.0.21"
25
25
  },
26
26
  "publishConfig": {
27
27
  "access": "public"
package/style.css CHANGED
@@ -245,7 +245,8 @@ div.rb-shortcode {
245
245
 
246
246
  /* fallback ---------------------------------------------------------------- */
247
247
 
248
- .rb-shortcode--unknown {
248
+ .rb-shortcode--unknown,
249
+ .rb-shortcode--inline-unsupported {
249
250
  color: var(--rb-shortcode-accent);
250
251
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
251
252
  font-size: 0.875em;