@agentforge-io/chat-sdk 2.5.0 → 2.5.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.
- package/dist/react.js +79 -52
- package/package.json +1 -1
package/dist/react.js
CHANGED
|
@@ -563,28 +563,34 @@ function ChatWidget(props) {
|
|
|
563
563
|
}
|
|
564
564
|
visibleIdxs.push(i);
|
|
565
565
|
}
|
|
566
|
-
//
|
|
567
|
-
//
|
|
568
|
-
//
|
|
569
|
-
//
|
|
570
|
-
const
|
|
571
|
-
for (
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
566
|
+
// Find the index of the next assistant message after each
|
|
567
|
+
// position. Used to decide whether an empty bubble has been
|
|
568
|
+
// superseded by a later speaker (in which case the visitor
|
|
569
|
+
// should not still see it typing).
|
|
570
|
+
const nextAssistantIdx = new Array(messages.length).fill(-1);
|
|
571
|
+
for (let k = messages.length - 2; k >= 0; k--) {
|
|
572
|
+
const nextI = k + 1;
|
|
573
|
+
nextAssistantIdx[k] =
|
|
574
|
+
messages[nextI].role === 'assistant'
|
|
575
|
+
? nextI
|
|
576
|
+
: nextAssistantIdx[nextI];
|
|
575
577
|
}
|
|
576
578
|
const finalIdxs = visibleIdxs.filter((i) => {
|
|
577
579
|
const m = messages[i];
|
|
578
580
|
if (m.role !== 'assistant')
|
|
579
581
|
return true;
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
//
|
|
583
|
-
//
|
|
584
|
-
//
|
|
585
|
-
//
|
|
586
|
-
//
|
|
587
|
-
|
|
582
|
+
// Any empty assistant bubble that has been overtaken by
|
|
583
|
+
// a LATER assistant bubble is an intermediate — hide it.
|
|
584
|
+
// Covers two cases at once:
|
|
585
|
+
// 1. The classic "chain head/intermediate" path: bubble
|
|
586
|
+
// i is empty and there's a final bubble after it.
|
|
587
|
+
// 2. The "live delegation" path: while a chain streams,
|
|
588
|
+
// the previous member sits at content='' isStreaming=
|
|
589
|
+
// true (the SDK only flips isStreaming when the
|
|
590
|
+
// bubble produced text). A naive filter would keep
|
|
591
|
+
// showing its typing dots; we drop it as soon as
|
|
592
|
+
// a fresher speaker exists.
|
|
593
|
+
if (!m.content && nextAssistantIdx[i] !== -1)
|
|
588
594
|
return false;
|
|
589
595
|
return true;
|
|
590
596
|
});
|
|
@@ -612,37 +618,41 @@ function ChatWidget(props) {
|
|
|
612
618
|
let chainParticipants;
|
|
613
619
|
if (isAssistant && showAvatar) {
|
|
614
620
|
const head = chainHeadFor[i];
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
621
|
+
// Collect all distinct speakers in [head…i] in
|
|
622
|
+
// chronological order. The bubble that's rendering now
|
|
623
|
+
// (i) is the most recent speaker so it becomes the
|
|
624
|
+
// "active" one at the front of the stack; everything
|
|
625
|
+
// earlier in the chain trails behind in reverse-
|
|
626
|
+
// chronological order. Works the same whether the
|
|
627
|
+
// chain has finished or is still streaming — visitor
|
|
628
|
+
// sees one bubble with a live-updating header.
|
|
629
|
+
const seen = new Set();
|
|
630
|
+
const ordered = [];
|
|
631
|
+
for (let j = head; j <= i; j++) {
|
|
632
|
+
const mj = messages[j];
|
|
633
|
+
if (mj.role !== 'assistant')
|
|
634
|
+
continue;
|
|
635
|
+
const key = mj.actingAgentId ?? null;
|
|
636
|
+
if (seen.has(key))
|
|
637
|
+
continue;
|
|
638
|
+
seen.add(key);
|
|
639
|
+
const mem = mj.actingAgentId
|
|
640
|
+
? membersById.get(mj.actingAgentId)
|
|
641
|
+
: undefined;
|
|
642
|
+
ordered.push({
|
|
643
|
+
agentId: mj.actingAgentId ?? agent?.slug,
|
|
644
|
+
name: mem?.name ?? personaName ?? agent?.name,
|
|
645
|
+
theme: mem
|
|
646
|
+
? { ...theme, avatarUrl: mem.avatarUrl }
|
|
647
|
+
: theme,
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
if (ordered.length > 1) {
|
|
651
|
+
// The visible bubble's speaker (i) sits at the END of
|
|
652
|
+
// `ordered`. Move them to the front so the active
|
|
653
|
+
// avatar leads the stack.
|
|
654
|
+
const active = ordered.pop();
|
|
655
|
+
chainParticipants = [active, ...ordered.reverse()];
|
|
646
656
|
}
|
|
647
657
|
}
|
|
648
658
|
return ((0, jsx_runtime_1.jsx)(MessageBubble, { message: m, session: session, readOnly: readOnlyApprovals, onDecision: onApprovalDecision, bare: bare, showAvatar: showAvatar, avatarTheme: bubbleAvatarTheme, avatarName: bubbleAvatarName, avatarAgentId: bubbleAgentId, speakerLabel: member?.name, chainParticipants: chainParticipants, onContinue: () => {
|
|
@@ -1357,22 +1367,38 @@ const WIDGET_CSS = `
|
|
|
1357
1367
|
background-color: var(--af-primary, #8b5cf6);
|
|
1358
1368
|
}
|
|
1359
1369
|
/* Delegation-chain avatar stack. Override the default 26x26 size so
|
|
1360
|
-
* the row can host multiple overlapping items without clipping.
|
|
1370
|
+
* the row can host multiple overlapping items without clipping.
|
|
1371
|
+
* `, align;
|
|
1372
|
+
-items;
|
|
1373
|
+
center ` is critical: with `;
|
|
1374
|
+
flex - end ` the smaller
|
|
1375
|
+
* items glue to the bottom of the row and the column they sit in
|
|
1376
|
+
* looks unbalanced — circles read as ovals because the bounding box
|
|
1377
|
+
* isn't square anymore. Centering keeps every item axis-aligned and
|
|
1378
|
+
* the ring + size cap together force a 1:1 aspect ratio. */
|
|
1361
1379
|
.af-widget-root.af-variant-bare .af-msg-avatar-stack {
|
|
1362
1380
|
width: auto;
|
|
1363
1381
|
height: 26px;
|
|
1364
1382
|
border-radius: 0;
|
|
1365
1383
|
overflow: visible;
|
|
1384
|
+
background: transparent;
|
|
1366
1385
|
display: flex;
|
|
1367
1386
|
flex-direction: row;
|
|
1368
|
-
align-items:
|
|
1387
|
+
align-items: center;
|
|
1369
1388
|
/* Slight padding-right so the rightmost stacked item doesn't sit
|
|
1370
1389
|
* flush against the bubble bevel. */
|
|
1371
1390
|
padding-right: 4px;
|
|
1372
1391
|
}
|
|
1373
1392
|
.af-widget-root.af-variant-bare .af-msg-avatar-stack-item {
|
|
1393
|
+
/* Hard-pin the aspect ratio so the box never deforms into an
|
|
1394
|
+
* ellipse when the parent flexes — `;
|
|
1395
|
+
aspect - ratio;
|
|
1396
|
+
1 ` plus explicit
|
|
1397
|
+
* width AND min-width guarantees a square box on every flex child. */
|
|
1374
1398
|
width: 18px;
|
|
1399
|
+
min-width: 18px;
|
|
1375
1400
|
height: 18px;
|
|
1401
|
+
aspect-ratio: 1;
|
|
1376
1402
|
border-radius: 50%;
|
|
1377
1403
|
overflow: hidden;
|
|
1378
1404
|
display: flex;
|
|
@@ -1390,9 +1416,9 @@ const WIDGET_CSS = `
|
|
|
1390
1416
|
}
|
|
1391
1417
|
.af-widget-root.af-variant-bare .af-msg-avatar-stack-active {
|
|
1392
1418
|
/* The active speaker is rendered full-size so it visually anchors
|
|
1393
|
-
* the chain.
|
|
1394
|
-
* the smaller upstream delegators. */
|
|
1419
|
+
* the chain. */
|
|
1395
1420
|
width: 26px;
|
|
1421
|
+
min-width: 26px;
|
|
1396
1422
|
height: 26px;
|
|
1397
1423
|
}
|
|
1398
1424
|
.af-widget-root.af-variant-bare .af-msg-avatar-stack-img {
|
|
@@ -1406,6 +1432,7 @@ const WIDGET_CSS = `
|
|
|
1406
1432
|
font-size: 9.5px;
|
|
1407
1433
|
font-weight: 600;
|
|
1408
1434
|
background-color: var(--af-primary, #8b5cf6);
|
|
1435
|
+
line-height: 1;
|
|
1409
1436
|
}
|
|
1410
1437
|
.af-widget-root.af-variant-bare .af-msg-avatar-stack-active.af-msg-avatar-stack-fallback {
|
|
1411
1438
|
font-size: 11.5px;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentforge-io/chat-sdk",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.2",
|
|
4
4
|
"description": "Framework-free chat session SDK for AgentForge public chat tokens. Headless — no DOM. Drop into any frontend (React, Vue, Svelte, vanilla) and listen for events.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|