@flamingo-stack/openframe-frontend-core 0.0.584-1973.5461.1 → 0.0.584
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/chat-protocol/events.d.ts +2 -8
- package/dist/chat-protocol/events.d.ts.map +1 -1
- package/dist/chat-protocol/index.cjs +0 -188
- package/dist/chat-protocol/index.cjs.map +1 -1
- package/dist/chat-protocol/index.js +0 -188
- package/dist/chat-protocol/index.js.map +1 -1
- package/dist/chat-protocol/nats-decoder.d.ts.map +1 -1
- package/dist/{chunk-U7MJV2V7.cjs → chunk-33DHEEMP.cjs} +33 -33
- package/dist/{chunk-U7MJV2V7.cjs.map → chunk-33DHEEMP.cjs.map} +1 -1
- package/dist/{chunk-23VBGHBL.js → chunk-3ELW4VML.js} +2 -2
- package/dist/{chunk-7KBKLN2Z.cjs → chunk-3ZA2OYRE.cjs} +3 -3
- package/dist/{chunk-7KBKLN2Z.cjs.map → chunk-3ZA2OYRE.cjs.map} +1 -1
- package/dist/{chunk-73XDYWYG.js → chunk-4T5KOD3G.js} +5 -5
- package/dist/{chunk-MI6PPL3P.js → chunk-5CJKZHWO.js} +5 -5
- package/dist/{chunk-E7Q24T4G.js → chunk-6BJYPG2V.js} +3 -3
- package/dist/{chunk-YXNMLGFI.cjs → chunk-7X5TCK4R.cjs} +10 -10
- package/dist/{chunk-YXNMLGFI.cjs.map → chunk-7X5TCK4R.cjs.map} +1 -1
- package/dist/{chunk-7DEGEI7G.js → chunk-EJKEDCTH.js} +6 -6
- package/dist/{chunk-XIV5VDWL.cjs → chunk-HCKEDTKJ.cjs} +10 -10
- package/dist/{chunk-XIV5VDWL.cjs.map → chunk-HCKEDTKJ.cjs.map} +1 -1
- package/dist/{chunk-EQGAYDTC.cjs → chunk-IIT5HWLY.cjs} +9 -9
- package/dist/{chunk-EQGAYDTC.cjs.map → chunk-IIT5HWLY.cjs.map} +1 -1
- package/dist/{chunk-7SMYBCYH.cjs → chunk-IKUSKKFZ.cjs} +540 -883
- package/dist/chunk-IKUSKKFZ.cjs.map +1 -0
- package/dist/{chunk-JKYALZB7.cjs → chunk-JAJQ5P2Q.cjs} +22 -22
- package/dist/{chunk-JKYALZB7.cjs.map → chunk-JAJQ5P2Q.cjs.map} +1 -1
- package/dist/{chunk-WAXL3E3B.cjs → chunk-JFKK3ZOA.cjs} +43 -43
- package/dist/{chunk-WAXL3E3B.cjs.map → chunk-JFKK3ZOA.cjs.map} +1 -1
- package/dist/{chunk-AWAHYAII.js → chunk-JVWRMLQZ.js} +2 -2
- package/dist/{chunk-PZ27ELFP.cjs → chunk-JWKAJTNA.cjs} +43 -33
- package/dist/chunk-JWKAJTNA.cjs.map +1 -0
- package/dist/{chunk-PISJGXXI.js → chunk-KP6VJP36.js} +3 -3
- package/dist/{chunk-NSDZAUJK.js → chunk-LNKULR4V.js} +2 -2
- package/dist/{chunk-AFQB4YL7.cjs → chunk-MEJFHD5N.cjs} +15 -15
- package/dist/{chunk-AFQB4YL7.cjs.map → chunk-MEJFHD5N.cjs.map} +1 -1
- package/dist/{chunk-5VHCSN44.js → chunk-MYEG5ZDT.js} +2 -2
- package/dist/{chunk-MMA3MTJD.cjs → chunk-P2VRLSAP.cjs} +21 -21
- package/dist/{chunk-MMA3MTJD.cjs.map → chunk-P2VRLSAP.cjs.map} +1 -1
- package/dist/{chunk-7Q34BVL7.cjs → chunk-Q66AMADB.cjs} +4 -4
- package/dist/{chunk-7Q34BVL7.cjs.map → chunk-Q66AMADB.cjs.map} +1 -1
- package/dist/{chunk-4ARYZTGV.cjs → chunk-QJR32JM2.cjs} +159 -159
- package/dist/{chunk-4ARYZTGV.cjs.map → chunk-QJR32JM2.cjs.map} +1 -1
- package/dist/{chunk-M4IIK3B7.js → chunk-RURZ6QLN.js} +3 -3
- package/dist/{chunk-TCEU6DCF.js → chunk-TLKHLGAB.js} +3 -3
- package/dist/{chunk-JCWUJHOD.js → chunk-TOUD3AGK.js} +23 -13
- package/dist/chunk-TOUD3AGK.js.map +1 -0
- package/dist/{chunk-CHI7GJ3K.cjs → chunk-UCNEQKIT.cjs} +5 -5
- package/dist/{chunk-CHI7GJ3K.cjs.map → chunk-UCNEQKIT.cjs.map} +1 -1
- package/dist/{chunk-ZRGBLM6W.cjs → chunk-VOAZU46B.cjs} +69 -69
- package/dist/{chunk-ZRGBLM6W.cjs.map → chunk-VOAZU46B.cjs.map} +1 -1
- package/dist/{chunk-GYJVLFK5.js → chunk-VZSTL5VG.js} +2 -2
- package/dist/{chunk-3CAC3AAE.js → chunk-XPJKQ63S.js} +3 -3
- package/dist/{chunk-KSC5LNSY.js → chunk-XWBND4G5.js} +187 -530
- package/dist/chunk-XWBND4G5.js.map +1 -0
- package/dist/{chunk-22DWXIV3.js → chunk-YFZDIEVC.js} +5 -5
- package/dist/{chunk-7J7XDGY6.cjs → chunk-ZHUBNWEX.cjs} +3 -3
- package/dist/{chunk-7J7XDGY6.cjs.map → chunk-ZHUBNWEX.cjs.map} +1 -1
- package/dist/components/case-studies/index.cjs +9 -9
- package/dist/components/case-studies/index.js +3 -3
- package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
- package/dist/components/chat/chat-message-list.d.ts.map +1 -1
- package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
- package/dist/components/chat/entity-cards/dispatch.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +1 -4
- package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-slash-commands.d.ts +3 -3
- package/dist/components/chat/hooks/use-slash-commands.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts +28 -2
- package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +2 -2
- package/dist/components/chat/index.js +1 -1
- package/dist/components/chat/stream/chat-stream-reducer.d.ts.map +1 -1
- package/dist/components/chat/tool-execution-display.d.ts.map +1 -1
- package/dist/components/chat/types/component.types.d.ts +0 -4
- package/dist/components/chat/types/component.types.d.ts.map +1 -1
- package/dist/components/chat/types/message.types.d.ts +1 -35
- package/dist/components/chat/types/message.types.d.ts.map +1 -1
- package/dist/components/chat/types/unified-chat-state.types.d.ts +3 -3
- package/dist/components/chat/types/unified-chat-state.types.d.ts.map +1 -1
- package/dist/components/chat/utils/process-historical-messages.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +4 -4
- package/dist/components/contact/index.js +3 -3
- package/dist/components/docs/index.cjs +7 -7
- package/dist/components/docs/index.js +6 -6
- package/dist/components/embeds/index.cjs +4 -4
- package/dist/components/embeds/index.js +3 -3
- package/dist/components/faq/index.cjs +4 -4
- package/dist/components/faq/index.js +3 -3
- package/dist/components/features/index.cjs +3 -3
- package/dist/components/features/index.js +2 -2
- package/dist/components/help-center-pages/index.cjs +23 -23
- package/dist/components/help-center-pages/index.js +14 -14
- package/dist/components/index.cjs +36 -36
- package/dist/components/index.js +13 -13
- package/dist/components/meeting-scheduler/index.cjs +21 -21
- package/dist/components/meeting-scheduler/index.js +2 -2
- package/dist/components/navigation/index.cjs +6 -6
- package/dist/components/navigation/index.js +5 -5
- package/dist/components/navigation/navigation-sidebar-item.d.ts.map +1 -1
- package/dist/components/onboarding-guides/index.cjs +6 -6
- package/dist/components/onboarding-guides/index.js +5 -5
- package/dist/components/tickets/index.cjs +7 -7
- package/dist/components/tickets/index.js +6 -6
- package/dist/components/ui/hover-dropdown.d.ts.map +1 -1
- package/dist/components/ui/index.cjs +3 -3
- package/dist/components/ui/index.js +2 -2
- package/dist/index.cjs +7 -7
- package/dist/index.js +6 -6
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
- package/src/chat-protocol/__tests__/nats-decoder-golden.test.ts +0 -130
- package/src/chat-protocol/events.ts +1 -9
- package/src/chat-protocol/nats-decoder.ts +0 -7
- package/src/components/chat/__tests__/chat-message-list.test.tsx +0 -70
- package/src/components/chat/__tests__/chat-message-streaming-memo.test.tsx +0 -86
- package/src/components/chat/chat-message-enhanced.tsx +13 -28
- package/src/components/chat/chat-message-list.tsx +22 -27
- package/src/components/chat/embeddable-chat.tsx +36 -55
- package/src/components/chat/entity-cards/dispatch.tsx +17 -86
- package/src/components/chat/hooks/use-nats-chat-adapter.ts +0 -7
- package/src/components/chat/hooks/use-slash-commands.ts +5 -9
- package/src/components/chat/hooks/use-sse-chat-adapter.ts +29 -2
- package/src/components/chat/stream/__tests__/chat-stream-reducer.test.ts +0 -56
- package/src/components/chat/stream/chat-stream-reducer.ts +0 -31
- package/src/components/chat/tool-execution-display.tsx +2 -5
- package/src/components/chat/types/component.types.ts +0 -4
- package/src/components/chat/types/message.types.ts +0 -39
- package/src/components/chat/types/unified-chat-state.types.ts +3 -4
- package/src/components/chat/utils/__tests__/process-historical-messages-golden.test.ts +0 -57
- package/src/components/chat/utils/process-historical-messages.ts +1 -19
- package/src/components/navigation/navigation-sidebar-item.tsx +19 -9
- package/src/components/ui/hover-dropdown.tsx +3 -8
- package/dist/chat-protocol/source-metadata.d.ts +0 -5
- package/dist/chat-protocol/source-metadata.d.ts.map +0 -1
- package/dist/chunk-7SMYBCYH.cjs.map +0 -1
- package/dist/chunk-JCWUJHOD.js.map +0 -1
- package/dist/chunk-KSC5LNSY.js.map +0 -1
- package/dist/chunk-PZ27ELFP.cjs.map +0 -1
- package/dist/components/chat/utils/chat-source-metadata.d.ts +0 -5
- package/dist/components/chat/utils/chat-source-metadata.d.ts.map +0 -1
- package/src/chat-protocol/__tests__/source-metadata.test.ts +0 -74
- package/src/chat-protocol/source-metadata.ts +0 -202
- package/src/components/chat/__tests__/embeddable-chat-sources.test.tsx +0 -411
- package/src/components/chat/__tests__/tool-execution-display.test.tsx +0 -93
- package/src/components/chat/entity-cards/__tests__/dispatch-video-refs.test.tsx +0 -74
- package/src/components/chat/hooks/__tests__/use-slash-commands.test.ts +0 -64
- package/src/components/chat/utils/chat-source-metadata.ts +0 -19
- /package/dist/{chunk-23VBGHBL.js.map → chunk-3ELW4VML.js.map} +0 -0
- /package/dist/{chunk-73XDYWYG.js.map → chunk-4T5KOD3G.js.map} +0 -0
- /package/dist/{chunk-MI6PPL3P.js.map → chunk-5CJKZHWO.js.map} +0 -0
- /package/dist/{chunk-E7Q24T4G.js.map → chunk-6BJYPG2V.js.map} +0 -0
- /package/dist/{chunk-7DEGEI7G.js.map → chunk-EJKEDCTH.js.map} +0 -0
- /package/dist/{chunk-AWAHYAII.js.map → chunk-JVWRMLQZ.js.map} +0 -0
- /package/dist/{chunk-PISJGXXI.js.map → chunk-KP6VJP36.js.map} +0 -0
- /package/dist/{chunk-NSDZAUJK.js.map → chunk-LNKULR4V.js.map} +0 -0
- /package/dist/{chunk-5VHCSN44.js.map → chunk-MYEG5ZDT.js.map} +0 -0
- /package/dist/{chunk-M4IIK3B7.js.map → chunk-RURZ6QLN.js.map} +0 -0
- /package/dist/{chunk-TCEU6DCF.js.map → chunk-TLKHLGAB.js.map} +0 -0
- /package/dist/{chunk-GYJVLFK5.js.map → chunk-VZSTL5VG.js.map} +0 -0
- /package/dist/{chunk-3CAC3AAE.js.map → chunk-XPJKQ63S.js.map} +0 -0
- /package/dist/{chunk-22DWXIV3.js.map → chunk-YFZDIEVC.js.map} +0 -0
package/package.json
CHANGED
|
@@ -329,133 +329,3 @@ describe('decodeNatsChunk — seq envelope', () => {
|
|
|
329
329
|
}).toMatchSnapshot();
|
|
330
330
|
});
|
|
331
331
|
});
|
|
332
|
-
|
|
333
|
-
describe('decodeNatsChunk — Mingo source metadata', () => {
|
|
334
|
-
it.each(['GUIDE', 'SOURCES'] as const)('decodes %s payloads into safe sources and playable refs', type => {
|
|
335
|
-
expect(
|
|
336
|
-
decodeNatsChunk({
|
|
337
|
-
type,
|
|
338
|
-
streamSeq: 81,
|
|
339
|
-
payload: {
|
|
340
|
-
sources: [
|
|
341
|
-
{
|
|
342
|
-
index: 1,
|
|
343
|
-
name: 'Install agent',
|
|
344
|
-
path: 'docs/install.md',
|
|
345
|
-
documentType: 'markdown',
|
|
346
|
-
externalUrl: 'https://docs.example.test/install',
|
|
347
|
-
},
|
|
348
|
-
{
|
|
349
|
-
index: 2,
|
|
350
|
-
name: 'Unsafe source',
|
|
351
|
-
path: 'docs/unsafe.md',
|
|
352
|
-
documentType: 'markdown',
|
|
353
|
-
externalUrl: 'javascript:alert(1)',
|
|
354
|
-
},
|
|
355
|
-
{
|
|
356
|
-
index: 1,
|
|
357
|
-
name: 'Duplicate',
|
|
358
|
-
path: 'docs/duplicate.md',
|
|
359
|
-
documentType: 'markdown',
|
|
360
|
-
},
|
|
361
|
-
{ index: 3, name: '', path: 'docs/empty.md', documentType: 'markdown' },
|
|
362
|
-
],
|
|
363
|
-
videos: [
|
|
364
|
-
{
|
|
365
|
-
ref: '[card://video:MdFJNoJeqZQ]',
|
|
366
|
-
title: 'Install the agent',
|
|
367
|
-
url: 'https://www.youtube.com/watch?v=MdFJNoJeqZQ',
|
|
368
|
-
sourceRepo: 'embedded-videos',
|
|
369
|
-
metadata: { youtubeUrl: 'MdFJNoJeqZQ' },
|
|
370
|
-
},
|
|
371
|
-
{
|
|
372
|
-
ref: '[card://video:mux-9b6586b494]',
|
|
373
|
-
title: 'Community demo',
|
|
374
|
-
url: 'https://stream.mux.com/playback-id.m3u8',
|
|
375
|
-
metadata: { videoUrl: 'javascript:alert(1)' },
|
|
376
|
-
},
|
|
377
|
-
{
|
|
378
|
-
ref: '[card://video:mp4-1b2047dc1b]',
|
|
379
|
-
title: 'Product overview',
|
|
380
|
-
url: 'https://cdn.example.test/product-overview.mp4',
|
|
381
|
-
metadata: { videoUrl: 'https://cdn.example.test/product-overview.mp4' },
|
|
382
|
-
},
|
|
383
|
-
{
|
|
384
|
-
ref: '[card://video:MdFJNoJeqZQ]',
|
|
385
|
-
title: 'Duplicate video',
|
|
386
|
-
url: 'https://cdn.example.test/duplicate.mp4',
|
|
387
|
-
},
|
|
388
|
-
],
|
|
389
|
-
cards: [
|
|
390
|
-
{
|
|
391
|
-
ref: '[card://markdown:install-agent]',
|
|
392
|
-
entityType: 'markdown',
|
|
393
|
-
entityId: 'install-agent',
|
|
394
|
-
},
|
|
395
|
-
],
|
|
396
|
-
},
|
|
397
|
-
}),
|
|
398
|
-
).toEqual({
|
|
399
|
-
type: 'sources',
|
|
400
|
-
seq: 81,
|
|
401
|
-
sources: [
|
|
402
|
-
{
|
|
403
|
-
index: 1,
|
|
404
|
-
name: 'Install agent',
|
|
405
|
-
path: 'docs/install.md',
|
|
406
|
-
documentType: 'markdown',
|
|
407
|
-
externalUrl: 'https://docs.example.test/install',
|
|
408
|
-
},
|
|
409
|
-
{
|
|
410
|
-
index: 2,
|
|
411
|
-
name: 'Unsafe source',
|
|
412
|
-
path: 'docs/unsafe.md',
|
|
413
|
-
documentType: 'markdown',
|
|
414
|
-
},
|
|
415
|
-
],
|
|
416
|
-
refs: [
|
|
417
|
-
{
|
|
418
|
-
type: 'video',
|
|
419
|
-
id: 'MdFJNoJeqZQ',
|
|
420
|
-
title: 'Install the agent',
|
|
421
|
-
url: 'https://www.youtube.com/watch?v=MdFJNoJeqZQ',
|
|
422
|
-
sourceRepo: 'embedded-videos',
|
|
423
|
-
metadata: { youtubeUrl: 'MdFJNoJeqZQ' },
|
|
424
|
-
},
|
|
425
|
-
{
|
|
426
|
-
type: 'video',
|
|
427
|
-
id: 'mux-9b6586b494',
|
|
428
|
-
title: 'Community demo',
|
|
429
|
-
url: 'https://stream.mux.com/playback-id.m3u8',
|
|
430
|
-
metadata: { videoUrl: 'https://stream.mux.com/playback-id.m3u8' },
|
|
431
|
-
},
|
|
432
|
-
{
|
|
433
|
-
type: 'video',
|
|
434
|
-
id: 'mp4-1b2047dc1b',
|
|
435
|
-
title: 'Product overview',
|
|
436
|
-
url: 'https://cdn.example.test/product-overview.mp4',
|
|
437
|
-
metadata: { videoUrl: 'https://cdn.example.test/product-overview.mp4' },
|
|
438
|
-
},
|
|
439
|
-
{
|
|
440
|
-
type: 'markdown',
|
|
441
|
-
id: 'install-agent',
|
|
442
|
-
title: 'install-agent',
|
|
443
|
-
url: null,
|
|
444
|
-
},
|
|
445
|
-
],
|
|
446
|
-
});
|
|
447
|
-
});
|
|
448
|
-
|
|
449
|
-
it('drops a metadata payload with no valid sources or refs', () => {
|
|
450
|
-
expect(
|
|
451
|
-
decodeNatsChunk({
|
|
452
|
-
type: 'SOURCES',
|
|
453
|
-
payload: {
|
|
454
|
-
sources: [{ index: 0, name: 'Invalid', path: '', documentType: '' }],
|
|
455
|
-
videos: [{ ref: 'https://example.test/video' }],
|
|
456
|
-
cards: [{ ref: '[CARD://blog:wrong-case]' }],
|
|
457
|
-
},
|
|
458
|
-
}),
|
|
459
|
-
).toBeNull();
|
|
460
|
-
});
|
|
461
|
-
});
|
|
@@ -12,8 +12,7 @@
|
|
|
12
12
|
// rows straight to the accumulator, so restating them here would be two
|
|
13
13
|
// declarations of one wire contract. Type-only import from a React-free
|
|
14
14
|
// module; `nats-decoder.ts` already depends on the same file for MESSAGE_TYPE.
|
|
15
|
-
import type {
|
|
16
|
-
import type { AskOptionData, ChatSource } from '../components/chat/types/message.types';
|
|
15
|
+
import type { AskOptionData } from '../components/chat/types/message.types';
|
|
17
16
|
// The wire-frame shapes these events carry through are defined ONCE in
|
|
18
17
|
// `./frames.ts` — reuse them here rather than restating their fields.
|
|
19
18
|
import type { ApprovalRequestField, DecisionResolvedFrame, UsageTelemetry } from './frames';
|
|
@@ -241,12 +240,6 @@ export interface ChatMetadataEvent extends ChatStreamEventBase {
|
|
|
241
240
|
};
|
|
242
241
|
}
|
|
243
242
|
|
|
244
|
-
export interface SourcesEvent extends ChatStreamEventBase {
|
|
245
|
-
type: 'sources';
|
|
246
|
-
sources: ChatSource[];
|
|
247
|
-
refs?: ChatRef[];
|
|
248
|
-
}
|
|
249
|
-
|
|
250
243
|
/** SSE usage frames — raw wire keys (snake_case) preserved. */
|
|
251
244
|
export interface UsageEvent extends ChatStreamEventBase {
|
|
252
245
|
type: 'usage';
|
|
@@ -369,7 +362,6 @@ export type ChatStreamEvent =
|
|
|
369
362
|
| TicketEscalatedEvent
|
|
370
363
|
| TicketEventEvent
|
|
371
364
|
| ChatMetadataEvent
|
|
372
|
-
| SourcesEvent
|
|
373
365
|
| UsageEvent
|
|
374
366
|
| TokenUsageEvent
|
|
375
367
|
| CompactionEvent
|
|
@@ -20,7 +20,6 @@ import { MESSAGE_TYPE } from '../components/chat/types/message.types';
|
|
|
20
20
|
import type { AskOptionData } from '../components/chat/types/message.types';
|
|
21
21
|
import { ESCALATION_STATE, escalationResolvedStatus } from './events';
|
|
22
22
|
import type { ApprovalToolCall, ChatStreamEvent } from './events';
|
|
23
|
-
import { sourceMetadataEvent } from './source-metadata';
|
|
24
23
|
|
|
25
24
|
/** Minimal structural view of a NATS chunk (see `ChunkData` in
|
|
26
25
|
* `src/components/chat/types/network.types.ts`). */
|
|
@@ -126,12 +125,6 @@ export function decodeNatsChunk(chunk: unknown): ChatStreamEvent | null {
|
|
|
126
125
|
}
|
|
127
126
|
return null;
|
|
128
127
|
|
|
129
|
-
case MESSAGE_TYPE.GUIDE:
|
|
130
|
-
case MESSAGE_TYPE.SOURCES: {
|
|
131
|
-
const event = sourceMetadataEvent(data.payload);
|
|
132
|
-
return event ? { ...event, ...seq } : null;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
128
|
// An ask card is only an ask card with something to pick: a question and at
|
|
136
129
|
// least one option. Anything less is dropped rather than rendered as an
|
|
137
130
|
// empty card the user can't answer — the backend already falls back to a
|
|
@@ -146,76 +146,6 @@ describe('ChatMessageList force-scroll decisions', () => {
|
|
|
146
146
|
});
|
|
147
147
|
});
|
|
148
148
|
|
|
149
|
-
describe('ChatMessageList message footers', () => {
|
|
150
|
-
const renderSourceFooter = (message: Message) =>
|
|
151
|
-
message.sources?.map(source => <span key={`${message.id}-${source.index}`}>{source.name}</span>);
|
|
152
|
-
|
|
153
|
-
it('keeps a sourced assistant footer after a later plain assistant message', () => {
|
|
154
|
-
const sourcedAssistant: Message = {
|
|
155
|
-
...msg('a1', 'assistant'),
|
|
156
|
-
sources: [
|
|
157
|
-
{
|
|
158
|
-
index: 1,
|
|
159
|
-
name: 'Windows installation guide',
|
|
160
|
-
path: '/docs/windows',
|
|
161
|
-
documentType: 'openframe-docs',
|
|
162
|
-
},
|
|
163
|
-
],
|
|
164
|
-
};
|
|
165
|
-
const plainAssistant = msg('a2', 'assistant');
|
|
166
|
-
|
|
167
|
-
render(
|
|
168
|
-
<ChatMessageList
|
|
169
|
-
dialogId="d1"
|
|
170
|
-
messages={[sourcedAssistant, plainAssistant]}
|
|
171
|
-
renderAfterMessage={renderSourceFooter}
|
|
172
|
-
/>,
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
const sourceFooter = screen.getByText('Windows installation guide');
|
|
176
|
-
expect(sourceFooter).toBeInTheDocument();
|
|
177
|
-
expect(screen.getByText('body of a1')).toAppearBefore(sourceFooter);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it('renders separate source names when citation indices restart on each turn', () => {
|
|
181
|
-
const firstAssistant: Message = {
|
|
182
|
-
...msg('a1', 'assistant'),
|
|
183
|
-
sources: [
|
|
184
|
-
{
|
|
185
|
-
index: 1,
|
|
186
|
-
name: 'First turn source',
|
|
187
|
-
path: '/docs/first',
|
|
188
|
-
documentType: 'openframe-docs',
|
|
189
|
-
},
|
|
190
|
-
],
|
|
191
|
-
};
|
|
192
|
-
const secondAssistant: Message = {
|
|
193
|
-
...msg('a2', 'assistant'),
|
|
194
|
-
sources: [
|
|
195
|
-
{
|
|
196
|
-
index: 1,
|
|
197
|
-
name: 'Second turn source',
|
|
198
|
-
path: '/docs/second',
|
|
199
|
-
documentType: 'openframe-docs',
|
|
200
|
-
},
|
|
201
|
-
],
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
render(
|
|
205
|
-
<ChatMessageList
|
|
206
|
-
dialogId="d1"
|
|
207
|
-
messages={[firstAssistant, secondAssistant]}
|
|
208
|
-
renderAfterMessage={renderSourceFooter}
|
|
209
|
-
/>,
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
const firstSourceFooter = screen.getByText('First turn source');
|
|
213
|
-
const secondSourceFooter = screen.getByText('Second turn source');
|
|
214
|
-
expect(screen.getByText('body of a1')).toAppearBefore(firstSourceFooter);
|
|
215
|
-
expect(screen.getByText('body of a2')).toAppearBefore(secondSourceFooter);
|
|
216
|
-
});
|
|
217
|
-
});
|
|
218
|
-
|
|
219
149
|
// The library's own `isAtBottom` lock is lost silently when the scroller's
|
|
220
150
|
// box changes (source chips mounting below it) or when a card settling out
|
|
221
151
|
// of its skeleton produces a resize-driven scroll the library reads as a
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
import { render } from '@testing-library/react';
|
|
21
21
|
import type React from 'react';
|
|
22
22
|
import { describe, it, expect, vi } from 'vitest';
|
|
23
|
-
import type { ChatRef } from '../chat-ref.types';
|
|
24
23
|
import type { MessageSegment } from '../types';
|
|
25
24
|
|
|
26
25
|
/** Every `componentOverrides` / `additionalRemarkPlugins` identity the
|
|
@@ -130,89 +129,4 @@ describe('ChatMessageEnhanced — override identity across a streamed turn', ()
|
|
|
130
129
|
expect(seenRefs).toEqual([{ type: 'blog', id: 'abc' }]);
|
|
131
130
|
expect(view.container.textContent).toContain('card:abc');
|
|
132
131
|
});
|
|
133
|
-
|
|
134
|
-
it('enriches exact video markers with YouTube, hashed Mux, and hashed MP4 metadata', () => {
|
|
135
|
-
const seenRefs: Array<{ id: string; metadata?: Record<string, unknown> }> = [];
|
|
136
|
-
const refs: ChatRef[] = [
|
|
137
|
-
{
|
|
138
|
-
type: 'video',
|
|
139
|
-
id: 'MdFJNoJeqZQ',
|
|
140
|
-
title: 'Install the agent',
|
|
141
|
-
url: 'https://www.youtube.com/watch?v=MdFJNoJeqZQ',
|
|
142
|
-
metadata: { youtubeUrl: 'MdFJNoJeqZQ' },
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
type: 'video',
|
|
146
|
-
id: 'mux-9b6586b494',
|
|
147
|
-
title: 'Community demo',
|
|
148
|
-
url: 'https://stream.mux.com/playback-id.m3u8',
|
|
149
|
-
metadata: { videoUrl: 'https://stream.mux.com/playback-id.m3u8' },
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
type: 'video',
|
|
153
|
-
id: 'mp4-1b2047dc1b',
|
|
154
|
-
title: 'Product overview',
|
|
155
|
-
url: 'https://cdn.example.test/product-overview.mp4',
|
|
156
|
-
metadata: { videoUrl: 'https://cdn.example.test/product-overview.mp4' },
|
|
157
|
-
},
|
|
158
|
-
];
|
|
159
|
-
|
|
160
|
-
render(
|
|
161
|
-
<ChatMessageEnhanced
|
|
162
|
-
role="assistant"
|
|
163
|
-
content={[
|
|
164
|
-
{
|
|
165
|
-
type: 'text',
|
|
166
|
-
text: 'Watch [card://video:MdFJNoJeqZQ] [card://video:mux-9b6586b494] [card://video:mp4-1b2047dc1b].',
|
|
167
|
-
},
|
|
168
|
-
]}
|
|
169
|
-
refs={refs}
|
|
170
|
-
renderEntityCard={ref => {
|
|
171
|
-
seenRefs.push({ id: ref.id, metadata: ref.metadata });
|
|
172
|
-
return <div data-testid={`card-${ref.id}`}>{ref.title}</div>;
|
|
173
|
-
}}
|
|
174
|
-
NavLinkAnchor={NavLinkAnchor}
|
|
175
|
-
/>,
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
expect(seenRefs).toEqual([
|
|
179
|
-
{ id: 'MdFJNoJeqZQ', metadata: { youtubeUrl: 'MdFJNoJeqZQ' } },
|
|
180
|
-
{ id: 'mux-9b6586b494', metadata: { videoUrl: 'https://stream.mux.com/playback-id.m3u8' } },
|
|
181
|
-
{ id: 'mp4-1b2047dc1b', metadata: { videoUrl: 'https://cdn.example.test/product-overview.mp4' } },
|
|
182
|
-
]);
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
it('rerenders a completed marker when its enriched metadata arrives later', () => {
|
|
186
|
-
const seenMetadata: Array<Record<string, unknown> | undefined> = [];
|
|
187
|
-
const content: MessageSegment[] = [{ type: 'text', text: 'Watch [card://video:mux-9b6586b494].' }];
|
|
188
|
-
const props = {
|
|
189
|
-
role: 'assistant' as const,
|
|
190
|
-
content,
|
|
191
|
-
renderEntityCard: (ref: ChatRef) => {
|
|
192
|
-
seenMetadata.push(ref.metadata);
|
|
193
|
-
return <div>video</div>;
|
|
194
|
-
},
|
|
195
|
-
NavLinkAnchor,
|
|
196
|
-
};
|
|
197
|
-
const view = render(<ChatMessageEnhanced {...props} />);
|
|
198
|
-
|
|
199
|
-
view.rerender(
|
|
200
|
-
<ChatMessageEnhanced
|
|
201
|
-
{...props}
|
|
202
|
-
refs={[
|
|
203
|
-
{
|
|
204
|
-
type: 'video',
|
|
205
|
-
id: 'mux-9b6586b494',
|
|
206
|
-
title: 'Community demo',
|
|
207
|
-
url: 'https://stream.mux.com/playback-id.m3u8',
|
|
208
|
-
metadata: { videoUrl: 'https://stream.mux.com/playback-id.m3u8' },
|
|
209
|
-
},
|
|
210
|
-
]}
|
|
211
|
-
/>,
|
|
212
|
-
);
|
|
213
|
-
|
|
214
|
-
expect(seenMetadata[seenMetadata.length - 1]).toEqual({
|
|
215
|
-
videoUrl: 'https://stream.mux.com/playback-id.m3u8',
|
|
216
|
-
});
|
|
217
|
-
});
|
|
218
132
|
});
|
|
@@ -103,7 +103,6 @@ const ChatMessageEnhanced = forwardRef<HTMLDivElement, ChatMessageEnhancedProps>
|
|
|
103
103
|
renderContextItem,
|
|
104
104
|
renderMention,
|
|
105
105
|
renderEntityCard,
|
|
106
|
-
refs,
|
|
107
106
|
onAskSelect,
|
|
108
107
|
NavLinkAnchor,
|
|
109
108
|
...props
|
|
@@ -128,11 +127,6 @@ const ChatMessageEnhanced = forwardRef<HTMLDivElement, ChatMessageEnhancedProps>
|
|
|
128
127
|
// literal `[card://...]` URL.
|
|
129
128
|
const hasMarkerSupport = !!renderEntityCard;
|
|
130
129
|
|
|
131
|
-
const refsByKey = useMemo(
|
|
132
|
-
() => new Map((refs ?? []).map(reference => [`${reference.type}:${reference.id}`, reference])),
|
|
133
|
-
[refs],
|
|
134
|
-
);
|
|
135
|
-
|
|
136
130
|
const segments = useMemo(() => normalizeContent(content), [content]);
|
|
137
131
|
|
|
138
132
|
const askRuns = useMemo(() => groupAskRuns(segments), [segments]);
|
|
@@ -186,14 +180,7 @@ const ChatMessageEnhanced = forwardRef<HTMLDivElement, ChatMessageEnhancedProps>
|
|
|
186
180
|
// (and its open menu) survives across chunks. Invalidated per key when the
|
|
187
181
|
// render fn identity changes.
|
|
188
182
|
const renderedCardNodeCache = useRef(
|
|
189
|
-
new Map<
|
|
190
|
-
string,
|
|
191
|
-
{
|
|
192
|
-
render: ((ref: ChatRef) => React.ReactNode) | undefined;
|
|
193
|
-
reference: ChatRef;
|
|
194
|
-
node: React.ReactNode;
|
|
195
|
-
}
|
|
196
|
-
>(),
|
|
183
|
+
new Map<string, { render: ((ref: ChatRef) => React.ReactNode) | undefined; node: React.ReactNode }>(),
|
|
197
184
|
);
|
|
198
185
|
|
|
199
186
|
/**
|
|
@@ -255,17 +242,10 @@ const ChatMessageEnhanced = forwardRef<HTMLDivElement, ChatMessageEnhancedProps>
|
|
|
255
242
|
// menu / re-fetching) on every stream chunk. Also dedups the same
|
|
256
243
|
// key emitted twice within one message.
|
|
257
244
|
let entry = cache.get(key);
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
type
|
|
261
|
-
|
|
262
|
-
title: cardId,
|
|
263
|
-
url: null,
|
|
264
|
-
};
|
|
265
|
-
if (!entry || entry.render !== render || entry.reference !== refForRender) {
|
|
266
|
-
// Prefer same-turn SOURCES metadata when available. Otherwise
|
|
267
|
-
// cards hydrate by id from the host's per-object APIs, so a
|
|
268
|
-
// minimal {type, id} descriptor still mounts the loader.
|
|
245
|
+
if (!entry || entry.render !== render) {
|
|
246
|
+
// The marker is the ONLY data on the wire: cards hydrate by id
|
|
247
|
+
// from the host's per-object APIs, so a minimal {type, id}
|
|
248
|
+
// descriptor is all the renderer needs to mount the loader.
|
|
269
249
|
// For types with nothing to fetch the host's render() returns
|
|
270
250
|
// null and we fall through to the bare-cardId fallback in the
|
|
271
251
|
// `<a card://…>` override below.
|
|
@@ -277,7 +257,13 @@ const ChatMessageEnhanced = forwardRef<HTMLDivElement, ChatMessageEnhancedProps>
|
|
|
277
257
|
// the id rather than `undefined`) and `url` to null (matches
|
|
278
258
|
// the no-link semantics fetch-mode cards rely on — they
|
|
279
259
|
// resolve their own URL after fetch).
|
|
280
|
-
|
|
260
|
+
const refForRender: ChatRef = {
|
|
261
|
+
type: cardType,
|
|
262
|
+
id: cardId,
|
|
263
|
+
title: cardId,
|
|
264
|
+
url: null,
|
|
265
|
+
};
|
|
266
|
+
entry = { render, node: render(refForRender) };
|
|
281
267
|
cache.set(key, entry);
|
|
282
268
|
}
|
|
283
269
|
const rendered = entry.node;
|
|
@@ -340,7 +326,7 @@ const ChatMessageEnhanced = forwardRef<HTMLDivElement, ChatMessageEnhancedProps>
|
|
|
340
326
|
}
|
|
341
327
|
});
|
|
342
328
|
return { inlineByKey, partsBySegment, usedKeys };
|
|
343
|
-
}, [hasMarkerSupport,
|
|
329
|
+
}, [hasMarkerSupport, renderEntityCard, segments]);
|
|
344
330
|
|
|
345
331
|
// Drop cached nodes for markers no longer present so the cache can't grow
|
|
346
332
|
// unbounded as a long message's markers change. Deliberately an EFFECT,
|
|
@@ -730,7 +716,6 @@ const MemoizedChatMessageEnhanced = memo(ChatMessageEnhanced, (prevProps, nextPr
|
|
|
730
716
|
// equality holds across streaming chunks.
|
|
731
717
|
prevProps.renderMention === nextProps.renderMention &&
|
|
732
718
|
prevProps.renderEntityCard === nextProps.renderEntityCard &&
|
|
733
|
-
prevProps.refs === nextProps.refs &&
|
|
734
719
|
// Same stability contract as the renderers above: hosts pass a `useCallback`
|
|
735
720
|
// (EmbeddableChat passes its memoized `handleSend`), so this holds across
|
|
736
721
|
// streaming chunks instead of re-rendering every ask card per chunk.
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
-
Fragment,
|
|
5
4
|
useRef,
|
|
6
5
|
useState,
|
|
7
6
|
useCallback,
|
|
@@ -139,7 +138,6 @@ const ChatMessageList = forwardRef<HTMLDivElement, ChatMessageListProps>(
|
|
|
139
138
|
renderContextItem,
|
|
140
139
|
renderMention,
|
|
141
140
|
onAskSelect,
|
|
142
|
-
renderAfterMessage,
|
|
143
141
|
NavLinkAnchor,
|
|
144
142
|
...props
|
|
145
143
|
},
|
|
@@ -988,31 +986,28 @@ const ChatMessageList = forwardRef<HTMLDivElement, ChatMessageListProps>(
|
|
|
988
986
|
(!Array.isArray(message.content) || message.content.every(s => s.type === 'text'));
|
|
989
987
|
if (isEmptyPendingTurn) return null;
|
|
990
988
|
return (
|
|
991
|
-
<
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
/>
|
|
1014
|
-
{renderAfterMessage?.(message, index)}
|
|
1015
|
-
</Fragment>
|
|
989
|
+
<ChatMessageEnhanced
|
|
990
|
+
key={message.id}
|
|
991
|
+
ref={getRegisterMessageEl(message.id)}
|
|
992
|
+
role={message.role}
|
|
993
|
+
name={message.name}
|
|
994
|
+
content={message.content}
|
|
995
|
+
timestamp={message.timestamp}
|
|
996
|
+
isTyping={index === messages.length - 1 && isTyping && message.role === 'assistant'}
|
|
997
|
+
avatar={showAvatars ? message.avatar : null}
|
|
998
|
+
showAvatar={showAvatars}
|
|
999
|
+
assistantType={message.assistantType || assistantType}
|
|
1000
|
+
approvalVariant={approvalVariant}
|
|
1001
|
+
authorType={message.authorType}
|
|
1002
|
+
assistantIcon={message.role !== 'user' ? assistantIcon : undefined}
|
|
1003
|
+
contextItems={message.contextItems}
|
|
1004
|
+
resolveContextIcon={resolveContextIcon}
|
|
1005
|
+
renderContextItem={renderContextItem}
|
|
1006
|
+
renderMention={renderMention}
|
|
1007
|
+
renderEntityCard={renderEntityCard}
|
|
1008
|
+
onAskSelect={index > lastUserMessageIndex ? onAskSelect : undefined}
|
|
1009
|
+
NavLinkAnchor={NavLinkAnchor}
|
|
1010
|
+
/>
|
|
1016
1011
|
);
|
|
1017
1012
|
})}
|
|
1018
1013
|
</div>
|
|
@@ -808,14 +808,13 @@ function SourceChips({
|
|
|
808
808
|
const [expanded, setExpanded] = useState(false);
|
|
809
809
|
|
|
810
810
|
if (cited.length === 0) {
|
|
811
|
-
const
|
|
812
|
-
|
|
813
|
-
if (visible.length === 0) return null;
|
|
811
|
+
const fallback = uncited.slice(0, FALLBACK_TOP_RETRIEVED);
|
|
812
|
+
if (fallback.length === 0) return null;
|
|
814
813
|
return (
|
|
815
814
|
<div className="mt-2 flex flex-col gap-1.5 border-t border-ods-border pt-2">
|
|
816
815
|
<span className="uppercase text-ods-text-muted text-h6">Top retrieved sources</span>
|
|
817
|
-
<div className=
|
|
818
|
-
{
|
|
816
|
+
<div className="flex flex-wrap gap-1.5">
|
|
817
|
+
{fallback.map(src => (
|
|
819
818
|
<SourceChip
|
|
820
819
|
key={src.index}
|
|
821
820
|
src={src}
|
|
@@ -825,16 +824,6 @@ function SourceChips({
|
|
|
825
824
|
onDiscuss={onDiscuss}
|
|
826
825
|
/>
|
|
827
826
|
))}
|
|
828
|
-
{hiddenCount > 0 && (
|
|
829
|
-
<button
|
|
830
|
-
onClick={() => setExpanded(!expanded)}
|
|
831
|
-
className="inline-flex cursor-pointer items-center gap-1 rounded border border-ods-accent bg-ods-card px-2 py-0.5 text-ods-accent transition-colors text-h6 hover:bg-ods-accent/10"
|
|
832
|
-
aria-expanded={expanded}
|
|
833
|
-
aria-label={expanded ? 'Show fewer sources' : `Show ${hiddenCount} additional retrieved sources`}
|
|
834
|
-
>
|
|
835
|
-
{expanded ? 'Show less' : `+${hiddenCount} more retrieved ${hiddenCount === 1 ? 'source' : 'sources'}`}
|
|
836
|
-
</button>
|
|
837
|
-
)}
|
|
838
827
|
</div>
|
|
839
828
|
</div>
|
|
840
829
|
);
|
|
@@ -1590,10 +1579,6 @@ function EmbeddableChatInner({
|
|
|
1590
1579
|
// explicitly — see `Message.hidden` and `chat-message-list`'s skip.
|
|
1591
1580
|
...(m.hidden ? { hidden: true } : {}),
|
|
1592
1581
|
...(m.scrollAnchor ? { scrollAnchor: m.scrollAnchor } : {}),
|
|
1593
|
-
// Preserve same-turn rich metadata. Source chips and entity-card
|
|
1594
|
-
// renderers consume it after this raw-to-Message projection.
|
|
1595
|
-
...(m.sources && m.sources.length > 0 ? { sources: m.sources } : {}),
|
|
1596
|
-
...(m.refs && m.refs.length > 0 ? { refs: m.refs } : {}),
|
|
1597
1582
|
// Forward attached context items so the user bubble renders its chips.
|
|
1598
1583
|
...(m.contextItems && m.contextItems.length > 0 ? { contextItems: m.contextItems } : {}),
|
|
1599
1584
|
};
|
|
@@ -1844,41 +1829,24 @@ function EmbeddableChatInner({
|
|
|
1844
1829
|
return out;
|
|
1845
1830
|
}, [commandsById, enabledSet]);
|
|
1846
1831
|
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
.sort((a, b) => (seenOrder.get(a.index) ?? 0) - (seenOrder.get(b.index) ?? 0));
|
|
1866
|
-
const uncited = message.sources.filter(s => !seenOrder.has(s.index));
|
|
1867
|
-
return (
|
|
1868
|
-
<div className="flex-shrink-0 pb-2">
|
|
1869
|
-
<SourceChips
|
|
1870
|
-
cited={cited}
|
|
1871
|
-
uncited={uncited}
|
|
1872
|
-
baseRoute={resolvedBaseRoute}
|
|
1873
|
-
chipBasePlatform={chipBasePlatform}
|
|
1874
|
-
onClose={handleNavigationClose}
|
|
1875
|
-
onDiscuss={discussRef}
|
|
1876
|
-
/>
|
|
1877
|
-
</div>
|
|
1878
|
-
);
|
|
1879
|
-
},
|
|
1880
|
-
[chatLoading, messages.length, resolvedBaseRoute, chipBasePlatform, handleNavigationClose, discussRef],
|
|
1881
|
-
);
|
|
1832
|
+
// Find sources for the last assistant message; split into cited / uncited.
|
|
1833
|
+
const lastAssistantMsg = [...rawMessages].reverse().find(m => m.role === 'assistant');
|
|
1834
|
+
const lastSources = useMemo(() => {
|
|
1835
|
+
if (chatLoading) return undefined;
|
|
1836
|
+
const sources = lastAssistantMsg?.sources;
|
|
1837
|
+
if (!sources || sources.length === 0) return undefined;
|
|
1838
|
+
const content = lastAssistantMsg?.content || '';
|
|
1839
|
+
const citationOrder = [...content.matchAll(/\[(\d+)\]/g)].map(m => parseInt(m[1], 10));
|
|
1840
|
+
const seenOrder = new Map<number, number>();
|
|
1841
|
+
citationOrder.forEach(idx => {
|
|
1842
|
+
if (!seenOrder.has(idx)) seenOrder.set(idx, seenOrder.size);
|
|
1843
|
+
});
|
|
1844
|
+
const cited = sources
|
|
1845
|
+
.filter(s => seenOrder.has(s.index))
|
|
1846
|
+
.sort((a, b) => (seenOrder.get(a.index) ?? 0) - (seenOrder.get(b.index) ?? 0));
|
|
1847
|
+
const uncited = sources.filter(s => !seenOrder.has(s.index));
|
|
1848
|
+
return { cited, uncited };
|
|
1849
|
+
}, [lastAssistantMsg, chatLoading]);
|
|
1882
1850
|
|
|
1883
1851
|
// Host node for in-panel Radix portals (see the body wrapper below).
|
|
1884
1852
|
const [portalHost, setPortalHost] = useState<HTMLDivElement | null>(null);
|
|
@@ -2422,7 +2390,6 @@ function EmbeddableChatInner({
|
|
|
2422
2390
|
resolveContextIcon={resolveContextIcon}
|
|
2423
2391
|
renderContextItem={renderContextItem}
|
|
2424
2392
|
renderMention={renderMention}
|
|
2425
|
-
renderAfterMessage={renderSourcesAfterMessage}
|
|
2426
2393
|
// Gated on `chatLoading` for the same reason the composer
|
|
2427
2394
|
// is: no second send while a turn is in flight. Passive
|
|
2428
2395
|
// demo hosts (previewMode) stay read-only.
|
|
@@ -2460,6 +2427,20 @@ function EmbeddableChatInner({
|
|
|
2460
2427
|
onLoadMore={loadMoreMessages}
|
|
2461
2428
|
/>
|
|
2462
2429
|
)}
|
|
2430
|
+
{lastSources &&
|
|
2431
|
+
(lastSources.cited.length > 0 || lastSources.uncited.length > 0) &&
|
|
2432
|
+
!chatLoading && (
|
|
2433
|
+
<div className="flex-shrink-0 pb-2">
|
|
2434
|
+
<SourceChips
|
|
2435
|
+
cited={lastSources.cited}
|
|
2436
|
+
uncited={lastSources.uncited}
|
|
2437
|
+
baseRoute={resolvedBaseRoute}
|
|
2438
|
+
chipBasePlatform={chipBasePlatform}
|
|
2439
|
+
onClose={handleNavigationClose}
|
|
2440
|
+
onDiscuss={discussRef}
|
|
2441
|
+
/>
|
|
2442
|
+
</div>
|
|
2443
|
+
)}
|
|
2463
2444
|
</div>
|
|
2464
2445
|
) : activeMode === 'mingo' ? (
|
|
2465
2446
|
/* Figma node 7532:222444 — default (Mingo-mode) empty state:
|