@adminforth/agent 2.0.10 → 2.2.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.
Files changed (201) hide show
  1. package/README.md +333 -10
  2. package/dist/application/ports.d.ts +66 -0
  3. package/dist/application/runTurnUseCase.d.ts +71 -0
  4. package/dist/application/runTurnUseCase.js +364 -0
  5. package/dist/custom/ChatFooter.vue +58 -30
  6. package/dist/custom/SteerDebugPanel.vue +45 -0
  7. package/dist/custom/SteerQueue.vue +54 -0
  8. package/dist/custom/composables/agentStore/constants.ts +1 -0
  9. package/dist/custom/composables/agentStore/useAgentSessions.ts +35 -16
  10. package/dist/custom/composables/agentStore/useAgentSteerQueue.ts +114 -0
  11. package/dist/custom/composables/useAgentStore.ts +46 -1
  12. package/dist/custom/conversation_area/MessageRenderer.vue +7 -1
  13. package/dist/custom/speech_recognition_frontend/AudioLines.vue +1 -1
  14. package/dist/custom/speech_recognition_frontend/MicrophoneButon.vue +3 -3
  15. package/dist/{agentEvents.d.ts → domain/agentEvents.d.ts} +5 -1
  16. package/dist/domain/steerBuffer.d.ts +33 -0
  17. package/dist/domain/steerBuffer.js +51 -0
  18. package/dist/{agent → domain}/systemPrompt.js +1 -1
  19. package/dist/{agent/turn → domain}/turnTypes.d.ts +41 -37
  20. package/dist/{agent/turn/VegaLiteStreamBuffer.d.ts → domain/vegaLiteStreamBuffer.d.ts} +1 -1
  21. package/dist/index.d.ts +4 -2
  22. package/dist/index.js +49 -35
  23. package/dist/{agent/runtime/AgentContext.d.ts → llm/agentContext.d.ts} +3 -3
  24. package/dist/{agent/simpleAgent.d.ts → llm/agentModels.d.ts} +18 -16
  25. package/dist/llm/agentModels.js +65 -0
  26. package/dist/{agent/runtime/AgentRuntime.d.ts → llm/agentRuntime.d.ts} +14 -2
  27. package/dist/{agent/runtime/AgentRuntime.js → llm/agentRuntime.js} +41 -6
  28. package/dist/llm/langGraphLlm.d.ts +25 -0
  29. package/dist/llm/langGraphLlm.js +54 -0
  30. package/dist/{agent/middleware/apiBasedTools.d.ts → llm/middleware/apiToolsMiddleware.d.ts} +1 -1
  31. package/dist/{agent/middleware/apiBasedTools.js → llm/middleware/apiToolsMiddleware.js} +8 -14
  32. package/dist/{agent → llm}/middleware/sequenceDebug.d.ts +1 -1
  33. package/dist/llm/middleware/steerMiddleware.d.ts +21 -0
  34. package/dist/llm/middleware/steerMiddleware.js +50 -0
  35. package/dist/{agent/models/AgentModelFactory.d.ts → llm/modelFactory.d.ts} +1 -1
  36. package/dist/{agent/models/AgentModelFactory.js → llm/modelFactory.js} +1 -1
  37. package/dist/llm/streamAdapter.d.ts +18 -0
  38. package/dist/llm/streamAdapter.js +117 -0
  39. package/dist/{sessionStore.d.ts → persistence/sessionStore.d.ts} +19 -2
  40. package/dist/{sessionStore.js → persistence/sessionStore.js} +53 -0
  41. package/dist/{agent/tools/AgentToolProvider.d.ts → tools/agentToolProvider.d.ts} +1 -1
  42. package/dist/{agent/tools/AgentToolProvider.js → tools/agentToolProvider.js} +1 -1
  43. package/dist/{apiBasedTools.js → tools/apiBasedTools.js} +0 -3
  44. package/dist/{agent/tools → tools}/apiTool.d.ts +1 -1
  45. package/dist/{agent/tools → tools}/fetchSkill.js +1 -1
  46. package/dist/{agent/tools → tools}/fetchToolSchema.d.ts +1 -1
  47. package/dist/{agent/tools → tools}/index.d.ts +1 -1
  48. package/dist/{agent/tools → tools}/navigateUser.d.ts +3 -3
  49. package/dist/{endpoints → transport/http}/context.d.ts +12 -4
  50. package/dist/transport/http/context.js +1 -0
  51. package/dist/{endpoints/core.js → transport/http/coreEndpoints.js} +23 -3
  52. package/dist/{endpoints/sessions.js → transport/http/sessionEndpoints.js} +36 -1
  53. package/dist/transport/sse/sseWriter.d.ts +29 -0
  54. package/dist/{surfaces/web-sse/createSseEventEmitter.js → transport/sse/sseWriter.js} +27 -0
  55. package/dist/{chatSurfaceService.d.ts → transport/surfaces/chatSurfaceService.d.ts} +3 -3
  56. package/dist/{chatSurfaceService.js → transport/surfaces/chatSurfaceService.js} +7 -5
  57. package/dist/{agent/speech/SpeechTurnService.d.ts → transport/surfaces/speechTurnService.d.ts} +1 -1
  58. package/dist/{agent/speech/SpeechTurnService.js → transport/surfaces/speechTurnService.js} +2 -2
  59. package/dist/types.d.ts +10 -4
  60. package/package.json +11 -2
  61. package/.woodpecker/buildRelease.sh +0 -13
  62. package/.woodpecker/buildSlackNotify.sh +0 -46
  63. package/.woodpecker/release.yml +0 -57
  64. package/agent/checkpointer.ts +0 -329
  65. package/agent/languageDetect.ts +0 -92
  66. package/agent/middleware/apiBasedTools.ts +0 -174
  67. package/agent/middleware/openAiResponsesContinuation.ts +0 -92
  68. package/agent/middleware/sequenceDebug.ts +0 -429
  69. package/agent/models/AgentModeResolver.ts +0 -9
  70. package/agent/models/AgentModelFactory.ts +0 -28
  71. package/agent/runtime/AgentContext.ts +0 -30
  72. package/agent/runtime/AgentRuntime.ts +0 -89
  73. package/agent/simpleAgent.ts +0 -199
  74. package/agent/skills/registry.ts +0 -162
  75. package/agent/speech/SpeechTurnService.ts +0 -179
  76. package/agent/systemPrompt.ts +0 -133
  77. package/agent/toolCallEvents.ts +0 -135
  78. package/agent/tools/AgentToolProvider.ts +0 -28
  79. package/agent/tools/apiTool.ts +0 -66
  80. package/agent/tools/fetchSkill.ts +0 -68
  81. package/agent/tools/fetchToolSchema.ts +0 -47
  82. package/agent/tools/getUserLocation.ts +0 -45
  83. package/agent/tools/index.ts +0 -31
  84. package/agent/tools/navigateUser.ts +0 -210
  85. package/agent/turn/TurnContextBuilder.ts +0 -36
  86. package/agent/turn/TurnLifecycleService.ts +0 -47
  87. package/agent/turn/TurnPersistenceService.ts +0 -33
  88. package/agent/turn/TurnPromptBuilder.ts +0 -51
  89. package/agent/turn/TurnStreamConsumer.ts +0 -69
  90. package/agent/turn/VegaLiteStreamBuffer.ts +0 -90
  91. package/agent/turn/turnTypes.ts +0 -100
  92. package/agentEvents.ts +0 -75
  93. package/agentTurnService.ts +0 -314
  94. package/apiBasedTools.ts +0 -712
  95. package/build.log +0 -67
  96. package/chatSurfaceService.ts +0 -319
  97. package/custom/ChatFooter.vue +0 -173
  98. package/custom/ChatHeader.vue +0 -78
  99. package/custom/ChatSurface.vue +0 -160
  100. package/custom/CustomAutoScrollContainer.vue +0 -164
  101. package/custom/SessionsHistory.vue +0 -88
  102. package/custom/chat.ts +0 -82
  103. package/custom/composables/agentAudio/utils.ts +0 -220
  104. package/custom/composables/agentStore/constants.ts +0 -19
  105. package/custom/composables/agentStore/pageContext.ts +0 -8
  106. package/custom/composables/agentStore/useAgentChat.ts +0 -241
  107. package/custom/composables/agentStore/useAgentPlaceholder.ts +0 -142
  108. package/custom/composables/agentStore/useAgentSessions.ts +0 -350
  109. package/custom/composables/useAgentAudio.ts +0 -272
  110. package/custom/composables/useAgentStore.ts +0 -510
  111. package/custom/composables/useAgentTransitions.ts +0 -58
  112. package/custom/conversation_area/ConversationArea.vue +0 -301
  113. package/custom/conversation_area/MessageRenderer.vue +0 -49
  114. package/custom/conversation_area/ProcessingTimeline.vue +0 -286
  115. package/custom/conversation_area/ReasoningRenderer.vue +0 -79
  116. package/custom/conversation_area/SystemMessageRenderer.vue +0 -28
  117. package/custom/conversation_area/TextRenderer.vue +0 -182
  118. package/custom/conversation_area/ThreeDotsAnimation.vue +0 -35
  119. package/custom/conversation_area/ToolApprovalRenderer.vue +0 -98
  120. package/custom/conversation_area/ToolRenderer.vue +0 -178
  121. package/custom/conversation_area/ToolsGroup.vue +0 -59
  122. package/custom/env.d.ts +0 -7
  123. package/custom/incremark_code_renderers/IncremarkShikiCodeBlock.vue +0 -393
  124. package/custom/incremark_code_renderers/incremarkCodeHighlight.ts +0 -285
  125. package/custom/incremark_code_renderers/incremarkRenderer.ts +0 -653
  126. package/custom/incremark_code_renderers/renderIncremarkMarkdown.ts +0 -118
  127. package/custom/package.json +0 -30
  128. package/custom/pnpm-lock.yaml +0 -2394
  129. package/custom/public/agentAudio/agent-processing.mp3 +0 -0
  130. package/custom/skills/analyze_data/SKILL.md +0 -25
  131. package/custom/skills/charts/SKILL.md +0 -193
  132. package/custom/skills/fetch_data/SKILL.md +0 -31
  133. package/custom/skills/mutate_data/SKILL.md +0 -121
  134. package/custom/speech_recognition_frontend/AudioLines.vue +0 -43
  135. package/custom/speech_recognition_frontend/MicrophoneButon.vue +0 -185
  136. package/custom/speech_recognition_frontend/types/voice-activity-detection.d.ts +0 -22
  137. package/custom/speech_recognition_frontend/voiceActivityDetection.ts +0 -62
  138. package/custom/tsconfig.json +0 -15
  139. package/custom/types.ts +0 -108
  140. package/custom/utils.ts +0 -38
  141. package/dist/agent/middleware/openAiResponsesContinuation.d.ts +0 -1
  142. package/dist/agent/middleware/openAiResponsesContinuation.js +0 -66
  143. package/dist/agent/models/AgentModeResolver.d.ts +0 -9
  144. package/dist/agent/models/AgentModeResolver.js +0 -9
  145. package/dist/agent/simpleAgent.js +0 -118
  146. package/dist/agent/turn/TurnContextBuilder.d.ts +0 -14
  147. package/dist/agent/turn/TurnContextBuilder.js +0 -31
  148. package/dist/agent/turn/TurnLifecycleService.d.ts +0 -24
  149. package/dist/agent/turn/TurnLifecycleService.js +0 -47
  150. package/dist/agent/turn/TurnPersistenceService.d.ts +0 -13
  151. package/dist/agent/turn/TurnPersistenceService.js +0 -35
  152. package/dist/agent/turn/TurnPromptBuilder.d.ts +0 -19
  153. package/dist/agent/turn/TurnPromptBuilder.js +0 -43
  154. package/dist/agent/turn/TurnStreamConsumer.d.ts +0 -11
  155. package/dist/agent/turn/TurnStreamConsumer.js +0 -84
  156. package/dist/agentTurnService.d.ts +0 -24
  157. package/dist/agentTurnService.js +0 -269
  158. package/dist/surfaces/web-sse/createSseEventEmitter.d.ts +0 -14
  159. package/endpoints/chatSurfaces.ts +0 -66
  160. package/endpoints/context.ts +0 -52
  161. package/endpoints/core.ts +0 -181
  162. package/endpoints/sessions.ts +0 -183
  163. package/errors.ts +0 -10
  164. package/index.ts +0 -192
  165. package/pnpm-workspace.yaml +0 -4
  166. package/sanitizeSpeechText.ts +0 -32
  167. package/sessionStore.ts +0 -105
  168. package/surfaces/web-sse/createSseEventEmitter.ts +0 -311
  169. package/tsconfig.json +0 -14
  170. package/types.ts +0 -127
  171. /package/dist/{agent/turn/turnTypes.js → application/ports.js} +0 -0
  172. /package/dist/{agentEvents.js → domain/agentEvents.js} +0 -0
  173. /package/dist/{agent → domain}/languageDetect.d.ts +0 -0
  174. /package/dist/{agent → domain}/languageDetect.js +0 -0
  175. /package/dist/{agent → domain}/systemPrompt.d.ts +0 -0
  176. /package/dist/{agent → domain}/toolCallEvents.d.ts +0 -0
  177. /package/dist/{agent → domain}/toolCallEvents.js +0 -0
  178. /package/dist/{endpoints/context.js → domain/turnTypes.js} +0 -0
  179. /package/dist/{agent/turn/VegaLiteStreamBuffer.js → domain/vegaLiteStreamBuffer.js} +0 -0
  180. /package/dist/{agent/runtime/AgentContext.js → llm/agentContext.js} +0 -0
  181. /package/dist/{agent → llm}/middleware/sequenceDebug.js +0 -0
  182. /package/dist/{agent/checkpointer.d.ts → persistence/checkpointStore.d.ts} +0 -0
  183. /package/dist/{agent/checkpointer.js → persistence/checkpointStore.js} +0 -0
  184. /package/dist/{errors.d.ts → shared/errors.d.ts} +0 -0
  185. /package/dist/{errors.js → shared/errors.js} +0 -0
  186. /package/dist/{sanitizeSpeechText.d.ts → shared/sanitizeSpeechText.d.ts} +0 -0
  187. /package/dist/{sanitizeSpeechText.js → shared/sanitizeSpeechText.js} +0 -0
  188. /package/dist/{apiBasedTools.d.ts → tools/apiBasedTools.d.ts} +0 -0
  189. /package/dist/{agent/tools → tools}/apiTool.js +0 -0
  190. /package/dist/{agent/tools → tools}/fetchSkill.d.ts +0 -0
  191. /package/dist/{agent/tools → tools}/fetchToolSchema.js +0 -0
  192. /package/dist/{agent/tools → tools}/getUserLocation.d.ts +0 -0
  193. /package/dist/{agent/tools → tools}/getUserLocation.js +0 -0
  194. /package/dist/{agent/tools → tools}/index.js +0 -0
  195. /package/dist/{agent/tools → tools}/navigateUser.js +0 -0
  196. /package/dist/{agent → tools}/skills/registry.d.ts +0 -0
  197. /package/dist/{agent → tools}/skills/registry.js +0 -0
  198. /package/dist/{endpoints/chatSurfaces.d.ts → transport/http/chatSurfaceEndpoints.d.ts} +0 -0
  199. /package/dist/{endpoints/chatSurfaces.js → transport/http/chatSurfaceEndpoints.js} +0 -0
  200. /package/dist/{endpoints/core.d.ts → transport/http/coreEndpoints.d.ts} +0 -0
  201. /package/dist/{endpoints/sessions.d.ts → transport/http/sessionEndpoints.d.ts} +0 -0
@@ -1,653 +0,0 @@
1
- import type { Root } from '@incremark/core';
2
-
3
- type KatexRenderer = typeof import('katex')['default'];
4
-
5
- interface NodeLike {
6
- type?: unknown;
7
- children?: unknown;
8
- [key: string]: unknown;
9
- }
10
-
11
- const SAFE_URL_PROTOCOLS = new Set(['http:', 'https:', 'mailto:', 'tel:']);
12
- const HTTP_URL_RE = /^https?:\/\//i;
13
- const CLASS_TOKEN_INVALID_CHARS_RE = /[^a-z0-9_-]+/g;
14
- const EDGE_HYPHENS_RE = /^-+|-+$/g;
15
- const INCREMARK_BLOCK_MATH_PLACEHOLDER_RE = /<div class="incremark-block incremark-math incremark-math--pending" data-incremark-math="([\s\S]*?)" data-incremark-math-display="block"><pre><code>[\s\S]*?<\/code><\/pre><\/div>/g;
16
- const INCREMARK_INLINE_MATH_PLACEHOLDER_RE = /<code class="incremark-inline-code incremark-inline-math incremark-inline-math--pending" data-incremark-math="([\s\S]*?)" data-incremark-math-display="inline">[\s\S]*?<\/code>/g;
17
- const HTML_ENTITY_RE = /&(#x?[0-9a-fA-F]+|[a-zA-Z]+);/g;
18
- const SAFE_HTML_TAGS = new Set([
19
- 'a',
20
- 'article',
21
- 'b',
22
- 'blockquote',
23
- 'br',
24
- 'code',
25
- 'del',
26
- 'details',
27
- 'div',
28
- 'em',
29
- 'figcaption',
30
- 'figure',
31
- 'footer',
32
- 'h1',
33
- 'h2',
34
- 'h3',
35
- 'h4',
36
- 'h5',
37
- 'h6',
38
- 'header',
39
- 'hr',
40
- 'i',
41
- 'img',
42
- 'kbd',
43
- 'li',
44
- 'ol',
45
- 'p',
46
- 'pre',
47
- 's',
48
- 'section',
49
- 'span',
50
- 'strong',
51
- 'sub',
52
- 'summary',
53
- 'sup',
54
- 'table',
55
- 'tbody',
56
- 'td',
57
- 'th',
58
- 'thead',
59
- 'tr',
60
- 'u',
61
- 'ul'
62
- ]);
63
- const BLOCK_HTML_TAGS = new Set([
64
- 'article',
65
- 'blockquote',
66
- 'details',
67
- 'div',
68
- 'figcaption',
69
- 'figure',
70
- 'footer',
71
- 'h1',
72
- 'h2',
73
- 'h3',
74
- 'h4',
75
- 'h5',
76
- 'h6',
77
- 'header',
78
- 'hr',
79
- 'li',
80
- 'ol',
81
- 'p',
82
- 'pre',
83
- 'section',
84
- 'table',
85
- 'tbody',
86
- 'td',
87
- 'th',
88
- 'thead',
89
- 'tr',
90
- 'ul'
91
- ]);
92
- const VOID_HTML_TAGS = new Set(['br', 'hr', 'img']);
93
- const SAFE_HTML_ATTRIBUTES = new Set([
94
- 'align',
95
- 'alt',
96
- 'class',
97
- 'colspan',
98
- 'height',
99
- 'href',
100
- 'id',
101
- 'open',
102
- 'rel',
103
- 'rowspan',
104
- 'src',
105
- 'target',
106
- 'title',
107
- 'width'
108
- ]);
109
- const HEADING_SIZES: Record<number, string> = {
110
- 1: '1.5rem',
111
- 2: '1.2rem',
112
- 3: '1.1rem',
113
- 4: '1.05rem',
114
- 5: '1rem',
115
- 6: '0.94rem'
116
- };
117
- const NAMED_HTML_ENTITIES: Record<string, string> = {
118
- amp: '&',
119
- apos: "'",
120
- gt: '>',
121
- lt: '<',
122
- quot: '"'
123
- };
124
- const CODE_COPY_BUTTON_HTML =
125
- '<button type="button" class="incremark-code-copy" aria-label="Copy code" title="Copy code"></button>';
126
-
127
- export function renderIncremarkAst(root: Root): string {
128
- return root.children.map((child) => renderBlock(child as NodeLike)).join('');
129
- }
130
-
131
- export async function renderKatexIncremarkHtml(html: string): Promise<string> {
132
- if (!html.includes('data-incremark-math=')) {
133
- return html;
134
- }
135
-
136
- try {
137
- const { default: katex } = await import('katex');
138
- const withRenderedBlockMath = html.replace(
139
- INCREMARK_BLOCK_MATH_PLACEHOLDER_RE,
140
- (_match, encodedFormula: string) => renderKatexFormulaHtml(katex, decodeHtmlEntities(encodedFormula), true)
141
- );
142
-
143
- return withRenderedBlockMath.replace(
144
- INCREMARK_INLINE_MATH_PLACEHOLDER_RE,
145
- (_match, encodedFormula: string) => renderKatexFormulaHtml(katex, decodeHtmlEntities(encodedFormula), false)
146
- );
147
- } catch {
148
- return html;
149
- }
150
- }
151
-
152
- function renderBlock(node: NodeLike): string {
153
- switch (getType(node)) {
154
- case 'paragraph':
155
- return wrapBlock('p', 'incremark-paragraph', renderInlineChildren(getChildren(node)));
156
- case 'heading': {
157
- const depth = clampHeadingDepth(getNumber(node, 'depth'));
158
- const size = HEADING_SIZES[depth];
159
- return `<h${depth} class="incremark-block incremark-heading incremark-heading-${depth}" style="font-size: ${size};">${renderInlineChildren(getChildren(node))}</h${depth}>`;
160
- }
161
- case 'code':
162
- return renderCodeBlock(node);
163
- case 'blockquote':
164
- return wrapBlock('blockquote', 'incremark-blockquote', renderBlockChildren(getChildren(node)));
165
- case 'list':
166
- return renderList(node);
167
- case 'listItem':
168
- return renderListItem(node);
169
- case 'thematicBreak':
170
- return '<hr class="incremark-block incremark-thematic-break" />';
171
- case 'table':
172
- return renderTable(node);
173
- case 'math':
174
- return renderMathBlock(node);
175
- case 'htmlElement':
176
- return renderHtmlElement(node);
177
- case 'containerDirective':
178
- case 'leafDirective':
179
- return renderDirectiveBlock(node);
180
- case 'footnoteDefinition':
181
- return renderFootnoteDefinition(node);
182
- case 'definition':
183
- return '';
184
- case 'html':
185
- return renderCodeShell(`<pre><code>${escapeHtml(getString(node, 'value'))}</code></pre>`);
186
- default: {
187
- const children = getChildren(node);
188
- if (children.length > 0) {
189
- return renderBlockChildren(children);
190
- }
191
-
192
- const value = getString(node, 'value');
193
- return value ? wrapBlock('p', 'incremark-paragraph', escapeHtml(value)) : '';
194
- }
195
- }
196
- }
197
-
198
- function renderInline(node: NodeLike): string {
199
- switch (getType(node)) {
200
- case 'text':
201
- return escapeHtml(getString(node, 'value'));
202
- case 'strong':
203
- return `<strong>${renderInlineChildren(getChildren(node))}</strong>`;
204
- case 'emphasis':
205
- return `<em>${renderInlineChildren(getChildren(node))}</em>`;
206
- case 'delete':
207
- return `<del>${renderInlineChildren(getChildren(node))}</del>`;
208
- case 'inlineCode':
209
- return `<code class="incremark-inline-code">${escapeHtml(getString(node, 'value'))}</code>`;
210
- case 'link':
211
- return renderLink(node);
212
- case 'linkReference':
213
- return renderInlineChildren(getChildren(node)) || escapeHtml(getString(node, 'label') || getString(node, 'identifier'));
214
- case 'image':
215
- return renderImage(node);
216
- case 'imageReference':
217
- return escapeHtml(getString(node, 'alt'));
218
- case 'break':
219
- return '<br />';
220
- case 'inlineMath':
221
- return renderInlineMath(node);
222
- case 'footnoteReference':
223
- return `<sup class="incremark-footnote-reference">[${escapeHtml(getString(node, 'identifier'))}]</sup>`;
224
- case 'textDirective':
225
- return renderDirectiveInline(node);
226
- case 'htmlElement':
227
- return renderHtmlElement(node);
228
- case 'html':
229
- return escapeHtml(getString(node, 'value'));
230
- default: {
231
- const children = getChildren(node);
232
- if (children.length > 0) {
233
- return renderInlineChildren(children);
234
- }
235
-
236
- return escapeHtml(getString(node, 'value'));
237
- }
238
- }
239
- }
240
-
241
- function renderBlockChildren(children: NodeLike[]): string {
242
- return children.map((child) => renderBlock(child)).join('');
243
- }
244
-
245
- function renderInlineChildren(children: NodeLike[]): string {
246
- return children.map((child) => renderInline(child)).join('');
247
- }
248
-
249
- function renderCodeShell(preHtml: string, headerHtml = ''): string {
250
- return `<div class="incremark-block incremark-code"><div class="incremark-code-toolbar">${headerHtml}${CODE_COPY_BUTTON_HTML}</div>${preHtml}</div>`;
251
- }
252
-
253
- function renderCodeBlock(node: NodeLike): string {
254
- const lang = sanitizeClassToken(getString(node, 'lang'));
255
- const langLabel = lang ? `<div class="incremark-code-header">${escapeHtml(lang)}</div>` : '';
256
- const languageClass = lang ? ` class="language-${escapeAttribute(lang)}"` : '';
257
- const code = `<pre><code${languageClass}>${escapeHtml(getString(node, 'value'))}</code></pre>`;
258
- return renderCodeShell(code, langLabel);
259
- }
260
-
261
- function renderMathBlock(node: NodeLike): string {
262
- return renderKatexPlaceholder(getString(node, 'value'), true);
263
- }
264
-
265
- function renderInlineMath(node: NodeLike): string {
266
- return renderKatexPlaceholder(getString(node, 'value'), false);
267
- }
268
-
269
- function renderList(node: NodeLike): string {
270
- const ordered = getBoolean(node, 'ordered') === true;
271
- const tag = ordered ? 'ol' : 'ul';
272
- const classes = `incremark-block incremark-list incremark-list--${ordered ? 'ordered' : 'unordered'}`;
273
- const start = ordered ? getNumber(node, 'start') : undefined;
274
- const startAttr = ordered && typeof start === 'number' && start > 1 ? ` start="${start}"` : '';
275
- return `<${tag} class="${classes}"${startAttr}>${getChildren(node).map((child) => renderListItem(child)).join('')}</${tag}>`;
276
- }
277
-
278
- function renderListItem(node: NodeLike): string {
279
- const children = getChildren(node);
280
- const checked = getBoolean(node, 'checked');
281
-
282
- if (typeof checked === 'boolean' && children[0] && getType(children[0]) === 'paragraph') {
283
- const [first, ...rest] = children;
284
- const firstContent = renderInlineChildren(getChildren(first));
285
- const restContent = rest.map((child) => renderBlock(child)).join('');
286
- return `<li class="incremark-list-item incremark-list-item--task"><label class="incremark-task-item"><input class="incremark-task-checkbox" type="checkbox" disabled${checked ? ' checked' : ''} /><span class="incremark-task-content">${firstContent}</span></label>${restContent}</li>`;
287
- }
288
-
289
- const checkbox = typeof checked === 'boolean'
290
- ? `<input class="incremark-task-checkbox" type="checkbox" disabled${checked ? ' checked' : ''} />`
291
- : '';
292
- return `<li class="incremark-list-item">${checkbox}${children.map((child) => renderBlock(child)).join('')}</li>`;
293
- }
294
-
295
- function renderTable(node: NodeLike): string {
296
- const rows = getChildren(node);
297
- if (rows.length === 0) {
298
- return '';
299
- }
300
-
301
- const align = Array.isArray(node.align) ? node.align : [];
302
- const [headerRow, ...bodyRows] = rows;
303
- const thead = `<thead>${renderTableRow(headerRow, true, align)}</thead>`;
304
- const tbody = bodyRows.length > 0
305
- ? `<tbody>${bodyRows.map((row) => renderTableRow(row, false, align)).join('')}</tbody>`
306
- : '';
307
-
308
- return `<div class="incremark-block incremark-table-wrapper"><table class="incremark-table">${thead}${tbody}</table></div>`;
309
- }
310
-
311
- function renderTableRow(row: NodeLike, header: boolean, align: unknown[]): string {
312
- const cells = getChildren(row);
313
- const tag = header ? 'th' : 'td';
314
- const className = header ? 'incremark-table-header' : 'incremark-table-cell';
315
- const renderedCells = cells.map((cell, index) => {
316
- const alignment = typeof align[index] === 'string' ? String(align[index]) : '';
317
- const style = alignment ? ` style="text-align: ${escapeAttribute(alignment)};"` : '';
318
- return `<${tag} class="${className}"${style}>${renderInlineChildren(getChildren(cell))}</${tag}>`;
319
- }).join('');
320
-
321
- return `<tr>${renderedCells}</tr>`;
322
- }
323
-
324
- function renderLink(node: NodeLike): string {
325
- const href = sanitizeUrl(getString(node, 'url'));
326
- const content = renderInlineChildren(getChildren(node)) || escapeHtml(getString(node, 'url'));
327
- if (!href) {
328
- return content;
329
- }
330
-
331
- const title = getString(node, 'title');
332
- const titleAttr = title ? ` title="${escapeAttribute(title)}"` : '';
333
- const relAttr = isExternalHttpUrl(href) ? ' rel="noreferrer"' : '';
334
- return `<a class="incremark-link" href="${escapeAttribute(href)}"${titleAttr}${relAttr}>${content}</a>`;
335
- }
336
-
337
- function renderImage(node: NodeLike): string {
338
- const src = sanitizeUrl(getString(node, 'url'));
339
- if (!src) {
340
- return escapeHtml(getString(node, 'alt'));
341
- }
342
-
343
- const alt = escapeAttribute(getString(node, 'alt'));
344
- const title = getString(node, 'title');
345
- const titleAttr = title ? ` title="${escapeAttribute(title)}"` : '';
346
- return `<img class="incremark-image" src="${escapeAttribute(src)}" alt="${alt}"${titleAttr} />`;
347
- }
348
-
349
- function renderDirectiveBlock(node: NodeLike): string {
350
- const name = sanitizeClassToken(getString(node, 'name')) || 'directive';
351
- const content = renderBlockChildren(getChildren(node));
352
- const attrs = renderHtmlAttributes(getAttributes(node, 'attributes'), [
353
- 'incremark-block',
354
- 'incremark-container',
355
- `incremark-container--${name}`
356
- ]);
357
- return `<section${attrs}>${content}</section>`;
358
- }
359
-
360
- function renderDirectiveInline(node: NodeLike): string {
361
- const name = sanitizeClassToken(getString(node, 'name')) || 'directive';
362
- const content = renderInlineChildren(getChildren(node));
363
- const attrs = renderHtmlAttributes(getAttributes(node, 'attributes'), [
364
- 'incremark-inline-directive',
365
- `incremark-inline-directive--${name}`
366
- ]);
367
- return `<span${attrs}>${content}</span>`;
368
- }
369
-
370
- function renderHtmlElement(node: NodeLike): string {
371
- const tagName = getString(node, 'tagName').toLowerCase();
372
- if (!SAFE_HTML_TAGS.has(tagName)) {
373
- return renderBlockChildren(getChildren(node));
374
- }
375
-
376
- const extraClasses = [
377
- 'incremark-html-element',
378
- `incremark-html-element--${sanitizeClassToken(tagName)}`
379
- ];
380
-
381
- if (tagName === 'img') {
382
- extraClasses.push('incremark-image');
383
- }
384
-
385
- if (BLOCK_HTML_TAGS.has(tagName)) {
386
- extraClasses.unshift('incremark-block');
387
- }
388
-
389
- const attrs = renderHtmlAttributes(getAttributes(node, 'attrs'), extraClasses);
390
- if (VOID_HTML_TAGS.has(tagName)) {
391
- return `<${tagName}${attrs} />`;
392
- }
393
-
394
- const content = BLOCK_HTML_TAGS.has(tagName)
395
- ? renderMixedChildren(getChildren(node))
396
- : renderInlineChildren(getChildren(node));
397
- return `<${tagName}${attrs}>${content}</${tagName}>`;
398
- }
399
-
400
- function renderFootnoteDefinition(node: NodeLike): string {
401
- const identifier = sanitizeClassToken(getString(node, 'identifier'));
402
- const idAttr = identifier ? ` id="footnote-${escapeAttribute(identifier)}"` : '';
403
- return `<div class="incremark-block incremark-footnote-definition"${idAttr}>${renderBlockChildren(getChildren(node))}</div>`;
404
- }
405
-
406
- function renderMixedChildren(children: NodeLike[]): string {
407
- return children.map((child) => renderAny(child)).join('');
408
- }
409
-
410
- function renderAny(node: NodeLike): string {
411
- const type = getType(node);
412
- if (type === 'textDirective') {
413
- return renderDirectiveInline(node);
414
- }
415
-
416
- if (type === 'htmlElement') {
417
- const tagName = getString(node, 'tagName').toLowerCase();
418
- return BLOCK_HTML_TAGS.has(tagName) ? renderBlock(node) : renderInline(node);
419
- }
420
-
421
- if (isInlineType(type)) {
422
- return renderInline(node);
423
- }
424
-
425
- return renderBlock(node);
426
- }
427
-
428
- function wrapBlock(tag: string, className: string, content: string): string {
429
- return `<${tag} class="incremark-block ${className}">${content}</${tag}>`;
430
- }
431
-
432
- function renderKatexPlaceholder(formula: string, displayMode: boolean): string {
433
- const trimmedFormula = formula.trim();
434
- if (!trimmedFormula) {
435
- return displayMode ? '' : '<span class="incremark-inline-math"></span>';
436
- }
437
-
438
- if (displayMode) {
439
- return `<div class="incremark-block incremark-math incremark-math--pending" data-incremark-math="${escapeAttribute(trimmedFormula)}" data-incremark-math-display="block"><pre><code>${escapeHtml(trimmedFormula)}</code></pre></div>`;
440
- }
441
-
442
- return `<code class="incremark-inline-code incremark-inline-math incremark-inline-math--pending" data-incremark-math="${escapeAttribute(trimmedFormula)}" data-incremark-math-display="inline">${escapeHtml(trimmedFormula)}</code>`;
443
- }
444
-
445
- function renderKatexFormulaHtml(katex: KatexRenderer, formula: string, displayMode: boolean): string {
446
- const trimmedFormula = formula.trim();
447
- if (!trimmedFormula) {
448
- return displayMode ? '' : '<span class="incremark-inline-math"></span>';
449
- }
450
-
451
- try {
452
- const renderedFormula = katex.renderToString(trimmedFormula, {
453
- displayMode,
454
- output: 'htmlAndMathml',
455
- throwOnError: false,
456
- trust: false
457
- });
458
-
459
- if (displayMode) {
460
- return `<div class="incremark-block incremark-math incremark-math--rendered">${renderedFormula}</div>`;
461
- }
462
-
463
- return `<span class="incremark-inline-math incremark-inline-math--rendered">${renderedFormula}</span>`;
464
- } catch {
465
- return renderKatexFallbackHtml(trimmedFormula, displayMode);
466
- }
467
- }
468
-
469
- function renderKatexFallbackHtml(formula: string, displayMode: boolean): string {
470
- if (displayMode) {
471
- return `<div class="incremark-block incremark-math incremark-math--fallback"><pre><code>${escapeHtml(formula)}</code></pre></div>`;
472
- }
473
-
474
- return `<code class="incremark-inline-code incremark-inline-math incremark-inline-math--fallback">${escapeHtml(formula)}</code>`;
475
- }
476
-
477
- function renderHtmlAttributes(attributes: Record<string, string>, extraClasses: string[]): string {
478
- const rendered: string[] = [];
479
- const classNames = [...extraClasses];
480
-
481
- for (const [name, rawValue] of Object.entries(attributes)) {
482
- const normalizedName = name.toLowerCase();
483
- if (normalizedName.startsWith('on')) {
484
- continue;
485
- }
486
-
487
- if (normalizedName === 'class') {
488
- if (rawValue.trim()) {
489
- classNames.push(rawValue.trim());
490
- }
491
- continue;
492
- }
493
-
494
- if (normalizedName === 'style') {
495
- continue;
496
- }
497
-
498
- if (!SAFE_HTML_ATTRIBUTES.has(normalizedName) && !normalizedName.startsWith('aria-') && !normalizedName.startsWith('data-')) {
499
- continue;
500
- }
501
-
502
- if (normalizedName === 'href' || normalizedName === 'src') {
503
- const safeUrl = sanitizeUrl(rawValue);
504
- if (!safeUrl) {
505
- continue;
506
- }
507
- rendered.push(` ${normalizedName}="${escapeAttribute(safeUrl)}"`);
508
- continue;
509
- }
510
-
511
- if (normalizedName === 'open') {
512
- if (rawValue !== 'false') {
513
- rendered.push(' open');
514
- }
515
- continue;
516
- }
517
-
518
- rendered.push(` ${normalizedName}="${escapeAttribute(rawValue)}"`);
519
- }
520
-
521
- if (classNames.length > 0) {
522
- rendered.unshift(` class="${escapeAttribute(classNames.join(' '))}"`);
523
- }
524
-
525
- return rendered.join('');
526
- }
527
-
528
- function getAttributes(node: NodeLike, key: string): Record<string, string> {
529
- const value = node[key];
530
- if (!value || typeof value !== 'object') {
531
- return {};
532
- }
533
-
534
- return Object.fromEntries(
535
- Object.entries(value as Record<string, unknown>).flatMap(([name, attrValue]) => {
536
- if (typeof attrValue !== 'string') {
537
- return [];
538
- }
539
-
540
- return [[name, attrValue]];
541
- })
542
- );
543
- }
544
-
545
- function getChildren(node: NodeLike): NodeLike[] {
546
- return Array.isArray(node.children) ? (node.children as NodeLike[]) : [];
547
- }
548
-
549
- function getType(node: NodeLike): string {
550
- return typeof node.type === 'string' ? node.type : '';
551
- }
552
-
553
- function getString(node: NodeLike, key: string): string {
554
- const value = node[key];
555
- return typeof value === 'string' ? value : '';
556
- }
557
-
558
- function getBoolean(node: NodeLike, key: string): boolean | undefined {
559
- const value = node[key];
560
- return typeof value === 'boolean' ? value : undefined;
561
- }
562
-
563
- function getNumber(node: NodeLike, key: string): number | undefined {
564
- const value = node[key];
565
- return typeof value === 'number' ? value : undefined;
566
- }
567
-
568
- function clampHeadingDepth(depth: number | undefined): 1 | 2 | 3 | 4 | 5 | 6 {
569
- if (!depth || depth < 1) {
570
- return 1;
571
- }
572
-
573
- if (depth > 6) {
574
- return 6;
575
- }
576
-
577
- return depth as 1 | 2 | 3 | 4 | 5 | 6;
578
- }
579
-
580
- function sanitizeUrl(url: string): string | null {
581
- const trimmed = url.trim();
582
- if (!trimmed) {
583
- return null;
584
- }
585
-
586
- if (trimmed.startsWith('#') || trimmed.startsWith('/') || trimmed.startsWith('./') || trimmed.startsWith('../')) {
587
- return trimmed;
588
- }
589
-
590
- try {
591
- const parsed = new URL(trimmed);
592
- return SAFE_URL_PROTOCOLS.has(parsed.protocol) ? trimmed : null;
593
- } catch {
594
- return null;
595
- }
596
- }
597
-
598
- function isExternalHttpUrl(url: string): boolean {
599
- return HTTP_URL_RE.test(url);
600
- }
601
-
602
- function isInlineType(type: string): boolean {
603
- return type === 'break'
604
- || type === 'delete'
605
- || type === 'emphasis'
606
- || type === 'footnoteReference'
607
- || type === 'html'
608
- || type === 'image'
609
- || type === 'imageReference'
610
- || type === 'inlineCode'
611
- || type === 'inlineMath'
612
- || type === 'link'
613
- || type === 'linkReference'
614
- || type === 'strong'
615
- || type === 'text';
616
- }
617
-
618
- function sanitizeClassToken(value: string): string {
619
- return value
620
- .trim()
621
- .toLowerCase()
622
- .replace(CLASS_TOKEN_INVALID_CHARS_RE, '-')
623
- .replace(EDGE_HYPHENS_RE, '');
624
- }
625
-
626
- function escapeHtml(value: string): string {
627
- return value
628
- .replace(/&/g, '&amp;')
629
- .replace(/</g, '&lt;')
630
- .replace(/>/g, '&gt;')
631
- .replace(/"/g, '&quot;')
632
- .replace(/'/g, '&#39;');
633
- }
634
-
635
- function escapeAttribute(value: string): string {
636
- return escapeHtml(value);
637
- }
638
-
639
- function decodeHtmlEntities(value: string): string {
640
- return value.replace(HTML_ENTITY_RE, (match, entity) => {
641
- if (entity.startsWith('#x') || entity.startsWith('#X')) {
642
- const codePoint = Number.parseInt(entity.slice(2), 16);
643
- return Number.isNaN(codePoint) ? match : String.fromCodePoint(codePoint);
644
- }
645
-
646
- if (entity.startsWith('#')) {
647
- const codePoint = Number.parseInt(entity.slice(1), 10);
648
- return Number.isNaN(codePoint) ? match : String.fromCodePoint(codePoint);
649
- }
650
-
651
- return NAMED_HTML_ENTITIES[entity] ?? match;
652
- });
653
- }