@promptbook/cli 0.112.0-93 → 0.112.0-95
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/apps/agents-server/src/app/admin/_components/AdminConfigurationShell.tsx +13 -7
- package/apps/agents-server/src/app/admin/code-runners/CodeRunnersClient.tsx +225 -0
- package/apps/agents-server/src/app/admin/code-runners/page.tsx +14 -0
- package/apps/agents-server/src/app/admin/environment/EnvironmentVariablesClient.tsx +259 -0
- package/apps/agents-server/src/app/admin/environment/page.tsx +21 -0
- package/apps/agents-server/src/app/admin/logs/LogsClient.tsx +78 -0
- package/apps/agents-server/src/app/admin/logs/page.tsx +14 -0
- package/apps/agents-server/src/app/admin/servers/ServersClient.tsx +64 -33
- package/apps/agents-server/src/app/admin/servers/ServersRegistryApi.ts +5 -0
- package/apps/agents-server/src/app/admin/servers/ServersRegistryTable.tsx +15 -2
- package/apps/agents-server/src/app/admin/servers/page.tsx +3 -3
- package/apps/agents-server/src/app/admin/servers/useServersRegistryState.ts +12 -2
- package/apps/agents-server/src/app/api/admin/code-runners/route.ts +104 -0
- package/apps/agents-server/src/app/api/admin/environment/route.ts +65 -0
- package/apps/agents-server/src/app/api/admin/logs/route.ts +24 -0
- package/apps/agents-server/src/app/api/admin/servers/[serverId]/route.ts +79 -3
- package/apps/agents-server/src/app/api/admin/servers/route.ts +36 -1
- package/apps/agents-server/src/app/page.tsx +101 -1
- package/apps/agents-server/src/components/Header/buildHeaderSystemMenuItems.ts +23 -4
- package/apps/agents-server/src/languages/ServerTranslationKeys.ts +4 -0
- package/apps/agents-server/src/languages/translations/czech.yaml +4 -0
- package/apps/agents-server/src/languages/translations/english.yaml +4 -0
- package/apps/agents-server/src/tools/$provideServer.ts +27 -0
- package/apps/agents-server/src/utils/serverRegistry.ts +20 -1
- package/apps/agents-server/src/utils/session.ts +123 -2
- package/apps/agents-server/src/utils/vpsConfiguration.ts +550 -0
- package/esm/index.es.js +1 -1
- package/esm/index.es.js.map +1 -1
- package/esm/src/book-components/Chat/utils/renderMarkdown.test.d.ts +1 -0
- package/esm/src/version.d.ts +1 -1
- package/package.json +2 -1
- package/src/book-components/Chat/MarkdownContent/MarkdownContent.tsx +9 -398
- package/src/book-components/Chat/utils/renderMarkdown.ts +323 -8
- package/src/other/templates/getTemplatesPipelineCollection.ts +683 -879
- package/src/version.ts +2 -2
- package/src/versions.txt +2 -0
- package/umd/index.umd.js +1 -1
- package/umd/index.umd.js.map +1 -1
- package/umd/src/book-components/Chat/utils/renderMarkdown.test.d.ts +1 -0
- package/umd/src/version.d.ts +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/esm/src/version.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export declare const BOOK_LANGUAGE_VERSION: string_semantic_version;
|
|
|
15
15
|
export declare const PROMPTBOOK_ENGINE_VERSION: string_promptbook_version;
|
|
16
16
|
/**
|
|
17
17
|
* Represents the version string of the Promptbook engine.
|
|
18
|
-
* It follows semantic versioning (e.g., `0.112.0-
|
|
18
|
+
* It follows semantic versioning (e.g., `0.112.0-94`).
|
|
19
19
|
*
|
|
20
20
|
* @generated
|
|
21
21
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@promptbook/cli",
|
|
3
|
-
"version": "0.112.0-
|
|
3
|
+
"version": "0.112.0-95",
|
|
4
4
|
"description": "Promptbook: Create persistent AI agents that turn your company's scattered knowledge into action",
|
|
5
5
|
"private": false,
|
|
6
6
|
"sideEffects": false,
|
|
@@ -161,6 +161,7 @@
|
|
|
161
161
|
"configchecker": "1.5.132",
|
|
162
162
|
"cross-fetch": "4.0.0",
|
|
163
163
|
"destroyable": "0.12.145",
|
|
164
|
+
"dompurify": "3.4.6",
|
|
164
165
|
"formidable": "3.5.4",
|
|
165
166
|
"highlight.js": "11.11.1",
|
|
166
167
|
"html-to-image": "1.11.13",
|
|
@@ -1,195 +1,13 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import katex from 'katex';
|
|
4
3
|
import { memo, useEffect, useMemo, useRef } from 'react';
|
|
5
4
|
import { createRoot, type Root } from 'react-dom/client';
|
|
6
|
-
import {
|
|
7
|
-
import type { string_html, string_markdown } from '../../../types/string_markdown';
|
|
8
|
-
import { TODO_USE } from '../../../utils/organization/TODO_USE';
|
|
5
|
+
import type { string_markdown } from '../../../types/string_markdown';
|
|
9
6
|
import { classNames } from '../../_common/react-utils/classNames';
|
|
10
7
|
import { CodeBlock } from '../CodeBlock/CodeBlock';
|
|
11
|
-
import {
|
|
8
|
+
import { renderMarkdown } from '../utils/renderMarkdown';
|
|
12
9
|
import styles from './MarkdownContent.module.css';
|
|
13
10
|
|
|
14
|
-
/**
|
|
15
|
-
* Normalizes markdown sublists so they render correctly under ordered list items.
|
|
16
|
-
*
|
|
17
|
-
* @param markdown - Markdown content to normalize.
|
|
18
|
-
* @returns Markdown with normalized sublist indentation.
|
|
19
|
-
*
|
|
20
|
-
* @private utility of `MarkdownContent` component
|
|
21
|
-
*/
|
|
22
|
-
function normalizeMarkdownSublists(markdown: string_markdown): string_markdown {
|
|
23
|
-
const lines = markdown.split(/\r?\n/);
|
|
24
|
-
let orderedIndent: number | null = null;
|
|
25
|
-
let shouldIndentUnordered = false;
|
|
26
|
-
let activeFence: '```' | '~~~' | null = null;
|
|
27
|
-
|
|
28
|
-
const normalizedLines = lines.map((line) => {
|
|
29
|
-
const trimmedLine = line.trim();
|
|
30
|
-
|
|
31
|
-
const fenceMatch = trimmedLine.match(/^(```|~~~)/);
|
|
32
|
-
if (fenceMatch) {
|
|
33
|
-
const fence = fenceMatch[1] as '```' | '~~~';
|
|
34
|
-
activeFence = activeFence === fence ? null : fence;
|
|
35
|
-
return line;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (activeFence) {
|
|
39
|
-
return line;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (trimmedLine === '') {
|
|
43
|
-
return line;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const orderedMatch = line.match(/^(\s*)(\d+)\.\s+/);
|
|
47
|
-
if (orderedMatch) {
|
|
48
|
-
orderedIndent = orderedMatch[1]!.length;
|
|
49
|
-
shouldIndentUnordered = true;
|
|
50
|
-
return line;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const unorderedMatch = line.match(/^(\s*)([-+*])\s+/);
|
|
54
|
-
if (unorderedMatch) {
|
|
55
|
-
if (shouldIndentUnordered && orderedIndent !== null) {
|
|
56
|
-
const currentIndent = unorderedMatch[1]!.length;
|
|
57
|
-
const targetIndent = orderedIndent + 4;
|
|
58
|
-
|
|
59
|
-
if (currentIndent < targetIndent) {
|
|
60
|
-
return `${' '.repeat(targetIndent)}${line.trimStart()}`;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return line;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
orderedIndent = null;
|
|
68
|
-
shouldIndentUnordered = false;
|
|
69
|
-
|
|
70
|
-
return line;
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
return normalizedLines.join('\n') as string_markdown;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Creates a showdown converter configured for chat markdown rendering
|
|
78
|
-
*
|
|
79
|
-
* @private utility of `MarkdownContent` component
|
|
80
|
-
*/
|
|
81
|
-
function createChatMarkdownConverter(): ShowdownConverter {
|
|
82
|
-
return new ShowdownConverter({
|
|
83
|
-
flavor: 'github',
|
|
84
|
-
tables: true,
|
|
85
|
-
strikethrough: true,
|
|
86
|
-
tasklists: true,
|
|
87
|
-
ghCodeBlocks: true,
|
|
88
|
-
ghMentions: false,
|
|
89
|
-
ghMentionsLink: '',
|
|
90
|
-
openLinksInNewWindow: true,
|
|
91
|
-
backslashEscapesHTMLTags: true,
|
|
92
|
-
emoji: true,
|
|
93
|
-
underline: true,
|
|
94
|
-
completeHTMLDocument: false,
|
|
95
|
-
metadata: false,
|
|
96
|
-
splitAdjacentBlockquotes: true,
|
|
97
|
-
noHeaderId: true,
|
|
98
|
-
headerLevelStart: 1,
|
|
99
|
-
parseImgDimensions: true,
|
|
100
|
-
simplifiedAutoLink: true,
|
|
101
|
-
literalMidWordUnderscores: true,
|
|
102
|
-
literalMidWordAsterisks: false,
|
|
103
|
-
simpleLineBreaks: true,
|
|
104
|
-
requireSpaceBeforeHeadingText: true,
|
|
105
|
-
ghCompatibleHeaderId: true,
|
|
106
|
-
prefixHeaderId: 'chat-header-',
|
|
107
|
-
rawPrefixHeaderId: false,
|
|
108
|
-
rawHeaderId: false,
|
|
109
|
-
smoothLivePreview: true,
|
|
110
|
-
smartIndentationFix: true,
|
|
111
|
-
disableForced4SpacesIndentedSublists: false,
|
|
112
|
-
encodeEmails: true,
|
|
113
|
-
extensions: [
|
|
114
|
-
() => ({
|
|
115
|
-
type: 'lang',
|
|
116
|
-
regex: createCitationMarkerRegex(),
|
|
117
|
-
replace: (match: string) => {
|
|
118
|
-
const citationMarker = parseCitationMarker(match);
|
|
119
|
-
if (!citationMarker) {
|
|
120
|
-
return match;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
TODO_USE(citationMarker.source);
|
|
124
|
-
|
|
125
|
-
// Note: Citations are now rendered as chips in ChatMessageItem
|
|
126
|
-
// We replace them with numbered superscript references
|
|
127
|
-
return `<sup class="${styles.citationRef}">[${citationMarker.id}]</sup>`;
|
|
128
|
-
},
|
|
129
|
-
}),
|
|
130
|
-
],
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Pre-configured showdown converter for chat markdown
|
|
136
|
-
*
|
|
137
|
-
* @private utility of `MarkdownContent` component
|
|
138
|
-
*/
|
|
139
|
-
const chatMarkdownConverter = createChatMarkdownConverter();
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Definition of math delimiter.
|
|
143
|
-
*/
|
|
144
|
-
type MathDelimiterDefinition = {
|
|
145
|
-
regex: RegExp;
|
|
146
|
-
displayMode: boolean;
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Collection of math delimiter definitions.
|
|
151
|
-
*/
|
|
152
|
-
const mathDelimiterDefinitions: ReadonlyArray<MathDelimiterDefinition> = [
|
|
153
|
-
{ regex: /(^|[^\\])\$\$([\s\S]+?)\$\$/g, displayMode: true },
|
|
154
|
-
{ regex: /(^|[^\\])\\\[([\s\S]+?)\\\]/g, displayMode: true },
|
|
155
|
-
{ regex: /(^|[^\\])\\\(([\s\S]+?)\\\)/g, displayMode: false },
|
|
156
|
-
{ regex: /(^|[^\\])\$([^$\n]+?)\$/g, displayMode: false },
|
|
157
|
-
];
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Pattern matching CODE FENCE.
|
|
161
|
-
*/
|
|
162
|
-
const CODE_FENCE_REGEX = /(`{3,}|~{3,})(?:[^\n\r]*)\r?\n[\s\S]*?\r?\n\1[^\n\r]*/g;
|
|
163
|
-
/**
|
|
164
|
-
* Pattern matching INLINE CODE.
|
|
165
|
-
*/
|
|
166
|
-
const INLINE_CODE_REGEX = /(`+)([\s\S]*?)(\1)/g;
|
|
167
|
-
/**
|
|
168
|
-
* Prefix for CODE PLACEHOLDER.
|
|
169
|
-
*/
|
|
170
|
-
const CODE_PLACEHOLDER_PREFIX = '@@PROMPTBOOK_CODE_PLACEHOLDER__';
|
|
171
|
-
/**
|
|
172
|
-
* Pattern matching CODE PLACEHOLDER.
|
|
173
|
-
*/
|
|
174
|
-
const CODE_PLACEHOLDER_REGEX = new RegExp(`${CODE_PLACEHOLDER_PREFIX}(\\d+)__`, 'g');
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Pattern matching DETAILS BLOCK.
|
|
178
|
-
*/
|
|
179
|
-
const DETAILS_BLOCK_REGEX = /<details[\s\S]*?<\/details\s*>/gi;
|
|
180
|
-
/**
|
|
181
|
-
* Prefix for DETAILS PLACEHOLDER.
|
|
182
|
-
*/
|
|
183
|
-
const DETAILS_PLACEHOLDER_PREFIX = '@@PROMPTBOOK_DETAILS_PLACEHOLDER__';
|
|
184
|
-
/**
|
|
185
|
-
* Pattern matching DETAILS PLACEHOLDER.
|
|
186
|
-
*/
|
|
187
|
-
const DETAILS_PLACEHOLDER_REGEX = new RegExp(`${DETAILS_PLACEHOLDER_PREFIX}(\\d+)__`, 'g');
|
|
188
|
-
/**
|
|
189
|
-
* Matches a Showdown-wrapped placeholder such as `<p>@@PROMPTBOOK_DETAILS_PLACEHOLDER__0__</p>`
|
|
190
|
-
*/
|
|
191
|
-
const DETAILS_PLACEHOLDER_WRAPPED_REGEX = new RegExp(`<p>\\s*(${DETAILS_PLACEHOLDER_PREFIX}\\d+__)\\s*<\\/p>`, 'g');
|
|
192
|
-
|
|
193
11
|
/**
|
|
194
12
|
* Selector used by the delegated summary click handler.
|
|
195
13
|
*
|
|
@@ -197,219 +15,6 @@ const DETAILS_PLACEHOLDER_WRAPPED_REGEX = new RegExp(`<p>\\s*(${DETAILS_PLACEHOL
|
|
|
197
15
|
*/
|
|
198
16
|
const DETAILS_SUMMARY_SELECTOR = 'summary';
|
|
199
17
|
|
|
200
|
-
/**
|
|
201
|
-
* Result of masked code segments.
|
|
202
|
-
*/
|
|
203
|
-
type MaskedCodeSegmentsResult = {
|
|
204
|
-
masked: string_markdown;
|
|
205
|
-
restore: (value: string_markdown) => string_markdown;
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Result of masked details blocks.
|
|
210
|
-
*/
|
|
211
|
-
type MaskedDetailsBlocksResult = {
|
|
212
|
-
masked: string_markdown;
|
|
213
|
-
restore: (value: string_html) => string_html;
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Renders the body of one raw `<details>` block as markdown while keeping the
|
|
218
|
-
* outer `<details>` and optional `<summary>` markup untouched.
|
|
219
|
-
*
|
|
220
|
-
* @param detailsBlock - Raw `<details>...</details>` HTML captured from markdown.
|
|
221
|
-
* @returns `<details>` HTML whose body has been converted from markdown to HTML.
|
|
222
|
-
*
|
|
223
|
-
* @private utility of `MarkdownContent` component
|
|
224
|
-
*/
|
|
225
|
-
function renderDetailsBlock(detailsBlock: string): string_html {
|
|
226
|
-
const openTagMatch = detailsBlock.match(/^<details\b[^>]*>/i);
|
|
227
|
-
const closeTagMatch = detailsBlock.match(/<\/details\s*>$/i);
|
|
228
|
-
|
|
229
|
-
if (!openTagMatch || !closeTagMatch) {
|
|
230
|
-
return detailsBlock as string_html;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const openTag = openTagMatch[0];
|
|
234
|
-
const closeTag = closeTagMatch[0];
|
|
235
|
-
const innerContent = detailsBlock.slice(openTag.length, detailsBlock.length - closeTag.length);
|
|
236
|
-
const summaryMatch = innerContent.match(/^(\s*<summary\b[^>]*>[\s\S]*?<\/summary\s*>)([\s\S]*)$/i);
|
|
237
|
-
const summaryHtml = summaryMatch?.[1] ?? '';
|
|
238
|
-
const bodyMarkdown = (summaryMatch?.[2] ?? innerContent) as string_markdown;
|
|
239
|
-
const renderedBody = renderMarkdown(bodyMarkdown);
|
|
240
|
-
|
|
241
|
-
return `${openTag}${summaryHtml}${renderedBody}${closeTag}` as string_html;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Masks inline and fenced code segments so math rendering never touches them.
|
|
246
|
-
*
|
|
247
|
-
* @param markdown - Markdown text to mask.
|
|
248
|
-
* @returns Masked markdown and a restore helper.
|
|
249
|
-
*
|
|
250
|
-
* @private utility of `MarkdownContent` component
|
|
251
|
-
*/
|
|
252
|
-
function maskMarkdownCodeSegments(markdown: string_markdown): MaskedCodeSegmentsResult {
|
|
253
|
-
const segments: string[] = [];
|
|
254
|
-
let masked = markdown;
|
|
255
|
-
|
|
256
|
-
const addPlaceholder = (segment: string) => {
|
|
257
|
-
const placeholder = `${CODE_PLACEHOLDER_PREFIX}${segments.length}__`;
|
|
258
|
-
segments.push(segment);
|
|
259
|
-
return placeholder;
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
const maskWith = (regex: RegExp) => {
|
|
263
|
-
regex.lastIndex = 0;
|
|
264
|
-
masked = masked.replace(regex, (match) => addPlaceholder(match));
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
maskWith(CODE_FENCE_REGEX);
|
|
268
|
-
maskWith(INLINE_CODE_REGEX);
|
|
269
|
-
|
|
270
|
-
return {
|
|
271
|
-
masked: masked as string_markdown,
|
|
272
|
-
restore(value: string_markdown): string_markdown {
|
|
273
|
-
return value.replace(CODE_PLACEHOLDER_REGEX, (_match, index) => segments[Number(index)] ?? '');
|
|
274
|
-
},
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Masks `<details>…</details>` blocks in the markdown source so that Showdown never
|
|
280
|
-
* processes their content (which would break them with `simpleLineBreaks: true`).
|
|
281
|
-
*
|
|
282
|
-
* The original blocks are replaced with placeholders before Showdown runs, then restored
|
|
283
|
-
* afterwards with their non-summary body converted through the normal markdown pipeline.
|
|
284
|
-
* Any surrounding `<p>` wrapper that Showdown may have injected around a placeholder is
|
|
285
|
-
* stripped so the `<details>` element remains a proper block-level element.
|
|
286
|
-
*
|
|
287
|
-
* @param markdown - Markdown text that may contain raw HTML `<details>` blocks.
|
|
288
|
-
* @returns Masked markdown and a restore helper that returns `string_html`.
|
|
289
|
-
*
|
|
290
|
-
* @private utility of `MarkdownContent` component
|
|
291
|
-
*/
|
|
292
|
-
function maskDetailsBlocks(markdown: string_markdown): MaskedDetailsBlocksResult {
|
|
293
|
-
const blocks: string_html[] = [];
|
|
294
|
-
|
|
295
|
-
DETAILS_BLOCK_REGEX.lastIndex = 0;
|
|
296
|
-
const masked = markdown.replace(DETAILS_BLOCK_REGEX, (match) => {
|
|
297
|
-
const placeholder = `${DETAILS_PLACEHOLDER_PREFIX}${blocks.length}__`;
|
|
298
|
-
blocks.push(renderDetailsBlock(match));
|
|
299
|
-
return placeholder;
|
|
300
|
-
}) as string_markdown;
|
|
301
|
-
|
|
302
|
-
return {
|
|
303
|
-
masked,
|
|
304
|
-
restore(value: string_html): string_html {
|
|
305
|
-
return value
|
|
306
|
-
.replace(DETAILS_PLACEHOLDER_WRAPPED_REGEX, '$1')
|
|
307
|
-
.replace(DETAILS_PLACEHOLDER_REGEX, (_match, index) => blocks[Number(index)] ?? '') as string_html;
|
|
308
|
-
},
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* Replaces math delimiter.
|
|
314
|
-
*/
|
|
315
|
-
function replaceMathDelimiter(md: string, delimiter: MathDelimiterDefinition): string {
|
|
316
|
-
return md.replace(delimiter.regex, (...args) => {
|
|
317
|
-
const match = args[0] ?? '';
|
|
318
|
-
const prefix = args[1] ?? '';
|
|
319
|
-
const math = args[2] ?? '';
|
|
320
|
-
|
|
321
|
-
if (!math) {
|
|
322
|
-
return match;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
try {
|
|
326
|
-
const rendered = katex.renderToString(math, {
|
|
327
|
-
displayMode: delimiter.displayMode,
|
|
328
|
-
throwOnError: false,
|
|
329
|
-
});
|
|
330
|
-
return `${prefix}${rendered}`;
|
|
331
|
-
} catch {
|
|
332
|
-
return match;
|
|
333
|
-
}
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Renders math expressions in markdown using KaTeX for the supported delimiter pairs.
|
|
339
|
-
*
|
|
340
|
-
* Supported delimiters: `$$...$$`, `\[...\]`, `\(...\)`, and `$...$`.
|
|
341
|
-
*
|
|
342
|
-
* @private utility of `MarkdownContent` component
|
|
343
|
-
*/
|
|
344
|
-
function renderMathInMarkdown(md: string): string {
|
|
345
|
-
const { masked, restore } = maskMarkdownCodeSegments(md);
|
|
346
|
-
let processed = masked;
|
|
347
|
-
|
|
348
|
-
for (const delimiter of mathDelimiterDefinitions) {
|
|
349
|
-
processed = replaceMathDelimiter(processed, delimiter);
|
|
350
|
-
}
|
|
351
|
-
processed = processed.replace(/\\$/g, '$');
|
|
352
|
-
return restore(processed);
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* Convert markdown content to HTML
|
|
357
|
-
*
|
|
358
|
-
* TODO: [🧠] Maybe export from `@promptbook/markdown-utils`
|
|
359
|
-
*
|
|
360
|
-
* @param markdown - The markdown content to convert
|
|
361
|
-
* @returns HTML string ready for rendering
|
|
362
|
-
*
|
|
363
|
-
* @public exported from `@promptbook/components`
|
|
364
|
-
*/
|
|
365
|
-
function renderMarkdown(markdown: string_markdown): string_html {
|
|
366
|
-
if (!markdown || typeof markdown !== 'string') {
|
|
367
|
-
return '' as string_html;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
try {
|
|
371
|
-
const normalizedMarkdown = normalizeMarkdownSublists(markdown);
|
|
372
|
-
const { masked: maskedMarkdown, restore: restoreDetails } = maskDetailsBlocks(normalizedMarkdown);
|
|
373
|
-
const processedMarkdown = renderMathInMarkdown(maskedMarkdown);
|
|
374
|
-
const html = chatMarkdownConverter.makeHtml(processedMarkdown);
|
|
375
|
-
|
|
376
|
-
if (typeof window !== 'undefined') {
|
|
377
|
-
if (html.match(/class="katex/)) {
|
|
378
|
-
const katexCssId = 'katex-css';
|
|
379
|
-
if (!window.document.getElementById(katexCssId)) {
|
|
380
|
-
const link = window.document.createElement('link');
|
|
381
|
-
link.id = katexCssId;
|
|
382
|
-
link.rel = 'stylesheet';
|
|
383
|
-
link.href = 'https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css';
|
|
384
|
-
window.document.head.appendChild(link);
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
const restoredHtml = restoreDetails(html as string_html);
|
|
390
|
-
const sanitizedHtml = restoredHtml
|
|
391
|
-
.replace(/<\s*(script|style|iframe|object|embed)[^>]*>[\s\S]*?<\s*\/\s*\1\s*>/gi, '')
|
|
392
|
-
.replace(/\s+on\w+="[^"]*"/gi, '')
|
|
393
|
-
.replace(/\s+javascript:/gi, '')
|
|
394
|
-
.replace(/\s+data:/gi, '')
|
|
395
|
-
.replace(/\s+vbscript:/gi, '');
|
|
396
|
-
|
|
397
|
-
return sanitizedHtml as string_html;
|
|
398
|
-
} catch (error) {
|
|
399
|
-
console.error('Error rendering markdown:', error);
|
|
400
|
-
return markdown.replace(/[<>&"']/g, (char) => {
|
|
401
|
-
const entities: Record<string, string> = {
|
|
402
|
-
'<': '<',
|
|
403
|
-
'>': '>',
|
|
404
|
-
'&': '&',
|
|
405
|
-
'"': '"',
|
|
406
|
-
"'": ''',
|
|
407
|
-
};
|
|
408
|
-
return entities[char] || char;
|
|
409
|
-
}) as string_html;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
|
|
413
18
|
/**
|
|
414
19
|
* Props for markdown content.
|
|
415
20
|
*/
|
|
@@ -481,7 +86,13 @@ function resolveClickedDetailsElement(target: EventTarget | null, container: HTM
|
|
|
481
86
|
*/
|
|
482
87
|
export const MarkdownContent = memo(function MarkdownContent(props: MarkdownContentProps) {
|
|
483
88
|
const { content, className, onCreateAgent } = props;
|
|
484
|
-
const htmlContent = useMemo(
|
|
89
|
+
const htmlContent = useMemo(
|
|
90
|
+
() =>
|
|
91
|
+
renderMarkdown(content, {
|
|
92
|
+
citationReferenceClassName: styles.citationRef,
|
|
93
|
+
}),
|
|
94
|
+
[content],
|
|
95
|
+
);
|
|
485
96
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
486
97
|
const rootsRef = useRef<Root[]>([]);
|
|
487
98
|
/** Tracks which `<details>` elements (by summary key) are currently open */
|