@lokutor/sdk 1.1.37 → 1.1.40

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.
package/dist/index.d.mts CHANGED
@@ -20,8 +20,35 @@ declare enum Language {
20
20
  ENGLISH = "en",
21
21
  SPANISH = "es",
22
22
  FRENCH = "fr",
23
+ GERMAN = "de",
24
+ ITALIAN = "it",
23
25
  PORTUGUESE = "pt",
24
- KOREAN = "ko"
26
+ JAPANESE = "ja",
27
+ KOREAN = "ko",
28
+ CHINESE = "zh",
29
+ ARABIC = "ar",
30
+ BULGARIAN = "bg",
31
+ CROATIAN = "hr",
32
+ CZECH = "cs",
33
+ DANISH = "da",
34
+ DUTCH = "nl",
35
+ ESTONIAN = "et",
36
+ FINNISH = "fi",
37
+ GREEK = "el",
38
+ HINDI = "hi",
39
+ HUNGARIAN = "hu",
40
+ INDONESIAN = "id",
41
+ LATVIAN = "lv",
42
+ LITHUANIAN = "lt",
43
+ POLISH = "pl",
44
+ ROMANIAN = "ro",
45
+ RUSSIAN = "ru",
46
+ SLOVAK = "sk",
47
+ SLOVENIAN = "sl",
48
+ SWEDISH = "sv",
49
+ TURKISH = "tr",
50
+ UKRAINIAN = "uk",
51
+ VIETNAMESE = "vi"
25
52
  }
26
53
  /**
27
54
  * Audio configuration constants
@@ -687,6 +714,7 @@ declare class ConversationalPanel {
687
714
  showError(text: string): void;
688
715
  /** Destroy the component, removing all DOM and stopping any active session */
689
716
  destroy(): void;
717
+ private fmt;
690
718
  private startTimer;
691
719
  private toggleMute;
692
720
  private playConnectingSound;
package/dist/index.d.ts CHANGED
@@ -20,8 +20,35 @@ declare enum Language {
20
20
  ENGLISH = "en",
21
21
  SPANISH = "es",
22
22
  FRENCH = "fr",
23
+ GERMAN = "de",
24
+ ITALIAN = "it",
23
25
  PORTUGUESE = "pt",
24
- KOREAN = "ko"
26
+ JAPANESE = "ja",
27
+ KOREAN = "ko",
28
+ CHINESE = "zh",
29
+ ARABIC = "ar",
30
+ BULGARIAN = "bg",
31
+ CROATIAN = "hr",
32
+ CZECH = "cs",
33
+ DANISH = "da",
34
+ DUTCH = "nl",
35
+ ESTONIAN = "et",
36
+ FINNISH = "fi",
37
+ GREEK = "el",
38
+ HINDI = "hi",
39
+ HUNGARIAN = "hu",
40
+ INDONESIAN = "id",
41
+ LATVIAN = "lv",
42
+ LITHUANIAN = "lt",
43
+ POLISH = "pl",
44
+ ROMANIAN = "ro",
45
+ RUSSIAN = "ru",
46
+ SLOVAK = "sk",
47
+ SLOVENIAN = "sl",
48
+ SWEDISH = "sv",
49
+ TURKISH = "tr",
50
+ UKRAINIAN = "uk",
51
+ VIETNAMESE = "vi"
25
52
  }
26
53
  /**
27
54
  * Audio configuration constants
@@ -687,6 +714,7 @@ declare class ConversationalPanel {
687
714
  showError(text: string): void;
688
715
  /** Destroy the component, removing all DOM and stopping any active session */
689
716
  destroy(): void;
717
+ private fmt;
690
718
  private startTimer;
691
719
  private toggleMute;
692
720
  private playConnectingSound;
package/dist/index.js CHANGED
@@ -63,8 +63,35 @@ var Language = /* @__PURE__ */ ((Language2) => {
63
63
  Language2["ENGLISH"] = "en";
64
64
  Language2["SPANISH"] = "es";
65
65
  Language2["FRENCH"] = "fr";
66
+ Language2["GERMAN"] = "de";
67
+ Language2["ITALIAN"] = "it";
66
68
  Language2["PORTUGUESE"] = "pt";
69
+ Language2["JAPANESE"] = "ja";
67
70
  Language2["KOREAN"] = "ko";
71
+ Language2["CHINESE"] = "zh";
72
+ Language2["ARABIC"] = "ar";
73
+ Language2["BULGARIAN"] = "bg";
74
+ Language2["CROATIAN"] = "hr";
75
+ Language2["CZECH"] = "cs";
76
+ Language2["DANISH"] = "da";
77
+ Language2["DUTCH"] = "nl";
78
+ Language2["ESTONIAN"] = "et";
79
+ Language2["FINNISH"] = "fi";
80
+ Language2["GREEK"] = "el";
81
+ Language2["HINDI"] = "hi";
82
+ Language2["HUNGARIAN"] = "hu";
83
+ Language2["INDONESIAN"] = "id";
84
+ Language2["LATVIAN"] = "lv";
85
+ Language2["LITHUANIAN"] = "lt";
86
+ Language2["POLISH"] = "pl";
87
+ Language2["ROMANIAN"] = "ro";
88
+ Language2["RUSSIAN"] = "ru";
89
+ Language2["SLOVAK"] = "sk";
90
+ Language2["SLOVENIAN"] = "sl";
91
+ Language2["SWEDISH"] = "sv";
92
+ Language2["TURKISH"] = "tr";
93
+ Language2["UKRAINIAN"] = "uk";
94
+ Language2["VIETNAMESE"] = "vi";
68
95
  return Language2;
69
96
  })(Language || {});
70
97
  var AUDIO_CONFIG = {
@@ -1335,7 +1362,7 @@ var PANEL_CSS = (
1335
1362
  position: relative;
1336
1363
  width: 100%;
1337
1364
  aspect-ratio: 16 / 9;
1338
- max-height: 800px;
1365
+ max-height: clamp(400px, 50vh, 800px);
1339
1366
  display: flex;
1340
1367
  flex-direction: column;
1341
1368
  align-items: center;
@@ -1344,7 +1371,7 @@ var PANEL_CSS = (
1344
1371
  margin: 0;
1345
1372
  background: var(--cv-bg, #0a0a0a);
1346
1373
  box-shadow: inset 0 10px 40px rgba(0, 0, 0, 0.1), inset 0 0 100px rgba(0, 0, 0, 0.05);
1347
- border-radius: 40px;
1374
+ border-radius: clamp(20px, 5vw, 40px);
1348
1375
  overflow: hidden;
1349
1376
  }
1350
1377
  .cv-curtain {
@@ -1484,35 +1511,35 @@ var PANEL_CSS = (
1484
1511
  display: flex;
1485
1512
  flex-direction: column;
1486
1513
  align-items: center;
1487
- gap: 1.5rem;
1514
+ gap: clamp(1rem, 2vw, 1.5rem);
1488
1515
  margin-top: auto;
1489
- padding-bottom: 1.5rem;
1516
+ padding-bottom: clamp(1rem, 2vw, 1.5rem);
1490
1517
  z-index: 20;
1491
1518
  }
1492
1519
  .cv-pill {
1493
1520
  display: flex;
1494
1521
  align-items: center;
1495
- gap: 0.75rem;
1522
+ gap: clamp(0.5rem, 1vw, 0.75rem);
1496
1523
  background: var(--cv-ui-bg, rgba(255, 255, 255, 0.03));
1497
1524
  backdrop-filter: blur(30px);
1498
1525
  -webkit-backdrop-filter: blur(30px);
1499
1526
  border: 1px solid var(--cv-ui-border, rgba(255, 255, 255, 0.08));
1500
- padding: 0.3rem 0.75rem;
1527
+ padding: clamp(0.25rem, 0.5vw, 0.3rem) clamp(0.5rem, 1vw, 0.75rem);
1501
1528
  border-radius: 100px;
1502
1529
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.15);
1503
1530
  }
1504
1531
  .cv-btn {
1505
1532
  display: flex;
1506
1533
  align-items: center;
1507
- gap: 0.5rem;
1534
+ gap: clamp(0.35rem, 1vw, 0.5rem);
1508
1535
  background: transparent;
1509
1536
  border: none;
1510
1537
  color: var(--cv-text-dim, rgba(255,255,255,0.55));
1511
1538
  font-weight: 600;
1512
- font-size: 0.75rem;
1539
+ font-size: clamp(0.65rem, 1.5vw, 0.75rem);
1513
1540
  cursor: pointer;
1514
1541
  transition: all 0.2s ease;
1515
- padding: 0.35rem 0.75rem;
1542
+ padding: clamp(0.25rem, 0.5vw, 0.35rem) clamp(0.5rem, 1vw, 0.75rem);
1516
1543
  border-radius: 50px;
1517
1544
  }
1518
1545
  .cv-btn:hover { color: var(--cv-text, #fff); background: var(--cv-ui-bg, rgba(255,255,255,0.05)); }
@@ -1526,31 +1553,50 @@ var PANEL_CSS = (
1526
1553
  .cv-btn.is-muted { color: var(--cv-accent); }
1527
1554
  .cv-error {
1528
1555
  position: absolute;
1529
- bottom: 1.25rem;
1556
+ bottom: clamp(0.75rem, 2vw, 1.25rem);
1530
1557
  left: 50%;
1531
1558
  transform: translateX(-50%);
1532
1559
  background: #0a0a0a;
1533
1560
  color: #e0e0e0;
1534
- padding: 0.75rem 1.5rem;
1535
- border-radius: 12px;
1536
- font-size: 0.875rem;
1561
+ padding: clamp(0.5rem, 1vw, 0.75rem) clamp(1rem, 2vw, 1.5rem);
1562
+ border-radius: clamp(8px, 1.5vw, 12px);
1563
+ font-size: clamp(0.75rem, 1.5vw, 0.875rem);
1537
1564
  font-weight: 500;
1538
1565
  display: none;
1539
1566
  align-items: center;
1540
- gap: 0.75rem;
1567
+ gap: clamp(0.5rem, 1vw, 0.75rem);
1541
1568
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
1542
1569
  z-index: 1000;
1543
1570
  border: 1px solid rgba(255, 255, 255, 0.06);
1544
1571
  backdrop-filter: blur(20px);
1572
+ max-width: clamp(280px, 90vw, 400px);
1545
1573
  }
1546
1574
  .cv-error.is-visible { display: flex; }
1547
1575
  .cv-error-icon { color: var(--cv-accent); flex-shrink: 0; }
1548
- @media (max-width: 640px) {
1549
- .cv-panel { aspect-ratio: 3 / 4; max-height: 600px; border-radius: 20px; }
1550
- .cv-curtain-content { padding: 0.75rem; gap: 0.5rem; }
1551
- .cv-curtain-title { font-size: clamp(1rem, 5vw, 1.6rem); }
1552
- .cv-curtain-btn { padding: 0.5rem 1.25rem; font-size: clamp(0.7rem, 2vw, 0.85rem); }
1553
- .cv-visualizer-wrap { width: clamp(100px, 50vw, 200px); height: clamp(100px, 50vw, 200px); }
1576
+
1577
+ @media (max-width: 768px) {
1578
+ .cv-panel { max-height: 600px; border-radius: 24px; }
1579
+ .cv-curtain-content { padding: clamp(0.75rem, 3vw, 1.5rem); gap: clamp(0.5rem, 2vw, 1rem); }
1580
+ .cv-curtain-title { font-size: clamp(1rem, 5vw, 1.8rem); }
1581
+ .cv-curtain-desc { font-size: clamp(0.65rem, 2.5vw, 0.95rem); max-width: 90vw; }
1582
+ .cv-curtain-btn { padding: clamp(0.45rem, 1.5vw, 0.75rem) clamp(1rem, 3vw, 2rem); font-size: clamp(0.65rem, 1.5vw, 0.9rem); }
1583
+ .cv-visualizer-wrap { width: clamp(100px, 45vw, 220px); height: clamp(100px, 45vw, 220px); }
1584
+ .cv-title { font-size: clamp(0.9rem, 2vw, 1.5rem); gap: clamp(0.5rem, 1vw, 0.75rem); }
1585
+ .cv-controls { gap: clamp(1rem, 2vw, 1.5rem); padding-bottom: clamp(1rem, 2vw, 1.5rem); }
1586
+ .cv-pill { padding: clamp(0.2rem, 1vw, 0.4rem) clamp(0.5rem, 1.5vw, 0.75rem); font-size: clamp(0.65rem, 1.5vw, 0.75rem); }
1587
+ .cv-btn { padding: clamp(0.25rem, 1vw, 0.35rem) clamp(0.5rem, 1.5vw, 0.75rem); font-size: clamp(0.65rem, 1.5vw, 0.75rem); }
1588
+ .cv-error { padding: clamp(0.5rem, 1.5vw, 0.75rem) clamp(1rem, 2vw, 1.5rem); font-size: clamp(0.75rem, 1.5vw, 0.875rem); }
1589
+ }
1590
+
1591
+ @media (max-width: 480px) {
1592
+ .cv-panel { aspect-ratio: 3 / 4; max-height: 500px; border-radius: 20px; }
1593
+ .cv-curtain-content { padding: clamp(0.5rem, 2vw, 1rem); gap: clamp(0.4rem, 1.5vw, 0.75rem); }
1594
+ .cv-curtain-title { font-size: clamp(0.9rem, 4.5vw, 1.4rem); }
1595
+ .cv-curtain-desc { font-size: clamp(0.6rem, 2vw, 0.8rem); }
1596
+ .cv-curtain-btn { padding: clamp(0.4rem, 1.2vw, 0.6rem) clamp(0.8rem, 2.5vw, 1.5rem); font-size: clamp(0.6rem, 1.2vw, 0.8rem); }
1597
+ .cv-visualizer-wrap { width: clamp(80px, 40vw, 180px); height: clamp(80px, 40vw, 180px); }
1598
+ .cv-header { padding-top: clamp(1rem, 2vw, 1.5rem); }
1599
+ .cv-controls { gap: clamp(0.8rem, 1.5vw, 1.2rem); padding-bottom: clamp(0.8rem, 1.5vw, 1.2rem); }
1554
1600
  }
1555
1601
  `
1556
1602
  );
@@ -1869,27 +1915,31 @@ var ConversationalPanel = class {
1869
1915
  this.el.remove();
1870
1916
  }
1871
1917
  // ─── internal helpers ────────────────────────────────────
1918
+ fmt(s) {
1919
+ const m = Math.floor(s / 60).toString().padStart(2, "0");
1920
+ const sec = (s % 60).toString().padStart(2, "0");
1921
+ return `${m}:${sec}`;
1922
+ }
1872
1923
  startTimer() {
1873
1924
  let elapsed = 0;
1874
1925
  const maxDur = this.cfg.maxDuration || 300;
1875
1926
  const silentMax = this.cfg.silenceTimeout || 30;
1876
1927
  this._lastSpeechTime = Date.now();
1877
- this.timerEl.textContent = "00:00";
1928
+ this.timerEl.textContent = this.fmt(maxDur);
1878
1929
  this.timerTicker = window.setInterval(() => {
1879
1930
  elapsed++;
1880
- const m = Math.floor(elapsed / 60).toString().padStart(2, "0");
1881
- const s = (elapsed % 60).toString().padStart(2, "0");
1882
- this.timerEl.textContent = `${m}:${s}`;
1931
+ const remaining = Math.max(0, maxDur - elapsed);
1932
+ this.timerEl.textContent = this.fmt(remaining);
1883
1933
  if (elapsed >= maxDur) {
1884
1934
  this._locked = true;
1885
1935
  this.stop();
1886
- this.showError("Conversation time limit reached. Refresh to start a new one.");
1936
+ this.showError("Tiempo l\xEDmite alcanzado.");
1887
1937
  return;
1888
1938
  }
1889
1939
  if (Date.now() - this._lastSpeechTime > silentMax * 1e3) {
1890
1940
  this._locked = true;
1891
1941
  this.stop();
1892
- this.showError("Session ended due to inactivity. Refresh to start a new one.");
1942
+ this.showError("Sesi\xF3n finalizada por inactividad.");
1893
1943
  }
1894
1944
  }, 1e3);
1895
1945
  }
package/dist/index.mjs CHANGED
@@ -16,8 +16,35 @@ var Language = /* @__PURE__ */ ((Language2) => {
16
16
  Language2["ENGLISH"] = "en";
17
17
  Language2["SPANISH"] = "es";
18
18
  Language2["FRENCH"] = "fr";
19
+ Language2["GERMAN"] = "de";
20
+ Language2["ITALIAN"] = "it";
19
21
  Language2["PORTUGUESE"] = "pt";
22
+ Language2["JAPANESE"] = "ja";
20
23
  Language2["KOREAN"] = "ko";
24
+ Language2["CHINESE"] = "zh";
25
+ Language2["ARABIC"] = "ar";
26
+ Language2["BULGARIAN"] = "bg";
27
+ Language2["CROATIAN"] = "hr";
28
+ Language2["CZECH"] = "cs";
29
+ Language2["DANISH"] = "da";
30
+ Language2["DUTCH"] = "nl";
31
+ Language2["ESTONIAN"] = "et";
32
+ Language2["FINNISH"] = "fi";
33
+ Language2["GREEK"] = "el";
34
+ Language2["HINDI"] = "hi";
35
+ Language2["HUNGARIAN"] = "hu";
36
+ Language2["INDONESIAN"] = "id";
37
+ Language2["LATVIAN"] = "lv";
38
+ Language2["LITHUANIAN"] = "lt";
39
+ Language2["POLISH"] = "pl";
40
+ Language2["ROMANIAN"] = "ro";
41
+ Language2["RUSSIAN"] = "ru";
42
+ Language2["SLOVAK"] = "sk";
43
+ Language2["SLOVENIAN"] = "sl";
44
+ Language2["SWEDISH"] = "sv";
45
+ Language2["TURKISH"] = "tr";
46
+ Language2["UKRAINIAN"] = "uk";
47
+ Language2["VIETNAMESE"] = "vi";
21
48
  return Language2;
22
49
  })(Language || {});
23
50
  var AUDIO_CONFIG = {
@@ -1288,7 +1315,7 @@ var PANEL_CSS = (
1288
1315
  position: relative;
1289
1316
  width: 100%;
1290
1317
  aspect-ratio: 16 / 9;
1291
- max-height: 800px;
1318
+ max-height: clamp(400px, 50vh, 800px);
1292
1319
  display: flex;
1293
1320
  flex-direction: column;
1294
1321
  align-items: center;
@@ -1297,7 +1324,7 @@ var PANEL_CSS = (
1297
1324
  margin: 0;
1298
1325
  background: var(--cv-bg, #0a0a0a);
1299
1326
  box-shadow: inset 0 10px 40px rgba(0, 0, 0, 0.1), inset 0 0 100px rgba(0, 0, 0, 0.05);
1300
- border-radius: 40px;
1327
+ border-radius: clamp(20px, 5vw, 40px);
1301
1328
  overflow: hidden;
1302
1329
  }
1303
1330
  .cv-curtain {
@@ -1437,35 +1464,35 @@ var PANEL_CSS = (
1437
1464
  display: flex;
1438
1465
  flex-direction: column;
1439
1466
  align-items: center;
1440
- gap: 1.5rem;
1467
+ gap: clamp(1rem, 2vw, 1.5rem);
1441
1468
  margin-top: auto;
1442
- padding-bottom: 1.5rem;
1469
+ padding-bottom: clamp(1rem, 2vw, 1.5rem);
1443
1470
  z-index: 20;
1444
1471
  }
1445
1472
  .cv-pill {
1446
1473
  display: flex;
1447
1474
  align-items: center;
1448
- gap: 0.75rem;
1475
+ gap: clamp(0.5rem, 1vw, 0.75rem);
1449
1476
  background: var(--cv-ui-bg, rgba(255, 255, 255, 0.03));
1450
1477
  backdrop-filter: blur(30px);
1451
1478
  -webkit-backdrop-filter: blur(30px);
1452
1479
  border: 1px solid var(--cv-ui-border, rgba(255, 255, 255, 0.08));
1453
- padding: 0.3rem 0.75rem;
1480
+ padding: clamp(0.25rem, 0.5vw, 0.3rem) clamp(0.5rem, 1vw, 0.75rem);
1454
1481
  border-radius: 100px;
1455
1482
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.15);
1456
1483
  }
1457
1484
  .cv-btn {
1458
1485
  display: flex;
1459
1486
  align-items: center;
1460
- gap: 0.5rem;
1487
+ gap: clamp(0.35rem, 1vw, 0.5rem);
1461
1488
  background: transparent;
1462
1489
  border: none;
1463
1490
  color: var(--cv-text-dim, rgba(255,255,255,0.55));
1464
1491
  font-weight: 600;
1465
- font-size: 0.75rem;
1492
+ font-size: clamp(0.65rem, 1.5vw, 0.75rem);
1466
1493
  cursor: pointer;
1467
1494
  transition: all 0.2s ease;
1468
- padding: 0.35rem 0.75rem;
1495
+ padding: clamp(0.25rem, 0.5vw, 0.35rem) clamp(0.5rem, 1vw, 0.75rem);
1469
1496
  border-radius: 50px;
1470
1497
  }
1471
1498
  .cv-btn:hover { color: var(--cv-text, #fff); background: var(--cv-ui-bg, rgba(255,255,255,0.05)); }
@@ -1479,31 +1506,50 @@ var PANEL_CSS = (
1479
1506
  .cv-btn.is-muted { color: var(--cv-accent); }
1480
1507
  .cv-error {
1481
1508
  position: absolute;
1482
- bottom: 1.25rem;
1509
+ bottom: clamp(0.75rem, 2vw, 1.25rem);
1483
1510
  left: 50%;
1484
1511
  transform: translateX(-50%);
1485
1512
  background: #0a0a0a;
1486
1513
  color: #e0e0e0;
1487
- padding: 0.75rem 1.5rem;
1488
- border-radius: 12px;
1489
- font-size: 0.875rem;
1514
+ padding: clamp(0.5rem, 1vw, 0.75rem) clamp(1rem, 2vw, 1.5rem);
1515
+ border-radius: clamp(8px, 1.5vw, 12px);
1516
+ font-size: clamp(0.75rem, 1.5vw, 0.875rem);
1490
1517
  font-weight: 500;
1491
1518
  display: none;
1492
1519
  align-items: center;
1493
- gap: 0.75rem;
1520
+ gap: clamp(0.5rem, 1vw, 0.75rem);
1494
1521
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
1495
1522
  z-index: 1000;
1496
1523
  border: 1px solid rgba(255, 255, 255, 0.06);
1497
1524
  backdrop-filter: blur(20px);
1525
+ max-width: clamp(280px, 90vw, 400px);
1498
1526
  }
1499
1527
  .cv-error.is-visible { display: flex; }
1500
1528
  .cv-error-icon { color: var(--cv-accent); flex-shrink: 0; }
1501
- @media (max-width: 640px) {
1502
- .cv-panel { aspect-ratio: 3 / 4; max-height: 600px; border-radius: 20px; }
1503
- .cv-curtain-content { padding: 0.75rem; gap: 0.5rem; }
1504
- .cv-curtain-title { font-size: clamp(1rem, 5vw, 1.6rem); }
1505
- .cv-curtain-btn { padding: 0.5rem 1.25rem; font-size: clamp(0.7rem, 2vw, 0.85rem); }
1506
- .cv-visualizer-wrap { width: clamp(100px, 50vw, 200px); height: clamp(100px, 50vw, 200px); }
1529
+
1530
+ @media (max-width: 768px) {
1531
+ .cv-panel { max-height: 600px; border-radius: 24px; }
1532
+ .cv-curtain-content { padding: clamp(0.75rem, 3vw, 1.5rem); gap: clamp(0.5rem, 2vw, 1rem); }
1533
+ .cv-curtain-title { font-size: clamp(1rem, 5vw, 1.8rem); }
1534
+ .cv-curtain-desc { font-size: clamp(0.65rem, 2.5vw, 0.95rem); max-width: 90vw; }
1535
+ .cv-curtain-btn { padding: clamp(0.45rem, 1.5vw, 0.75rem) clamp(1rem, 3vw, 2rem); font-size: clamp(0.65rem, 1.5vw, 0.9rem); }
1536
+ .cv-visualizer-wrap { width: clamp(100px, 45vw, 220px); height: clamp(100px, 45vw, 220px); }
1537
+ .cv-title { font-size: clamp(0.9rem, 2vw, 1.5rem); gap: clamp(0.5rem, 1vw, 0.75rem); }
1538
+ .cv-controls { gap: clamp(1rem, 2vw, 1.5rem); padding-bottom: clamp(1rem, 2vw, 1.5rem); }
1539
+ .cv-pill { padding: clamp(0.2rem, 1vw, 0.4rem) clamp(0.5rem, 1.5vw, 0.75rem); font-size: clamp(0.65rem, 1.5vw, 0.75rem); }
1540
+ .cv-btn { padding: clamp(0.25rem, 1vw, 0.35rem) clamp(0.5rem, 1.5vw, 0.75rem); font-size: clamp(0.65rem, 1.5vw, 0.75rem); }
1541
+ .cv-error { padding: clamp(0.5rem, 1.5vw, 0.75rem) clamp(1rem, 2vw, 1.5rem); font-size: clamp(0.75rem, 1.5vw, 0.875rem); }
1542
+ }
1543
+
1544
+ @media (max-width: 480px) {
1545
+ .cv-panel { aspect-ratio: 3 / 4; max-height: 500px; border-radius: 20px; }
1546
+ .cv-curtain-content { padding: clamp(0.5rem, 2vw, 1rem); gap: clamp(0.4rem, 1.5vw, 0.75rem); }
1547
+ .cv-curtain-title { font-size: clamp(0.9rem, 4.5vw, 1.4rem); }
1548
+ .cv-curtain-desc { font-size: clamp(0.6rem, 2vw, 0.8rem); }
1549
+ .cv-curtain-btn { padding: clamp(0.4rem, 1.2vw, 0.6rem) clamp(0.8rem, 2.5vw, 1.5rem); font-size: clamp(0.6rem, 1.2vw, 0.8rem); }
1550
+ .cv-visualizer-wrap { width: clamp(80px, 40vw, 180px); height: clamp(80px, 40vw, 180px); }
1551
+ .cv-header { padding-top: clamp(1rem, 2vw, 1.5rem); }
1552
+ .cv-controls { gap: clamp(0.8rem, 1.5vw, 1.2rem); padding-bottom: clamp(0.8rem, 1.5vw, 1.2rem); }
1507
1553
  }
1508
1554
  `
1509
1555
  );
@@ -1822,27 +1868,31 @@ var ConversationalPanel = class {
1822
1868
  this.el.remove();
1823
1869
  }
1824
1870
  // ─── internal helpers ────────────────────────────────────
1871
+ fmt(s) {
1872
+ const m = Math.floor(s / 60).toString().padStart(2, "0");
1873
+ const sec = (s % 60).toString().padStart(2, "0");
1874
+ return `${m}:${sec}`;
1875
+ }
1825
1876
  startTimer() {
1826
1877
  let elapsed = 0;
1827
1878
  const maxDur = this.cfg.maxDuration || 300;
1828
1879
  const silentMax = this.cfg.silenceTimeout || 30;
1829
1880
  this._lastSpeechTime = Date.now();
1830
- this.timerEl.textContent = "00:00";
1881
+ this.timerEl.textContent = this.fmt(maxDur);
1831
1882
  this.timerTicker = window.setInterval(() => {
1832
1883
  elapsed++;
1833
- const m = Math.floor(elapsed / 60).toString().padStart(2, "0");
1834
- const s = (elapsed % 60).toString().padStart(2, "0");
1835
- this.timerEl.textContent = `${m}:${s}`;
1884
+ const remaining = Math.max(0, maxDur - elapsed);
1885
+ this.timerEl.textContent = this.fmt(remaining);
1836
1886
  if (elapsed >= maxDur) {
1837
1887
  this._locked = true;
1838
1888
  this.stop();
1839
- this.showError("Conversation time limit reached. Refresh to start a new one.");
1889
+ this.showError("Tiempo l\xEDmite alcanzado.");
1840
1890
  return;
1841
1891
  }
1842
1892
  if (Date.now() - this._lastSpeechTime > silentMax * 1e3) {
1843
1893
  this._locked = true;
1844
1894
  this.stop();
1845
- this.showError("Session ended due to inactivity. Refresh to start a new one.");
1895
+ this.showError("Sesi\xF3n finalizada por inactividad.");
1846
1896
  }
1847
1897
  }, 1e3);
1848
1898
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lokutor/sdk",
3
3
 
4
- "version": "1.1.37",
4
+ "version": "1.1.40",
5
5
  "description": "JavaScript/TypeScript SDK for Lokutor Real-time Voice AI",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",
@@ -3,7 +3,7 @@ const PANEL_CSS = /*css*/ `
3
3
  position: relative;
4
4
  width: 100%;
5
5
  aspect-ratio: 16 / 9;
6
- max-height: 800px;
6
+ max-height: clamp(400px, 50vh, 800px);
7
7
  display: flex;
8
8
  flex-direction: column;
9
9
  align-items: center;
@@ -12,7 +12,7 @@ const PANEL_CSS = /*css*/ `
12
12
  margin: 0;
13
13
  background: var(--cv-bg, #0a0a0a);
14
14
  box-shadow: inset 0 10px 40px rgba(0, 0, 0, 0.1), inset 0 0 100px rgba(0, 0, 0, 0.05);
15
- border-radius: 40px;
15
+ border-radius: clamp(20px, 5vw, 40px);
16
16
  overflow: hidden;
17
17
  }
18
18
  .cv-curtain {
@@ -152,35 +152,35 @@ const PANEL_CSS = /*css*/ `
152
152
  display: flex;
153
153
  flex-direction: column;
154
154
  align-items: center;
155
- gap: 1.5rem;
155
+ gap: clamp(1rem, 2vw, 1.5rem);
156
156
  margin-top: auto;
157
- padding-bottom: 1.5rem;
157
+ padding-bottom: clamp(1rem, 2vw, 1.5rem);
158
158
  z-index: 20;
159
159
  }
160
160
  .cv-pill {
161
161
  display: flex;
162
162
  align-items: center;
163
- gap: 0.75rem;
163
+ gap: clamp(0.5rem, 1vw, 0.75rem);
164
164
  background: var(--cv-ui-bg, rgba(255, 255, 255, 0.03));
165
165
  backdrop-filter: blur(30px);
166
166
  -webkit-backdrop-filter: blur(30px);
167
167
  border: 1px solid var(--cv-ui-border, rgba(255, 255, 255, 0.08));
168
- padding: 0.3rem 0.75rem;
168
+ padding: clamp(0.25rem, 0.5vw, 0.3rem) clamp(0.5rem, 1vw, 0.75rem);
169
169
  border-radius: 100px;
170
170
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.15);
171
171
  }
172
172
  .cv-btn {
173
173
  display: flex;
174
174
  align-items: center;
175
- gap: 0.5rem;
175
+ gap: clamp(0.35rem, 1vw, 0.5rem);
176
176
  background: transparent;
177
177
  border: none;
178
178
  color: var(--cv-text-dim, rgba(255,255,255,0.55));
179
179
  font-weight: 600;
180
- font-size: 0.75rem;
180
+ font-size: clamp(0.65rem, 1.5vw, 0.75rem);
181
181
  cursor: pointer;
182
182
  transition: all 0.2s ease;
183
- padding: 0.35rem 0.75rem;
183
+ padding: clamp(0.25rem, 0.5vw, 0.35rem) clamp(0.5rem, 1vw, 0.75rem);
184
184
  border-radius: 50px;
185
185
  }
186
186
  .cv-btn:hover { color: var(--cv-text, #fff); background: var(--cv-ui-bg, rgba(255,255,255,0.05)); }
@@ -194,31 +194,50 @@ const PANEL_CSS = /*css*/ `
194
194
  .cv-btn.is-muted { color: var(--cv-accent); }
195
195
  .cv-error {
196
196
  position: absolute;
197
- bottom: 1.25rem;
197
+ bottom: clamp(0.75rem, 2vw, 1.25rem);
198
198
  left: 50%;
199
199
  transform: translateX(-50%);
200
200
  background: #0a0a0a;
201
201
  color: #e0e0e0;
202
- padding: 0.75rem 1.5rem;
203
- border-radius: 12px;
204
- font-size: 0.875rem;
202
+ padding: clamp(0.5rem, 1vw, 0.75rem) clamp(1rem, 2vw, 1.5rem);
203
+ border-radius: clamp(8px, 1.5vw, 12px);
204
+ font-size: clamp(0.75rem, 1.5vw, 0.875rem);
205
205
  font-weight: 500;
206
206
  display: none;
207
207
  align-items: center;
208
- gap: 0.75rem;
208
+ gap: clamp(0.5rem, 1vw, 0.75rem);
209
209
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
210
210
  z-index: 1000;
211
211
  border: 1px solid rgba(255, 255, 255, 0.06);
212
212
  backdrop-filter: blur(20px);
213
+ max-width: clamp(280px, 90vw, 400px);
213
214
  }
214
215
  .cv-error.is-visible { display: flex; }
215
216
  .cv-error-icon { color: var(--cv-accent); flex-shrink: 0; }
216
- @media (max-width: 640px) {
217
- .cv-panel { aspect-ratio: 3 / 4; max-height: 600px; border-radius: 20px; }
218
- .cv-curtain-content { padding: 0.75rem; gap: 0.5rem; }
219
- .cv-curtain-title { font-size: clamp(1rem, 5vw, 1.6rem); }
220
- .cv-curtain-btn { padding: 0.5rem 1.25rem; font-size: clamp(0.7rem, 2vw, 0.85rem); }
221
- .cv-visualizer-wrap { width: clamp(100px, 50vw, 200px); height: clamp(100px, 50vw, 200px); }
217
+
218
+ @media (max-width: 768px) {
219
+ .cv-panel { max-height: 600px; border-radius: 24px; }
220
+ .cv-curtain-content { padding: clamp(0.75rem, 3vw, 1.5rem); gap: clamp(0.5rem, 2vw, 1rem); }
221
+ .cv-curtain-title { font-size: clamp(1rem, 5vw, 1.8rem); }
222
+ .cv-curtain-desc { font-size: clamp(0.65rem, 2.5vw, 0.95rem); max-width: 90vw; }
223
+ .cv-curtain-btn { padding: clamp(0.45rem, 1.5vw, 0.75rem) clamp(1rem, 3vw, 2rem); font-size: clamp(0.65rem, 1.5vw, 0.9rem); }
224
+ .cv-visualizer-wrap { width: clamp(100px, 45vw, 220px); height: clamp(100px, 45vw, 220px); }
225
+ .cv-title { font-size: clamp(0.9rem, 2vw, 1.5rem); gap: clamp(0.5rem, 1vw, 0.75rem); }
226
+ .cv-controls { gap: clamp(1rem, 2vw, 1.5rem); padding-bottom: clamp(1rem, 2vw, 1.5rem); }
227
+ .cv-pill { padding: clamp(0.2rem, 1vw, 0.4rem) clamp(0.5rem, 1.5vw, 0.75rem); font-size: clamp(0.65rem, 1.5vw, 0.75rem); }
228
+ .cv-btn { padding: clamp(0.25rem, 1vw, 0.35rem) clamp(0.5rem, 1.5vw, 0.75rem); font-size: clamp(0.65rem, 1.5vw, 0.75rem); }
229
+ .cv-error { padding: clamp(0.5rem, 1.5vw, 0.75rem) clamp(1rem, 2vw, 1.5rem); font-size: clamp(0.75rem, 1.5vw, 0.875rem); }
230
+ }
231
+
232
+ @media (max-width: 480px) {
233
+ .cv-panel { aspect-ratio: 3 / 4; max-height: 500px; border-radius: 20px; }
234
+ .cv-curtain-content { padding: clamp(0.5rem, 2vw, 1rem); gap: clamp(0.4rem, 1.5vw, 0.75rem); }
235
+ .cv-curtain-title { font-size: clamp(0.9rem, 4.5vw, 1.4rem); }
236
+ .cv-curtain-desc { font-size: clamp(0.6rem, 2vw, 0.8rem); }
237
+ .cv-curtain-btn { padding: clamp(0.4rem, 1.2vw, 0.6rem) clamp(0.8rem, 2.5vw, 1.5rem); font-size: clamp(0.6rem, 1.2vw, 0.8rem); }
238
+ .cv-visualizer-wrap { width: clamp(80px, 40vw, 180px); height: clamp(80px, 40vw, 180px); }
239
+ .cv-header { padding-top: clamp(1rem, 2vw, 1.5rem); }
240
+ .cv-controls { gap: clamp(0.8rem, 1.5vw, 1.2rem); padding-bottom: clamp(0.8rem, 1.5vw, 1.2rem); }
222
241
  }
223
242
  `;
224
243
 
@@ -601,31 +620,34 @@ export class ConversationalPanel {
601
620
 
602
621
  // ─── internal helpers ────────────────────────────────────
603
622
 
623
+ private fmt(s: number): string {
624
+ const m = Math.floor(s / 60).toString().padStart(2, '0');
625
+ const sec = (s % 60).toString().padStart(2, '0');
626
+ return `${m}:${sec}`;
627
+ }
628
+
604
629
  private startTimer() {
605
630
  let elapsed = 0;
606
631
  const maxDur = this.cfg.maxDuration || 300;
607
632
  const silentMax = this.cfg.silenceTimeout || 30;
608
633
  this._lastSpeechTime = Date.now();
609
- this.timerEl.textContent = '00:00';
634
+ this.timerEl.textContent = this.fmt(maxDur);
610
635
  this.timerTicker = window.setInterval(() => {
611
636
  elapsed++;
612
- const m = Math.floor(elapsed / 60).toString().padStart(2, '0');
613
- const s = (elapsed % 60).toString().padStart(2, '0');
614
- this.timerEl.textContent = `${m}:${s}`;
637
+ const remaining = Math.max(0, maxDur - elapsed);
638
+ this.timerEl.textContent = this.fmt(remaining);
615
639
 
616
- // Check max duration
617
640
  if (elapsed >= maxDur) {
618
641
  this._locked = true;
619
642
  this.stop();
620
- this.showError('Conversation time limit reached. Refresh to start a new one.');
643
+ this.showError('Tiempo límite alcanzado.');
621
644
  return;
622
645
  }
623
646
 
624
- // Check silence timeout
625
647
  if (Date.now() - this._lastSpeechTime > silentMax * 1000) {
626
648
  this._locked = true;
627
649
  this.stop();
628
- this.showError('Session ended due to inactivity. Refresh to start a new one.');
650
+ this.showError('Sesión finalizada por inactividad.');
629
651
  }
630
652
  }, 1000);
631
653
  }
package/src/types.ts CHANGED
@@ -24,8 +24,35 @@ export enum Language {
24
24
  ENGLISH = "en",
25
25
  SPANISH = "es",
26
26
  FRENCH = "fr",
27
+ GERMAN = "de",
28
+ ITALIAN = "it",
27
29
  PORTUGUESE = "pt",
30
+ JAPANESE = "ja",
28
31
  KOREAN = "ko",
32
+ CHINESE = "zh",
33
+ ARABIC = "ar",
34
+ BULGARIAN = "bg",
35
+ CROATIAN = "hr",
36
+ CZECH = "cs",
37
+ DANISH = "da",
38
+ DUTCH = "nl",
39
+ ESTONIAN = "et",
40
+ FINNISH = "fi",
41
+ GREEK = "el",
42
+ HINDI = "hi",
43
+ HUNGARIAN = "hu",
44
+ INDONESIAN = "id",
45
+ LATVIAN = "lv",
46
+ LITHUANIAN = "lt",
47
+ POLISH = "pl",
48
+ ROMANIAN = "ro",
49
+ RUSSIAN = "ru",
50
+ SLOVAK = "sk",
51
+ SLOVENIAN = "sl",
52
+ SWEDISH = "sv",
53
+ TURKISH = "tr",
54
+ UKRAINIAN = "uk",
55
+ VIETNAMESE = "vi",
29
56
  }
30
57
 
31
58
  /**