@runtypelabs/persona 4.14.0 → 4.15.0
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/README.md +4 -4
- package/dist/animations/glyph-cycle.d.cts +1 -1
- package/dist/animations/glyph-cycle.d.ts +1 -1
- package/dist/animations/{types-DveIaNx6.d.cts → types-BpWGm7_6.d.cts} +17 -0
- package/dist/animations/{types-DveIaNx6.d.ts → types-BpWGm7_6.d.ts} +17 -0
- package/dist/animations/wipe.d.cts +1 -1
- package/dist/animations/wipe.d.ts +1 -1
- package/dist/chunk-QJ4LV2KW.js +1 -0
- package/dist/codegen.cjs +12 -11
- package/dist/codegen.js +14 -13
- package/dist/{context-mentions-ONG7A7M6.js → context-mentions-BTSTIBFC.js} +1 -1
- package/dist/context-mentions.cjs +11 -8
- package/dist/context-mentions.d.cts +241 -16
- package/dist/context-mentions.d.ts +241 -16
- package/dist/context-mentions.js +11 -8
- package/dist/index.cjs +67 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +301 -41
- package/dist/index.d.ts +301 -41
- package/dist/index.global.js +59 -56
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +67 -66
- package/dist/index.js.map +1 -1
- package/dist/launcher.global.js +5 -2
- package/dist/launcher.global.js.map +1 -1
- package/dist/smart-dom-reader.d.cts +241 -16
- package/dist/smart-dom-reader.d.ts +241 -16
- package/dist/theme-editor-preview.cjs +66 -66
- package/dist/theme-editor-preview.d.cts +255 -17
- package/dist/theme-editor-preview.d.ts +255 -17
- package/dist/theme-editor-preview.js +56 -56
- package/dist/theme-editor.cjs +5 -5
- package/dist/theme-editor.d.cts +242 -17
- package/dist/theme-editor.d.ts +242 -17
- package/dist/theme-editor.js +7 -7
- package/dist/theme-reference.cjs +1 -1
- package/dist/theme-reference.d.cts +38 -0
- package/dist/theme-reference.d.ts +38 -0
- package/dist/theme-reference.js +1 -1
- package/dist/widget.css +1 -1
- package/package.json +1 -1
- package/src/ask-user-question-tool.ts +10 -7
- package/src/components/artifact-preview.ts +1 -1
- package/src/components/composer-parts.test.ts +103 -4
- package/src/components/composer-parts.ts +53 -15
- package/src/components/messages.ts +5 -2
- package/src/components/panel.ts +31 -5
- package/src/components/pill-composer-builder.test.ts +18 -0
- package/src/components/suggestions.test.ts +233 -0
- package/src/components/suggestions.ts +463 -81
- package/src/components/widget-view.ts +4 -0
- package/src/defaults.ts +5 -3
- package/src/generated/runtype-openapi-contract.ts +17 -0
- package/src/index-core.ts +18 -2
- package/src/launcher-global.ts +1 -1
- package/src/plugins/types.ts +71 -3
- package/src/presets.ts +3 -3
- package/src/session.ts +10 -8
- package/src/styles/widget-styles.test.ts +162 -0
- package/src/styles/widget.css +430 -0
- package/src/suggest-replies-tool.test.ts +233 -2
- package/src/suggest-replies-tool.ts +115 -39
- package/src/theme-editor/preview-utils.test.ts +34 -0
- package/src/theme-editor/preview-utils.ts +77 -2
- package/src/theme-editor/sections.test.ts +33 -0
- package/src/theme-editor/sections.ts +53 -5
- package/src/theme-reference.ts +9 -2
- package/src/types/theme.ts +32 -0
- package/src/types.ts +152 -16
- package/src/ui.detached-panel.test.ts +1 -1
- package/src/ui.suggest-replies.test.ts +1066 -1
- package/src/ui.ts +268 -20
- package/src/utils/artifact-file.test.ts +1 -1
- package/src/utils/code-generators.test.ts +40 -0
- package/src/utils/code-generators.ts +22 -0
- package/src/utils/config-merge.test.ts +24 -0
- package/src/utils/icons.test.ts +26 -0
- package/src/utils/icons.ts +8 -0
- package/src/utils/theme.test.ts +79 -0
- package/src/utils/tokens.ts +64 -0
- package/dist/chunk-MMUPR2JW.js +0 -1
package/src/styles/widget.css
CHANGED
|
@@ -236,6 +236,418 @@
|
|
|
236
236
|
border-radius: var(--persona-button-radius);
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
/* Suggestions ----------------------------------------------------------- */
|
|
240
|
+
|
|
241
|
+
.persona-suggestions {
|
|
242
|
+
--persona-suggestion-icon-size: var(
|
|
243
|
+
--persona-components-suggestion-chip-iconSize,
|
|
244
|
+
16px
|
|
245
|
+
);
|
|
246
|
+
display: flex;
|
|
247
|
+
align-items: stretch;
|
|
248
|
+
flex-shrink: 0;
|
|
249
|
+
gap: 8px;
|
|
250
|
+
min-width: 0;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.persona-suggestions[hidden] {
|
|
254
|
+
display: none !important;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.persona-suggestions[data-persona-suggestion-surface="starter"] {
|
|
258
|
+
margin-top: 16px;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
[data-persona-suggestions="follow-up"] {
|
|
262
|
+
/* The transcript body uses a roomy 24px section gap; pull contextual
|
|
263
|
+
follow-ups closer to the answer they belong to. */
|
|
264
|
+
margin-top: -12px;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.persona-suggestions[data-variant="card"] {
|
|
268
|
+
--persona-suggestion-icon-size: var(
|
|
269
|
+
--persona-components-suggestion-card-iconSize,
|
|
270
|
+
18px
|
|
271
|
+
);
|
|
272
|
+
display: grid;
|
|
273
|
+
grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/* Welcome-surface policy, not a card property: the cap keeps a wide inline
|
|
277
|
+
panel at two starter columns instead of one stretched row. */
|
|
278
|
+
.persona-suggestions[data-variant="card"][data-persona-suggestion-surface="starter"] {
|
|
279
|
+
max-width: 600px;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.persona-suggestions[data-variant="list"] {
|
|
283
|
+
--persona-suggestion-icon-size: var(
|
|
284
|
+
--persona-components-suggestion-list-iconSize,
|
|
285
|
+
17px
|
|
286
|
+
);
|
|
287
|
+
flex-direction: column;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.persona-suggestions[data-variant="chip"][data-overflow="scroll"] {
|
|
291
|
+
--persona-suggestion-scroll-fade-size: 32px;
|
|
292
|
+
flex-wrap: nowrap;
|
|
293
|
+
overflow-x: auto;
|
|
294
|
+
overscroll-behavior-inline: contain;
|
|
295
|
+
scrollbar-width: thin;
|
|
296
|
+
scroll-snap-type: inline proximity;
|
|
297
|
+
padding: 2px;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.persona-suggestions[data-variant="chip"][data-overflow="scroll"][data-scroll-right]:not(
|
|
301
|
+
[data-scroll-left]
|
|
302
|
+
) {
|
|
303
|
+
-webkit-mask-image: linear-gradient(
|
|
304
|
+
to right,
|
|
305
|
+
#000 0,
|
|
306
|
+
#000 calc(100% - var(--persona-suggestion-scroll-fade-size)),
|
|
307
|
+
transparent 100%
|
|
308
|
+
);
|
|
309
|
+
mask-image: linear-gradient(
|
|
310
|
+
to right,
|
|
311
|
+
#000 0,
|
|
312
|
+
#000 calc(100% - var(--persona-suggestion-scroll-fade-size)),
|
|
313
|
+
transparent 100%
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.persona-suggestions[data-variant="chip"][data-overflow="scroll"][data-scroll-left]:not(
|
|
318
|
+
[data-scroll-right]
|
|
319
|
+
) {
|
|
320
|
+
-webkit-mask-image: linear-gradient(
|
|
321
|
+
to right,
|
|
322
|
+
transparent 0,
|
|
323
|
+
#000 var(--persona-suggestion-scroll-fade-size),
|
|
324
|
+
#000 100%
|
|
325
|
+
);
|
|
326
|
+
mask-image: linear-gradient(
|
|
327
|
+
to right,
|
|
328
|
+
transparent 0,
|
|
329
|
+
#000 var(--persona-suggestion-scroll-fade-size),
|
|
330
|
+
#000 100%
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.persona-suggestions[data-variant="chip"][data-overflow="scroll"][data-scroll-left][data-scroll-right] {
|
|
335
|
+
-webkit-mask-image: linear-gradient(
|
|
336
|
+
to right,
|
|
337
|
+
transparent 0,
|
|
338
|
+
#000 var(--persona-suggestion-scroll-fade-size),
|
|
339
|
+
#000 calc(100% - var(--persona-suggestion-scroll-fade-size)),
|
|
340
|
+
transparent 100%
|
|
341
|
+
);
|
|
342
|
+
mask-image: linear-gradient(
|
|
343
|
+
to right,
|
|
344
|
+
transparent 0,
|
|
345
|
+
#000 var(--persona-suggestion-scroll-fade-size),
|
|
346
|
+
#000 calc(100% - var(--persona-suggestion-scroll-fade-size)),
|
|
347
|
+
transparent 100%
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.persona-suggestions[data-variant="chip"][data-overflow="wrap"] {
|
|
352
|
+
flex-wrap: wrap;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.persona-suggestion {
|
|
356
|
+
appearance: none;
|
|
357
|
+
display: flex;
|
|
358
|
+
align-items: center;
|
|
359
|
+
justify-content: flex-start;
|
|
360
|
+
min-width: 0;
|
|
361
|
+
text-align: left;
|
|
362
|
+
font-family: inherit;
|
|
363
|
+
font-weight: 500;
|
|
364
|
+
cursor: pointer;
|
|
365
|
+
transition:
|
|
366
|
+
background-color 140ms ease,
|
|
367
|
+
border-color 140ms ease,
|
|
368
|
+
color 140ms ease,
|
|
369
|
+
box-shadow 140ms ease,
|
|
370
|
+
transform 100ms ease;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.persona-suggestion--chip {
|
|
374
|
+
flex: 0 0 auto;
|
|
375
|
+
scroll-snap-align: start;
|
|
376
|
+
min-height: var(--persona-components-suggestion-chip-minHeight, 36px);
|
|
377
|
+
gap: var(--persona-components-suggestion-chip-gap, 0.5rem);
|
|
378
|
+
padding: var(
|
|
379
|
+
--persona-components-suggestion-chip-padding,
|
|
380
|
+
0.5rem 0.875rem
|
|
381
|
+
);
|
|
382
|
+
border: 1px solid
|
|
383
|
+
var(--persona-components-suggestion-chip-border, var(--persona-border));
|
|
384
|
+
border-radius: var(
|
|
385
|
+
--persona-components-suggestion-chip-borderRadius,
|
|
386
|
+
9999px
|
|
387
|
+
);
|
|
388
|
+
background: var(
|
|
389
|
+
--persona-components-suggestion-chip-background,
|
|
390
|
+
var(--persona-surface)
|
|
391
|
+
);
|
|
392
|
+
color: var(
|
|
393
|
+
--persona-components-suggestion-chip-foreground,
|
|
394
|
+
var(--persona-text)
|
|
395
|
+
);
|
|
396
|
+
font-size: var(--persona-components-suggestion-chip-fontSize, 0.8125rem);
|
|
397
|
+
line-height: var(--persona-components-suggestion-chip-lineHeight, 1.25);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.persona-suggestion--card {
|
|
401
|
+
align-items: flex-start;
|
|
402
|
+
min-height: var(--persona-components-suggestion-card-minHeight, 72px);
|
|
403
|
+
gap: var(--persona-components-suggestion-card-gap, 0.625rem);
|
|
404
|
+
padding: var(--persona-components-suggestion-card-padding, 1rem);
|
|
405
|
+
border: 1px solid
|
|
406
|
+
var(--persona-components-suggestion-card-border, var(--persona-border));
|
|
407
|
+
border-radius: var(
|
|
408
|
+
--persona-components-suggestion-card-borderRadius,
|
|
409
|
+
var(--persona-radius-xl)
|
|
410
|
+
);
|
|
411
|
+
background: var(
|
|
412
|
+
--persona-components-suggestion-card-background,
|
|
413
|
+
var(--persona-surface)
|
|
414
|
+
);
|
|
415
|
+
color: var(
|
|
416
|
+
--persona-components-suggestion-card-foreground,
|
|
417
|
+
var(--persona-text)
|
|
418
|
+
);
|
|
419
|
+
box-shadow: var(--persona-components-suggestion-card-shadow, none);
|
|
420
|
+
font-size: var(--persona-components-suggestion-card-fontSize, 0.875rem);
|
|
421
|
+
line-height: var(--persona-components-suggestion-card-lineHeight, 1.35);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.persona-suggestion--list {
|
|
425
|
+
align-items: flex-start;
|
|
426
|
+
width: 100%;
|
|
427
|
+
min-height: var(--persona-components-suggestion-list-minHeight, 44px);
|
|
428
|
+
gap: var(--persona-components-suggestion-list-gap, 0.625rem);
|
|
429
|
+
padding: var(--persona-components-suggestion-list-padding, 0.75rem);
|
|
430
|
+
border: 1px solid
|
|
431
|
+
var(--persona-components-suggestion-list-border, var(--persona-border));
|
|
432
|
+
border-radius: var(
|
|
433
|
+
--persona-components-suggestion-list-borderRadius,
|
|
434
|
+
var(--persona-radius-md)
|
|
435
|
+
);
|
|
436
|
+
background: var(
|
|
437
|
+
--persona-components-suggestion-list-background,
|
|
438
|
+
transparent
|
|
439
|
+
);
|
|
440
|
+
color: var(
|
|
441
|
+
--persona-components-suggestion-list-foreground,
|
|
442
|
+
var(--persona-text)
|
|
443
|
+
);
|
|
444
|
+
font-size: var(--persona-components-suggestion-list-fontSize, 0.875rem);
|
|
445
|
+
line-height: var(--persona-components-suggestion-list-lineHeight, 1.35);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.persona-suggestion__icon,
|
|
449
|
+
.persona-suggestion__arrow {
|
|
450
|
+
flex: 0 0 auto;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/* The taller viewport box centers the graphic on the label's first line box;
|
|
454
|
+
the icon keeps its size because the width constraint drives `meet` scaling. */
|
|
455
|
+
.persona-suggestion--card .persona-suggestion__icon {
|
|
456
|
+
block-size: calc(
|
|
457
|
+
1em * var(--persona-components-suggestion-card-lineHeight, 1.35)
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.persona-suggestion--list .persona-suggestion__icon {
|
|
462
|
+
block-size: calc(
|
|
463
|
+
1em * var(--persona-components-suggestion-list-lineHeight, 1.35)
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
.persona-suggestion__copy {
|
|
468
|
+
display: flex;
|
|
469
|
+
flex: 1 1 auto;
|
|
470
|
+
min-width: 0;
|
|
471
|
+
flex-direction: column;
|
|
472
|
+
gap: 3px;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
.persona-suggestion__label {
|
|
476
|
+
overflow-wrap: anywhere;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.persona-suggestion__description {
|
|
480
|
+
color: var(--persona-text-muted);
|
|
481
|
+
font-size: 0.75rem;
|
|
482
|
+
font-weight: 400;
|
|
483
|
+
line-height: 1.35;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/* Hover-revealed: the arrow is an affordance hint, not standing decoration. */
|
|
487
|
+
.persona-suggestion__arrow {
|
|
488
|
+
margin-left: auto;
|
|
489
|
+
opacity: 0;
|
|
490
|
+
transition: opacity 140ms ease;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
.persona-suggestion:focus-visible .persona-suggestion__arrow {
|
|
494
|
+
opacity: 0.55;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/* Quiet accent, never a solid fill: an emphasized item reads as a sibling of
|
|
498
|
+
the set rather than a CTA among ghost buttons. */
|
|
499
|
+
.persona-suggestion[data-emphasis="primary"] {
|
|
500
|
+
border-color: var(--persona-primary);
|
|
501
|
+
background: transparent;
|
|
502
|
+
background: color-mix(in srgb, var(--persona-primary) 6%, transparent);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.persona-suggestion[data-emphasis="primary"] .persona-suggestion__icon {
|
|
506
|
+
color: var(--persona-primary);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
@media (hover: hover) {
|
|
510
|
+
.persona-suggestion--chip:hover {
|
|
511
|
+
border-color: var(
|
|
512
|
+
--persona-components-suggestion-chip-hoverBorder,
|
|
513
|
+
var(--persona-border)
|
|
514
|
+
);
|
|
515
|
+
background: var(
|
|
516
|
+
--persona-components-suggestion-chip-hoverBackground,
|
|
517
|
+
var(--persona-container)
|
|
518
|
+
);
|
|
519
|
+
color: var(
|
|
520
|
+
--persona-components-suggestion-chip-hoverForeground,
|
|
521
|
+
inherit
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
.persona-suggestion:hover .persona-suggestion__arrow {
|
|
526
|
+
opacity: 0.55;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
.persona-suggestion--card:hover {
|
|
530
|
+
border-color: var(
|
|
531
|
+
--persona-components-suggestion-card-hoverBorder,
|
|
532
|
+
var(--persona-border)
|
|
533
|
+
);
|
|
534
|
+
background: var(
|
|
535
|
+
--persona-components-suggestion-card-hoverBackground,
|
|
536
|
+
var(--persona-container)
|
|
537
|
+
);
|
|
538
|
+
box-shadow: var(--persona-shadow-sm);
|
|
539
|
+
transform: translateY(-1px);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.persona-suggestion--list:hover {
|
|
543
|
+
border-color: var(
|
|
544
|
+
--persona-components-suggestion-list-hoverBorder,
|
|
545
|
+
var(--persona-border)
|
|
546
|
+
);
|
|
547
|
+
background: var(
|
|
548
|
+
--persona-components-suggestion-list-hoverBackground,
|
|
549
|
+
var(--persona-container)
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
.persona-suggestion[data-emphasis="primary"]:hover {
|
|
554
|
+
border-color: var(--persona-primary);
|
|
555
|
+
background: transparent;
|
|
556
|
+
background: color-mix(in srgb, var(--persona-primary) 12%, transparent);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
.persona-suggestion:focus-visible {
|
|
561
|
+
outline: 2px solid
|
|
562
|
+
var(
|
|
563
|
+
--persona-components-suggestion-chip-focusRing,
|
|
564
|
+
var(--persona-accent)
|
|
565
|
+
);
|
|
566
|
+
outline-offset: 2px;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
.persona-suggestion--card:focus-visible {
|
|
570
|
+
outline-color: var(
|
|
571
|
+
--persona-components-suggestion-card-focusRing,
|
|
572
|
+
var(--persona-accent)
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
.persona-suggestion--list:focus-visible {
|
|
577
|
+
outline-color: var(
|
|
578
|
+
--persona-components-suggestion-list-focusRing,
|
|
579
|
+
var(--persona-accent)
|
|
580
|
+
);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
.persona-suggestion--chip:active {
|
|
584
|
+
background: var(
|
|
585
|
+
--persona-components-suggestion-chip-pressedBackground,
|
|
586
|
+
var(--persona-container)
|
|
587
|
+
);
|
|
588
|
+
transform: scale(0.98);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.persona-suggestion--card:active {
|
|
592
|
+
background: var(
|
|
593
|
+
--persona-components-suggestion-card-pressedBackground,
|
|
594
|
+
var(--persona-container)
|
|
595
|
+
);
|
|
596
|
+
transform: translateY(1px);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
.persona-suggestion--list:active {
|
|
600
|
+
background: var(
|
|
601
|
+
--persona-components-suggestion-list-pressedBackground,
|
|
602
|
+
var(--persona-container)
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
.persona-suggestion[data-emphasis="primary"]:active {
|
|
607
|
+
background: transparent;
|
|
608
|
+
background: color-mix(in srgb, var(--persona-primary) 16%, transparent);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
.persona-suggestion:disabled {
|
|
612
|
+
cursor: not-allowed;
|
|
613
|
+
opacity: var(
|
|
614
|
+
--persona-components-suggestion-chip-disabledOpacity,
|
|
615
|
+
0.5
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
.persona-suggestion--card:disabled {
|
|
620
|
+
opacity: var(
|
|
621
|
+
--persona-components-suggestion-card-disabledOpacity,
|
|
622
|
+
0.5
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
.persona-suggestion--list:disabled {
|
|
627
|
+
opacity: var(
|
|
628
|
+
--persona-components-suggestion-list-disabledOpacity,
|
|
629
|
+
0.5
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
@media (pointer: coarse) {
|
|
634
|
+
.persona-suggestion {
|
|
635
|
+
min-height: 44px;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/* No hover to reveal it, so the arrow would be permanent decoration. */
|
|
639
|
+
.persona-suggestion__arrow {
|
|
640
|
+
display: none;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
@media (prefers-reduced-motion: reduce) {
|
|
645
|
+
.persona-suggestion,
|
|
646
|
+
.persona-suggestion__arrow {
|
|
647
|
+
transition: none;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
239
651
|
/* Composer ghost icon buttons (attachment + mention): transparent, neutral
|
|
240
652
|
foreground, subtle hover. Themeable via `components.button.ghost.*`
|
|
241
653
|
→ `--persona-button-ghost-*`. The `[data-persona-root]` prefix outranks the
|
|
@@ -990,6 +1402,24 @@
|
|
|
990
1402
|
box-shadow: var(--persona-composer-shadow, none);
|
|
991
1403
|
}
|
|
992
1404
|
|
|
1405
|
+
/* Composer spacing, themeable via `components.composer.padding` / `.gap`.
|
|
1406
|
+
The `[data-persona-root]` prefix outranks the `persona-px-4 persona-py-3
|
|
1407
|
+
persona-gap-2` utilities the form still carries; the fallbacks reproduce
|
|
1408
|
+
them exactly. The pill composer is excluded because it sets its own
|
|
1409
|
+
grid padding/gap (see `.persona-pill-composer`). */
|
|
1410
|
+
[data-persona-root] .persona-widget-composer:not(.persona-pill-composer) {
|
|
1411
|
+
padding: var(--persona-composer-padding, 0.75rem 1rem);
|
|
1412
|
+
gap: var(--persona-composer-gap, 0.5rem);
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
/* Composer type scale, themeable via `components.composer.fontSize` /
|
|
1416
|
+
`.lineHeight`. Outranks the `persona-text-sm` utility on the textarea;
|
|
1417
|
+
fallbacks match it. Applies to the pill composer's textarea too. */
|
|
1418
|
+
[data-persona-root] .persona-composer-textarea {
|
|
1419
|
+
font-size: var(--persona-composer-font-size, 0.875rem);
|
|
1420
|
+
line-height: var(--persona-composer-line-height, 1.25rem);
|
|
1421
|
+
}
|
|
1422
|
+
|
|
993
1423
|
.persona-form-grid {
|
|
994
1424
|
display: flex;
|
|
995
1425
|
flex-direction: column;
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
SUGGEST_REPLIES_TOOL_NAME,
|
|
7
7
|
latestAgentSuggestions,
|
|
8
8
|
parseSuggestRepliesPayload,
|
|
9
|
+
resolveFollowUpsFeature,
|
|
9
10
|
} from "./suggest-replies-tool";
|
|
10
11
|
import {
|
|
11
12
|
ASK_USER_QUESTION_CLIENT_TOOL,
|
|
@@ -26,14 +27,27 @@ describe("SUGGEST_REPLIES_CLIENT_TOOL definition", () => {
|
|
|
26
27
|
expect(SUGGEST_REPLIES_CLIENT_TOOL.annotations?.readOnlyHint).toBe(true);
|
|
27
28
|
});
|
|
28
29
|
|
|
29
|
-
it("bounds suggestions to
|
|
30
|
+
it("bounds suggestions to semantic-only object items", () => {
|
|
30
31
|
const suggestions =
|
|
31
32
|
SUGGEST_REPLIES_PARAMETERS_SCHEMA.properties.suggestions;
|
|
32
33
|
expect(suggestions.minItems).toBe(1);
|
|
33
34
|
expect(suggestions.maxItems).toBe(SUGGEST_REPLIES_MAX);
|
|
34
|
-
expect(suggestions.items.
|
|
35
|
+
expect(suggestions.items.type).toBe("object");
|
|
36
|
+
expect(suggestions.items.required).toEqual(["label"]);
|
|
37
|
+
expect(suggestions.items.additionalProperties).toBe(false);
|
|
35
38
|
expect(SUGGEST_REPLIES_PARAMETERS_SCHEMA.required).toEqual(["suggestions"]);
|
|
36
39
|
});
|
|
40
|
+
|
|
41
|
+
it("advertises no oneOf union and no presentation fields", () => {
|
|
42
|
+
const items = SUGGEST_REPLIES_PARAMETERS_SCHEMA.properties.suggestions
|
|
43
|
+
.items as Record<string, unknown>;
|
|
44
|
+
expect(items.oneOf).toBeUndefined();
|
|
45
|
+
expect(Object.keys(items.properties as Record<string, unknown>)).toEqual([
|
|
46
|
+
"label",
|
|
47
|
+
"prompt",
|
|
48
|
+
"description",
|
|
49
|
+
]);
|
|
50
|
+
});
|
|
37
51
|
});
|
|
38
52
|
|
|
39
53
|
describe("parseSuggestRepliesPayload", () => {
|
|
@@ -55,6 +69,44 @@ describe("parseSuggestRepliesPayload", () => {
|
|
|
55
69
|
).toEqual(["A", "B"]);
|
|
56
70
|
});
|
|
57
71
|
|
|
72
|
+
it("parses rich items while dropping malformed objects", () => {
|
|
73
|
+
expect(
|
|
74
|
+
parseSuggestRepliesPayload({
|
|
75
|
+
suggestions: [
|
|
76
|
+
{
|
|
77
|
+
label: " Pricing ",
|
|
78
|
+
prompt: " Tell me about pricing ",
|
|
79
|
+
description: " Compare plans ",
|
|
80
|
+
},
|
|
81
|
+
{ label: " " },
|
|
82
|
+
{ nope: true },
|
|
83
|
+
],
|
|
84
|
+
}),
|
|
85
|
+
).toEqual([
|
|
86
|
+
{
|
|
87
|
+
label: "Pricing",
|
|
88
|
+
prompt: "Tell me about pricing",
|
|
89
|
+
description: "Compare plans",
|
|
90
|
+
},
|
|
91
|
+
]);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("strips presentation fields a model may send anyway", () => {
|
|
95
|
+
expect(
|
|
96
|
+
parseSuggestRepliesPayload({
|
|
97
|
+
suggestions: [
|
|
98
|
+
{
|
|
99
|
+
id: "pricing",
|
|
100
|
+
label: "Pricing",
|
|
101
|
+
icon: "dollar-sign",
|
|
102
|
+
behavior: "fill",
|
|
103
|
+
emphasis: "primary",
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
}),
|
|
107
|
+
).toEqual([{ label: "Pricing" }]);
|
|
108
|
+
});
|
|
109
|
+
|
|
58
110
|
it("truncates past the cap with a console warning", () => {
|
|
59
111
|
const warn = vi.spyOn(console, "warn").mockImplementation(() => undefined);
|
|
60
112
|
const result = parseSuggestRepliesPayload({
|
|
@@ -145,6 +197,172 @@ describe("latestAgentSuggestions", () => {
|
|
|
145
197
|
});
|
|
146
198
|
});
|
|
147
199
|
|
|
200
|
+
describe("resolveFollowUpsFeature", () => {
|
|
201
|
+
const cfg = (config: Partial<AgentWidgetConfig>): AgentWidgetConfig =>
|
|
202
|
+
config as AgentWidgetConfig;
|
|
203
|
+
|
|
204
|
+
it("defaults to enabled and not exposed", () => {
|
|
205
|
+
expect(resolveFollowUpsFeature(undefined)).toEqual({
|
|
206
|
+
enabled: true,
|
|
207
|
+
expose: false,
|
|
208
|
+
});
|
|
209
|
+
expect(resolveFollowUpsFeature(cfg({}))).toEqual({
|
|
210
|
+
enabled: true,
|
|
211
|
+
expose: false,
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it("reads each key from suggestions.followUps", () => {
|
|
216
|
+
expect(
|
|
217
|
+
resolveFollowUpsFeature(
|
|
218
|
+
cfg({ suggestions: { followUps: { expose: true } } }),
|
|
219
|
+
),
|
|
220
|
+
).toEqual({ enabled: true, expose: true });
|
|
221
|
+
expect(
|
|
222
|
+
resolveFollowUpsFeature(
|
|
223
|
+
cfg({ suggestions: { followUps: { enabled: false } } }),
|
|
224
|
+
),
|
|
225
|
+
).toEqual({ enabled: false, expose: false });
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it("falls back per key to the deprecated features.suggestReplies alias", () => {
|
|
229
|
+
expect(
|
|
230
|
+
resolveFollowUpsFeature(
|
|
231
|
+
cfg({
|
|
232
|
+
features: { suggestReplies: { enabled: true, expose: true } },
|
|
233
|
+
suggestions: { followUps: { variant: "card" } },
|
|
234
|
+
}),
|
|
235
|
+
),
|
|
236
|
+
).toEqual({ enabled: true, expose: true });
|
|
237
|
+
// New home supplies `expose`, alias still supplies `enabled`.
|
|
238
|
+
expect(
|
|
239
|
+
resolveFollowUpsFeature(
|
|
240
|
+
cfg({
|
|
241
|
+
features: { suggestReplies: { enabled: false } },
|
|
242
|
+
suggestions: { followUps: { expose: true } },
|
|
243
|
+
}),
|
|
244
|
+
),
|
|
245
|
+
).toEqual({ enabled: false, expose: false });
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it("does not silently re-enable when only presentation keys are added", () => {
|
|
249
|
+
expect(
|
|
250
|
+
resolveFollowUpsFeature(
|
|
251
|
+
cfg({
|
|
252
|
+
features: { suggestReplies: { enabled: false } },
|
|
253
|
+
suggestions: { followUps: { variant: "card" } },
|
|
254
|
+
}),
|
|
255
|
+
),
|
|
256
|
+
).toEqual({ enabled: false, expose: false });
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it("lets suggestions.followUps win on conflict", () => {
|
|
260
|
+
const warn = vi.spyOn(console, "warn").mockImplementation(() => undefined);
|
|
261
|
+
expect(
|
|
262
|
+
resolveFollowUpsFeature(
|
|
263
|
+
cfg({
|
|
264
|
+
features: { suggestReplies: { enabled: false, expose: false } },
|
|
265
|
+
suggestions: { followUps: { enabled: true, expose: true } },
|
|
266
|
+
}),
|
|
267
|
+
),
|
|
268
|
+
).toEqual({ enabled: true, expose: true });
|
|
269
|
+
expect(
|
|
270
|
+
resolveFollowUpsFeature(
|
|
271
|
+
cfg({
|
|
272
|
+
features: { suggestReplies: { enabled: true, expose: true } },
|
|
273
|
+
suggestions: { followUps: { enabled: false } },
|
|
274
|
+
}),
|
|
275
|
+
),
|
|
276
|
+
).toEqual({ enabled: false, expose: false });
|
|
277
|
+
warn.mockRestore();
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it("forces expose off when enabled resolves false", () => {
|
|
281
|
+
expect(
|
|
282
|
+
resolveFollowUpsFeature(
|
|
283
|
+
cfg({
|
|
284
|
+
suggestions: { followUps: { enabled: false, expose: true } },
|
|
285
|
+
}),
|
|
286
|
+
),
|
|
287
|
+
).toEqual({ enabled: false, expose: false });
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
describe("resolveFollowUpsFeature conflict warnings", () => {
|
|
293
|
+
const cfg = (config: Partial<AgentWidgetConfig>): AgentWidgetConfig =>
|
|
294
|
+
config as AgentWidgetConfig;
|
|
295
|
+
|
|
296
|
+
// The dedupe Set is module-level, so a shared conflict signature would let
|
|
297
|
+
// one test mask another. Each case gets a fresh module.
|
|
298
|
+
const freshResolve = async () => {
|
|
299
|
+
vi.resetModules();
|
|
300
|
+
return (await import("./suggest-replies-tool")).resolveFollowUpsFeature;
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
it("warns once per key without debug", async () => {
|
|
304
|
+
const resolve = await freshResolve();
|
|
305
|
+
const warn = vi.spyOn(console, "warn").mockImplementation(() => undefined);
|
|
306
|
+
|
|
307
|
+
const production = cfg({
|
|
308
|
+
features: { suggestReplies: { enabled: true, expose: false } },
|
|
309
|
+
suggestions: { followUps: { enabled: false, expose: true } },
|
|
310
|
+
});
|
|
311
|
+
resolve(production);
|
|
312
|
+
resolve(production);
|
|
313
|
+
expect(warn).toHaveBeenCalledTimes(2);
|
|
314
|
+
expect(warn.mock.calls.map((call) => String(call[0]))).toEqual([
|
|
315
|
+
expect.stringContaining("suggestions.followUps.enabled"),
|
|
316
|
+
expect.stringContaining("suggestions.followUps.expose"),
|
|
317
|
+
]);
|
|
318
|
+
|
|
319
|
+
warn.mockRestore();
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it("warns with debug on too", async () => {
|
|
323
|
+
const resolve = await freshResolve();
|
|
324
|
+
const warn = vi.spyOn(console, "warn").mockImplementation(() => undefined);
|
|
325
|
+
|
|
326
|
+
const debug = cfg({
|
|
327
|
+
debug: true,
|
|
328
|
+
features: { suggestReplies: { enabled: true, expose: false } },
|
|
329
|
+
suggestions: { followUps: { enabled: false, expose: true } },
|
|
330
|
+
});
|
|
331
|
+
resolve(debug);
|
|
332
|
+
resolve(debug);
|
|
333
|
+
expect(warn).toHaveBeenCalledTimes(2);
|
|
334
|
+
|
|
335
|
+
warn.mockRestore();
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it("stays silent when a rebuilt config object repeats the same conflict", async () => {
|
|
339
|
+
const resolve = await freshResolve();
|
|
340
|
+
const warn = vi.spyOn(console, "warn").mockImplementation(() => undefined);
|
|
341
|
+
const conflicting = () =>
|
|
342
|
+
cfg({
|
|
343
|
+
features: { suggestReplies: { enabled: true } },
|
|
344
|
+
suggestions: { followUps: { enabled: false } },
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
// `controller.update()` rebuilds the config object on every call.
|
|
348
|
+
resolve(conflicting());
|
|
349
|
+
resolve(conflicting());
|
|
350
|
+
resolve(conflicting());
|
|
351
|
+
expect(warn).toHaveBeenCalledTimes(1);
|
|
352
|
+
|
|
353
|
+
// A different conflict is still a distinct developer error.
|
|
354
|
+
resolve(
|
|
355
|
+
cfg({
|
|
356
|
+
features: { suggestReplies: { expose: true } },
|
|
357
|
+
suggestions: { followUps: { expose: false } },
|
|
358
|
+
}),
|
|
359
|
+
);
|
|
360
|
+
expect(warn).toHaveBeenCalledTimes(2);
|
|
361
|
+
|
|
362
|
+
warn.mockRestore();
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
|
|
148
366
|
describe("builtInClientToolsForDispatch - suggest_replies gating", () => {
|
|
149
367
|
it("returns nothing by default (expose is opt-in)", () => {
|
|
150
368
|
expect(builtInClientToolsForDispatch(undefined)).toEqual([]);
|
|
@@ -173,6 +391,19 @@ describe("builtInClientToolsForDispatch - suggest_replies gating", () => {
|
|
|
173
391
|
).toEqual([]);
|
|
174
392
|
});
|
|
175
393
|
|
|
394
|
+
it("reads expose from suggestions.followUps too", () => {
|
|
395
|
+
expect(
|
|
396
|
+
builtInClientToolsForDispatch({
|
|
397
|
+
suggestions: { followUps: { expose: true } },
|
|
398
|
+
} as AgentWidgetConfig),
|
|
399
|
+
).toEqual([SUGGEST_REPLIES_CLIENT_TOOL]);
|
|
400
|
+
expect(
|
|
401
|
+
builtInClientToolsForDispatch({
|
|
402
|
+
suggestions: { followUps: { expose: true, enabled: false } },
|
|
403
|
+
} as AgentWidgetConfig),
|
|
404
|
+
).toEqual([]);
|
|
405
|
+
});
|
|
406
|
+
|
|
176
407
|
it("composes with ask_user_question, ask first", () => {
|
|
177
408
|
expect(
|
|
178
409
|
builtInClientToolsForDispatch({
|