@memori.ai/memori-react 8.41.1 → 8.41.3
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/CHANGELOG.md +21 -0
- package/dist/components/Avatar/Avatar.js +5 -10
- package/dist/components/Avatar/Avatar.js.map +1 -1
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js +8 -1
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js.map +1 -1
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/controllers/MorphTargetController.js +1 -1
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/controllers/MorphTargetController.js.map +1 -1
- package/dist/components/Avatar/AvatarView/AvatarComponent/constants.d.ts +2 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/constants.js +14 -5
- package/dist/components/Avatar/AvatarView/AvatarComponent/constants.js.map +1 -1
- package/dist/components/ChatBubble/ChatBubble.js +16 -11
- package/dist/components/ChatBubble/ChatBubble.js.map +1 -1
- package/dist/components/UploadButton/UploadDocuments/UploadDocuments.js +79 -52
- package/dist/components/UploadButton/UploadDocuments/UploadDocuments.js.map +1 -1
- package/dist/components/layouts/website-assistant.css +2 -1
- package/dist/types/integration.d.ts +3 -0
- package/dist/types/integration.js +16 -0
- package/dist/types/integration.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/esm/components/Avatar/Avatar.js +5 -10
- package/esm/components/Avatar/Avatar.js.map +1 -1
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js +9 -2
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js.map +1 -1
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/controllers/MorphTargetController.js +2 -2
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/controllers/MorphTargetController.js.map +1 -1
- package/esm/components/Avatar/AvatarView/AvatarComponent/constants.d.ts +2 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/constants.js +13 -4
- package/esm/components/Avatar/AvatarView/AvatarComponent/constants.js.map +1 -1
- package/esm/components/ChatBubble/ChatBubble.js +16 -11
- package/esm/components/ChatBubble/ChatBubble.js.map +1 -1
- package/esm/components/UploadButton/UploadDocuments/UploadDocuments.js +79 -52
- package/esm/components/UploadButton/UploadDocuments/UploadDocuments.js.map +1 -1
- package/esm/components/layouts/website-assistant.css +2 -1
- package/esm/types/integration.d.ts +3 -0
- package/esm/types/integration.js +11 -0
- package/esm/types/integration.js.map +1 -0
- package/esm/version.d.ts +1 -1
- package/esm/version.js +1 -1
- package/package.json +1 -1
- package/src/components/Avatar/Avatar.stories.tsx +45 -28
- package/src/components/Avatar/Avatar.test.tsx +54 -0
- package/src/components/Avatar/Avatar.tsx +8 -12
- package/src/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.tsx +12 -5
- package/src/components/Avatar/AvatarView/AvatarComponent/components/controllers/MorphTargetController.ts +2 -1
- package/src/components/Avatar/AvatarView/AvatarComponent/constants.ts +16 -4
- package/src/components/Avatar/__snapshots__/Avatar.test.tsx.snap +126 -0
- package/src/components/ChatBubble/ChatBubble.tsx +28 -3
- package/src/components/UploadButton/UploadDocuments/UploadDocuments.tsx +103 -55
- package/src/components/layouts/layouts.stories.tsx +6 -13
- package/src/components/layouts/website-assistant.css +2 -1
- package/src/types/integration.test.ts +32 -0
- package/src/types/integration.ts +31 -0
- package/src/version.ts +1 -1
|
@@ -17,6 +17,7 @@ import Edit from '../icons/Edit';
|
|
|
17
17
|
import cx from 'classnames';
|
|
18
18
|
import ContainerAvatarView from './AvatarView';
|
|
19
19
|
import { useViseme } from '../../context/visemeContext';
|
|
20
|
+
import { is3dAvatarWithUrl, usesRpmAvatarView } from '../../types/integration';
|
|
20
21
|
|
|
21
22
|
export interface Props {
|
|
22
23
|
memori: Memori;
|
|
@@ -90,11 +91,10 @@ const Avatar: React.FC<Props> = ({
|
|
|
90
91
|
|
|
91
92
|
const renderAvatar = () => {
|
|
92
93
|
if (
|
|
93
|
-
(
|
|
94
|
-
integrationConfig?.avatar
|
|
95
|
-
integrationConfig?.
|
|
96
|
-
|
|
97
|
-
integrationConfig?.avatarURL &&
|
|
94
|
+
is3dAvatarWithUrl(
|
|
95
|
+
integrationConfig?.avatar,
|
|
96
|
+
integrationConfig?.avatarURL
|
|
97
|
+
) &&
|
|
98
98
|
avatarType &&
|
|
99
99
|
avatarType !== 'blob'
|
|
100
100
|
) {
|
|
@@ -123,12 +123,8 @@ const Avatar: React.FC<Props> = ({
|
|
|
123
123
|
};
|
|
124
124
|
|
|
125
125
|
const renderAvatarContent = () => {
|
|
126
|
-
if (!isClient) return null;
|
|
127
|
-
if (
|
|
128
|
-
integrationConfig?.avatar === 'readyplayerme' ||
|
|
129
|
-
integrationConfig?.avatar === 'readyplayerme-full' ||
|
|
130
|
-
integrationConfig?.avatar === 'customrpm'
|
|
131
|
-
) {
|
|
126
|
+
if (!isClient || !integrationConfig) return null;
|
|
127
|
+
if (usesRpmAvatarView(integrationConfig.avatar)) {
|
|
132
128
|
return (
|
|
133
129
|
<ErrorBoundary
|
|
134
130
|
fallback={
|
|
@@ -163,7 +159,7 @@ const Avatar: React.FC<Props> = ({
|
|
|
163
159
|
);
|
|
164
160
|
}
|
|
165
161
|
|
|
166
|
-
if (integrationConfig
|
|
162
|
+
if (integrationConfig.avatar === 'customglb') {
|
|
167
163
|
return (
|
|
168
164
|
<ModelViewer
|
|
169
165
|
poster={getAvatarUrl() || ''}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
ANIMATION_URLS,
|
|
10
10
|
AVATAR_POSITION,
|
|
11
11
|
AVATAR_ROTATION,
|
|
12
|
+
MALE_EXCLUDED_ANIMATIONS,
|
|
12
13
|
SCALE_LERP_FACTOR,
|
|
13
14
|
} from '../../constants';
|
|
14
15
|
import DynamicShadow from '../../Shadow/DynamicShadow';
|
|
@@ -64,11 +65,17 @@ export function FullbodyAvatar({
|
|
|
64
65
|
return found;
|
|
65
66
|
}, [scene]);
|
|
66
67
|
|
|
67
|
-
// Merge base and additional animations only if needed
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
// Merge base and additional animations only if needed.
|
|
69
|
+
// For male avatars, exclude problematic idle clips from the full set (base + pack).
|
|
70
|
+
const mergedAnimations = useMemo(() => {
|
|
71
|
+
const clips = needsAdditionalAnimations
|
|
72
|
+
? [...baseAnimations, ...additionalAnimations]
|
|
73
|
+
: baseAnimations;
|
|
74
|
+
|
|
75
|
+
if (sex !== 'MALE') return clips;
|
|
76
|
+
|
|
77
|
+
return clips.filter(clip => !MALE_EXCLUDED_ANIMATIONS.has(clip.name));
|
|
78
|
+
}, [baseAnimations, additionalAnimations, needsAdditionalAnimations, sex]);
|
|
72
79
|
|
|
73
80
|
// Create animation actions from the merged animations
|
|
74
81
|
const { actions } = useAnimations(mergedAnimations, scene);
|
|
@@ -2,6 +2,7 @@ import { SkinnedMesh } from 'three';
|
|
|
2
2
|
import { MathUtils } from 'three';
|
|
3
3
|
import {
|
|
4
4
|
EMOTION_SMOOTHING,
|
|
5
|
+
EMOTION_INTENSITY,
|
|
5
6
|
VISEME_SMOOTHING,
|
|
6
7
|
BLINK_CONFIG,
|
|
7
8
|
MAPPING_EMOTIONS_ITALIAN_TO_ENGLISH,
|
|
@@ -214,7 +215,7 @@ export class MorphTargetController {
|
|
|
214
215
|
const currentEmotionValue = this.currentEmotionValues[key] || 0;
|
|
215
216
|
const newEmotionValue = MathUtils.lerp(
|
|
216
217
|
currentEmotionValue,
|
|
217
|
-
targetEmotionValue *
|
|
218
|
+
targetEmotionValue * EMOTION_INTENSITY,
|
|
218
219
|
EMOTION_SMOOTHING
|
|
219
220
|
);
|
|
220
221
|
this.currentEmotionValues[key] = newEmotionValue;
|
|
@@ -58,7 +58,8 @@ export const MAPPING_BLEND_SHAPE_TO_EMOTION_RPM: BlendShapeMap = [
|
|
|
58
58
|
'browDownRight': 0.5,
|
|
59
59
|
'browOuterUpLeft': 0.5,
|
|
60
60
|
'browOuterUpRight': 0.5,
|
|
61
|
-
'
|
|
61
|
+
'mouthSmileLeft': -0.2,
|
|
62
|
+
'mouthSmileRight': -0.2,
|
|
62
63
|
},
|
|
63
64
|
},
|
|
64
65
|
{
|
|
@@ -77,7 +78,8 @@ export const MAPPING_BLEND_SHAPE_TO_EMOTION_RPM: BlendShapeMap = [
|
|
|
77
78
|
'browDownRight': -0.5,
|
|
78
79
|
'eyeSquintLeft': 0.5,
|
|
79
80
|
'eyeSquintRight': 0.5,
|
|
80
|
-
'
|
|
81
|
+
'mouthSmileLeft': -0.6,
|
|
82
|
+
'mouthSmileRight': -0.6,
|
|
81
83
|
},
|
|
82
84
|
},
|
|
83
85
|
{
|
|
@@ -88,7 +90,8 @@ export const MAPPING_BLEND_SHAPE_TO_EMOTION_RPM: BlendShapeMap = [
|
|
|
88
90
|
'browOuterUpRight': 0.5,
|
|
89
91
|
'eyeWideLeft': 0.5,
|
|
90
92
|
'eyeWideRight': 0.5,
|
|
91
|
-
'
|
|
93
|
+
'mouthSmileLeft': 0.5,
|
|
94
|
+
'mouthSmileRight': 0.5,
|
|
92
95
|
},
|
|
93
96
|
},
|
|
94
97
|
{
|
|
@@ -97,7 +100,8 @@ export const MAPPING_BLEND_SHAPE_TO_EMOTION_RPM: BlendShapeMap = [
|
|
|
97
100
|
'browDownLeft': 0.5,
|
|
98
101
|
'browDownRight': 0.5,
|
|
99
102
|
'browInnerUp': 0.5,
|
|
100
|
-
'
|
|
103
|
+
'mouthSmileLeft': 0.5,
|
|
104
|
+
'mouthSmileRight': 0.5,
|
|
101
105
|
},
|
|
102
106
|
},
|
|
103
107
|
];
|
|
@@ -109,6 +113,12 @@ export const ANIMATION_URLS = {
|
|
|
109
113
|
'https://assets.memori.ai/api/v2/asset/2adc934b-24b2-45bd-94ad-ffec58d3cb32.glb',
|
|
110
114
|
};
|
|
111
115
|
|
|
116
|
+
/** Idle clips excluded from the male animation pack. */
|
|
117
|
+
export const MALE_EXCLUDED_ANIMATIONS = new Set([
|
|
118
|
+
'Idle',
|
|
119
|
+
'Idle2'
|
|
120
|
+
]);
|
|
121
|
+
|
|
112
122
|
// Blink configuration
|
|
113
123
|
export const BLINK_CONFIG = {
|
|
114
124
|
minInterval: 1000,
|
|
@@ -127,3 +137,5 @@ export const DEFAULT_CONFIG: AnimationConfig = {
|
|
|
127
137
|
// Smoothing factors for emotion and viseme morphs
|
|
128
138
|
export const EMOTION_SMOOTHING = 0.3;
|
|
129
139
|
export const VISEME_SMOOTHING = 0.5;
|
|
140
|
+
/** Multiplier applied to emotion morph target values before clamping (1 = raw mapping). */
|
|
141
|
+
export const EMOTION_INTENSITY = 2.5;
|
|
@@ -1,5 +1,131 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
|
+
exports[`renders Avatar with avatar-configurator like readyplayerme-full 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<div
|
|
6
|
+
class="memori--avatar-wrapper memori--avatar-avatar-configurator"
|
|
7
|
+
>
|
|
8
|
+
<div
|
|
9
|
+
class="memori--blob-container"
|
|
10
|
+
>
|
|
11
|
+
<div
|
|
12
|
+
class="memori-blob"
|
|
13
|
+
>
|
|
14
|
+
<div
|
|
15
|
+
class="mainDiv"
|
|
16
|
+
/>
|
|
17
|
+
<div
|
|
18
|
+
class="mainDiv"
|
|
19
|
+
/>
|
|
20
|
+
<div
|
|
21
|
+
class="mainDiv"
|
|
22
|
+
/>
|
|
23
|
+
<div
|
|
24
|
+
class="mainDiv"
|
|
25
|
+
/>
|
|
26
|
+
<div
|
|
27
|
+
class="mainDiv"
|
|
28
|
+
/>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
<div
|
|
33
|
+
class="memori--avatar-toggle"
|
|
34
|
+
>
|
|
35
|
+
<button
|
|
36
|
+
class="memori-button memori-button--ghost memori-button--rounded memori-button--padded memori-button--with-icon"
|
|
37
|
+
>
|
|
38
|
+
<span
|
|
39
|
+
class="memori-button--icon"
|
|
40
|
+
>
|
|
41
|
+
<svg
|
|
42
|
+
aria-hidden="true"
|
|
43
|
+
focusable="false"
|
|
44
|
+
role="img"
|
|
45
|
+
viewBox="0 0 1024 1024"
|
|
46
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
47
|
+
>
|
|
48
|
+
<path
|
|
49
|
+
d="M942.2 486.2Q889.47 375.11 816.7 305l-50.88 50.88C807.31 395.53 843.45 447.4 874.7 512 791.5 684.2 673.4 766 512 766q-72.67 0-133.87-22.38L323 798.75Q408 838 512 838q288.3 0 430.2-300.3a60.29 60.29 0 000-51.5zm-63.57-320.64L836 122.88a8 8 0 00-11.32 0L715.31 232.2Q624.86 186 512 186q-288.3 0-430.2 300.3a60.3 60.3 0 000 51.5q56.69 119.4 136.5 191.41L112.48 835a8 8 0 000 11.31L155.17 889a8 8 0 0011.31 0l712.15-712.12a8 8 0 000-11.32zM149.3 512C232.6 339.8 350.7 258 512 258c54.54 0 104.13 9.36 149.12 28.39l-70.3 70.3a176 176 0 00-238.13 238.13l-83.42 83.42C223.1 637.49 183.3 582.28 149.3 512zm246.7 0a112.11 112.11 0 01146.2-106.69L401.31 546.2A112 112 0 01396 512z"
|
|
50
|
+
/>
|
|
51
|
+
<path
|
|
52
|
+
d="M508 624c-3.46 0-6.87-.16-10.25-.47l-52.82 52.82a176.09 176.09 0 00227.42-227.42l-52.82 52.82c.31 3.38.47 6.79.47 10.25a111.94 111.94 0 01-112 112z"
|
|
53
|
+
/>
|
|
54
|
+
</svg>
|
|
55
|
+
</span>
|
|
56
|
+
<span
|
|
57
|
+
class="memori--avatar-toggle-text"
|
|
58
|
+
>
|
|
59
|
+
hide
|
|
60
|
+
</span>
|
|
61
|
+
</button>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
`;
|
|
65
|
+
|
|
66
|
+
exports[`renders Avatar with avatar-configurator on totem layout unchanged 1`] = `
|
|
67
|
+
<div>
|
|
68
|
+
<div
|
|
69
|
+
class="memori--avatar-wrapper memori--avatar-avatar-configurator"
|
|
70
|
+
>
|
|
71
|
+
<div
|
|
72
|
+
class="memori--blob-container"
|
|
73
|
+
>
|
|
74
|
+
<div
|
|
75
|
+
class="memori-blob"
|
|
76
|
+
>
|
|
77
|
+
<div
|
|
78
|
+
class="mainDiv"
|
|
79
|
+
/>
|
|
80
|
+
<div
|
|
81
|
+
class="mainDiv"
|
|
82
|
+
/>
|
|
83
|
+
<div
|
|
84
|
+
class="mainDiv"
|
|
85
|
+
/>
|
|
86
|
+
<div
|
|
87
|
+
class="mainDiv"
|
|
88
|
+
/>
|
|
89
|
+
<div
|
|
90
|
+
class="mainDiv"
|
|
91
|
+
/>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
<div
|
|
96
|
+
class="memori--avatar-toggle"
|
|
97
|
+
>
|
|
98
|
+
<button
|
|
99
|
+
class="memori-button memori-button--ghost memori-button--rounded memori-button--padded memori-button--with-icon"
|
|
100
|
+
>
|
|
101
|
+
<span
|
|
102
|
+
class="memori-button--icon"
|
|
103
|
+
>
|
|
104
|
+
<svg
|
|
105
|
+
aria-hidden="true"
|
|
106
|
+
focusable="false"
|
|
107
|
+
role="img"
|
|
108
|
+
viewBox="0 0 1024 1024"
|
|
109
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
110
|
+
>
|
|
111
|
+
<path
|
|
112
|
+
d="M942.2 486.2Q889.47 375.11 816.7 305l-50.88 50.88C807.31 395.53 843.45 447.4 874.7 512 791.5 684.2 673.4 766 512 766q-72.67 0-133.87-22.38L323 798.75Q408 838 512 838q288.3 0 430.2-300.3a60.29 60.29 0 000-51.5zm-63.57-320.64L836 122.88a8 8 0 00-11.32 0L715.31 232.2Q624.86 186 512 186q-288.3 0-430.2 300.3a60.3 60.3 0 000 51.5q56.69 119.4 136.5 191.41L112.48 835a8 8 0 000 11.31L155.17 889a8 8 0 0011.31 0l712.15-712.12a8 8 0 000-11.32zM149.3 512C232.6 339.8 350.7 258 512 258c54.54 0 104.13 9.36 149.12 28.39l-70.3 70.3a176 176 0 00-238.13 238.13l-83.42 83.42C223.1 637.49 183.3 582.28 149.3 512zm246.7 0a112.11 112.11 0 01146.2-106.69L401.31 546.2A112 112 0 01396 512z"
|
|
113
|
+
/>
|
|
114
|
+
<path
|
|
115
|
+
d="M508 624c-3.46 0-6.87-.16-10.25-.47l-52.82 52.82a176.09 176.09 0 00227.42-227.42l-52.82 52.82c.31 3.38.47 6.79.47 10.25a111.94 111.94 0 01-112 112z"
|
|
116
|
+
/>
|
|
117
|
+
</svg>
|
|
118
|
+
</span>
|
|
119
|
+
<span
|
|
120
|
+
class="memori--avatar-toggle-text"
|
|
121
|
+
>
|
|
122
|
+
hide
|
|
123
|
+
</span>
|
|
124
|
+
</button>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
`;
|
|
128
|
+
|
|
3
129
|
exports[`renders Avatar with blob and avatar in blob unchanged 1`] = `
|
|
4
130
|
<div>
|
|
5
131
|
<div
|
|
@@ -100,13 +100,16 @@ const ChatBubble: React.FC<Props> = ({
|
|
|
100
100
|
const [copyFeedback, setCopyFeedback] = useState({
|
|
101
101
|
plain: false,
|
|
102
102
|
raw: false,
|
|
103
|
+
functionCache: false,
|
|
103
104
|
});
|
|
104
105
|
const copyFeedbackTimers = useRef<{
|
|
105
106
|
plain: ReturnType<typeof setTimeout> | null;
|
|
106
107
|
raw: ReturnType<typeof setTimeout> | null;
|
|
108
|
+
functionCache: ReturnType<typeof setTimeout> | null;
|
|
107
109
|
}>({
|
|
108
110
|
plain: null,
|
|
109
111
|
raw: null,
|
|
112
|
+
functionCache: null,
|
|
110
113
|
});
|
|
111
114
|
// Initialize MathJax on component mount
|
|
112
115
|
useEffect(() => {
|
|
@@ -191,7 +194,9 @@ const ChatBubble: React.FC<Props> = ({
|
|
|
191
194
|
useEffect(() => {
|
|
192
195
|
return () => {
|
|
193
196
|
(
|
|
194
|
-
Object.keys(copyFeedbackTimers.current) as Array<
|
|
197
|
+
Object.keys(copyFeedbackTimers.current) as Array<
|
|
198
|
+
'plain' | 'raw' | 'functionCache'
|
|
199
|
+
>
|
|
195
200
|
).forEach(key => {
|
|
196
201
|
const timer = copyFeedbackTimers.current[key];
|
|
197
202
|
if (timer) {
|
|
@@ -202,7 +207,12 @@ const ChatBubble: React.FC<Props> = ({
|
|
|
202
207
|
};
|
|
203
208
|
}, []);
|
|
204
209
|
|
|
205
|
-
const
|
|
210
|
+
const functionCacheCopyText =
|
|
211
|
+
functionCacheData
|
|
212
|
+
?.map(f => `${f.title}\n\n${f.content}`)
|
|
213
|
+
.join('\n\n---\n\n') ?? '';
|
|
214
|
+
|
|
215
|
+
const triggerCopyFeedback = (type: 'plain' | 'raw' | 'functionCache') => {
|
|
206
216
|
setCopyFeedback(prev => ({ ...prev, [type]: true }));
|
|
207
217
|
if (copyFeedbackTimers.current[type]) {
|
|
208
218
|
clearTimeout(copyFeedbackTimers.current[type]!);
|
|
@@ -213,7 +223,10 @@ const ChatBubble: React.FC<Props> = ({
|
|
|
213
223
|
}, 1500);
|
|
214
224
|
};
|
|
215
225
|
|
|
216
|
-
const handleCopyClick = (
|
|
226
|
+
const handleCopyClick = (
|
|
227
|
+
type: 'plain' | 'raw' | 'functionCache',
|
|
228
|
+
text: string
|
|
229
|
+
) => {
|
|
217
230
|
if (!text?.length) return;
|
|
218
231
|
if (typeof navigator !== 'undefined' && navigator.clipboard?.writeText) {
|
|
219
232
|
navigator.clipboard
|
|
@@ -718,6 +731,18 @@ const ChatBubble: React.FC<Props> = ({
|
|
|
718
731
|
open={openFunctionCache}
|
|
719
732
|
onClose={() => setOpenFunctionCache(false)}
|
|
720
733
|
className="memori-chat--function-cache-modal"
|
|
734
|
+
footer={
|
|
735
|
+
<Button
|
|
736
|
+
icon={<Copy />}
|
|
737
|
+
onClick={() =>
|
|
738
|
+
handleCopyClick('functionCache', functionCacheCopyText)
|
|
739
|
+
}
|
|
740
|
+
>
|
|
741
|
+
{copyFeedback.functionCache
|
|
742
|
+
? copiedLabel
|
|
743
|
+
: t('copy') || 'Copy'}
|
|
744
|
+
</Button>
|
|
745
|
+
}
|
|
721
746
|
>
|
|
722
747
|
{functionCacheData?.map((f, i) => (
|
|
723
748
|
<div
|
|
@@ -167,64 +167,101 @@ const UploadDocuments: React.FC<UploadDocumentsProps> = ({
|
|
|
167
167
|
}
|
|
168
168
|
};
|
|
169
169
|
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
170
|
+
const loadXLSXLibrary = async (): Promise<void> => {
|
|
171
|
+
if (window.XLSX) return;
|
|
172
|
+
|
|
173
|
+
await new Promise((resolve, reject) => {
|
|
174
|
+
const script = document.createElement('script');
|
|
175
|
+
script.src = XLSX_URL;
|
|
176
|
+
script.onload = resolve;
|
|
177
|
+
script.onerror = reject;
|
|
178
|
+
document.head.appendChild(script);
|
|
179
|
+
});
|
|
180
|
+
};
|
|
181
181
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
182
|
+
const readXLSXWorkbook = async (file: File) => {
|
|
183
|
+
await loadXLSXLibrary();
|
|
184
|
+
|
|
185
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
186
|
+
return window.XLSX.read(arrayBuffer, {
|
|
187
|
+
type: 'array',
|
|
188
|
+
cellFormula: true,
|
|
189
|
+
cellNF: true,
|
|
190
|
+
cellText: true,
|
|
191
|
+
cellDates: true,
|
|
192
|
+
});
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const formatXLSXWorkbookForDisplay = (workbook: {
|
|
196
|
+
SheetNames: string[];
|
|
197
|
+
Sheets: Record<string, unknown>;
|
|
198
|
+
}): string => {
|
|
199
|
+
let text = '';
|
|
200
|
+
|
|
201
|
+
for (const sheetName of workbook.SheetNames) {
|
|
202
|
+
const worksheet = workbook.Sheets[sheetName];
|
|
203
|
+
const data = window.XLSX.utils.sheet_to_json(worksheet, {
|
|
204
|
+
header: 1,
|
|
205
|
+
raw: false,
|
|
189
206
|
});
|
|
190
207
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
header: 1,
|
|
196
|
-
raw: false,
|
|
208
|
+
const colWidths = data.reduce((widths: number[], row: any[]) => {
|
|
209
|
+
row.forEach((cell, i) => {
|
|
210
|
+
const cellWidth = (cell || '').toString().length;
|
|
211
|
+
widths[i] = Math.max(widths[i] || 0, cellWidth);
|
|
197
212
|
});
|
|
213
|
+
return widths;
|
|
214
|
+
}, []);
|
|
215
|
+
|
|
216
|
+
const formattedText = data.map((row: any[]) => {
|
|
217
|
+
return row
|
|
218
|
+
.map((cell, i) => {
|
|
219
|
+
const cellStr = (cell || '').toString();
|
|
220
|
+
return cellStr.padEnd(colWidths[i] + 2);
|
|
221
|
+
})
|
|
222
|
+
.join('|')
|
|
223
|
+
.trim();
|
|
224
|
+
});
|
|
198
225
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}, []);
|
|
206
|
-
|
|
207
|
-
const formattedText = data.map((row: any[]) => {
|
|
208
|
-
return row
|
|
209
|
-
.map((cell, i) => {
|
|
210
|
-
const cellStr = (cell || '').toString();
|
|
211
|
-
return cellStr.padEnd(colWidths[i] + 2);
|
|
212
|
-
})
|
|
213
|
-
.join('|')
|
|
214
|
-
.trim();
|
|
215
|
-
});
|
|
226
|
+
if (formattedText.length > 0) {
|
|
227
|
+
const separator = colWidths
|
|
228
|
+
.map((w: number) => '-'.repeat(w + 2))
|
|
229
|
+
.join('+');
|
|
230
|
+
formattedText.splice(1, 0, separator);
|
|
231
|
+
}
|
|
216
232
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
233
|
+
text += `Sheet: ${sheetName}\n${formattedText.join('\n')}\n\n`;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return text;
|
|
237
|
+
};
|
|
223
238
|
|
|
224
|
-
|
|
239
|
+
const formatXLSXWorkbookAsCsv = (workbook: {
|
|
240
|
+
SheetNames: string[];
|
|
241
|
+
Sheets: Record<string, unknown>;
|
|
242
|
+
}): string => {
|
|
243
|
+
let text = '';
|
|
244
|
+
|
|
245
|
+
for (const sheetName of workbook.SheetNames) {
|
|
246
|
+
const worksheet = workbook.Sheets[sheetName];
|
|
247
|
+
const csv = window.XLSX.utils.sheet_to_csv(worksheet, { raw: false });
|
|
248
|
+
if (csv.trim()) {
|
|
249
|
+
text += `Sheet: ${sheetName}\n${csv}\n\n`;
|
|
225
250
|
}
|
|
251
|
+
}
|
|
226
252
|
|
|
227
|
-
|
|
253
|
+
return text;
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const extractTextFromXLSX = async (
|
|
257
|
+
file: File
|
|
258
|
+
): Promise<{ display: string; asset: string }> => {
|
|
259
|
+
try {
|
|
260
|
+
const workbook = await readXLSXWorkbook(file);
|
|
261
|
+
return {
|
|
262
|
+
display: formatXLSXWorkbookForDisplay(workbook),
|
|
263
|
+
asset: formatXLSXWorkbookAsCsv(workbook),
|
|
264
|
+
};
|
|
228
265
|
} catch (error) {
|
|
229
266
|
console.error('XLSX extraction failed:', error);
|
|
230
267
|
throw new Error(
|
|
@@ -237,7 +274,11 @@ const UploadDocuments: React.FC<UploadDocumentsProps> = ({
|
|
|
237
274
|
|
|
238
275
|
const processDocumentFile = async (
|
|
239
276
|
file: File
|
|
240
|
-
): Promise<{
|
|
277
|
+
): Promise<{
|
|
278
|
+
text: string | null;
|
|
279
|
+
textForAsset?: string;
|
|
280
|
+
uploadAsOriginal?: boolean;
|
|
281
|
+
}> => {
|
|
241
282
|
if (isOfficeNativeFilename(file.name)) {
|
|
242
283
|
return { text: null, uploadAsOriginal: true };
|
|
243
284
|
}
|
|
@@ -245,16 +286,19 @@ const UploadDocuments: React.FC<UploadDocumentsProps> = ({
|
|
|
245
286
|
const ext = file.name.split('.').pop()?.toLowerCase() || '';
|
|
246
287
|
try {
|
|
247
288
|
let text: string | null = null;
|
|
289
|
+
let textForAsset: string | undefined;
|
|
248
290
|
|
|
249
291
|
if (ext === 'pdf') {
|
|
250
292
|
text = await extractTextFromPDF(file);
|
|
251
293
|
} else if (['txt', 'md', 'json', 'csv', 'html'].includes(ext)) {
|
|
252
294
|
text = await file.text();
|
|
253
295
|
} else if (ext === 'xlsx') {
|
|
254
|
-
|
|
296
|
+
const extracted = await extractTextFromXLSX(file);
|
|
297
|
+
text = extracted.display;
|
|
298
|
+
textForAsset = extracted.asset;
|
|
255
299
|
}
|
|
256
300
|
|
|
257
|
-
return { text };
|
|
301
|
+
return { text, textForAsset };
|
|
258
302
|
} catch (error) {
|
|
259
303
|
console.error('Document processing failed:', error);
|
|
260
304
|
throw new Error(
|
|
@@ -370,7 +414,8 @@ const UploadDocuments: React.FC<UploadDocumentsProps> = ({
|
|
|
370
414
|
const fileId = Math.random().toString(36).substr(2, 9);
|
|
371
415
|
|
|
372
416
|
try {
|
|
373
|
-
const { text, uploadAsOriginal } =
|
|
417
|
+
const { text, textForAsset, uploadAsOriginal } =
|
|
418
|
+
await processDocumentFile(file);
|
|
374
419
|
|
|
375
420
|
if (uploadAsOriginal) {
|
|
376
421
|
// Office native format: upload the original file as asset, no text extraction
|
|
@@ -403,7 +448,8 @@ const UploadDocuments: React.FC<UploadDocumentsProps> = ({
|
|
|
403
448
|
});
|
|
404
449
|
} else if (text) {
|
|
405
450
|
const baseName = file.name.replace(/\.[^/.]+$/, '') || file.name;
|
|
406
|
-
const
|
|
451
|
+
const assetText = textForAsset ?? text;
|
|
452
|
+
const textFile = new File([assetText], `${baseName}.txt`, {
|
|
407
453
|
type: 'text/plain',
|
|
408
454
|
});
|
|
409
455
|
|
|
@@ -468,7 +514,9 @@ const UploadDocuments: React.FC<UploadDocumentsProps> = ({
|
|
|
468
514
|
<input
|
|
469
515
|
ref={documentInputRef}
|
|
470
516
|
type="file"
|
|
471
|
-
accept={`.pdf,.txt,.md,.json,.xlsx,.csv,.html,${officeNativeExtensions.join(
|
|
517
|
+
accept={`.pdf,.txt,.md,.json,.xlsx,.csv,.html,${officeNativeExtensions.join(
|
|
518
|
+
','
|
|
519
|
+
)}`}
|
|
472
520
|
multiple
|
|
473
521
|
className="memori--upload-file-input"
|
|
474
522
|
onChange={handleDocumentUpload}
|
|
@@ -32,25 +32,18 @@ const Template: Story<Props> = args => (
|
|
|
32
32
|
|
|
33
33
|
const DefaultLayout = Template.bind({});
|
|
34
34
|
DefaultLayout.args = {
|
|
35
|
-
memoriName: '
|
|
36
|
-
ownerUserName: '
|
|
37
|
-
memoriID: '
|
|
35
|
+
memoriName: 'prova-sharepoint',
|
|
36
|
+
ownerUserName: 'Andrea-Patini',
|
|
37
|
+
memoriID: '019f129c-a5a3-74d3-b0c8-b7794437bb23',
|
|
38
38
|
ownerUserID: '91dbc9ba-b684-4fbe-9828-b5980af6cda9',
|
|
39
39
|
tenantID: 'aisuru-staging.aclambda.online',
|
|
40
40
|
engineURL: 'https://engine-staging.memori.ai/memori/v2',
|
|
41
41
|
apiURL: 'https://backend-staging.memori.ai/api/v2',
|
|
42
|
-
|
|
42
|
+
baseURL: 'https://aisuru-staging.aclambda.online',
|
|
43
|
+
layout: 'TOTEM',
|
|
43
44
|
uiLang: 'IT',
|
|
44
45
|
spokenLang: 'IT',
|
|
45
|
-
integrationID: '
|
|
46
|
-
autoStart: true,
|
|
47
|
-
sessionID: '' as string | undefined,
|
|
48
|
-
showUpload: true,
|
|
49
|
-
showReasoning: false,
|
|
50
|
-
showLogin: true,
|
|
51
|
-
multilingual: true,
|
|
52
|
-
avatar3dHidden: true,
|
|
53
|
-
showMessageConsumption: true,
|
|
46
|
+
integrationID: '019f1896-730d-7e19-9a88-6413f485247b',
|
|
54
47
|
};
|
|
55
48
|
|
|
56
49
|
export const Default = Template.bind({});
|
|
@@ -397,7 +397,8 @@
|
|
|
397
397
|
transform: none !important;
|
|
398
398
|
}
|
|
399
399
|
|
|
400
|
-
.memori-layout-website_assistant.memori--avatar-readyplayerme-full .memori--avatar-wrapper > div
|
|
400
|
+
.memori-layout-website_assistant.memori--avatar-readyplayerme-full .memori--avatar-wrapper > div,
|
|
401
|
+
.memori-layout-website_assistant.memori--avatar-avatar-configurator .memori--avatar-wrapper > div {
|
|
401
402
|
transform: scale(1.7) translate(0px, 10vh);
|
|
402
403
|
}
|
|
403
404
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {
|
|
2
|
+
is3dAvatarWithUrl,
|
|
3
|
+
usesRpmAvatarView,
|
|
4
|
+
} from './integration';
|
|
5
|
+
|
|
6
|
+
describe('integration avatar helpers', () => {
|
|
7
|
+
const avatarURL = 'https://example.com/avatar.glb';
|
|
8
|
+
|
|
9
|
+
describe('is3dAvatarWithUrl', () => {
|
|
10
|
+
it('returns true for avatar-configurator with avatarURL', () => {
|
|
11
|
+
expect(is3dAvatarWithUrl('avatar-configurator', avatarURL)).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('returns false for avatar-configurator without avatarURL', () => {
|
|
15
|
+
expect(is3dAvatarWithUrl('avatar-configurator', undefined)).toBe(false);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe('usesRpmAvatarView', () => {
|
|
20
|
+
it('returns true for avatar-configurator', () => {
|
|
21
|
+
expect(usesRpmAvatarView('avatar-configurator')).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('returns true for readyplayerme-full', () => {
|
|
25
|
+
expect(usesRpmAvatarView('readyplayerme-full')).toBe(true);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('returns false for customglb', () => {
|
|
29
|
+
expect(usesRpmAvatarView('customglb')).toBe(false);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
});
|