@memori.ai/memori-react 8.8.5 → 8.9.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 (176) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +28 -0
  3. package/dist/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js +0 -1
  4. package/dist/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js.map +1 -1
  5. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js +0 -10
  6. package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js.map +1 -1
  7. package/dist/components/Avatar/AvatarView/AvatarComponent/components/controllers/AvatarAnimator.js +0 -9
  8. package/dist/components/Avatar/AvatarView/AvatarComponent/components/controllers/AvatarAnimator.js.map +1 -1
  9. package/dist/components/Chat/Chat.css +31 -0
  10. package/dist/components/Chat/Chat.js +18 -4
  11. package/dist/components/Chat/Chat.js.map +1 -1
  12. package/dist/components/ChatBubble/ChatBubble.js +1 -2
  13. package/dist/components/ChatBubble/ChatBubble.js.map +1 -1
  14. package/dist/components/ChatInputs/ChatInputs.css +23 -0
  15. package/dist/components/ChatInputs/ChatInputs.d.ts +1 -0
  16. package/dist/components/ChatInputs/ChatInputs.js +37 -21
  17. package/dist/components/ChatInputs/ChatInputs.js.map +1 -1
  18. package/dist/components/ChatTextArea/ChatTextArea.css +31 -0
  19. package/dist/components/ChatTextArea/ChatTextArea.d.ts +1 -0
  20. package/dist/components/ChatTextArea/ChatTextArea.js +9 -2
  21. package/dist/components/ChatTextArea/ChatTextArea.js.map +1 -1
  22. package/dist/components/FilePreview/FilePreview.css +39 -0
  23. package/dist/components/Header/Header.js +3 -16
  24. package/dist/components/Header/Header.js.map +1 -1
  25. package/dist/components/MediaWidget/LinkItemWidget.js +1 -1
  26. package/dist/components/MediaWidget/LinkItemWidget.js.map +1 -1
  27. package/dist/components/MediaWidget/MediaItemWidget.js +5 -9
  28. package/dist/components/MediaWidget/MediaItemWidget.js.map +1 -1
  29. package/dist/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.js +65 -51
  30. package/dist/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.js.map +1 -1
  31. package/dist/components/MemoriArtifactSystem/utils/ArtifactAPI.d.ts +5 -0
  32. package/dist/components/MemoriArtifactSystem/utils/ArtifactAPI.js +287 -0
  33. package/dist/components/MemoriArtifactSystem/utils/ArtifactAPI.js.map +1 -0
  34. package/dist/components/MemoriWidget/MemoriWidget.d.ts +12 -0
  35. package/dist/components/MemoriWidget/MemoriWidget.js +12 -3
  36. package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
  37. package/dist/components/Snippet/Snippet.js +4 -3
  38. package/dist/components/Snippet/Snippet.js.map +1 -1
  39. package/dist/components/StartPanel/StartPanel.css +14 -0
  40. package/dist/components/StartPanel/StartPanel.js +2 -2
  41. package/dist/components/StartPanel/StartPanel.js.map +1 -1
  42. package/dist/components/UploadButton/UploadDocuments/UploadDocuments.js +0 -21
  43. package/dist/components/UploadButton/UploadDocuments/UploadDocuments.js.map +1 -1
  44. package/dist/components/VenueWidget/VenueWidget.js +0 -1
  45. package/dist/components/VenueWidget/VenueWidget.js.map +1 -1
  46. package/dist/components/layouts/HiddenChat.js +0 -15
  47. package/dist/components/layouts/HiddenChat.js.map +1 -1
  48. package/dist/components/layouts/chat.css +2 -2
  49. package/dist/context/visemeContext.js +0 -6
  50. package/dist/context/visemeContext.js.map +1 -1
  51. package/dist/helpers/constants.d.ts +11 -0
  52. package/dist/helpers/constants.js +24 -2
  53. package/dist/helpers/constants.js.map +1 -1
  54. package/dist/helpers/tts/useTTS.js +0 -3
  55. package/dist/helpers/tts/useTTS.js.map +1 -1
  56. package/dist/helpers/utils.d.ts +1 -0
  57. package/dist/helpers/utils.js +6 -1
  58. package/dist/helpers/utils.js.map +1 -1
  59. package/dist/index.js.map +1 -1
  60. package/dist/locales/de.json +2 -0
  61. package/dist/locales/en.json +2 -0
  62. package/dist/locales/es.json +2 -0
  63. package/dist/locales/fr.json +2 -0
  64. package/dist/locales/it.json +2 -0
  65. package/esm/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js +0 -1
  66. package/esm/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js.map +1 -1
  67. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js +0 -10
  68. package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js.map +1 -1
  69. package/esm/components/Avatar/AvatarView/AvatarComponent/components/controllers/AvatarAnimator.js +0 -9
  70. package/esm/components/Avatar/AvatarView/AvatarComponent/components/controllers/AvatarAnimator.js.map +1 -1
  71. package/esm/components/Chat/Chat.css +31 -0
  72. package/esm/components/Chat/Chat.js +19 -5
  73. package/esm/components/Chat/Chat.js.map +1 -1
  74. package/esm/components/ChatBubble/ChatBubble.js +1 -2
  75. package/esm/components/ChatBubble/ChatBubble.js.map +1 -1
  76. package/esm/components/ChatInputs/ChatInputs.css +23 -0
  77. package/esm/components/ChatInputs/ChatInputs.d.ts +1 -0
  78. package/esm/components/ChatInputs/ChatInputs.js +37 -21
  79. package/esm/components/ChatInputs/ChatInputs.js.map +1 -1
  80. package/esm/components/ChatTextArea/ChatTextArea.css +31 -0
  81. package/esm/components/ChatTextArea/ChatTextArea.d.ts +1 -0
  82. package/esm/components/ChatTextArea/ChatTextArea.js +9 -2
  83. package/esm/components/ChatTextArea/ChatTextArea.js.map +1 -1
  84. package/esm/components/FilePreview/FilePreview.css +39 -0
  85. package/esm/components/Header/Header.js +3 -16
  86. package/esm/components/Header/Header.js.map +1 -1
  87. package/esm/components/MediaWidget/LinkItemWidget.js +1 -1
  88. package/esm/components/MediaWidget/LinkItemWidget.js.map +1 -1
  89. package/esm/components/MediaWidget/MediaItemWidget.js +5 -9
  90. package/esm/components/MediaWidget/MediaItemWidget.js.map +1 -1
  91. package/esm/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.js +65 -51
  92. package/esm/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.js.map +1 -1
  93. package/esm/components/MemoriArtifactSystem/utils/ArtifactAPI.d.ts +5 -0
  94. package/esm/components/MemoriArtifactSystem/utils/ArtifactAPI.js +282 -0
  95. package/esm/components/MemoriArtifactSystem/utils/ArtifactAPI.js.map +1 -0
  96. package/esm/components/MemoriWidget/MemoriWidget.d.ts +12 -0
  97. package/esm/components/MemoriWidget/MemoriWidget.js +12 -3
  98. package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
  99. package/esm/components/Snippet/Snippet.js +4 -3
  100. package/esm/components/Snippet/Snippet.js.map +1 -1
  101. package/esm/components/StartPanel/StartPanel.css +14 -0
  102. package/esm/components/StartPanel/StartPanel.js +3 -3
  103. package/esm/components/StartPanel/StartPanel.js.map +1 -1
  104. package/esm/components/UploadButton/UploadDocuments/UploadDocuments.js +0 -21
  105. package/esm/components/UploadButton/UploadDocuments/UploadDocuments.js.map +1 -1
  106. package/esm/components/VenueWidget/VenueWidget.js +0 -1
  107. package/esm/components/VenueWidget/VenueWidget.js.map +1 -1
  108. package/esm/components/layouts/HiddenChat.js +0 -15
  109. package/esm/components/layouts/HiddenChat.js.map +1 -1
  110. package/esm/components/layouts/chat.css +2 -2
  111. package/esm/context/visemeContext.js +0 -6
  112. package/esm/context/visemeContext.js.map +1 -1
  113. package/esm/helpers/constants.d.ts +11 -0
  114. package/esm/helpers/constants.js +22 -1
  115. package/esm/helpers/constants.js.map +1 -1
  116. package/esm/helpers/tts/useTTS.js +0 -3
  117. package/esm/helpers/tts/useTTS.js.map +1 -1
  118. package/esm/helpers/utils.d.ts +1 -0
  119. package/esm/helpers/utils.js +4 -0
  120. package/esm/helpers/utils.js.map +1 -1
  121. package/esm/index.js.map +1 -1
  122. package/esm/locales/de.json +2 -0
  123. package/esm/locales/en.json +2 -0
  124. package/esm/locales/es.json +2 -0
  125. package/esm/locales/fr.json +2 -0
  126. package/esm/locales/it.json +2 -0
  127. package/package.json +1 -1
  128. package/src/components/Avatar/AvatarView/AvatarComponent/avatarComponent.tsx +0 -1
  129. package/src/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.tsx +0 -17
  130. package/src/components/Avatar/AvatarView/AvatarComponent/components/controllers/AvatarAnimator.ts +0 -20
  131. package/src/components/Chat/Chat.css +31 -0
  132. package/src/components/Chat/Chat.stories.tsx +503 -9
  133. package/src/components/Chat/Chat.tsx +23 -3
  134. package/src/components/Chat/__snapshots__/Chat.test.tsx.snap +73 -73
  135. package/src/components/ChatBubble/ChatBubble.tsx +1 -2
  136. package/src/components/ChatBubble/__snapshots__/ChatBubble.test.tsx.snap +25 -25
  137. package/src/components/ChatInputs/ChatInputs.css +23 -0
  138. package/src/components/ChatInputs/ChatInputs.tsx +36 -14
  139. package/src/components/ChatTextArea/ChatTextArea.css +31 -0
  140. package/src/components/ChatTextArea/ChatTextArea.tsx +11 -1
  141. package/src/components/FilePreview/FilePreview.css +39 -0
  142. package/src/components/Header/Header.tsx +0 -13
  143. package/src/components/MediaWidget/LinkItemWidget.tsx +1 -1
  144. package/src/components/MediaWidget/MediaItemWidget.stories.tsx +33 -0
  145. package/src/components/MediaWidget/MediaItemWidget.tsx +7 -10
  146. package/src/components/MediaWidget/__snapshots__/LinkItemWidget.test.tsx.snap +4 -4
  147. package/src/components/MediaWidget/__snapshots__/MediaItemWidget.test.tsx.snap +6 -6
  148. package/src/components/MediaWidget/__snapshots__/MediaWidget.test.tsx.snap +2 -2
  149. package/src/components/MemoriArtifactSystem/ArtifactDrawer.stories.tsx +766 -2
  150. package/src/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.tsx +103 -89
  151. package/src/components/MemoriArtifactSystem/utils/ArtifactAPI.test.tsx +307 -0
  152. package/src/components/MemoriArtifactSystem/utils/ArtifactAPI.tsx +373 -0
  153. package/src/components/MemoriWidget/MemoriWidget.tsx +26 -4
  154. package/src/components/Snippet/Snippet.tsx +3 -2
  155. package/src/components/StartPanel/StartPanel.css +14 -0
  156. package/src/components/StartPanel/StartPanel.tsx +23 -10
  157. package/src/components/StartPanel/__snapshots__/StartPanel.test.tsx.snap +206 -84
  158. package/src/components/UploadButton/UploadDocuments/UploadDocuments.tsx +0 -23
  159. package/src/components/VenueWidget/VenueWidget.tsx +0 -1
  160. package/src/components/layouts/HiddenChat.tsx +0 -16
  161. package/src/components/layouts/__snapshots__/Chat.test.tsx.snap +204 -82
  162. package/src/components/layouts/__snapshots__/FullPage.test.tsx.snap +408 -164
  163. package/src/components/layouts/__snapshots__/HiddenChat.test.tsx.snap +204 -82
  164. package/src/components/layouts/__snapshots__/Totem.test.tsx.snap +204 -82
  165. package/src/components/layouts/__snapshots__/ZoomedFullBody.test.tsx.snap +204 -82
  166. package/src/components/layouts/chat.css +2 -2
  167. package/src/context/visemeContext.tsx +0 -7
  168. package/src/helpers/constants.ts +28 -3
  169. package/src/helpers/tts/useTTS.ts +0 -2
  170. package/src/helpers/utils.ts +5 -0
  171. package/src/index.tsx +0 -1
  172. package/src/locales/de.json +2 -0
  173. package/src/locales/en.json +2 -0
  174. package/src/locales/es.json +2 -0
  175. package/src/locales/fr.json +2 -0
  176. package/src/locales/it.json +2 -0
@@ -39,9 +39,9 @@ const Template: Story<Props> = args => {
39
39
  return (
40
40
  <I18nWrapper>
41
41
  <ArtifactProvider>
42
- <Chat
43
- {...args}
44
- userMessage={userMessage}
42
+ <Chat
43
+ {...args}
44
+ userMessage={userMessage}
45
45
  onChangeUserMessage={setUserMessage}
46
46
  />
47
47
  </ArtifactProvider>
@@ -100,7 +100,6 @@ WithHints.args = {
100
100
  setSendOnEnter: () => {},
101
101
  };
102
102
 
103
-
104
103
  export const WithArtifacts = Template.bind({});
105
104
  WithArtifacts.args = {
106
105
  memori,
@@ -327,7 +326,6 @@ WithCustomUserAvatarAsElement.args = {
327
326
  setSendOnEnter: () => {},
328
327
  };
329
328
 
330
-
331
329
  export const WithExpandable = Template.bind({});
332
330
  WithExpandable.args = {
333
331
  memori,
@@ -342,6 +340,434 @@ WithExpandable.args = {
342
340
  resetTranscript: () => {},
343
341
  };
344
342
 
343
+ export const WithMultipleArtifactsInOneMessage = Template.bind({});
344
+ WithMultipleArtifactsInOneMessage.args = {
345
+ memori,
346
+ tenant,
347
+ sessionID,
348
+ history: [
349
+ {
350
+ text: 'Can you help me build a landing page?',
351
+ fromUser: true,
352
+ timestamp: '2021-03-01T12:00:00.000Z',
353
+ },
354
+ {
355
+ text: `I'll create a complete landing page with HTML, CSS, and JavaScript for you:
356
+
357
+ <output class="memori-artifact" data-mimetype="html" data-title="Landing Page HTML">
358
+ <!DOCTYPE html>
359
+ <html lang="en">
360
+ <head>
361
+ <meta charset="UTF-8">
362
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
363
+ <title>Product Landing Page</title>
364
+ </head>
365
+ <body>
366
+ <header class="hero">
367
+ <h1>Welcome to Our Amazing Product</h1>
368
+ <p>The solution you've been waiting for</p>
369
+ <button id="ctaButton" class="cta-button">Get Started</button>
370
+ </header>
371
+
372
+ <section class="features">
373
+ <div class="feature">
374
+ <h3>⚡ Fast</h3>
375
+ <p>Lightning-quick performance</p>
376
+ </div>
377
+ <div class="feature">
378
+ <h3>🔒 Secure</h3>
379
+ <p>Enterprise-grade security</p>
380
+ </div>
381
+ <div class="feature">
382
+ <h3>📱 Responsive</h3>
383
+ <p>Works on all devices</p>
384
+ </div>
385
+ </section>
386
+ </body>
387
+ </html>
388
+ </output>
389
+
390
+ <output class="memori-artifact" data-mimetype="css" data-title="Landing Page Styles">
391
+ * {
392
+ margin: 0;
393
+ padding: 0;
394
+ box-sizing: border-box;
395
+ }
396
+
397
+ body {
398
+ font-family: 'Segoe UI', system-ui, sans-serif;
399
+ line-height: 1.6;
400
+ color: #333;
401
+ }
402
+
403
+ .hero {
404
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
405
+ color: white;
406
+ text-align: center;
407
+ padding: 100px 20px;
408
+ }
409
+
410
+ .hero h1 {
411
+ font-size: 3rem;
412
+ margin-bottom: 20px;
413
+ animation: fadeInUp 0.8s ease-out;
414
+ }
415
+
416
+ .hero p {
417
+ font-size: 1.5rem;
418
+ margin-bottom: 30px;
419
+ opacity: 0.9;
420
+ }
421
+
422
+ .cta-button {
423
+ background: white;
424
+ color: #667eea;
425
+ border: none;
426
+ padding: 15px 40px;
427
+ font-size: 1.1rem;
428
+ font-weight: 600;
429
+ border-radius: 50px;
430
+ cursor: pointer;
431
+ transition: all 0.3s ease;
432
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
433
+ }
434
+
435
+ .cta-button:hover {
436
+ transform: translateY(-3px);
437
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
438
+ }
439
+
440
+ .features {
441
+ display: grid;
442
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
443
+ gap: 40px;
444
+ padding: 80px 20px;
445
+ max-width: 1200px;
446
+ margin: 0 auto;
447
+ }
448
+
449
+ .feature {
450
+ text-align: center;
451
+ padding: 30px;
452
+ background: white;
453
+ border-radius: 10px;
454
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
455
+ transition: transform 0.3s ease;
456
+ }
457
+
458
+ .feature:hover {
459
+ transform: translateY(-5px);
460
+ }
461
+
462
+ .feature h3 {
463
+ font-size: 2rem;
464
+ margin-bottom: 15px;
465
+ }
466
+
467
+ @keyframes fadeInUp {
468
+ from {
469
+ opacity: 0;
470
+ transform: translateY(30px);
471
+ }
472
+ to {
473
+ opacity: 1;
474
+ transform: translateY(0);
475
+ }
476
+ }
477
+ </output>
478
+
479
+ <output class="memori-artifact" data-mimetype="javascript" data-title="Landing Page Script">
480
+ // Add interactivity to the landing page
481
+ document.addEventListener('DOMContentLoaded', () => {
482
+ const ctaButton = document.getElementById('ctaButton');
483
+
484
+ if (ctaButton) {
485
+ ctaButton.addEventListener('click', () => {
486
+ // Smooth scroll animation
487
+ const features = document.querySelector('.features');
488
+ if (features) {
489
+ features.scrollIntoView({
490
+ behavior: 'smooth',
491
+ block: 'start'
492
+ });
493
+ }
494
+
495
+ // Show a welcome message
496
+ setTimeout(() => {
497
+ alert('Welcome! Explore our amazing features below 🚀');
498
+ }, 500);
499
+ });
500
+
501
+ // Add pulse animation on hover
502
+ ctaButton.addEventListener('mouseenter', () => {
503
+ ctaButton.style.animation = 'pulse 0.5s ease';
504
+ });
505
+
506
+ ctaButton.addEventListener('animationend', () => {
507
+ ctaButton.style.animation = '';
508
+ });
509
+ }
510
+
511
+ // Add scroll reveal effect to features
512
+ const features = document.querySelectorAll('.feature');
513
+ const observerOptions = {
514
+ threshold: 0.2,
515
+ rootMargin: '0px 0px -50px 0px'
516
+ };
517
+
518
+ const observer = new IntersectionObserver((entries) => {
519
+ entries.forEach((entry, index) => {
520
+ if (entry.isIntersecting) {
521
+ setTimeout(() => {
522
+ entry.target.style.opacity = '1';
523
+ entry.target.style.transform = 'translateY(0)';
524
+ }, index * 100);
525
+ observer.unobserve(entry.target);
526
+ }
527
+ });
528
+ }, observerOptions);
529
+
530
+ features.forEach(feature => {
531
+ feature.style.opacity = '0';
532
+ feature.style.transform = 'translateY(20px)';
533
+ feature.style.transition = 'all 0.6s ease';
534
+ observer.observe(feature);
535
+ });
536
+ });
537
+
538
+ // Add dynamic CSS for pulse animation
539
+ const style = document.createElement('style');
540
+ style.textContent = \`
541
+ @keyframes pulse {
542
+ 0%, 100% { transform: scale(1); }
543
+ 50% { transform: scale(1.05); }
544
+ }
545
+ \`;
546
+ document.head.appendChild(style);
547
+ </output>
548
+
549
+ I've created three artifacts for you:
550
+ 1. **HTML** - The page structure with a hero section and feature cards
551
+ 2. **CSS** - Beautiful styling with gradients, animations, and responsive design
552
+ 3. **JavaScript** - Interactive features including smooth scrolling and scroll animations
553
+
554
+ Click on each card to view and customize the code!`,
555
+ timestamp: '2021-03-01T12:01:00.000Z',
556
+ },
557
+ ],
558
+ dialogState,
559
+ layout: 'DEFAULT',
560
+ simulateUserPrompt: () => {},
561
+ sendMessage: (msg: string) => console.log(msg),
562
+ stopListening: () => {},
563
+ resetTranscript: () => {},
564
+ setAttachmentsMenuOpen: () => {},
565
+ setSendOnEnter: () => {},
566
+ };
567
+
568
+ export const WithMultipleArtifactsInChatlogPanel = Template.bind({});
569
+ WithMultipleArtifactsInChatlogPanel.args = {
570
+ memori,
571
+ tenant,
572
+ sessionID,
573
+ isChatlogPanel: true,
574
+ history: [
575
+ {
576
+ text: 'Show me a React component with its styles and tests',
577
+ fromUser: true,
578
+ timestamp: '2021-03-01T12:00:00.000Z',
579
+ },
580
+ {
581
+ text: `Here's a complete React component setup with TypeScript, styles, and tests:
582
+
583
+ <output class="memori-artifact" data-mimetype="typescript" data-title="Button.tsx">
584
+ import React from 'react';
585
+ import './Button.css';
586
+
587
+ interface ButtonProps {
588
+ label: string;
589
+ onClick?: () => void;
590
+ variant?: 'primary' | 'secondary' | 'danger';
591
+ disabled?: boolean;
592
+ }
593
+
594
+ export const Button: React.FC<ButtonProps> = ({
595
+ label,
596
+ onClick,
597
+ variant = 'primary',
598
+ disabled = false,
599
+ }) => {
600
+ return (
601
+ <button
602
+ className={\`btn btn-\${variant}\`}
603
+ onClick={onClick}
604
+ disabled={disabled}
605
+ aria-label={label}
606
+ >
607
+ {label}
608
+ </button>
609
+ );
610
+ };
611
+ </output>
612
+
613
+ <output class="memori-artifact" data-mimetype="css" data-title="Button.css">
614
+ .btn {
615
+ padding: 10px 20px;
616
+ font-size: 16px;
617
+ font-weight: 600;
618
+ border: none;
619
+ border-radius: 8px;
620
+ cursor: pointer;
621
+ transition: all 0.3s ease;
622
+ font-family: inherit;
623
+ }
624
+
625
+ .btn:disabled {
626
+ opacity: 0.5;
627
+ cursor: not-allowed;
628
+ }
629
+
630
+ .btn-primary {
631
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
632
+ color: white;
633
+ }
634
+
635
+ .btn-primary:hover:not(:disabled) {
636
+ transform: translateY(-2px);
637
+ box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
638
+ }
639
+
640
+ .btn-secondary {
641
+ background: #6c757d;
642
+ color: white;
643
+ }
644
+
645
+ .btn-secondary:hover:not(:disabled) {
646
+ background: #5a6268;
647
+ }
648
+
649
+ .btn-danger {
650
+ background: #dc3545;
651
+ color: white;
652
+ }
653
+
654
+ .btn-danger:hover:not(:disabled) {
655
+ background: #c82333;
656
+ }
657
+ </output>
658
+
659
+ <output class="memori-artifact" data-mimetype="typescript" data-title="Button.test.tsx">
660
+ import { render, screen, fireEvent } from '@testing-library/react';
661
+ import { Button } from './Button';
662
+
663
+ describe('Button Component', () => {
664
+ it('renders with correct label', () => {
665
+ render(<Button label="Click me" />);
666
+ expect(screen.getByText('Click me')).toBeInTheDocument();
667
+ });
668
+
669
+ it('calls onClick handler when clicked', () => {
670
+ const handleClick = jest.fn();
671
+ render(<Button label="Click me" onClick={handleClick} />);
672
+
673
+ fireEvent.click(screen.getByText('Click me'));
674
+ expect(handleClick).toHaveBeenCalledTimes(1);
675
+ });
676
+
677
+ it('applies correct variant class', () => {
678
+ const { rerender } = render(<Button label="Test" variant="primary" />);
679
+ expect(screen.getByRole('button')).toHaveClass('btn-primary');
680
+
681
+ rerender(<Button label="Test" variant="secondary" />);
682
+ expect(screen.getByRole('button')).toHaveClass('btn-secondary');
683
+
684
+ rerender(<Button label="Test" variant="danger" />);
685
+ expect(screen.getByRole('button')).toHaveClass('btn-danger');
686
+ });
687
+
688
+ it('is disabled when disabled prop is true', () => {
689
+ const handleClick = jest.fn();
690
+ render(<Button label="Click me" onClick={handleClick} disabled />);
691
+
692
+ const button = screen.getByRole('button');
693
+ expect(button).toBeDisabled();
694
+
695
+ fireEvent.click(button);
696
+ expect(handleClick).not.toHaveBeenCalled();
697
+ });
698
+
699
+ it('has correct accessibility attributes', () => {
700
+ render(<Button label="Submit Form" />);
701
+ const button = screen.getByRole('button');
702
+ expect(button).toHaveAttribute('aria-label', 'Submit Form');
703
+ });
704
+ });
705
+ </output>
706
+
707
+ <output class="memori-artifact" data-mimetype="markdown" data-title="Button Documentation">
708
+ # Button Component
709
+
710
+ A reusable button component with multiple variants and full TypeScript support.
711
+
712
+ ## Usage
713
+
714
+ \`\`\`tsx
715
+ import { Button } from './Button';
716
+
717
+ function App() {
718
+ return (
719
+ <Button
720
+ label="Click me"
721
+ variant="primary"
722
+ onClick={() => console.log('Clicked!')}
723
+ />
724
+ );
725
+ }
726
+ \`\`\`
727
+
728
+ ## Props
729
+
730
+ | Prop | Type | Default | Description |
731
+ |------|------|---------|-------------|
732
+ | label | string | required | Button text content |
733
+ | onClick | function | - | Click handler function |
734
+ | variant | 'primary' \\| 'secondary' \\| 'danger' | 'primary' | Button style variant |
735
+ | disabled | boolean | false | Disabled state |
736
+
737
+ ## Variants
738
+
739
+ - **primary**: Main action button with gradient background
740
+ - **secondary**: Secondary actions with gray background
741
+ - **danger**: Destructive actions with red background
742
+
743
+ ## Accessibility
744
+
745
+ - Uses semantic \`<button>\` element
746
+ - Includes \`aria-label\` attribute
747
+ - Keyboard accessible
748
+ - Proper disabled state handling
749
+
750
+ ## Testing
751
+
752
+ Run tests with:
753
+ \`\`\`bash
754
+ npm test Button.test.tsx
755
+ \`\`\`
756
+ </output>
757
+
758
+ You now have a complete component with implementation, styles, tests, and documentation! Each artifact can be clicked to view and modify.`,
759
+ timestamp: '2021-03-01T12:01:00.000Z',
760
+ },
761
+ ],
762
+ dialogState,
763
+ layout: 'CHAT',
764
+ simulateUserPrompt: () => {},
765
+ sendMessage: (msg: string) => console.log(msg),
766
+ stopListening: () => {},
767
+ resetTranscript: () => {},
768
+ setAttachmentsMenuOpen: () => {},
769
+ setSendOnEnter: () => {},
770
+ };
345
771
 
346
772
  export const WithLongHTMLTable = Template.bind({});
347
773
  WithLongHTMLTable.args = {
@@ -360,7 +786,7 @@ WithUploads.args = {
360
786
  sessionID,
361
787
  history: [
362
788
  {
363
- text: "Ciao! Sono qui per aiutarti. Puoi condividere con me documenti, immagini o link che vorresti che analizzi.",
789
+ text: 'Ciao! Sono qui per aiutarti. Puoi condividere con me documenti, immagini o link che vorresti che analizzi.',
364
790
  timestamp: '2021-03-01T12:00:00.000Z',
365
791
  },
366
792
  {
@@ -513,7 +939,7 @@ WithAllMediaTypes.args = {
513
939
  sessionID,
514
940
  history: [
515
941
  {
516
- text: "Ciao! Sono qui per aiutarti. Posso gestire molti tipi di file e contenuti multimediali. Dimmi cosa ti serve!",
942
+ text: 'Ciao! Sono qui per aiutarti. Posso gestire molti tipi di file e contenuti multimediali. Dimmi cosa ti serve!',
517
943
  timestamp: '2021-03-01T12:00:00.000Z',
518
944
  },
519
945
  {
@@ -753,9 +1179,44 @@ build-storybook = "build-storybook"`,
753
1179
  fromUser: true,
754
1180
  timestamp: '2021-03-01T12:04:00.000Z',
755
1181
  text: 'E anche file di configurazione e dati strutturati:\n\n<document_attachment filename="database.sql" type="text/sql">-- Schema del database\nCREATE TABLE users (\n id SERIAL PRIMARY KEY,\n username VARCHAR(50) UNIQUE NOT NULL,\n email VARCHAR(100) UNIQUE NOT NULL,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE TABLE posts (\n id SERIAL PRIMARY KEY,\n user_id INTEGER REFERENCES users(id),\n title VARCHAR(200) NOT NULL,\n content TEXT,\n published BOOLEAN DEFAULT FALSE,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\n-- Indici per performance\nCREATE INDEX idx_posts_user_id ON posts(user_id);\nCREATE INDEX idx_posts_published ON posts(published);</document_attachment>\n\n<document_attachment filename="data.xml" type="text/xml"><?xml version="1.0" encoding="UTF-8"?>\n<root>\n <users>\n <user id="1">\n <name>Mario Rossi</name>\n <email>mario@example.com</email>\n <role>admin</role>\n </user>\n <user id="2">\n <name>Giulia Bianchi</name>\n <email>giulia@example.com</email>\n <role>user</role>\n </user>\n </users>\n <settings>\n <theme>dark</theme>\n <language>it</language>\n <notifications>true</notifications>\n </settings>\n</root></document_attachment>',
1182
+ media: [
1183
+ {
1184
+ mediumID: 'long-2',
1185
+ mimeType: 'text/javascript',
1186
+ title: 'Long JSON',
1187
+ content: `{
1188
+ "id": 1,
1189
+ "title": "Ciao",
1190
+ "description": "I'm a test!",
1191
+ "refs": [
1192
+ {
1193
+ "id": 1,
1194
+ "tag": "TEST"
1195
+ }
1196
+ ]
1197
+ }
1198
+ `,
1199
+ },
1200
+ {
1201
+ mediumID: 'long-3',
1202
+ mimeType: 'text/plain',
1203
+ title: 'Long Text',
1204
+ content: `{
1205
+ "id": 1,
1206
+ "title": "Ciao",
1207
+ "description": "I'm a test!",
1208
+ "refs": [
1209
+ {
1210
+ "id": 1,
1211
+ "tag": "TEST"
1212
+ }
1213
+ ]
1214
+ }`,
1215
+ },
1216
+ ],
756
1217
  },
757
1218
  {
758
- text: 'Perfetto! Come puoi vedere, posso gestire molti tipi di contenuti:\n\n• **Immagini**: JPEG, PNG, GIF\n• **Documenti**: PDF, Word, Excel\n• **Link**: Siti web con preview\n• **Video**: MP4, AVI, QuickTime\n• **Audio**: MP3, WAV\n• **Modelli 3D**: GLB, GLTF\n• **Codice**: JavaScript, Python, CSS, SQL\n• **Dati**: JSON, XML, Markdown\n\nTutti questi contenuti vengono visualizzati in modo appropriato e possono essere interagiti dall\'utente!',
1219
+ text: "Perfetto! Come puoi vedere, posso gestire molti tipi di contenuti:\n\n• **Immagini**: JPEG, PNG, GIF\n• **Documenti**: PDF, Word, Excel\n• **Link**: Siti web con preview\n• **Video**: MP4, AVI, QuickTime\n• **Audio**: MP3, WAV\n• **Modelli 3D**: GLB, GLTF\n• **Codice**: JavaScript, Python, CSS, SQL\n• **Dati**: JSON, XML, Markdown\n\nTutti questi contenuti vengono visualizzati in modo appropriato e possono essere interagiti dall'utente!",
759
1220
  timestamp: '2021-03-01T12:05:00.000Z',
760
1221
  media: [
761
1222
  {
@@ -770,6 +1231,39 @@ build-storybook = "build-storybook"`,
770
1231
  mimeType: 'text/html',
771
1232
  title: 'Documentazione completa',
772
1233
  },
1234
+ {
1235
+ mediumID: 'long-2',
1236
+ mimeType: 'text/javascript',
1237
+ title: 'Long JSON',
1238
+ content: `{
1239
+ "id": 1,
1240
+ "title": "Ciao",
1241
+ "description": "I'm a test!",
1242
+ "refs": [
1243
+ {
1244
+ "id": 1,
1245
+ "tag": "TEST"
1246
+ }
1247
+ ]
1248
+ }
1249
+ `,
1250
+ },
1251
+ {
1252
+ mediumID: 'long-3',
1253
+ mimeType: 'text/plain',
1254
+ title: 'Long Text',
1255
+ content: `{
1256
+ "id": 1,
1257
+ "title": "Ciao",
1258
+ "description": "I'm a test!",
1259
+ "refs": [
1260
+ {
1261
+ "id": 1,
1262
+ "tag": "TEST"
1263
+ }
1264
+ ]
1265
+ }`,
1266
+ },
773
1267
  ],
774
1268
  },
775
1269
  ],
@@ -781,4 +1275,4 @@ build-storybook = "build-storybook"`,
781
1275
  resetTranscript: () => {},
782
1276
  setAttachmentsMenuOpen: () => {},
783
1277
  setSendOnEnter: () => {},
784
- };
1278
+ };
@@ -124,11 +124,13 @@ const Chat: React.FC<Props> = ({
124
124
  isChatlogPanel = false,
125
125
  showFunctionCache = false,
126
126
  }) => {
127
+ const [isTextareaExpanded, setIsTextareaExpanded] = useState(false);
128
+
127
129
  const scrollToBottom = () => {
128
130
  if (isHistoryView) return;
129
131
  setTimeout(() => {
130
132
  let userMsgs = document.querySelectorAll(
131
- '.memori-chat--bubble-container.memori-chat--bubble-from-user'
133
+ '.memori-chat-scroll-item'
132
134
  );
133
135
  userMsgs[userMsgs.length - 1]?.scrollIntoView?.();
134
136
  }, 200);
@@ -137,6 +139,15 @@ const Chat: React.FC<Props> = ({
137
139
  !preview && !isHistoryView && scrollToBottom();
138
140
  }, [history, preview, isHistoryView]);
139
141
 
142
+ // Scroll to bottom when textarea is expanded
143
+ useEffect(() => {
144
+ if (isTextareaExpanded && !isHistoryView) {
145
+ setTimeout(() => {
146
+ scrollToBottom();
147
+ }, 250);
148
+ }
149
+ }, [isTextareaExpanded, isHistoryView]);
150
+
140
151
  const onTextareaFocus = () => {
141
152
  stopListening();
142
153
  const hasTouch = hasTouchscreen();
@@ -168,10 +179,15 @@ const Chat: React.FC<Props> = ({
168
179
  }
169
180
  };
170
181
 
182
+ const onTextareaExpanded = (expanded: boolean) => {
183
+ setIsTextareaExpanded(expanded);
184
+ };
185
+
171
186
  return (
172
187
  <div
173
188
  className={cx('memori-chat--wrapper', {
174
189
  'memori-chat-wrapper--translate': translateTo,
190
+ 'memori-chat-wrapper--expanded': isTextareaExpanded,
175
191
  })}
176
192
  id="chat-wrapper"
177
193
  lang={translateTo?.toUpperCase()}
@@ -301,7 +317,7 @@ const Chat: React.FC<Props> = ({
301
317
  // Filter out HTML and plain text media items from the message
302
318
  ...(message?.media?.filter(
303
319
  m =>
304
- m.mimeType !== 'text/html' && m.mimeType !== 'text/plain'
320
+ m.mimeType !== 'text/html'
305
321
  ) || []),
306
322
 
307
323
  // Extract document attachments that are embedded in the message text
@@ -316,6 +332,7 @@ const Chat: React.FC<Props> = ({
316
332
 
317
333
  const attachments: (Medium & { type?: string })[] = [];
318
334
  let match;
335
+ let attachmentIndex = 0;
319
336
 
320
337
  // Find all document attachments in the text
321
338
  while (
@@ -330,7 +347,7 @@ const Chat: React.FC<Props> = ({
330
347
  // - Trimmed content from the attachment
331
348
  // - Properties to mark it as a document attachment
332
349
  attachments.push({
333
- mediumID: `doc_${Date.now()}_${Math.random()
350
+ mediumID: `doc_${Date.now()}_${attachmentIndex}_${Math.random()
334
351
  .toString(36)
335
352
  .substr(2, 9)}`,
336
353
  url: '',
@@ -340,6 +357,8 @@ const Chat: React.FC<Props> = ({
340
357
  properties: { isDocumentAttachment: true },
341
358
  type: 'document',
342
359
  });
360
+
361
+ attachmentIndex++;
343
362
  }
344
363
 
345
364
  return attachments;
@@ -422,6 +441,7 @@ const Chat: React.FC<Props> = ({
422
441
  setAttachmentsMenuOpen={setAttachmentsMenuOpen}
423
442
  onTextareaFocus={onTextareaFocus}
424
443
  onTextareaBlur={onTextareaBlur}
444
+ onTextareaExpanded={onTextareaExpanded}
425
445
  startListening={startListening}
426
446
  stopListening={stopListening}
427
447
  stopAudio={stopAudio}