@memori.ai/memori-react 8.8.1 → 8.8.2

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 (36) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/components/Header/Header.js +8 -8
  3. package/dist/components/Header/Header.js.map +1 -1
  4. package/dist/components/MediaWidget/MediaItemWidget.css +5 -0
  5. package/dist/components/MediaWidget/MediaItemWidget.d.ts +10 -0
  6. package/dist/components/MediaWidget/MediaItemWidget.js +49 -5
  7. package/dist/components/MediaWidget/MediaItemWidget.js.map +1 -1
  8. package/dist/components/MemoriWidget/MemoriWidget.js +12 -7
  9. package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
  10. package/dist/components/SettingsDrawer/SettingsDrawer.d.ts +1 -1
  11. package/dist/components/SettingsDrawer/SettingsDrawer.js +2 -12
  12. package/dist/components/SettingsDrawer/SettingsDrawer.js.map +1 -1
  13. package/dist/helpers/tts/useTTS.js +14 -6
  14. package/dist/helpers/tts/useTTS.js.map +1 -1
  15. package/esm/components/Header/Header.js +8 -8
  16. package/esm/components/Header/Header.js.map +1 -1
  17. package/esm/components/MediaWidget/MediaItemWidget.css +5 -0
  18. package/esm/components/MediaWidget/MediaItemWidget.d.ts +10 -0
  19. package/esm/components/MediaWidget/MediaItemWidget.js +47 -4
  20. package/esm/components/MediaWidget/MediaItemWidget.js.map +1 -1
  21. package/esm/components/MemoriWidget/MemoriWidget.js +12 -7
  22. package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
  23. package/esm/components/SettingsDrawer/SettingsDrawer.d.ts +1 -1
  24. package/esm/components/SettingsDrawer/SettingsDrawer.js +2 -12
  25. package/esm/components/SettingsDrawer/SettingsDrawer.js.map +1 -1
  26. package/esm/helpers/tts/useTTS.js +15 -7
  27. package/esm/helpers/tts/useTTS.js.map +1 -1
  28. package/package.json +1 -1
  29. package/src/components/Header/Header.tsx +10 -9
  30. package/src/components/MediaWidget/MediaItemWidget.css +5 -0
  31. package/src/components/MediaWidget/MediaItemWidget.stories.tsx +78 -0
  32. package/src/components/MediaWidget/MediaItemWidget.tsx +165 -35
  33. package/src/components/MediaWidget/__snapshots__/MediaItemWidget.test.tsx.snap +128 -102
  34. package/src/components/MemoriWidget/MemoriWidget.tsx +19 -7
  35. package/src/components/SettingsDrawer/SettingsDrawer.tsx +6 -6
  36. package/src/helpers/tts/useTTS.ts +22 -8
@@ -106,6 +106,84 @@ ImagesGrid.args = {
106
106
  })),
107
107
  };
108
108
 
109
+ export const JavaScript = Template.bind({});
110
+ JavaScript.args = {
111
+ items: [
112
+ {
113
+ mediumID: '65ca4a6d-f20b-402e-9d79-5e470f247928',
114
+ mimeType: 'text/javascript',
115
+ title: 'JavaScript',
116
+ content: `[
117
+ {
118
+ "name": "France",
119
+ "capital": "Paris",
120
+ "population": 67364357,
121
+ "area": 551695,
122
+ "currency": "Euro",
123
+ "languages": [
124
+ "French"
125
+ ],
126
+ "region": "Europe",
127
+ "subregion": "Western Europe",
128
+ "flag": "https://upload.wikimedia.org/wikipedia/commons/c/c3/Flag_of_France.svg"
129
+ },
130
+ {
131
+ "name": "Germany",
132
+ "capital": "Berlin",
133
+ "population": 83240525,
134
+ "area": 357022,
135
+ "currency": "Euro",
136
+ "languages": [
137
+ "German"
138
+ ],
139
+ "region": "Europe",
140
+ "subregion": "Western Europe",
141
+ "flag": "https://upload.wikimedia.org/wikipedia/commons/b/ba/Flag_of_Germany.svg"
142
+ },
143
+ {
144
+ "name": "United States",
145
+ "capital": "Washington, D.C.",
146
+ "population": 331893745,
147
+ "area": 9833517,
148
+ "currency": "USD",
149
+ "languages": [
150
+ "English"
151
+ ],
152
+ "region": "Americas",
153
+ "subregion": "Northern America",
154
+ "flag": "https://upload.wikimedia.org/wikipedia/commons/a/a4/Flag_of_the_United_States.svg"
155
+ },
156
+ {
157
+ "name": "Belgium",
158
+ "capital": "Brussels",
159
+ "population": 11589623,
160
+ "area": 30528,
161
+ "currency": "Euro",
162
+ "languages": [
163
+ "Flemish",
164
+ "French",
165
+ "German"
166
+ ],
167
+ "region": "Europe",
168
+ "subregion": "Western Europe",
169
+ "flag": "https://upload.wikimedia.org/wikipedia/commons/6/65/Flag_of_Belgium.svg"
170
+ ]`,
171
+ },
172
+ ],
173
+ };
174
+
175
+ export const longTXT = Template.bind({});
176
+ longTXT.args = {
177
+ items: [
178
+ {
179
+ mediumID: '65ca4a6d-f20b-402e-9d79-5e470f247928',
180
+ mimeType: 'text/plain',
181
+ title: 'Long Text',
182
+ content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi.',
183
+ },
184
+ ],
185
+ };
186
+
109
187
  export const WithCustomMediaRenderer = Template.bind({});
110
188
  WithCustomMediaRenderer.args = {
111
189
  items: [
@@ -65,6 +65,8 @@ export const RenderMediaItem = ({
65
65
  return customRenderer;
66
66
  }
67
67
 
68
+ const isCodeSnippet = prismSyntaxLangs.map(l => l.mimeType).includes(item.mimeType);
69
+
68
70
  const renderMediaContent = (item: Medium) => {
69
71
  switch (item.mimeType) {
70
72
  case 'image/jpeg':
@@ -160,6 +162,38 @@ export const RenderMediaItem = ({
160
162
  }
161
163
  };
162
164
 
165
+ // Handle code snippets with modal
166
+ if (isCodeSnippet && item.content) {
167
+ return (
168
+ <>
169
+ <a
170
+ className="memori-media-item--link"
171
+ href="#"
172
+ onClick={e => {
173
+ e.preventDefault();
174
+ setModalOpen(true);
175
+ }}
176
+ title={item.title}
177
+ >
178
+ <Card hoverable cover={renderMediaContent(item)} title={item.title} />
179
+ </a>
180
+
181
+ <Modal
182
+ open={modalOpen}
183
+ onClose={() => setModalOpen(false)}
184
+ title={item.title}
185
+ className="memori-media-item-preview--modal"
186
+ width="80%"
187
+ widthMd="90%"
188
+ >
189
+ <div className="memori-media-item-preview--content">
190
+ <Snippet medium={item} showCopyButton={true} />
191
+ </div>
192
+ </Modal>
193
+ </>
194
+ );
195
+ }
196
+
163
197
  if (!item.url && item?.type === 'document' && item.content) {
164
198
  return (
165
199
  <>
@@ -183,7 +217,13 @@ export const RenderMediaItem = ({
183
217
  width="60%"
184
218
  widthMd="70%"
185
219
  footer={
186
- <div style={{ display: 'flex', justifyContent: 'flex-end', gap: '0.5rem' }}>
220
+ <div
221
+ style={{
222
+ display: 'flex',
223
+ justifyContent: 'flex-end',
224
+ gap: '0.5rem',
225
+ }}
226
+ >
187
227
  <Button
188
228
  onClick={async () => {
189
229
  try {
@@ -328,6 +368,50 @@ export const RenderMediaItem = ({
328
368
  }
329
369
  };
330
370
 
371
+ export const RenderSnippetItem = ({
372
+ item,
373
+ sessionID: _sessionID,
374
+ tenantID: _tenantID,
375
+ baseURL: _baseURL,
376
+ apiURL: _apiURL,
377
+ onClick,
378
+ }: {
379
+ item: Medium & { type: string };
380
+ sessionID?: string;
381
+ tenantID?: string;
382
+ baseURL?: string;
383
+ apiURL?: string;
384
+ onClick?: (mediumID: string) => void;
385
+ }) => {
386
+ return (
387
+ <>
388
+ <a
389
+ href="#"
390
+ onClick={e => {
391
+ e.preventDefault();
392
+ if (onClick) {
393
+ console.log('Snippet item.mediumID:', item.mediumID);
394
+ onClick(item.mediumID);
395
+ console.log('clicked snippet');
396
+ }
397
+ }}
398
+ className="memori-media-item--link"
399
+ title={item.title}
400
+ >
401
+ <Card
402
+ hoverable
403
+ // onClick={() => setModalOpen(true)}
404
+ className="memori-media-item--card memori-media-item--snippet"
405
+ >
406
+ <div className="memori-media-item--snippet-preview">
407
+ <Snippet showCopyButton={false} preview={true} medium={item} />
408
+ </div>
409
+ </Card>
410
+ </a>
411
+ </>
412
+ );
413
+ };
414
+
331
415
  const MediaItemWidget: React.FC<Props> = ({
332
416
  items,
333
417
  sessionID,
@@ -391,13 +475,19 @@ const MediaItemWidget: React.FC<Props> = ({
391
475
  m => m.mimeType === 'text/css' && !!m.properties?.executable
392
476
  );
393
477
 
478
+ console.log('openModalMedium', openModalMedium);
479
+ console.log('codeSnippets', codeSnippets);
480
+ console.log('nonCodeDisplayMedia', nonCodeDisplayMedia);
481
+
394
482
  return (
395
483
  <Transition appear show as="div" className="memori-media-items">
396
484
  {!!nonCodeDisplayMedia.length && (
397
- <div className={cx('memori-media-items--grid', {
398
- 'memori-media-items--user': fromUser,
399
- 'memori-media-items--agent': !fromUser,
400
- })}>
485
+ <div
486
+ className={cx('memori-media-items--grid', {
487
+ 'memori-media-items--user': fromUser,
488
+ 'memori-media-items--agent': !fromUser,
489
+ })}
490
+ >
401
491
  {nonCodeDisplayMedia.map((item: Medium, index: number) => (
402
492
  <Transition.Child
403
493
  as="div"
@@ -417,7 +507,7 @@ const MediaItemWidget: React.FC<Props> = ({
417
507
  baseURL={baseURL}
418
508
  apiURL={apiURL}
419
509
  onClick={mediumID => {
420
- setOpenModalMedium(media.find(m => m.mediumID === mediumID));
510
+ setOpenModalMedium(nonCodeDisplayMedia.find(m => m.mediumID === mediumID));
421
511
  }}
422
512
  item={{
423
513
  ...item,
@@ -432,21 +522,48 @@ const MediaItemWidget: React.FC<Props> = ({
432
522
  ))}
433
523
  </div>
434
524
  )}
435
- {codeSnippets.map(medium => (
436
- <Transition.Child
437
- as="div"
438
- className="memori-media-item--snippet"
439
- key={medium.mediumID}
440
- enter="ease-out duration-500"
441
- enterFrom="opacity-0 translate-y-1"
442
- enterTo="opacity-1 translate-y-0"
443
- leave="ease-in duration-300"
444
- leaveFrom="opacity-1"
445
- leaveTo="opacity-0"
525
+ {!!codeSnippets.length && (
526
+ <div
527
+ className={cx('memori-media-items--grid', {
528
+ 'memori-media-items--user': fromUser,
529
+ 'memori-media-items--agent': !fromUser,
530
+ })}
446
531
  >
447
- <Snippet key={medium.mediumID} medium={medium} />
448
- </Transition.Child>
449
- ))}
532
+ {codeSnippets.map((item: Medium, index: number) => (
533
+ <Transition.Child
534
+ as="div"
535
+ className="memori-media-item"
536
+ key={item.mediumID + '&index=' + index}
537
+ enter={`ease-out duration-500 delay-${index * 100}`}
538
+ enterFrom="opacity-0 scale-95"
539
+ enterTo="opacity-1 scale-100"
540
+ leave="ease-in duration-300"
541
+ leaveFrom="opacity-1 scale-100"
542
+ leaveTo="opacity-0 scale-95"
543
+ >
544
+ <RenderSnippetItem
545
+ sessionID={sessionID}
546
+ tenantID={tenantID}
547
+ baseURL={baseURL}
548
+ apiURL={apiURL}
549
+ onClick={mediumID => {
550
+ console.log('Snippet clicked, mediumID:', mediumID);
551
+ const foundMedium = codeSnippets.find(m => m.mediumID === mediumID);
552
+ console.log('Found medium:', foundMedium);
553
+ setOpenModalMedium(foundMedium);
554
+ }}
555
+ item={{
556
+ ...item,
557
+ title: item.title,
558
+ url: item.url,
559
+ content: item.content,
560
+ type: 'document',
561
+ }}
562
+ />
563
+ </Transition.Child>
564
+ ))}
565
+ </div>
566
+ )}
450
567
  {cssExecutableCode.map(medium => (
451
568
  <style
452
569
  key={medium.mediumID}
@@ -454,7 +571,7 @@ const MediaItemWidget: React.FC<Props> = ({
454
571
  ></style>
455
572
  ))}
456
573
 
457
- {openModalMedium?.mediumID && (
574
+ {openModalMedium && (
458
575
  <Modal
459
576
  width="100%"
460
577
  widthMd="100%"
@@ -463,21 +580,34 @@ const MediaItemWidget: React.FC<Props> = ({
463
580
  onClose={() => setOpenModalMedium(undefined)}
464
581
  footer={null}
465
582
  >
466
- <RenderMediaItem
467
- isChild
468
- sessionID={sessionID}
469
- tenantID={tenantID}
470
- baseURL={baseURL}
471
- apiURL={apiURL}
472
- item={{
473
- ...openModalMedium,
474
- title: openModalMedium.title,
475
- url: openModalMedium.url,
476
- content: openModalMedium.content,
477
- type: 'document',
583
+ {prismSyntaxLangs
584
+ .map(l => l.mimeType)
585
+ .includes(openModalMedium.mimeType) ? (
586
+ <div
587
+ style={{
588
+ minHeight: '100%',
589
+ background: 'none',
478
590
  }}
479
- customMediaRenderer={customMediaRenderer}
480
- />
591
+ className="memori-media-item-preview--content">
592
+ <Snippet preview={false} medium={openModalMedium} />
593
+ </div>
594
+ ) : (
595
+ <RenderMediaItem
596
+ isChild
597
+ sessionID={sessionID}
598
+ tenantID={tenantID}
599
+ baseURL={baseURL}
600
+ apiURL={apiURL}
601
+ item={{
602
+ ...openModalMedium,
603
+ title: openModalMedium.title,
604
+ url: openModalMedium.url,
605
+ content: openModalMedium.content,
606
+ type: 'document',
607
+ }}
608
+ customMediaRenderer={customMediaRenderer}
609
+ />
610
+ )}
481
611
  </Modal>
482
612
  )}
483
613
  </Transition>
@@ -20,66 +20,79 @@ exports[`renders MediaItemWidget unchanged with css snippet to show 1`] = `
20
20
  class="memori-media-items"
21
21
  >
22
22
  <div
23
- class="memori-media-item--snippet ease-out duration-500 opacity-0 translate-y-1"
23
+ class="memori-media-items--grid memori-media-items--agent"
24
24
  >
25
25
  <div
26
- class="memori-snippet"
26
+ class="memori-media-item ease-out duration-500 delay-0 opacity-0 scale-95"
27
27
  >
28
- <div
29
- class="memori-snippet--content"
28
+ <a
29
+ class="memori-media-item--link"
30
+ href="#"
31
+ title="Snippet"
30
32
  >
31
- <pre
32
- aria-labelledby="#snippet-65ca4a6d-f20b-402e-9d79-5e470f247927"
33
- class="line-numbers"
33
+ <div
34
+ class="memori-card memori-media-item--card memori-media-item--snippet memori-card--hoverable"
34
35
  >
35
- <code
36
- class="language-scss"
36
+ <div
37
+ class="memori-spin"
37
38
  >
38
- body{
39
+ <div
40
+ class="memori-card--content"
41
+ >
42
+ <div
43
+ class="memori-card--children"
44
+ >
45
+ <div
46
+ class="memori-media-item--snippet-preview"
47
+ >
48
+ <div
49
+ class="memori-snippet"
50
+ >
51
+ <div
52
+ class="memori-snippet--content"
53
+ >
54
+ <pre
55
+ aria-labelledby="#snippet-65ca4a6d-f20b-402e-9d79-5e470f247927"
56
+ class="line-numbers"
57
+ >
58
+ <code
59
+ class="language-scss"
60
+ >
61
+ body{
39
62
  background-color: #f00;
40
63
  }
41
- </code>
42
- </pre>
43
- <button
44
- class="memori-button memori-button--ghost memori-button--rounded memori-button--icon-only memori-snippet--copy-button"
45
- title="copy"
46
- >
47
- <span
48
- class="memori-button--icon"
49
- >
50
- <svg
51
- aria-hidden="true"
52
- fill="none"
53
- focusable="false"
54
- role="img"
55
- stroke="currentColor"
56
- stroke-linecap="round"
57
- stroke-linejoin="round"
58
- stroke-width="1.5"
59
- viewBox="0 0 24 24"
60
- xmlns="http://www.w3.org/2000/svg"
64
+ </code>
65
+ </pre>
66
+ </div>
67
+ <p
68
+ class="memori-snippet--caption"
69
+ id="snippet-65ca4a6d-f20b-402e-9d79-5e470f247927"
70
+ >
71
+ Snippet
72
+ </p>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ </div>
77
+ <div
78
+ class="memori-spin--spinner"
61
79
  >
62
- <rect
63
- height="14"
64
- rx="2"
65
- ry="2"
66
- width="14"
67
- x="8"
68
- y="8"
69
- />
70
- <path
71
- d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"
72
- />
73
- </svg>
74
- </span>
75
- </button>
76
- </div>
77
- <p
78
- class="memori-snippet--caption"
79
- id="snippet-65ca4a6d-f20b-402e-9d79-5e470f247927"
80
- >
81
- Snippet
82
- </p>
80
+ <svg
81
+ aria-hidden="true"
82
+ class="memori-loading-icon"
83
+ focusable="false"
84
+ role="img"
85
+ viewBox="0 0 1024 1024"
86
+ xmlns="http://www.w3.org/2000/svg"
87
+ >
88
+ <path
89
+ d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 00-94.3-139.9 437.71 437.71 0 00-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"
90
+ />
91
+ </svg>
92
+ </div>
93
+ </div>
94
+ </div>
95
+ </a>
83
96
  </div>
84
97
  </div>
85
98
  </div>
@@ -262,64 +275,77 @@ exports[`renders MediaItemWidget unchanged with js snippet to show 1`] = `
262
275
  class="memori-media-items"
263
276
  >
264
277
  <div
265
- class="memori-media-item--snippet ease-out duration-500 opacity-0 translate-y-1"
278
+ class="memori-media-items--grid memori-media-items--agent"
266
279
  >
267
280
  <div
268
- class="memori-snippet"
281
+ class="memori-media-item ease-out duration-500 delay-0 opacity-0 scale-95"
269
282
  >
270
- <div
271
- class="memori-snippet--content"
283
+ <a
284
+ class="memori-media-item--link"
285
+ href="#"
286
+ title="Snippet"
272
287
  >
273
- <pre
274
- aria-labelledby="#snippet-a669fadb-12c0-469b-9b6c-34db22d371ca"
275
- class="line-numbers"
276
- >
277
- <code
278
- class="language-jsx"
279
- >
280
- console.log("Hello World!");
281
- </code>
282
- </pre>
283
- <button
284
- class="memori-button memori-button--ghost memori-button--rounded memori-button--icon-only memori-snippet--copy-button"
285
- title="copy"
288
+ <div
289
+ class="memori-card memori-media-item--card memori-media-item--snippet memori-card--hoverable"
286
290
  >
287
- <span
288
- class="memori-button--icon"
291
+ <div
292
+ class="memori-spin"
289
293
  >
290
- <svg
291
- aria-hidden="true"
292
- fill="none"
293
- focusable="false"
294
- role="img"
295
- stroke="currentColor"
296
- stroke-linecap="round"
297
- stroke-linejoin="round"
298
- stroke-width="1.5"
299
- viewBox="0 0 24 24"
300
- xmlns="http://www.w3.org/2000/svg"
294
+ <div
295
+ class="memori-card--content"
301
296
  >
302
- <rect
303
- height="14"
304
- rx="2"
305
- ry="2"
306
- width="14"
307
- x="8"
308
- y="8"
309
- />
310
- <path
311
- d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"
312
- />
313
- </svg>
314
- </span>
315
- </button>
316
- </div>
317
- <p
318
- class="memori-snippet--caption"
319
- id="snippet-a669fadb-12c0-469b-9b6c-34db22d371ca"
320
- >
321
- Snippet
322
- </p>
297
+ <div
298
+ class="memori-card--children"
299
+ >
300
+ <div
301
+ class="memori-media-item--snippet-preview"
302
+ >
303
+ <div
304
+ class="memori-snippet"
305
+ >
306
+ <div
307
+ class="memori-snippet--content"
308
+ >
309
+ <pre
310
+ aria-labelledby="#snippet-a669fadb-12c0-469b-9b6c-34db22d371ca"
311
+ class="line-numbers"
312
+ >
313
+ <code
314
+ class="language-jsx"
315
+ >
316
+ console.log("Hello World!");
317
+ </code>
318
+ </pre>
319
+ </div>
320
+ <p
321
+ class="memori-snippet--caption"
322
+ id="snippet-a669fadb-12c0-469b-9b6c-34db22d371ca"
323
+ >
324
+ Snippet
325
+ </p>
326
+ </div>
327
+ </div>
328
+ </div>
329
+ </div>
330
+ <div
331
+ class="memori-spin--spinner"
332
+ >
333
+ <svg
334
+ aria-hidden="true"
335
+ class="memori-loading-icon"
336
+ focusable="false"
337
+ role="img"
338
+ viewBox="0 0 1024 1024"
339
+ xmlns="http://www.w3.org/2000/svg"
340
+ >
341
+ <path
342
+ d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 00-94.3-139.9 437.71 437.71 0 00-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"
343
+ />
344
+ </svg>
345
+ </div>
346
+ </div>
347
+ </div>
348
+ </a>
323
349
  </div>
324
350
  </div>
325
351
  </div>
@@ -1799,7 +1799,7 @@ const MemoriWidget = ({
1799
1799
  voiceType: memori.voiceType,
1800
1800
  layout: selectedLayout,
1801
1801
  }),
1802
- [ttsProvider, userLang, memori.culture, memori.voiceType]
1802
+ [ttsProvider, userLang, memori.culture, memori.voiceType, selectedLayout]
1803
1803
  );
1804
1804
 
1805
1805
  const sttConfig = useMemo(
@@ -2802,7 +2802,13 @@ const MemoriWidget = ({
2802
2802
  mute = true;
2803
2803
  }
2804
2804
 
2805
+ // Use the toggleMute function from useTTS hook which properly manages state
2805
2806
  toggleMute(mute);
2807
+
2808
+ // Update local config for persistence
2809
+ setLocalConfig('muteSpeaker', !!mute);
2810
+
2811
+ // Handle microphone mode changes
2806
2812
  let microphoneMode = getLocalConfig<string>(
2807
2813
  'microphoneMode',
2808
2814
  'HOLD_TO_TALK'
@@ -2811,15 +2817,21 @@ const MemoriWidget = ({
2811
2817
  setContinuousSpeech(false);
2812
2818
  setLocalConfig('microphoneMode', 'HOLD_TO_TALK');
2813
2819
  }
2814
- setLocalConfig('muteSpeaker', !!mute);
2820
+
2821
+ // Stop audio if muting
2815
2822
  if (mute) {
2816
2823
  ttsStop();
2817
2824
  } else {
2818
- audioContext = new AudioContext();
2819
- let buffer = audioContext.createBuffer(1, 10000, 22050);
2820
- let source = audioContext.createBufferSource();
2821
- source.buffer = buffer;
2822
- source.connect(audioContext.destination);
2825
+ // Initialize audio context when unmuting
2826
+ try {
2827
+ audioContext = new AudioContext();
2828
+ let buffer = audioContext.createBuffer(1, 10000, 22050);
2829
+ let source = audioContext.createBufferSource();
2830
+ source.buffer = buffer;
2831
+ source.connect(audioContext.destination);
2832
+ } catch (error) {
2833
+ console.warn('Failed to initialize audio context:', error);
2834
+ }
2823
2835
  }
2824
2836
  },
2825
2837
  setShowChatHistoryDrawer,