@memori.ai/memori-react 8.4.2 → 8.5.1

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 (132) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.css +0 -4
  3. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.d.ts +1 -0
  4. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.js +3 -3
  5. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.js.map +1 -1
  6. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyButtonWithDropdown.js +110 -8
  7. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyButtonWithDropdown.js.map +1 -1
  8. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyMenuItem.js +3 -0
  9. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyMenuItem.js.map +1 -1
  10. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/hooks/useCopyArtifact.js +42 -38
  11. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/hooks/useCopyArtifact.js.map +1 -1
  12. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/styles.css +1 -2
  13. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/types.d.ts +2 -1
  14. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.css +428 -15
  15. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.js +46 -8
  16. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.js.map +1 -1
  17. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/components/TabSwitch.css +150 -0
  18. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/components/TabSwitch.d.ts +9 -0
  19. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/components/TabSwitch.js +35 -0
  20. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/components/TabSwitch.js.map +1 -0
  21. package/dist/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.css +2 -0
  22. package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.css +1 -1
  23. package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.d.ts +2 -1
  24. package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js +15 -26
  25. package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js.map +1 -1
  26. package/dist/components/MemoriArtifactSystem/context/ArtifactContext.js +1 -5
  27. package/dist/components/MemoriArtifactSystem/context/ArtifactContext.js.map +1 -1
  28. package/dist/components/icons/Close.d.ts +2 -1
  29. package/dist/components/icons/Close.js +1 -1
  30. package/dist/components/icons/Close.js.map +1 -1
  31. package/dist/components/icons/MenuVertical.d.ts +7 -0
  32. package/dist/components/icons/MenuVertical.js +6 -0
  33. package/dist/components/icons/MenuVertical.js.map +1 -0
  34. package/dist/components/layouts/Chat.js +1 -1
  35. package/dist/components/layouts/Chat.js.map +1 -1
  36. package/dist/components/layouts/FullPage.js +1 -1
  37. package/dist/components/layouts/FullPage.js.map +1 -1
  38. package/dist/components/layouts/ZoomedFullBody.js +1 -1
  39. package/dist/components/layouts/ZoomedFullBody.js.map +1 -1
  40. package/dist/components/layouts/chat.css +67 -80
  41. package/dist/components/ui/Drawer.d.ts +1 -0
  42. package/dist/components/ui/Drawer.js +2 -2
  43. package/dist/components/ui/Drawer.js.map +1 -1
  44. package/dist/locales/de.json +37 -1
  45. package/dist/locales/en.json +37 -1
  46. package/dist/locales/es.json +37 -1
  47. package/dist/locales/fr.json +37 -1
  48. package/dist/locales/it.json +37 -1
  49. package/dist/styles.css +1 -0
  50. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.css +0 -4
  51. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.d.ts +1 -0
  52. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.js +3 -3
  53. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.js.map +1 -1
  54. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyButtonWithDropdown.js +110 -8
  55. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyButtonWithDropdown.js.map +1 -1
  56. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyMenuItem.js +3 -0
  57. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyMenuItem.js.map +1 -1
  58. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/hooks/useCopyArtifact.js +42 -38
  59. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/hooks/useCopyArtifact.js.map +1 -1
  60. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/styles.css +1 -2
  61. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/types.d.ts +2 -1
  62. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.css +428 -15
  63. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.js +47 -9
  64. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.js.map +1 -1
  65. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/components/TabSwitch.css +150 -0
  66. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/components/TabSwitch.d.ts +9 -0
  67. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/components/TabSwitch.js +32 -0
  68. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/components/TabSwitch.js.map +1 -0
  69. package/esm/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.css +2 -0
  70. package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.css +1 -1
  71. package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.d.ts +2 -1
  72. package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js +16 -27
  73. package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js.map +1 -1
  74. package/esm/components/MemoriArtifactSystem/context/ArtifactContext.js +1 -5
  75. package/esm/components/MemoriArtifactSystem/context/ArtifactContext.js.map +1 -1
  76. package/esm/components/icons/Close.d.ts +2 -1
  77. package/esm/components/icons/Close.js +1 -1
  78. package/esm/components/icons/Close.js.map +1 -1
  79. package/esm/components/icons/MenuVertical.d.ts +7 -0
  80. package/esm/components/icons/MenuVertical.js +4 -0
  81. package/esm/components/icons/MenuVertical.js.map +1 -0
  82. package/esm/components/layouts/Chat.js +1 -1
  83. package/esm/components/layouts/Chat.js.map +1 -1
  84. package/esm/components/layouts/FullPage.js +1 -1
  85. package/esm/components/layouts/FullPage.js.map +1 -1
  86. package/esm/components/layouts/ZoomedFullBody.js +1 -1
  87. package/esm/components/layouts/ZoomedFullBody.js.map +1 -1
  88. package/esm/components/layouts/chat.css +67 -80
  89. package/esm/components/ui/Drawer.d.ts +1 -0
  90. package/esm/components/ui/Drawer.js +2 -2
  91. package/esm/components/ui/Drawer.js.map +1 -1
  92. package/esm/locales/de.json +37 -1
  93. package/esm/locales/en.json +37 -1
  94. package/esm/locales/es.json +37 -1
  95. package/esm/locales/fr.json +37 -1
  96. package/esm/locales/it.json +37 -1
  97. package/esm/styles.css +1 -0
  98. package/package.json +1 -1
  99. package/src/components/FilePreview/__snapshots__/FilePreview.test.tsx.snap +9 -0
  100. package/src/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.css +0 -4
  101. package/src/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.tsx +7 -5
  102. package/src/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyButtonWithDropdown.tsx +148 -12
  103. package/src/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyMenuItem.tsx +3 -0
  104. package/src/components/MemoriArtifactSystem/components/ArtifactActions/hooks/useCopyArtifact.ts +54 -47
  105. package/src/components/MemoriArtifactSystem/components/ArtifactActions/styles.css +1 -2
  106. package/src/components/MemoriArtifactSystem/components/ArtifactActions/types.ts +2 -1
  107. package/src/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.css +428 -15
  108. package/src/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.tsx +239 -42
  109. package/src/components/MemoriArtifactSystem/components/ArtifactDrawer/components/TabSwitch.css +150 -0
  110. package/src/components/MemoriArtifactSystem/components/ArtifactDrawer/components/TabSwitch.tsx +79 -0
  111. package/src/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.css +2 -0
  112. package/src/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.css +1 -1
  113. package/src/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.tsx +2 -41
  114. package/src/components/MemoriArtifactSystem/context/ArtifactContext.tsx +1 -5
  115. package/src/components/icons/Close.tsx +8 -1
  116. package/src/components/icons/MenuVertical.tsx +29 -0
  117. package/src/components/layouts/Chat.tsx +3 -1
  118. package/src/components/layouts/FullPage.tsx +7 -2
  119. package/src/components/layouts/ZoomedFullBody.tsx +8 -3
  120. package/src/components/layouts/__snapshots__/Chat.test.tsx.snap +1 -1
  121. package/src/components/layouts/__snapshots__/FullPage.test.tsx.snap +112 -104
  122. package/src/components/layouts/__snapshots__/HiddenChat.test.tsx.snap +3 -0
  123. package/src/components/layouts/__snapshots__/ZoomedFullBody.test.tsx.snap +56 -52
  124. package/src/components/layouts/chat.css +67 -80
  125. package/src/components/ui/Drawer.tsx +3 -1
  126. package/src/components/ui/__snapshots__/Alert.test.tsx.snap +3 -0
  127. package/src/locales/de.json +37 -1
  128. package/src/locales/en.json +37 -1
  129. package/src/locales/es.json +37 -1
  130. package/src/locales/fr.json +37 -1
  131. package/src/locales/it.json +38 -1
  132. package/src/styles.css +1 -0
@@ -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,12 @@ 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';
20
26
 
21
27
  const ArtifactDrawer: React.FC<{ isChatLogPanel?: boolean }> = ({
22
28
  isChatLogPanel = false,
@@ -26,6 +32,44 @@ const ArtifactDrawer: React.FC<{ isChatLogPanel?: boolean }> = ({
26
32
  const { t } = useTranslation();
27
33
  const [showHistory, setShowHistory] = useState(false);
28
34
  const [isAnimating, setIsAnimating] = useState(false);
35
+ const [isMobile, setIsMobile] = useState(false);
36
+
37
+ const [activeTab, setActiveTab] = useState<ArtifactTab>('preview');
38
+
39
+ /**
40
+ * Handle tab switching
41
+ */
42
+ const handleTabChange = useCallback(
43
+ (tab: ArtifactTab) => {
44
+ setActiveTab(tab);
45
+ },
46
+ [activeTab]
47
+ );
48
+
49
+ // Use copy artifact hook for dynamic actions
50
+ const {
51
+ copyState,
52
+ formats,
53
+ handleCopy: handleCopyFormat,
54
+ handleCopyClick,
55
+ } = useCopyArtifact(
56
+ state.currentArtifact || { content: '', mimeType: 'text/plain' },
57
+ () => console.log('Copy completed'),
58
+ () => console.log('Download completed'),
59
+ () => console.log('Print completed')
60
+ );
61
+
62
+ // Mobile detection
63
+ useEffect(() => {
64
+ const checkMobile = () => {
65
+ setIsMobile(window.innerWidth <= 768);
66
+ };
67
+
68
+ checkMobile();
69
+ window.addEventListener('resize', checkMobile);
70
+
71
+ return () => window.removeEventListener('resize', checkMobile);
72
+ }, []);
29
73
 
30
74
  /**
31
75
  * Handle copy action
@@ -92,7 +136,7 @@ const ArtifactDrawer: React.FC<{ isChatLogPanel?: boolean }> = ({
92
136
  * Handle close with escape key
93
137
  */
94
138
  const handleClose = useCallback(() => {
95
- closeArtifact();
139
+ closeArtifact();
96
140
  }, [closeArtifact]);
97
141
 
98
142
  /**
@@ -102,10 +146,21 @@ const ArtifactDrawer: React.FC<{ isChatLogPanel?: boolean }> = ({
102
146
  setShowHistory(prev => !prev);
103
147
  }, []);
104
148
 
149
+ /**
150
+ * Mobile dropdown menu handlers
151
+ */
152
+ const handleMobileOpenExternal = useCallback(() => {
153
+ handleOpenExternal();
154
+ }, [handleOpenExternal]);
155
+
105
156
  if (!state.currentArtifact) {
106
157
  return null;
107
158
  }
108
159
 
160
+ const hasPreview =
161
+ state.currentArtifact.mimeType === 'html' ||
162
+ state.currentArtifact.mimeType === 'markdown';
163
+
109
164
  const ContentContainer = useCallback(
110
165
  ({ children }: { children: React.ReactNode }) => {
111
166
  if (isChatLogPanel) {
@@ -136,6 +191,7 @@ const ArtifactDrawer: React.FC<{ isChatLogPanel?: boolean }> = ({
136
191
  closable={false}
137
192
  animated={true}
138
193
  showBackdrop={false}
194
+ preventBackdropClose={true}
139
195
  confirmDialogTitle={
140
196
  t('artifact.confirmDialogTitle') ||
141
197
  'Are you sure you want to close this artifact?'
@@ -151,66 +207,207 @@ const ArtifactDrawer: React.FC<{ isChatLogPanel?: boolean }> = ({
151
207
  );
152
208
  }
153
209
  },
154
- [isChatLogPanel, handleClose, state.isDrawerOpen, state.isFullscreen, t]
210
+ [isChatLogPanel, handleClose, state.isDrawerOpen, state.isFullscreen]
155
211
  );
156
212
 
157
213
  // Render web split panel
158
214
  return (
159
215
  <ContentContainer>
160
216
  {/* Header */}
161
-
162
217
  <div className="memori-artifact-drawer-container-actions">
163
- {!isChatLogPanel && (
218
+ {/* Desktop Actions */}
219
+ {!isMobile && (
220
+ <>
221
+ {/* {!isChatLogPanel && (
222
+ <Button
223
+ onClick={handleToggleFullscreen}
224
+ className={cx(
225
+ 'memori-artifact-drawer-fullscreen',
226
+ 'memori-button--circle',
227
+ 'memori-button--icon-only'
228
+ )}
229
+ ghost
230
+ icon={
231
+ state.isFullscreen ? (
232
+ <Fullscreen className="memori-artifact-panel--close-icon" />
233
+ ) : (
234
+ <FullscreenExit className="memori-artifact-panel--close-icon" />
235
+ )
236
+ }
237
+ title={
238
+ state.isFullscreen
239
+ ? t('artifact.exitFullscreen') || 'Exit Fullscreen'
240
+ : t('artifact.fullscreen') || 'Fullscreen'
241
+ }
242
+ />
243
+ )} */}
244
+ {/* Modern Tab Switch */}
245
+ <TabSwitch
246
+ activeTab={activeTab}
247
+ onTabChange={handleTabChange}
248
+ hasPreview={hasPreview}
249
+ />
250
+ <ArtifactActions
251
+ artifact={state.currentArtifact}
252
+ onCopy={handleCopy}
253
+ onDownload={handleDownload}
254
+ loading={false}
255
+ onPrint={handlePrint}
256
+ onOpenExternal={handleOpenExternal}
257
+ isMobile={isMobile}
258
+ />
259
+ <Button
260
+ onClick={closeArtifact}
261
+ className={cx(
262
+ 'memori-artifact-drawer--close',
263
+ 'memori-button--icon-only'
264
+ )}
265
+ ghost
266
+ title={t('artifact.close') || 'Close'}
267
+ >
268
+ <Close className="memori-artifact-panel--close-icon" />
269
+ </Button>
270
+ </>
271
+ )}
272
+ </div>
273
+
274
+ {/* Top Right Header Section */}
275
+ <div className="memori-artifact-drawer-top-right">
276
+ {/* Mobile Dropdown Menu */}
277
+ {isMobile && (
278
+ <>
279
+ <TabSwitch
280
+ activeTab={activeTab}
281
+ onTabChange={handleTabChange}
282
+ hasPreview={hasPreview}
283
+ />
284
+ <Menu as="div" className="memori-mobile-actions-menu">
285
+ <Menu.Button as="div" className="memori-mobile-actions-trigger">
286
+ <Button
287
+ className={cx(
288
+ 'memori-button',
289
+ 'memori-button--more-options',
290
+ 'memori-button--icon-only'
291
+ )}
292
+ ghost
293
+ title={t('artifact.actions') || 'Actions'}
294
+ >
295
+ <MenuVertical className="memori-artifact-action-icon" />
296
+ </Button>
297
+ </Menu.Button>
298
+
299
+ <Transition
300
+ as={React.Fragment}
301
+ enter="memori-mobile-dropdown-enter"
302
+ enterFrom="memori-mobile-dropdown-enter-from"
303
+ enterTo="memori-mobile-dropdown-enter-to"
304
+ leave="memori-mobile-dropdown-leave"
305
+ leaveFrom="memori-mobile-dropdown-leave-from"
306
+ leaveTo="memori-mobile-dropdown-leave-to"
307
+ >
308
+ <Menu.Items className="memori-mobile-dropdown">
309
+ <div className="memori-mobile-dropdown-list">
310
+ <Button
311
+ onClick={handleCopy}
312
+ disabled={false}
313
+ className="memori-artifact-action-btn"
314
+ ghost
315
+ title={t('artifact.copy') || 'Copy'}
316
+ >
317
+ <span className="memori-artifact-action-text">
318
+ {t('artifact.copy') || 'Copy'}
319
+ </span>
320
+ </Button>
321
+
322
+ {formats.map(format => {
323
+ // Get appropriate icon based on action type
324
+ const getIcon = () => {
325
+ switch (format.action) {
326
+ case 'copy':
327
+ return (
328
+ <Link className="memori-artifact-action-icon" />
329
+ );
330
+ case 'download':
331
+ return (
332
+ <Download className="memori-artifact-action-icon" />
333
+ );
334
+ case 'print':
335
+ case 'pdf':
336
+ return (
337
+ <PrintIcon className="memori-artifact-action-icon" />
338
+ );
339
+ default:
340
+ return (
341
+ <Link className="memori-artifact-action-icon" />
342
+ );
343
+ }
344
+ };
345
+
346
+ return (
347
+ <Button
348
+ key={format.id}
349
+ onClick={() => handleCopyFormat(format)}
350
+ disabled={
351
+ copyState.loading &&
352
+ copyState.activeFormat === format.id
353
+ }
354
+ className="memori-artifact-action-btn"
355
+ ghost
356
+ icon={getIcon()}
357
+ title={format.label}
358
+ >
359
+ <span className="memori-artifact-action-text">
360
+ {format.label}
361
+ </span>
362
+ </Button>
363
+ );
364
+ })}
365
+
366
+ {/* External open action (not from hook) */}
367
+ <Button
368
+ onClick={handleMobileOpenExternal}
369
+ disabled={false}
370
+ className="memori-artifact-action-btn"
371
+ ghost
372
+ icon={<Link className="memori-artifact-action-icon" />}
373
+ title={t('artifact.external') || 'External'}
374
+ >
375
+ <span className="memori-artifact-action-text">
376
+ {t('artifact.external') || 'External'}
377
+ </span>
378
+ </Button>
379
+ </div>
380
+ </Menu.Items>
381
+ </Transition>
382
+ </Menu>
383
+ </>
384
+ )}
385
+
386
+ {/* Close Button */}
387
+ {isMobile && (
164
388
  <Button
165
- onClick={handleToggleFullscreen}
389
+ onClick={closeArtifact}
166
390
  className={cx(
167
- 'memori-artifact-drawer-fullscreen',
168
- 'memori-button--circle',
391
+ 'memori-artifact-drawer--close',
169
392
  'memori-button--icon-only'
170
393
  )}
171
394
  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
- />
395
+ title={t('artifact.close') || 'Close'}
396
+ >
397
+ <Close className="memori-artifact-panel--close-icon" />
398
+ </Button>
185
399
  )}
186
- <ArtifactActions
187
- artifact={state.currentArtifact}
188
- onCopy={handleCopy}
189
- onDownload={handleDownload}
190
- loading={false}
191
- onPrint={handlePrint}
192
- onOpenExternal={handleOpenExternal}
193
- />
194
400
  </div>
195
401
 
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
402
  <div className="memori-artifact-panel--header"></div>
209
403
 
210
404
  {/* Content */}
211
405
  <div className="memori-artifact-panel--content">
212
406
  <div className="memori-artifact-panel--main">
213
- <ArtifactPreview artifact={state.currentArtifact} />
407
+ <ArtifactPreview
408
+ artifact={state.currentArtifact}
409
+ activeTab={activeTab}
410
+ />
214
411
  </div>
215
412
  </div>
216
413
  </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,