@memori.ai/memori-react 8.4.1 → 8.5.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 (143) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/components/LoginDrawer/LoginDrawer.d.ts +1 -2
  3. package/dist/components/LoginDrawer/LoginDrawer.js +2 -105
  4. package/dist/components/LoginDrawer/LoginDrawer.js.map +1 -1
  5. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.css +0 -4
  6. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.d.ts +1 -0
  7. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.js +3 -3
  8. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.js.map +1 -1
  9. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyButtonWithDropdown.js +110 -8
  10. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyButtonWithDropdown.js.map +1 -1
  11. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyMenuItem.js +3 -0
  12. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyMenuItem.js.map +1 -1
  13. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/hooks/useCopyArtifact.js +42 -38
  14. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/hooks/useCopyArtifact.js.map +1 -1
  15. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/styles.css +1 -2
  16. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/types.d.ts +2 -1
  17. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.css +428 -15
  18. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.d.ts +1 -0
  19. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.js +47 -8
  20. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.js.map +1 -1
  21. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/components/TabSwitch.css +150 -0
  22. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/components/TabSwitch.d.ts +9 -0
  23. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/components/TabSwitch.js +35 -0
  24. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/components/TabSwitch.js.map +1 -0
  25. package/dist/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.css +2 -0
  26. package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.css +1 -1
  27. package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.d.ts +2 -1
  28. package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js +15 -26
  29. package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js.map +1 -1
  30. package/dist/components/MemoriArtifactSystem/context/ArtifactContext.js +1 -5
  31. package/dist/components/MemoriArtifactSystem/context/ArtifactContext.js.map +1 -1
  32. package/dist/components/MemoriWidget/MemoriWidget.js +1 -1
  33. package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
  34. package/dist/components/icons/Close.d.ts +2 -1
  35. package/dist/components/icons/Close.js +1 -1
  36. package/dist/components/icons/Close.js.map +1 -1
  37. package/dist/components/icons/MenuVertical.d.ts +7 -0
  38. package/dist/components/icons/MenuVertical.js +6 -0
  39. package/dist/components/icons/MenuVertical.js.map +1 -0
  40. package/dist/components/layouts/Chat.js +1 -1
  41. package/dist/components/layouts/Chat.js.map +1 -1
  42. package/dist/components/layouts/FullPage.js +1 -1
  43. package/dist/components/layouts/FullPage.js.map +1 -1
  44. package/dist/components/layouts/ZoomedFullBody.js +1 -1
  45. package/dist/components/layouts/ZoomedFullBody.js.map +1 -1
  46. package/dist/components/layouts/chat.css +73 -85
  47. package/dist/components/ui/Drawer.d.ts +1 -0
  48. package/dist/components/ui/Drawer.js +2 -2
  49. package/dist/components/ui/Drawer.js.map +1 -1
  50. package/dist/locales/de.json +37 -1
  51. package/dist/locales/en.json +37 -1
  52. package/dist/locales/es.json +37 -1
  53. package/dist/locales/fr.json +37 -1
  54. package/dist/locales/it.json +37 -1
  55. package/esm/components/LoginDrawer/LoginDrawer.d.ts +1 -2
  56. package/esm/components/LoginDrawer/LoginDrawer.js +3 -106
  57. package/esm/components/LoginDrawer/LoginDrawer.js.map +1 -1
  58. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.css +0 -4
  59. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.d.ts +1 -0
  60. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.js +3 -3
  61. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.js.map +1 -1
  62. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyButtonWithDropdown.js +110 -8
  63. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyButtonWithDropdown.js.map +1 -1
  64. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyMenuItem.js +3 -0
  65. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyMenuItem.js.map +1 -1
  66. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/hooks/useCopyArtifact.js +42 -38
  67. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/hooks/useCopyArtifact.js.map +1 -1
  68. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/styles.css +1 -2
  69. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/types.d.ts +2 -1
  70. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.css +428 -15
  71. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.d.ts +1 -0
  72. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.js +48 -9
  73. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.js.map +1 -1
  74. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/components/TabSwitch.css +150 -0
  75. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/components/TabSwitch.d.ts +9 -0
  76. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/components/TabSwitch.js +32 -0
  77. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/components/TabSwitch.js.map +1 -0
  78. package/esm/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.css +2 -0
  79. package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.css +1 -1
  80. package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.d.ts +2 -1
  81. package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js +16 -27
  82. package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js.map +1 -1
  83. package/esm/components/MemoriArtifactSystem/context/ArtifactContext.js +1 -5
  84. package/esm/components/MemoriArtifactSystem/context/ArtifactContext.js.map +1 -1
  85. package/esm/components/MemoriWidget/MemoriWidget.js +1 -1
  86. package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
  87. package/esm/components/icons/Close.d.ts +2 -1
  88. package/esm/components/icons/Close.js +1 -1
  89. package/esm/components/icons/Close.js.map +1 -1
  90. package/esm/components/icons/MenuVertical.d.ts +7 -0
  91. package/esm/components/icons/MenuVertical.js +4 -0
  92. package/esm/components/icons/MenuVertical.js.map +1 -0
  93. package/esm/components/layouts/Chat.js +1 -1
  94. package/esm/components/layouts/Chat.js.map +1 -1
  95. package/esm/components/layouts/FullPage.js +1 -1
  96. package/esm/components/layouts/FullPage.js.map +1 -1
  97. package/esm/components/layouts/ZoomedFullBody.js +1 -1
  98. package/esm/components/layouts/ZoomedFullBody.js.map +1 -1
  99. package/esm/components/layouts/chat.css +73 -85
  100. package/esm/components/ui/Drawer.d.ts +1 -0
  101. package/esm/components/ui/Drawer.js +2 -2
  102. package/esm/components/ui/Drawer.js.map +1 -1
  103. package/esm/locales/de.json +37 -1
  104. package/esm/locales/en.json +37 -1
  105. package/esm/locales/es.json +37 -1
  106. package/esm/locales/fr.json +37 -1
  107. package/esm/locales/it.json +37 -1
  108. package/package.json +1 -1
  109. package/src/components/FilePreview/__snapshots__/FilePreview.test.tsx.snap +9 -0
  110. package/src/components/LoginDrawer/LoginDrawer.tsx +46 -221
  111. package/src/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.css +0 -4
  112. package/src/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.tsx +7 -5
  113. package/src/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyButtonWithDropdown.tsx +148 -12
  114. package/src/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyMenuItem.tsx +3 -0
  115. package/src/components/MemoriArtifactSystem/components/ArtifactActions/hooks/useCopyArtifact.ts +54 -47
  116. package/src/components/MemoriArtifactSystem/components/ArtifactActions/styles.css +1 -2
  117. package/src/components/MemoriArtifactSystem/components/ArtifactActions/types.ts +2 -1
  118. package/src/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.css +428 -15
  119. package/src/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.tsx +240 -42
  120. package/src/components/MemoriArtifactSystem/components/ArtifactDrawer/components/TabSwitch.css +150 -0
  121. package/src/components/MemoriArtifactSystem/components/ArtifactDrawer/components/TabSwitch.tsx +79 -0
  122. package/src/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.css +2 -0
  123. package/src/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.css +1 -1
  124. package/src/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.tsx +2 -41
  125. package/src/components/MemoriArtifactSystem/context/ArtifactContext.tsx +1 -5
  126. package/src/components/MemoriWidget/MemoriWidget.tsx +0 -1
  127. package/src/components/icons/Close.tsx +8 -1
  128. package/src/components/icons/MenuVertical.tsx +29 -0
  129. package/src/components/layouts/Chat.tsx +3 -1
  130. package/src/components/layouts/FullPage.tsx +7 -2
  131. package/src/components/layouts/ZoomedFullBody.tsx +8 -3
  132. package/src/components/layouts/__snapshots__/Chat.test.tsx.snap +1 -1
  133. package/src/components/layouts/__snapshots__/FullPage.test.tsx.snap +112 -104
  134. package/src/components/layouts/__snapshots__/HiddenChat.test.tsx.snap +3 -0
  135. package/src/components/layouts/__snapshots__/ZoomedFullBody.test.tsx.snap +56 -52
  136. package/src/components/layouts/chat.css +73 -85
  137. package/src/components/ui/Drawer.tsx +3 -1
  138. package/src/components/ui/__snapshots__/Alert.test.tsx.snap +3 -0
  139. package/src/locales/de.json +37 -1
  140. package/src/locales/en.json +37 -1
  141. package/src/locales/es.json +37 -1
  142. package/src/locales/fr.json +37 -1
  143. package/src/locales/it.json +38 -1
@@ -6,7 +6,7 @@
6
6
 
7
7
  import React, { useState, useCallback, useMemo, useEffect } from 'react';
8
8
  import { useTranslation } from 'react-i18next';
9
- import { Dialog, Transition } from '@headlessui/react';
9
+ import { Dialog, Transition, Menu } from '@headlessui/react';
10
10
  import Button from '../../../ui/Button';
11
11
  import Close from '../../../icons/Close';
12
12
  import ArtifactActions from '../ArtifactActions/ArtifactActions';
@@ -17,6 +17,13 @@ import cx from 'classnames';
17
17
  import Drawer from '../../../ui/Drawer';
18
18
  import Fullscreen from '../../../icons/Fullscreen';
19
19
  import FullscreenExit from '../../../icons/FullscreenExit';
20
+ import MenuVertical from '../../../icons/MenuVertical';
21
+ import Download from '../../../icons/Download';
22
+ import Link from '../../../icons/Link';
23
+ import PrintIcon from '../../../icons/Print';
24
+ import { useCopyArtifact } from '../ArtifactActions/hooks/useCopyArtifact';
25
+ import TabSwitch from './components/TabSwitch';
26
+ import './components/TabSwitch.css';
20
27
 
21
28
  const ArtifactDrawer: React.FC<{ isChatLogPanel?: boolean }> = ({
22
29
  isChatLogPanel = false,
@@ -26,6 +33,44 @@ const ArtifactDrawer: React.FC<{ isChatLogPanel?: boolean }> = ({
26
33
  const { t } = useTranslation();
27
34
  const [showHistory, setShowHistory] = useState(false);
28
35
  const [isAnimating, setIsAnimating] = useState(false);
36
+ const [isMobile, setIsMobile] = useState(false);
37
+
38
+ const [activeTab, setActiveTab] = useState<ArtifactTab>('preview');
39
+
40
+ /**
41
+ * Handle tab switching
42
+ */
43
+ const handleTabChange = useCallback(
44
+ (tab: ArtifactTab) => {
45
+ setActiveTab(tab);
46
+ },
47
+ [activeTab]
48
+ );
49
+
50
+ // Use copy artifact hook for dynamic actions
51
+ const {
52
+ copyState,
53
+ formats,
54
+ handleCopy: handleCopyFormat,
55
+ handleCopyClick,
56
+ } = useCopyArtifact(
57
+ state.currentArtifact || { content: '', mimeType: 'text/plain' },
58
+ () => console.log('Copy completed'),
59
+ () => console.log('Download completed'),
60
+ () => console.log('Print completed')
61
+ );
62
+
63
+ // Mobile detection
64
+ useEffect(() => {
65
+ const checkMobile = () => {
66
+ setIsMobile(window.innerWidth <= 768);
67
+ };
68
+
69
+ checkMobile();
70
+ window.addEventListener('resize', checkMobile);
71
+
72
+ return () => window.removeEventListener('resize', checkMobile);
73
+ }, []);
29
74
 
30
75
  /**
31
76
  * Handle copy action
@@ -92,7 +137,7 @@ const ArtifactDrawer: React.FC<{ isChatLogPanel?: boolean }> = ({
92
137
  * Handle close with escape key
93
138
  */
94
139
  const handleClose = useCallback(() => {
95
- closeArtifact();
140
+ closeArtifact();
96
141
  }, [closeArtifact]);
97
142
 
98
143
  /**
@@ -102,10 +147,21 @@ const ArtifactDrawer: React.FC<{ isChatLogPanel?: boolean }> = ({
102
147
  setShowHistory(prev => !prev);
103
148
  }, []);
104
149
 
150
+ /**
151
+ * Mobile dropdown menu handlers
152
+ */
153
+ const handleMobileOpenExternal = useCallback(() => {
154
+ handleOpenExternal();
155
+ }, [handleOpenExternal]);
156
+
105
157
  if (!state.currentArtifact) {
106
158
  return null;
107
159
  }
108
160
 
161
+ const hasPreview =
162
+ state.currentArtifact.mimeType === 'html' ||
163
+ state.currentArtifact.mimeType === 'markdown';
164
+
109
165
  const ContentContainer = useCallback(
110
166
  ({ children }: { children: React.ReactNode }) => {
111
167
  if (isChatLogPanel) {
@@ -136,6 +192,7 @@ const ArtifactDrawer: React.FC<{ isChatLogPanel?: boolean }> = ({
136
192
  closable={false}
137
193
  animated={true}
138
194
  showBackdrop={false}
195
+ preventBackdropClose={true}
139
196
  confirmDialogTitle={
140
197
  t('artifact.confirmDialogTitle') ||
141
198
  'Are you sure you want to close this artifact?'
@@ -151,66 +208,207 @@ const ArtifactDrawer: React.FC<{ isChatLogPanel?: boolean }> = ({
151
208
  );
152
209
  }
153
210
  },
154
- [isChatLogPanel, handleClose, state.isDrawerOpen, state.isFullscreen, t]
211
+ [isChatLogPanel, handleClose, state.isDrawerOpen, state.isFullscreen]
155
212
  );
156
213
 
157
214
  // Render web split panel
158
215
  return (
159
216
  <ContentContainer>
160
217
  {/* Header */}
161
-
162
218
  <div className="memori-artifact-drawer-container-actions">
163
- {!isChatLogPanel && (
219
+ {/* Desktop Actions */}
220
+ {!isMobile && (
221
+ <>
222
+ {/* {!isChatLogPanel && (
223
+ <Button
224
+ onClick={handleToggleFullscreen}
225
+ className={cx(
226
+ 'memori-artifact-drawer-fullscreen',
227
+ 'memori-button--circle',
228
+ 'memori-button--icon-only'
229
+ )}
230
+ ghost
231
+ icon={
232
+ state.isFullscreen ? (
233
+ <Fullscreen className="memori-artifact-panel--close-icon" />
234
+ ) : (
235
+ <FullscreenExit className="memori-artifact-panel--close-icon" />
236
+ )
237
+ }
238
+ title={
239
+ state.isFullscreen
240
+ ? t('artifact.exitFullscreen') || 'Exit Fullscreen'
241
+ : t('artifact.fullscreen') || 'Fullscreen'
242
+ }
243
+ />
244
+ )} */}
245
+ {/* Modern Tab Switch */}
246
+ <TabSwitch
247
+ activeTab={activeTab}
248
+ onTabChange={handleTabChange}
249
+ hasPreview={hasPreview}
250
+ />
251
+ <ArtifactActions
252
+ artifact={state.currentArtifact}
253
+ onCopy={handleCopy}
254
+ onDownload={handleDownload}
255
+ loading={false}
256
+ onPrint={handlePrint}
257
+ onOpenExternal={handleOpenExternal}
258
+ isMobile={isMobile}
259
+ />
260
+ <Button
261
+ onClick={closeArtifact}
262
+ className={cx(
263
+ 'memori-artifact-drawer--close',
264
+ 'memori-button--icon-only'
265
+ )}
266
+ ghost
267
+ title={t('artifact.close') || 'Close'}
268
+ >
269
+ <Close className="memori-artifact-panel--close-icon" />
270
+ </Button>
271
+ </>
272
+ )}
273
+ </div>
274
+
275
+ {/* Top Right Header Section */}
276
+ <div className="memori-artifact-drawer-top-right">
277
+ {/* Mobile Dropdown Menu */}
278
+ {isMobile && (
279
+ <>
280
+ <TabSwitch
281
+ activeTab={activeTab}
282
+ onTabChange={handleTabChange}
283
+ hasPreview={hasPreview}
284
+ />
285
+ <Menu as="div" className="memori-mobile-actions-menu">
286
+ <Menu.Button as="div" className="memori-mobile-actions-trigger">
287
+ <Button
288
+ className={cx(
289
+ 'memori-button',
290
+ 'memori-button--more-options',
291
+ 'memori-button--icon-only'
292
+ )}
293
+ ghost
294
+ title={t('artifact.actions') || 'Actions'}
295
+ >
296
+ <MenuVertical className="memori-artifact-action-icon" />
297
+ </Button>
298
+ </Menu.Button>
299
+
300
+ <Transition
301
+ as={React.Fragment}
302
+ enter="memori-mobile-dropdown-enter"
303
+ enterFrom="memori-mobile-dropdown-enter-from"
304
+ enterTo="memori-mobile-dropdown-enter-to"
305
+ leave="memori-mobile-dropdown-leave"
306
+ leaveFrom="memori-mobile-dropdown-leave-from"
307
+ leaveTo="memori-mobile-dropdown-leave-to"
308
+ >
309
+ <Menu.Items className="memori-mobile-dropdown">
310
+ <div className="memori-mobile-dropdown-list">
311
+ <Button
312
+ onClick={handleCopy}
313
+ disabled={false}
314
+ className="memori-artifact-action-btn"
315
+ ghost
316
+ title={t('artifact.copy') || 'Copy'}
317
+ >
318
+ <span className="memori-artifact-action-text">
319
+ {t('artifact.copy') || 'Copy'}
320
+ </span>
321
+ </Button>
322
+
323
+ {formats.map(format => {
324
+ // Get appropriate icon based on action type
325
+ const getIcon = () => {
326
+ switch (format.action) {
327
+ case 'copy':
328
+ return (
329
+ <Link className="memori-artifact-action-icon" />
330
+ );
331
+ case 'download':
332
+ return (
333
+ <Download className="memori-artifact-action-icon" />
334
+ );
335
+ case 'print':
336
+ case 'pdf':
337
+ return (
338
+ <PrintIcon className="memori-artifact-action-icon" />
339
+ );
340
+ default:
341
+ return (
342
+ <Link className="memori-artifact-action-icon" />
343
+ );
344
+ }
345
+ };
346
+
347
+ return (
348
+ <Button
349
+ key={format.id}
350
+ onClick={() => handleCopyFormat(format)}
351
+ disabled={
352
+ copyState.loading &&
353
+ copyState.activeFormat === format.id
354
+ }
355
+ className="memori-artifact-action-btn"
356
+ ghost
357
+ icon={getIcon()}
358
+ title={format.label}
359
+ >
360
+ <span className="memori-artifact-action-text">
361
+ {format.label}
362
+ </span>
363
+ </Button>
364
+ );
365
+ })}
366
+
367
+ {/* External open action (not from hook) */}
368
+ <Button
369
+ onClick={handleMobileOpenExternal}
370
+ disabled={false}
371
+ className="memori-artifact-action-btn"
372
+ ghost
373
+ icon={<Link className="memori-artifact-action-icon" />}
374
+ title={t('artifact.external') || 'External'}
375
+ >
376
+ <span className="memori-artifact-action-text">
377
+ {t('artifact.external') || 'External'}
378
+ </span>
379
+ </Button>
380
+ </div>
381
+ </Menu.Items>
382
+ </Transition>
383
+ </Menu>
384
+ </>
385
+ )}
386
+
387
+ {/* Close Button */}
388
+ {isMobile && (
164
389
  <Button
165
- onClick={handleToggleFullscreen}
390
+ onClick={closeArtifact}
166
391
  className={cx(
167
- 'memori-artifact-drawer-fullscreen',
168
- 'memori-button--circle',
392
+ 'memori-artifact-drawer--close',
169
393
  'memori-button--icon-only'
170
394
  )}
171
395
  ghost
172
- icon={
173
- state.isFullscreen ? (
174
- <Fullscreen className="memori-artifact-panel--close-icon" />
175
- ) : (
176
- <FullscreenExit className="memori-artifact-panel--close-icon" />
177
- )
178
- }
179
- title={
180
- state.isFullscreen
181
- ? t('artifact.exitFullscreen') || 'Exit Fullscreen'
182
- : t('artifact.fullscreen') || 'Fullscreen'
183
- }
184
- />
396
+ title={t('artifact.close') || 'Close'}
397
+ >
398
+ <Close className="memori-artifact-panel--close-icon" />
399
+ </Button>
185
400
  )}
186
- <ArtifactActions
187
- artifact={state.currentArtifact}
188
- onCopy={handleCopy}
189
- onDownload={handleDownload}
190
- loading={false}
191
- onPrint={handlePrint}
192
- onOpenExternal={handleOpenExternal}
193
- />
194
401
  </div>
195
402
 
196
- <Button
197
- onClick={closeArtifact}
198
- className={cx(
199
- 'memori-artifact-drawer--close',
200
- 'memori-button--circle',
201
- 'memori-button--icon-only'
202
- )}
203
- ghost
204
- title={t('artifact.close') || 'Close'}
205
- >
206
- <Close className="memori-artifact-panel--close-icon" />
207
- </Button>
208
403
  <div className="memori-artifact-panel--header"></div>
209
404
 
210
405
  {/* Content */}
211
406
  <div className="memori-artifact-panel--content">
212
407
  <div className="memori-artifact-panel--main">
213
- <ArtifactPreview artifact={state.currentArtifact} />
408
+ <ArtifactPreview
409
+ artifact={state.currentArtifact}
410
+ activeTab={activeTab}
411
+ />
214
412
  </div>
215
413
  </div>
216
414
  </ContentContainer>
@@ -0,0 +1,150 @@
1
+ /**
2
+ * TabSwitch CSS Styles
3
+ * Modern animated switch component with smooth transitions
4
+ */
5
+
6
+ .memori-tab-switch {
7
+ display: flex;
8
+ align-items: center;
9
+ justify-content: center;
10
+ }
11
+
12
+ .memori-tab-switch__container {
13
+ position: relative;
14
+ display: inline-flex;
15
+ overflow: hidden;
16
+ border: 1px solid #e9ecef;
17
+ border-radius: 6px;
18
+ background: #f8f9fa;
19
+ box-shadow: none;
20
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
21
+ }
22
+
23
+ .memori-tab-switch__track {
24
+ position: relative;
25
+ display: flex;
26
+ overflow: hidden;
27
+ border-radius: 8px;
28
+ background: transparent;
29
+ }
30
+
31
+ .memori-tab-switch__indicator {
32
+ position: absolute;
33
+ z-index: 1;
34
+ top: 2px;
35
+ bottom: 2px;
36
+ left: 2px;
37
+ width: calc((100% - 4px) / var(--tab-count, 2));
38
+ border-radius: 6px;
39
+ animation: tab-switch-slide 0.3s cubic-bezier(0.4, 0, 0.2, 1);
40
+ background: #ffffff;
41
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
42
+ transform: translateX(calc(var(--active-index, 0) * 100%));
43
+ transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
44
+ }
45
+
46
+ .memori-tab-switch__indicator::before {
47
+ position: absolute;
48
+ top: 0;
49
+ right: 0;
50
+ bottom: 0;
51
+ left: 0;
52
+ border-radius: 6px;
53
+ background: linear-gradient(135deg, rgba(0, 0, 0, 0.02) 0%, rgba(0, 0, 0, 0) 100%);
54
+ content: '';
55
+ pointer-events: none;
56
+ }
57
+
58
+ .memori-tab-switch__button {
59
+ position: relative;
60
+ z-index: 2;
61
+ display: flex;
62
+ min-width: 70px;
63
+ height: 36px;
64
+ align-items: center;
65
+ justify-content: center;
66
+ padding: 6px 12px;
67
+ border: none;
68
+ border-radius: 6px;
69
+ background: transparent;
70
+ color: #6b7280;
71
+ cursor: pointer;
72
+ font-size: 0.875rem;
73
+ font-weight: 500;
74
+ gap: 6px;
75
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
76
+ white-space: nowrap;
77
+ }
78
+
79
+ .memori-tab-switch__button:hover {
80
+ color: #374151;
81
+ transform: none;
82
+ }
83
+
84
+ .memori-tab-switch__button--active {
85
+ /* color: #1´ç11827; */
86
+ font-weight: 600;
87
+ }
88
+
89
+ .memori-tab-switch__button:focus {
90
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
91
+ outline: none;
92
+ }
93
+
94
+ .memori-tab-switch__button:active {
95
+ transform: none;
96
+ }
97
+
98
+ .memori-tab-switch__icon {
99
+ width: 18px;
100
+ height: 18px;
101
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
102
+ }
103
+
104
+ .memori-tab-switch__button--active .memori-tab-switch__icon {
105
+ filter: none;
106
+ }
107
+
108
+ .memori-tab-switch__label {
109
+ font-size: 0.875rem;
110
+ font-weight: 500;
111
+ letter-spacing: 0.01em;
112
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
113
+ }
114
+
115
+ /* Hover effects for the entire switch */
116
+ .memori-tab-switch__container:hover {
117
+ border-color: #d1d5db;
118
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
119
+ }
120
+
121
+ .memori-tab-switch__container:hover .memori-tab-switch__indicator {
122
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
123
+ }
124
+
125
+ /* Animation for tab switching */
126
+ @keyframes tab-switch-slide {
127
+ from {
128
+ transform: translateX(calc((var(--active-index, 0) - 1) * 100%));
129
+ }
130
+ to {
131
+ transform: translateX(calc(var(--active-index, 0) * 100%));
132
+ }
133
+ }
134
+
135
+ /* Responsive design */
136
+
137
+ /* High contrast mode support */
138
+ @media (prefers-contrast: high) {
139
+ .memori-tab-switch__container {
140
+ border-width: 2px;
141
+ }
142
+
143
+ .memori-tab-switch__indicator {
144
+ box-shadow: 0 0 0 2px white, 0 2px 8px var(--memori-primary);
145
+ }
146
+
147
+ .memori-tab-switch__button:focus {
148
+ box-shadow: 0 0 0 4px var(--memori-primary);
149
+ }
150
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * TabSwitch Component
3
+ * Modern animated switch for toggling between code and preview tabs
4
+ */
5
+
6
+ import React from 'react';
7
+ import { ArtifactTab } from '../../../types/artifact.types';
8
+ import cx from 'classnames';
9
+ import Code from '../../../../icons/Code';
10
+ import { PreviewIcon } from '../../../../icons/Preview';
11
+
12
+ interface TabSwitchProps {
13
+ activeTab: ArtifactTab;
14
+ onTabChange: (tab: ArtifactTab) => void;
15
+ hasPreview: boolean;
16
+ }
17
+
18
+ const TabSwitch: React.FC<TabSwitchProps> = ({
19
+ activeTab,
20
+ onTabChange,
21
+ hasPreview,
22
+ }) => {
23
+ const tabs = [
24
+ {
25
+ id: 'code' as ArtifactTab,
26
+ icon: Code,
27
+ // label: 'Code',
28
+ },
29
+ ...(hasPreview
30
+ ? [
31
+ {
32
+ id: 'preview' as ArtifactTab,
33
+ icon: PreviewIcon,
34
+ // label: 'Preview',
35
+ },
36
+ ]
37
+ : []),
38
+ ];
39
+
40
+ return (
41
+ <div className="memori-tab-switch">
42
+ <div className="memori-tab-switch__container">
43
+ <div
44
+ className="memori-tab-switch__track"
45
+ style={{
46
+ '--tab-count': tabs.length,
47
+ } as React.CSSProperties}
48
+ >
49
+ <div
50
+ className="memori-tab-switch__indicator"
51
+ style={{
52
+ '--active-index': tabs.findIndex(tab => tab.id === activeTab),
53
+ } as React.CSSProperties}
54
+ />
55
+ {tabs.map((tab) => {
56
+ const IconComponent = tab.icon;
57
+ return (
58
+ <button
59
+ key={tab.id}
60
+ type="button"
61
+ className={cx('memori-tab-switch__button', {
62
+ 'memori-tab-switch__button--active': activeTab === tab.id,
63
+ })}
64
+ onClick={() => onTabChange(tab.id)}
65
+ aria-pressed={activeTab === tab.id}
66
+ // title={tab.label}
67
+ >
68
+ <IconComponent className="memori-tab-switch__icon" />
69
+ {/* <span className="memori-tab-switch__label">{tab.label}</span> */}
70
+ </button>
71
+ );
72
+ })}
73
+ </div>
74
+ </div>
75
+ </div>
76
+ );
77
+ };
78
+
79
+ export default TabSwitch;
@@ -8,9 +8,11 @@
8
8
  align-items: center;
9
9
  justify-content: space-between;
10
10
  padding: 12px;
11
+ padding: 1.5rem;
11
12
  border: 1px solid var(--memori-button-border-color, #e9ecef);
12
13
  border-radius: var(--memori-border-radius, 12px);
13
14
  margin: 0.75rem 0;
15
+ margin: 0.5rem;
14
16
  animation: fadeInUp 0.5s ease forwards;
15
17
  backdrop-filter: blur(4px);
16
18
  background: rgba(255, 255, 255, 0.95);
@@ -101,7 +101,7 @@
101
101
  /* Preview Content */
102
102
  .memori-artifact-preview-content {
103
103
  position: relative;
104
- overflow: auto;
104
+ overflow: hidden;
105
105
  flex: 1;
106
106
  /* border: 1px solid var(--memori-button-border-color, #e9ecef); */
107
107
  border-radius: var(--memori-border-radius, 6px);
@@ -16,19 +16,9 @@ import { Medium } from '@memori.ai/memori-api-client/dist/types';
16
16
 
17
17
  const ArtifactPreview: React.FC<{
18
18
  artifact: ArtifactData;
19
- }> = ({ artifact }) => {
19
+ activeTab: ArtifactTab;
20
+ }> = ({ artifact, activeTab }) => {
20
21
  const { t } = useTranslation();
21
- const [activeTab, setActiveTab] = useState<ArtifactTab>('preview');
22
-
23
- /**
24
- * Handle tab switching
25
- */
26
- const handleTabChange = useCallback(
27
- (tab: ArtifactTab) => {
28
- setActiveTab(tab);
29
- },
30
- [activeTab]
31
- );
32
22
 
33
23
  /**
34
24
  * Render preview content based on MIME type
@@ -124,35 +114,6 @@ const ArtifactPreview: React.FC<{
124
114
 
125
115
  return (
126
116
  <div className="memori-artifact-preview">
127
- {/* Tabs */}
128
- <div className="memori-artifact-tabs">
129
- <Button
130
- onClick={() => handleTabChange('code' as ArtifactTab)}
131
- className={cx('memori-artifact-tab', {
132
- 'memori-artifact-tab--active': activeTab === 'code',
133
- })}
134
- ghost
135
- >
136
- <Code className="memori-artifact-tab-icon" />
137
- <span className="memori-artifact-tab-text">
138
- {t('artifact.code') || 'Code'}
139
- </span>
140
- </Button>
141
- {hasPreview && (
142
- <Button
143
- onClick={() => handleTabChange('preview' as ArtifactTab)}
144
- className={cx('memori-artifact-tab', {
145
- 'memori-artifact-tab--active': activeTab === 'preview',
146
- })}
147
- ghost
148
- >
149
- <PreviewIcon className="memori-artifact-tab-icon" />
150
- <span className="memori-artifact-tab-text">
151
- {t('artifact.preview') || 'Preview'}
152
- </span>
153
- </Button>
154
- )}
155
- </div>
156
117
 
157
118
  {/* Content */}
158
119
  <div className="memori-artifact-content">
@@ -18,11 +18,7 @@ export const ArtifactProvider = ({ children }: { children: ReactNode }) => {
18
18
  });
19
19
 
20
20
  const openArtifact = useCallback((artifact: ArtifactData) => {
21
- setState(prev => {
22
- // Only update if the artifact is different
23
- if (prev.currentArtifact?.id === artifact.id && prev.isDrawerOpen) {
24
- return prev;
25
- }
21
+ setState(() => {
26
22
  return {
27
23
  currentArtifact: artifact,
28
24
  isDrawerOpen: true,
@@ -3379,7 +3379,6 @@ const MemoriWidget = ({
3379
3379
  tenant={tenant}
3380
3380
  apiClient={client}
3381
3381
  open={!!showLoginDrawer}
3382
- baseURL={baseUrl}
3383
3382
  user={user}
3384
3383
  loginToken={loginToken}
3385
3384
  onClose={() => setShowLoginDrawer(false)}