vitepress-linkcard 2.0.0 → 2.1.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.
@@ -1,76 +1,11 @@
1
- /**
2
- * HTML parser module for extracting metadata from web pages.
3
- *
4
- * This module provides functions to parse HTML strings and extract metadata such as
5
- * title, description, and logo/icon URLs. It supports both standard HTML meta tags
6
- * and Open Graph Protocol (OGP) tags.
7
- *
8
- * @module parser
9
- * @todo Refactor to improve maintainability and add support for more meta tag formats
10
- */
11
1
  import { isString } from '@luckrya/utility';
12
2
  import { cleanPath, extractUrl } from './url';
13
- /**
14
- * Default logo URL used when no logo can be extracted from the page.
15
- */
16
3
  const DEFAULT_LOGO = 'https://resources.whatwg.org/logo-url.svg';
17
- /**
18
- * Regular expression to match HTML tag content between opening and closing tags.
19
- * @example Matches: `<title>Page Title</title>` → captures "Page Title"
20
- */
21
4
  const HtmlTagContentReg = /(<[A-Za-z]+\s*[^>]*>(.*)<\/[A-Za-z]+>)/;
22
- /**
23
- * Regular expression to extract the `content` attribute value from HTML meta tags.
24
- * @example Matches: `content="value"` or `content='value'`
25
- */
26
5
  const ContentAttrValueHtmlMetaTagReg = /content=["|']([^>]*)["|']/;
27
- /**
28
- * Regular expression to extract the `href` attribute value from HTML link tags.
29
- * @example Matches: `href="value"` or `href='value'`
30
- */
31
6
  const HrefAttrValueHtmlLinkTagReg = /href=["|']([^>]*)["|']/;
32
- /**
33
- * Regular expression to match HTML title tags.
34
- * @example Matches: `<title>Page Title</title>`
35
- */
36
7
  const HtmlTitleTagReg = /(<title\s*[^>]*>(.*)<\/title>)/g;
37
- /**
38
- * Creates a regular expression to match self-closing or non-closing HTML tags with a specific attribute.
39
- *
40
- * This function generates patterns to find meta or link tags that contain a specific attribute,
41
- * which is useful for finding OGP tags like `og:title`, `og:description`, etc.
42
- *
43
- * @param attr - The attribute to search for (e.g., 'title', 'description', 'image')
44
- * @param tag - The HTML tag name to match (default: 'meta')
45
- * @returns A RegExp that matches tags containing the specified attribute
46
- *
47
- * @example
48
- * ```typescript
49
- * const reg = containArrSelfLosingHtmlTagReg('og:title')
50
- * // Matches: <meta property="og:title" content="...">
51
- * ```
52
- *
53
- * @internal
54
- */
55
8
  const containArrSelfLosingHtmlTagReg = (attr, tag = 'meta') => new RegExp(`<${tag}\\s[^>]*\\w+=['|"]([a-zA-Z]|:|\\s)*${attr}['|"][^>]*\\/?>`);
56
- /**
57
- * Extracts the page title from HTML string.
58
- *
59
- * Attempts to find the title in the following order:
60
- * 1. Meta tags with 'title' attribute (e.g., `<meta property="og:title" content="...">`)
61
- * 2. Standard HTML `<title>` tag
62
- *
63
- * @param htmlString - The HTML content to parse
64
- * @returns The extracted title, or undefined if not found
65
- *
66
- * @example
67
- * ```typescript
68
- * const title = matchTitleByMetaTag('<title>Example Page</title>')
69
- * // Returns: "Example Page"
70
- * ```
71
- *
72
- * @internal
73
- */
74
9
  function matchTitleByMetaTag(htmlString) {
75
10
  let title;
76
11
  const metas = htmlString.match(containArrSelfLosingHtmlTagReg('title'));
@@ -89,24 +24,6 @@ function matchTitleByMetaTag(htmlString) {
89
24
  }
90
25
  return title;
91
26
  }
92
- /**
93
- * Extracts the page description from HTML string.
94
- *
95
- * Searches for description in meta tags, supporting both standard and OGP formats:
96
- * - `<meta name="description" content="...">`
97
- * - `<meta property="og:description" content="...">`
98
- *
99
- * @param htmlString - The HTML content to parse
100
- * @returns The extracted description, or undefined if not found
101
- *
102
- * @example
103
- * ```typescript
104
- * const desc = matchDescriptionByMetaTag('<meta name="description" content="A great site">')
105
- * // Returns: "A great site"
106
- * ```
107
- *
108
- * @internal
109
- */
110
27
  function matchDescriptionByMetaTag(htmlString) {
111
28
  let description;
112
29
  const metas = htmlString.match(containArrSelfLosingHtmlTagReg('description'));
@@ -117,24 +34,6 @@ function matchDescriptionByMetaTag(htmlString) {
117
34
  }
118
35
  return description;
119
36
  }
120
- /**
121
- * Extracts the page logo/icon URL from HTML string.
122
- *
123
- * Attempts to find the logo in the following order:
124
- * 1. OGP image tag: `<meta property="og:image" content="...">`
125
- * 2. Icon link tag: `<link rel="icon" href="...">`
126
- *
127
- * @param htmlString - The HTML content to parse
128
- * @returns The extracted logo URL (may be relative), or undefined if not found
129
- *
130
- * @example
131
- * ```typescript
132
- * const logo = matchLogoByLinkOrMetaTag('<link rel="icon" href="/favicon.ico">')
133
- * // Returns: "/favicon.ico"
134
- * ```
135
- *
136
- * @internal
137
- */
138
37
  function matchLogoByLinkOrMetaTag(htmlString) {
139
38
  let logo;
140
39
  const metas = htmlString.match(containArrSelfLosingHtmlTagReg('image'));
@@ -147,7 +46,6 @@ function matchLogoByLinkOrMetaTag(htmlString) {
147
46
  const linkHtmlTags = htmlString.match(containArrSelfLosingHtmlTagReg('icon', 'link'));
148
47
  if (linkHtmlTags?.length) {
149
48
  const content = linkHtmlTags[0].match(HrefAttrValueHtmlLinkTagReg);
150
- // logo 判断是否是完整地址
151
49
  if (content && isString(content[1]))
152
50
  logo = content[1];
153
51
  }
@@ -157,43 +55,17 @@ function matchLogoByLinkOrMetaTag(htmlString) {
157
55
  /**
158
56
  * Parses HTML string to extract structured metadata for link card generation.
159
57
  *
160
- * This is the main parsing function that extracts title, description, and logo
161
- * from an HTML page. It handles both absolute and relative URLs, converting
162
- * relative logo paths to absolute URLs when necessary.
163
- *
164
58
  * @param htmlString - The HTML content to parse
165
- * @param url - The URL of the page (used to resolve relative logo URLs)
166
- * @returns Parsed metadata object, or null if no valid metadata could be extracted
167
- *
168
- * @example
169
- * ```typescript
170
- * const html = '<title>Example</title><meta name="description" content="Test">'
171
- * const metadata = parserMetadata(html, 'https://example.com')
172
- * // Returns: { title: 'Example', description: 'Test', logo: '...' }
173
- * ```
174
- *
175
- * @remarks
176
- * - Returns null if all metadata fields (title, description, logo) are empty
177
- * - Relative logo URLs are converted to absolute URLs using the page's origin
178
- * - Falls back to a default logo if no logo can be found
179
- *
180
- * @todo Handle protocol-relative URLs like `//img.example.com/logo.png`
59
+ * @param url - The URL of the page
60
+ * @returns Parsed metadata object, or null if no valid metadata found
181
61
  */
182
62
  export function parserMetadata(htmlString, url) {
183
- /**
184
- * Converts a potentially relative logo URL to an absolute URL.
185
- *
186
- * @param logo - The logo URL (may be relative or absolute)
187
- * @returns Absolute URL for the logo, or default logo if input is invalid
188
- *
189
- * @internal
190
- */
191
63
  function absolute(logo) {
192
64
  if (!logo)
193
65
  return DEFAULT_LOGO;
194
66
  return extractUrl(logo)
195
67
  ? logo
196
- : `${extractUrl(url)?.origin}${cleanPath(`/${logo}`)}`; // TODO: no match "content='//img.xx.com/a.png'"
68
+ : `${extractUrl(url)?.origin}${cleanPath(`/${logo}`)}`;
197
69
  }
198
70
  const metadata = {
199
71
  title: matchTitleByMetaTag(htmlString),
@@ -205,17 +77,6 @@ export function parserMetadata(htmlString, url) {
205
77
  else
206
78
  return metadata;
207
79
  }
208
- /**
209
- * Checks if an object contains only undefined or empty string values.
210
- *
211
- * This utility function is used to determine if any valid metadata was extracted
212
- * from a page. If all fields are empty, the metadata is considered invalid.
213
- *
214
- * @param obj - Object to check (typically a metadata object)
215
- * @returns true if the object has no non-empty string values, false otherwise
216
- *
217
- * @internal
218
- */
219
80
  function isEmptyStringObject(obj) {
220
81
  return !Object.values(obj).filter((v) => isString(v)).length;
221
82
  }
@@ -1,49 +1,6 @@
1
- /**
2
- * Style generation utilities for link card rendering.
3
- *
4
- * This module provides functions to generate inline CSS styles and class names
5
- * for the link card components. The styles support customizable colors and
6
- * responsive design with text ellipsis for long content.
7
- *
8
- * @module style
9
- */
10
- /**
11
- * Converts a camelCase string to hyphenated kebab-case.
12
- *
13
- * This is used to convert JavaScript style property names (e.g., `backgroundColor`)
14
- * to CSS property names (e.g., `background-color`).
15
- *
16
- * @param str - The camelCase string to convert
17
- * @returns The hyphenated kebab-case string
18
- *
19
- * @example
20
- * ```typescript
21
- * hyphenate('backgroundColor') // Returns: 'background-color'
22
- * hyphenate('fontSize') // Returns: 'font-size'
23
- * ```
24
- *
25
- * @internal
26
- */
27
1
  function hyphenate(str) {
28
2
  return str.replace(/\B([A-Z])/g, '-$1').toLowerCase();
29
3
  }
30
- /**
31
- * Joins style properties into a CSS string.
32
- *
33
- * Converts a JavaScript object of style properties into a semicolon-separated
34
- * CSS string suitable for inline styles.
35
- *
36
- * @param style - Object containing CSS properties and values
37
- * @returns A string of CSS declarations
38
- *
39
- * @example
40
- * ```typescript
41
- * join({ fontSize: '16px', color: 'red' })
42
- * // Returns: 'font-size: 16px; color: red;'
43
- * ```
44
- *
45
- * @internal
46
- */
47
4
  function join(style) {
48
5
  return Object.entries(style)
49
6
  .map(([k, v]) => {
@@ -53,40 +10,9 @@ function join(style) {
53
10
  .filter(Boolean)
54
11
  .join(' ');
55
12
  }
56
- /**
57
- * Wraps CSS declarations in an inline style attribute.
58
- *
59
- * @param style - Object containing CSS properties and values
60
- * @returns A complete HTML style attribute string
61
- *
62
- * @example
63
- * ```typescript
64
- * inlineStyle({ color: 'red', fontSize: '16px' })
65
- * // Returns: 'style="color: red; font-size: 16px;"'
66
- * ```
67
- *
68
- * @internal
69
- */
70
13
  function inlineStyle(style) {
71
14
  return `style="${join(style)}"`;
72
15
  }
73
- /**
74
- * Generates CSS properties for text ellipsis with line clamping.
75
- *
76
- * Creates styles that truncate text after a specified number of lines with
77
- * an ellipsis. Uses both WebKit-specific properties and standard line-clamp.
78
- *
79
- * @param line - Maximum number of lines to display before truncation
80
- * @returns CSS properties object for ellipsis effect
81
- *
82
- * @example
83
- * ```typescript
84
- * ellipsisStyle(2)
85
- * // Returns styles that truncate text after 2 lines with "..."
86
- * ```
87
- *
88
- * @internal
89
- */
90
16
  const ellipsisStyle = (line) => ({
91
17
  '-webkit-box-orient': 'vertical',
92
18
  '-webkit-line-clamp': line,
@@ -100,30 +26,6 @@ const ellipsisStyle = (line) => ({
100
26
  });
101
27
  /**
102
28
  * Generates complete inline styles for all link card components.
103
- *
104
- * Creates a set of inline style strings for each part of the link card:
105
- * - Container: main card box with border and background
106
- * - Image: logo/icon display
107
- * - Texts: wrapper for text content
108
- * - Title: card title (2-line ellipsis)
109
- * - Domain: domain name with underline
110
- * - Description: description text (2-line ellipsis)
111
- *
112
- * The styles are inspired by VitePress's VPFeature component design.
113
- *
114
- * The container uses CSS custom properties for theming:
115
- * - `--vitepress-linkcard-border-color`: Border color (default: #7d7d7dff)
116
- * - `--vitepress-linkcard-bg-color`: Background color (default: transparent)
117
- *
118
- * @returns Object containing style attribute strings for each card component
119
- *
120
- * @example
121
- * ```typescript
122
- * const styles = STYLE()
123
- * // Use in HTML: <div ${styles.container}>...</div>
124
- * ```
125
- *
126
- * @see {@link https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/components/VPFeature.vue | VPFeature component}
127
29
  */
128
30
  export const STYLE = () => ({
129
31
  a: inlineStyle({
@@ -148,14 +50,14 @@ export const STYLE = () => ({
148
50
  img: inlineStyle({
149
51
  borderRadius: '0px 12px 12px 0px',
150
52
  maxWidth: '40%',
151
- height: '128px', // container.height - 2px
53
+ height: '129px',
152
54
  flexShrink: 0,
153
55
  objectFit: 'contain',
154
56
  overflow: 'hidden'
155
57
  }),
156
58
  texts: inlineStyle({
157
59
  flex: '1 1 0%',
158
- minWidth: '0' // ellipsisを有効にするために必要
60
+ minWidth: '0'
159
61
  }),
160
62
  title: inlineStyle({
161
63
  ...ellipsisStyle(2),
@@ -181,29 +83,6 @@ export const STYLE = () => ({
181
83
  margin: '8px 16px 0px 16px'
182
84
  })
183
85
  });
184
- /**
185
- * Generates CSS class names with a custom prefix.
186
- *
187
- * When using the `classPrefix` option, this function creates consistent
188
- * class names for all card components following a BEM-like naming convention.
189
- *
190
- * @param prefix - The prefix to prepend to all class names
191
- * @returns Object mapping component names to their class names
192
- *
193
- * @example
194
- * ```typescript
195
- * const classes = classNames('my-card')
196
- * // Returns: {
197
- * // container: 'my-card__container',
198
- * // title: 'my-card__texts--title',
199
- * // ...
200
- * // }
201
- * ```
202
- *
203
- * @remarks
204
- * When class names are used instead of inline styles, you must provide
205
- * your own CSS definitions for these classes.
206
- */
207
86
  export const classNames = (prefix) => ({
208
87
  container: `${prefix}__container`,
209
88
  img: `${prefix}__img`,
@@ -1,26 +1,8 @@
1
- /**
2
- * URL utility functions for parsing and manipulating URLs.
3
- *
4
- * @module url
5
- */
6
1
  /**
7
2
  * Parses a URL string and returns a URL object.
8
3
  *
9
- * This is a wrapper around the native URL constructor that provides
10
- * a consistent interface for URL parsing throughout the codebase.
11
- *
12
4
  * @param url - The URL string to parse
13
5
  * @returns A URL object containing the parsed components
14
- * @throws {TypeError} If the URL string is invalid
15
- *
16
- * @example
17
- * ```typescript
18
- * const urlObj = extractUrl('https://example.com/path')
19
- * console.log(urlObj.origin) // "https://example.com"
20
- * console.log(urlObj.pathname) // "/path"
21
- * ```
22
- *
23
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/URL | MDN - URL}
24
6
  */
25
7
  export function extractUrl(url) {
26
8
  return new URL(url);
@@ -28,17 +10,8 @@ export function extractUrl(url) {
28
10
  /**
29
11
  * Removes duplicate consecutive slashes from a path string.
30
12
  *
31
- * This function normalizes paths by replacing multiple consecutive slashes
32
- * with a single slash. Useful for constructing clean URLs from path segments.
33
- *
34
13
  * @param path - The path string to clean
35
14
  * @returns The cleaned path with no consecutive slashes
36
- *
37
- * @example
38
- * ```typescript
39
- * cleanPath('/path//to///file') // Returns: '/path/to/file'
40
- * cleanPath('//images//logo.png') // Returns: '/images/logo.png'
41
- * ```
42
15
  */
43
16
  export function cleanPath(path) {
44
17
  return path.replace(/\/\//g, '/');
@@ -1,52 +1,13 @@
1
- /**
2
- * XHR module for fetching remote URL content.
3
- *
4
- * This module provides both synchronous and asynchronous HTTP GET request functionality
5
- * with built-in caching. It works in both browser and Node.js environments by using
6
- * the appropriate XMLHttpRequest implementation.
7
- *
8
- * @module xhr
9
- * @todo Replace xmlhttprequest with a modern alternative (e.g., node-fetch or axios)
10
- */
11
- // Refactor: xmlhttprequest will be replaced later
12
1
  // @ts-expect-error: xmlhttprequest has no types
13
2
  import xhrForNode from 'xmlhttprequest';
14
3
  import { inBrowser, isString } from '@luckrya/utility';
15
- /**
16
- * In-memory cache for storing fetched HTML content by URL.
17
- * Prevents redundant network requests for the same URLs.
18
- * @internal
19
- */
20
4
  const cache = new Map();
21
- /**
22
- * XMLHttpRequest implementation that works in both browser and Node.js.
23
- * @internal
24
- */
25
5
  const XHR = inBrowser ? window.XMLHttpRequest : xhrForNode.XMLHttpRequest;
26
6
  /**
27
7
  * Performs a synchronous HTTP GET request to fetch HTML content from a URL.
28
8
  *
29
- * This function fetches the content synchronously, which means it blocks execution
30
- * until the request completes. The response is cached to avoid redundant requests.
31
- *
32
9
  * @param url - The URL to fetch content from
33
10
  * @returns The HTML content as a string, or undefined if the request fails
34
- *
35
- * @example
36
- * ```typescript
37
- * const html = sync('https://example.com')
38
- * if (html) {
39
- * console.log('Fetched HTML content')
40
- * }
41
- * ```
42
- *
43
- * @remarks
44
- * - Returns cached content if available
45
- * - Only returns content if HTTP status is 200
46
- * - Errors are logged to console but not thrown
47
- * - Synchronous requests block the event loop - use with caution
48
- *
49
- * @see {@link async} for an asynchronous alternative
50
11
  */
51
12
  export function sync(url) {
52
13
  if (cache.has(url))
@@ -70,33 +31,8 @@ export function sync(url) {
70
31
  /**
71
32
  * Performs an asynchronous HTTP GET request to fetch HTML content from a URL.
72
33
  *
73
- * This function fetches content asynchronously using Promises. The response is
74
- * cached to avoid redundant requests.
75
- *
76
34
  * @param url - The URL to fetch content from
77
- * @returns A Promise that resolves to the HTML content string, or undefined if the request fails
78
- *
79
- * @example
80
- * ```typescript
81
- * async function fetchPage() {
82
- * try {
83
- * const html = await async('https://example.com')
84
- * if (html) {
85
- * console.log('Fetched HTML content')
86
- * }
87
- * } catch (error) {
88
- * console.error('Failed to fetch:', error)
89
- * }
90
- * }
91
- * ```
92
- *
93
- * @remarks
94
- * - Returns cached content if available
95
- * - Only resolves with content if HTTP status is 200 and readyState is 4
96
- * - Promise is rejected if the request throws an error
97
- * - Preferred over `sync()` for non-blocking operations
98
- *
99
- * @see {@link sync} for a synchronous alternative
35
+ * @returns A Promise that resolves to the HTML content string
100
36
  */
101
37
  export function async(url) {
102
38
  return new Promise((resolve, reject) => {
@@ -3,45 +3,10 @@ import { getUrlMetadata, generateCardDomFragment } from './assemble';
3
3
  /**
4
4
  * Markdown-it plugin that converts specially-formatted links into rich link preview cards.
5
5
  *
6
- * This plugin intercepts markdown links that start with the `@:` prefix and transforms them
7
- * into interactive cards displaying metadata (title, description, logo) fetched from the target URL.
8
- *
9
- * ## Usage
10
- *
11
- * In your markdown file:
12
- * ```md
13
- * [@:https://example.com](Link Title)
14
- * ```
15
- *
16
- * Plugin configuration:
17
- * ```typescript
18
- * import MarkdownIt from 'markdown-it'
19
- * import { linkToCardPlugin } from 'vitepress-linkcard'
20
- *
21
- * const md = new MarkdownIt()
22
- * md.use(linkToCardPlugin, {
23
- * target: '_blank'
24
- * })
25
- * ```
26
- *
27
6
  * @param md - The markdown-it instance
28
7
  * @param pluginOptions - Configuration options for the plugin
29
- *
30
- * @see {@link LinkToCardPluginOptions} for available options
31
8
  */
32
9
  export const linkToCardPlugin = (md, pluginOptions = {}) => {
33
- /**
34
- * Parses a link href to determine if it's a card link and extracts the URL.
35
- *
36
- * Card links are identified by the `@:` prefix (e.g., `@:https://example.com`).
37
- *
38
- * @param href - The href attribute from a markdown link token
39
- * @returns An object containing:
40
- * - `isCardLink`: true if the href matches the card link pattern
41
- * - `url`: the extracted URL (without the `@:` prefix)
42
- *
43
- * @internal
44
- */
45
10
  function parseCardLinkHref(href) {
46
11
  const tagRegexp = new RegExp(`^(${'@'}:)([a-zA-Z0-9]+.*)`);
47
12
  const match = href?.match(tagRegexp);
@@ -50,27 +15,10 @@ export const linkToCardPlugin = (md, pluginOptions = {}) => {
50
15
  url: match?.[2]
51
16
  };
52
17
  }
53
- /**
54
- * Assembles the HTML template for a link card by fetching metadata and rendering.
55
- *
56
- * This function:
57
- * 1. Fetches URL metadata (title, description, logo)
58
- * 2. Hides remaining tokens in the link to prevent duplicate content
59
- * 3. Extracts the link title from tokens
60
- * 4. Renders the card using either a custom renderer or the default one
61
- *
62
- * @param options - Contains the URL and token information
63
- * @param options.url - The URL to create a card for
64
- * @param options.tokens - Array of markdown-it tokens
65
- * @param options.i - Current token index
66
- * @returns HTML string of the rendered card, or undefined if metadata cannot be fetched
67
- *
68
- * @internal
69
- */
70
18
  function assembleCardTpl(options) {
71
19
  const urlMetadata = getUrlMetadata(options.url);
72
20
  if (urlMetadata) {
73
- ignoreRestToken(options.tokens, options.i); // linkTitle 依赖 ignoreRestToken 的处理结果
21
+ ignoreRestToken(options.tokens, options.i);
74
22
  const cardDomOptions = {
75
23
  href: options.url,
76
24
  linkTitle: joinLinkTitle(options.tokens),
@@ -82,19 +30,6 @@ export const linkToCardPlugin = (md, pluginOptions = {}) => {
82
30
  : generateCardDomFragment(urlMetadata, cardDomOptions);
83
31
  }
84
32
  }
85
- /**
86
- * Custom inline renderer that preserves hidden token handling.
87
- *
88
- * This overrides the default markdown-it inline renderer to properly handle
89
- * tokens marked as hidden, which is necessary for the card link processing.
90
- *
91
- * @param tokens - Array of tokens to render
92
- * @param rootOptions - Markdown-it rendering options
93
- * @param env - Markdown-it environment variables
94
- * @returns The rendered HTML string
95
- *
96
- * @see {@link https://markdown-it.github.io/markdown-it/#MarkdownIt.renderInline | MarkdownIt.renderInline}
97
- */
98
33
  md.renderer.renderInline = (tokens, rootOptions, env) => {
99
34
  let result = '';
100
35
  for (let i = 0; i < tokens.length; i++) {
@@ -112,24 +47,6 @@ export const linkToCardPlugin = (md, pluginOptions = {}) => {
112
47
  }
113
48
  return result;
114
49
  };
115
- /**
116
- * Custom renderer for link_open tokens that intercepts card links.
117
- *
118
- * This function checks if a link is a card link (prefixed with `@:`) and if so,
119
- * generates and returns the card HTML. Regular links are passed through to the
120
- * default renderer.
121
- *
122
- * @param tokens - Array of tokens being rendered
123
- * @param i - Current token index
124
- * @param rootOptions - Markdown-it rendering options
125
- * @param env - Markdown-it environment (must be present even if unused to maintain signature)
126
- * @param self - The renderer instance
127
- * @returns HTML string for the link (either a card or a regular link)
128
- *
129
- * @remarks
130
- * The `env` parameter must not be removed even if unused, as it's part of the
131
- * markdown-it renderer signature.
132
- */
133
50
  md.renderer.rules.link_open = (tokens, i, rootOptions, env, self) => {
134
51
  const token = tokens[i];
135
52
  const isLinkOpenToken = token.tag === 'a' && token.type === 'link_open';
@@ -143,38 +60,12 @@ export const linkToCardPlugin = (md, pluginOptions = {}) => {
143
60
  return self.renderToken(tokens, i, rootOptions);
144
61
  };
145
62
  };
146
- /**
147
- * Marks all tokens except the one at index `i` as hidden.
148
- *
149
- * This is used to prevent the link text and closing tag from being rendered
150
- * when a card link is detected, as the card HTML replaces the entire link element.
151
- *
152
- * @param tokens - Array of tokens to modify
153
- * @param i - Index of the token to keep visible
154
- *
155
- * @todo Handle softbreak tokens properly
156
- * @see {@link https://markdown-it.github.io/ | markdown-it documentation}
157
- *
158
- * @internal
159
- */
160
63
  function ignoreRestToken(tokens, i) {
161
64
  tokens.forEach((token, index) => {
162
65
  if (index !== i)
163
66
  token.hidden = true;
164
67
  });
165
68
  }
166
- /**
167
- * Extracts and joins the content from hidden tokens to form the link title.
168
- *
169
- * When processing a card link, the link text tokens are marked as hidden.
170
- * This function collects the content from those hidden tokens to use as the
171
- * card's title attribute.
172
- *
173
- * @param tokens - Array of tokens to extract content from
174
- * @returns The concatenated content from all hidden tokens
175
- *
176
- * @internal
177
- */
178
69
  function joinLinkTitle(tokens) {
179
70
  return tokens
180
71
  .map(({ hidden, content }) => {