@flamingo-stack/openframe-frontend-core 0.0.587-1973.5528.1 → 0.0.587-2000.5532.1
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-5VCDP6UA.cjs → chunk-3FHWLQDG.cjs} +10 -10
- package/dist/{chunk-5VCDP6UA.cjs.map → chunk-3FHWLQDG.cjs.map} +1 -1
- package/dist/{chunk-RE3KVI2Q.js → chunk-3PDTGDOA.js} +5 -5
- package/dist/{chunk-5X7AJ67N.js → chunk-436G4DI4.js} +3 -3
- package/dist/{chunk-BLC4JOFC.cjs → chunk-4IIFSFJ3.cjs} +15 -15
- package/dist/{chunk-BLC4JOFC.cjs.map → chunk-4IIFSFJ3.cjs.map} +1 -1
- package/dist/{chunk-JR37UTA2.cjs → chunk-5O5JMGEN.cjs} +33 -33
- package/dist/{chunk-JR37UTA2.cjs.map → chunk-5O5JMGEN.cjs.map} +1 -1
- package/dist/{chunk-7A6J4JTW.js → chunk-6BFEJXMI.js} +3 -3
- package/dist/{chunk-S2UY2C4A.cjs → chunk-7WIKOL25.cjs} +10 -10
- package/dist/{chunk-S2UY2C4A.cjs.map → chunk-7WIKOL25.cjs.map} +1 -1
- package/dist/{chunk-DVMQV3M4.cjs → chunk-A5PXHMCJ.cjs} +69 -69
- package/dist/{chunk-DVMQV3M4.cjs.map → chunk-A5PXHMCJ.cjs.map} +1 -1
- package/dist/{chunk-RQ5PYSRJ.js → chunk-BLJEPNMJ.js} +4 -4
- package/dist/{chunk-4MVGFYRE.js → chunk-D7YISHXC.js} +190 -534
- package/dist/chunk-D7YISHXC.js.map +1 -0
- package/dist/{chunk-4S7GMJYA.cjs → chunk-DMQMRFGS.cjs} +21 -21
- package/dist/{chunk-4S7GMJYA.cjs.map → chunk-DMQMRFGS.cjs.map} +1 -1
- package/dist/{chunk-AIUIIGO3.cjs → chunk-DTQCZ6AR.cjs} +24 -24
- package/dist/{chunk-AIUIIGO3.cjs.map → chunk-DTQCZ6AR.cjs.map} +1 -1
- package/dist/{chunk-QKAUBYU5.cjs → chunk-E7NFHRLX.cjs} +3 -3
- package/dist/{chunk-QKAUBYU5.cjs.map → chunk-E7NFHRLX.cjs.map} +1 -1
- package/dist/{chunk-WFWWQO2I.js → chunk-F3YRPKUA.js} +2 -2
- package/dist/{chunk-EKKR5ZFB.cjs → chunk-G45RGWHF.cjs} +543 -887
- package/dist/chunk-G45RGWHF.cjs.map +1 -0
- package/dist/{chunk-ZRFSUCAH.js → chunk-HCWZP44O.js} +5 -3
- package/dist/{chunk-ZRFSUCAH.js.map → chunk-HCWZP44O.js.map} +1 -1
- package/dist/{chunk-NEI7XMCM.cjs → chunk-IGBNEPTD.cjs} +9 -9
- package/dist/{chunk-NEI7XMCM.cjs.map → chunk-IGBNEPTD.cjs.map} +1 -1
- package/dist/{chunk-3UJMZADA.cjs → chunk-KQ5JQVYL.cjs} +43 -43
- package/dist/{chunk-3UJMZADA.cjs.map → chunk-KQ5JQVYL.cjs.map} +1 -1
- package/dist/{chunk-FDWGO244.js → chunk-LODCKZ72.js} +2 -2
- package/dist/{chunk-ULOMJH7C.cjs → chunk-MF7TMFVB.cjs} +5 -5
- package/dist/{chunk-ULOMJH7C.cjs.map → chunk-MF7TMFVB.cjs.map} +1 -1
- package/dist/{chunk-DSXD77KX.cjs → chunk-MXCWOMNT.cjs} +3 -3
- package/dist/{chunk-DSXD77KX.cjs.map → chunk-MXCWOMNT.cjs.map} +1 -1
- package/dist/{chunk-E2QFZDCP.js → chunk-OKD7MX7Y.js} +5 -5
- package/dist/{chunk-OW3FUJ2I.js → chunk-P24OI2CD.js} +3 -3
- package/dist/{chunk-QSO24MEK.js → chunk-R2U2D7LU.js} +2 -2
- package/dist/{chunk-KQWI25NP.js → chunk-SI57FQZ2.js} +2 -2
- package/dist/{chunk-JQO47UHC.js → chunk-SVR75PFW.js} +5 -5
- package/dist/{chunk-UYHETUX3.cjs → chunk-UBSIENYF.cjs} +162 -160
- package/dist/chunk-UBSIENYF.cjs.map +1 -0
- package/dist/{chunk-OZRT3ZI3.js → chunk-V4WUSOGS.js} +3 -3
- package/dist/{chunk-UODM3HJC.js → chunk-VWNU2PFX.js} +3 -3
- package/dist/{chunk-SCVIBVFS.cjs → chunk-WQXWH7LF.cjs} +4 -4
- package/dist/{chunk-SCVIBVFS.cjs.map → chunk-WQXWH7LF.cjs.map} +1 -1
- package/dist/{chunk-7COFWOCJ.cjs → chunk-XNEPABUD.cjs} +22 -22
- package/dist/{chunk-7COFWOCJ.cjs.map → chunk-XNEPABUD.cjs.map} +1 -1
- package/dist/{chunk-QYDIVL4X.js → chunk-YB5DFQKD.js} +6 -6
- 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/remark-strip-citations.d.ts.map +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/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/data-table/use-data-table.d.ts.map +1 -1
- 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 -2
- 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/remark-strip-citations.ts +3 -5
- 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/ui/__tests__/data-table-compiler-identity.test.tsx +74 -0
- package/src/components/ui/data-table/use-data-table.ts +36 -1
- 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-4MVGFYRE.js.map +0 -1
- package/dist/chunk-EKKR5ZFB.cjs.map +0 -1
- package/dist/chunk-UYHETUX3.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__/remark-strip-citations.test.ts +0 -52
- 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-RE3KVI2Q.js.map → chunk-3PDTGDOA.js.map} +0 -0
- /package/dist/{chunk-5X7AJ67N.js.map → chunk-436G4DI4.js.map} +0 -0
- /package/dist/{chunk-7A6J4JTW.js.map → chunk-6BFEJXMI.js.map} +0 -0
- /package/dist/{chunk-RQ5PYSRJ.js.map → chunk-BLJEPNMJ.js.map} +0 -0
- /package/dist/{chunk-WFWWQO2I.js.map → chunk-F3YRPKUA.js.map} +0 -0
- /package/dist/{chunk-FDWGO244.js.map → chunk-LODCKZ72.js.map} +0 -0
- /package/dist/{chunk-E2QFZDCP.js.map → chunk-OKD7MX7Y.js.map} +0 -0
- /package/dist/{chunk-OW3FUJ2I.js.map → chunk-P24OI2CD.js.map} +0 -0
- /package/dist/{chunk-QSO24MEK.js.map → chunk-R2U2D7LU.js.map} +0 -0
- /package/dist/{chunk-KQWI25NP.js.map → chunk-SI57FQZ2.js.map} +0 -0
- /package/dist/{chunk-JQO47UHC.js.map → chunk-SVR75PFW.js.map} +0 -0
- /package/dist/{chunk-OZRT3ZI3.js.map → chunk-V4WUSOGS.js.map} +0 -0
- /package/dist/{chunk-UODM3HJC.js.map → chunk-VWNU2PFX.js.map} +0 -0
- /package/dist/{chunk-QYDIVL4X.js.map → chunk-YB5DFQKD.js.map} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flamingo-stack/openframe-frontend-core",
|
|
3
|
-
"version": "0.0.587-
|
|
3
|
+
"version": "0.0.587-2000.5532.1",
|
|
4
4
|
"description": "Shared design system and components for all Flamingo platforms",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -444,7 +444,6 @@
|
|
|
444
444
|
"@hookform/resolvers": "~5.2.2",
|
|
445
445
|
"@storybook/nextjs-vite": "^10.1.11",
|
|
446
446
|
"@svgr/cli": "^8.1.0",
|
|
447
|
-
"@tanstack/react-query": "^5.101.4",
|
|
448
447
|
"@testing-library/jest-dom": "^6.9.1",
|
|
449
448
|
"@testing-library/react": "^16.3.1",
|
|
450
449
|
"@types/node": "^24.10.4",
|
|
@@ -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>
|