@peaceroad/markdown-it-renderer-fence 0.4.0 → 0.5.0

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
@@ -1,172 +1,500 @@
1
1
  # p7d-markdown-it-renderer-fence
2
2
 
3
- In code blocks, the `<samp>` tag is used with the language keywords `samp`, `shell`, and `console`.
3
+ A `markdown-it` plugin for code block rendering and enhancements.
4
4
 
5
- To add specific styles to code blocks and display line numbers in code blocks, enclose each line in a span element and set CSS.
5
+ Default is `markup` mode. Custom Highlight API mode is available as an advanced/experimental path.
6
6
 
7
- ## Use
7
+ ## Install
8
8
 
9
- It is assumed that you will use highlight.js and markdown-it-attrs together. It will probably work without them though.
9
+ ```bash
10
+ npm i @peaceroad/markdown-it-renderer-fence markdown-it markdown-it-attrs
11
+ ```
10
12
 
11
- ```js
12
- import mdit from 'markdown-it'
13
- import mditAttrs from 'markdown-it-attrs'
14
- import highlightjs from 'highlihgtjs'
15
- import mditRendererFence from '@peaceroad/markdown-it-renderer-fence'
13
+ If you use syntax highlighting, install a highlighter too (for example `highlight.js` or `shiki`).
16
14
 
17
- const md = mdit({
18
- langPrefix: 'language-',
19
- highlight: (str, lang) => {
20
- if (lang && highlightjs.getLanguage(lang)) {
21
- try {
22
- return highlightjs.highlight(str, { language: lang }).value
23
- } catch (__) {}
24
- }
25
- return md.utils.escapeHtml(str)
26
- }
27
- }).use(mditAttrs).use(mditRendererFence)
15
+ ## Entry Points
28
16
 
29
- const htmlCont = md.render('...')
30
- ```
17
+ - `@peaceroad/markdown-it-renderer-fence`
18
+ Dispatcher entry (`highlightRenderer` selects mode).
19
+ - `@peaceroad/markdown-it-renderer-fence/markup-highlight`
20
+ Markup-focused entry.
21
+ - `@peaceroad/markdown-it-renderer-fence/custom-highlight`
22
+ API-mode entry + runtime/payload helpers.
23
+ - `@peaceroad/markdown-it-renderer-fence/custom-highlight-runtime`
24
+ Runtime-only entry (`applyCustomHighlights` / `observeCustomHighlights` / `clearCustomHighlights`).
31
25
 
32
- It is also intended to be used in conjunction with @peaceroad/markdown-it-figure-with-p-caption.
26
+ ## Markup Mode (Default)
33
27
 
34
- ## Code block to samp block
28
+ ### Quick Start
35
29
 
36
- ~~~
37
- [Markdown]
38
- ```samp
39
- $ pwd
40
- /home/User
30
+ Dispatcher entry:
31
+
32
+ ```js
33
+ import MarkdownIt from 'markdown-it'
34
+ import markdownItAttrs from 'markdown-it-attrs'
35
+ import hljs from 'highlight.js'
36
+ import rendererFence from '@peaceroad/markdown-it-renderer-fence'
37
+ // Markup-only entry (skips dispatcher/API branch):
38
+ // import rendererFence from '@peaceroad/markdown-it-renderer-fence/markup-highlight'
39
+
40
+ const md = MarkdownIt({
41
+ html: true,
42
+ langPrefix: 'language-',
43
+ highlight: (code, lang) => {
44
+ if (lang && hljs.getLanguage(lang)) {
45
+ try {
46
+ return hljs.highlight(code, { language: lang }).value
47
+ } catch (e) {}
48
+ }
49
+ return md.utils.escapeHtml(code)
50
+ },
51
+ })
52
+ .use(markdownItAttrs)
53
+ .use(rendererFence) // defaults to markup; direct markup entry can be leaner
41
54
  ```
42
- [HTML]
43
- <pre><samp>$ pwd
44
- /home/User
45
- </samp></pre>
46
55
 
56
+ ### Markup with Shiki
47
57
 
48
- [Markdown]
49
- ```shell
50
- $ pwd
51
- /home/User
58
+ Use the markup-only entry and provide `md.options.highlight` with Shiki.
59
+ For production/blog builds, pre-scan markdown and preload only used languages.
60
+
61
+ ```js
62
+ import MarkdownIt from 'markdown-it'
63
+ import markdownItAttrs from 'markdown-it-attrs'
64
+ import { createHighlighter } from 'shiki'
65
+ import rendererFence from '@peaceroad/markdown-it-renderer-fence/markup-highlight'
66
+ import fs from 'node:fs'
67
+ import path from 'node:path'
68
+
69
+ const fenceInfoReg = /^```([^\s`{]+)/gm
70
+ const collectFenceLangs = (markdown) => {
71
+ const out = new Set()
72
+ if (!markdown) return out
73
+ let m
74
+ while ((m = fenceInfoReg.exec(markdown)) !== null) {
75
+ const lang = String(m[1] || '').trim().toLowerCase()
76
+ if (lang) out.add(lang)
77
+ }
78
+ return out
79
+ }
80
+
81
+ // Example: scan markdown source(s) before highlighter creation
82
+ const markdown = fs.readFileSync(path.join(process.cwd(), 'article.md'), 'utf8')
83
+ const langs = Array.from(collectFenceLangs(markdown))
84
+ langs.push('text') // safe fallback
85
+
86
+ const highlighter = await createHighlighter({
87
+ themes: ['github-light'],
88
+ langs, // preload grammars; Shiki does not auto-detect language
89
+ })
90
+
91
+ const md = MarkdownIt({
92
+ html: true,
93
+ langPrefix: 'language-',
94
+ highlight: (code, lang) => {
95
+ const targetLang = lang || 'text'
96
+ try {
97
+ return highlighter.codeToHtml(code, { lang: targetLang, theme: 'github-light' })
98
+ } catch (e) {
99
+ // when lang grammar is not loaded (or invalid), keep output safe
100
+ return md.utils.escapeHtml(code)
101
+ }
102
+ },
103
+ })
104
+ .use(markdownItAttrs)
105
+ .use(rendererFence)
52
106
  ```
53
- [HTML]
54
- <pre><samp class="language-shell"><span class="hljs-meta prompt_">$ </span><span class="language-bash"><span class="hljs-built_in">pwd</span></span>
55
- /home/User
56
- </samp></pre>
57
107
 
108
+ Note:
58
109
 
59
- [Markdown]
60
- ```console
110
+ - For small projects, a fixed list like `['javascript', 'typescript', 'json', 'text']` is also fine.
111
+ - If Shiki grammar for a language is not loaded, `codeToHtml` can fail.
112
+ - In markup mode, handle this in your `highlight` callback (for example fallback to escaped plain text as above).
113
+
114
+ ### Main Features
115
+
116
+ - `samp` rendering for `samp`, `shell`, `console` languages.
117
+ - line number wrapping via `start` / `data-pre-start`.
118
+ - emphasized lines via `em-lines` / `emphasize-lines`.
119
+ - optional line-end spacer via `lineEndSpanThreshold`.
120
+ - optional pre-wrap support via `wrap` / `pre-wrap`.
121
+ - comment line markers via `comment-mark`.
122
+
123
+ ### Fence Attribute Examples
124
+
125
+ Simplified HTML below focuses on renderer-fence structure (not full highlighter token spans).
126
+
127
+ `samp` conversion:
128
+
129
+ ~~~md
130
+ ```shell
61
131
  $ pwd
62
- /home/User
63
132
  ```
64
- [HTML]
65
- <pre><samp class="language-console"><span class="hljs-meta prompt_">$ </span><span class="language-bash"><span class="hljs-built_in">pwd</span></span>
66
- /home/User
67
- </samp></pre>
68
133
  ~~~
69
134
 
135
+ ```html
136
+ <pre><samp class="language-shell">$ pwd
137
+ </samp></pre>
138
+ ```
70
139
 
71
- ## Add span elements to display line number.
72
-
73
- Add `start` or `data-pre-start` attribute by adding attributes used markdown-it-attrs.
140
+ Line numbers:
74
141
 
75
142
  ~~~md
76
143
  ```js {start="1"}
77
- import mdit from 'markdonw-it'
78
- const md = mdit()
79
- md.render('Nyaan')
144
+ const a = 1
145
+ console.log(a)
80
146
  ```
81
147
  ~~~
82
148
 
83
- ~~~html
84
- <pre><code class="language-js" data-pre-start="1" style="counter-set:pre-line-number 1;"><span class="pre-line"><span class="hljs-keyword">import</span> mdit <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;markdonw-it&#x27;</span></span>
85
- <span class="pre-line"><span class="hljs-keyword">const</span> md = <span class="hljs-title function_">mdit</span>()</span>
86
- <span class="pre-line">md.<span class="hljs-title function_">render</span>(<span class="hljs-string">&#x27;Nyaan&#x27;</span>)</span>
149
+ ```html
150
+ <pre><code class="language-js" data-pre-start="1" style="counter-set:pre-line-number 1;">
151
+ <span class="pre-line">const a = 1</span>
152
+ <span class="pre-line">console.log(a)</span>
87
153
  </code></pre>
88
- ~~~
89
-
90
- CSS example: <https://codepen.io/peaceroad/pen/qBGpYGK>
91
-
92
- ## Add span elements to add background color to the row ranges.
154
+ ```
93
155
 
94
- Add `em-lines` or `emphasize-lines` attribute by by adding attributes used markdown-it-attrs.
95
- Open-ended ranges are supported: `-5` (1..5), `3-` (3..last), `-` (all).
156
+ Emphasis:
96
157
 
97
158
  ~~~md
98
- ``` {em-lines="2,4-5"}
99
- 1
100
- 2
101
- 3
102
- 4
103
- 5
104
- 6
159
+ ```js {em-lines="2,4-5"}
160
+ line1
161
+ line2
162
+ line3
163
+ line4
164
+ line5
105
165
  ```
106
166
  ~~~
107
167
 
108
- ~~~html
109
- [HTML]
110
- <pre><code data-pre-emphasis="2,4-5">1
111
- <span class="pre-lines-emphasis">2
112
- </span>3
113
- <span class="pre-lines-emphasis">4
114
- 5
115
- </span>6
168
+ ```html
169
+ <pre><code class="language-js" data-pre-emphasis="2,4-5">line1
170
+ <span class="pre-lines-emphasis">line2</span>
171
+ line3
172
+ <span class="pre-lines-emphasis">line4
173
+ line5</span>
116
174
  </code></pre>
117
- ~~~
118
-
119
- ## Enable pre-wrap for code blocks
175
+ ```
120
176
 
121
- Add `wrap` or `pre-wrap` attribute (optionally `="true"`) by adding attributes used markdown-it-attrs.
177
+ Wrap:
122
178
 
123
179
  ~~~md
124
180
  ```js {wrap}
125
- const longLine = 'This line is long but wraps.'
181
+ const veryLongLine = '...'
126
182
  ```
127
183
  ~~~
128
184
 
129
- ~~~html
130
- <pre data-pre-wrap="true" style="white-space: pre-wrap; overflow-wrap: anywhere;"><code class="language-js">const longLine = 'This line is long but wraps.'
185
+ ```html
186
+ <pre data-pre-wrap="true" style="white-space: pre-wrap; overflow-wrap: anywhere;">
187
+ <code class="language-js">const veryLongLine = '...'
131
188
  </code></pre>
132
- ~~~
133
-
134
- When `setPreWrapStyle` is set to false, only `data-pre-wrap="true"` is added and the inline style is omitted.
135
-
136
- ## Mark comment lines in code blocks
189
+ ```
137
190
 
138
- Add `comment-line` attribute to mark comment lines. Lines that start with the given marker (after leading whitespace) are wrapped with `<span class="pre-comment-line">`.
191
+ Comment line marker:
139
192
 
140
193
  ~~~md
141
- ```samp {comment-line="#"}
194
+ ```samp {comment-mark="#"}
142
195
  # comment
143
196
  echo 1
144
197
  ```
145
198
  ~~~
146
199
 
147
- ~~~html
148
- <pre><samp data-pre-comment-line="#"><span class="pre-comment-line"># comment</span>
200
+ ```html
201
+ <pre><samp data-pre-comment-mark="#" class="language-samp">
202
+ <span class="pre-line-comment"># comment</span>
149
203
  echo 1
150
204
  </samp></pre>
151
- ~~~
205
+ ```
206
+
207
+ ### Markup Notes
208
+
209
+ - `useHighlightPre: true` keeps highlighter-provided `<pre><code>` when present.
210
+ - In that passthrough path, line-splitting features are intentionally disabled:
211
+ - line numbers
212
+ - `em-lines`
213
+ - line-end spacer
214
+ - `comment-mark`
215
+ - `samp` conversion
216
+
217
+ ### Markup Options
218
+
219
+ - `attrsOrder` (default: `['class', 'id', 'data-*', 'style']`): output attribute order (`data-*` wildcard supported).
220
+ - `setHighlight` (default: `true`): call `md.options.highlight` when available.
221
+ - `setLineNumber` (default: `true`): enable line wrapper spans when `start` is valid.
222
+ - `setEmphasizeLines` (default: `true`): enable `em-lines` / `emphasize-lines`.
223
+ - `lineEndSpanThreshold` (default: `0`): append line-end spacer span when visual width threshold is met.
224
+ - `setLineEndSpan`: alias of `lineEndSpanThreshold`.
225
+ - `lineEndSpanClass` (default: `'pre-lineend-spacer'`): CSS class of the spacer span.
226
+ - `setPreWrapStyle` (default: `true`): inject inline pre-wrap style for wrap-enabled blocks.
227
+ - `sampLang` (default: `'shell,console'`): additional fence langs rendered as `<samp>`.
228
+ - `langPrefix` (default: `md.options.langPrefix || 'language-'`): class prefix for language class.
229
+ - `useHighlightPre` (default: `false`): passthrough highlighter `<pre><code>` wrappers and skip line-splitting features.
230
+ - `onFenceDecision` (default: `null`): debug hook for per-fence branch decisions.
231
+ - `onFenceDecisionTiming` (default: `false`): include timing fields in `onFenceDecision`.
232
+
233
+ `em-lines` syntax note:
234
+
235
+ - supports single values (`2`) and ranges (`4-6`)
236
+ - supports open-ended forms (`3-`, `-2`)
237
+ - reversed ranges are normalized (`5-3` behaves as `3-5`)
238
+
239
+ Migration note (`0.5.0`):
240
+
241
+ - `comment-line` / `data-pre-comment-line` / `pre-comment-line` were removed
242
+ - use `comment-mark` / `data-pre-comment-mark` / `pre-line-comment`
243
+
244
+ ## Custom Highlight API Mode (Experimental / Advanced)
245
+
246
+ Custom Highlight API mode renders plain code text and emits range payloads for browser-side Custom Highlight API application.
247
+
248
+ Important:
249
+
250
+ - You must run runtime apply in the browser (`applyCustomHighlights` or `observeCustomHighlights`).
251
+ - Without runtime apply, payload exists but browser highlights are not activated.
252
+ - `test/custom-highlight/pre-highlight.js` is a demo helper, not the package runtime API contract.
253
+
254
+ Use API mode only if you need runtime range highlighting and can manage runtime/CSS integration in your app.
255
+
256
+ ### Minimal API Example (Shiki provider)
152
257
 
153
- Note: this feature relies on line splitting and is disabled when `useHighlightPre` is true and `<pre><code>` is provided by the highlighter.
258
+ ```js
259
+ import MarkdownIt from 'markdown-it'
260
+ import markdownItAttrs from 'markdown-it-attrs'
261
+ import { createHighlighter } from 'shiki'
262
+ import rendererFenceApi, {
263
+ applyCustomHighlights,
264
+ renderCustomHighlightPayloadScript,
265
+ } from '@peaceroad/markdown-it-renderer-fence/custom-highlight'
266
+ // Dispatcher entry alternative:
267
+ // import rendererFenceApi from '@peaceroad/markdown-it-renderer-fence'
268
+
269
+ const highlighter = await createHighlighter({
270
+ themes: ['github-light'],
271
+ langs: ['javascript', 'typescript', 'json'], // preload grammars; Shiki does not auto-detect language
272
+ })
273
+
274
+ const md = MarkdownIt({ html: true })
275
+ .use(markdownItAttrs)
276
+ .use(rendererFenceApi, {
277
+ // highlightRenderer: 'api', // required only with dispatcher entry
278
+ customHighlight: {
279
+ provider: 'shiki',
280
+ highlighter,
281
+ theme: 'github-light',
282
+ transport: 'env',
283
+ },
284
+ })
285
+
286
+ const env = {}
287
+ const html = md.render('```js\nconst x = 1\n```', env)
288
+ const payloadScript = renderCustomHighlightPayloadScript(env) // id="pre-highlight-data"
289
+ ```
290
+
291
+ Browser-side apply:
292
+
293
+ ```js
294
+ applyCustomHighlights(document)
295
+ ```
296
+
297
+ When using `colorScheme: 'auto'`, scheme resolution happens at apply time.
298
+ If OS/browser theme changes after first apply, re-run apply (or use observer watch mode below).
299
+
300
+ If you only need runtime apply on the browser, import runtime-only entry:
301
+
302
+ ```js
303
+ import { observeCustomHighlights } from '@peaceroad/markdown-it-renderer-fence/custom-highlight-runtime'
304
+ ```
305
+
306
+ `langs` note:
307
+
308
+ - `createHighlighter({ langs: [...] })` should include the languages you expect to render.
309
+ - Shiki does not auto-detect language from code text.
310
+ - if a target language is not loaded, this plugin falls back to Shiki `text` tokenization for that block (safe, but no syntax color buckets).
311
+
312
+ ### Minimal API Example (highlight.js provider)
313
+
314
+ ```js
315
+ import MarkdownIt from 'markdown-it'
316
+ import markdownItAttrs from 'markdown-it-attrs'
317
+ import hljs from 'highlight.js'
318
+ import rendererFenceApi from '@peaceroad/markdown-it-renderer-fence/custom-highlight'
319
+ // Dispatcher entry alternative:
320
+ // import rendererFenceApi from '@peaceroad/markdown-it-renderer-fence'
321
+
322
+ const md = MarkdownIt({ html: true })
323
+ .use(markdownItAttrs)
324
+ .use(rendererFenceApi, {
325
+ // highlightRenderer: 'api', // required only with dispatcher entry
326
+ customHighlight: {
327
+ provider: 'hljs',
328
+ hljsHighlight: (code, lang) => {
329
+ if (lang && hljs.getLanguage(lang)) {
330
+ return hljs.highlight(code, { language: lang }).value
331
+ }
332
+ return hljs.highlight(code, { language: 'plaintext' }).value
333
+ },
334
+ transport: 'env',
335
+ },
336
+ })
337
+ ```
338
+
339
+ ### API Providers
340
+
341
+ - `customHighlight.provider: 'shiki'`
342
+ Requires `customHighlight.highlighter.codeToTokens(...)` (synchronous).
343
+ - `customHighlight.provider: 'hljs'`
344
+ Uses `customHighlight.hljsHighlight`, fallback to `customHighlight.highlight`, then `md.options.highlight`.
345
+ - `customHighlight.provider: 'custom'`
346
+ Escape hatch. Requires synchronous `customHighlight.getRanges(...)`.
347
+
348
+ ### Shiki Scope Modes
349
+
350
+ - `auto` (default)
351
+ - `color`
352
+ - `semantic`
353
+ - `keyword` (best for stable CSS-managed styling)
354
+
355
+ Migration note (`0.5.0`):
356
+
357
+ - legacy aliases like `json`, `bucket`, `keyword-only` are removed
358
+ - use canonical values: `auto | color | semantic | keyword`
359
+
360
+ Recommended production profile for API mode:
361
+
362
+ ```js
363
+ customHighlight: {
364
+ provider: 'shiki',
365
+ shikiScopeMode: 'keyword',
366
+ includeScopeStyles: false
367
+ }
368
+ ```
369
+
370
+ ### API Multi-Theme (Shiki, `v:1` additive payload)
371
+
372
+ When you need runtime light/dark switching with Shiki color styles, pass object form theme:
373
+
374
+ ```js
375
+ customHighlight: {
376
+ provider: 'shiki',
377
+ highlighter,
378
+ shikiScopeMode: 'color',
379
+ includeScopeStyles: true,
380
+ theme: {
381
+ light: 'github-light',
382
+ dark: 'github-dark',
383
+ default: 'light',
384
+ },
385
+ }
386
+ ```
387
+
388
+ Runtime apply can choose variant:
389
+
390
+ ```js
391
+ applyCustomHighlights(document, { colorScheme: 'auto' }) // 'auto' | 'light' | 'dark'
392
+ ```
393
+
394
+ Auto re-apply on color-scheme change:
395
+
396
+ ```js
397
+ observeCustomHighlights(document, {
398
+ applyOptions: { colorScheme: 'auto', incremental: true },
399
+ watchColorScheme: true,
400
+ })
401
+ ```
402
+
403
+ ### CMS / Copy-Paste Operation
404
+
405
+ If you copy rendered HTML+payload JSON into another CMS/page:
406
+
407
+ - API mode works only when runtime JS is also available on that page.
408
+ - payload JSON alone is not enough.
409
+ - if the target CMS cannot run custom JS, use `markup` mode instead.
410
+ - this plugin does not auto-generate/write runtime JS files during markdown render.
411
+
412
+ Practical patterns:
413
+
414
+ 1. Site/app with bundler
415
+ Bundle `@peaceroad/markdown-it-renderer-fence/custom-highlight-runtime` and run `observeCustomHighlights(...)`.
416
+ 2. Static page with module scripts
417
+ Load runtime entry via your ESM delivery path and call `applyCustomHighlights(...)`.
418
+ 3. HTML-only CMS (no JS injection)
419
+ Use `markup` mode (API mode is not suitable).
420
+
421
+ ### CLI Build Artifact Contract
154
422
 
423
+ For CLI/static generation, treat API mode output as two artifacts:
155
424
 
156
- ## Options
425
+ 1. HTML artifact
426
+ Includes `<pre data-pre-highlight="...">...</pre>` and payload JSON (`env` script or inline-script transport).
427
+ 2. Runtime artifact
428
+ Browser JS that imports `@peaceroad/markdown-it-renderer-fence/custom-highlight-runtime` and runs `applyCustomHighlights(...)` or `observeCustomHighlights(...)`.
157
429
 
158
- The following options can be specified when initializing the plugin:
430
+ Typical runtime bridge file:
159
431
 
160
- - attrsOrder: default ['class','id','data-*','style'] — order of attributes in output; wildcards supported.
161
- - setHighlight: default true enable calling highlight function (e.g., highlight.js).
162
- - setLineNumber: default true — wrap lines in spans for line numbers.
163
- - setEmphasizeLines: default true — enable emphasis based on emphasize-lines attribute.
164
- - lineEndSpanThreshold: default 0 character count threshold to append end-of-line span (0 to disable).
165
- - setLineEndSpan: alias for lineEndSpanThreshold.
166
- - lineEndSpanClass: default 'pre-lineend-spacer' — CSS class for end-of-line span.
167
- - setPreWrapStyle: default true — include inline pre-wrap styles on `<pre>` when wrap is enabled.
168
- - useHighlightPre: default false — if highlight returns `<pre><code>`, keep that wrapper and skip line-splitting features; attributes are still merged.
432
+ ```js
433
+ import { observeCustomHighlights } from '@peaceroad/markdown-it-renderer-fence/custom-highlight-runtime'
434
+
435
+ observeCustomHighlights(document, {
436
+ applyOptions: { colorScheme: 'auto', incremental: true },
437
+ watchColorScheme: true,
438
+ })
439
+ ```
440
+
441
+ Then include that bridge in generated HTML:
442
+
443
+ ```html
444
+ <script type="module" src="/assets/custom-highlight-runtime.js"></script>
445
+ ```
169
446
 
170
- When `useHighlightPre` is true and the highlight output contains `<pre><code>`, line-splitting features are disabled (setLineNumber, setEmphasizeLines, lineEndSpanThreshold, comment-line).
171
- - sampLang: default 'shell,console' — comma-separated list of languages (in addition to 'samp') that will be rendered using `<samp>`.
172
- - langPrefix: default md.options.langPrefix || 'language-' prefix for language class on code blocks.
447
+ ### API Options
448
+
449
+ - `highlightRenderer` (`'markup' | 'api' | 'custom-highlight-api'`, default: `'markup'`): dispatcher mode selector (`/custom-highlight` entry does not need this).
450
+ - `customHighlight.provider` (default: `'shiki'`): range source (`'shiki' | 'hljs' | 'custom'`).
451
+ - `customHighlight.getRanges`: required when `provider: 'custom'`; must return synchronous ranges.
452
+ - `customHighlight.highlighter`: Shiki highlighter object with synchronous `codeToTokens`.
453
+ - `customHighlight.hljsHighlight`: highlight.js-style function used when `provider: 'hljs'`.
454
+ - `customHighlight.highlight`: fallback highlight function for `hljs` provider.
455
+ - `customHighlight.defaultLang`: default language when fence language is empty.
456
+ - `customHighlight.theme`: Shiki theme name string, or object `{ light, dark, default? }` for dual-theme payload.
457
+ - `customHighlight.shikiScopeMode` (default: `'auto'`): scope naming mode (`auto | color | semantic | keyword`).
458
+ - `customHighlight.shikiKeywordClassifier`: custom classifier hook for keyword mode.
459
+ - `customHighlight.shikiKeywordLangResolver`: custom language resolver hook for keyword mode.
460
+ - `customHighlight.shikiKeywordLangAliases`: language alias map for keyword resolver.
461
+ - `customHighlight.includeScopeStyles` (default: `true`): include payload `scopeStyles` when available.
462
+ - `customHighlight.fallback` (`'plain' | 'markup'`, default: `'plain'`): server-side fallback renderer on provider errors.
463
+ - `customHighlight.fallbackOn`: restrict fallback trigger reasons.
464
+ - `customHighlight.transport` (`'env' | 'inline-script'`, default: `'env'`): payload transport target.
465
+ - `customHighlight.idPrefix` (default: `'hl-'`): generated per-block payload id prefix.
466
+ - `customHighlight.scopePrefix` (default: `'hl'`): scope name prefix used for highlight registration.
467
+ - `customHighlight.lineFeatureStrategy` (`'hybrid' | 'disable'`, default: `'hybrid'`): keep or disable line-span features in API rendering.
468
+
469
+ ### API Helper Exports
470
+
471
+ - `applyCustomHighlights`
472
+ - `observeCustomHighlights`
473
+ - `clearCustomHighlights`
474
+ - `shouldRuntimeFallback`
475
+ - `getCustomHighlightPayloadMap`
476
+ - `renderCustomHighlightPayloadScript`
477
+ - `renderCustomHighlightScopeStyleTag`
478
+ - `customHighlightPayloadSchemaVersion`
479
+ - `customHighlightPayloadSupportedVersions`
480
+
481
+ ## Docs and Examples
482
+
483
+ - API styling guide: `docs/custom-highlight-styling-guide.md`
484
+ - default preset CSS sample: `docs/default-highlight-theme.css`
485
+ - provider matrix page: `example/custom-highlight-provider-matrix.html`
486
+
487
+ ## Tests and Benchmarks
488
+
489
+ - all tests: `npm test`
490
+ - provider contract: `npm run test:provider:contract`
491
+ - keyword coverage: `npm run test:provider:keyword`
492
+ - keyword holdout parity: `npm run test:provider:keyword:holdout`
493
+ - performance baseline: `npm run test:performance`
494
+ - runtime apply benchmark: `npm run test:performance:runtime`
495
+
496
+ ## License and Notices
497
+
498
+ - Project license: MIT (`LICENSE`)
499
+ - Third-party notices: `THIRD_PARTY_NOTICES.md`
500
+ - `docs/default-highlight-theme.css` contains mappings derived from highlight.js theme families (BSD-3-Clause; see notices).
@@ -0,0 +1,56 @@
1
+ # Third-Party Notices
2
+
3
+ This repository is licensed under MIT (see `LICENSE`), and also contains documentation assets derived from third-party themes.
4
+
5
+ ## highlight.js theme references used in docs
6
+
7
+ Files:
8
+
9
+ - `docs/default-highlight-theme.css`
10
+
11
+ Source references:
12
+
13
+ - `highlight.js` style family (GitHub / GitHub Dark)
14
+ - Upstream paths (for reference):
15
+ - `node_modules/highlight.js/styles/github.css`
16
+ - `node_modules/highlight.js/styles/github-dark.css`
17
+
18
+ Upstream license:
19
+
20
+ - BSD 3-Clause License
21
+ - Copyright (c) 2006, Ivan Sagalaev.
22
+
23
+ Full upstream license text:
24
+
25
+ ```text
26
+ BSD 3-Clause License
27
+
28
+ Copyright (c) 2006, Ivan Sagalaev.
29
+ All rights reserved.
30
+
31
+ Redistribution and use in source and binary forms, with or without
32
+ modification, are permitted provided that the following conditions are met:
33
+
34
+ * Redistributions of source code must retain the above copyright notice, this
35
+ list of conditions and the following disclaimer.
36
+
37
+ * Redistributions in binary form must reproduce the above copyright notice,
38
+ this list of conditions and the following disclaimer in the documentation
39
+ and/or other materials provided with the distribution.
40
+
41
+ * Neither the name of the copyright holder nor the names of its
42
+ contributors may be used to endorse or promote products derived from
43
+ this software without specific prior written permission.
44
+
45
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
46
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
48
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
49
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
51
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
52
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
53
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
54
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
55
+ ```
56
+