@fsegurai/marked-extended-comments 17.0.0-beta.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.
@@ -0,0 +1,147 @@
1
+ import type { Token, Tokens } from 'marked';
2
+ /**
3
+ * Comment types
4
+ */
5
+ export type CommentType = 'note' | 'question' | 'suggestion' | 'issue' | 'internal' | 'review' | 'todo';
6
+ /**
7
+ * Comment visibility levels
8
+ */
9
+ export type CommentVisibility = 'public' | 'private' | 'dev-only' | 'editors-only';
10
+ /**
11
+ * Comment status
12
+ */
13
+ export type CommentStatus = 'open' | 'resolved' | 'archived';
14
+ /**
15
+ * Metadata for a comment
16
+ */
17
+ export interface CommentMeta {
18
+ author?: string;
19
+ date?: string;
20
+ type?: CommentType;
21
+ visibility?: CommentVisibility;
22
+ status?: CommentStatus;
23
+ priority?: 'low' | 'medium' | 'high';
24
+ tags?: string;
25
+ id?: string;
26
+ replyTo?: string;
27
+ resolved?: string;
28
+ className?: string;
29
+ [key: string]: string | undefined;
30
+ }
31
+ /**
32
+ * Token for inline comments
33
+ */
34
+ export interface InlineCommentToken extends Tokens.Generic {
35
+ type: 'inlineComment';
36
+ raw: string;
37
+ text: string;
38
+ meta: CommentMeta;
39
+ tokens?: Token[];
40
+ }
41
+ /**
42
+ * Token for block comments
43
+ */
44
+ export interface BlockCommentToken extends Tokens.Generic {
45
+ type: 'blockComment';
46
+ raw: string;
47
+ text: string;
48
+ meta: CommentMeta;
49
+ tokens?: Token[];
50
+ }
51
+ /**
52
+ * Options for the comment extension
53
+ */
54
+ export interface CommentOptions {
55
+ /**
56
+ * CSS class prefix for comment elements
57
+ * @default 'marked-extended-comment'
58
+ */
59
+ className?: string;
60
+ /**
61
+ * Prefix for generating unique IDs
62
+ * @default 'comment'
63
+ */
64
+ prefixId?: string;
65
+ /**
66
+ * Show comments by default
67
+ * @default true
68
+ */
69
+ showComments?: boolean;
70
+ /**
71
+ * Enable inline comments
72
+ * @default true
73
+ */
74
+ enableInlineComments?: boolean;
75
+ /**
76
+ * Enable block comments
77
+ * @default true
78
+ */
79
+ enableBlockComments?: boolean;
80
+ /**
81
+ * Show comment metadata (author, date, etc.)
82
+ * @default true
83
+ */
84
+ showMetadata?: boolean;
85
+ /**
86
+ * Show resolved comments
87
+ * @default false
88
+ */
89
+ showResolvedComments?: boolean;
90
+ /**
91
+ * Default comment type
92
+ * @default 'note'
93
+ */
94
+ defaultType?: CommentType;
95
+ /**
96
+ * Default visibility
97
+ * @default 'public'
98
+ */
99
+ defaultVisibility?: CommentVisibility;
100
+ /**
101
+ * Filter comments by visibility
102
+ * @default []
103
+ */
104
+ visibilityFilter?: CommentVisibility[];
105
+ /**
106
+ * Filter comments by type
107
+ * @default []
108
+ */
109
+ typeFilter?: CommentType[];
110
+ /**
111
+ * Custom template for inline comments
112
+ */
113
+ inlineTemplate?: string;
114
+ /**
115
+ * Custom template for block comments
116
+ */
117
+ blockTemplate?: string;
118
+ /**
119
+ * Inject CSS styles
120
+ * @default true
121
+ */
122
+ injectStyles?: boolean;
123
+ /**
124
+ * Custom callback to modify comment tokens
125
+ */
126
+ customizeToken?: (token: InlineCommentToken | BlockCommentToken) => void;
127
+ /**
128
+ * Callback when comment visibility changes
129
+ */
130
+ onVisibilityChange?: (commentId: string, visible: boolean) => void;
131
+ /**
132
+ * Callback when comment is resolved/unresolved
133
+ */
134
+ onStatusChange?: (commentId: string, status: CommentStatus) => void;
135
+ }
136
+ /**
137
+ * Renderer options for comments
138
+ */
139
+ export interface CommentRendererOptions {
140
+ commentId: string;
141
+ meta: CommentMeta;
142
+ content: string;
143
+ isInline: boolean;
144
+ className: string;
145
+ showMetadata: boolean;
146
+ template?: string;
147
+ }
@@ -0,0 +1,29 @@
1
+ import type { CommentOptions } from '../types';
2
+ /**
3
+ * Default options for the comment extension
4
+ */
5
+ export declare const DEFAULT_OPTIONS: Required<Omit<CommentOptions, 'customizeToken' | 'onVisibilityChange' | 'onStatusChange' | 'inlineTemplate' | 'blockTemplate' | 'visibilityFilter' | 'typeFilter'>>;
6
+ /**
7
+ * Comment type SVG icons
8
+ */
9
+ export declare const COMMENT_ICONS: Record<string, string>;
10
+ /**
11
+ * Comment type labels
12
+ */
13
+ export declare const COMMENT_LABELS: Record<string, string>;
14
+ /**
15
+ * Default template for inline comments
16
+ */
17
+ export declare const DEFAULT_INLINE_TEMPLATE = "\n<span class=\"{className} {className}-inline {className}-type-{type} {className}-status-{status} {visibilityClass}\" \n id=\"{commentId}\"\n data-comment-type=\"{type}\"\n data-comment-status=\"{status}\"\n data-comment-visibility=\"{visibility}\"\n data-comment-author=\"{author}\"\n data-comment-date=\"{date}\"\n role=\"note\"\n aria-label=\"Comment: {text}\"\n title=\"{tooltipText}\">\n <span class=\"{className}-content\">{content}</span>\n <span class=\"{className}-indicator\" aria-hidden=\"true\">{icon}</span>\n</span>\n";
18
+ /**
19
+ * Default template for block comments
20
+ */
21
+ export declare const DEFAULT_BLOCK_TEMPLATE = "\n<aside class=\"{className} {className}-block {className}-type-{type} {className}-status-{status} {visibilityClass} {priorityClass}\"\n id=\"{commentId}\"\n data-comment-type=\"{type}\"\n data-comment-status=\"{status}\"\n data-comment-visibility=\"{visibility}\"\n data-comment-author=\"{author}\"\n data-comment-date=\"{date}\"\n role=\"complementary\"\n aria-label=\"Comment by {author}\">\n <div class=\"{className}-header\">\n <span class=\"{className}-icon\" aria-hidden=\"true\">{icon}</span>\n <span class=\"{className}-type-label\">{typeLabel}</span>\n {metadata}\n </div>\n <div class=\"{className}-body\">\n {content}\n </div>\n</aside>\n";
22
+ /**
23
+ * Metadata template
24
+ */
25
+ export declare const METADATA_TEMPLATE = "\n<div class=\"{className}-metadata\">\n {authorInfo}\n {dateInfo}\n {tagsInfo}\n {priorityInfo}\n</div>\n";
26
+ /**
27
+ * Default structural styles for comments (minimal, layout-only)
28
+ */
29
+ export declare const DEFAULT_STYLES = "\n.marked-extended-comment { position: relative; display: inline-block; }\n.marked-extended-comment-inline { display: inline; }\n.marked-extended-comment-indicator { position: absolute; top: -8px; right: -8px; font-size: 12px; line-height: 1; }\n.marked-extended-comment-block { display: block; margin: 1rem 0; }\n.marked-extended-comment-header { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.5rem; }\n.marked-extended-comment-icon { line-height: 1; }\n.marked-extended-comment-body { margin: 0.5rem 0; }\n.marked-extended-comment-metadata { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 0.5rem; }\n.marked-extended-comment-metadata > span { display: flex; align-items: center; gap: 0.25rem; }\n.marked-extended-comment.hidden { display: none; }\n";
@@ -0,0 +1,30 @@
1
+ export declare function generateUniqueId(prefix?: string): string;
2
+ /**
3
+ * Reset the comment counter (useful for testing)
4
+ */
5
+ export declare function resetCommentCounter(): void;
6
+ /**
7
+ * Parse property string like 'author="John" type="note"'
8
+ */
9
+ export declare function parsePropString(propString: string): Record<string, string>;
10
+ /**
11
+ * Replace template placeholders with values
12
+ */
13
+ export declare function replaceTemplatePlaceholders(template: string, values: Record<string, string>): string;
14
+ /**
15
+ * Format date string
16
+ */
17
+ export declare function formatDate(dateString?: string): string;
18
+ /**
19
+ * Check if comment should be shown based on visibility and filters
20
+ */
21
+ export declare function shouldShowComment(visibility?: string, status?: string, type?: string, visibilityFilter?: string[], typeFilter?: string[], showResolved?: boolean): boolean;
22
+ /**
23
+ * Build metadata HTML for comments
24
+ */
25
+ export declare function buildMetadataHtml(author?: string, date?: string, tags?: string, priority?: string, className?: string): {
26
+ authorInfo: string;
27
+ dateInfo: string;
28
+ tagsInfo: string;
29
+ priorityInfo: string;
30
+ };
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Ensure styles are injected into the document
3
+ */
4
+ export declare function ensureStyles(id: string, styles: string): void;
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@fsegurai/marked-extended-comments",
3
+ "version": "17.0.0-beta.0",
4
+ "description": "Marked extension for collaborative comments and annotations",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.esm.js",
8
+ "browser": "./dist/index.umd.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "import": "./dist/index.esm.js",
13
+ "require": "./dist/index.cjs",
14
+ "types": "./dist/index.d.ts"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist/",
19
+ "src/"
20
+ ],
21
+ "keywords": [
22
+ "extensions",
23
+ "javascript",
24
+ "library",
25
+ "markdown",
26
+ "markedjs",
27
+ "marked-extended-comments",
28
+ "parser",
29
+ "syntax",
30
+ "typescript"
31
+ ],
32
+ "author": {
33
+ "name": "Fabián Segura",
34
+ "url": "https://www.fsegurai.com/"
35
+ },
36
+ "license": "MIT",
37
+ "scripts": {
38
+ "prepare": "rollup -c rollup.config.js",
39
+ "test": "bun test",
40
+ "test:coverage": "bun test --coverage"
41
+ },
42
+ "peerDependencies": {
43
+ "marked": ">=17.0.0"
44
+ },
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "git+https://github.com/fsegurai/marked-extensions.git"
48
+ },
49
+ "bugs": {
50
+ "url": "https://github.com/fsegurai/marked-extensions/issues"
51
+ },
52
+ "homepage": "https://github.com/fsegurai/marked-extensions#readme"
53
+ }
@@ -0,0 +1,13 @@
1
+ declare global {
2
+ interface Window {
3
+ __injectedStyles__?: Record<string, boolean>;
4
+ }
5
+ }
6
+
7
+ declare module 'marked' {
8
+ interface TokensList {
9
+ kanban?: import('./types').CommentType;
10
+ }
11
+ }
12
+
13
+ export {};
package/src/index.ts ADDED
@@ -0,0 +1,103 @@
1
+ import type { Token } from 'marked';
2
+ import { ensureStyles } from './utils/inject-styles';
3
+ import { DEFAULT_OPTIONS, DEFAULT_STYLES } from './utils/constants';
4
+ import {
5
+ createBlockCommentRenderer,
6
+ createBlockCommentTokenizer,
7
+ createInlineCommentRenderer,
8
+ createInlineCommentTokenizer,
9
+ } from './tokenizer';
10
+ import type { BlockCommentToken, CommentOptions, InlineCommentToken } from './types';
11
+
12
+ // Export types for external use
13
+ export type {
14
+ CommentOptions,
15
+ InlineCommentToken,
16
+ BlockCommentToken,
17
+ CommentMeta,
18
+ CommentType,
19
+ CommentVisibility,
20
+ CommentStatus,
21
+ } from './types';
22
+
23
+ /**
24
+ * Marked Extended Comments extension
25
+ * Adds support for inline and block comments with rich metadata
26
+ *
27
+ * @example
28
+ * ```markdown
29
+ * // Inline comment
30
+ * This text has :::comment{author="Alice" type="note"}a comment::: here.
31
+ *
32
+ * // Block comment
33
+ * ::::comment{author="Bob" type="review" status="open"}
34
+ * This section needs revision.
35
+ * ::::commentend
36
+ * ```
37
+ *
38
+ * @param options - Configuration options
39
+ * @returns Marked extension object
40
+ */
41
+ export default function markedExtendedComments(options: CommentOptions = {}) {
42
+ // Merge options with defaults
43
+ const config = { ...DEFAULT_OPTIONS, ...options } as Required<CommentOptions>;
44
+
45
+ // Inject styles if needed
46
+ if (config.injectStyles) {
47
+ ensureStyles('marked-extended-comments-styles', DEFAULT_STYLES);
48
+ }
49
+
50
+ // Return the extension
51
+ return {
52
+ walkTokens(token: Token) {
53
+ // Handle inline comments
54
+ if (token.type === 'inlineComment') {
55
+ const commentToken = token as InlineCommentToken;
56
+
57
+ // Apply custom token modifications if configured
58
+ if (config.customizeToken && typeof config.customizeToken === 'function') {
59
+ config.customizeToken(commentToken);
60
+ }
61
+
62
+ // Trigger visibility change callback
63
+ if (config.onVisibilityChange && typeof config.onVisibilityChange === 'function') {
64
+ const commentId = commentToken.meta.id || `${config.prefixId}-${Date.now()}`;
65
+ const visible = config.showComments
66
+ && (commentToken.meta.status !== 'resolved' || config.showResolvedComments);
67
+ config.onVisibilityChange(commentId, visible);
68
+ }
69
+ }
70
+
71
+ // Handle block comments
72
+ if (token.type === 'blockComment') {
73
+ const commentToken = token as BlockCommentToken;
74
+
75
+ // Apply custom token modifications if configured
76
+ if (config.customizeToken && typeof config.customizeToken === 'function') {
77
+ config.customizeToken(commentToken);
78
+ }
79
+
80
+ // Trigger visibility change callback
81
+ if (config.onVisibilityChange && typeof config.onVisibilityChange === 'function') {
82
+ const commentId = commentToken.meta.id || `${config.prefixId}-${Date.now()}`;
83
+ const visible = config.showComments
84
+ && (commentToken.meta.status !== 'resolved' || config.showResolvedComments);
85
+ config.onVisibilityChange(commentId, visible);
86
+ }
87
+
88
+ // Trigger status change callback
89
+ if (config.onStatusChange && typeof config.onStatusChange === 'function' && commentToken.meta.status) {
90
+ const commentId = commentToken.meta.id || `${config.prefixId}-${Date.now()}`;
91
+ config.onStatusChange(commentId, commentToken.meta.status);
92
+ }
93
+ }
94
+ },
95
+ extensions: [
96
+ createInlineCommentTokenizer(config),
97
+ createBlockCommentTokenizer(config),
98
+ createInlineCommentRenderer(config),
99
+ createBlockCommentRenderer(config),
100
+ ],
101
+ };
102
+ }
103
+
@@ -0,0 +1,126 @@
1
+ import type { Token } from 'marked';
2
+ import {
3
+ COMMENT_ICONS,
4
+ COMMENT_LABELS,
5
+ DEFAULT_BLOCK_TEMPLATE,
6
+ DEFAULT_INLINE_TEMPLATE,
7
+ METADATA_TEMPLATE,
8
+ } from './utils/constants';
9
+ import { buildMetadataHtml, replaceTemplatePlaceholders } from './utils/helpers';
10
+ import type { CommentMeta } from './types';
11
+
12
+ /**
13
+ * Render options for comments
14
+ */
15
+ interface RenderOptions {
16
+ commentId: string;
17
+ meta: CommentMeta;
18
+ text: string;
19
+ tokens: Token[];
20
+ parser: { parse: (tokens: Token[]) => string; parseInline: (tokens: Token[]) => string };
21
+ className: string;
22
+ template?: string;
23
+ showMetadata: boolean;
24
+ }
25
+
26
+ /**
27
+ * Render an inline comment
28
+ */
29
+ export function renderInlineComment(options: RenderOptions): string {
30
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
31
+ const { commentId, meta, text, className, template, showMetadata } = options;
32
+
33
+ const type = meta.type || 'note';
34
+ const status = meta.status || 'open';
35
+ const visibility = meta.visibility || 'public';
36
+ const icon = COMMENT_ICONS[type] || '📝';
37
+
38
+ // Build visibility class
39
+ const visibilityClass = visibility !== 'public' ? `${className}-visibility-${visibility}` : '';
40
+
41
+ // Build tooltip text
42
+ const tooltipParts: string[] = [];
43
+ if (meta.author) tooltipParts.push(`Author: ${meta.author}`);
44
+ if (meta.type) tooltipParts.push(`Type: ${COMMENT_LABELS[meta.type] || meta.type}`);
45
+ if (meta.date) tooltipParts.push(`Date: ${meta.date}`);
46
+ const tooltipText = tooltipParts.join(' | ');
47
+
48
+ const templateToUse = template || DEFAULT_INLINE_TEMPLATE;
49
+
50
+ return replaceTemplatePlaceholders(templateToUse, {
51
+ className,
52
+ commentId,
53
+ type,
54
+ status,
55
+ visibility,
56
+ visibilityClass,
57
+ author: meta.author || '',
58
+ date: meta.date || '',
59
+ text,
60
+ content: text,
61
+ icon,
62
+ tooltipText,
63
+ });
64
+ }
65
+
66
+ /**
67
+ * Render a block comment
68
+ */
69
+ export function renderBlockComment(options: RenderOptions): string {
70
+ const { commentId, meta, tokens, parser, className, template, showMetadata } = options;
71
+
72
+ const type = meta.type || 'note';
73
+ const status = meta.status || 'open';
74
+ const visibility = meta.visibility || 'public';
75
+ const priority = meta.priority || '';
76
+ const icon = COMMENT_ICONS[type] || '📝';
77
+ const typeLabel = COMMENT_LABELS[type] || type;
78
+
79
+ // Build visibility class
80
+ const visibilityClass = visibility !== 'public' ? `${className}-visibility-${visibility}` : '';
81
+
82
+ // Build priority class
83
+ const priorityClass = priority ? `${className}-priority-${priority}` : '';
84
+
85
+ // Parse comment content (nested markdown)
86
+ const content = parser.parse(tokens);
87
+
88
+ // Build metadata HTML
89
+ let metadata = '';
90
+ if (showMetadata) {
91
+ const { authorInfo, dateInfo, tagsInfo, priorityInfo } = buildMetadataHtml(
92
+ meta.author,
93
+ meta.date,
94
+ meta.tags,
95
+ meta.priority,
96
+ className,
97
+ );
98
+
99
+ metadata = replaceTemplatePlaceholders(METADATA_TEMPLATE, {
100
+ className,
101
+ authorInfo,
102
+ dateInfo,
103
+ tagsInfo,
104
+ priorityInfo,
105
+ });
106
+ }
107
+
108
+ const templateToUse = template || DEFAULT_BLOCK_TEMPLATE;
109
+
110
+ return replaceTemplatePlaceholders(templateToUse, {
111
+ className,
112
+ commentId,
113
+ type,
114
+ status,
115
+ visibility,
116
+ visibilityClass,
117
+ priorityClass,
118
+ author: meta.author || '',
119
+ date: meta.date || '',
120
+ icon,
121
+ typeLabel,
122
+ content,
123
+ metadata,
124
+ });
125
+ }
126
+
@@ -0,0 +1,161 @@
1
+ import type { RendererExtension, TokenizerExtension } from 'marked';
2
+ import { generateUniqueId, parsePropString } from './utils/helpers';
3
+ import { renderBlockComment, renderInlineComment } from './renderer';
4
+ import type { BlockCommentToken, CommentMeta, CommentOptions, InlineCommentToken } from './types';
5
+
6
+ /**
7
+ * Create an inline comment tokenizer
8
+ */
9
+ export function createInlineCommentTokenizer(options: Required<Omit<CommentOptions, 'customizeToken' | 'onVisibilityChange' | 'onStatusChange' | 'inlineTemplate' | 'blockTemplate' | 'visibilityFilter' | 'typeFilter'>>): TokenizerExtension {
10
+ return {
11
+ name: 'inlineComment',
12
+ level: 'inline',
13
+ start(src: string) {
14
+ return src.indexOf(':::comment');
15
+ },
16
+ tokenizer(src: string): InlineCommentToken | undefined {
17
+ // Regex for inline comments: :::comment{props}text:::
18
+ const match = src.match(/^:::comment(?:\{([^}]*)\})?([^:]+):::/);
19
+
20
+ if (!match) return undefined;
21
+
22
+ const [raw, propString = '', text] = match;
23
+
24
+ // Parse properties
25
+ const props = parsePropString(propString);
26
+
27
+ const meta: CommentMeta = {
28
+ author: props['author'],
29
+ date: props['date'],
30
+ type: (props['type'] as CommentMeta['type']) || options.defaultType,
31
+ visibility: (props['visibility'] as CommentMeta['visibility']) || options.defaultVisibility,
32
+ status: (props['status'] as CommentMeta['status']) || 'open',
33
+ priority: props['priority'] as CommentMeta['priority'],
34
+ tags: props['tags'],
35
+ id: props['id'],
36
+ className: props['className'],
37
+ };
38
+
39
+ return {
40
+ type: 'inlineComment',
41
+ raw,
42
+ text: text.trim(),
43
+ meta,
44
+ };
45
+ },
46
+ };
47
+ }
48
+
49
+ /**
50
+ * Create block comment tokenizer
51
+ */
52
+ export function createBlockCommentTokenizer(options: Required<Omit<CommentOptions, 'customizeToken' | 'onVisibilityChange' | 'onStatusChange' | 'inlineTemplate' | 'blockTemplate' | 'visibilityFilter' | 'typeFilter'>>): TokenizerExtension {
53
+ return {
54
+ name: 'blockComment',
55
+ level: 'block',
56
+ tokenizer(src: string): BlockCommentToken | undefined {
57
+ // Regex for block comments: ::::comment{props}\ncontent\n::::commentend
58
+ const match = src.match(/^::::comment(?:\{([^}]*)\})?\s*\n([\s\S]*?)::::commentend/);
59
+
60
+ if (!match) return undefined;
61
+
62
+ const [raw, propString = '', content] = match;
63
+
64
+ // Parse properties
65
+ const props = parsePropString(propString);
66
+
67
+ const meta: CommentMeta = {
68
+ author: props['author'],
69
+ date: props['date'],
70
+ type: (props['type'] as CommentMeta['type']) || options.defaultType,
71
+ visibility: (props['visibility'] as CommentMeta['visibility']) || options.defaultVisibility,
72
+ status: (props['status'] as CommentMeta['status']) || 'open',
73
+ priority: props['priority'] as CommentMeta['priority'],
74
+ tags: props['tags'],
75
+ id: props['id'],
76
+ replyTo: props['replyTo'],
77
+ className: props['className'],
78
+ };
79
+
80
+ // Tokenize the content
81
+ const tokens = this.lexer.blockTokens(content.trim());
82
+
83
+ return {
84
+ type: 'blockComment',
85
+ raw,
86
+ text: content.trim(),
87
+ meta,
88
+ tokens,
89
+ };
90
+ },
91
+ };
92
+ }
93
+
94
+ /**
95
+ * Create renderer for inline comments
96
+ */
97
+ export function createInlineCommentRenderer(
98
+ options: Required<Omit<CommentOptions, 'customizeToken' | 'onVisibilityChange' | 'onStatusChange' | 'visibilityFilter' | 'typeFilter'>>,
99
+ ): RendererExtension {
100
+ return {
101
+ name: 'inlineComment',
102
+ renderer(token): string {
103
+ const commentToken = token as InlineCommentToken;
104
+ const meta = commentToken.meta;
105
+
106
+ // Check if comment should be shown
107
+ if (!options.showComments) return commentToken.text;
108
+ if (!options.enableInlineComments) return commentToken.text;
109
+ if (meta.status === 'resolved' && !options.showResolvedComments) return commentToken.text;
110
+
111
+ // Generate unique ID
112
+ const commentId = meta.id || generateUniqueId(options.prefixId);
113
+
114
+ return renderInlineComment({
115
+ commentId,
116
+ meta,
117
+ text: commentToken.text,
118
+ tokens: [],
119
+ parser: this.parser,
120
+ className: meta.className || options.className,
121
+ template: options.inlineTemplate,
122
+ showMetadata: options.showMetadata,
123
+ });
124
+ },
125
+ };
126
+ }
127
+
128
+ /**
129
+ * Create renderer for block comments
130
+ */
131
+ export function createBlockCommentRenderer(
132
+ options: Required<Omit<CommentOptions, 'customizeToken' | 'onVisibilityChange' | 'onStatusChange' | 'visibilityFilter' | 'typeFilter'>>,
133
+ ): RendererExtension {
134
+ return {
135
+ name: 'blockComment',
136
+ renderer(token): string {
137
+ const commentToken = token as BlockCommentToken;
138
+ const meta = commentToken.meta;
139
+
140
+ // Check if comment should be shown
141
+ if (!options.showComments) return '';
142
+ if (!options.enableBlockComments) return '';
143
+ if (meta.status === 'resolved' && !options.showResolvedComments) return '';
144
+
145
+ // Generate unique ID
146
+ const commentId = meta.id || generateUniqueId(options.prefixId);
147
+
148
+ return renderBlockComment({
149
+ commentId,
150
+ meta,
151
+ text: commentToken.text,
152
+ tokens: commentToken.tokens || [],
153
+ parser: this.parser,
154
+ className: meta.className || options.className,
155
+ template: options.blockTemplate,
156
+ showMetadata: options.showMetadata,
157
+ });
158
+ },
159
+ };
160
+ }
161
+