starlight-cannoli-plugins 2.15.0 → 2.17.1

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
@@ -231,6 +231,28 @@ The following attributes can be added to the opening fence:
231
231
  ```
232
232
  ````
233
233
 
234
+ ### Remark GFM Slug
235
+
236
+ A remark plugin that assigns heading IDs using the same GFM slug algorithm as VS Code and GitHub, before Astro's internal `rehypeHeadingIds` runs. Since `rehypeHeadingIds` skips headings that already have an `id`, this takes precedence and produces fragment anchors that match what VS Code's markdown link resolver expects.
237
+
238
+ This is especially useful when headings contain inline math or other syntax that Astro/Starlight would otherwise slugify from the rendered HTML output (producing long encoded IDs that differ from VS Code's raw-text slugification).
239
+
240
+ **Usage:**
241
+
242
+ ```ts
243
+ // astro.config.mjs
244
+ import { defineConfig } from "astro/config";
245
+ import { remarkGfmSlug } from "starlight-cannoli-plugins";
246
+
247
+ export default defineConfig({
248
+ markdown: {
249
+ remarkPlugins: [
250
+ remarkGfmSlug, // must come before remark-math or other transforming plugins
251
+ ],
252
+ },
253
+ });
254
+ ```
255
+
234
256
  ### Rehype Validate Links
235
257
 
236
258
  A rehype plugin that validates all internal links in your Markdown/MDX files at build time. Links without matching files will cause the build to fail.
@@ -360,7 +382,9 @@ An Astro integration that injects a client-side script to apply opt-in DOM patch
360
382
  - `syncTocLabelsFromHeadings` (optional, default: `false`): Copies the rendered HTML of each heading into its matching Starlight TOC anchor label, so the TOC properly reflects any custom markup (e.g. math) present in the heading.
361
383
  - `limitDetailsElementHeight` (optional, default: `false`): Limits the height of expanded `<details>` elements and makes their content scrollable.
362
384
  - `offerToggleAllDetails` (optional, default: `false`): Injects an "Expand All Dropdowns" toggle checkbox into the right sidebar (before `nav[aria-labelledby="starlight__on-this-page"]`). Clicking it opens or closes every `<details>` element on the page at once. Only appears on pages that contain at least one visible `<details>` element.
363
- - `offerTabbedContent` (optional, default: `false`): Injects a "Tabbed view" toggle checkbox immediately after `#starlight__on-this-page` in the right sidebar. When enabled by the user, the page's markdown content is reorganised into tabs — one per `<h2>` heading, plus an optional "Main" tab for any content that appears before the first `<h2>`. The toggle state is persisted to `localStorage`; active tab selection is not persisted. Only activates when the page has at least two sections (i.e. two or more `<h2>` elements, or one `<h2>` with pre-heading content). Has no effect on pages that lack `#starlight__on-this-page` (e.g. pages with the TOC disabled). Clicking a TOC anchor while tabs are enabled automatically switches to the tab containing the target heading. The generated elements use the classes `tabbed-content`, `tabbed-content-nav`, `tabbed-content-tab`, and `tabbed-content-panel` for styling; toggle button uses the existing `.toggle-checkbox-btn` class.
385
+ - `offerTabbedContent` (optional, default: `false`): Injects a "Tabbed view" toggle checkbox immediately after `#starlight__on-this-page` in the right sidebar. When enabled by the user, the page's markdown content is reorganised into tabs — one per `<h2>` heading, plus an optional "Main" tab for any content that appears before the first `<h2>`. The toggle defaults to off on every page load and is not persisted. Only activates when the page has at least two sections (i.e. two or more `<h2>` elements, or one `<h2>` with pre-heading content). Has no effect on pages that lack `#starlight__on-this-page` (e.g. pages with the TOC disabled). Clicking a TOC anchor while tabs are enabled automatically switches to the tab containing the target heading. The generated elements use the classes `tabbed-content`, `tabbed-content-nav`, `tabbed-content-tab`, and `tabbed-content-panel` for styling; toggle button uses the existing `.toggle-checkbox-btn` class.
386
+ - `decorateExternalLinks` (optional): Scans all links inside `.sl-markdown-content` and prepends a destination-specific icon to recognised link targets. Pass an object enabling each supported destination individually:
387
+ - `youtube` (default: `false`): prepends a YouTube play-button SVG (class `yt-icon`, `aria-hidden`, inherits `currentColor`) to links pointing to `youtube.com` or `youtu.be`.
364
388
 
365
389
  **Usage:**
366
390
 
@@ -378,6 +402,9 @@ export default defineConfig({
378
402
  limitDetailsElementHeight: true,
379
403
  offerToggleAllDetails: true,
380
404
  offerTabbedContent: true,
405
+ decorateExternalLinks: {
406
+ youtube: true,
407
+ },
381
408
  }),
382
409
  starlight({ title: "My Docs" }),
383
410
  ],
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseFrontmatter
3
- } from "./chunk-WFN7OFMJ.js";
3
+ } from "./chunk-EEPQVOZC.js";
4
4
 
5
5
  // src/plugins/astro-sync-docs-to-public/index.ts
6
6
  import { fileURLToPath } from "url";
@@ -13502,8 +13502,58 @@ import { visit } from "unist-util-visit";
13502
13502
  import path from "path";
13503
13503
  import { pathToFileURL } from "url";
13504
13504
 
13505
+ // node_modules/github-slugger/regex.js
13506
+ var regex = /[\0-\x1F!-,\.\/:-@\[-\^`\{-\xA9\xAB-\xB4\xB6-\xB9\xBB-\xBF\xD7\xF7\u02C2-\u02C5\u02D2-\u02DF\u02E5-\u02EB\u02ED\u02EF-\u02FF\u0375\u0378\u0379\u037E\u0380-\u0385\u0387\u038B\u038D\u03A2\u03F6\u0482\u0530\u0557\u0558\u055A-\u055F\u0589-\u0590\u05BE\u05C0\u05C3\u05C6\u05C8-\u05CF\u05EB-\u05EE\u05F3-\u060F\u061B-\u061F\u066A-\u066D\u06D4\u06DD\u06DE\u06E9\u06FD\u06FE\u0700-\u070F\u074B\u074C\u07B2-\u07BF\u07F6-\u07F9\u07FB\u07FC\u07FE\u07FF\u082E-\u083F\u085C-\u085F\u086B-\u089F\u08B5\u08C8-\u08D2\u08E2\u0964\u0965\u0970\u0984\u098D\u098E\u0991\u0992\u09A9\u09B1\u09B3-\u09B5\u09BA\u09BB\u09C5\u09C6\u09C9\u09CA\u09CF-\u09D6\u09D8-\u09DB\u09DE\u09E4\u09E5\u09F2-\u09FB\u09FD\u09FF\u0A00\u0A04\u0A0B-\u0A0E\u0A11\u0A12\u0A29\u0A31\u0A34\u0A37\u0A3A\u0A3B\u0A3D\u0A43-\u0A46\u0A49\u0A4A\u0A4E-\u0A50\u0A52-\u0A58\u0A5D\u0A5F-\u0A65\u0A76-\u0A80\u0A84\u0A8E\u0A92\u0AA9\u0AB1\u0AB4\u0ABA\u0ABB\u0AC6\u0ACA\u0ACE\u0ACF\u0AD1-\u0ADF\u0AE4\u0AE5\u0AF0-\u0AF8\u0B00\u0B04\u0B0D\u0B0E\u0B11\u0B12\u0B29\u0B31\u0B34\u0B3A\u0B3B\u0B45\u0B46\u0B49\u0B4A\u0B4E-\u0B54\u0B58-\u0B5B\u0B5E\u0B64\u0B65\u0B70\u0B72-\u0B81\u0B84\u0B8B-\u0B8D\u0B91\u0B96-\u0B98\u0B9B\u0B9D\u0BA0-\u0BA2\u0BA5-\u0BA7\u0BAB-\u0BAD\u0BBA-\u0BBD\u0BC3-\u0BC5\u0BC9\u0BCE\u0BCF\u0BD1-\u0BD6\u0BD8-\u0BE5\u0BF0-\u0BFF\u0C0D\u0C11\u0C29\u0C3A-\u0C3C\u0C45\u0C49\u0C4E-\u0C54\u0C57\u0C5B-\u0C5F\u0C64\u0C65\u0C70-\u0C7F\u0C84\u0C8D\u0C91\u0CA9\u0CB4\u0CBA\u0CBB\u0CC5\u0CC9\u0CCE-\u0CD4\u0CD7-\u0CDD\u0CDF\u0CE4\u0CE5\u0CF0\u0CF3-\u0CFF\u0D0D\u0D11\u0D45\u0D49\u0D4F-\u0D53\u0D58-\u0D5E\u0D64\u0D65\u0D70-\u0D79\u0D80\u0D84\u0D97-\u0D99\u0DB2\u0DBC\u0DBE\u0DBF\u0DC7-\u0DC9\u0DCB-\u0DCE\u0DD5\u0DD7\u0DE0-\u0DE5\u0DF0\u0DF1\u0DF4-\u0E00\u0E3B-\u0E3F\u0E4F\u0E5A-\u0E80\u0E83\u0E85\u0E8B\u0EA4\u0EA6\u0EBE\u0EBF\u0EC5\u0EC7\u0ECE\u0ECF\u0EDA\u0EDB\u0EE0-\u0EFF\u0F01-\u0F17\u0F1A-\u0F1F\u0F2A-\u0F34\u0F36\u0F38\u0F3A-\u0F3D\u0F48\u0F6D-\u0F70\u0F85\u0F98\u0FBD-\u0FC5\u0FC7-\u0FFF\u104A-\u104F\u109E\u109F\u10C6\u10C8-\u10CC\u10CE\u10CF\u10FB\u1249\u124E\u124F\u1257\u1259\u125E\u125F\u1289\u128E\u128F\u12B1\u12B6\u12B7\u12BF\u12C1\u12C6\u12C7\u12D7\u1311\u1316\u1317\u135B\u135C\u1360-\u137F\u1390-\u139F\u13F6\u13F7\u13FE-\u1400\u166D\u166E\u1680\u169B-\u169F\u16EB-\u16ED\u16F9-\u16FF\u170D\u1715-\u171F\u1735-\u173F\u1754-\u175F\u176D\u1771\u1774-\u177F\u17D4-\u17D6\u17D8-\u17DB\u17DE\u17DF\u17EA-\u180A\u180E\u180F\u181A-\u181F\u1879-\u187F\u18AB-\u18AF\u18F6-\u18FF\u191F\u192C-\u192F\u193C-\u1945\u196E\u196F\u1975-\u197F\u19AC-\u19AF\u19CA-\u19CF\u19DA-\u19FF\u1A1C-\u1A1F\u1A5F\u1A7D\u1A7E\u1A8A-\u1A8F\u1A9A-\u1AA6\u1AA8-\u1AAF\u1AC1-\u1AFF\u1B4C-\u1B4F\u1B5A-\u1B6A\u1B74-\u1B7F\u1BF4-\u1BFF\u1C38-\u1C3F\u1C4A-\u1C4C\u1C7E\u1C7F\u1C89-\u1C8F\u1CBB\u1CBC\u1CC0-\u1CCF\u1CD3\u1CFB-\u1CFF\u1DFA\u1F16\u1F17\u1F1E\u1F1F\u1F46\u1F47\u1F4E\u1F4F\u1F58\u1F5A\u1F5C\u1F5E\u1F7E\u1F7F\u1FB5\u1FBD\u1FBF-\u1FC1\u1FC5\u1FCD-\u1FCF\u1FD4\u1FD5\u1FDC-\u1FDF\u1FED-\u1FF1\u1FF5\u1FFD-\u203E\u2041-\u2053\u2055-\u2070\u2072-\u207E\u2080-\u208F\u209D-\u20CF\u20F1-\u2101\u2103-\u2106\u2108\u2109\u2114\u2116-\u2118\u211E-\u2123\u2125\u2127\u2129\u212E\u213A\u213B\u2140-\u2144\u214A-\u214D\u214F-\u215F\u2189-\u24B5\u24EA-\u2BFF\u2C2F\u2C5F\u2CE5-\u2CEA\u2CF4-\u2CFF\u2D26\u2D28-\u2D2C\u2D2E\u2D2F\u2D68-\u2D6E\u2D70-\u2D7E\u2D97-\u2D9F\u2DA7\u2DAF\u2DB7\u2DBF\u2DC7\u2DCF\u2DD7\u2DDF\u2E00-\u2E2E\u2E30-\u3004\u3008-\u3020\u3030\u3036\u3037\u303D-\u3040\u3097\u3098\u309B\u309C\u30A0\u30FB\u3100-\u3104\u3130\u318F-\u319F\u31C0-\u31EF\u3200-\u33FF\u4DC0-\u4DFF\u9FFD-\u9FFF\uA48D-\uA4CF\uA4FE\uA4FF\uA60D-\uA60F\uA62C-\uA63F\uA673\uA67E\uA6F2-\uA716\uA720\uA721\uA789\uA78A\uA7C0\uA7C1\uA7CB-\uA7F4\uA828-\uA82B\uA82D-\uA83F\uA874-\uA87F\uA8C6-\uA8CF\uA8DA-\uA8DF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA954-\uA95F\uA97D-\uA97F\uA9C1-\uA9CE\uA9DA-\uA9DF\uA9FF\uAA37-\uAA3F\uAA4E\uAA4F\uAA5A-\uAA5F\uAA77-\uAA79\uAAC3-\uAADA\uAADE\uAADF\uAAF0\uAAF1\uAAF7-\uAB00\uAB07\uAB08\uAB0F\uAB10\uAB17-\uAB1F\uAB27\uAB2F\uAB5B\uAB6A-\uAB6F\uABEB\uABEE\uABEF\uABFA-\uABFF\uD7A4-\uD7AF\uD7C7-\uD7CA\uD7FC-\uD7FF\uE000-\uF8FF\uFA6E\uFA6F\uFADA-\uFAFF\uFB07-\uFB12\uFB18-\uFB1C\uFB29\uFB37\uFB3D\uFB3F\uFB42\uFB45\uFBB2-\uFBD2\uFD3E-\uFD4F\uFD90\uFD91\uFDC8-\uFDEF\uFDFC-\uFDFF\uFE10-\uFE1F\uFE30-\uFE32\uFE35-\uFE4C\uFE50-\uFE6F\uFE75\uFEFD-\uFF0F\uFF1A-\uFF20\uFF3B-\uFF3E\uFF40\uFF5B-\uFF65\uFFBF-\uFFC1\uFFC8\uFFC9\uFFD0\uFFD1\uFFD8\uFFD9\uFFDD-\uFFFF]|\uD800[\uDC0C\uDC27\uDC3B\uDC3E\uDC4E\uDC4F\uDC5E-\uDC7F\uDCFB-\uDD3F\uDD75-\uDDFC\uDDFE-\uDE7F\uDE9D-\uDE9F\uDED1-\uDEDF\uDEE1-\uDEFF\uDF20-\uDF2C\uDF4B-\uDF4F\uDF7B-\uDF7F\uDF9E\uDF9F\uDFC4-\uDFC7\uDFD0\uDFD6-\uDFFF]|\uD801[\uDC9E\uDC9F\uDCAA-\uDCAF\uDCD4-\uDCD7\uDCFC-\uDCFF\uDD28-\uDD2F\uDD64-\uDDFF\uDF37-\uDF3F\uDF56-\uDF5F\uDF68-\uDFFF]|\uD802[\uDC06\uDC07\uDC09\uDC36\uDC39-\uDC3B\uDC3D\uDC3E\uDC56-\uDC5F\uDC77-\uDC7F\uDC9F-\uDCDF\uDCF3\uDCF6-\uDCFF\uDD16-\uDD1F\uDD3A-\uDD7F\uDDB8-\uDDBD\uDDC0-\uDDFF\uDE04\uDE07-\uDE0B\uDE14\uDE18\uDE36\uDE37\uDE3B-\uDE3E\uDE40-\uDE5F\uDE7D-\uDE7F\uDE9D-\uDEBF\uDEC8\uDEE7-\uDEFF\uDF36-\uDF3F\uDF56-\uDF5F\uDF73-\uDF7F\uDF92-\uDFFF]|\uD803[\uDC49-\uDC7F\uDCB3-\uDCBF\uDCF3-\uDCFF\uDD28-\uDD2F\uDD3A-\uDE7F\uDEAA\uDEAD-\uDEAF\uDEB2-\uDEFF\uDF1D-\uDF26\uDF28-\uDF2F\uDF51-\uDFAF\uDFC5-\uDFDF\uDFF7-\uDFFF]|\uD804[\uDC47-\uDC65\uDC70-\uDC7E\uDCBB-\uDCCF\uDCE9-\uDCEF\uDCFA-\uDCFF\uDD35\uDD40-\uDD43\uDD48-\uDD4F\uDD74\uDD75\uDD77-\uDD7F\uDDC5-\uDDC8\uDDCD\uDDDB\uDDDD-\uDDFF\uDE12\uDE38-\uDE3D\uDE3F-\uDE7F\uDE87\uDE89\uDE8E\uDE9E\uDEA9-\uDEAF\uDEEB-\uDEEF\uDEFA-\uDEFF\uDF04\uDF0D\uDF0E\uDF11\uDF12\uDF29\uDF31\uDF34\uDF3A\uDF45\uDF46\uDF49\uDF4A\uDF4E\uDF4F\uDF51-\uDF56\uDF58-\uDF5C\uDF64\uDF65\uDF6D-\uDF6F\uDF75-\uDFFF]|\uD805[\uDC4B-\uDC4F\uDC5A-\uDC5D\uDC62-\uDC7F\uDCC6\uDCC8-\uDCCF\uDCDA-\uDD7F\uDDB6\uDDB7\uDDC1-\uDDD7\uDDDE-\uDDFF\uDE41-\uDE43\uDE45-\uDE4F\uDE5A-\uDE7F\uDEB9-\uDEBF\uDECA-\uDEFF\uDF1B\uDF1C\uDF2C-\uDF2F\uDF3A-\uDFFF]|\uD806[\uDC3B-\uDC9F\uDCEA-\uDCFE\uDD07\uDD08\uDD0A\uDD0B\uDD14\uDD17\uDD36\uDD39\uDD3A\uDD44-\uDD4F\uDD5A-\uDD9F\uDDA8\uDDA9\uDDD8\uDDD9\uDDE2\uDDE5-\uDDFF\uDE3F-\uDE46\uDE48-\uDE4F\uDE9A-\uDE9C\uDE9E-\uDEBF\uDEF9-\uDFFF]|\uD807[\uDC09\uDC37\uDC41-\uDC4F\uDC5A-\uDC71\uDC90\uDC91\uDCA8\uDCB7-\uDCFF\uDD07\uDD0A\uDD37-\uDD39\uDD3B\uDD3E\uDD48-\uDD4F\uDD5A-\uDD5F\uDD66\uDD69\uDD8F\uDD92\uDD99-\uDD9F\uDDAA-\uDEDF\uDEF7-\uDFAF\uDFB1-\uDFFF]|\uD808[\uDF9A-\uDFFF]|\uD809[\uDC6F-\uDC7F\uDD44-\uDFFF]|[\uD80A\uD80B\uD80E-\uD810\uD812-\uD819\uD824-\uD82B\uD82D\uD82E\uD830-\uD833\uD837\uD839\uD83D\uD83F\uD87B-\uD87D\uD87F\uD885-\uDB3F\uDB41-\uDBFF][\uDC00-\uDFFF]|\uD80D[\uDC2F-\uDFFF]|\uD811[\uDE47-\uDFFF]|\uD81A[\uDE39-\uDE3F\uDE5F\uDE6A-\uDECF\uDEEE\uDEEF\uDEF5-\uDEFF\uDF37-\uDF3F\uDF44-\uDF4F\uDF5A-\uDF62\uDF78-\uDF7C\uDF90-\uDFFF]|\uD81B[\uDC00-\uDE3F\uDE80-\uDEFF\uDF4B-\uDF4E\uDF88-\uDF8E\uDFA0-\uDFDF\uDFE2\uDFE5-\uDFEF\uDFF2-\uDFFF]|\uD821[\uDFF8-\uDFFF]|\uD823[\uDCD6-\uDCFF\uDD09-\uDFFF]|\uD82C[\uDD1F-\uDD4F\uDD53-\uDD63\uDD68-\uDD6F\uDEFC-\uDFFF]|\uD82F[\uDC6B-\uDC6F\uDC7D-\uDC7F\uDC89-\uDC8F\uDC9A-\uDC9C\uDC9F-\uDFFF]|\uD834[\uDC00-\uDD64\uDD6A-\uDD6C\uDD73-\uDD7A\uDD83\uDD84\uDD8C-\uDDA9\uDDAE-\uDE41\uDE45-\uDFFF]|\uD835[\uDC55\uDC9D\uDCA0\uDCA1\uDCA3\uDCA4\uDCA7\uDCA8\uDCAD\uDCBA\uDCBC\uDCC4\uDD06\uDD0B\uDD0C\uDD15\uDD1D\uDD3A\uDD3F\uDD45\uDD47-\uDD49\uDD51\uDEA6\uDEA7\uDEC1\uDEDB\uDEFB\uDF15\uDF35\uDF4F\uDF6F\uDF89\uDFA9\uDFC3\uDFCC\uDFCD]|\uD836[\uDC00-\uDDFF\uDE37-\uDE3A\uDE6D-\uDE74\uDE76-\uDE83\uDE85-\uDE9A\uDEA0\uDEB0-\uDFFF]|\uD838[\uDC07\uDC19\uDC1A\uDC22\uDC25\uDC2B-\uDCFF\uDD2D-\uDD2F\uDD3E\uDD3F\uDD4A-\uDD4D\uDD4F-\uDEBF\uDEFA-\uDFFF]|\uD83A[\uDCC5-\uDCCF\uDCD7-\uDCFF\uDD4C-\uDD4F\uDD5A-\uDFFF]|\uD83B[\uDC00-\uDDFF\uDE04\uDE20\uDE23\uDE25\uDE26\uDE28\uDE33\uDE38\uDE3A\uDE3C-\uDE41\uDE43-\uDE46\uDE48\uDE4A\uDE4C\uDE50\uDE53\uDE55\uDE56\uDE58\uDE5A\uDE5C\uDE5E\uDE60\uDE63\uDE65\uDE66\uDE6B\uDE73\uDE78\uDE7D\uDE7F\uDE8A\uDE9C-\uDEA0\uDEA4\uDEAA\uDEBC-\uDFFF]|\uD83C[\uDC00-\uDD2F\uDD4A-\uDD4F\uDD6A-\uDD6F\uDD8A-\uDFFF]|\uD83E[\uDC00-\uDFEF\uDFFA-\uDFFF]|\uD869[\uDEDE-\uDEFF]|\uD86D[\uDF35-\uDF3F]|\uD86E[\uDC1E\uDC1F]|\uD873[\uDEA2-\uDEAF]|\uD87A[\uDFE1-\uDFFF]|\uD87E[\uDE1E-\uDFFF]|\uD884[\uDF4B-\uDFFF]|\uDB40[\uDC00-\uDCFF\uDDF0-\uDFFF]/g;
13507
+
13505
13508
  // node_modules/github-slugger/index.js
13506
13509
  var own4 = Object.hasOwnProperty;
13510
+ var BananaSlug = class {
13511
+ /**
13512
+ * Create a new slug class.
13513
+ */
13514
+ constructor() {
13515
+ this.occurrences;
13516
+ this.reset();
13517
+ }
13518
+ /**
13519
+ * Generate a unique slug.
13520
+ *
13521
+ * Tracks previously generated slugs: repeated calls with the same value
13522
+ * will result in different slugs.
13523
+ * Use the `slug` function to get same slugs.
13524
+ *
13525
+ * @param {string} value
13526
+ * String of text to slugify
13527
+ * @param {boolean} [maintainCase=false]
13528
+ * Keep the current case, otherwise make all lowercase
13529
+ * @return {string}
13530
+ * A unique slug string
13531
+ */
13532
+ slug(value, maintainCase) {
13533
+ const self2 = this;
13534
+ let result = slug(value, maintainCase === true);
13535
+ const originalSlug = result;
13536
+ while (own4.call(self2.occurrences, result)) {
13537
+ self2.occurrences[originalSlug]++;
13538
+ result = originalSlug + "-" + self2.occurrences[originalSlug];
13539
+ }
13540
+ self2.occurrences[result] = 0;
13541
+ return result;
13542
+ }
13543
+ /**
13544
+ * Reset - Forget all previous slugs
13545
+ *
13546
+ * @return void
13547
+ */
13548
+ reset() {
13549
+ this.occurrences = /* @__PURE__ */ Object.create(null);
13550
+ }
13551
+ };
13552
+ function slug(value, maintainCase) {
13553
+ if (typeof value !== "string") return "";
13554
+ if (!maintainCase) value = value.toLowerCase();
13555
+ return value.replace(regex, "").replace(/ /g, "-");
13556
+ }
13507
13557
 
13508
13558
  // node_modules/@astrojs/markdown-remark/dist/rehype-collect-headings.js
13509
13559
  import { visit as visit2 } from "unist-util-visit";
@@ -13522,8 +13572,10 @@ import { visit as visit4 } from "unist-util-visit";
13522
13572
  var isPerformanceBenchmark = Boolean(process.env.ASTRO_PERFORMANCE_BENCHMARK);
13523
13573
 
13524
13574
  export {
13575
+ toString2 as toString,
13525
13576
  remarkParse,
13526
13577
  unified,
13578
+ BananaSlug,
13527
13579
  parseFrontmatter
13528
13580
  };
13529
13581
  /*! Bundled license information:
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseFrontmatter
3
- } from "./chunk-C2VXRQOK.js";
3
+ } from "./chunk-7D2KLZGE.js";
4
4
 
5
5
  // src/plugins/utils/workspace-utils.ts
6
6
  import * as fs from "fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseFrontmatter
3
- } from "./chunk-C2VXRQOK.js";
3
+ } from "./chunk-7D2KLZGE.js";
4
4
 
5
5
  // src/plugins/astro-latex-compile/index.ts
6
6
  import { readFileSync as readFileSync2 } from "fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseFrontmatter
3
- } from "./chunk-WFN7OFMJ.js";
3
+ } from "./chunk-EEPQVOZC.js";
4
4
 
5
5
  // src/plugins/starlight-index-only-sidebar/index.ts
6
6
  import { join as join2 } from "path";
@@ -90,6 +90,7 @@ function starlightDomPatches(options = {}) {
90
90
  limitDetailsElementHeight: limitDetailsElementHeightOption = false,
91
91
  offerTabbedContent = false,
92
92
  offerToggleAllDetails = false,
93
+ decorateExternalLinks,
93
94
  wrapDetailsContent
94
95
  } = options;
95
96
  if (wrapDetailsContent !== void 0) {
@@ -132,10 +133,16 @@ function starlightDomPatches(options = {}) {
132
133
  syncTocLabelsFromHeadings ? "syncTocLabelsFromHeadings" : null,
133
134
  limitDetailsElementHeight ? "limitDetailsElementHeight" : null,
134
135
  offerTabbedContent ? "tabbedH2Content" : null,
135
- offerToggleAllDetails ? "toggleAllDetails" : null
136
+ offerToggleAllDetails ? "toggleAllDetails" : null,
137
+ decorateExternalLinks ? "decorateExternalLinks" : null
136
138
  ].filter(Boolean);
137
139
  if (imports.length === 0) return;
138
- const calls = imports.map((fn) => `${fn}();`).join("\n");
140
+ const calls = imports.map((fn) => {
141
+ if (fn === "decorateExternalLinks" && decorateExternalLinks) {
142
+ return `${fn}(${JSON.stringify(decorateExternalLinks)});`;
143
+ }
144
+ return `${fn}();`;
145
+ }).join("\n");
139
146
  injectScript(
140
147
  "page",
141
148
  `import { ${imports.join(", ")} } from ${scriptPath};
package/dist/index.d.ts CHANGED
@@ -3,6 +3,8 @@ import { HookParameters } from '@astrojs/starlight/types';
3
3
  export { DomPatchesOptions, starlightDomPatches } from './plugins/starlight-dom-patches.js';
4
4
  export { SyncDocsToPublicOptions, syncDocsToPublic } from './plugins/astro-sync-docs-to-public.js';
5
5
  export { rehypeValidateLinks } from './plugins/rehype-validate-links.js';
6
+ import { Root } from 'mdast';
7
+ import { Plugin } from 'unified';
6
8
  export { astroNormalizePaths } from './plugins/astro-normalize-paths.js';
7
9
  export { expressiveCodeEmphasis } from './plugins/expressive-code-emphasis.js';
8
10
  import { AstroIntegration } from 'astro';
@@ -10,7 +12,6 @@ import { LatexCompilePluginOptions } from './plugins/astro-latex-compile.js';
10
12
  import 'hast';
11
13
  import 'vfile';
12
14
  import 'expressive-code';
13
- import 'mdast';
14
15
 
15
16
  type TBadgeVariant = "note" | "danger" | "success" | "caution" | "tip" | "default";
16
17
  type TIndexMarker = {
@@ -66,6 +67,14 @@ declare function starlightIndexSourcedSidebar(options: TOptions): {
66
67
  };
67
68
  };
68
69
 
70
+ /**
71
+ * Remark plugin that assigns heading IDs matching VS Code's markdown link
72
+ * resolver: GFM slugification of the raw heading text, with math nodes stripped.
73
+ * Runs before Astro's rehypeHeadingIds, which skips headings that already
74
+ * have an id, so this takes precedence.
75
+ */
76
+ declare const remarkGfmSlug: Plugin<[], Root>;
77
+
69
78
  interface LatexCompileOptions extends LatexCompilePluginOptions {
70
79
  /**
71
80
  * When `true`, SVG files in `svgOutputDir` that are no longer referenced by
@@ -78,4 +87,4 @@ interface LatexCompileOptions extends LatexCompilePluginOptions {
78
87
  }
79
88
  declare function astroLatexCompile(options: LatexCompileOptions): AstroIntegration;
80
89
 
81
- export { type LatexCompileOptions, astroLatexCompile, starlightIndexSourcedSidebar };
90
+ export { type LatexCompileOptions, astroLatexCompile, remarkGfmSlug, starlightIndexSourcedSidebar };
package/dist/index.js CHANGED
@@ -3,30 +3,32 @@ import {
3
3
  } from "./chunk-O2TZASB7.js";
4
4
  import {
5
5
  starlightIndexOnlySidebar
6
- } from "./chunk-JFHWCFHF.js";
6
+ } from "./chunk-GTVQDNAE.js";
7
7
  import {
8
8
  rehypeValidateLinks
9
9
  } from "./chunk-3UMY7T6G.js";
10
10
  import {
11
11
  latexCompileTransformer
12
- } from "./chunk-RAKGKJJO.js";
12
+ } from "./chunk-FDEHNCBK.js";
13
13
  import {
14
14
  astroNormalizePaths
15
15
  } from "./chunk-TLOFSB33.js";
16
16
  import "./chunk-LJWSZSPB.js";
17
17
  import {
18
18
  syncDocsToPublic
19
- } from "./chunk-QPHWDN2I.js";
19
+ } from "./chunk-4ZJKEEDA.js";
20
20
  import {
21
21
  parseFrontmatter
22
- } from "./chunk-WFN7OFMJ.js";
22
+ } from "./chunk-EEPQVOZC.js";
23
23
  import {
24
+ BananaSlug,
24
25
  remarkParse,
26
+ toString,
25
27
  unified
26
- } from "./chunk-C2VXRQOK.js";
28
+ } from "./chunk-7D2KLZGE.js";
27
29
  import {
28
30
  starlightDomPatches
29
- } from "./chunk-PPNIMJPX.js";
31
+ } from "./chunk-SKQV57MG.js";
30
32
  import "./chunk-4VNS5WPM.js";
31
33
 
32
34
  // src/plugins/starlight-index-sourced-sidebar/index.ts
@@ -228,6 +230,32 @@ function starlightIndexSourcedSidebar(options) {
228
230
  };
229
231
  }
230
232
 
233
+ // src/plugins/remark-gfm-slug.ts
234
+ import { visit as visit2 } from "unist-util-visit";
235
+ var STRIP_NODE_TYPES = /* @__PURE__ */ new Set(["math", "inlineMath"]);
236
+ function headingText(node) {
237
+ const parts = [];
238
+ for (const child of node.children) {
239
+ if (STRIP_NODE_TYPES.has(child.type)) continue;
240
+ parts.push(toString(child, { includeImageAlt: false }));
241
+ }
242
+ return parts.join("").trim();
243
+ }
244
+ var remarkGfmSlug = () => {
245
+ return (tree) => {
246
+ const slugger = new BananaSlug();
247
+ visit2(tree, "heading", (node) => {
248
+ const text = headingText(node);
249
+ const id = slugger.slug(text);
250
+ const data = node.data ??= {};
251
+ data.id = id;
252
+ data.hProperties ??= {};
253
+ data.hProperties.id = id;
254
+ });
255
+ };
256
+ };
257
+ var remark_gfm_slug_default = remarkGfmSlug;
258
+
231
259
  // src/plugins/astro-latex-compile/astro-integration.ts
232
260
  import fs2 from "fs";
233
261
  import { readdir, rm } from "fs/promises";
@@ -328,6 +356,7 @@ export {
328
356
  astroNormalizePaths,
329
357
  expressiveCodeEmphasis,
330
358
  rehypeValidateLinks,
359
+ remark_gfm_slug_default as remarkGfmSlug,
331
360
  starlightDomPatches,
332
361
  starlightIndexOnlySidebar,
333
362
  starlightIndexSourcedSidebar,
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  compileLatexToSvg,
3
3
  latexCompileTransformer
4
- } from "../chunk-RAKGKJJO.js";
5
- import "../chunk-C2VXRQOK.js";
4
+ } from "../chunk-FDEHNCBK.js";
5
+ import "../chunk-7D2KLZGE.js";
6
6
  import "../chunk-4VNS5WPM.js";
7
7
  export {
8
8
  compileLatexToSvg,
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  syncDocsToPublic
3
- } from "../chunk-QPHWDN2I.js";
4
- import "../chunk-WFN7OFMJ.js";
5
- import "../chunk-C2VXRQOK.js";
3
+ } from "../chunk-4ZJKEEDA.js";
4
+ import "../chunk-EEPQVOZC.js";
5
+ import "../chunk-7D2KLZGE.js";
6
6
  import "../chunk-4VNS5WPM.js";
7
7
  export {
8
8
  syncDocsToPublic
@@ -2,6 +2,10 @@ declare function hideSingleLineGutters(): void;
2
2
  declare function syncTocLabelsFromHeadings(): void;
3
3
  declare function tabbedH2Content(): void;
4
4
  declare function toggleAllDetails(): void;
5
+ type DecorateExternalLinksOptions = {
6
+ youtube?: boolean;
7
+ };
8
+ declare function decorateExternalLinks(options: DecorateExternalLinksOptions): void;
5
9
  declare function limitDetailsElementHeight(): void;
6
10
 
7
- export { hideSingleLineGutters, limitDetailsElementHeight, syncTocLabelsFromHeadings, tabbedH2Content, toggleAllDetails };
11
+ export { decorateExternalLinks, hideSingleLineGutters, limitDetailsElementHeight, syncTocLabelsFromHeadings, tabbedH2Content, toggleAllDetails };
@@ -50,7 +50,6 @@ function syncTocLabelsFromHeadings() {
50
50
  });
51
51
  }
52
52
  function tabbedH2Content() {
53
- const LS_KEY = "starlight-dom-patches:tabbed-content";
54
53
  const contentContainer = document.querySelector(
55
54
  ".main-pane .sl-markdown-content"
56
55
  );
@@ -241,16 +240,10 @@ function tabbedH2Content() {
241
240
  );
242
241
  viewToggleCheckbox2.addEventListener("change", () => {
243
242
  setEnabled(viewToggleCheckbox2.checked);
244
- localStorage.setItem(
245
- LS_KEY,
246
- viewToggleCheckbox2.checked ? "enabled" : "disabled"
247
- );
248
243
  });
249
244
  }
250
245
  const viewToggleCheckbox = buildToggle();
251
- const initialEnabled = localStorage.getItem(LS_KEY) === "enabled";
252
- viewToggleCheckbox.checked = initialEnabled;
253
- setEnabled(initialEnabled);
246
+ setEnabled(false);
254
247
  if (window.location.hash) navigateToHash(window.location.hash);
255
248
  wireInteractions(viewToggleCheckbox);
256
249
  }
@@ -285,6 +278,53 @@ function toggleAllDetails() {
285
278
  }, 10);
286
279
  });
287
280
  }
281
+ function decorateExternalLinks(options) {
282
+ const MATCHERS = [
283
+ {
284
+ key: "youtube",
285
+ domains: ["youtube.com", "youtu.be", "www.youtube.com"],
286
+ iconClass: "yt-icon",
287
+ viewBox: "0 0 24 24",
288
+ d: "M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"
289
+ }
290
+ ];
291
+ const enabledMatchers = MATCHERS.filter((m) => options[m.key]);
292
+ function matchIcon(href) {
293
+ try {
294
+ const { hostname } = new URL(href);
295
+ return enabledMatchers.find((m) => m.domains.includes(hostname)) ?? null;
296
+ } catch {
297
+ return null;
298
+ }
299
+ }
300
+ document.querySelectorAll(".sl-markdown-content a[href]").forEach((anchor) => {
301
+ const href = anchor.getAttribute("href") ?? "";
302
+ const matcher = matchIcon(href);
303
+ if (!matcher) return;
304
+ if (anchor.querySelector(`span.not-content > .${matcher.iconClass}`))
305
+ return;
306
+ const wrapper = document.createElement("span");
307
+ wrapper.className = "not-content pe-1 align-middle";
308
+ const icon = document.createElementNS(
309
+ "http://www.w3.org/2000/svg",
310
+ "svg"
311
+ );
312
+ icon.setAttribute("class", matcher.iconClass);
313
+ icon.setAttribute("viewBox", matcher.viewBox);
314
+ icon.setAttribute("aria-hidden", "true");
315
+ icon.setAttribute("width", "1em");
316
+ icon.setAttribute("height", "1em");
317
+ const path = document.createElementNS(
318
+ "http://www.w3.org/2000/svg",
319
+ "path"
320
+ );
321
+ path.setAttribute("d", matcher.d);
322
+ path.setAttribute("fill", "currentColor");
323
+ icon.appendChild(path);
324
+ wrapper.appendChild(icon);
325
+ anchor.prepend(wrapper);
326
+ });
327
+ }
288
328
  function limitDetailsElementHeight() {
289
329
  const detailsElements = document.querySelectorAll(".main-pane details");
290
330
  detailsElements.forEach((details) => {
@@ -313,6 +353,7 @@ function limitDetailsElementHeight() {
313
353
  });
314
354
  }
315
355
  export {
356
+ decorateExternalLinks,
316
357
  hideSingleLineGutters,
317
358
  limitDetailsElementHeight,
318
359
  syncTocLabelsFromHeadings,
@@ -20,6 +20,14 @@ interface DomPatchesOptions {
20
20
  * @default false
21
21
  */
22
22
  offerToggleAllDetails?: boolean;
23
+ /**
24
+ * Prepend icons to links based on their destination.
25
+ * Each key enables a specific icon; omitting a key or setting it to `false` disables it.
26
+ */
27
+ decorateExternalLinks?: {
28
+ /** Prepend a YouTube play-button icon to links pointing to youtube.com or youtu.be. */
29
+ youtube?: boolean;
30
+ };
23
31
  /**
24
32
  * @deprecated Use `limitDetailsElementHeight` instead.
25
33
  */
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  starlightDomPatches
3
- } from "../chunk-PPNIMJPX.js";
3
+ } from "../chunk-SKQV57MG.js";
4
4
  import "../chunk-4VNS5WPM.js";
5
5
  export {
6
6
  starlightDomPatches
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  starlightIndexOnlySidebar
3
- } from "../chunk-JFHWCFHF.js";
4
- import "../chunk-WFN7OFMJ.js";
5
- import "../chunk-C2VXRQOK.js";
3
+ } from "../chunk-GTVQDNAE.js";
4
+ import "../chunk-EEPQVOZC.js";
5
+ import "../chunk-7D2KLZGE.js";
6
6
  import "../chunk-4VNS5WPM.js";
7
7
  export {
8
8
  starlightIndexOnlySidebar
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "starlight-cannoli-plugins",
3
3
  "type": "module",
4
- "version": "2.15.0",
4
+ "version": "2.17.1",
5
5
  "description": "Starlight plugins for automatic sidebar generation and link validation",
6
6
  "license": "ISC",
7
7
  "main": "./dist/index.js",