@promptbook/components 0.102.0-3 → 0.102.0-5
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/esm/index.es.js +350 -108
- package/esm/index.es.js.map +1 -1
- package/esm/typings/src/_packages/components.index.d.ts +2 -0
- package/esm/typings/src/book-components/BookEditor/utils.d.ts +8 -0
- package/esm/typings/src/book-components/Chat/save/index.d.ts +6 -0
- package/esm/typings/src/book-components/Chat/save/pdf/pdfSaveFormatDefinition.d.ts +12 -0
- package/esm/typings/src/llm-providers/openai/OpenAiCompatibleExecutionTools.d.ts +5 -1
- package/esm/typings/src/types/Prompt.d.ts +5 -0
- package/esm/typings/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/umd/index.umd.js +349 -106
- package/umd/index.umd.js.map +1 -1
package/esm/index.es.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
-
import { useMemo, useState, useRef, useCallback, useEffect } from 'react';
|
|
2
|
+
import { useMemo, useState, useRef, useCallback, useEffect, memo } from 'react';
|
|
3
3
|
import spaceTrim, { spaceTrim as spaceTrim$1 } from 'spacetrim';
|
|
4
4
|
import 'path';
|
|
5
5
|
import 'crypto';
|
|
@@ -21,7 +21,7 @@ const BOOK_LANGUAGE_VERSION = '1.0.0';
|
|
|
21
21
|
* @generated
|
|
22
22
|
* @see https://github.com/webgptorg/promptbook
|
|
23
23
|
*/
|
|
24
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.102.0-
|
|
24
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.102.0-5';
|
|
25
25
|
/**
|
|
26
26
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
27
27
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -3161,6 +3161,23 @@ const DEFAULT_BOOK_FONT_CLASS = styles.bookEditorSerif;
|
|
|
3161
3161
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
3162
3162
|
*/
|
|
3163
3163
|
|
|
3164
|
+
// Debounce utility for BookEditor and related components
|
|
3165
|
+
// [🧠] Use this for debouncing highlight and other expensive operations
|
|
3166
|
+
/**
|
|
3167
|
+
* @private
|
|
3168
|
+
*/
|
|
3169
|
+
function debounce(fn, delay) {
|
|
3170
|
+
let timeout = null;
|
|
3171
|
+
return (...args) => {
|
|
3172
|
+
if (timeout)
|
|
3173
|
+
clearTimeout(timeout);
|
|
3174
|
+
timeout = setTimeout(() => fn(...args), delay);
|
|
3175
|
+
};
|
|
3176
|
+
}
|
|
3177
|
+
/**
|
|
3178
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
3179
|
+
* TODO: !!! remove this file */
|
|
3180
|
+
|
|
3164
3181
|
/**
|
|
3165
3182
|
* @private util of `<BookEditor />`
|
|
3166
3183
|
*/
|
|
@@ -3173,6 +3190,33 @@ function BookEditorInner(props) {
|
|
|
3173
3190
|
const highlightRef = useRef(null);
|
|
3174
3191
|
const [lineHeight, setLineHeight] = useState(32);
|
|
3175
3192
|
const [isDragOver, setIsDragOver] = useState(false);
|
|
3193
|
+
// Virtualization state: visible line range
|
|
3194
|
+
const [visibleRange, setVisibleRange] = useState([0, 30]);
|
|
3195
|
+
// Debounced update for visible range
|
|
3196
|
+
const updateVisibleRange = useCallback(debounce(() => {
|
|
3197
|
+
const textarea = textareaRef.current;
|
|
3198
|
+
if (!textarea)
|
|
3199
|
+
return;
|
|
3200
|
+
const scrollTop = textarea.scrollTop;
|
|
3201
|
+
const clientHeight = textarea.clientHeight;
|
|
3202
|
+
const totalLines = (value !== null && value !== void 0 ? value : '').split('\n').length;
|
|
3203
|
+
const firstLine = Math.max(0, Math.floor(scrollTop / lineHeight) - 10); // buffer
|
|
3204
|
+
const lastLine = Math.min(totalLines, Math.ceil((scrollTop + clientHeight) / lineHeight) + 10);
|
|
3205
|
+
setVisibleRange([firstLine, lastLine]);
|
|
3206
|
+
}, 30), [value, lineHeight]);
|
|
3207
|
+
// Update visible range on scroll/resize/value change
|
|
3208
|
+
useEffect(() => {
|
|
3209
|
+
updateVisibleRange();
|
|
3210
|
+
const textarea = textareaRef.current;
|
|
3211
|
+
if (!textarea)
|
|
3212
|
+
return;
|
|
3213
|
+
textarea.addEventListener('scroll', updateVisibleRange);
|
|
3214
|
+
window.addEventListener('resize', updateVisibleRange);
|
|
3215
|
+
return () => {
|
|
3216
|
+
textarea.removeEventListener('scroll', updateVisibleRange);
|
|
3217
|
+
window.removeEventListener('resize', updateVisibleRange);
|
|
3218
|
+
};
|
|
3219
|
+
}, [updateVisibleRange]);
|
|
3176
3220
|
const handleChange = useCallback((event) => {
|
|
3177
3221
|
const newValue = event.target.value;
|
|
3178
3222
|
if (controlledValue !== undefined) {
|
|
@@ -3485,29 +3529,32 @@ function BookEditorInner(props) {
|
|
|
3485
3529
|
});
|
|
3486
3530
|
return parameters.sort((a, b) => a.start - b.start);
|
|
3487
3531
|
}, [atParameterRegex, braceParameterRegex]);
|
|
3532
|
+
// Virtualized, debounced highlight rendering for large books
|
|
3488
3533
|
const highlightedHtml = useMemo(() => {
|
|
3489
3534
|
const text = value !== null && value !== void 0 ? value : '';
|
|
3490
|
-
|
|
3535
|
+
const lines = text.split('\n');
|
|
3536
|
+
const [firstLine, lastLine] = visibleRange;
|
|
3537
|
+
const visibleLines = lines.slice(firstLine, lastLine);
|
|
3538
|
+
// Compute offset for correct line numbers
|
|
3539
|
+
const offset = lines.slice(0, firstLine).join('\n').length + (firstLine > 0 ? 1 : 0);
|
|
3491
3540
|
let out = '';
|
|
3492
3541
|
const processedRanges = [];
|
|
3493
|
-
//
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3542
|
+
// Highlighting logic for visible lines only
|
|
3543
|
+
const visibleText = visibleLines.join('\n');
|
|
3544
|
+
// First, handle comment-like commitments (NOTE, COMMENT, NONCE)
|
|
3545
|
+
visibleText.replace(commentRegex, (match, ...args) => {
|
|
3546
|
+
const index = args[args.length - 2] + offset;
|
|
3498
3547
|
const adjustedStart = match.startsWith('\n') ? index + 1 : index;
|
|
3499
3548
|
const adjustedMatch = match.startsWith('\n') ? match.slice(1) : match;
|
|
3500
3549
|
processedRanges.push({ start: adjustedStart, end: adjustedStart + adjustedMatch.length, type: 'comment' });
|
|
3501
3550
|
return match;
|
|
3502
3551
|
});
|
|
3503
|
-
//
|
|
3504
|
-
|
|
3505
|
-
const index = args[args.length - 2];
|
|
3506
|
-
// Adjust index to skip the newline character if present at the beginning of match
|
|
3552
|
+
// META commitments
|
|
3553
|
+
visibleText.replace(metaRegex, (match, ...args) => {
|
|
3554
|
+
const index = args[args.length - 2] + offset;
|
|
3507
3555
|
const adjustedStart = match.startsWith('\n') ? index + 1 : index;
|
|
3508
3556
|
const adjustedMatch = match.startsWith('\n') ? match.slice(1) : match;
|
|
3509
3557
|
const matchEnd = adjustedStart + adjustedMatch.length;
|
|
3510
|
-
// Check if this match overlaps with any existing range (especially comments)
|
|
3511
3558
|
const overlaps = processedRanges.some((range) => (adjustedStart >= range.start && adjustedStart < range.end) ||
|
|
3512
3559
|
(matchEnd > range.start && matchEnd <= range.end) ||
|
|
3513
3560
|
(adjustedStart < range.start && matchEnd > range.end));
|
|
@@ -3516,14 +3563,12 @@ function BookEditorInner(props) {
|
|
|
3516
3563
|
}
|
|
3517
3564
|
return match;
|
|
3518
3565
|
});
|
|
3519
|
-
//
|
|
3520
|
-
|
|
3521
|
-
const index = args[args.length - 2];
|
|
3522
|
-
// Adjust index to skip the newline character if present at the beginning of match
|
|
3566
|
+
// Regular commitment types
|
|
3567
|
+
visibleText.replace(typeRegex, (match, ...args) => {
|
|
3568
|
+
const index = args[args.length - 2] + offset;
|
|
3523
3569
|
const adjustedStart = match.startsWith('\n') ? index + 1 : index;
|
|
3524
3570
|
const adjustedMatch = match.startsWith('\n') ? match.slice(1) : match;
|
|
3525
3571
|
const matchEnd = adjustedStart + adjustedMatch.length;
|
|
3526
|
-
// Check if this match overlaps with any existing range
|
|
3527
3572
|
const overlaps = processedRanges.some((range) => (adjustedStart >= range.start && adjustedStart < range.end) ||
|
|
3528
3573
|
(matchEnd > range.start && matchEnd <= range.end) ||
|
|
3529
3574
|
(adjustedStart < range.start && matchEnd > range.end));
|
|
@@ -3532,29 +3577,28 @@ function BookEditorInner(props) {
|
|
|
3532
3577
|
}
|
|
3533
3578
|
return match;
|
|
3534
3579
|
});
|
|
3535
|
-
//
|
|
3536
|
-
|
|
3537
|
-
const unifiedParameters = extractUnifiedParameters(text);
|
|
3580
|
+
// Parameters
|
|
3581
|
+
const unifiedParameters = extractUnifiedParameters(visibleText);
|
|
3538
3582
|
unifiedParameters.forEach((param) => {
|
|
3539
|
-
|
|
3540
|
-
const
|
|
3541
|
-
|
|
3542
|
-
(
|
|
3583
|
+
const paramStart = param.start + offset;
|
|
3584
|
+
const paramEnd = param.end + offset;
|
|
3585
|
+
const overlaps = processedRanges.some((range) => (paramStart >= range.start && paramStart < range.end) ||
|
|
3586
|
+
(paramEnd > range.start && paramEnd <= range.end) ||
|
|
3587
|
+
(paramStart < range.start && paramEnd > range.end));
|
|
3543
3588
|
if (!overlaps) {
|
|
3544
3589
|
processedRanges.push({
|
|
3545
|
-
start:
|
|
3546
|
-
end:
|
|
3590
|
+
start: paramStart,
|
|
3591
|
+
end: paramEnd,
|
|
3547
3592
|
type: 'parameter',
|
|
3548
3593
|
});
|
|
3549
3594
|
}
|
|
3550
3595
|
});
|
|
3551
3596
|
// Sort ranges by start position
|
|
3552
3597
|
processedRanges.sort((a, b) => a.start - b.start);
|
|
3553
|
-
// Build the highlighted HTML
|
|
3598
|
+
// Build the highlighted HTML for the visible lines only
|
|
3599
|
+
let visibleLastIndex = offset;
|
|
3554
3600
|
processedRanges.forEach((range) => {
|
|
3555
|
-
|
|
3556
|
-
out += escapeHtml(text.slice(lastIndex, range.start));
|
|
3557
|
-
// Add highlighted text with appropriate class
|
|
3601
|
+
out += escapeHtml(text.slice(visibleLastIndex, range.start));
|
|
3558
3602
|
const matchText = text.slice(range.start, range.end);
|
|
3559
3603
|
let cssClass;
|
|
3560
3604
|
switch (range.type) {
|
|
@@ -3562,11 +3606,9 @@ function BookEditorInner(props) {
|
|
|
3562
3606
|
cssClass = 'book-highlight-keyword';
|
|
3563
3607
|
break;
|
|
3564
3608
|
case 'parameter':
|
|
3565
|
-
// Use the unified parameter class, but maintain backward compatibility
|
|
3566
3609
|
cssClass = 'book-highlight-parameter';
|
|
3567
3610
|
break;
|
|
3568
3611
|
case 'comment':
|
|
3569
|
-
// NOTE, COMMENT, NONCE commitments should be highlighted as gray comments
|
|
3570
3612
|
cssClass = 'book-highlight-comment';
|
|
3571
3613
|
break;
|
|
3572
3614
|
default:
|
|
@@ -3574,16 +3616,15 @@ function BookEditorInner(props) {
|
|
|
3574
3616
|
break;
|
|
3575
3617
|
}
|
|
3576
3618
|
out += `<span class="${cssClass}">${escapeHtml(matchText)}</span>`;
|
|
3577
|
-
|
|
3619
|
+
visibleLastIndex = range.end;
|
|
3578
3620
|
});
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
}, [value, typeRegex, metaRegex, extractUnifiedParameters]);
|
|
3621
|
+
out += escapeHtml(text.slice(visibleLastIndex, offset + visibleText.length));
|
|
3622
|
+
const resultLines = out.split('\n').slice(firstLine, lastLine);
|
|
3623
|
+
if (resultLines.length > 0 && firstLine === 0) {
|
|
3624
|
+
resultLines[0] = `<span class="book-highlight-title">${resultLines[0]}</span>`;
|
|
3625
|
+
}
|
|
3626
|
+
return resultLines.join('\n');
|
|
3627
|
+
}, [value, typeRegex, metaRegex, extractUnifiedParameters, visibleRange]);
|
|
3587
3628
|
return (jsx("div", { className: classNames(styles.bookEditorContainer, isVerbose && styles.isVerbose, className), children: jsxs("div", { className: classNames(styles.bookEditorWrapper, effectiveFontClassName, isBorderRadiusDisabled && styles.isBorderRadiusDisabled), children: [jsx("div", { "aria-hidden": true, className: styles.bookEditorBackground, style: { backgroundImage: 'none' } }), jsx("pre", { ref: highlightRef, "aria-hidden": true, className: `${styles.bookEditorHighlight} ${effectiveFontClassName}`, style: {
|
|
3588
3629
|
lineHeight: `${lineHeight}px`,
|
|
3589
3630
|
backgroundImage: `linear-gradient(90deg, transparent 30px, rgba(59,130,246,0.3) 30px, rgba(59,130,246,0.3) 31px, transparent 31px), repeating-linear-gradient(0deg, transparent, transparent calc(${lineHeight}px - 1px), rgba(0,0,0,0.06) ${lineHeight}px)`,
|
|
@@ -4712,6 +4753,18 @@ function useChatAutoScroll(config = {}) {
|
|
|
4712
4753
|
};
|
|
4713
4754
|
}
|
|
4714
4755
|
|
|
4756
|
+
/**
|
|
4757
|
+
* Utility to compute readable text color based on background
|
|
4758
|
+
*/
|
|
4759
|
+
function getTextColor(bgColor) {
|
|
4760
|
+
// Simple luminance check for contrast
|
|
4761
|
+
const hex = bgColor.replace('#', '');
|
|
4762
|
+
const r = parseInt(hex.substring(0, 2), 16);
|
|
4763
|
+
const g = parseInt(hex.substring(2, 4), 16);
|
|
4764
|
+
const b = parseInt(hex.substring(4, 6), 16);
|
|
4765
|
+
const luminance = (0.299 * r + 0.587 * g + 0.114 * b);
|
|
4766
|
+
return luminance > 186 ? '#222' : '#fff';
|
|
4767
|
+
}
|
|
4715
4768
|
/**
|
|
4716
4769
|
* HTML export plugin
|
|
4717
4770
|
*
|
|
@@ -4720,7 +4773,112 @@ function useChatAutoScroll(config = {}) {
|
|
|
4720
4773
|
const htmlSaveFormatDefinition = {
|
|
4721
4774
|
formatName: 'html',
|
|
4722
4775
|
label: 'HTML',
|
|
4723
|
-
getContent: (messages) =>
|
|
4776
|
+
getContent: (messages) => spaceTrim(`
|
|
4777
|
+
<!DOCTYPE html>
|
|
4778
|
+
<html lang="en">
|
|
4779
|
+
<head>
|
|
4780
|
+
<meta charset="UTF-8" />
|
|
4781
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
4782
|
+
<title>Chat Export - Promptbook</title>
|
|
4783
|
+
<style>
|
|
4784
|
+
body {
|
|
4785
|
+
font-family: 'Segoe UI', Arial, sans-serif;
|
|
4786
|
+
background: #f7f8fa;
|
|
4787
|
+
margin: 0;
|
|
4788
|
+
padding: 0;
|
|
4789
|
+
}
|
|
4790
|
+
.chat-container {
|
|
4791
|
+
max-width: 600px;
|
|
4792
|
+
margin: 40px auto 20px auto;
|
|
4793
|
+
background: #fff;
|
|
4794
|
+
border-radius: 12px;
|
|
4795
|
+
box-shadow: 0 2px 16px rgba(0,0,0,0.07);
|
|
4796
|
+
padding: 24px 18px 18px 18px;
|
|
4797
|
+
}
|
|
4798
|
+
.chat-message {
|
|
4799
|
+
display: flex;
|
|
4800
|
+
align-items: flex-start;
|
|
4801
|
+
margin-bottom: 18px;
|
|
4802
|
+
border-radius: 10px;
|
|
4803
|
+
padding: 0;
|
|
4804
|
+
}
|
|
4805
|
+
.avatar {
|
|
4806
|
+
width: 38px;
|
|
4807
|
+
height: 38px;
|
|
4808
|
+
border-radius: 50%;
|
|
4809
|
+
background: #ccc;
|
|
4810
|
+
margin-right: 14px;
|
|
4811
|
+
flex-shrink: 0;
|
|
4812
|
+
display: flex;
|
|
4813
|
+
align-items: center;
|
|
4814
|
+
justify-content: center;
|
|
4815
|
+
font-weight: bold;
|
|
4816
|
+
font-size: 18px;
|
|
4817
|
+
color: #fff;
|
|
4818
|
+
box-shadow: 0 1px 4px rgba(0,0,0,0.04);
|
|
4819
|
+
}
|
|
4820
|
+
.bubble {
|
|
4821
|
+
border-radius: 10px;
|
|
4822
|
+
padding: 12px 16px;
|
|
4823
|
+
min-width: 80px;
|
|
4824
|
+
max-width: 100%;
|
|
4825
|
+
word-break: break-word;
|
|
4826
|
+
box-shadow: 0 1px 4px rgba(0,0,0,0.03);
|
|
4827
|
+
font-size: 16px;
|
|
4828
|
+
line-height: 1.6;
|
|
4829
|
+
margin-top: 2px;
|
|
4830
|
+
}
|
|
4831
|
+
.from-label {
|
|
4832
|
+
font-weight: bold;
|
|
4833
|
+
font-size: 15px;
|
|
4834
|
+
margin-bottom: 4px;
|
|
4835
|
+
display: block;
|
|
4836
|
+
opacity: 0.85;
|
|
4837
|
+
}
|
|
4838
|
+
.footer {
|
|
4839
|
+
text-align: center;
|
|
4840
|
+
margin: 32px 0 18px 0;
|
|
4841
|
+
color: #888;
|
|
4842
|
+
font-size: 15px;
|
|
4843
|
+
opacity: 0.85;
|
|
4844
|
+
}
|
|
4845
|
+
.footer a {
|
|
4846
|
+
color: #2b7cff;
|
|
4847
|
+
text-decoration: none;
|
|
4848
|
+
font-weight: bold;
|
|
4849
|
+
}
|
|
4850
|
+
</style>
|
|
4851
|
+
</head>
|
|
4852
|
+
<body>
|
|
4853
|
+
<div class="chat-container">
|
|
4854
|
+
${messages.map((m) => {
|
|
4855
|
+
// Fallback color map for common participants
|
|
4856
|
+
const participantColors = {
|
|
4857
|
+
USER: '#2b7cff',
|
|
4858
|
+
ASSISTANT: '#ffb300',
|
|
4859
|
+
SYSTEM: '#888',
|
|
4860
|
+
};
|
|
4861
|
+
const bgColor = participantColors[String(m.from)] || '#2b7cff';
|
|
4862
|
+
const textColor = getTextColor(bgColor);
|
|
4863
|
+
return spaceTrim(`
|
|
4864
|
+
<div class="chat-message">
|
|
4865
|
+
<div class="avatar" style="background:${bgColor};color:${getTextColor(bgColor)};">
|
|
4866
|
+
${String(m.from)[0] || '?'}
|
|
4867
|
+
</div>
|
|
4868
|
+
<div class="bubble" style="background:${bgColor};color:${textColor};">
|
|
4869
|
+
<span class="from-label">${String(m.from)}:</span>
|
|
4870
|
+
${m.content}
|
|
4871
|
+
</div>
|
|
4872
|
+
</div>
|
|
4873
|
+
`);
|
|
4874
|
+
}).join('')}
|
|
4875
|
+
</div>
|
|
4876
|
+
<div class="footer">
|
|
4877
|
+
Exported from <a href="https://ptbk.io" target="_blank" rel="noopener">Promptbook</a>
|
|
4878
|
+
</div>
|
|
4879
|
+
</body>
|
|
4880
|
+
</html>
|
|
4881
|
+
`),
|
|
4724
4882
|
mimeType: 'text/html',
|
|
4725
4883
|
fileExtension: 'html',
|
|
4726
4884
|
};
|
|
@@ -4746,7 +4904,19 @@ const jsonSaveFormatDefinition = {
|
|
|
4746
4904
|
const mdSaveFormatDefinition = {
|
|
4747
4905
|
formatName: 'md',
|
|
4748
4906
|
label: 'Markdown',
|
|
4749
|
-
getContent: (messages) =>
|
|
4907
|
+
getContent: (messages) => spaceTrim$1(`
|
|
4908
|
+
${messages
|
|
4909
|
+
.map((m) => spaceTrim$1(`
|
|
4910
|
+
**${m.from}:**
|
|
4911
|
+
|
|
4912
|
+
> ${m.content.replace(/\n/g, '\n> ')}
|
|
4913
|
+
`))
|
|
4914
|
+
.join('\n\n---\n\n')}
|
|
4915
|
+
|
|
4916
|
+
---
|
|
4917
|
+
|
|
4918
|
+
_Exported from [Promptbook](https://ptbk.io)_
|
|
4919
|
+
`),
|
|
4750
4920
|
mimeType: 'text/markdown',
|
|
4751
4921
|
fileExtension: 'md',
|
|
4752
4922
|
};
|
|
@@ -4764,6 +4934,25 @@ const txtSaveFormatDefinition = {
|
|
|
4764
4934
|
fileExtension: 'txt',
|
|
4765
4935
|
};
|
|
4766
4936
|
|
|
4937
|
+
/**
|
|
4938
|
+
* PDF export plugin
|
|
4939
|
+
*
|
|
4940
|
+
* @public exported from `@promptbook/components`
|
|
4941
|
+
*/
|
|
4942
|
+
const pdfSaveFormatDefinition = {
|
|
4943
|
+
formatName: 'pdf',
|
|
4944
|
+
label: 'PDF',
|
|
4945
|
+
getContent: (messages) => {
|
|
4946
|
+
htmlSaveFormatDefinition.getContent(messages);
|
|
4947
|
+
// <- TODO: !!!!
|
|
4948
|
+
// PDF conversion should be implemented here (sync or pre-generated)
|
|
4949
|
+
// For now, return a placeholder string
|
|
4950
|
+
return '[PDF chat save not implemented. Integrate a PDF library for conversion.]';
|
|
4951
|
+
},
|
|
4952
|
+
mimeType: 'application/pdf',
|
|
4953
|
+
fileExtension: 'pdf',
|
|
4954
|
+
};
|
|
4955
|
+
|
|
4767
4956
|
/**
|
|
4768
4957
|
* Registry of all built-in chat save plugins
|
|
4769
4958
|
*
|
|
@@ -4774,6 +4963,7 @@ const CHAT_SAVE_FORMATS = [
|
|
|
4774
4963
|
txtSaveFormatDefinition,
|
|
4775
4964
|
mdSaveFormatDefinition,
|
|
4776
4965
|
htmlSaveFormatDefinition,
|
|
4966
|
+
pdfSaveFormatDefinition,
|
|
4777
4967
|
];
|
|
4778
4968
|
/**
|
|
4779
4969
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -4951,6 +5141,107 @@ const LOADING_INTERACTIVE_IMAGE = 'Loading...';
|
|
|
4951
5141
|
|
|
4952
5142
|
// [🚉] Avatar dimensions constant to prevent layout jumps and maintain DRY principle
|
|
4953
5143
|
const AVATAR_SIZE = 40;
|
|
5144
|
+
const ChatMessageItem = memo(({ message, participant, participants, isLastMessage, onMessage, setExpandedMessageId, isExpanded, currentRating, handleRating, mode, }) => {
|
|
5145
|
+
const avatarSrc = (participant === null || participant === void 0 ? void 0 : participant.avatarSrc) || '';
|
|
5146
|
+
const color = Color.from((participant && participant.color) || '#ccc');
|
|
5147
|
+
const colorOfText = color.then(textColor);
|
|
5148
|
+
const { contentWithoutButtons, buttons } = parseMessageButtons(message.content);
|
|
5149
|
+
const shouldShowButtons = isLastMessage && buttons.length > 0 && onMessage;
|
|
5150
|
+
const [localHoveredRating, setLocalHoveredRating] = useState(0);
|
|
5151
|
+
useEffect(() => {
|
|
5152
|
+
if (!isExpanded) {
|
|
5153
|
+
setLocalHoveredRating(0);
|
|
5154
|
+
}
|
|
5155
|
+
}, [isExpanded]);
|
|
5156
|
+
return (jsxs("div", { className: classNames(chatStyles.chatMessage, (participant === null || participant === void 0 ? void 0 : participant.isMe) && chatStyles.isMe, !message.isComplete && chatStyles.isNotCompleteMessage), onClick: () => {
|
|
5157
|
+
console.group('💬', message.content);
|
|
5158
|
+
console.info('message', message);
|
|
5159
|
+
console.info('participant', participant);
|
|
5160
|
+
console.info('participants', participants);
|
|
5161
|
+
console.info('participant avatarSrc', avatarSrc);
|
|
5162
|
+
console.info('participant color', { color, colorOfText });
|
|
5163
|
+
console.groupEnd();
|
|
5164
|
+
}, children: [avatarSrc && (jsx("div", { className: chatStyles.avatar, children: jsx("img", { width: AVATAR_SIZE, height: AVATAR_SIZE, src: avatarSrc, alt: `Avatar of ${message.from.toString().toLocaleLowerCase()}`, style: {
|
|
5165
|
+
backgroundColor: color.toHex(),
|
|
5166
|
+
width: AVATAR_SIZE,
|
|
5167
|
+
height: AVATAR_SIZE,
|
|
5168
|
+
borderRadius: '50%',
|
|
5169
|
+
objectFit: 'cover',
|
|
5170
|
+
border: '2px solid rgba(125, 125, 125, 0.1)',
|
|
5171
|
+
flexShrink: 0,
|
|
5172
|
+
} }) })), jsxs("div", { className: chatStyles.messageText, style: {
|
|
5173
|
+
backgroundColor: color.toHex(),
|
|
5174
|
+
color: colorOfText.toHex(),
|
|
5175
|
+
}, children: [message.isVoiceCall && (jsx("div", { className: chatStyles.voiceCallIndicator, children: jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: jsx("path", { d: "M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z" }) }) })), message.content === LOADING_INTERACTIVE_IMAGE ? (jsx(Fragment, {})) : (jsx("div", { dangerouslySetInnerHTML: {
|
|
5176
|
+
__html: renderMarkdown(contentWithoutButtons),
|
|
5177
|
+
} })), !message.isComplete && jsx("span", { className: chatStyles.NonCompleteMessageFiller, children: '_'.repeat(70) }), shouldShowButtons && (jsx("div", { className: chatStyles.messageButtons, children: buttons.map((button, buttonIndex) => (jsx("button", { className: chatStyles.messageButton, onClick: (event) => {
|
|
5178
|
+
event.stopPropagation();
|
|
5179
|
+
if (onMessage) {
|
|
5180
|
+
onMessage(button.message);
|
|
5181
|
+
}
|
|
5182
|
+
}, dangerouslySetInnerHTML: {
|
|
5183
|
+
__html: renderMarkdown(button.text),
|
|
5184
|
+
} }, buttonIndex))) })), message.isComplete && (jsx("div", { className: chatStyles.rating, onMouseEnter: () => {
|
|
5185
|
+
setExpandedMessageId(message.id);
|
|
5186
|
+
}, onMouseLeave: () => {
|
|
5187
|
+
setExpandedMessageId(null);
|
|
5188
|
+
setLocalHoveredRating(0);
|
|
5189
|
+
}, children: isExpanded ? ([1, 2, 3, 4, 5].map((star) => (jsx("span", { onClick: () => handleRating(message, star), onMouseEnter: () => setLocalHoveredRating(star), style: {
|
|
5190
|
+
cursor: 'pointer',
|
|
5191
|
+
fontSize: '20px',
|
|
5192
|
+
color: star <= (localHoveredRating || currentRating || 0)
|
|
5193
|
+
? '#FFD700'
|
|
5194
|
+
: mode === 'LIGHT'
|
|
5195
|
+
? '#ccc'
|
|
5196
|
+
: '#555',
|
|
5197
|
+
transition: 'color 0.2s',
|
|
5198
|
+
}, children: "\u2B50" }, star)))) : (jsx("span", { onClick: () => handleRating(message, currentRating || 1), style: {
|
|
5199
|
+
cursor: 'pointer',
|
|
5200
|
+
fontSize: '20px',
|
|
5201
|
+
color: currentRating ? '#FFD700' : mode === 'LIGHT' ? '#888' : '#666',
|
|
5202
|
+
transition: 'color 0.2s',
|
|
5203
|
+
}, children: "\u2B50" })) }))] })] }));
|
|
5204
|
+
}, (prev, next) => {
|
|
5205
|
+
var _a, _b, _c, _d;
|
|
5206
|
+
if (prev.message.id !== next.message.id) {
|
|
5207
|
+
return false;
|
|
5208
|
+
}
|
|
5209
|
+
if (prev.message.content !== next.message.content) {
|
|
5210
|
+
return false;
|
|
5211
|
+
}
|
|
5212
|
+
if (((_a = prev.message.isComplete) !== null && _a !== void 0 ? _a : true) !== ((_b = next.message.isComplete) !== null && _b !== void 0 ? _b : true)) {
|
|
5213
|
+
return false;
|
|
5214
|
+
}
|
|
5215
|
+
if (((_c = prev.message.isVoiceCall) !== null && _c !== void 0 ? _c : false) !== ((_d = next.message.isVoiceCall) !== null && _d !== void 0 ? _d : false)) {
|
|
5216
|
+
return false;
|
|
5217
|
+
}
|
|
5218
|
+
if (prev.participant !== next.participant) {
|
|
5219
|
+
return false;
|
|
5220
|
+
}
|
|
5221
|
+
if (prev.participants !== next.participants) {
|
|
5222
|
+
return false;
|
|
5223
|
+
}
|
|
5224
|
+
if (prev.isLastMessage !== next.isLastMessage) {
|
|
5225
|
+
return false;
|
|
5226
|
+
}
|
|
5227
|
+
if (prev.onMessage !== next.onMessage) {
|
|
5228
|
+
return false;
|
|
5229
|
+
}
|
|
5230
|
+
if (prev.setExpandedMessageId !== next.setExpandedMessageId) {
|
|
5231
|
+
return false;
|
|
5232
|
+
}
|
|
5233
|
+
if (prev.isExpanded !== next.isExpanded) {
|
|
5234
|
+
return false;
|
|
5235
|
+
}
|
|
5236
|
+
if (prev.currentRating !== next.currentRating) {
|
|
5237
|
+
return false;
|
|
5238
|
+
}
|
|
5239
|
+
if (prev.handleRating !== next.handleRating) {
|
|
5240
|
+
return false;
|
|
5241
|
+
}
|
|
5242
|
+
return prev.mode === next.mode;
|
|
5243
|
+
});
|
|
5244
|
+
ChatMessageItem.displayName = 'ChatMessageItem';
|
|
4954
5245
|
/**
|
|
4955
5246
|
* Renders a chat with messages and input for new messages
|
|
4956
5247
|
*
|
|
@@ -5133,9 +5424,13 @@ function Chat(props) {
|
|
|
5133
5424
|
const scrollToBottomCssClassName = useChatCssClassName('scrollToBottom');
|
|
5134
5425
|
const handleRating = useCallback(async (message, newRating) => {
|
|
5135
5426
|
setSelectedMessage(message);
|
|
5136
|
-
setMessageRatings(
|
|
5427
|
+
setMessageRatings((previousRatings) => {
|
|
5428
|
+
const nextRatings = new Map(previousRatings);
|
|
5429
|
+
nextRatings.set(message.id, newRating);
|
|
5430
|
+
return nextRatings;
|
|
5431
|
+
});
|
|
5137
5432
|
setRatingModalOpen(true);
|
|
5138
|
-
}, [
|
|
5433
|
+
}, []);
|
|
5139
5434
|
const submitRating = useCallback(async () => {
|
|
5140
5435
|
if (!selectedMessage)
|
|
5141
5436
|
return;
|
|
@@ -5248,67 +5543,10 @@ function Chat(props) {
|
|
|
5248
5543
|
cursor: 'pointer',
|
|
5249
5544
|
}, onClick: () => handleDownload(formatDefinition.formatName), children: formatDefinition.label }, formatDefinition.formatName))) }))] })), onUseTemplate && (jsxs("button", { className: classNames(chatStyles.useTemplateButton), onClick: onUseTemplate, children: [jsx("span", { className: chatStyles.resetButtonText, children: "Use this template" }), jsx(TemplateIcon, { size: 16 })] })), extraActions] }), jsx("div", { className: classNames(chatStyles.chatMessages, useChatCssClassName('chatMessages')), ref: chatMessagesRef, onScroll: handleScroll, children: postprocessedMessages.map((message, i) => {
|
|
5250
5545
|
const participant = participants.find((participant) => participant.name === message.from);
|
|
5251
|
-
const avatarSrc = (participant && participant.avatarSrc) || '';
|
|
5252
|
-
const color = Color.from((participant && participant.color) || '#ccc');
|
|
5253
|
-
const colorOfText = color.then(textColor);
|
|
5254
|
-
// Parse buttons from message content
|
|
5255
|
-
const { contentWithoutButtons, buttons } = parseMessageButtons(message.content);
|
|
5256
|
-
// Only show buttons for the last message
|
|
5257
5546
|
const isLastMessage = i === postprocessedMessages.length - 1;
|
|
5258
|
-
const
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
console.info('message', message);
|
|
5262
|
-
console.info('participant', participant);
|
|
5263
|
-
console.info('participants', participants);
|
|
5264
|
-
console.info('participant avatarSrc', avatarSrc);
|
|
5265
|
-
console.info('participant color', { color, colorOfText });
|
|
5266
|
-
console.groupEnd();
|
|
5267
|
-
}, children: [avatarSrc && (jsx("div", { className: chatStyles.avatar, children: jsx("img", { width: AVATAR_SIZE, height: AVATAR_SIZE, src: avatarSrc, alt: `Avatar of ${message.from.toString().toLocaleLowerCase()}`, style: {
|
|
5268
|
-
backgroundColor: color.toHex(),
|
|
5269
|
-
width: AVATAR_SIZE,
|
|
5270
|
-
height: AVATAR_SIZE,
|
|
5271
|
-
borderRadius: '50%',
|
|
5272
|
-
objectFit: 'cover',
|
|
5273
|
-
border: '2px solid rgba(125, 125, 125, 0.1)',
|
|
5274
|
-
flexShrink: 0,
|
|
5275
|
-
} }) })), jsxs("div", { className: chatStyles.messageText, style: {
|
|
5276
|
-
backgroundColor: color.toHex(),
|
|
5277
|
-
color: colorOfText.toHex(),
|
|
5278
|
-
}, children: [message.isVoiceCall && (jsx("div", { className: chatStyles.voiceCallIndicator, children: jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: jsx("path", { d: "M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z" }) }) })), message.content === LOADING_INTERACTIVE_IMAGE ? (jsx(Fragment, {})) : (jsx("div", { dangerouslySetInnerHTML: {
|
|
5279
|
-
__html: renderMarkdown(contentWithoutButtons),
|
|
5280
|
-
} })), !message.isComplete && (jsx("span", { className: chatStyles.NonCompleteMessageFiller, children: '_'.repeat(70) })), shouldShowButtons && (jsx("div", { className: chatStyles.messageButtons, children: buttons.map((button, buttonIndex) => (jsx("button", { className: chatStyles.messageButton, onClick: (event) => {
|
|
5281
|
-
event.stopPropagation();
|
|
5282
|
-
if (onMessage) {
|
|
5283
|
-
onMessage(button.message);
|
|
5284
|
-
}
|
|
5285
|
-
}, dangerouslySetInnerHTML: {
|
|
5286
|
-
__html: renderMarkdown(button.text),
|
|
5287
|
-
} }, buttonIndex))) })), message.isComplete && (jsx("div", { className: chatStyles.rating, onMouseEnter: () => setExpandedMessageId(message.id), onMouseLeave: () => {
|
|
5288
|
-
setExpandedMessageId(null);
|
|
5289
|
-
setHoveredRating(0);
|
|
5290
|
-
}, children: expandedMessageId === message.id ? ([1, 2, 3, 4, 5].map((star) => (jsx("span", { onClick: () => handleRating(message, star), onMouseEnter: () => setHoveredRating(star), style: {
|
|
5291
|
-
cursor: 'pointer',
|
|
5292
|
-
fontSize: '20px',
|
|
5293
|
-
color: star <=
|
|
5294
|
-
(hoveredRating ||
|
|
5295
|
-
messageRatings.get(message.id) ||
|
|
5296
|
-
0)
|
|
5297
|
-
? '#FFD700'
|
|
5298
|
-
: mode === 'LIGHT'
|
|
5299
|
-
? '#ccc'
|
|
5300
|
-
: '#555',
|
|
5301
|
-
transition: 'color 0.2s',
|
|
5302
|
-
}, children: "\u2B50" }, star)))) : (jsx("span", { onClick: () => handleRating(message, messageRatings.get(message.id) || 1), style: {
|
|
5303
|
-
cursor: 'pointer',
|
|
5304
|
-
fontSize: '20px',
|
|
5305
|
-
color: messageRatings.get(message.id)
|
|
5306
|
-
? '#FFD700'
|
|
5307
|
-
: mode === 'LIGHT'
|
|
5308
|
-
? '#888'
|
|
5309
|
-
: '#666',
|
|
5310
|
-
transition: 'color 0.2s',
|
|
5311
|
-
}, children: "\u2B50" })) }))] })] }, message.id));
|
|
5547
|
+
const isExpanded = expandedMessageId === message.id;
|
|
5548
|
+
const currentRating = messageRatings.get(message.id) || 0;
|
|
5549
|
+
return (jsx(ChatMessageItem, { message: message, participant: participant, participants: participants, isLastMessage: isLastMessage, onMessage: onMessage, setExpandedMessageId: setExpandedMessageId, isExpanded: isExpanded, currentRating: currentRating, handleRating: handleRating, mode: mode }, message.id));
|
|
5312
5550
|
}) }), onMessage && (jsxs("div", { className: classNames(chatStyles.chatInput, useChatCssClassName('chatInput'), isDragOver && chatStyles.dragOver), ...(onFileUpload
|
|
5313
5551
|
? {
|
|
5314
5552
|
onDrop: handleDrop,
|
|
@@ -5350,7 +5588,11 @@ function Chat(props) {
|
|
|
5350
5588
|
if (e.target === e.currentTarget && isMobile) {
|
|
5351
5589
|
setRatingModalOpen(false);
|
|
5352
5590
|
}
|
|
5353
|
-
}, children: jsxs("div", { className: chatStyles.ratingModalContent, children: [jsx("h3", { children: "Rate this response" }), jsx("div", { className: chatStyles.stars, children: [1, 2, 3, 4, 5].map((star) => (jsx("span", { onClick: () => setMessageRatings(
|
|
5591
|
+
}, children: jsxs("div", { className: chatStyles.ratingModalContent, children: [jsx("h3", { children: "Rate this response" }), jsx("div", { className: chatStyles.stars, children: [1, 2, 3, 4, 5].map((star) => (jsx("span", { onClick: () => setMessageRatings((previousRatings) => {
|
|
5592
|
+
const nextRatings = new Map(previousRatings);
|
|
5593
|
+
nextRatings.set(selectedMessage.id, star);
|
|
5594
|
+
return nextRatings;
|
|
5595
|
+
}), onMouseEnter: () => setHoveredRating(star), onMouseLeave: () => setHoveredRating(0), style: {
|
|
5354
5596
|
cursor: 'pointer',
|
|
5355
5597
|
fontSize: '24px',
|
|
5356
5598
|
color: star <= (hoveredRating || messageRatings.get(selectedMessage.id) || 0)
|
|
@@ -6001,5 +6243,5 @@ function MockedChat(props) {
|
|
|
6001
6243
|
onMessage: isSimulationComplete && chatProps.onMessage ? chatProps.onMessage : undefined }));
|
|
6002
6244
|
}
|
|
6003
6245
|
|
|
6004
|
-
export { ArrowIcon, AttachmentIcon, AvatarChip, AvatarChipFromSource, AvatarProfile, AvatarProfileFromSource, BLOCKY_FLOW, BOOK_LANGUAGE_VERSION, BookEditor, CHAT_SAVE_FORMATS, Chat, CloseIcon, DEFAULT_BOOK_FONT_CLASS, FAST_FLOW, LlmChat, MOCKED_CHAT_DELAY_CONFIGS, MockedChat, NORMAL_FLOW, PROMPTBOOK_ENGINE_VERSION, PauseIcon, PlayIcon, RANDOM_FLOW, ResetIcon, SLOW_FLOW, SendIcon, TemplateIcon, getChatSaveFormatDefinitions, htmlSaveFormatDefinition, isMarkdownContent, jsonSaveFormatDefinition, mdSaveFormatDefinition, parseMessageButtons, renderMarkdown, txtSaveFormatDefinition, useChatAutoScroll, useSendMessageToLlmChat };
|
|
6246
|
+
export { ArrowIcon, AttachmentIcon, AvatarChip, AvatarChipFromSource, AvatarProfile, AvatarProfileFromSource, BLOCKY_FLOW, BOOK_LANGUAGE_VERSION, BookEditor, CHAT_SAVE_FORMATS, Chat, CloseIcon, DEFAULT_BOOK_FONT_CLASS, FAST_FLOW, LlmChat, MOCKED_CHAT_DELAY_CONFIGS, MockedChat, NORMAL_FLOW, PROMPTBOOK_ENGINE_VERSION, PauseIcon, PlayIcon, RANDOM_FLOW, ResetIcon, SLOW_FLOW, SendIcon, TemplateIcon, getChatSaveFormatDefinitions, htmlSaveFormatDefinition, isMarkdownContent, jsonSaveFormatDefinition, mdSaveFormatDefinition, parseMessageButtons, pdfSaveFormatDefinition, renderMarkdown, txtSaveFormatDefinition, useChatAutoScroll, useSendMessageToLlmChat };
|
|
6005
6247
|
//# sourceMappingURL=index.es.js.map
|