@lokutor/sdk 1.1.30 → 1.1.32

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
@@ -657,6 +657,8 @@ declare class ConversationalPanel {
657
657
  stop(): void;
658
658
  /** Update accent color dynamically */
659
659
  setColor(color: string): void;
660
+ /** Update background color dynamically and ensure text/UI contrast */
661
+ setBackgroundColor(color: string): void;
660
662
  /** Update title text */
661
663
  setTitle(title: string): void;
662
664
  /** Update description text */
package/dist/index.d.ts CHANGED
@@ -657,6 +657,8 @@ declare class ConversationalPanel {
657
657
  stop(): void;
658
658
  /** Update accent color dynamically */
659
659
  setColor(color: string): void;
660
+ /** Update background color dynamically and ensure text/UI contrast */
661
+ setBackgroundColor(color: string): void;
660
662
  /** Update title text */
661
663
  setTitle(title: string): void;
662
664
  /** Update description text */
package/dist/index.js CHANGED
@@ -889,9 +889,11 @@ var VoiceAgentClient = class {
889
889
  });
890
890
  if (msg.role === "user") {
891
891
  if (this.onTranscription) this.onTranscription(msg.data);
892
+ this.emit("transcription", msg.data);
892
893
  console.log(`\u{1F4AC} You: ${msg.data}`);
893
894
  } else {
894
895
  if (this.onResponse) this.onResponse(msg.data);
896
+ this.emit("response", msg.data);
895
897
  console.log(`\u{1F916} Agent: ${msg.data}`);
896
898
  }
897
899
  break;
@@ -908,6 +910,7 @@ var VoiceAgentClient = class {
908
910
  this.audioManager.stopPlayback();
909
911
  }
910
912
  if (this.onStatus) this.onStatus(msg.data);
913
+ this.emit("status", msg.data);
911
914
  const icons = {
912
915
  "interrupted": "\u26A1",
913
916
  "thinking": "\u{1F9E0}",
@@ -945,10 +948,12 @@ var VoiceAgentClient = class {
945
948
  retryable: backendRetryable
946
949
  });
947
950
  if (this.onError) this.onError(error);
951
+ this.emit("error", error);
948
952
  console.error(`\u274C Server error: [${error.code}] ${error.message}`);
949
953
  break;
950
954
  }
951
955
  case "tool_call":
956
+ this.emit("tool_call", { name: msg.name, arguments: msg.arguments });
952
957
  console.log(`\u{1F6E0}\uFE0F Tool Call: ${msg.name}(${msg.arguments})`);
953
958
  this.emit("tool_call", msg);
954
959
  break;
@@ -1305,7 +1310,7 @@ var PANEL_CSS = (
1305
1310
  flex-direction: column;
1306
1311
  align-items: center;
1307
1312
  gap: clamp(0.75rem, 1.5vw, 1.5rem);
1308
- color: #fff;
1313
+ color: var(--cv-text, #fff);
1309
1314
  text-align: center;
1310
1315
  z-index: 2;
1311
1316
  padding: clamp(1rem, 2vw, 2rem);
@@ -1315,11 +1320,12 @@ var PANEL_CSS = (
1315
1320
  font-weight: 800;
1316
1321
  letter-spacing: -0.03em;
1317
1322
  margin: 0;
1318
- text-shadow: 0 4px 20px rgba(0,0,0,0.3);
1323
+ text-shadow: 0 4px 20px rgba(0,0,0,0.1);
1319
1324
  }
1320
1325
  .cv-curtain-desc {
1321
1326
  font-size: clamp(0.75rem, 2vw, 1.1rem);
1322
1327
  opacity: 0.8;
1328
+ color: var(--cv-text-dim, rgba(255,255,255,0.8));
1323
1329
  max-width: 400px;
1324
1330
  margin: 0;
1325
1331
  line-height: 1.5;
@@ -1328,8 +1334,8 @@ var PANEL_CSS = (
1328
1334
  margin-top: 1rem;
1329
1335
  padding: clamp(0.6rem, 1.5vw, 1rem) clamp(1.5rem, 3vw, 2.5rem);
1330
1336
  border-radius: 100px;
1331
- background: #fff;
1332
- color: #000;
1337
+ background: var(--cv-text, #fff);
1338
+ color: var(--cv-bg, #000);
1333
1339
  border: none;
1334
1340
  font-weight: 700;
1335
1341
  font-size: clamp(0.8rem, 1.5vw, 1rem);
@@ -1338,12 +1344,12 @@ var PANEL_CSS = (
1338
1344
  gap: 0.75rem;
1339
1345
  cursor: pointer;
1340
1346
  transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
1341
- box-shadow: 0 10px 30px rgba(0,0,0,0.2);
1347
+ box-shadow: 0 10px 30px rgba(0,0,0,0.15);
1342
1348
  }
1343
1349
  .cv-curtain-btn:hover {
1344
1350
  transform: scale(1.05);
1345
- background: #f0f0f0;
1346
- box-shadow: 0 15px 40px rgba(0,0,0,0.3);
1351
+ opacity: 0.9;
1352
+ box-shadow: 0 15px 40px rgba(0,0,0,0.2);
1347
1353
  }
1348
1354
  .cv-curtain-btn svg { transition: transform 0.3s ease; }
1349
1355
  .cv-curtain-btn:hover svg { transform: translateX(4px); }
@@ -1359,7 +1365,7 @@ var PANEL_CSS = (
1359
1365
  .cv-title {
1360
1366
  font-size: clamp(1rem, 2.5vw, 1.75rem);
1361
1367
  font-weight: 700;
1362
- color: #e0e0e0;
1368
+ color: var(--cv-text, #e0e0e0);
1363
1369
  display: flex;
1364
1370
  align-items: center;
1365
1371
  gap: 1rem;
@@ -1420,13 +1426,13 @@ var PANEL_CSS = (
1420
1426
  display: flex;
1421
1427
  align-items: center;
1422
1428
  gap: 0.75rem;
1423
- background: rgba(255, 255, 255, 0.03);
1429
+ background: var(--cv-ui-bg, rgba(255, 255, 255, 0.03));
1424
1430
  backdrop-filter: blur(30px);
1425
1431
  -webkit-backdrop-filter: blur(30px);
1426
- border: 1px solid rgba(255, 255, 255, 0.08);
1432
+ border: 1px solid var(--cv-ui-border, rgba(255, 255, 255, 0.08));
1427
1433
  padding: 0.3rem 0.75rem;
1428
1434
  border-radius: 100px;
1429
- box-shadow: 0 20px 50px rgba(0, 0, 0, 0.3);
1435
+ box-shadow: 0 20px 50px rgba(0, 0, 0, 0.15);
1430
1436
  }
1431
1437
  .cv-btn {
1432
1438
  display: flex;
@@ -1434,7 +1440,7 @@ var PANEL_CSS = (
1434
1440
  gap: 0.5rem;
1435
1441
  background: transparent;
1436
1442
  border: none;
1437
- color: rgba(255,255,255,0.55);
1443
+ color: var(--cv-text-dim, rgba(255,255,255,0.55));
1438
1444
  font-weight: 600;
1439
1445
  font-size: 0.75rem;
1440
1446
  cursor: pointer;
@@ -1442,7 +1448,7 @@ var PANEL_CSS = (
1442
1448
  padding: 0.35rem 0.75rem;
1443
1449
  border-radius: 50px;
1444
1450
  }
1445
- .cv-btn:hover { color: #fff; background: rgba(255,255,255,0.05); }
1451
+ .cv-btn:hover { color: var(--cv-text, #fff); background: var(--cv-ui-bg, rgba(255,255,255,0.05)); }
1446
1452
  .cv-btn--end { color: #ff4444; }
1447
1453
  .cv-btn--end .cv-btn-box {
1448
1454
  background: #ff4444;
@@ -1531,7 +1537,7 @@ var ConversationalPanel = class {
1531
1537
  const accent = this.cfg.accentColor || "#a25a6b";
1532
1538
  const bg = this.cfg.backgroundColor || "#0a0a0a";
1533
1539
  this.container.style.setProperty("--cv-accent", accent);
1534
- this.container.style.setProperty("--cv-bg", bg);
1540
+ this.setBackgroundColor(bg);
1535
1541
  this.el = document.createElement("div");
1536
1542
  this.el.className = "cv-panel";
1537
1543
  this.el.innerHTML = `
@@ -1739,6 +1745,29 @@ var ConversationalPanel = class {
1739
1745
  this.visualizer.setAccentColor(r, g, b);
1740
1746
  }
1741
1747
  }
1748
+ /** Update background color dynamically and ensure text/UI contrast */
1749
+ setBackgroundColor(color) {
1750
+ this.cfg.backgroundColor = color;
1751
+ this.container.style.setProperty("--cv-bg", color);
1752
+ let r = 0, g = 0, b = 0;
1753
+ if (color.startsWith("#")) {
1754
+ if (color.length === 4) {
1755
+ r = parseInt(color[1] + color[1], 16);
1756
+ g = parseInt(color[2] + color[2], 16);
1757
+ b = parseInt(color[3] + color[3], 16);
1758
+ } else {
1759
+ r = parseInt(color.slice(1, 3), 16);
1760
+ g = parseInt(color.slice(3, 5), 16);
1761
+ b = parseInt(color.slice(5, 7), 16);
1762
+ }
1763
+ }
1764
+ const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
1765
+ const isLight = luminance > 0.5;
1766
+ this.container.style.setProperty("--cv-text", isLight ? "#000000" : "#ffffff");
1767
+ this.container.style.setProperty("--cv-text-dim", isLight ? "rgba(0,0,0,0.6)" : "rgba(255,255,255,0.6)");
1768
+ this.container.style.setProperty("--cv-ui-bg", isLight ? "rgba(0,0,0,0.05)" : "rgba(255,255,255,0.05)");
1769
+ this.container.style.setProperty("--cv-ui-border", isLight ? "rgba(0,0,0,0.1)" : "rgba(255,255,255,0.1)");
1770
+ }
1742
1771
  /** Update title text */
1743
1772
  setTitle(title) {
1744
1773
  this.cfg.title = title;
package/dist/index.mjs CHANGED
@@ -842,9 +842,11 @@ var VoiceAgentClient = class {
842
842
  });
843
843
  if (msg.role === "user") {
844
844
  if (this.onTranscription) this.onTranscription(msg.data);
845
+ this.emit("transcription", msg.data);
845
846
  console.log(`\u{1F4AC} You: ${msg.data}`);
846
847
  } else {
847
848
  if (this.onResponse) this.onResponse(msg.data);
849
+ this.emit("response", msg.data);
848
850
  console.log(`\u{1F916} Agent: ${msg.data}`);
849
851
  }
850
852
  break;
@@ -861,6 +863,7 @@ var VoiceAgentClient = class {
861
863
  this.audioManager.stopPlayback();
862
864
  }
863
865
  if (this.onStatus) this.onStatus(msg.data);
866
+ this.emit("status", msg.data);
864
867
  const icons = {
865
868
  "interrupted": "\u26A1",
866
869
  "thinking": "\u{1F9E0}",
@@ -898,10 +901,12 @@ var VoiceAgentClient = class {
898
901
  retryable: backendRetryable
899
902
  });
900
903
  if (this.onError) this.onError(error);
904
+ this.emit("error", error);
901
905
  console.error(`\u274C Server error: [${error.code}] ${error.message}`);
902
906
  break;
903
907
  }
904
908
  case "tool_call":
909
+ this.emit("tool_call", { name: msg.name, arguments: msg.arguments });
905
910
  console.log(`\u{1F6E0}\uFE0F Tool Call: ${msg.name}(${msg.arguments})`);
906
911
  this.emit("tool_call", msg);
907
912
  break;
@@ -1258,7 +1263,7 @@ var PANEL_CSS = (
1258
1263
  flex-direction: column;
1259
1264
  align-items: center;
1260
1265
  gap: clamp(0.75rem, 1.5vw, 1.5rem);
1261
- color: #fff;
1266
+ color: var(--cv-text, #fff);
1262
1267
  text-align: center;
1263
1268
  z-index: 2;
1264
1269
  padding: clamp(1rem, 2vw, 2rem);
@@ -1268,11 +1273,12 @@ var PANEL_CSS = (
1268
1273
  font-weight: 800;
1269
1274
  letter-spacing: -0.03em;
1270
1275
  margin: 0;
1271
- text-shadow: 0 4px 20px rgba(0,0,0,0.3);
1276
+ text-shadow: 0 4px 20px rgba(0,0,0,0.1);
1272
1277
  }
1273
1278
  .cv-curtain-desc {
1274
1279
  font-size: clamp(0.75rem, 2vw, 1.1rem);
1275
1280
  opacity: 0.8;
1281
+ color: var(--cv-text-dim, rgba(255,255,255,0.8));
1276
1282
  max-width: 400px;
1277
1283
  margin: 0;
1278
1284
  line-height: 1.5;
@@ -1281,8 +1287,8 @@ var PANEL_CSS = (
1281
1287
  margin-top: 1rem;
1282
1288
  padding: clamp(0.6rem, 1.5vw, 1rem) clamp(1.5rem, 3vw, 2.5rem);
1283
1289
  border-radius: 100px;
1284
- background: #fff;
1285
- color: #000;
1290
+ background: var(--cv-text, #fff);
1291
+ color: var(--cv-bg, #000);
1286
1292
  border: none;
1287
1293
  font-weight: 700;
1288
1294
  font-size: clamp(0.8rem, 1.5vw, 1rem);
@@ -1291,12 +1297,12 @@ var PANEL_CSS = (
1291
1297
  gap: 0.75rem;
1292
1298
  cursor: pointer;
1293
1299
  transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
1294
- box-shadow: 0 10px 30px rgba(0,0,0,0.2);
1300
+ box-shadow: 0 10px 30px rgba(0,0,0,0.15);
1295
1301
  }
1296
1302
  .cv-curtain-btn:hover {
1297
1303
  transform: scale(1.05);
1298
- background: #f0f0f0;
1299
- box-shadow: 0 15px 40px rgba(0,0,0,0.3);
1304
+ opacity: 0.9;
1305
+ box-shadow: 0 15px 40px rgba(0,0,0,0.2);
1300
1306
  }
1301
1307
  .cv-curtain-btn svg { transition: transform 0.3s ease; }
1302
1308
  .cv-curtain-btn:hover svg { transform: translateX(4px); }
@@ -1312,7 +1318,7 @@ var PANEL_CSS = (
1312
1318
  .cv-title {
1313
1319
  font-size: clamp(1rem, 2.5vw, 1.75rem);
1314
1320
  font-weight: 700;
1315
- color: #e0e0e0;
1321
+ color: var(--cv-text, #e0e0e0);
1316
1322
  display: flex;
1317
1323
  align-items: center;
1318
1324
  gap: 1rem;
@@ -1373,13 +1379,13 @@ var PANEL_CSS = (
1373
1379
  display: flex;
1374
1380
  align-items: center;
1375
1381
  gap: 0.75rem;
1376
- background: rgba(255, 255, 255, 0.03);
1382
+ background: var(--cv-ui-bg, rgba(255, 255, 255, 0.03));
1377
1383
  backdrop-filter: blur(30px);
1378
1384
  -webkit-backdrop-filter: blur(30px);
1379
- border: 1px solid rgba(255, 255, 255, 0.08);
1385
+ border: 1px solid var(--cv-ui-border, rgba(255, 255, 255, 0.08));
1380
1386
  padding: 0.3rem 0.75rem;
1381
1387
  border-radius: 100px;
1382
- box-shadow: 0 20px 50px rgba(0, 0, 0, 0.3);
1388
+ box-shadow: 0 20px 50px rgba(0, 0, 0, 0.15);
1383
1389
  }
1384
1390
  .cv-btn {
1385
1391
  display: flex;
@@ -1387,7 +1393,7 @@ var PANEL_CSS = (
1387
1393
  gap: 0.5rem;
1388
1394
  background: transparent;
1389
1395
  border: none;
1390
- color: rgba(255,255,255,0.55);
1396
+ color: var(--cv-text-dim, rgba(255,255,255,0.55));
1391
1397
  font-weight: 600;
1392
1398
  font-size: 0.75rem;
1393
1399
  cursor: pointer;
@@ -1395,7 +1401,7 @@ var PANEL_CSS = (
1395
1401
  padding: 0.35rem 0.75rem;
1396
1402
  border-radius: 50px;
1397
1403
  }
1398
- .cv-btn:hover { color: #fff; background: rgba(255,255,255,0.05); }
1404
+ .cv-btn:hover { color: var(--cv-text, #fff); background: var(--cv-ui-bg, rgba(255,255,255,0.05)); }
1399
1405
  .cv-btn--end { color: #ff4444; }
1400
1406
  .cv-btn--end .cv-btn-box {
1401
1407
  background: #ff4444;
@@ -1484,7 +1490,7 @@ var ConversationalPanel = class {
1484
1490
  const accent = this.cfg.accentColor || "#a25a6b";
1485
1491
  const bg = this.cfg.backgroundColor || "#0a0a0a";
1486
1492
  this.container.style.setProperty("--cv-accent", accent);
1487
- this.container.style.setProperty("--cv-bg", bg);
1493
+ this.setBackgroundColor(bg);
1488
1494
  this.el = document.createElement("div");
1489
1495
  this.el.className = "cv-panel";
1490
1496
  this.el.innerHTML = `
@@ -1692,6 +1698,29 @@ var ConversationalPanel = class {
1692
1698
  this.visualizer.setAccentColor(r, g, b);
1693
1699
  }
1694
1700
  }
1701
+ /** Update background color dynamically and ensure text/UI contrast */
1702
+ setBackgroundColor(color) {
1703
+ this.cfg.backgroundColor = color;
1704
+ this.container.style.setProperty("--cv-bg", color);
1705
+ let r = 0, g = 0, b = 0;
1706
+ if (color.startsWith("#")) {
1707
+ if (color.length === 4) {
1708
+ r = parseInt(color[1] + color[1], 16);
1709
+ g = parseInt(color[2] + color[2], 16);
1710
+ b = parseInt(color[3] + color[3], 16);
1711
+ } else {
1712
+ r = parseInt(color.slice(1, 3), 16);
1713
+ g = parseInt(color.slice(3, 5), 16);
1714
+ b = parseInt(color.slice(5, 7), 16);
1715
+ }
1716
+ }
1717
+ const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
1718
+ const isLight = luminance > 0.5;
1719
+ this.container.style.setProperty("--cv-text", isLight ? "#000000" : "#ffffff");
1720
+ this.container.style.setProperty("--cv-text-dim", isLight ? "rgba(0,0,0,0.6)" : "rgba(255,255,255,0.6)");
1721
+ this.container.style.setProperty("--cv-ui-bg", isLight ? "rgba(0,0,0,0.05)" : "rgba(255,255,255,0.05)");
1722
+ this.container.style.setProperty("--cv-ui-border", isLight ? "rgba(0,0,0,0.1)" : "rgba(255,255,255,0.1)");
1723
+ }
1695
1724
  /** Update title text */
1696
1725
  setTitle(title) {
1697
1726
  this.cfg.title = title;
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@lokutor/sdk",
3
- "version": "1.1.30",
3
+
4
+ "version": "1.1.32",
4
5
  "description": "JavaScript/TypeScript SDK for Lokutor Real-time Voice AI",
5
6
  "main": "./dist/index.js",
6
7
  "module": "./dist/index.mjs",
package/src/client.ts CHANGED
@@ -456,9 +456,11 @@ export class VoiceAgentClient {
456
456
 
457
457
  if (msg.role === 'user') {
458
458
  if (this.onTranscription) this.onTranscription(msg.data);
459
+ this.emit('transcription', msg.data);
459
460
  console.log(`💬 You: ${msg.data}`);
460
461
  } else {
461
462
  if (this.onResponse) this.onResponse(msg.data);
463
+ this.emit('response', msg.data);
462
464
  console.log(`🤖 Agent: ${msg.data}`);
463
465
  }
464
466
  break;
@@ -475,6 +477,7 @@ export class VoiceAgentClient {
475
477
  this.audioManager.stopPlayback();
476
478
  }
477
479
  if (this.onStatus) this.onStatus(msg.data);
480
+ this.emit('status', msg.data);
478
481
  const icons: Record<string, string> = {
479
482
  'interrupted': '⚡',
480
483
  'thinking': '🧠',
@@ -512,10 +515,12 @@ export class VoiceAgentClient {
512
515
  retryable: backendRetryable,
513
516
  });
514
517
  if (this.onError) this.onError(error);
518
+ this.emit('error', error);
515
519
  console.error(`❌ Server error: [${error.code}] ${error.message}`);
516
520
  break;
517
521
  }
518
522
  case 'tool_call':
523
+ this.emit('tool_call', { name: msg.name, arguments: msg.arguments });
519
524
  console.log(`🛠️ Tool Call: ${msg.name}(${msg.arguments})`);
520
525
  this.emit('tool_call', msg);
521
526
  break;
@@ -45,7 +45,7 @@ const PANEL_CSS = /*css*/ `
45
45
  flex-direction: column;
46
46
  align-items: center;
47
47
  gap: clamp(0.75rem, 1.5vw, 1.5rem);
48
- color: #fff;
48
+ color: var(--cv-text, #fff);
49
49
  text-align: center;
50
50
  z-index: 2;
51
51
  padding: clamp(1rem, 2vw, 2rem);
@@ -55,11 +55,12 @@ const PANEL_CSS = /*css*/ `
55
55
  font-weight: 800;
56
56
  letter-spacing: -0.03em;
57
57
  margin: 0;
58
- text-shadow: 0 4px 20px rgba(0,0,0,0.3);
58
+ text-shadow: 0 4px 20px rgba(0,0,0,0.1);
59
59
  }
60
60
  .cv-curtain-desc {
61
61
  font-size: clamp(0.75rem, 2vw, 1.1rem);
62
62
  opacity: 0.8;
63
+ color: var(--cv-text-dim, rgba(255,255,255,0.8));
63
64
  max-width: 400px;
64
65
  margin: 0;
65
66
  line-height: 1.5;
@@ -68,8 +69,8 @@ const PANEL_CSS = /*css*/ `
68
69
  margin-top: 1rem;
69
70
  padding: clamp(0.6rem, 1.5vw, 1rem) clamp(1.5rem, 3vw, 2.5rem);
70
71
  border-radius: 100px;
71
- background: #fff;
72
- color: #000;
72
+ background: var(--cv-text, #fff);
73
+ color: var(--cv-bg, #000);
73
74
  border: none;
74
75
  font-weight: 700;
75
76
  font-size: clamp(0.8rem, 1.5vw, 1rem);
@@ -78,12 +79,12 @@ const PANEL_CSS = /*css*/ `
78
79
  gap: 0.75rem;
79
80
  cursor: pointer;
80
81
  transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
81
- box-shadow: 0 10px 30px rgba(0,0,0,0.2);
82
+ box-shadow: 0 10px 30px rgba(0,0,0,0.15);
82
83
  }
83
84
  .cv-curtain-btn:hover {
84
85
  transform: scale(1.05);
85
- background: #f0f0f0;
86
- box-shadow: 0 15px 40px rgba(0,0,0,0.3);
86
+ opacity: 0.9;
87
+ box-shadow: 0 15px 40px rgba(0,0,0,0.2);
87
88
  }
88
89
  .cv-curtain-btn svg { transition: transform 0.3s ease; }
89
90
  .cv-curtain-btn:hover svg { transform: translateX(4px); }
@@ -99,7 +100,7 @@ const PANEL_CSS = /*css*/ `
99
100
  .cv-title {
100
101
  font-size: clamp(1rem, 2.5vw, 1.75rem);
101
102
  font-weight: 700;
102
- color: #e0e0e0;
103
+ color: var(--cv-text, #e0e0e0);
103
104
  display: flex;
104
105
  align-items: center;
105
106
  gap: 1rem;
@@ -160,13 +161,13 @@ const PANEL_CSS = /*css*/ `
160
161
  display: flex;
161
162
  align-items: center;
162
163
  gap: 0.75rem;
163
- background: rgba(255, 255, 255, 0.03);
164
+ background: var(--cv-ui-bg, rgba(255, 255, 255, 0.03));
164
165
  backdrop-filter: blur(30px);
165
166
  -webkit-backdrop-filter: blur(30px);
166
- border: 1px solid rgba(255, 255, 255, 0.08);
167
+ border: 1px solid var(--cv-ui-border, rgba(255, 255, 255, 0.08));
167
168
  padding: 0.3rem 0.75rem;
168
169
  border-radius: 100px;
169
- box-shadow: 0 20px 50px rgba(0, 0, 0, 0.3);
170
+ box-shadow: 0 20px 50px rgba(0, 0, 0, 0.15);
170
171
  }
171
172
  .cv-btn {
172
173
  display: flex;
@@ -174,7 +175,7 @@ const PANEL_CSS = /*css*/ `
174
175
  gap: 0.5rem;
175
176
  background: transparent;
176
177
  border: none;
177
- color: rgba(255,255,255,0.55);
178
+ color: var(--cv-text-dim, rgba(255,255,255,0.55));
178
179
  font-weight: 600;
179
180
  font-size: 0.75rem;
180
181
  cursor: pointer;
@@ -182,7 +183,7 @@ const PANEL_CSS = /*css*/ `
182
183
  padding: 0.35rem 0.75rem;
183
184
  border-radius: 50px;
184
185
  }
185
- .cv-btn:hover { color: #fff; background: rgba(255,255,255,0.05); }
186
+ .cv-btn:hover { color: var(--cv-text, #fff); background: var(--cv-ui-bg, rgba(255,255,255,0.05)); }
186
187
  .cv-btn--end { color: #ff4444; }
187
188
  .cv-btn--end .cv-btn-box {
188
189
  background: #ff4444;
@@ -307,7 +308,7 @@ export class ConversationalPanel {
307
308
  const bg = this.cfg.backgroundColor || '#0a0a0a';
308
309
 
309
310
  this.container.style.setProperty('--cv-accent', accent);
310
- this.container.style.setProperty('--cv-bg', bg);
311
+ this.setBackgroundColor(bg);
311
312
 
312
313
  this.el = document.createElement('div');
313
314
  this.el.className = 'cv-panel';
@@ -528,6 +529,37 @@ export class ConversationalPanel {
528
529
  }
529
530
  }
530
531
 
532
+ /** Update background color dynamically and ensure text/UI contrast */
533
+ setBackgroundColor(color: string) {
534
+ this.cfg.backgroundColor = color;
535
+ this.container.style.setProperty('--cv-bg', color);
536
+
537
+ // Calculate luminance to decide on light/dark theme contrast
538
+ // Simple hex to RGB conversion
539
+ let r = 0, g = 0, b = 0;
540
+ if (color.startsWith('#')) {
541
+ if (color.length === 4) {
542
+ r = parseInt(color[1] + color[1], 16);
543
+ g = parseInt(color[2] + color[2], 16);
544
+ b = parseInt(color[3] + color[3], 16);
545
+ } else {
546
+ r = parseInt(color.slice(1, 3), 16);
547
+ g = parseInt(color.slice(3, 5), 16);
548
+ b = parseInt(color.slice(5, 7), 16);
549
+ }
550
+ }
551
+
552
+ // Relative luminance formula
553
+ const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
554
+ const isLight = luminance > 0.5;
555
+
556
+ // Set contrast variables
557
+ this.container.style.setProperty('--cv-text', isLight ? '#000000' : '#ffffff');
558
+ this.container.style.setProperty('--cv-text-dim', isLight ? 'rgba(0,0,0,0.6)' : 'rgba(255,255,255,0.6)');
559
+ this.container.style.setProperty('--cv-ui-bg', isLight ? 'rgba(0,0,0,0.05)' : 'rgba(255,255,255,0.05)');
560
+ this.container.style.setProperty('--cv-ui-border', isLight ? 'rgba(0,0,0,0.1)' : 'rgba(255,255,255,0.1)');
561
+ }
562
+
531
563
  /** Update title text */
532
564
  setTitle(title: string) {
533
565
  this.cfg.title = title;