@memori.ai/memori-react 8.2.0 → 8.4.0-rc.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 (146) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/components/Chat/Chat.js +8 -1
  3. package/dist/components/Chat/Chat.js.map +1 -1
  4. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.css +160 -0
  5. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.d.ts +4 -0
  6. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.js +166 -0
  7. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.js.map +1 -0
  8. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.css +877 -0
  9. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.d.ts +3 -0
  10. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.js +115 -0
  11. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.js.map +1 -0
  12. package/dist/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.css +238 -0
  13. package/dist/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.d.ts +4 -0
  14. package/dist/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.js +104 -0
  15. package/dist/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.js.map +1 -0
  16. package/dist/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.css +319 -0
  17. package/dist/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.d.ts +4 -0
  18. package/dist/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.js +50 -0
  19. package/dist/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.js.map +1 -0
  20. package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.css +343 -0
  21. package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.d.ts +4 -0
  22. package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js +78 -0
  23. package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js.map +1 -0
  24. package/dist/components/MemoriArtifactSystem/context/ArtifactSystemContext.d.ts +12 -0
  25. package/dist/components/MemoriArtifactSystem/context/ArtifactSystemContext.js +22 -0
  26. package/dist/components/MemoriArtifactSystem/context/ArtifactSystemContext.js.map +1 -0
  27. package/dist/components/MemoriArtifactSystem/hooks/useArtifactSystem.d.ts +12 -0
  28. package/dist/components/MemoriArtifactSystem/hooks/useArtifactSystem.js +288 -0
  29. package/dist/components/MemoriArtifactSystem/hooks/useArtifactSystem.js.map +1 -0
  30. package/dist/components/MemoriArtifactSystem/index.d.ts +9 -0
  31. package/dist/components/MemoriArtifactSystem/index.js +28 -0
  32. package/dist/components/MemoriArtifactSystem/index.js.map +1 -0
  33. package/dist/components/MemoriArtifactSystem/types/artifact.types.d.ts +108 -0
  34. package/dist/components/MemoriArtifactSystem/types/artifact.types.js +31 -0
  35. package/dist/components/MemoriArtifactSystem/types/artifact.types.js.map +1 -0
  36. package/dist/components/icons/Print.d.ts +6 -0
  37. package/dist/components/icons/Print.js +6 -0
  38. package/dist/components/icons/Print.js.map +1 -0
  39. package/dist/components/layouts/Chat.js +29 -1
  40. package/dist/components/layouts/Chat.js.map +1 -1
  41. package/dist/components/layouts/FullPage.js +33 -1
  42. package/dist/components/layouts/FullPage.js.map +1 -1
  43. package/dist/components/layouts/ZoomedFullBody.js +29 -2
  44. package/dist/components/layouts/ZoomedFullBody.js.map +1 -1
  45. package/dist/components/layouts/chat.css +335 -13
  46. package/dist/components/layouts/zoomed-full-body.css +1 -3
  47. package/dist/helpers/message.js +1 -0
  48. package/dist/helpers/message.js.map +1 -1
  49. package/dist/helpers/stt/useSTT.js +76 -9
  50. package/dist/helpers/stt/useSTT.js.map +1 -1
  51. package/dist/index.js +58 -15
  52. package/dist/index.js.map +1 -1
  53. package/dist/styles.css +5 -0
  54. package/esm/components/Chat/Chat.js +8 -1
  55. package/esm/components/Chat/Chat.js.map +1 -1
  56. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.css +160 -0
  57. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.d.ts +4 -0
  58. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.js +163 -0
  59. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.js.map +1 -0
  60. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.css +877 -0
  61. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.d.ts +3 -0
  62. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.js +112 -0
  63. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.js.map +1 -0
  64. package/esm/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.css +238 -0
  65. package/esm/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.d.ts +4 -0
  66. package/esm/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.js +101 -0
  67. package/esm/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.js.map +1 -0
  68. package/esm/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.css +319 -0
  69. package/esm/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.d.ts +4 -0
  70. package/esm/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.js +47 -0
  71. package/esm/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.js.map +1 -0
  72. package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.css +343 -0
  73. package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.d.ts +4 -0
  74. package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js +75 -0
  75. package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js.map +1 -0
  76. package/esm/components/MemoriArtifactSystem/context/ArtifactSystemContext.d.ts +12 -0
  77. package/esm/components/MemoriArtifactSystem/context/ArtifactSystemContext.js +17 -0
  78. package/esm/components/MemoriArtifactSystem/context/ArtifactSystemContext.js.map +1 -0
  79. package/esm/components/MemoriArtifactSystem/hooks/useArtifactSystem.d.ts +12 -0
  80. package/esm/components/MemoriArtifactSystem/hooks/useArtifactSystem.js +281 -0
  81. package/esm/components/MemoriArtifactSystem/hooks/useArtifactSystem.js.map +1 -0
  82. package/esm/components/MemoriArtifactSystem/index.d.ts +9 -0
  83. package/esm/components/MemoriArtifactSystem/index.js +9 -0
  84. package/esm/components/MemoriArtifactSystem/index.js.map +1 -0
  85. package/esm/components/MemoriArtifactSystem/types/artifact.types.d.ts +108 -0
  86. package/esm/components/MemoriArtifactSystem/types/artifact.types.js +28 -0
  87. package/esm/components/MemoriArtifactSystem/types/artifact.types.js.map +1 -0
  88. package/esm/components/icons/Print.d.ts +6 -0
  89. package/esm/components/icons/Print.js +4 -0
  90. package/esm/components/icons/Print.js.map +1 -0
  91. package/esm/components/layouts/Chat.js +29 -1
  92. package/esm/components/layouts/Chat.js.map +1 -1
  93. package/esm/components/layouts/FullPage.js +33 -1
  94. package/esm/components/layouts/FullPage.js.map +1 -1
  95. package/esm/components/layouts/ZoomedFullBody.js +30 -3
  96. package/esm/components/layouts/ZoomedFullBody.js.map +1 -1
  97. package/esm/components/layouts/chat.css +335 -13
  98. package/esm/components/layouts/zoomed-full-body.css +1 -3
  99. package/esm/helpers/message.js +1 -0
  100. package/esm/helpers/message.js.map +1 -1
  101. package/esm/helpers/stt/useSTT.js +76 -9
  102. package/esm/helpers/stt/useSTT.js.map +1 -1
  103. package/esm/index.js +58 -15
  104. package/esm/index.js.map +1 -1
  105. package/esm/styles.css +5 -0
  106. package/package.json +1 -1
  107. package/src/components/Avatar/Avatar.test.tsx +13 -0
  108. package/src/components/Chat/Chat.stories.tsx +33 -2
  109. package/src/components/Chat/Chat.test.tsx +340 -213
  110. package/src/components/Chat/Chat.tsx +27 -4
  111. package/src/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.css +160 -0
  112. package/src/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.tsx +278 -0
  113. package/src/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.css +877 -0
  114. package/src/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.tsx +308 -0
  115. package/src/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.css +238 -0
  116. package/src/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.tsx +282 -0
  117. package/src/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.css +319 -0
  118. package/src/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.tsx +178 -0
  119. package/src/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.css +343 -0
  120. package/src/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.tsx +190 -0
  121. package/src/components/MemoriArtifactSystem/context/ArtifactSystemContext.tsx +57 -0
  122. package/src/components/MemoriArtifactSystem/hooks/useArtifactSystem.ts +419 -0
  123. package/src/components/MemoriArtifactSystem/index.ts +45 -0
  124. package/src/components/MemoriArtifactSystem/types/artifact.types.ts +180 -0
  125. package/src/components/icons/Print.tsx +34 -0
  126. package/src/components/layouts/Chat.test.tsx +13 -0
  127. package/src/components/layouts/Chat.tsx +80 -25
  128. package/src/components/layouts/FullPage.test.tsx +40 -11
  129. package/src/components/layouts/FullPage.tsx +92 -24
  130. package/src/components/layouts/HiddenChat.test.tsx +13 -0
  131. package/src/components/layouts/Totem.test.tsx +13 -0
  132. package/src/components/layouts/WebsiteAssistant.test.tsx +13 -0
  133. package/src/components/layouts/ZoomedFullBody.test.tsx +13 -0
  134. package/src/components/layouts/ZoomedFullBody.tsx +78 -14
  135. package/src/components/layouts/__snapshots__/Chat.test.tsx.snap +252 -248
  136. package/src/components/layouts/__snapshots__/FullPage.test.tsx.snap +504 -496
  137. package/src/components/layouts/__snapshots__/ZoomedFullBody.test.tsx.snap +252 -248
  138. package/src/components/layouts/chat.css +335 -13
  139. package/src/components/layouts/layouts.stories.tsx +13 -2
  140. package/src/components/layouts/zoomed-full-body.css +1 -3
  141. package/src/helpers/message.ts +1 -0
  142. package/src/helpers/stt/useSTT.ts +101 -16
  143. package/src/index.stories.tsx +26 -22
  144. package/src/index.tsx +46 -0
  145. package/src/mocks/data.ts +258 -0
  146. package/src/styles.css +5 -0
@@ -0,0 +1,178 @@
1
+ /**
2
+ * ArtifactHistory Component
3
+ * Displays a list of artifact history entries with selection and management
4
+ * Following the project's component patterns and design system
5
+ */
6
+
7
+ import React, { useCallback, useMemo } from 'react';
8
+ import { useTranslation } from 'react-i18next';
9
+ import cx from 'classnames';
10
+ import Button from '../../../ui/Button';
11
+ import { ArtifactHistoryProps, ArtifactData } from '../../types/artifact.types';
12
+ import Close from '../../../icons/Close';
13
+ import Clear from '../../../icons/Clear';
14
+ import ArrowUp from '../../../icons/ArrowUp';
15
+
16
+ const ArtifactHistory: React.FC<ArtifactHistoryProps> = ({
17
+ history,
18
+ onSelectArtifact,
19
+ onClearHistory,
20
+ maxItems = 50,
21
+ }) => {
22
+ const { t } = useTranslation();
23
+
24
+ /**
25
+ * Format file size in human readable format
26
+ */
27
+ const formatBytes = useCallback((bytes: number): string => {
28
+ if (bytes === 0) return '0 Bytes';
29
+ const k = 1024;
30
+ const sizes = ['Bytes', 'KB', 'MB'];
31
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
32
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
33
+ }, []);
34
+
35
+ /**
36
+ * Format timestamp for display
37
+ */
38
+ const formatTimestamp = useCallback((timestamp: Date): string => {
39
+ return timestamp.toLocaleTimeString('en-US', {
40
+ hour: '2-digit',
41
+ minute: '2-digit',
42
+ });
43
+ }, []);
44
+
45
+ /**
46
+ * Handle artifact selection
47
+ */
48
+ const handleSelectArtifact = useCallback((artifact: ArtifactData) => {
49
+ onSelectArtifact(artifact);
50
+ }, [onSelectArtifact]);
51
+
52
+ /**
53
+ * Handle clear history
54
+ */
55
+ const handleClearHistory = useCallback(() => {
56
+ if (onClearHistory && window.confirm(t('artifact.clearHistoryConfirm', 'Are you sure you want to clear all artifact history?') || 'Are you sure you want to clear all artifact history?')) {
57
+ onClearHistory();
58
+ }
59
+ }, [onClearHistory, t]);
60
+
61
+ /**
62
+ * Get limited history items
63
+ */
64
+ const limitedHistory = useMemo(() => {
65
+ return history.slice(0, maxItems);
66
+ }, [history, maxItems]);
67
+
68
+ /**
69
+ * Check if there are more items than the limit
70
+ */
71
+ const hasMoreItems = useMemo(() => {
72
+ return history.length > maxItems;
73
+ }, [history.length, maxItems]);
74
+
75
+ if (history.length === 0) {
76
+ return (
77
+ <div className="memori-artifact-history">
78
+ <div className="memori-artifact-history-header">
79
+ <h3 className="memori-artifact-history-title">
80
+ {t('artifact.history', 'Artifact History')}
81
+ </h3>
82
+ </div>
83
+ <div className="memori-artifact-history-empty">
84
+ <div className="memori-artifact-history-empty-icon">📄</div>
85
+ <p className="memori-artifact-history-empty-text">
86
+ {t('artifact.noHistory', 'No artifacts yet')}
87
+ </p>
88
+ <p className="memori-artifact-history-empty-subtext">
89
+ {t('artifact.noHistorySubtext', 'Artifacts will appear here when generated')}
90
+ </p>
91
+ </div>
92
+ </div>
93
+ );
94
+ }
95
+
96
+ return (
97
+ <div className="memori-artifact-history">
98
+ <div className="memori-artifact-history-header">
99
+ <h3 className="memori-artifact-history-title">
100
+ {t('artifact.history', 'Artifact History')}
101
+ </h3>
102
+ <div className="memori-artifact-history-actions">
103
+ {onClearHistory && (
104
+ <Button
105
+ onClick={handleClearHistory}
106
+ className="memori-artifact-history-clear-btn"
107
+ ghost
108
+ title={t('artifact.clearHistory', 'Clear history') || 'Clear history'}
109
+ >
110
+ <Clear className="memori-artifact-history-clear-btn-icon" />
111
+ </Button>
112
+ )}
113
+ </div>
114
+ </div>
115
+
116
+ <div className="memori-artifact-history-list">
117
+ {limitedHistory.map((artifact) => (
118
+ <div
119
+ key={artifact.id}
120
+ className={cx('memori-artifact-history-item', {
121
+ 'memori-artifact-history-item--active': artifact.isActive,
122
+ })}
123
+ onClick={() => handleSelectArtifact(artifact)}
124
+ >
125
+ <div className="memori-artifact-history-item-content">
126
+ <div className="memori-artifact-history-item-icon">
127
+ {artifact.typeInfo.icon}
128
+ </div>
129
+
130
+ <div className="memori-artifact-history-item-info">
131
+ <div className="memori-artifact-history-item-title">
132
+ {artifact.title}
133
+ </div>
134
+ <div className="memori-artifact-history-item-subtitle">
135
+ {t('artifact.generatedAt', 'Generated at')} {formatTimestamp(artifact.timestamp)} • {formatBytes(artifact.size)}
136
+ </div>
137
+ <div className="memori-artifact-history-item-meta">
138
+ <span className="memori-artifact-history-item-type">
139
+ {artifact.typeInfo.name}
140
+ </span>
141
+ {artifact.customTitle && (
142
+ <span className="memori-artifact-history-item-custom-title">
143
+ {artifact.customTitle}
144
+ </span>
145
+ )}
146
+ </div>
147
+ </div>
148
+
149
+ <div className="memori-artifact-history-item-actions">
150
+ <Button
151
+ className="memori-artifact-history-item-toggle"
152
+ ghost
153
+ title={artifact.isActive ? (t('artifact.close', 'Close') || 'Close') : (t('artifact.open', 'Open') || 'Open')}
154
+ >
155
+ {artifact.isActive ? (
156
+ <Close className="memori-artifact-history-item-toggle-icon" />
157
+ ) : (
158
+ <ArrowUp className="memori-artifact-history-item-toggle-icon" />
159
+ )}
160
+ </Button>
161
+ </div>
162
+ </div>
163
+ </div>
164
+ ))}
165
+
166
+ {hasMoreItems && (
167
+ <div className="memori-artifact-history-more">
168
+ <p className="memori-artifact-history-more-text">
169
+ {t('artifact.moreItems', `+${history.length - maxItems} more items`)}
170
+ </p>
171
+ </div>
172
+ )}
173
+ </div>
174
+ </div>
175
+ );
176
+ };
177
+
178
+ export default ArtifactHistory;
@@ -0,0 +1,343 @@
1
+ /**
2
+ * ArtifactPreview CSS Styles
3
+ * Following the project's design system and CSS patterns
4
+ */
5
+
6
+ .memori-artifact-preview {
7
+ display: flex;
8
+ height: 100%;
9
+ min-height: 0;
10
+ flex-direction: column;
11
+ }
12
+
13
+ /* Tabs */
14
+ .memori-artifact-tabs {
15
+ display: flex;
16
+ justify-content: flex-end;
17
+ padding: 0.5rem;
18
+ /* border-bottom: 1px solid var(--memori-button-border-color, #e9ecef); */
19
+ border-radius: var(--memori-border-radius, 6px) var(--memori-border-radius, 6px) 0 0;
20
+ background: var(--memori-inner-bg, #f8f9fa);
21
+ gap: 0.25rem;
22
+ }
23
+
24
+ .memori-artifact-tab-icon{
25
+ width: 16px;
26
+ height: 16px;
27
+ }
28
+
29
+ .memori-artifact-tab {
30
+ display: flex;
31
+ align-items: center;
32
+ padding: 0.75rem 1rem;
33
+ border: none;
34
+ border-radius: var(--memori-border-radius, 4px);
35
+ background: transparent;
36
+ color: var(--memori-text-color, #666);
37
+ cursor: pointer;
38
+ font-size: 0.875rem;
39
+ font-weight: 500;
40
+ gap: 0.5rem;
41
+ transition: all 0.2s ease;
42
+ white-space: nowrap;
43
+ }
44
+
45
+ /* .memori-artifact-tab:hover:not(.memori-artifact-tab--active) {
46
+ background: rgba(0, 123, 255, 0.05);
47
+ color: var(--memori-text-color, #333);
48
+ } */
49
+
50
+ .memori-artifact-tab--active, .memori-artifact-tab:hover, .memori-artifact-tab:active, .memori-artifact-tab:focus {
51
+ background: var(--memori-chat-bubble-bg, #fff);
52
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
53
+ color: var(--memori-primary);
54
+ }
55
+
56
+ /* Content */
57
+ .memori-artifact-content {
58
+ display: flex;
59
+ overflow: hidden;
60
+ min-height: 0;
61
+ flex: 1;
62
+ flex-direction: column;
63
+ border-radius: 0 0 var(--memori-border-radius, 6px) var(--memori-border-radius, 6px);
64
+ background: var(--memori-chat-bubble-bg, #fff);
65
+ }
66
+
67
+ .memori-artifact-tab-content {
68
+ display: none;
69
+ overflow: hidden;
70
+ min-height: 0;
71
+ flex: 1;
72
+ }
73
+
74
+ .memori-artifact-tab-content--active {
75
+ display: flex;
76
+ flex-direction: column;
77
+ }
78
+
79
+ /* Code Content */
80
+ .memori-artifact-code {
81
+ overflow: auto;
82
+ flex: 1;
83
+ border: 1px solid var(--memori-button-border-color, #e9ecef);
84
+ border-radius: var(--memori-border-radius, 6px);
85
+ margin: 1rem;
86
+ background: var(--memori-chat-bubble-bg, #fff);
87
+ }
88
+
89
+ .memori-artifact-code-content {
90
+ padding: 1rem;
91
+ margin: 0;
92
+ color: var(--memori-text-color, #333);
93
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', monospace;
94
+ font-size: 0.875rem;
95
+ line-height: 1.5;
96
+ overflow-wrap: break-word;
97
+ white-space: pre-wrap;
98
+ word-wrap: break-word;
99
+ }
100
+
101
+ /* Preview Content */
102
+ .memori-artifact-preview-content {
103
+ position: relative;
104
+ overflow: auto;
105
+ flex: 1;
106
+ border: 1px solid var(--memori-button-border-color, #e9ecef);
107
+ border-radius: var(--memori-border-radius, 6px);
108
+ margin: 1rem;
109
+ background: var(--memori-chat-bubble-bg, #fff);
110
+ }
111
+
112
+ /* Markdown Preview */
113
+ .memori-artifact-preview-markdown {
114
+ padding: 1rem;
115
+ color: var(--memori-text-color, #333);
116
+ font-family: var(--memori-font-family);
117
+ line-height: 1.6;
118
+ }
119
+
120
+ .memori-artifact-preview-markdown h1,
121
+ .memori-artifact-preview-markdown h2,
122
+ .memori-artifact-preview-markdown h3,
123
+ .memori-artifact-preview-markdown h4,
124
+ .memori-artifact-preview-markdown h5,
125
+ .memori-artifact-preview-markdown h6 {
126
+ margin: 1.5rem 0 0.5rem;
127
+ font-weight: 600;
128
+ line-height: 1.3;
129
+ }
130
+
131
+ .memori-artifact-preview-css-content h3 {
132
+ margin: 0 0 1rem;
133
+ font-size: 1.125rem;
134
+ font-weight: 600;
135
+ }
136
+
137
+ .memori-artifact-preview-markdown h1:first-child,
138
+ .memori-artifact-preview-markdown h2:first-child,
139
+ .memori-artifact-preview-markdown h3:first-child {
140
+ margin-top: 0;
141
+ }
142
+
143
+ .memori-artifact-preview-markdown p {
144
+ margin: 0 0 1rem;
145
+ }
146
+
147
+ .memori-artifact-preview-markdown code {
148
+ padding: 0.125rem 0.25rem;
149
+ border-radius: 3px;
150
+ background: var(--memori-inner-bg, #f1f3f4);
151
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
152
+ font-size: 0.875em;
153
+ }
154
+
155
+ .memori-artifact-preview-markdown pre {
156
+ padding: 1rem;
157
+ border-radius: var(--memori-border-radius, 6px);
158
+ margin: 1rem 0;
159
+ background: var(--memori-inner-bg, #f1f3f4);
160
+ overflow-x: auto;
161
+ }
162
+
163
+ .memori-artifact-preview-markdown pre code {
164
+ padding: 0;
165
+ background: none;
166
+ }
167
+
168
+ .memori-artifact-preview-markdown a {
169
+ color: var(--memori-primary);
170
+ text-decoration: none;
171
+ }
172
+
173
+ .memori-artifact-preview-markdown a:hover {
174
+ text-decoration: underline;
175
+ }
176
+
177
+ /* SVG Preview */
178
+ .memori-artifact-preview-svg {
179
+ display: flex;
180
+ min-height: 200px;
181
+ align-items: center;
182
+ justify-content: center;
183
+ padding: 1rem;
184
+ }
185
+
186
+ .memori-artifact-preview-svg svg {
187
+ max-width: 100%;
188
+ max-height: 100%;
189
+ border: 1px solid var(--memori-button-border-color, #e9ecef);
190
+ border-radius: var(--memori-border-radius, 6px);
191
+ }
192
+
193
+ /* CSS Preview */
194
+ .memori-artifact-preview-css {
195
+ padding: 1rem;
196
+ }
197
+
198
+ .memori-artifact-preview-css-demo {
199
+ overflow: hidden;
200
+ border: 1px solid var(--memori-button-border-color, #e9ecef);
201
+ border-radius: var(--memori-border-radius, 6px);
202
+ }
203
+
204
+ .memori-artifact-preview-css-content {
205
+ padding: 1rem;
206
+ color: var(--memori-text-color, #333);
207
+ font-family: var(--memori-font-family);
208
+ line-height: 1.6;
209
+ }
210
+
211
+ .memori-artifact-preview-css-content p {
212
+ margin: 0 0 1rem;
213
+ }
214
+
215
+ .memori-artifact-preview-css-content button {
216
+ padding: 0.5rem 1rem;
217
+ border: none;
218
+ border-radius: var(--memori-border-radius, 4px);
219
+ margin: 0.5rem 0;
220
+ background: var(--memori-primary);
221
+ color: var(--memori-primary-text);
222
+ cursor: pointer;
223
+ }
224
+
225
+ .memori-artifact-preview-css-container {
226
+ padding: 1rem;
227
+ border-radius: var(--memori-border-radius, 4px);
228
+ margin: 1rem 0;
229
+ background: var(--memori-inner-bg, #f1f3f4);
230
+ }
231
+
232
+ /* Text Preview */
233
+ .memori-artifact-preview-text {
234
+ padding: 1rem;
235
+ color: var(--memori-text-color, #333);
236
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
237
+ font-size: 0.875rem;
238
+ line-height: 1.5;
239
+ white-space: pre-wrap;
240
+ word-wrap: break-word;
241
+ }
242
+
243
+ /* Syntax Highlighting */
244
+ .memori-artifact--comment {
245
+ color: #6a737d;
246
+ font-style: italic;
247
+ }
248
+
249
+ .memori-artifact--keyword {
250
+ color: #d73a49;
251
+ font-weight: 600;
252
+ }
253
+
254
+ .memori-artifact--string {
255
+ color: #032f62;
256
+ }
257
+
258
+ .memori-artifact--number {
259
+ color: #005cc5;
260
+ font-weight: 500;
261
+ }
262
+
263
+ .memori-artifact--function {
264
+ color: #6f42c1;
265
+ font-weight: 600;
266
+ }
267
+
268
+ .memori-artifact--operator {
269
+ color: #d73a49;
270
+ }
271
+
272
+ .memori-artifact--property {
273
+ color: #005cc5;
274
+ }
275
+
276
+ .memori-artifact--selector {
277
+ color: #6f42c1;
278
+ font-weight: 600;
279
+ }
280
+
281
+ .memori-artifact--tag {
282
+ color: #22863a;
283
+ font-weight: 600;
284
+ }
285
+
286
+ .memori-artifact--line-number {
287
+ margin-right: 1rem;
288
+ color: #6a737d;
289
+ user-select: none;
290
+ }
291
+
292
+ /* Responsive Design */
293
+ @media (max-width: 768px) {
294
+
295
+ .memori-artifact-tab {
296
+ min-height: 42px;
297
+ padding: 0.5rem 0.75rem;
298
+ font-size: 0.75rem;
299
+ }
300
+
301
+ .memori-artifact-code,
302
+ .memori-artifact-preview-content {
303
+ margin: 0.5rem;
304
+ }
305
+
306
+ .memori-artifact-code-content {
307
+ padding: 0.75rem;
308
+ font-size: 0.75rem;
309
+ }
310
+
311
+ .memori-artifact-preview-markdown,
312
+ .memori-artifact-preview-css,
313
+ .memori-artifact-preview-text {
314
+ padding: 0.75rem;
315
+ }
316
+ }
317
+
318
+ /* Dark Mode Support */
319
+ @media (prefers-color-scheme: dark) {
320
+ .memori-artifact-tabs {
321
+ border-bottom-color: var(--memori-button-border-color, #404040);
322
+ background: rgba(255, 255, 255, 0.86);
323
+ }
324
+
325
+ /* .memori-artifact-tab--active {
326
+ background: var(--memori-chat-bubble-bg, #1a1a1a);
327
+ } */
328
+
329
+ .memori-artifact-code,
330
+ .memori-artifact-preview-content {
331
+ border-color: var(--memori-button-border-color, #404040);
332
+ }
333
+
334
+ .memori-artifact-preview-markdown code,
335
+ .memori-artifact-preview-css-demo {
336
+ border-color: var(--memori-button-border-color, #404040);
337
+ background: var(--memori-inner-bg, #2a2a2a);
338
+ }
339
+
340
+ .memori-artifact-preview-css-container {
341
+ background: var(--memori-inner-bg, #2a2a2a);
342
+ }
343
+ }
@@ -0,0 +1,190 @@
1
+ /**
2
+ * ArtifactPreview Component
3
+ * Displays artifact content with tabs for code and preview
4
+ * Following the project's component patterns and design system
5
+ */
6
+
7
+ import React, { useState, useCallback, useMemo } from 'react';
8
+ import { useTranslation } from 'react-i18next';
9
+ import cx from 'classnames';
10
+ import Button from '../../../ui/Button';
11
+ import { ArtifactMimeType, ArtifactPreviewProps, ArtifactTab, SUPPORTED_MIME_TYPES } from '../../types/artifact.types';
12
+ import Code from '../../../icons/Code';
13
+ import { PreviewIcon } from '../../../icons/Preview';
14
+ import Snippet from '../../../Snippet/Snippet';
15
+ import { Medium } from '@memori.ai/memori-api-client/dist/types';
16
+
17
+ const ArtifactPreview: React.FC<ArtifactPreviewProps> = ({
18
+ artifact,
19
+ activeTab,
20
+ onTabChange,
21
+ }) => {
22
+ const { t } = useTranslation();
23
+
24
+ /**
25
+ * Handle tab switching
26
+ */
27
+ const handleTabChange = useCallback((tab: ArtifactTab) => {
28
+ onTabChange(tab);
29
+ }, [onTabChange]);
30
+ /**
31
+ * Render preview content based on MIME type
32
+ */
33
+ const renderPreview = useCallback(() => {
34
+ switch (artifact.mimeType) {
35
+ case 'html':
36
+ return (
37
+ <iframe
38
+ srcDoc={artifact.content}
39
+ style={{
40
+ width: '100%',
41
+ height: '100%',
42
+ border: 'none',
43
+ borderRadius: '6px',
44
+ }}
45
+ title={`${artifact.title} Preview`}
46
+ />
47
+ );
48
+
49
+ case 'markdown':
50
+ return (
51
+ <div
52
+ className="memori-artifact-preview-markdown"
53
+ dangerouslySetInnerHTML={{ __html: renderMarkdown(artifact.content) }}
54
+ />
55
+ );
56
+
57
+ case 'svg':
58
+ return (
59
+ <div
60
+ className="memori-artifact-preview-svg"
61
+ dangerouslySetInnerHTML={{ __html: artifact.content }}
62
+ />
63
+ );
64
+
65
+ case 'css':
66
+ return (
67
+ <div className="memori-artifact-preview-css">
68
+ <div className="memori-artifact-preview-css-demo">
69
+ <style dangerouslySetInnerHTML={{ __html: artifact.content }} />
70
+ <div className="memori-artifact-preview-css-content">
71
+ <h3>CSS Preview</h3>
72
+ <p>This is an example of text to see the CSS effect.</p>
73
+ <button>Example Button</button>
74
+ <div className="memori-artifact-preview-css-container">
75
+ Example Container
76
+ </div>
77
+ </div>
78
+ </div>
79
+ </div>
80
+ );
81
+
82
+ default:
83
+ return (
84
+ <div className="memori-artifact-preview-text">
85
+ <pre>{artifact.content}</pre>
86
+ </div>
87
+ );
88
+ }
89
+ }, [artifact]);
90
+
91
+ /**
92
+ * Basic markdown rendering
93
+ */
94
+ const renderMarkdown = useCallback((markdown: string): string => {
95
+ return markdown
96
+ .replace(/^# (.*$)/gim, '<h1>$1</h1>')
97
+ .replace(/^## (.*$)/gim, '<h2>$1</h2>')
98
+ .replace(/^### (.*$)/gim, '<h3>$1</h3>')
99
+ .replace(/^#### (.*$)/gim, '<h4>$1</h4>')
100
+ .replace(/^##### (.*$)/gim, '<h5>$1</h5>')
101
+ .replace(/^###### (.*$)/gim, '<h6>$1</h6>')
102
+ .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
103
+ .replace(/\*(.*?)\*/g, '<em>$1</em>')
104
+ .replace(/`(.*?)`/g, '<code>$1</code>')
105
+ .replace(/```([\s\S]*?)```/g, '<pre><code>$1</code></pre>')
106
+ .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>')
107
+ .replace(/\n/g, '<br>');
108
+ }, []);
109
+
110
+ /**
111
+ * Check if preview is available for this artifact type
112
+ */
113
+ const hasPreview = useMemo(() => {
114
+ return artifact.typeInfo.hasPreview;
115
+ }, [artifact.typeInfo.hasPreview]);
116
+
117
+ const mapArtifactMimeType = useCallback((mimeType: ArtifactMimeType) => {
118
+ return SUPPORTED_MIME_TYPES[mimeType as keyof typeof SUPPORTED_MIME_TYPES].mimeType;
119
+ }, []);
120
+
121
+ return (
122
+ <div className="memori-artifact-preview">
123
+ {/* Tabs */}
124
+ {hasPreview && (
125
+ <div className="memori-artifact-tabs">
126
+ <Button
127
+ onClick={() => handleTabChange('code')}
128
+ className={cx('memori-artifact-tab', {
129
+ 'memori-artifact-tab--active': activeTab === 'code',
130
+ })}
131
+ ghost
132
+ >
133
+ <Code className="memori-artifact-tab-icon" />
134
+ <span className="memori-artifact-tab-text">{t('artifact.code', 'Code')}</span>
135
+ </Button>
136
+ <Button
137
+ onClick={() => handleTabChange('preview')}
138
+ className={cx('memori-artifact-tab', {
139
+ 'memori-artifact-tab--active': activeTab === 'preview',
140
+ })}
141
+ ghost
142
+ >
143
+ <PreviewIcon className="memori-artifact-tab-icon" />
144
+ <span className="memori-artifact-tab-text">{t('artifact.preview', 'Preview')}</span>
145
+ </Button>
146
+ </div>
147
+ )}
148
+
149
+ {/* Content */}
150
+ <div className="memori-artifact-content">
151
+ {/* Code Tab */}
152
+ <div
153
+ className={cx('memori-artifact-tab-content', {
154
+ 'memori-artifact-tab-content--active': activeTab === 'code' || !hasPreview,
155
+ })}
156
+ >
157
+ <div className="memori-artifact-code">
158
+ <Snippet
159
+ // preview={true}
160
+ medium={{
161
+ mediumID: artifact.id,
162
+ mimeType: mapArtifactMimeType(artifact.mimeType as ArtifactMimeType),
163
+ content: artifact.content,
164
+ title: artifact.title,
165
+ creationTimestamp: artifact.timestamp.toISOString(),
166
+ creationName: 'System',
167
+ lastChangeTimestamp: artifact.timestamp.toISOString(),
168
+ lastChangeName: 'System',
169
+ } as Medium} />
170
+ </div>
171
+ </div>
172
+
173
+ {/* Preview Tab */}
174
+ {hasPreview && (
175
+ <div
176
+ className={cx('memori-artifact-tab-content', {
177
+ 'memori-artifact-tab-content--active': activeTab === 'preview',
178
+ })}
179
+ >
180
+ <div className="memori-artifact-preview-content">
181
+ {renderPreview()}
182
+ </div>
183
+ </div>
184
+ )}
185
+ </div>
186
+ </div>
187
+ );
188
+ };
189
+
190
+ export default ArtifactPreview;