@lokutor/sdk 1.1.19 → 1.1.20

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.mjs CHANGED
@@ -1206,9 +1206,584 @@ async function simpleTTS(options) {
1206
1206
  const client = new TTSClient({ apiKey: options.apiKey });
1207
1207
  return client.synthesize(options);
1208
1208
  }
1209
+
1210
+ // src/conversational-panel.ts
1211
+ var PANEL_CSS = (
1212
+ /*css*/
1213
+ `
1214
+ .cv-panel {
1215
+ position: relative;
1216
+ width: 100%;
1217
+ aspect-ratio: 16 / 9;
1218
+ max-height: 800px;
1219
+ display: flex;
1220
+ flex-direction: column;
1221
+ align-items: center;
1222
+ justify-content: center;
1223
+ padding: 2rem 0;
1224
+ margin: 0;
1225
+ background: var(--cv-bg, #0a0a0a);
1226
+ box-shadow: inset 0 10px 40px rgba(0, 0, 0, 0.1), inset 0 0 100px rgba(0, 0, 0, 0.05);
1227
+ border-radius: 40px;
1228
+ overflow: hidden;
1229
+ container-type: inline-size;
1230
+ }
1231
+ .cv-curtain {
1232
+ position: absolute;
1233
+ inset: 0;
1234
+ display: flex;
1235
+ flex-direction: column;
1236
+ align-items: center;
1237
+ justify-content: center;
1238
+ background: var(--cv-bg, #0a0a0a);
1239
+ z-index: 100;
1240
+ transition: transform 1s cubic-bezier(0.16, 1, 0.3, 1);
1241
+ }
1242
+ .cv-curtain .cv-curtain-bg {
1243
+ position: absolute;
1244
+ inset: 0;
1245
+ background: url('/background_gradient.jpeg') center / cover no-repeat;
1246
+ z-index: -1;
1247
+ }
1248
+ .cv-curtain .cv-curtain-overlay {
1249
+ position: absolute;
1250
+ inset: 0;
1251
+ background: var(--cv-accent);
1252
+ opacity: 0.35;
1253
+ z-index: -1;
1254
+ }
1255
+ .cv-curtain.is-up { transform: translateY(-100%); }
1256
+ .cv-curtain-content {
1257
+ display: flex;
1258
+ flex-direction: column;
1259
+ align-items: center;
1260
+ gap: clamp(0.75rem, 1.5cqi, 1.5rem);
1261
+ color: #fff;
1262
+ text-align: center;
1263
+ z-index: 2;
1264
+ padding: clamp(1rem, 2cqi, 2rem);
1265
+ }
1266
+ .cv-curtain-title {
1267
+ font-size: clamp(1.2rem, 4cqi, 2.5rem);
1268
+ font-weight: 800;
1269
+ letter-spacing: -0.03em;
1270
+ margin: 0;
1271
+ text-shadow: 0 4px 20px rgba(0,0,0,0.3);
1272
+ }
1273
+ .cv-curtain-desc {
1274
+ font-size: clamp(0.75rem, 2cqi, 1.1rem);
1275
+ opacity: 0.8;
1276
+ max-width: 400px;
1277
+ margin: 0;
1278
+ line-height: 1.5;
1279
+ }
1280
+ .cv-curtain-btn {
1281
+ margin-top: 1rem;
1282
+ padding: clamp(0.6rem, 1.5cqi, 1rem) clamp(1.5rem, 3cqi, 2.5rem);
1283
+ border-radius: 100px;
1284
+ background: #fff;
1285
+ color: #000;
1286
+ border: none;
1287
+ font-weight: 700;
1288
+ font-size: clamp(0.8rem, 1.5cqi, 1rem);
1289
+ display: flex;
1290
+ align-items: center;
1291
+ gap: 0.75rem;
1292
+ cursor: pointer;
1293
+ transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
1294
+ box-shadow: 0 10px 30px rgba(0,0,0,0.2);
1295
+ }
1296
+ .cv-curtain-btn:hover {
1297
+ transform: scale(1.05);
1298
+ background: #f0f0f0;
1299
+ box-shadow: 0 15px 40px rgba(0,0,0,0.3);
1300
+ }
1301
+ .cv-curtain-btn svg { transition: transform 0.3s ease; }
1302
+ .cv-curtain-btn:hover svg { transform: translateX(4px); }
1303
+ .cv-header {
1304
+ width: 100%;
1305
+ display: flex;
1306
+ flex-direction: column;
1307
+ align-items: center;
1308
+ gap: 0.25rem;
1309
+ z-index: 20;
1310
+ margin-bottom: auto;
1311
+ }
1312
+ .cv-title {
1313
+ font-size: clamp(1rem, 2.5cqi, 1.75rem);
1314
+ font-weight: 700;
1315
+ color: #e0e0e0;
1316
+ display: flex;
1317
+ align-items: center;
1318
+ gap: 1rem;
1319
+ letter-spacing: -0.02em;
1320
+ }
1321
+ .cv-title .cv-timer {
1322
+ font-variant-numeric: tabular-nums;
1323
+ color: var(--cv-accent);
1324
+ font-weight: 400;
1325
+ opacity: 0.8;
1326
+ }
1327
+ .cv-visualizer-wrap {
1328
+ position: absolute;
1329
+ top: 50%;
1330
+ left: 50%;
1331
+ transform: translate(-50%, -50%);
1332
+ width: clamp(140px, 40cqi, 280px);
1333
+ height: clamp(140px, 40cqi, 280px);
1334
+ display: flex;
1335
+ align-items: center;
1336
+ justify-content: center;
1337
+ z-index: 10;
1338
+ transition: all 0.5s cubic-bezier(0.16, 1, 0.3, 1);
1339
+ }
1340
+ .cv-is-speaking .cv-visualizer-wrap {
1341
+ animation: cv-pulse 2.5s infinite ease-in-out;
1342
+ }
1343
+ .cv-is-thinking .cv-visualizer-wrap {
1344
+ opacity: 0.5;
1345
+ transform: translate(-50%, -50%) scale(0.9);
1346
+ }
1347
+ @keyframes cv-pulse {
1348
+ 0%, 100% { transform: translate(-50%, -50%) scale(1); }
1349
+ 50% { transform: translate(-50%, -50%) scale(1.05); }
1350
+ }
1351
+ .cv-canvas {
1352
+ width: 100% !important;
1353
+ height: 100% !important;
1354
+ position: relative;
1355
+ z-index: 0;
1356
+ }
1357
+ .cv-canvas {
1358
+ width: 100% !important;
1359
+ height: 100% !important;
1360
+ position: relative;
1361
+ z-index: 0;
1362
+ }
1363
+ .cv-controls {
1364
+ width: 100%;
1365
+ display: flex;
1366
+ flex-direction: column;
1367
+ align-items: center;
1368
+ gap: 1.5rem;
1369
+ padding-top: 1rem;
1370
+ margin-top: auto;
1371
+ z-index: 20;
1372
+ }
1373
+ .cv-pill {
1374
+ display: flex;
1375
+ align-items: center;
1376
+ gap: 0.75rem;
1377
+ background: rgba(255, 255, 255, 0.03);
1378
+ backdrop-filter: blur(30px);
1379
+ -webkit-backdrop-filter: blur(30px);
1380
+ border: 1px solid rgba(255, 255, 255, 0.08);
1381
+ padding: 0.3rem 0.75rem;
1382
+ border-radius: 100px;
1383
+ box-shadow: 0 20px 50px rgba(0, 0, 0, 0.3);
1384
+ }
1385
+ .cv-btn {
1386
+ display: flex;
1387
+ align-items: center;
1388
+ gap: 0.5rem;
1389
+ background: transparent;
1390
+ border: none;
1391
+ color: rgba(255,255,255,0.55);
1392
+ font-weight: 600;
1393
+ font-size: 0.75rem;
1394
+ cursor: pointer;
1395
+ transition: all 0.2s ease;
1396
+ padding: 0.35rem 0.75rem;
1397
+ border-radius: 50px;
1398
+ }
1399
+ .cv-btn:hover { color: #fff; background: rgba(255,255,255,0.05); }
1400
+ .cv-btn--end { color: #ff4444; }
1401
+ .cv-btn--end .cv-btn-box {
1402
+ background: #ff4444;
1403
+ width: 8px;
1404
+ height: 8px;
1405
+ border-radius: 2px;
1406
+ }
1407
+ .cv-btn.is-muted { color: var(--cv-accent); }
1408
+ .cv-error {
1409
+ position: absolute;
1410
+ bottom: 1.25rem;
1411
+ left: 50%;
1412
+ transform: translateX(-50%);
1413
+ background: #0a0a0a;
1414
+ color: #e0e0e0;
1415
+ padding: 0.75rem 1.5rem;
1416
+ border-radius: 12px;
1417
+ font-size: 0.875rem;
1418
+ font-weight: 500;
1419
+ display: none;
1420
+ align-items: center;
1421
+ gap: 0.75rem;
1422
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
1423
+ z-index: 1000;
1424
+ border: 1px solid rgba(255, 255, 255, 0.06);
1425
+ backdrop-filter: blur(20px);
1426
+ }
1427
+ .cv-error.is-visible { display: flex; }
1428
+ .cv-error-icon { color: var(--cv-accent); flex-shrink: 0; }
1429
+ `
1430
+ );
1431
+ var styleInjected = false;
1432
+ function injectStyles() {
1433
+ if (styleInjected) return;
1434
+ const style = document.createElement("style");
1435
+ style.textContent = PANEL_CSS;
1436
+ document.head.appendChild(style);
1437
+ styleInjected = true;
1438
+ }
1439
+ var ConversationalPanel = class {
1440
+ cfg;
1441
+ container;
1442
+ agent = null;
1443
+ visualizer = null;
1444
+ timerTicker = null;
1445
+ connectingSound = null;
1446
+ connectingFadeTimer = null;
1447
+ isRunning = false;
1448
+ // Cached DOM refs
1449
+ el;
1450
+ curtain;
1451
+ curtainTitle;
1452
+ curtainDesc;
1453
+ startBtn;
1454
+ errorEl;
1455
+ errorText;
1456
+ timerEl;
1457
+ canvas;
1458
+ muteBtn;
1459
+ stopBtn;
1460
+ visualizerWrap;
1461
+ // Callbacks
1462
+ onTranscription;
1463
+ onResponse;
1464
+ onStart;
1465
+ onStop;
1466
+ onError;
1467
+ constructor(cfg) {
1468
+ this.cfg = cfg;
1469
+ this.container = cfg.container;
1470
+ injectStyles();
1471
+ this.buildDOM();
1472
+ }
1473
+ buildDOM() {
1474
+ const accent = this.cfg.accentColor || "#a25a6b";
1475
+ const bg = this.cfg.backgroundColor || "#0a0a0a";
1476
+ this.container.style.setProperty("--cv-accent", accent);
1477
+ this.container.style.setProperty("--cv-bg", bg);
1478
+ this.el = document.createElement("div");
1479
+ this.el.className = "cv-panel";
1480
+ this.el.innerHTML = `
1481
+ <div class="cv-curtain">
1482
+ <div class="cv-curtain-bg"></div>
1483
+ <div class="cv-curtain-overlay"></div>
1484
+ <div class="cv-curtain-content">
1485
+ <h3 class="cv-curtain-title">${this.esc(this.cfg.title)}</h3>
1486
+ <p class="cv-curtain-desc">${this.esc(this.cfg.description)}</p>
1487
+ <button class="cv-curtain-btn">
1488
+ <span>Start Conversation</span>
1489
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" width="20" height="20">
1490
+ <path d="M5 12h14M12 5l7 7-7 7"/>
1491
+ </svg>
1492
+ </button>
1493
+ </div>
1494
+ <div class="cv-error">
1495
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
1496
+ width="20" height="20" class="cv-error-icon">
1497
+ <circle cx="12" cy="12" r="10"></circle>
1498
+ <path d="M12 8v4"></path>
1499
+ <path d="M12 16h.01"></path>
1500
+ </svg>
1501
+ <span class="cv-error-text"></span>
1502
+ </div>
1503
+ </div>
1504
+ <div class="cv-header">
1505
+ <h2 class="cv-title">${this.esc(this.cfg.title)} <span class="cv-timer">00:00</span></h2>
1506
+ </div>
1507
+ <div class="cv-visualizer-wrap">
1508
+ <canvas class="cv-canvas"></canvas>
1509
+ </div>
1510
+ <div class="cv-controls">
1511
+ <div class="cv-pill">
1512
+ <button class="cv-btn cv-btn--mute">
1513
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" width="18" height="18">
1514
+ <path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/>
1515
+ <path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
1516
+ </svg>
1517
+ <span>Mute</span>
1518
+ </button>
1519
+ <button class="cv-btn cv-btn--end">
1520
+ <div class="cv-btn-box"></div>
1521
+ <span>End call</span>
1522
+ </button>
1523
+ </div>
1524
+ </div>
1525
+ `;
1526
+ this.container.appendChild(this.el);
1527
+ this.curtain = this.el.querySelector(".cv-curtain");
1528
+ if (this.cfg.curtainBgSrc) {
1529
+ this.curtain.style.backgroundImage = `url('${this.cfg.curtainBgSrc}')`;
1530
+ }
1531
+ this.curtainTitle = this.el.querySelector(".cv-curtain-title");
1532
+ this.curtainDesc = this.el.querySelector(".cv-curtain-desc");
1533
+ this.startBtn = this.el.querySelector(".cv-curtain-btn");
1534
+ this.errorEl = this.el.querySelector(".cv-error");
1535
+ this.errorText = this.el.querySelector(".cv-error-text");
1536
+ this.timerEl = this.el.querySelector(".cv-timer");
1537
+ this.canvas = this.el.querySelector(".cv-canvas");
1538
+ this.muteBtn = this.el.querySelector(".cv-btn--mute");
1539
+ this.stopBtn = this.el.querySelector(".cv-btn--end");
1540
+ this.visualizerWrap = this.el.querySelector(".cv-visualizer-wrap");
1541
+ this.startBtn.addEventListener("click", () => this.start());
1542
+ this.stopBtn.addEventListener("click", () => this.stop());
1543
+ this.muteBtn.addEventListener("click", () => this.toggleMute());
1544
+ }
1545
+ esc(s) {
1546
+ const d = document.createElement("div");
1547
+ d.textContent = s;
1548
+ return d.innerHTML;
1549
+ }
1550
+ /** Start the conversation (called when user clicks "Start Conversation" or externally) */
1551
+ async start() {
1552
+ if (this.isRunning) return;
1553
+ this.isRunning = true;
1554
+ this.startBtn.disabled = true;
1555
+ this.errorEl.classList.remove("is-visible");
1556
+ this.playConnectingSound();
1557
+ try {
1558
+ const ConvoAgent = this.cfg.ConvoAgent;
1559
+ const SphereVisualizer = this.cfg.SphereVisualizer;
1560
+ const visualizer = new SphereVisualizer(this.canvas);
1561
+ visualizer.resize(280, 280);
1562
+ this.visualizer = visualizer;
1563
+ if (this.cfg.accentColor && typeof visualizer.setAccentColor === "function") {
1564
+ const hex = this.cfg.accentColor;
1565
+ visualizer.setAccentColor(
1566
+ parseInt(hex.slice(1, 3), 16) / 255,
1567
+ parseInt(hex.slice(3, 5), 16) / 255,
1568
+ parseInt(hex.slice(5, 7), 16) / 255
1569
+ );
1570
+ }
1571
+ const agentHandle = new ConvoAgent({
1572
+ apiKey: this.cfg.apiKey,
1573
+ prompt: this.cfg.prompt,
1574
+ voice: this.cfg.voice || "M1",
1575
+ language: this.cfg.language || "en",
1576
+ onStatusChange: (status) => {
1577
+ this.el.classList.remove("cv-is-speaking", "cv-is-thinking");
1578
+ if (status === "speaking") this.el.classList.add("cv-is-speaking");
1579
+ else if (status === "thinking") this.el.classList.add("cv-is-thinking");
1580
+ },
1581
+ onTranscription: (text) => {
1582
+ this.onTranscription?.(text);
1583
+ },
1584
+ onResponse: (text) => {
1585
+ this.onResponse?.(text);
1586
+ },
1587
+ onError: (err) => {
1588
+ if (!this.isRunning) return;
1589
+ this.onError?.(err);
1590
+ this.stop();
1591
+ this.showError("Connection issue. Try again in a moment.");
1592
+ }
1593
+ });
1594
+ this.agent = agentHandle;
1595
+ visualizer.setAudioClient(agentHandle);
1596
+ this.curtain.classList.add("is-up");
1597
+ try {
1598
+ agentHandle.unlockAudioForMobile?.();
1599
+ } catch (_) {
1600
+ }
1601
+ const ok = await agentHandle.connect();
1602
+ if (ok) {
1603
+ this.fadeOutConnectingSound();
1604
+ const wrap = this.visualizerWrap;
1605
+ if (wrap) {
1606
+ const dpr = Math.min(window.devicePixelRatio || 1, 2);
1607
+ const w = Math.floor(wrap.clientWidth * dpr);
1608
+ const h = Math.floor(wrap.clientHeight * dpr);
1609
+ visualizer.resize(w, h);
1610
+ }
1611
+ visualizer.start();
1612
+ this.startTimer();
1613
+ this.onStart?.();
1614
+ if (window.lucide) window.lucide.createIcons();
1615
+ } else {
1616
+ this.fadeOutConnectingSound();
1617
+ this.playErrorTone();
1618
+ this.curtain.classList.remove("is-up");
1619
+ this.showError("Could not connect. Please try again.");
1620
+ this.isRunning = false;
1621
+ }
1622
+ } catch (err) {
1623
+ this.fadeOutConnectingSound();
1624
+ this.playErrorTone();
1625
+ console.error("ConversationalPanel start error:", err);
1626
+ this.stop();
1627
+ this.showError("Something went wrong. Please try again.");
1628
+ } finally {
1629
+ this.startBtn.disabled = false;
1630
+ }
1631
+ }
1632
+ /** Stop / disconnect the conversation */
1633
+ stop() {
1634
+ if (!this.isRunning && !this.agent && !this.visualizer) return;
1635
+ this.isRunning = false;
1636
+ this.fadeOutConnectingSound();
1637
+ this.errorEl.classList.remove("is-visible");
1638
+ if (this.agent) {
1639
+ try {
1640
+ this.agent.disconnect();
1641
+ } catch (_) {
1642
+ }
1643
+ this.agent = null;
1644
+ }
1645
+ if (this.timerTicker) {
1646
+ clearInterval(this.timerTicker);
1647
+ this.timerTicker = null;
1648
+ }
1649
+ if (this.visualizer) {
1650
+ this.visualizer.stop();
1651
+ this.visualizer = null;
1652
+ }
1653
+ this.curtain.classList.remove("is-up");
1654
+ this.el.classList.remove("cv-is-speaking", "cv-is-thinking");
1655
+ this.muteBtn.classList.remove("is-muted");
1656
+ const span = this.muteBtn.querySelector("span");
1657
+ if (span) span.textContent = "Mute";
1658
+ this.onStop?.();
1659
+ this.startBtn.disabled = false;
1660
+ }
1661
+ /** Update accent color dynamically */
1662
+ setColor(color) {
1663
+ this.cfg.accentColor = color;
1664
+ this.container.style.setProperty("--cv-accent", color);
1665
+ if (this.visualizer && typeof this.visualizer.setAccentColor === "function") {
1666
+ const r = parseInt(color.slice(1, 3), 16) / 255;
1667
+ const g = parseInt(color.slice(3, 5), 16) / 255;
1668
+ const b = parseInt(color.slice(5, 7), 16) / 255;
1669
+ this.visualizer.setAccentColor(r, g, b);
1670
+ }
1671
+ }
1672
+ /** Update title text */
1673
+ setTitle(title) {
1674
+ this.cfg.title = title;
1675
+ this.curtainTitle.textContent = title;
1676
+ const h2 = this.el.querySelector(".cv-title");
1677
+ if (h2) h2.innerHTML = `${this.esc(title)} <span class="cv-timer">${this.timerEl?.textContent || "00:00"}</span>`;
1678
+ }
1679
+ /** Update description text */
1680
+ setDescription(desc) {
1681
+ this.cfg.description = desc;
1682
+ this.curtainDesc.textContent = desc;
1683
+ }
1684
+ /** Update prompt (only takes effect on next start()) */
1685
+ setPrompt(prompt) {
1686
+ this.cfg.prompt = prompt;
1687
+ }
1688
+ /** Show an error message */
1689
+ showError(text) {
1690
+ this.errorText.textContent = text;
1691
+ this.errorEl.classList.add("is-visible");
1692
+ setTimeout(() => {
1693
+ this.errorEl.classList.remove("is-visible");
1694
+ }, 5e3);
1695
+ }
1696
+ /** Destroy the component, removing all DOM and stopping any active session */
1697
+ destroy() {
1698
+ this.stop();
1699
+ this.el.remove();
1700
+ }
1701
+ // ─── internal helpers ────────────────────────────────────
1702
+ startTimer() {
1703
+ let elapsed = 0;
1704
+ this.timerEl.textContent = "00:00";
1705
+ this.timerTicker = window.setInterval(() => {
1706
+ elapsed++;
1707
+ const m = Math.floor(elapsed / 60).toString().padStart(2, "0");
1708
+ const s = (elapsed % 60).toString().padStart(2, "0");
1709
+ this.timerEl.textContent = `${m}:${s}`;
1710
+ }, 1e3);
1711
+ }
1712
+ toggleMute() {
1713
+ if (!this.agent) return;
1714
+ try {
1715
+ const muted = this.agent.toggleMute();
1716
+ this.muteBtn.classList.toggle("is-muted", muted);
1717
+ const span = this.muteBtn.querySelector("span");
1718
+ if (span) span.textContent = muted ? "Unmute" : "Mute";
1719
+ if (muted) {
1720
+ this.agent.pauseAudio?.();
1721
+ this.visualizer?.setDeactivated?.(true);
1722
+ } else {
1723
+ this.agent.resumeAudio?.();
1724
+ this.visualizer?.setDeactivated?.(false);
1725
+ }
1726
+ } catch (_) {
1727
+ }
1728
+ }
1729
+ playConnectingSound() {
1730
+ this.fadeOutConnectingSound();
1731
+ const src = this.cfg.connectingSoundSrc || "/connecting.mp3";
1732
+ this.connectingSound = new Audio(src);
1733
+ this.connectingSound.volume = 0.6;
1734
+ this.connectingSound.play().catch(() => {
1735
+ });
1736
+ }
1737
+ fadeOutConnectingSound() {
1738
+ if (this.connectingFadeTimer) {
1739
+ clearInterval(this.connectingFadeTimer);
1740
+ this.connectingFadeTimer = null;
1741
+ }
1742
+ if (!this.connectingSound) return;
1743
+ const startVol = this.connectingSound.volume;
1744
+ const steps = 15;
1745
+ let step = 0;
1746
+ this.connectingFadeTimer = window.setInterval(() => {
1747
+ step++;
1748
+ const progress = step / steps;
1749
+ if (this.connectingSound) this.connectingSound.volume = startVol * Math.max(0, 1 - progress);
1750
+ if (step >= steps) {
1751
+ if (this.connectingFadeTimer) {
1752
+ clearInterval(this.connectingFadeTimer);
1753
+ this.connectingFadeTimer = null;
1754
+ }
1755
+ if (this.connectingSound) {
1756
+ this.connectingSound.pause();
1757
+ this.connectingSound.currentTime = 0;
1758
+ this.connectingSound = null;
1759
+ }
1760
+ }
1761
+ }, 100);
1762
+ }
1763
+ playErrorTone() {
1764
+ try {
1765
+ const ctx = new (window.AudioContext || window.webkitAudioContext)();
1766
+ const now = ctx.currentTime;
1767
+ [0, 0.3].forEach((offset, i) => {
1768
+ const osc = ctx.createOscillator();
1769
+ const gain = ctx.createGain();
1770
+ osc.connect(gain);
1771
+ gain.connect(ctx.destination);
1772
+ osc.type = "sine";
1773
+ osc.frequency.setValueAtTime(600 - i * 200, now + offset);
1774
+ gain.gain.setValueAtTime(0.25, now + offset);
1775
+ gain.gain.exponentialRampToValueAtTime(1e-3, now + offset + 0.35);
1776
+ osc.start(now + offset);
1777
+ osc.stop(now + offset + 0.4);
1778
+ });
1779
+ } catch (_) {
1780
+ }
1781
+ }
1782
+ };
1209
1783
  export {
1210
1784
  AUDIO_CONFIG,
1211
1785
  BrowserAudioManager,
1786
+ ConversationalPanel,
1212
1787
  DEFAULT_URLS,
1213
1788
  Language,
1214
1789
  LokutorError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lokutor/sdk",
3
- "version": "1.1.19",
3
+ "version": "1.1.20",
4
4
  "description": "JavaScript/TypeScript SDK for Lokutor Real-time Voice AI",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",