@websolutespa/bom-mixer-llm 0.0.1 → 0.0.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 (37) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/cjs/index.css +65 -15
  3. package/dist/cjs/index.js +124 -55
  4. package/dist/cjs/index.js.map +1 -1
  5. package/dist/esm/index.css +65 -15
  6. package/dist/esm/llm/components/Main/main.js +3 -12
  7. package/dist/esm/llm/components/Main/main.js.map +1 -1
  8. package/dist/esm/llm/components/Prompt/prompt.js +28 -4
  9. package/dist/esm/llm/components/Prompt/prompt.js.map +1 -1
  10. package/dist/esm/llm/components/Text/text.js +14 -1
  11. package/dist/esm/llm/components/Text/text.js.map +1 -1
  12. package/dist/esm/useLlm/mock.js +42 -33
  13. package/dist/esm/useLlm/mock.js.map +1 -1
  14. package/dist/esm/useLlm/speech.js +23 -3
  15. package/dist/esm/useLlm/speech.js.map +1 -1
  16. package/dist/esm/useLlm/useLlm.js +15 -3
  17. package/dist/esm/useLlm/useLlm.js.map +1 -1
  18. package/dist/types/llm/components/Main/main.d.ts.map +1 -1
  19. package/dist/types/llm/components/Prompt/prompt.d.ts.map +1 -1
  20. package/dist/types/llm/components/Text/text.d.ts.map +1 -1
  21. package/dist/types/types.d.ts +1 -0
  22. package/dist/types/types.d.ts.map +1 -1
  23. package/dist/types/useLlm/mock.d.ts.map +1 -1
  24. package/dist/types/useLlm/speech.d.ts +1 -0
  25. package/dist/types/useLlm/speech.d.ts.map +1 -1
  26. package/dist/types/useLlm/useLlm.d.ts +1 -0
  27. package/dist/types/useLlm/useLlm.d.ts.map +1 -1
  28. package/package.json +1 -1
  29. package/src/llm/components/Main/main.tsx +2 -7
  30. package/src/llm/components/Prompt/_style.scss +48 -8
  31. package/src/llm/components/Prompt/prompt.tsx +14 -3
  32. package/src/llm/components/Text/_style.scss +49 -24
  33. package/src/llm/components/Text/text.tsx +14 -1
  34. package/src/types.ts +1 -0
  35. package/src/useLlm/mock.ts +46 -35
  36. package/src/useLlm/speech.ts +23 -3
  37. package/src/useLlm/useLlm.tsx +15 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @websolutespa/bom-mixer-llm
2
2
 
3
+ ## 0.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Modified: Speech synthesis & recognition, disclaimer, markdown
8
+ - Updated dependencies
9
+ - @websolutespa/bom-mixer-icons@0.2.2
10
+
3
11
  ## 0.0.1
4
12
 
5
13
  ### Patch Changes
@@ -1243,7 +1243,7 @@
1243
1243
  outline: none;
1244
1244
  border: none;
1245
1245
  margin: 0;
1246
- padding: 10px clamp(16px, 16px + (100vw - 375px) / 1545 * (8), 24px);
1246
+ padding: 10px 40px;
1247
1247
  resize: none;
1248
1248
  max-height: 120px;
1249
1249
  min-height: 45px;
@@ -1254,25 +1254,39 @@
1254
1254
  }
1255
1255
  @media (min-width: 1024px) {
1256
1256
  .llm .llm__prompt-textarea {
1257
- padding: 18px clamp(16px, 16px + (100vw - 375px) / 1545 * (8), 24px);
1257
+ padding: 18px 40px;
1258
1258
  min-height: 60px;
1259
1259
  max-height: 220px;
1260
1260
  }
1261
1261
  }
1262
- .llm .llm__prompt-microphone {
1262
+ .llm .llm__prompt-speak, .llm .llm__prompt-microphone {
1263
1263
  position: absolute;
1264
- right: 12px;
1265
- bottom: 5px;
1266
1264
  color: var(--llm-color-ui-1);
1267
1265
  font-size: 16px;
1268
- width: 24px;
1269
- height: 24px;
1266
+ width: 32px;
1267
+ height: 32px;
1268
+ top: 50%;
1269
+ transform: translateY(-50%);
1270
1270
  display: flex;
1271
1271
  align-items: center;
1272
1272
  justify-content: center;
1273
1273
  border-radius: 50%;
1274
1274
  cursor: pointer;
1275
1275
  }
1276
+ .llm .llm__prompt-speak svg, .llm .llm__prompt-microphone svg {
1277
+ width: 18px;
1278
+ height: 18px;
1279
+ }
1280
+ .llm .llm__prompt-speak {
1281
+ left: 8px;
1282
+ }
1283
+ .llm .llm__prompt-microphone {
1284
+ right: 8px;
1285
+ outline: 2px solid transparent;
1286
+ }
1287
+ .llm .llm__prompt-microphone:active {
1288
+ outline: 2px solid var(--llm-color-ui-1);
1289
+ }
1276
1290
  .llm .llm__prompt-submit, .llm .llm__prompt-stop {
1277
1291
  flex-shrink: 0;
1278
1292
  width: 45px;
@@ -1346,6 +1360,24 @@
1346
1360
  color: white;
1347
1361
  cursor: pointer;
1348
1362
  }
1363
+ .llm .llm__prompt-powered-by {
1364
+ position: absolute;
1365
+ display: flex;
1366
+ justify-content: center;
1367
+ align-items: center;
1368
+ column-gap: 4px;
1369
+ right: 0;
1370
+ left: 0;
1371
+ font-size: 12px;
1372
+ color: white;
1373
+ padding: 6px 0;
1374
+ opacity: 0.4;
1375
+ transition: opacity ease-in-out 250ms;
1376
+ cursor: pointer;
1377
+ }
1378
+ .llm .llm__prompt-powered-by:hover {
1379
+ opacity: 1;
1380
+ }
1349
1381
 
1350
1382
  .llm .llm__message--user .llm__text .llm__text-body {
1351
1383
  line-height: 1;
@@ -1375,7 +1407,7 @@
1375
1407
  letter-spacing: 0.02em;
1376
1408
  font-size: clamp(16px, 16px + (100vw - 375px) / 1545 * (16), 32px);
1377
1409
  }
1378
- .llm .llm__message--assistant .llm__inner--string:not(:first-child) .llm__text .llm__text-body h1 {
1410
+ .llm .llm__message--assistant .llm__inner--string .llm__text .llm__text-body h1 {
1379
1411
  line-height: 1;
1380
1412
  font-family: var(--llm-font-primary);
1381
1413
  font-weight: 500;
@@ -1383,7 +1415,7 @@
1383
1415
  letter-spacing: 0.02em;
1384
1416
  font-size: clamp(24px, 24px + (100vw - 375px) / 1545 * (32), 56px);
1385
1417
  }
1386
- .llm .llm__message--assistant .llm__inner--string:not(:first-child) .llm__text .llm__text-body h2 {
1418
+ .llm .llm__message--assistant .llm__inner--string .llm__text .llm__text-body h2 {
1387
1419
  line-height: 1;
1388
1420
  font-family: var(--llm-font-primary);
1389
1421
  font-weight: 500;
@@ -1391,7 +1423,7 @@
1391
1423
  letter-spacing: 0.02em;
1392
1424
  font-size: clamp(24px, 24px + (100vw - 375px) / 1545 * (24), 48px);
1393
1425
  }
1394
- .llm .llm__message--assistant .llm__inner--string:not(:first-child) .llm__text .llm__text-body h3 {
1426
+ .llm .llm__message--assistant .llm__inner--string .llm__text .llm__text-body h3 {
1395
1427
  line-height: 1;
1396
1428
  font-family: var(--llm-font-primary);
1397
1429
  font-weight: 500;
@@ -1399,7 +1431,7 @@
1399
1431
  letter-spacing: 0.02em;
1400
1432
  font-size: clamp(24px, 24px + (100vw - 375px) / 1545 * (16), 40px);
1401
1433
  }
1402
- .llm .llm__message--assistant .llm__inner--string:not(:first-child) .llm__text .llm__text-body h4 {
1434
+ .llm .llm__message--assistant .llm__inner--string .llm__text .llm__text-body h4 {
1403
1435
  line-height: 1;
1404
1436
  font-family: var(--llm-font-primary);
1405
1437
  font-weight: 500;
@@ -1407,19 +1439,37 @@
1407
1439
  letter-spacing: 0.02em;
1408
1440
  font-size: clamp(16px, 16px + (100vw - 375px) / 1545 * (16), 32px);
1409
1441
  }
1410
- .llm .llm__message--assistant .llm__inner--string:not(:first-child) .llm__text .llm__text-body ol,
1411
- .llm .llm__message--assistant .llm__inner--string:not(:first-child) .llm__text .llm__text-body ul {
1442
+ .llm .llm__message--assistant .llm__inner--string .llm__text .llm__text-body ol,
1443
+ .llm .llm__message--assistant .llm__inner--string .llm__text .llm__text-body ul {
1412
1444
  display: flex;
1413
1445
  flex-direction: column;
1414
1446
  gap: clamp(24px, 24px + (100vw - 375px) / 1545 * (8), 32px);
1415
1447
  margin-left: 1em;
1416
1448
  }
1417
- .llm .llm__message--assistant .llm__inner--string:not(:first-child) .llm__text .llm__text-body ul {
1449
+ .llm .llm__message--assistant .llm__inner--string .llm__text .llm__text-body ul {
1418
1450
  list-style: disc;
1419
1451
  }
1420
- .llm .llm__message--assistant .llm__inner--string:not(:first-child) .llm__text .llm__text-body a {
1452
+ .llm .llm__message--assistant .llm__inner--string .llm__text .llm__text-body a {
1421
1453
  text-decoration: underline;
1422
1454
  }
1455
+ .llm .llm__message--assistant .llm__inner--string .llm__text .llm__text-body img {
1456
+ max-width: 320px;
1457
+ padding: 8px;
1458
+ border-radius: 8px;
1459
+ border: 2px solid rgba(255, 255, 255, 0.4);
1460
+ }
1461
+ .llm .llm__message--assistant .llm__inner--string .llm__text .llm__text-body .llm__text-img {
1462
+ display: inline-flex;
1463
+ justify-content: center;
1464
+ align-items: center;
1465
+ width: 320px;
1466
+ height: 320px;
1467
+ font-size: 0;
1468
+ text-decoration: none;
1469
+ }
1470
+ .llm .llm__message--assistant .llm__inner--string .llm__text .llm__text-body .llm__text-img > img {
1471
+ object-fit: cover;
1472
+ }
1423
1473
  .llm .llm__text-disclaimer {
1424
1474
  line-height: 1;
1425
1475
  font-family: var(--llm-font-secondary);
package/dist/cjs/index.js CHANGED
@@ -8,9 +8,9 @@ var react = require('react');
8
8
  var zustand = require('zustand');
9
9
  var middleware = require('zustand/middleware');
10
10
  var reactLenis = require('@studio-freight/react-lenis');
11
- var bomMixerIcons = require('@websolutespa/bom-mixer-icons');
12
11
  var ArrayBuffer = require('nanogl/arraybuffer');
13
12
  var Program = require('nanogl/program');
13
+ var bomMixerIcons = require('@websolutespa/bom-mixer-icons');
14
14
  require('swiper/css');
15
15
  var react$1 = require('swiper/react');
16
16
  var swiper = require('swiper');
@@ -501,6 +501,7 @@ const APP = {
501
501
  collapsedWelcomeTextHover: 'Cliccami e lascia che ti consigli cosa fare e vedere a Pesaro 2024.',
502
502
  shortWelcomeText: 'Ciao, sono l\'ambasciatore AI di Pesaro2024.',
503
503
  extendedWelcomeText: 'Ciao, sono l\'Ambasciatore AI di Pesaro2024. Più cose mi dirai su ciò che ti piace, più sarò in grado di aiutarti.',
504
+ disclaimer: 'Le risposte che riceverai sono create automaticamente elaborando sia le informazioni presenti sul nostro sito che altre informazioni presenti sul web. Controlla sempre la correttezza delle informazioni fornite.',
504
505
  sampleInputTexts: [{
505
506
  sampleInputText: 'Quali sono gli eventi che posso raggiungere a piedi dalla stazione dei treni?',
506
507
  id: '656095350d5d91982799c3fd'
@@ -626,6 +627,7 @@ const APP = {
626
627
  collapsedWelcomeTextHover: 'Click me and let me advise you on what to do and see in Pesaro 2024.',
627
628
  shortWelcomeText: 'Hi, I\'m the AI ​​ambassador of Pesaro2024.',
628
629
  extendedWelcomeText: 'Hi, I\'m the AI ​​Ambassador of Pesaro2024. The more you tell me about what you like, the more I will be able to help you.',
630
+ disclaimer: 'The answers you will receive are created automatically by processing both the information on our site and other information on the web. Always check the correctness of the information provided.',
629
631
  sampleInputTexts: [{
630
632
  sampleInputText: 'What events can I walk to from the train station?',
631
633
  id: '656095350d5d91982799c3fd'
@@ -717,44 +719,46 @@ const THREAD = {
717
719
  /*
718
720
  {
719
721
  chunks: [
720
- 'Se è la prima volta che vieni a Pesaro, ti consiglio di non perderti queste tappe!\n',
721
- {
722
- type: 'poiItem' as const,
723
- id: '310',
724
- title: 'Sfera Grande di A. Pomodoro',
725
- abstract: 'Scultura sferica bronzea, soprannominata "Il pomodoro", in piscina in un parco sul Mar Adriatico.',
726
- place: 'Piazzale della Libertà, 61121 Pesaro PU',
727
- url: 'https://design.wslabs.it/llm',
728
- image: 'https://design.wslabs.it/llm/media/casa-rossini.jpg',
729
- time: 'Aperto 24 su 24',
730
- },
731
- {
732
- type: 'poiItem' as const,
733
- id: 'xxxx',
734
- title: 'Baia Flaminia',
735
- abstract: 'Nella spiaggia libera di Baia Flaminia è presente una rara oasi naturalistica urbana, che si trova fra il fiume Foglia e il Parco San Bartolo nel quartiere.',
736
- place: '61121 Pesaro PU',
737
- url: 'https://design.wslabs.it/llm',
738
- image: 'https://design.wslabs.it/llm/media/casa-rossini.jpg',
739
- time: 'Aperto 24 su 24',
740
- },
741
- {
742
- type: 'poiItem' as const,
743
- id: '305',
744
- title: 'Parco Miralfiore',
745
- abstract: 'Il Parco Urbano Miralfiore si trova proprio nel cuore di Pesaro, a ridosso della stazione ferroviaria alla quale è collegato con un sottopassaggio.',
746
- place: 'Via Solferino, 61122 Pesaro PU',
747
- url: 'https://design.wslabs.it/llm',
748
- image: 'https://design.wslabs.it/llm/media/casa-rossini.jpg',
749
- time: 'lun-ven 07-23',
750
- },
751
- 'Vorresti sapere altro?. 🙂\n',
722
+ { "type": "string", "content": "Un attimo di pazienza! Sto cercando gli eventi adatti a te! - **Maggiori informazioni**: [clicca qui](https://pesaro2024.it/experience-event/?id=408)\n [Scopri di più.](https://pesaro2024.it/experience-event/?id=168)\n ![](https://api.pesaro.entourance.com/uploads/images/1706001095-photo_2024-01-23%2010.10.09.jpeg) ![](https://api.pesaro.entourance.com/uploads/images/1706001095-photo_2024-01-23%2010.10.09.jpeg) ![](https://api.pesaro.entourance.com/uploads/images/1706001095-photo_2024-01-23%2010.10.09.jpeg)\n" },
723
+ { "date": "01/01/2024 - 31/12/2024", "description": "<p><a href=\"https://pesaro2024.it/poi/?id=466\"><strong>Museo Officine Benelli</strong></a></p>\n<p> </p>\n<p><em>Apertura ordinaria</em></p>\n<p> </p>\n<p><strong>dal lunedì al sabato</strong></p>\n<p>9:00 / 13:00 e 16:30 / 19:00 </p>\n<p> </p>\n<p><strong>Stradomenica (teza domenica del...", "id": 408, "image": "https://api.pesaro.entourance.com/uploads/images/1706269631-Benelli.jpg", "place": "Pesaro, Viale Goffredo Mameli 22, PU", "time": "", "title": "Apertura Museo Officine Benelli", "type": "eventItem", "url": "https://pesaro2024.it/experience-event/?id=408" },
724
+ { "date": "21/01/2024 - 15/12/2024", "description": "<p>L’Università dell’Età libera di Pesaro presenta “Classici per un anno”, II edizione 2024.</p>\n<p>Dodici appuntamenti con la grande letteratura in dodici luoghi di grande bellezza della provincia di Pesaro e Urbino</p>\n<p> </p>\n<p><strong>21...", "id": 337, "image": "https://api.pesaro.entourance.com/uploads/images/1704810354-Locandina.jpg", "place": "Pesaro, Largo Aldo Moro 12, PU", "time": "", "title": "Classici per un anno", "type": "eventItem", "url": "https://pesaro2024.it/experience-event/?id=337" },
725
+ { "date": "22/01/2024 - 31/12/2024", "description": "<p><em>Promuovere i prodotti del territorio coniugando cultura e tradizione, 100 bar aderenti nel territorio provinciale diventeranno hub di #Pesaro2024 distribuendo prodotti locali e informazioni sul programma culturale.</em></p>\n<p> </p>\n<p><strong>Appuntamenti...", "id": 246, "image": "https://api.pesaro.entourance.com/uploads/images/1707486369-Schermata 1920x1080.jpeg", "place": "Pesaro, Largo Aldo Moro 12, PU", "time": "", "title": "Cosa c'è DOP", "type": "eventItem", "url": "https://pesaro2024.it/experience-event/?id=246" },
726
+ { "date": "01/03/2024 - 30/09/2024", "description": "<p><strong>MARCHE | 1 marzo - 30 settembre</strong></p>\n<p> </p>\n<p>Il progetto <strong>ARBOR</strong>, dal latino Albero, nasce dal puro desiderio di avvicinarmi alla natura, di lavorare nei boschi e in luoghi speciali, di potermi ispirare sul luogo, lasciare che un albero mi parli, sentire il...", "id": 482, "image": "https://api.pesaro.entourance.com/uploads/images/1707384529-Pesaro 2024 - Illustrazione Cristina Spanò.jpg", "place": "Pesaro, Largo Aldo Moro 12, PU", "time": "", "title": "Arbor / Sculture biodegradabili sui tronchi degli alberi", "type": "eventItem", "url": "https://pesaro2024.it/experience-event/?id=482" },
727
+ { "date": "01/03/2024 - 31/10/2024", "description": "<p>Ridonare alla città urbanizzata il verde di cui, troppo spesso, si sente la mancanza.  Attraverso un percorso partecipato viene definito un progetto di ‘bosco itinerante’ per Pesaro. L’idea ispiratrice viene direttamente da Leeuwarden (Olanda) - anche Capitale della cultura 2018 - che con il...", "id": 166, "image": "https://api.pesaro.entourance.com/uploads/images/1699352215-Bosco.jpeg", "place": "Pesaro, Largo Aldo Moro 12, PU", "time": "", "title": "Bosco itinerante", "type": "eventItem", "url": "https://pesaro2024.it/experience-event/?id=166" },
728
+ { "type": "string", "content": "Ecco alcuni eventi e luoghi che potete visitare a Pesaro di martedì:\n" },
729
+ { "type": "string", "content": "1. **Apertura Museo Officine Benelli**\n" },
730
+ { "type": "string", "content": " - **Descrizione**: Il Museo Officine Benelli offre un'apertura ordinaria dove potrete esplorare la storia e i modelli iconici della celebre casa motociclistica.\n" },
731
+ { "type": "string", "content": " - **Luogo**: Pesaro, Viale Goffredo Mameli 22\n" },
732
+ { "type": "string", "content": " - **Orari**: 9:00 / 13:00 e 16:30 / 19:00 (dal lunedì al sabato)\n" },
733
+ { "type": "string", "content": " - **Periodo**: Tutto l'anno\n" },
734
+ { "type": "string", "content": " - **Maggiori informazioni**: [clicca qui](https://pesaro2024.it/experience-event/?id=408)\n" },
735
+ { "type": "string", "content": "2. **Classici per un anno**\n" },
736
+ { "type": "string", "content": " - **Descrizione**: Un ciclo di dodici appuntamenti dedicati alla grande letteratura, ospitati in luoghi di grande bellezza della provincia di Pesaro e Urbino.\n" },
737
+ { "type": "string", "content": " - **Luogo**: Pesaro, Largo Aldo Moro 12\n" },
738
+ { "type": "string", "content": " - **Periodo**: 21/01/2024 - 15/12/2024\n" },
739
+ { "type": "string", "content": " - **Maggiori informazioni**: [clicca qui](https://pesaro2024.it/experience-event/?id=337)\n" },
740
+ { "type": "string", "content": "3. **Cosa c'è DOP**\n" },
741
+ { "type": "string", "content": " - **Descrizione**: Una promozione dei prodotti del territorio che coniuga cultura e tradizione, con 100 bar nel territorio provinciale che diventano hub di Pesaro2024.\n" },
742
+ { "type": "string", "content": " - **Luogo**: Pesaro, Largo Aldo Moro 12\n" },
743
+ { "type": "string", "content": " - **Periodo**: 22/01/2024 - 31/12/2024\n" },
744
+ { "type": "string", "content": " - **Maggiori informazioni**: [clicca qui](https://pesaro2024.it/experience-event/?id=246)\n" },
745
+ { "type": "string", "content": "4. **Arbor / Sculture biodegradabili sui tronchi degli alberi**\n" },
746
+ { "type": "string", "content": " - **Descrizione**: Un progetto artistico che celebra la natura attraverso sculture biodegradabili create direttamente sui tronchi degli alberi.\n" },
747
+ { "type": "string", "content": " - **Luogo**: Pesaro, Largo Aldo Moro 12\n" },
748
+ { "type": "string", "content": " - **Periodo**: 01/03/2024 - 30/09/2024\n" },
749
+ { "type": "string", "content": " - **Maggiori informazioni**: [clicca qui](https://pesaro2024.it/experience-event/?id=482)\n" },
750
+ { "type": "string", "content": "5. **Bosco itinerante**\n" },
751
+ { "type": "string", "content": " - **Descrizione**: Un'iniziativa per ridonare alla città urbanizzata il verde di cui si sente la mancanza, attraverso un progetto di bosco itinerante per Pesaro.\n" },
752
+ { "type": "string", "content": " - **Luogo**: Pesaro, Largo Aldo Moro 12\n" },
753
+ { "type": "string", "content": " - **Periodo**: 01/03/2024 - 31/10/2024\n" },
754
+ { "type": "string", "content": " - **Maggiori informazioni**: [clicca qui](https://pesaro2024.it/experience-event/?id=166)\n" },
755
+ { "type": "string", "content": "Questi eventi rappresentano solo una parte delle numerose attività e luoghi di interesse che Pesaro offre durante il suo anno speciale come Capitale della Cultura 2024." }
752
756
  ],
753
757
  role: 'assistant',
754
758
  },
755
759
  {
756
760
  chunks: [
757
- 'Sarò a Pesaro con la mia famiglia a giugno. Siamo tutti appassionati di arte e natura. Cosa ci consigli di fare qui?\n',
761
+ 'Ok grazie\n',
758
762
  ],
759
763
  role: 'user',
760
764
  },
@@ -1127,6 +1131,11 @@ function getMockChunks(index, locale) {
1127
1131
  message.chunks.forEach(chunk => {
1128
1132
  if (typeof chunk === 'string') {
1129
1133
  chunks.push(...chunk.split(' ').map((x, i, a) => `${x}${i < a.length - 1 ? ' ' : ''}`));
1134
+ } else if (chunk.type === 'string') {
1135
+ const content = chunk.content;
1136
+ if (content) {
1137
+ chunks.push(...content.split(' ').map((x, i, a) => `${x}${i < a.length - 1 ? ' ' : ''}`));
1138
+ }
1130
1139
  } else {
1131
1140
  chunks.push(chunk);
1132
1141
  }
@@ -1200,6 +1209,7 @@ class Speech extends Emitter {
1200
1209
  super();
1201
1210
  this.lang_ = '';
1202
1211
  this.chunks_ = [];
1212
+ this.recognizerStarted_ = false;
1203
1213
  this.lang = lang;
1204
1214
  this.enabled = enabled;
1205
1215
  if (!this.hasSupport) {
@@ -1288,18 +1298,28 @@ class Speech extends Emitter {
1288
1298
  }
1289
1299
  recognizeStart() {
1290
1300
  if (!this.hasSupport) {
1301
+ // console.log('Speech.recognizeStart.unsupported');
1291
1302
  return this.unsupported();
1292
1303
  }
1304
+ if (this.recognizerStarted_) {
1305
+ return;
1306
+ }
1293
1307
  const recognizer = this.recognizer;
1294
1308
  recognizer.lang = this.lang; // "en-US";
1295
1309
  recognizer.start();
1310
+ // console.log('Speech.recognizeStart');
1296
1311
  }
1297
1312
  recognizeStop() {
1298
1313
  if (!this.hasSupport) {
1299
1314
  return this.unsupported();
1300
1315
  }
1301
- const recognizer = this.recognizer;
1302
- recognizer.stop();
1316
+ setTimeout(() => {
1317
+ if (this.recognizerStarted_) {
1318
+ const recognizer = this.recognizer;
1319
+ recognizer.stop();
1320
+ // console.log('Speech.recognizeStop');
1321
+ }
1322
+ }, 1000);
1303
1323
  }
1304
1324
  onResult(event) {
1305
1325
  const results = event.results;
@@ -1309,6 +1329,7 @@ class Speech extends Emitter {
1309
1329
  return p ? c.confidence > p.confidence ? c : p : c;
1310
1330
  }, undefined);
1311
1331
  const transcript = alternative ? alternative.transcript : '';
1332
+ // console.log('Speech.transcript', transcript);
1312
1333
  this.emit('result', transcript);
1313
1334
  }
1314
1335
  unsupported() {
@@ -1349,8 +1370,16 @@ class Speech extends Emitter {
1349
1370
  recognizer.continuous = true;
1350
1371
  recognizer.interimResults = false;
1351
1372
  recognizer.maxAlternatives = 1;
1373
+ recognizer.addEventListener('start', () => {
1374
+ // console.log('recognizer.start');
1375
+ this.recognizerStarted_ = true;
1376
+ });
1377
+ recognizer.addEventListener('end', () => {
1378
+ // console.log('recognizer.end');
1379
+ this.recognizerStarted_ = false;
1380
+ });
1352
1381
  recognizer.addEventListener('result', event => {
1353
- // console.log(event);
1382
+ // console.log('recognizer.result');
1354
1383
  this.onResult(event);
1355
1384
  });
1356
1385
  /*
@@ -1496,7 +1525,7 @@ const createLlmStore = _ref => {
1496
1525
  fixed: false,
1497
1526
  idle: true,
1498
1527
  direction: 0,
1499
- speakEnabled: false
1528
+ speakEnabled: true
1500
1529
  };
1501
1530
  let messageService = undefined;
1502
1531
  const speech = new Speech();
@@ -1591,9 +1620,18 @@ const createLlmStore = _ref => {
1591
1620
  const state = get();
1592
1621
  speech.lang = locale;
1593
1622
  speech.enabled = state.speakEnabled;
1594
- speech.on('result', transcript => {
1623
+ speech.on('result', transcript => __awaiter(void 0, void 0, void 0, function* () {
1624
+ // console.log('useLlm.speech.result', transcript);
1625
+ const state = get();
1595
1626
  state.actions.setPrompt(transcript);
1596
- });
1627
+ /*
1628
+ await state.actions.send(transcript, (response) => {
1629
+ // console.log('Prompt.onChunk', response.chunks);
1630
+ }, (response) => {
1631
+ // console.log('Prompt.onEnd', response);
1632
+ });
1633
+ */
1634
+ }));
1597
1635
  const decodeThread = thread => {
1598
1636
  return thread ? {
1599
1637
  messages: decodeMessages(thread.messages),
@@ -1764,6 +1802,9 @@ const createLlmStore = _ref => {
1764
1802
  setPrompt: prompt => set(state => ({
1765
1803
  prompt
1766
1804
  })),
1805
+ hasSpeechSupport: () => {
1806
+ return speech.hasSupport;
1807
+ },
1767
1808
  toggleSpeak: () => {
1768
1809
  set(state => {
1769
1810
  const speakEnabled = !state.speakEnabled;
@@ -2481,12 +2522,15 @@ const Prompt = () => {
2481
2522
  const prompt = useLlm(state => state.prompt);
2482
2523
  const messages = useLlm(state => state.messages);
2483
2524
  const streaming = useLlm(state => state.streaming);
2525
+ const speakEnabled = useLlm(state => state.speakEnabled);
2484
2526
  const {
2485
2527
  send,
2486
2528
  setPrompt,
2487
2529
  recognizeStart,
2488
2530
  recognizeStop,
2489
- abort
2531
+ abort,
2532
+ toggleSpeak,
2533
+ hasSpeechSupport
2490
2534
  } = useLlm(state => state.actions);
2491
2535
  useAutosizeTextArea(textArea.current, prompt);
2492
2536
  const onChange = event => {
@@ -2522,7 +2566,12 @@ const Prompt = () => {
2522
2566
  onSubmit: onSubmit,
2523
2567
  children: [jsxRuntime.jsxs("div", {
2524
2568
  className: "llm__prompt-input",
2525
- children: [jsxRuntime.jsx("textarea", {
2569
+ children: [hasSpeechSupport() && jsxRuntime.jsx("button", {
2570
+ className: "llm__prompt-speak",
2571
+ type: "button",
2572
+ onClick: toggleSpeak,
2573
+ children: speakEnabled ? jsxRuntime.jsx(bomMixerIcons.IconLlmVolume, {}) : jsxRuntime.jsx(bomMixerIcons.IconLlmVolumeX, {})
2574
+ }), jsxRuntime.jsx("textarea", {
2526
2575
  className: "llm__prompt-textarea",
2527
2576
  name: "prompt",
2528
2577
  onKeyDown: onKeyDown,
@@ -2530,7 +2579,15 @@ const Prompt = () => {
2530
2579
  ref: textArea,
2531
2580
  rows: 1,
2532
2581
  value: prompt
2533
- }), false ]
2582
+ }), hasSpeechSupport() && jsxRuntime.jsx("button", {
2583
+ className: "llm__prompt-microphone",
2584
+ type: "button",
2585
+ onMouseDown: () => recognizeStart(),
2586
+ onMouseUp: () => recognizeStop(),
2587
+ onTouchStart: () => recognizeStart(),
2588
+ onTouchEnd: () => recognizeStop(),
2589
+ children: jsxRuntime.jsx(bomMixerIcons.IconLlmMicrophone, {})
2590
+ })]
2534
2591
  }), streaming ? jsxRuntime.jsxs("button", {
2535
2592
  className: "llm__prompt-stop",
2536
2593
  type: "button",
@@ -2546,6 +2603,14 @@ const Prompt = () => {
2546
2603
  children: jsxRuntime.jsx(bomMixerIcons.IconLlmSend, {})
2547
2604
  })]
2548
2605
  })]
2606
+ }), jsxRuntime.jsxs("a", {
2607
+ className: "llm__prompt-powered-by",
2608
+ href: "https://www.websolute.com/",
2609
+ target: "_blank",
2610
+ title: "Powered by Websolute",
2611
+ children: [jsxRuntime.jsx("span", {
2612
+ children: "powered by"
2613
+ }), " ", jsxRuntime.jsx(bomMixerIcons.IconWebsolute, {})]
2549
2614
  })]
2550
2615
  })
2551
2616
  });
@@ -3012,10 +3077,22 @@ function getEmailHtml(data, app) {
3012
3077
 
3013
3078
  const Text = props => {
3014
3079
  const label = bomMixerHooks.useLabel();
3080
+ const app = useLlm(state => state.app);
3081
+ const appDisclaimer = app === null || app === void 0 ? void 0 : app.contents.disclaimer;
3015
3082
  const html = react.useMemo(() => {
3083
+ marked.marked.use({
3084
+ renderer: {
3085
+ link(href, title, text) {
3086
+ return `<a href="${href}" target="_blank"${title ? ` title="${title}"` : ''}>${text}</a>`;
3087
+ },
3088
+ image(href, title, text) {
3089
+ return `<a class="llm__text-img" href="${href}" target="_blank"><img src="${href}"${title ? ` title="${title}"` : ''} alt="${text}" /></a>`;
3090
+ }
3091
+ }
3092
+ });
3016
3093
  return marked.marked.parse(props.body);
3017
3094
  }, [props.body]);
3018
- const disclaimer = props.disclaimer ? label('llm.disclaimer') : '';
3095
+ const disclaimer = props.disclaimer ? appDisclaimer || label('llm.disclaimer') : '';
3019
3096
  return jsxRuntime.jsxs("div", {
3020
3097
  className: "llm__text",
3021
3098
  children: [jsxRuntime.jsx("div", {
@@ -3361,13 +3438,9 @@ const Thread = () => {
3361
3438
  const Main = () => {
3362
3439
  var _a;
3363
3440
  const lenisRef = react.useRef(null);
3441
+ const messages = useLlm(state => state.messages);
3364
3442
  const {
3365
- messages,
3366
- speakEnabled
3367
- } = useLlm(state => state);
3368
- const {
3369
- setLenis,
3370
- toggleSpeak
3443
+ setLenis
3371
3444
  } = useLlm(state => state.actions);
3372
3445
  const classNames = bomCore.getClassNames('llm__main');
3373
3446
  setLenis((_a = lenisRef.current) === null || _a === void 0 ? void 0 : _a.lenis);
@@ -3429,11 +3502,7 @@ const Main = () => {
3429
3502
  animate: "show",
3430
3503
  exit: "exit",
3431
3504
  className: classNames,
3432
- children: [jsxRuntime.jsx("button", {
3433
- className: "llm__toggle-speak",
3434
- onClick: toggleSpeak,
3435
- children: speakEnabled ? jsxRuntime.jsx(bomMixerIcons.IconLlmSpeakDisabled, {}) : jsxRuntime.jsx(bomMixerIcons.IconLlmSpeakEnabled, {})
3436
- }), jsxRuntime.jsx(Canvas, {}), jsxRuntime.jsx(framerMotion.motion.div, {
3505
+ children: [jsxRuntime.jsx(Canvas, {}), jsxRuntime.jsx(framerMotion.motion.div, {
3437
3506
  className: "llm__top",
3438
3507
  variants: slideIn(-1),
3439
3508
  initial: "hidden",