@runtypelabs/persona 4.7.0 → 4.9.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.
Files changed (185) hide show
  1. package/README.md +1 -0
  2. package/dist/animations/glyph-cycle.cjs +2 -2
  3. package/dist/animations/glyph-cycle.d.cts +1 -1
  4. package/dist/animations/glyph-cycle.d.ts +1 -1
  5. package/dist/animations/glyph-cycle.js +2 -2
  6. package/dist/animations/{types-CSmiKRVa.d.cts → types-4ROVJ1gA.d.cts} +85 -3
  7. package/dist/animations/{types-CSmiKRVa.d.ts → types-4ROVJ1gA.d.ts} +85 -3
  8. package/dist/animations/wipe.cjs +2 -2
  9. package/dist/animations/wipe.d.cts +1 -1
  10. package/dist/animations/wipe.d.ts +1 -1
  11. package/dist/chunk-5EIIHQLQ.js +1 -0
  12. package/dist/chunk-IO5VVUKP.js +3 -0
  13. package/dist/chunk-IPVK3KOM.js +1 -0
  14. package/dist/chunk-UPO4GUFC.js +1 -0
  15. package/dist/codegen.cjs +13 -13
  16. package/dist/codegen.js +14 -14
  17. package/dist/context-mentions-7S5KVUTG.js +169 -0
  18. package/dist/context-mentions-inline-TTCN7ZM2.js +4 -0
  19. package/dist/context-mentions-inline.cjs +4 -0
  20. package/dist/context-mentions-inline.d.cts +203 -0
  21. package/dist/context-mentions-inline.d.ts +203 -0
  22. package/dist/context-mentions-inline.js +4 -0
  23. package/dist/context-mentions.cjs +295 -0
  24. package/dist/context-mentions.d.cts +7025 -0
  25. package/dist/context-mentions.d.ts +7025 -0
  26. package/dist/context-mentions.js +295 -0
  27. package/dist/index.cjs +99 -68
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +1507 -182
  30. package/dist/index.d.ts +1507 -182
  31. package/dist/index.global.js +88 -56
  32. package/dist/index.global.js.map +1 -1
  33. package/dist/index.js +99 -68
  34. package/dist/index.js.map +1 -1
  35. package/dist/install.global.js +1 -1
  36. package/dist/install.global.js.map +1 -1
  37. package/dist/launcher.global.js +3 -2
  38. package/dist/launcher.global.js.map +1 -1
  39. package/dist/markdown-parsers.js +24 -24
  40. package/dist/plugin-kit.cjs +1 -1
  41. package/dist/plugin-kit.d.cts +17 -0
  42. package/dist/plugin-kit.d.ts +17 -0
  43. package/dist/plugin-kit.js +1 -1
  44. package/dist/runtype-tts-entry-UJAEF7NZ.js +1 -0
  45. package/dist/runtype-tts.js +1 -1
  46. package/dist/session-reconnect-JKIJBHS5.js +1 -0
  47. package/dist/smart-dom-reader.cjs +18 -17
  48. package/dist/smart-dom-reader.d.cts +1260 -16
  49. package/dist/smart-dom-reader.d.ts +1260 -16
  50. package/dist/smart-dom-reader.js +18 -17
  51. package/dist/testing.cjs +3 -3
  52. package/dist/testing.js +3 -3
  53. package/dist/theme-editor-preview.cjs +260 -58
  54. package/dist/theme-editor-preview.d.cts +1234 -15
  55. package/dist/theme-editor-preview.d.ts +1234 -15
  56. package/dist/theme-editor-preview.js +87 -58
  57. package/dist/theme-editor.cjs +10 -10
  58. package/dist/theme-editor.d.cts +1226 -15
  59. package/dist/theme-editor.d.ts +1226 -15
  60. package/dist/theme-editor.js +10 -10
  61. package/dist/theme-reference.cjs +1 -1
  62. package/dist/theme-reference.d.cts +76 -0
  63. package/dist/theme-reference.d.ts +76 -0
  64. package/dist/theme-reference.js +1 -1
  65. package/dist/voice-worklet-player.cjs +2 -2
  66. package/dist/voice-worklet-player.js +2 -2
  67. package/dist/webmcp-polyfill.js +2 -2
  68. package/dist/widget.css +1 -1
  69. package/package.json +16 -5
  70. package/src/artifacts-session.test.ts +178 -0
  71. package/src/client.test.ts +255 -1
  72. package/src/client.ts +157 -60
  73. package/src/components/artifact-card.test.ts +333 -0
  74. package/src/components/artifact-card.ts +75 -28
  75. package/src/components/artifact-inline.test.ts +1328 -0
  76. package/src/components/artifact-inline.ts +920 -0
  77. package/src/components/artifact-pane.test.ts +1042 -0
  78. package/src/components/artifact-pane.ts +440 -131
  79. package/src/components/artifact-preview.test.ts +1155 -0
  80. package/src/components/artifact-preview.ts +994 -0
  81. package/src/components/composer-parts.ts +3 -12
  82. package/src/components/context-mention-button.test.ts +70 -0
  83. package/src/components/context-mention-button.ts +82 -0
  84. package/src/components/context-mention-chip.ts +134 -0
  85. package/src/components/context-mention-menu.test.ts +508 -0
  86. package/src/components/context-mention-menu.ts +0 -0
  87. package/src/components/message-bubble.test.ts +175 -0
  88. package/src/components/message-bubble.ts +177 -19
  89. package/src/components/panel.ts +7 -10
  90. package/src/components/pill-composer-builder.test.ts +6 -2
  91. package/src/components/pill-composer-builder.ts +6 -6
  92. package/src/components/reasoning-bubble.ts +1 -13
  93. package/src/components/registry.ts +38 -3
  94. package/src/components/tool-bubble.ts +1 -13
  95. package/src/context-mentions-bundle.test.ts +163 -0
  96. package/src/context-mentions-entry.ts +185 -0
  97. package/src/context-mentions-inline-entry.test.ts +136 -0
  98. package/src/context-mentions-inline-entry.ts +226 -0
  99. package/src/context-mentions-inline-loader.test.ts +30 -0
  100. package/src/context-mentions-inline-loader.ts +36 -0
  101. package/src/context-mentions-inline.ts +15 -0
  102. package/src/context-mentions-loader.ts +32 -0
  103. package/src/context-mentions.ts +16 -0
  104. package/src/defaults.ts +1 -0
  105. package/src/generated/runtype-openapi-contract.ts +55 -3
  106. package/src/index-core.ts +46 -1
  107. package/src/index-global.ts +51 -0
  108. package/src/index.ts +8 -0
  109. package/src/markdown-parsers-loader.test.ts +158 -0
  110. package/src/markdown-parsers-loader.ts +83 -27
  111. package/src/plugin-kit.test.ts +40 -0
  112. package/src/plugin-kit.ts +39 -5
  113. package/src/runtime/host-layout.test.ts +163 -0
  114. package/src/runtime/host-layout.ts +110 -7
  115. package/src/runtime/init.ts +18 -61
  116. package/src/session.mentions.test.ts +175 -0
  117. package/src/session.test.ts +52 -4
  118. package/src/session.ts +197 -27
  119. package/src/smart-dom-reader.test.ts +129 -2
  120. package/src/smart-dom-reader.ts +127 -1
  121. package/src/styles/context-mention-menu-css.ts +176 -0
  122. package/src/styles/widget.css +1002 -152
  123. package/src/theme-editor/preview.ts +2 -0
  124. package/src/theme-editor/sections.test.ts +26 -1
  125. package/src/theme-editor/sections.ts +13 -5
  126. package/src/theme-reference.ts +2 -2
  127. package/src/tool-call-display-defaults.test.ts +1 -0
  128. package/src/types/theme.ts +79 -0
  129. package/src/types.ts +1041 -17
  130. package/src/ui.artifact-pane-gating.test.ts +636 -0
  131. package/src/ui.component-directive.test.ts +104 -0
  132. package/src/ui.composer-bar.test.ts +60 -2
  133. package/src/ui.detached-panel.test.ts +1049 -0
  134. package/src/ui.mention-submit.test.ts +235 -0
  135. package/src/ui.tool-display.test.ts +51 -0
  136. package/src/ui.ts +998 -159
  137. package/src/utils/artifact-custom-actions.ts +128 -0
  138. package/src/utils/artifact-display.test.ts +42 -0
  139. package/src/utils/artifact-display.ts +84 -0
  140. package/src/utils/artifact-file.test.ts +116 -0
  141. package/src/utils/artifact-file.ts +117 -0
  142. package/src/utils/artifact-gate.test.ts +112 -5
  143. package/src/utils/artifact-gate.ts +39 -14
  144. package/src/utils/artifact-loading-status.ts +55 -0
  145. package/src/utils/artifact-status-label.ts +190 -0
  146. package/src/utils/buttons.ts +7 -1
  147. package/src/utils/chunk-loader.test.ts +97 -0
  148. package/src/utils/chunk-loader.ts +88 -0
  149. package/src/utils/code-highlight.test.ts +186 -0
  150. package/src/utils/code-highlight.ts +400 -0
  151. package/src/utils/composer-contenteditable.test.ts +507 -0
  152. package/src/utils/composer-contenteditable.ts +626 -0
  153. package/src/utils/composer-document.test.ts +280 -0
  154. package/src/utils/composer-document.ts +293 -0
  155. package/src/utils/composer-history.test.ts +35 -7
  156. package/src/utils/composer-history.ts +30 -20
  157. package/src/utils/composer-input.ts +159 -0
  158. package/src/utils/context-mention-controller.test.ts +1215 -0
  159. package/src/utils/context-mention-controller.ts +1186 -0
  160. package/src/utils/context-mention-manager.test.ts +422 -0
  161. package/src/utils/context-mention-manager.ts +410 -0
  162. package/src/utils/context-mention-orchestrator.test.ts +538 -0
  163. package/src/utils/context-mention-orchestrator.ts +348 -0
  164. package/src/utils/icons.ts +2 -0
  165. package/src/utils/live-region.test.ts +108 -0
  166. package/src/utils/live-region.ts +94 -0
  167. package/src/utils/mention-channels.ts +63 -0
  168. package/src/utils/mention-llm-format.test.ts +91 -0
  169. package/src/utils/mention-llm-format.ts +79 -0
  170. package/src/utils/mention-matcher.test.ts +86 -0
  171. package/src/utils/mention-matcher.ts +221 -0
  172. package/src/utils/mention-token.ts +72 -0
  173. package/src/utils/mention-trigger.test.ts +155 -0
  174. package/src/utils/mention-trigger.ts +156 -0
  175. package/src/utils/roving-tablist.test.ts +152 -0
  176. package/src/utils/roving-tablist.ts +111 -0
  177. package/src/utils/spinner.ts +45 -0
  178. package/src/utils/theme.test.ts +76 -4
  179. package/src/utils/theme.ts +7 -0
  180. package/src/utils/tokens.ts +118 -11
  181. package/src/utils/tool-loading-animation.test.ts +32 -0
  182. package/src/utils/tool-loading-animation.ts +24 -0
  183. package/dist/chunk-DFBSCFYN.js +0 -1
  184. package/dist/runtype-tts-entry-HFUV2UF7.js +0 -1
  185. package/dist/session-reconnect-U77QFUR7.js +0 -1
@@ -53,10 +53,6 @@
53
53
  align-items: center;
54
54
  }
55
55
 
56
- .persona-items-end {
57
- align-items: flex-end;
58
- }
59
-
60
56
  .persona-items-start {
61
57
  align-items: flex-start;
62
58
  }
@@ -238,6 +234,24 @@
238
234
  border-radius: var(--persona-button-radius);
239
235
  }
240
236
 
237
+ /* Composer ghost icon buttons (attachment + mention): transparent, neutral
238
+ foreground, subtle hover. Themeable via `components.button.ghost.*`
239
+ → `--persona-button-ghost-*`. The `[data-persona-root]` prefix outranks the
240
+ `.persona-rounded-button` full-radius above so the ghost radius wins. */
241
+ [data-persona-root] .persona-mention-button,
242
+ [data-persona-root] .persona-attachment-button {
243
+ background-color: var(--persona-button-ghost-bg, transparent);
244
+ color: var(--persona-button-ghost-fg, var(--persona-text, #111827));
245
+ border: none;
246
+ border-radius: var(--persona-button-ghost-radius, var(--persona-radius-md, 0.375rem));
247
+ transition: background-color 0.15s ease;
248
+ }
249
+
250
+ [data-persona-root] .persona-mention-button:hover,
251
+ [data-persona-root] .persona-attachment-button:hover {
252
+ background-color: var(--persona-button-ghost-hover-bg, rgba(0, 0, 0, 0.05));
253
+ }
254
+
241
255
  .persona-overflow-hidden {
242
256
  overflow: hidden;
243
257
  }
@@ -274,10 +288,6 @@
274
288
  background-color: #f3f4f6;
275
289
  }
276
290
 
277
- .persona-bg-gray-200 {
278
- background-color: #e5e7eb;
279
- }
280
-
281
291
  .persona-text-persona-primary {
282
292
  color: var(--persona-primary, #171717);
283
293
  }
@@ -436,14 +446,6 @@
436
446
  padding-left: 0.75rem;
437
447
  }
438
448
 
439
- .persona-pl-7 {
440
- padding-left: 1.75rem;
441
- }
442
-
443
- .persona-pr-7 {
444
- padding-right: 1.75rem;
445
- }
446
-
447
449
  .persona-pl-8 {
448
450
  padding-left: 2rem;
449
451
  }
@@ -526,22 +528,10 @@
526
528
  opacity: 0.5;
527
529
  }
528
530
 
529
- .persona-shadow-2xl {
530
- box-shadow: 0 30px 60px -15px rgba(15, 23, 42, 0.35);
531
- }
532
-
533
531
  .persona-shadow-sm {
534
532
  box-shadow: 0 5px 15px rgba(15, 23, 42, 0.08);
535
533
  }
536
534
 
537
- .persona-shadow-md {
538
- box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1);
539
- }
540
-
541
- .persona-shadow-lg {
542
- box-shadow: 0 20px 40px rgba(15, 23, 42, 0.18);
543
- }
544
-
545
535
  .persona-border {
546
536
  border-width: 1px;
547
537
  border-style: solid;
@@ -558,14 +548,6 @@
558
548
  border-bottom-color: rgba(148, 163, 184, 0.28);
559
549
  }
560
550
 
561
- .persona-border-gray-100 {
562
- border-color: #f1f5f9;
563
- }
564
-
565
- .persona-border-persona-secondary {
566
- border-color: var(--persona-secondary, #7c3aed);
567
- }
568
-
569
551
  .persona-border-persona-border {
570
552
  border-color: var(--persona-border, #e5e7eb);
571
553
  }
@@ -576,18 +558,6 @@
576
558
  border-top-color: rgba(148, 163, 184, 0.28);
577
559
  }
578
560
 
579
- .persona-border-t-persona-border {
580
- border-top-width: 1px;
581
- border-top-style: solid;
582
- border-top-color: var(--persona-border, #f1f5f9);
583
- }
584
-
585
- .persona-border-b-persona-border {
586
- border-bottom-width: 1px;
587
- border-bottom-style: solid;
588
- border-bottom-color: var(--persona-border, #f1f5f9);
589
- }
590
-
591
561
  .persona-border-t-persona-divider {
592
562
  border-top-width: 1px;
593
563
  border-top-style: solid;
@@ -664,13 +634,6 @@
664
634
  width: 2.5rem;
665
635
  }
666
636
 
667
- .persona-h-12 {
668
- height: 3rem;
669
- }
670
-
671
- .persona-w-12 {
672
- width: 3rem;
673
- }
674
637
 
675
638
  .persona-leading-relaxed {
676
639
  line-height: 1.75;
@@ -725,6 +688,10 @@
725
688
  flex-shrink: 0;
726
689
  }
727
690
 
691
+ .persona-shrink-0 {
692
+ flex-shrink: 0;
693
+ }
694
+
728
695
  .persona-min-w-0 {
729
696
  min-width: 0;
730
697
  }
@@ -773,8 +740,6 @@
773
740
  margin-top: 0.5rem;
774
741
  }
775
742
 
776
- .persona-h-12,
777
- .persona-w-12,
778
743
  .persona-h-10,
779
744
  .persona-w-10,
780
745
  .persona-h-8,
@@ -786,10 +751,6 @@
786
751
  text-align: right;
787
752
  }
788
753
 
789
- .hover\:persona-bg-gray-200:hover {
790
- background-color: #e5e7eb;
791
- }
792
-
793
754
  .hover\:persona-bg-persona-container:hover {
794
755
  background-color: var(--persona-container, #f8fafc);
795
756
  }
@@ -863,6 +824,10 @@
863
824
  overflow-y: auto;
864
825
  }
865
826
 
827
+ .persona-overflow-x-auto {
828
+ overflow-x: auto;
829
+ }
830
+
866
831
  .persona-widget-body {
867
832
  overscroll-behavior: contain;
868
833
  }
@@ -1340,68 +1305,6 @@
1340
1305
  font-size: 0.8125rem;
1341
1306
  }
1342
1307
 
1343
- /* Code block copy button */
1344
- .persona-code-block-wrapper {
1345
- position: relative;
1346
- margin: 0.5rem 0;
1347
- }
1348
-
1349
- .persona-code-block-wrapper pre {
1350
- margin: 0 !important;
1351
- border-top-left-radius: 0 !important;
1352
- border-top-right-radius: 0 !important;
1353
- }
1354
-
1355
- .persona-code-block-header {
1356
- display: flex;
1357
- align-items: center;
1358
- justify-content: space-between;
1359
- background-color: var(--persona-md-code-block-bg, #f3f4f6);
1360
- border: 1px solid var(--persona-md-code-block-border-color, #e5e7eb);
1361
- border-bottom: none;
1362
- border-top-left-radius: var(--persona-md-code-block-border-radius, 0.375rem);
1363
- border-top-right-radius: var(--persona-md-code-block-border-radius, 0.375rem);
1364
- padding: 0.25rem 0.5rem 0.25rem 0.75rem;
1365
- font-size: 0.75rem;
1366
- color: var(--persona-text-muted, #6b7280);
1367
- font-family: ui-monospace, SFMono-Regular, "SF Mono", Consolas, "Liberation Mono", Menlo, monospace;
1368
- }
1369
-
1370
- .persona-code-copy-btn {
1371
- display: inline-flex;
1372
- align-items: center;
1373
- gap: 0.25rem;
1374
- background: none;
1375
- border: 1px solid transparent;
1376
- border-radius: 0.25rem;
1377
- padding: 0.25rem 0.5rem;
1378
- cursor: pointer;
1379
- color: var(--persona-text-muted, #6b7280);
1380
- font-size: 0.75rem;
1381
- font-family: inherit;
1382
- line-height: 1;
1383
- transition: color 0.15s ease, border-color 0.15s ease;
1384
- }
1385
-
1386
- .persona-code-copy-btn:hover {
1387
- color: var(--persona-text, #111827);
1388
- border-color: var(--persona-md-code-block-border-color, #e5e7eb);
1389
- }
1390
-
1391
- .persona-code-copy-btn.persona-code-copied {
1392
- color: #16a34a;
1393
- }
1394
-
1395
- .persona-code-copy-btn.persona-code-copy-generating {
1396
- cursor: default;
1397
- opacity: 0.5;
1398
- }
1399
-
1400
- .persona-code-copy-btn.persona-code-copy-generating:hover {
1401
- color: var(--persona-text-muted, #6b7280);
1402
- border-color: transparent;
1403
- }
1404
-
1405
1308
  /* Ensure all links in chat bubbles have underlines */
1406
1309
  .persona-message-bubble a {
1407
1310
  text-decoration: underline;
@@ -2668,7 +2571,12 @@
2668
2571
  }
2669
2572
 
2670
2573
  /* Document toolbar preset: optional layout vars: --persona-artifact-doc-toolbar-icon-color, etc. */
2671
- [data-persona-root] .persona-artifact-toolbar-document .persona-artifact-doc-icon-btn {
2574
+ /* Document-toolbar icon/copy button styling is shared by the artifact pane's
2575
+ document preset and the inline chrome actions cluster, so both surfaces read
2576
+ the same `components.artifact.toolbar` tokens (the vars are emitted globally
2577
+ at the widget root). */
2578
+ [data-persona-root] .persona-artifact-toolbar-document .persona-artifact-doc-icon-btn,
2579
+ [data-persona-root] .persona-artifact-inline-chrome-actions .persona-artifact-doc-icon-btn {
2672
2580
  display: inline-flex;
2673
2581
  align-items: center;
2674
2582
  justify-content: center;
@@ -2681,17 +2589,20 @@
2681
2589
  line-height: 1;
2682
2590
  }
2683
2591
 
2684
- [data-persona-root] .persona-artifact-toolbar-document .persona-artifact-doc-icon-btn:hover {
2592
+ [data-persona-root] .persona-artifact-toolbar-document .persona-artifact-doc-icon-btn:hover,
2593
+ [data-persona-root] .persona-artifact-inline-chrome-actions .persona-artifact-doc-icon-btn:hover {
2685
2594
  color: var(--persona-artifact-toolbar-icon-hover-color, inherit);
2686
- background: var(--persona-artifact-toolbar-icon-hover-bg, var(--persona-container, #f3f4f6));
2595
+ background: var(--persona-artifact-toolbar-icon-hover-bg, var(--persona-icon-btn-hover-bg, var(--persona-container, #f3f4f6)));
2687
2596
  }
2688
2597
 
2689
- [data-persona-root] .persona-artifact-toolbar-document .persona-artifact-doc-icon-btn[aria-pressed="true"] {
2690
- background: var(--persona-artifact-doc-toggle-active-bg, var(--persona-container, #f3f4f6));
2691
- border-color: var(--persona-artifact-doc-toggle-active-border, var(--persona-border, #e5e7eb));
2598
+ [data-persona-root] .persona-artifact-toolbar-document .persona-artifact-doc-icon-btn[aria-pressed="true"],
2599
+ [data-persona-root] .persona-artifact-inline-chrome-actions .persona-artifact-doc-icon-btn[aria-pressed="true"] {
2600
+ background: var(--persona-artifact-doc-toggle-active-bg, var(--persona-icon-btn-active-bg, var(--persona-container, #f3f4f6)));
2601
+ border-color: var(--persona-artifact-doc-toggle-active-border, var(--persona-icon-btn-active-border, var(--persona-border, #e5e7eb)));
2692
2602
  }
2693
2603
 
2694
- [data-persona-root] .persona-artifact-toolbar-document .persona-artifact-doc-copy-btn {
2604
+ [data-persona-root] .persona-artifact-toolbar-document .persona-artifact-doc-copy-btn,
2605
+ [data-persona-root] .persona-artifact-inline-chrome-actions .persona-artifact-doc-copy-btn {
2695
2606
  display: inline-flex;
2696
2607
  align-items: center;
2697
2608
  gap: 0.35rem;
@@ -2705,19 +2616,23 @@
2705
2616
  line-height: 1.25;
2706
2617
  }
2707
2618
 
2708
- [data-persona-root] .persona-artifact-toolbar-document .persona-artifact-doc-copy-btn:hover {
2709
- background: var(--persona-artifact-toolbar-icon-hover-bg, var(--persona-container, #f3f4f6));
2619
+ [data-persona-root] .persona-artifact-toolbar-document .persona-artifact-doc-copy-btn:hover,
2620
+ [data-persona-root] .persona-artifact-inline-chrome-actions .persona-artifact-doc-copy-btn:hover {
2621
+ background: var(--persona-artifact-toolbar-icon-hover-bg, var(--persona-icon-btn-hover-bg, var(--persona-container, #f3f4f6)));
2710
2622
  }
2711
2623
 
2712
- [data-persona-root] .persona-artifact-toolbar-document .persona-artifact-doc-copy-label {
2713
- font-weight: 500;
2714
- }
2715
2624
 
2716
2625
  /* Artifact tab theming */
2717
2626
  [data-persona-root] .persona-artifact-tab {
2718
2627
  background: var(--persona-artifact-tab-bg, transparent);
2719
2628
  border-radius: var(--persona-artifact-tab-radius, 0.5rem);
2720
2629
  color: var(--persona-artifact-tab-color, inherit);
2630
+ /* Keep each tab on one line and cap its width so the strip scrolls
2631
+ horizontally instead of wrapping/overflowing the pane. */
2632
+ white-space: nowrap;
2633
+ max-width: var(--persona-artifact-tab-max-width, 12rem);
2634
+ overflow: hidden;
2635
+ text-overflow: ellipsis;
2721
2636
  }
2722
2637
 
2723
2638
  [data-persona-root] .persona-artifact-tab.persona-bg-persona-container {
@@ -2730,27 +2645,40 @@
2730
2645
  background: var(--persona-artifact-toolbar-bg, var(--persona-surface, #fff));
2731
2646
  }
2732
2647
 
2733
- /* Toggle group gap */
2734
- [data-persona-root] .persona-artifact-toggle-group {
2735
- gap: var(--persona-artifact-toolbar-toggle-group-gap, 0.25rem);
2736
- }
2737
-
2738
- /* Toggle button border-radius (view/code buttons) */
2739
- [data-persona-root] .persona-artifact-toolbar-document .persona-artifact-view-btn,
2740
- [data-persona-root] .persona-artifact-toolbar-document .persona-artifact-code-btn {
2741
- border-radius: var(--persona-artifact-toolbar-toggle-radius, var(--persona-artifact-toolbar-icon-radius, var(--persona-radius-md, 0.375rem)));
2742
- }
2648
+ /* Toggle group + view/code button styling lives in the segmented
2649
+ .persona-toggle-group.persona-artifact-toggle-group block further down. */
2743
2650
 
2744
2651
  /* Tab hover */
2745
2652
  [data-persona-root] .persona-artifact-tab:hover {
2746
2653
  background: var(--persona-artifact-tab-hover-bg, var(--persona-container, #f3f4f6));
2747
2654
  }
2748
2655
 
2656
+ /* Keyboard focus ring, themed to match cards/buttons instead of the UA default
2657
+ (which varies by browser). Custom renderTabBar bars reuse .persona-artifact-tab,
2658
+ so they inherit the same ring; give a scroll container enough padding that the
2659
+ 2px offset isn't clipped. */
2660
+ [data-persona-root] .persona-artifact-tab:focus-visible {
2661
+ outline: 2px solid var(--persona-accent, #171717);
2662
+ outline-offset: 2px;
2663
+ }
2664
+
2749
2665
  /* Tab list container */
2750
2666
  [data-persona-root] .persona-artifact-list {
2751
2667
  background: var(--persona-artifact-tab-list-bg, transparent);
2752
2668
  border-bottom-color: var(--persona-artifact-tab-list-border-color, var(--persona-border, #e5e7eb));
2753
2669
  padding: var(--persona-artifact-tab-list-padding, 0.5rem);
2670
+ /* Thin, unobtrusive horizontal scrollbar for the tab strip. */
2671
+ scrollbar-width: thin;
2672
+ scroll-behavior: smooth;
2673
+ }
2674
+
2675
+ [data-persona-root] .persona-artifact-list::-webkit-scrollbar {
2676
+ height: 6px;
2677
+ }
2678
+
2679
+ [data-persona-root] .persona-artifact-list::-webkit-scrollbar-thumb {
2680
+ background: var(--persona-artifact-tab-list-scrollbar, var(--persona-border, #e5e7eb));
2681
+ border-radius: 9999px;
2754
2682
  }
2755
2683
 
2756
2684
  /* Toolbar border override */
@@ -2758,6 +2686,23 @@
2758
2686
  border-bottom: var(--persona-artifact-toolbar-border, 1px solid var(--persona-border, #e5e7eb));
2759
2687
  }
2760
2688
 
2689
+ /* Artifact reference card (chat thread) */
2690
+ [data-persona-root] .persona-artifact-card {
2691
+ background: var(--persona-artifact-card-bg, var(--persona-surface, #ffffff));
2692
+ border: var(--persona-artifact-card-border, 1px solid var(--persona-border, #e5e7eb));
2693
+ border-radius: var(--persona-artifact-card-radius, var(--persona-message-assistant-radius, var(--persona-radius-lg, 0.5rem)));
2694
+ cursor: pointer;
2695
+ transition: background-color 0.15s ease, border-color 0.15s ease;
2696
+ }
2697
+ [data-persona-root] .persona-artifact-card:hover {
2698
+ background: var(--persona-artifact-card-hover-bg, #f3f4f6);
2699
+ border-color: var(--persona-artifact-card-hover-border, var(--persona-border, #e5e7eb));
2700
+ }
2701
+ [data-persona-root] .persona-artifact-card:focus-visible {
2702
+ outline: 2px solid var(--persona-accent, #171717);
2703
+ outline-offset: 2px;
2704
+ }
2705
+
2761
2706
  /* ── Composable button utilities ── */
2762
2707
 
2763
2708
  /* Icon button: base for all icon-only buttons created by createIconButton() */
@@ -2929,6 +2874,89 @@
2929
2874
  border-bottom-right-radius: var(--persona-toggle-group-radius, var(--persona-icon-btn-radius, var(--persona-radius-md, 0.375rem)));
2930
2875
  }
2931
2876
 
2877
+ /* Artifact view/source toggle: one segmented pill. The container carries the
2878
+ border; segments are borderless and the active one is highlighted, so the
2879
+ pair reads as a single mutually exclusive switch instead of two buttons. */
2880
+ [data-persona-root] .persona-toggle-group.persona-artifact-toggle-group {
2881
+ gap: var(--persona-artifact-toolbar-toggle-group-gap, 2px);
2882
+ padding: var(--persona-artifact-toolbar-toggle-group-padding, 2px);
2883
+ border: var(--persona-artifact-toolbar-toggle-group-border, 1px solid var(--persona-border, #e5e7eb));
2884
+ /* Default to the toolbar button radius (document preset var, then the plain
2885
+ icon-button var) so the pill matches the neighboring copy/close buttons
2886
+ instead of standing out as a capsule. */
2887
+ border-radius: var(--persona-artifact-toolbar-toggle-group-radius, var(--persona-artifact-toolbar-icon-radius, var(--persona-icon-btn-radius, var(--persona-radius-md, 0.375rem))));
2888
+ background: var(--persona-artifact-toolbar-toggle-group-bg, var(--persona-surface, #ffffff));
2889
+ }
2890
+ [data-persona-root] .persona-toggle-group.persona-artifact-toggle-group > .persona-icon-btn {
2891
+ border: none;
2892
+ background: transparent;
2893
+ /* Segment padding = standalone icon-button padding minus the container's
2894
+ padding, so the pill's outer height matches the neighboring icon buttons
2895
+ (the container's 1px border stands in for the border the segments lost).
2896
+ calc() clamps to 0 if a theme sets group padding larger than icon padding. */
2897
+ padding: calc(var(--persona-artifact-toolbar-icon-padding, var(--persona-icon-btn-padding, 0.25rem)) - var(--persona-artifact-toolbar-toggle-group-padding, 2px));
2898
+ /* Container radius minus the 2px padding so the segment nests concentrically. */
2899
+ border-radius: var(--persona-artifact-toolbar-toggle-radius, calc(var(--persona-artifact-toolbar-icon-radius, var(--persona-icon-btn-radius, var(--persona-radius-md, 0.375rem))) - 2px));
2900
+ }
2901
+ [data-persona-root] .persona-toggle-group.persona-artifact-toggle-group > .persona-icon-btn:hover {
2902
+ background: var(--persona-artifact-toolbar-icon-hover-bg, var(--persona-icon-btn-hover-bg, var(--persona-container, #f3f4f6)));
2903
+ }
2904
+ [data-persona-root] .persona-toggle-group.persona-artifact-toggle-group > .persona-icon-btn[aria-pressed="true"] {
2905
+ background: var(--persona-artifact-doc-toggle-active-bg, var(--persona-icon-btn-active-bg, var(--persona-container, #f3f4f6)));
2906
+ /* Inset ring instead of a border so documentToolbarToggleActiveBorderColor
2907
+ still applies inside the borderless segment (transparent by default). */
2908
+ box-shadow: inset 0 0 0 1px var(--persona-artifact-doc-toggle-active-border, transparent);
2909
+ }
2910
+
2911
+ /* Sliding selection thumb: a container ::before that translates between the
2912
+ two equal-width segments, keyed off aria-pressed via :has(). The static
2913
+ aria-pressed background above stays as the fallback for browsers without
2914
+ :has() and is disabled inside the @supports block so the highlight is only
2915
+ ever painted once. Geometry assumes the group's two segments are equal
2916
+ width (both are square icon buttons). */
2917
+ @supports selector(:has(*)) {
2918
+ [data-persona-root] .persona-toggle-group.persona-artifact-toggle-group {
2919
+ position: relative;
2920
+ }
2921
+ [data-persona-root] .persona-toggle-group.persona-artifact-toggle-group::before {
2922
+ content: "";
2923
+ position: absolute;
2924
+ top: var(--persona-artifact-toolbar-toggle-group-padding, 2px);
2925
+ bottom: var(--persona-artifact-toolbar-toggle-group-padding, 2px);
2926
+ left: var(--persona-artifact-toolbar-toggle-group-padding, 2px);
2927
+ width: calc(
2928
+ 50% - var(--persona-artifact-toolbar-toggle-group-padding, 2px) -
2929
+ (var(--persona-artifact-toolbar-toggle-group-gap, 2px) / 2)
2930
+ );
2931
+ border-radius: var(--persona-artifact-toolbar-toggle-radius, calc(var(--persona-artifact-toolbar-icon-radius, var(--persona-icon-btn-radius, var(--persona-radius-md, 0.375rem))) - 2px));
2932
+ background: var(--persona-artifact-doc-toggle-active-bg, var(--persona-icon-btn-active-bg, var(--persona-container, #f3f4f6)));
2933
+ box-shadow: inset 0 0 0 1px var(--persona-artifact-doc-toggle-active-border, transparent);
2934
+ transition: transform 0.18s ease;
2935
+ pointer-events: none;
2936
+ }
2937
+ [data-persona-root]
2938
+ .persona-toggle-group.persona-artifact-toggle-group:has(
2939
+ > .persona-icon-btn:last-child[aria-pressed="true"]
2940
+ )::before {
2941
+ transform: translateX(calc(100% + var(--persona-artifact-toolbar-toggle-group-gap, 2px)));
2942
+ }
2943
+ /* Positioned so the segments paint above the absolutely positioned thumb
2944
+ (document order applies once both are in the positioned layer). */
2945
+ [data-persona-root] .persona-toggle-group.persona-artifact-toggle-group > .persona-icon-btn {
2946
+ position: relative;
2947
+ }
2948
+ /* The thumb carries the highlight; keep the segment itself transparent. */
2949
+ [data-persona-root] .persona-toggle-group.persona-artifact-toggle-group > .persona-icon-btn[aria-pressed="true"] {
2950
+ background: transparent;
2951
+ box-shadow: none;
2952
+ }
2953
+ @media (prefers-reduced-motion: reduce) {
2954
+ [data-persona-root] .persona-toggle-group.persona-artifact-toggle-group::before {
2955
+ transition: none;
2956
+ }
2957
+ }
2958
+ }
2959
+
2932
2960
  /* Combo button: label + chevron with dropdown */
2933
2961
  [data-persona-root] .persona-combo-btn {
2934
2962
  font-size: var(--persona-combo-btn-font-size, 0.8125rem);
@@ -3019,11 +3047,560 @@
3019
3047
  max-width: var(--persona-artifact-pane-max-width, 28rem);
3020
3048
  min-width: var(--persona-artifact-pane-min-width, 0);
3021
3049
  flex-shrink: 0;
3022
- box-shadow: var(--persona-artifact-pane-shadow, -2px 0 12px rgba(15, 23, 42, 0.06));
3050
+ /* No default elevation on the pane; welded reads as one card, detached and
3051
+ the drawer set their own shadow. Explicit paneShadow still wins. */
3052
+ box-shadow: var(--persona-artifact-pane-shadow, none);
3053
+ }
3054
+ }
3055
+
3056
+ /* Expanded artifact pane: pane takes the whole split root, chat column hides.
3057
+ Split-view only: below 641px / narrow hosts the drawer already covers everything. */
3058
+ @media (min-width: 641px) {
3059
+ [data-persona-root].persona-artifact-expanded .persona-artifact-split-root > :first-child {
3060
+ display: none;
3061
+ }
3062
+ [data-persona-root].persona-artifact-expanded .persona-artifact-pane {
3063
+ width: 100%;
3064
+ max-width: none;
3065
+ }
3066
+ [data-persona-root].persona-artifact-expanded .persona-artifact-split-handle {
3067
+ display: none !important;
3068
+ }
3069
+ }
3070
+ @media (max-width: 640px) {
3071
+ [data-persona-root] .persona-artifact-expand-btn {
3072
+ display: none !important;
3023
3073
  }
3024
3074
  }
3075
+ [data-persona-root].persona-artifact-narrow-host .persona-artifact-expand-btn {
3076
+ display: none !important;
3077
+ }
3025
3078
 
3026
3079
  /* paneBorderRadius: works on any paneAppearance; overflow clips content to rounded shape */
3080
+ /* Positioned frame wrapping the sandboxed preview iframe (artifact-preview.ts).
3081
+ `position: relative` gives the absolute loading overlay a containing block
3082
+ (the shared preview body is display: contents, so the iframe alone has no
3083
+ positioning context). The frame owns the rendered height and the iframe is
3084
+ 100% of it — see the pane default here and the inline overrides below.
3085
+
3086
+ Opaque-canvas / color-scheme trap: an opaque-origin srcdoc (sandbox without
3087
+ allow-same-origin) that declares no color-scheme can get a UA-forced opaque
3088
+ canvas that flashes before content paints. Declaring color-scheme on the
3089
+ iframe plus an explicit background on both the frame and iframe makes the
3090
+ pre-paint surface a deliberate, themeable canvas rather than an uncontrolled
3091
+ flash. Artifact content generally assumes a light canvas, so white stays the
3092
+ default; override via --persona-artifact-frame-bg. */
3093
+ [data-persona-root] .persona-artifact-frame {
3094
+ position: relative;
3095
+ width: 100%;
3096
+ height: 100%;
3097
+ background: var(--persona-artifact-frame-bg, #ffffff);
3098
+ }
3099
+ /* Previewable file artifact: sandboxed iframe fills its frame. */
3100
+ [data-persona-root] .persona-artifact-iframe {
3101
+ display: block;
3102
+ width: 100%;
3103
+ height: 100%;
3104
+ border: 0;
3105
+ color-scheme: light;
3106
+ background: var(--persona-artifact-frame-bg, #ffffff);
3107
+ }
3108
+
3109
+ /* Loading overlay above the preview iframe (artifact-preview.ts): absolutely
3110
+ fills the frame with the same themed canvas the iframe paints onto, so the
3111
+ pre-paint area reads as one deliberate surface. Appended only after
3112
+ `loading.delayMs`; the --out modifier fades it before removal. */
3113
+ [data-persona-root] .persona-artifact-frame-loading {
3114
+ position: absolute;
3115
+ inset: 0;
3116
+ display: flex;
3117
+ align-items: center;
3118
+ justify-content: center;
3119
+ background: var(--persona-artifact-frame-bg, #ffffff);
3120
+ opacity: 1;
3121
+ transition: opacity var(--persona-artifact-frame-loading-fade, 200ms) ease;
3122
+ }
3123
+ [data-persona-root] .persona-artifact-frame-loading--out {
3124
+ opacity: 0;
3125
+ }
3126
+ /* Default indicator column: the icon spinner, with the escalation label (when
3127
+ present) stacked below it. Icon-first, text only as a later escalation. */
3128
+ [data-persona-root] .persona-artifact-frame-loading-indicator {
3129
+ display: flex;
3130
+ flex-direction: column;
3131
+ align-items: center;
3132
+ gap: 0.625rem;
3133
+ }
3134
+ /* Escalation label: calm, muted, small — NO shimmer (deliberate, per research).
3135
+ Hidden until setupPreviewLoading adds --visible after labelDelayMs; a plain
3136
+ opacity transition fades it in. */
3137
+ [data-persona-root] .persona-artifact-frame-loading-text {
3138
+ color: var(
3139
+ --persona-artifact-frame-loading-color,
3140
+ var(--persona-text-muted, #6b7280)
3141
+ );
3142
+ font-size: 0.8125rem;
3143
+ font-weight: 500;
3144
+ opacity: 0;
3145
+ transition: opacity 240ms ease;
3146
+ }
3147
+ [data-persona-root] .persona-artifact-frame-loading-text--visible {
3148
+ opacity: 1;
3149
+ }
3150
+
3151
+ /* Reusable icon spinner (utils/spinner.ts): a faint full track ring with a
3152
+ rotating arc on top. Icon-first loading is the norm across preview surfaces
3153
+ (Sandpack, embeds, v0/ChatGPT) and design systems (HIG/Material/Carbon/Geist);
3154
+ text-only "loading" is used by none. The arc spins via a GPU-friendly
3155
+ `transform: rotate` keyframe. Fallback chains:
3156
+ - size: --persona-artifact-spinner-size (default 28px)
3157
+ - arc: --persona-artifact-spinner-color → --persona-accent → --persona-primary
3158
+ → neutral dark (the interactive/brand hue)
3159
+ - track: --persona-artifact-spinner-track-color → --persona-border → light gray
3160
+ - speed: --persona-artifact-spinner-speed (default 0.8s) */
3161
+ [data-persona-root] .persona-spinner {
3162
+ width: var(--persona-artifact-spinner-size, 28px);
3163
+ height: var(--persona-artifact-spinner-size, 28px);
3164
+ transform-origin: center;
3165
+ animation: persona-spinner-rotate var(--persona-artifact-spinner-speed, 0.8s)
3166
+ linear infinite;
3167
+ }
3168
+ [data-persona-root] .persona-spinner-track {
3169
+ fill: none;
3170
+ stroke: var(
3171
+ --persona-artifact-spinner-track-color,
3172
+ var(--persona-border, #e5e7eb)
3173
+ );
3174
+ stroke-width: 2.5;
3175
+ }
3176
+ [data-persona-root] .persona-spinner-arc {
3177
+ fill: none;
3178
+ stroke: var(
3179
+ --persona-artifact-spinner-color,
3180
+ var(--persona-accent, var(--persona-primary, #171717))
3181
+ );
3182
+ stroke-width: 2.5;
3183
+ stroke-linecap: round;
3184
+ /* r=9 → circumference ≈ 56.5; ~15u arc reads as a quarter-ish sweep. */
3185
+ stroke-dasharray: 15 42;
3186
+ }
3187
+ @keyframes persona-spinner-rotate {
3188
+ from {
3189
+ transform: rotate(0deg);
3190
+ }
3191
+ to {
3192
+ transform: rotate(360deg);
3193
+ }
3194
+ }
3195
+ /* Reduced motion: stop the rotation (the static arc still reads as a ring) and
3196
+ drop the label fade. */
3197
+ @media (prefers-reduced-motion: reduce) {
3198
+ [data-persona-root] .persona-spinner {
3199
+ animation: none;
3200
+ }
3201
+ [data-persona-root] .persona-artifact-frame-loading-text {
3202
+ transition: none;
3203
+ }
3204
+ }
3205
+
3206
+ /* Shared artifact preview body wrapper (artifact-preview.ts): generates no box
3207
+ so children (e.g. the iframe's height: 100%) lay out against the host
3208
+ container exactly as if they were its direct children. */
3209
+ [data-persona-root] .persona-artifact-preview-body {
3210
+ display: contents;
3211
+ }
3212
+
3213
+ /* Syntax-highlighted source view (code-highlight.ts). The host <pre> keeps its
3214
+ wrapping (persona-whitespace-pre-wrap); <code> resets white-space to normal so
3215
+ the literal "\n" separator text nodes between block line spans collapse
3216
+ visually while still reconstructing the verbatim source via textContent. Each
3217
+ line reserves a left gutter whose number is drawn by a CSS counter — never
3218
+ real text, so it is not selectable or copyable. */
3219
+ /* The source view sits directly on the pane surface (transparent, matching
3220
+ Claude's code view); set --persona-code-bg via theme.components.code.background
3221
+ to render it as an inset sheet instead. */
3222
+ [data-persona-root] .persona-code-pre {
3223
+ margin: 0;
3224
+ background: var(--persona-code-bg, transparent);
3225
+ border-radius: var(--persona-radius-md, 0.375rem);
3226
+ padding: 0.5rem 0.75rem 0.5rem 0.25rem;
3227
+ }
3228
+
3229
+ /* Pane source view is full-bleed: the pane drops its content padding (class
3230
+ toggled in artifact-pane.ts when the body rendered a code pre) so the
3231
+ line-number gutter sits flush against the pane's left edge. */
3232
+ [data-persona-root] .persona-artifact-content-flush {
3233
+ padding: 0;
3234
+ }
3235
+ [data-persona-root] .persona-artifact-content-flush .persona-code-pre {
3236
+ padding: 0.5rem 0.75rem 0.5rem 0;
3237
+ border-radius: 0;
3238
+ min-height: 100%;
3239
+ }
3240
+ [data-persona-root] .persona-code {
3241
+ display: block;
3242
+ white-space: normal;
3243
+ counter-reset: persona-code-line;
3244
+ /* Defend against host-page element rules (`code { background/padding/… }`):
3245
+ without Shadow DOM those selectors reach into the widget, and inline-code
3246
+ chip styling on a full source view reads as a mystery tinted block. */
3247
+ background: transparent;
3248
+ padding: 0;
3249
+ border-radius: 0;
3250
+ font-family: inherit;
3251
+ font-size: inherit;
3252
+ color: inherit;
3253
+ }
3254
+ [data-persona-root] .persona-code-line {
3255
+ display: block;
3256
+ position: relative;
3257
+ white-space: pre-wrap;
3258
+ padding-left: 3em;
3259
+ counter-increment: persona-code-line;
3260
+ /* An empty source line emits a childless span — a zero-height block whose
3261
+ absolutely-positioned gutter number would paint on top of the next line's.
3262
+ Reserve one line box (1lh; 1em as the older-engine fallback). */
3263
+ min-height: 1em;
3264
+ min-height: 1lh;
3265
+ }
3266
+ [data-persona-root] .persona-code-line::before {
3267
+ content: counter(persona-code-line);
3268
+ position: absolute;
3269
+ top: 0;
3270
+ left: 0;
3271
+ width: 2em;
3272
+ padding-right: 0.5em;
3273
+ text-align: right;
3274
+ color: var(--persona-code-line-number-color, #a0a1a7);
3275
+ border-right: 1px solid
3276
+ var(--persona-code-gutter-border-color, var(--persona-border, #e5e7eb));
3277
+ -webkit-user-select: none;
3278
+ user-select: none;
3279
+ }
3280
+ /* Default syntax palette: One Light (Atom), the same theme Claude.ai's code
3281
+ view uses. Numbers and attribute names share One's constant orange; CSS
3282
+ properties / JSON keys take the function blue so keys stay distinct from
3283
+ string values. */
3284
+ [data-persona-root] .persona-code-token-keyword {
3285
+ color: var(--persona-code-keyword-color, #a626a4);
3286
+ }
3287
+ [data-persona-root] .persona-code-token-string {
3288
+ color: var(--persona-code-string-color, #50a14f);
3289
+ }
3290
+ [data-persona-root] .persona-code-token-comment {
3291
+ color: var(--persona-code-comment-color, #a0a1a7);
3292
+ font-style: italic;
3293
+ }
3294
+ [data-persona-root] .persona-code-token-number {
3295
+ color: var(--persona-code-number-color, #986801);
3296
+ }
3297
+ [data-persona-root] .persona-code-token-tag {
3298
+ color: var(--persona-code-tag-color, #e45649);
3299
+ }
3300
+ [data-persona-root] .persona-code-token-attr {
3301
+ color: var(--persona-code-attr-color, #986801);
3302
+ }
3303
+ [data-persona-root] .persona-code-token-property {
3304
+ color: var(--persona-code-property-color, #4078f2);
3305
+ }
3306
+
3307
+ /* Dark-scheme defaults for the syntax palette (One Dark counterparts of the
3308
+ One Light defaults above). Keyed off the widget's resolved scheme, stamped
3309
+ as data-persona-color-scheme by applyThemeVariables — NOT the OS
3310
+ prefers-color-scheme, which diverges whenever a host pins colorScheme.
3311
+ The widget's semantic vars can't drive these (syntax colors aren't
3312
+ derivable from surface/text). Explicit theme.components.code values still
3313
+ win in both schemes. */
3314
+ [data-persona-root][data-persona-color-scheme="dark"] .persona-code-line::before {
3315
+ color: var(--persona-code-line-number-color, #4b5263);
3316
+ border-right-color: var(--persona-code-gutter-border-color, #3b4048);
3317
+ }
3318
+ [data-persona-root][data-persona-color-scheme="dark"] .persona-code-token-keyword {
3319
+ color: var(--persona-code-keyword-color, #c678dd);
3320
+ }
3321
+ [data-persona-root][data-persona-color-scheme="dark"] .persona-code-token-string {
3322
+ color: var(--persona-code-string-color, #98c379);
3323
+ }
3324
+ [data-persona-root][data-persona-color-scheme="dark"] .persona-code-token-comment {
3325
+ color: var(--persona-code-comment-color, #5c6370);
3326
+ }
3327
+ [data-persona-root][data-persona-color-scheme="dark"] .persona-code-token-number {
3328
+ color: var(--persona-code-number-color, #d19a66);
3329
+ }
3330
+ [data-persona-root][data-persona-color-scheme="dark"] .persona-code-token-tag {
3331
+ color: var(--persona-code-tag-color, #e06c75);
3332
+ }
3333
+ [data-persona-root][data-persona-color-scheme="dark"] .persona-code-token-attr {
3334
+ color: var(--persona-code-attr-color, #d19a66);
3335
+ }
3336
+ [data-persona-root][data-persona-color-scheme="dark"] .persona-code-token-property {
3337
+ color: var(--persona-code-property-color, #61afef);
3338
+ }
3339
+
3340
+ /* Inline artifact block (artifact-inline.ts, display: "inline"): carries its
3341
+ own border/surface (registered with bubbleChrome: false, like the card).
3342
+ Reads like a file preview: a flush title/toolbar chrome bar above the body.
3343
+ Default vars draw from the same semantic surface/border/text/muted family
3344
+ the card uses, so light/dark themes work with zero config. */
3345
+ [data-persona-root] .persona-artifact-inline {
3346
+ border: var(--persona-artifact-inline-border, 1px solid var(--persona-border, #e5e7eb));
3347
+ border-radius: var(
3348
+ --persona-artifact-inline-radius,
3349
+ var(--persona-message-assistant-radius, var(--persona-radius-lg, 0.5rem))
3350
+ );
3351
+ background: var(--persona-artifact-inline-bg, var(--persona-surface, #ffffff));
3352
+ overflow: hidden;
3353
+ }
3354
+
3355
+ /* completeDisplay: "card" — once the block collapses to the compact reference
3356
+ card it keeps the same root (updater keying + theme zone survive) but must
3357
+ drop the inline frame styling, since the nested card carries its own border,
3358
+ background, and radius. overflow: visible so the card's focus outline (which
3359
+ uses outline-offset) isn't clipped. */
3360
+ [data-persona-root] .persona-artifact-inline.persona-artifact-inline--card {
3361
+ border: none;
3362
+ background: transparent;
3363
+ border-radius: 0;
3364
+ overflow: visible;
3365
+ }
3366
+
3367
+ /* Title/toolbar chrome bar: flush to the frame edge (no frame padding). */
3368
+ [data-persona-root] .persona-artifact-inline-chrome {
3369
+ display: flex;
3370
+ align-items: center;
3371
+ gap: 0.5rem;
3372
+ padding: 0.5rem 0.75rem;
3373
+ background: var(--persona-artifact-inline-chrome-bg, var(--persona-surface, #ffffff));
3374
+ border-bottom: 1px solid var(--persona-artifact-inline-chrome-border, var(--persona-border, #e5e7eb));
3375
+ }
3376
+
3377
+ /* Leading cluster: file icon + title + type label / streaming status. */
3378
+ [data-persona-root] .persona-artifact-inline-chrome-lead {
3379
+ display: flex;
3380
+ align-items: center;
3381
+ gap: 0.5rem;
3382
+ min-width: 0;
3383
+ flex: 1 1 auto;
3384
+ color: var(--persona-artifact-inline-title-color, var(--persona-text, #111827));
3385
+ font-size: 0.8125rem;
3386
+ font-weight: 500;
3387
+ }
3388
+
3389
+ [data-persona-root] .persona-artifact-inline-chrome-lead .persona-artifact-inline-type,
3390
+ [data-persona-root] .persona-artifact-inline-chrome-lead .persona-artifact-inline-status {
3391
+ color: var(--persona-artifact-inline-muted-color, var(--persona-text-muted, #6b7280));
3392
+ font-weight: 400;
3393
+ }
3394
+
3395
+ /* Streaming status: animated label span + optional plain detail span (live
3396
+ counters). The label carries the loading animation; the detail updates freely
3397
+ per delta and stays un-animated. Shared by the card, the inline chrome, and
3398
+ the inline status body (features.artifacts.statusLabel). Keep the status span
3399
+ from blowing out the chrome bar when a long detail streams in. */
3400
+ [data-persona-root] .persona-artifact-inline-chrome-lead .persona-artifact-inline-status {
3401
+ min-width: 0;
3402
+ overflow: hidden;
3403
+ white-space: nowrap;
3404
+ text-overflow: ellipsis;
3405
+ }
3406
+
3407
+ [data-persona-root] .persona-artifact-status-detail {
3408
+ color: var(--persona-artifact-inline-muted-color, var(--persona-text-muted, #6b7280));
3409
+ margin-left: 0.375rem;
3410
+ font-variant-numeric: tabular-nums;
3411
+ }
3412
+
3413
+ /* Trailing cluster: custom inlineActions, copy, expand. */
3414
+ [data-persona-root] .persona-artifact-inline-chrome-actions {
3415
+ display: flex;
3416
+ align-items: center;
3417
+ gap: 0.25rem;
3418
+ flex: 0 0 auto;
3419
+ }
3420
+
3421
+ /* Body wraps the chrome-free preview; padding lives here so the chrome sits
3422
+ flush to the frame edge. */
3423
+ [data-persona-root] .persona-artifact-inline-body {
3424
+ padding: 0.75rem;
3425
+ }
3426
+
3427
+ /* The preview frame (and its 100%-height iframe) is height: 100%, which
3428
+ collapses inside the auto-height inline block; pin a themeable fixed height on
3429
+ the frame instead. Height var chain: inlineBody.height
3430
+ (--persona-artifact-inline-body-height, set per state by artifact-inline.ts) →
3431
+ the legacy --persona-artifact-inline-frame-height override → 320px. The legacy
3432
+ var still wins when the new one is unset (inlineBody.height "auto"), so themes
3433
+ pinned to it keep working. The --sized rule below (equal specificity, declared
3434
+ later) overrides this to 100% when the body wrapper owns the height. */
3435
+ [data-persona-root] .persona-artifact-inline .persona-artifact-frame {
3436
+ height: var(
3437
+ --persona-artifact-inline-body-height,
3438
+ var(--persona-artifact-inline-frame-height, 320px)
3439
+ );
3440
+ }
3441
+
3442
+ /* Inline body height model (features.artifacts.inlineBody). The block root sets
3443
+ --persona-artifact-inline-body-height (px) for the current state; leaving it
3444
+ unset (inlineBody.height "auto") lets the body size to content and the iframe
3445
+ fall back through the chain above. */
3446
+
3447
+ /* Code bodies are full-bleed, matching the pane's source-view treatment (same
3448
+ class the pane toggles): the line-number gutter sits flush against the frame
3449
+ edge. Declared after .persona-artifact-inline-body so padding: 0 wins the
3450
+ specificity tie. */
3451
+ [data-persona-root] .persona-artifact-inline-body.persona-artifact-content-flush {
3452
+ padding: 0;
3453
+ }
3454
+
3455
+ /* Numeric inlineBody.height: the body wrapper owns the reserved height
3456
+ (border-box), so flush code, padded iframe, and padded status bodies all
3457
+ occupy the same outer box and every state swap is layout neutral. Fill-capable
3458
+ content stretches to the wrapper. */
3459
+ [data-persona-root] .persona-artifact-inline-body--sized {
3460
+ height: var(--persona-artifact-inline-body-height, auto);
3461
+ }
3462
+ [data-persona-root] .persona-artifact-inline-body--sized .persona-artifact-source-window--fixed,
3463
+ [data-persona-root] .persona-artifact-inline-body--sized .persona-artifact-frame {
3464
+ height: 100%;
3465
+ }
3466
+ [data-persona-root] .persona-artifact-inline-body--sized .persona-artifact-status-view {
3467
+ min-height: 100%;
3468
+ }
3469
+
3470
+ /* Fixed-height streaming source window: a reserved scroll viewport so streamed
3471
+ source no longer grows an unbounded <pre> and then snaps to the preview. The
3472
+ height comes from the sized body wrapper above (the --fixed class is only
3473
+ ever applied together with it). The bare (non-fixed) window is an inert
3474
+ wrapper (pane path never adds it; the source <pre> sits directly on el
3475
+ there). */
3476
+ [data-persona-root] .persona-artifact-source-window {
3477
+ min-width: 0;
3478
+ }
3479
+ [data-persona-root] .persona-artifact-source-window--fixed {
3480
+ overflow-y: auto;
3481
+ }
3482
+
3483
+ /* Clipped preview window (inlineBody.overflow: "clip"): a fixed-height window
3484
+ showing the top of the document with no internal scroll. Applied together with
3485
+ --fixed (which supplies the reserved height via the --sized body wrapper and
3486
+ the fill-capable detection in artifact-inline.ts); declared after --fixed so
3487
+ this overflow: hidden wins its overflow-y: auto on the specificity tie. */
3488
+ [data-persona-root] .persona-artifact-source-window--clip {
3489
+ overflow: hidden;
3490
+ }
3491
+
3492
+ /* Edge fade masks on the fixed window (top fade signals content clipped above).
3493
+ mask-image works over any themed background; size via --persona-artifact-fade-size
3494
+ (defaults to 40px). */
3495
+ [data-persona-root]
3496
+ .persona-artifact-fade-top:not(.persona-artifact-fade-bottom) {
3497
+ -webkit-mask-image: linear-gradient(
3498
+ to bottom,
3499
+ transparent 0,
3500
+ #000 var(--persona-artifact-fade-size, 40px)
3501
+ );
3502
+ mask-image: linear-gradient(
3503
+ to bottom,
3504
+ transparent 0,
3505
+ #000 var(--persona-artifact-fade-size, 40px)
3506
+ );
3507
+ }
3508
+ [data-persona-root]
3509
+ .persona-artifact-fade-bottom:not(.persona-artifact-fade-top) {
3510
+ -webkit-mask-image: linear-gradient(
3511
+ to top,
3512
+ transparent 0,
3513
+ #000 var(--persona-artifact-fade-size, 40px)
3514
+ );
3515
+ mask-image: linear-gradient(
3516
+ to top,
3517
+ transparent 0,
3518
+ #000 var(--persona-artifact-fade-size, 40px)
3519
+ );
3520
+ }
3521
+ [data-persona-root]
3522
+ .persona-artifact-fade-top.persona-artifact-fade-bottom {
3523
+ -webkit-mask-image: linear-gradient(
3524
+ to bottom,
3525
+ transparent 0,
3526
+ #000 var(--persona-artifact-fade-size, 40px),
3527
+ #000 calc(100% - var(--persona-artifact-fade-size, 40px)),
3528
+ transparent 100%
3529
+ );
3530
+ mask-image: linear-gradient(
3531
+ to bottom,
3532
+ transparent 0,
3533
+ #000 var(--persona-artifact-fade-size, 40px),
3534
+ #000 calc(100% - var(--persona-artifact-fade-size, 40px)),
3535
+ transparent 100%
3536
+ );
3537
+ }
3538
+
3539
+ /* Edge fade on the horizontally scrollable tab strip. Visual hint only; the
3540
+ tablist keyboard behavior is the real affordance. Sized via
3541
+ --persona-artifact-tab-fade-size (default 24px). */
3542
+ [data-persona-root]
3543
+ .persona-artifact-tab-fade-start:not(.persona-artifact-tab-fade-end) {
3544
+ -webkit-mask-image: linear-gradient(
3545
+ to right,
3546
+ transparent 0,
3547
+ #000 var(--persona-artifact-tab-fade-size, 24px)
3548
+ );
3549
+ mask-image: linear-gradient(
3550
+ to right,
3551
+ transparent 0,
3552
+ #000 var(--persona-artifact-tab-fade-size, 24px)
3553
+ );
3554
+ }
3555
+ [data-persona-root]
3556
+ .persona-artifact-tab-fade-end:not(.persona-artifact-tab-fade-start) {
3557
+ -webkit-mask-image: linear-gradient(
3558
+ to left,
3559
+ transparent 0,
3560
+ #000 var(--persona-artifact-tab-fade-size, 24px)
3561
+ );
3562
+ mask-image: linear-gradient(
3563
+ to left,
3564
+ transparent 0,
3565
+ #000 var(--persona-artifact-tab-fade-size, 24px)
3566
+ );
3567
+ }
3568
+ [data-persona-root]
3569
+ .persona-artifact-tab-fade-start.persona-artifact-tab-fade-end {
3570
+ -webkit-mask-image: linear-gradient(
3571
+ to right,
3572
+ transparent 0,
3573
+ #000 var(--persona-artifact-tab-fade-size, 24px),
3574
+ #000 calc(100% - var(--persona-artifact-tab-fade-size, 24px)),
3575
+ transparent 100%
3576
+ );
3577
+ mask-image: linear-gradient(
3578
+ to right,
3579
+ transparent 0,
3580
+ #000 var(--persona-artifact-tab-fade-size, 24px),
3581
+ #000 calc(100% - var(--persona-artifact-tab-fade-size, 24px)),
3582
+ transparent 100%
3583
+ );
3584
+ }
3585
+
3586
+ /* Reserved-height status placeholder (inlineBody.streamingView: "status").
3587
+ Numeric height reserves the var; "auto" falls back to a modest 160px min-height
3588
+ so the card doesn't collapse. */
3589
+ [data-persona-root] .persona-artifact-status-view {
3590
+ display: flex;
3591
+ align-items: center;
3592
+ justify-content: center;
3593
+ min-height: var(--persona-artifact-inline-body-height, 160px);
3594
+ }
3595
+
3596
+ /* Complete-state height cap for non-iframe bodies (rendered markdown, source
3597
+ view, component). The iframe path is sized by the iframe rule above instead,
3598
+ so this class is only applied when the completed body has no iframe. */
3599
+ [data-persona-root] .persona-artifact-inline-body--cap {
3600
+ max-height: var(--persona-artifact-inline-body-height, none);
3601
+ overflow-y: auto;
3602
+ }
3603
+
3027
3604
  [data-persona-root] .persona-artifact-pane {
3028
3605
  border-radius: var(--persona-artifact-pane-radius, 0);
3029
3606
  overflow: hidden;
@@ -3072,23 +3649,71 @@
3072
3649
  border: var(--persona-artifact-pane-border) !important;
3073
3650
  }
3074
3651
 
3652
+ /* paneAppearance: 'detached': floating card with gap, all-side border, radius, elevation, canvas behind */
3653
+ /* Split gap defaults to --persona-panel-inset from ui.ts; canvas paints behind both columns. */
3654
+ [data-persona-root].persona-artifact-appearance-detached .persona-artifact-split-root {
3655
+ background: var(--persona-panel-canvas-bg);
3656
+ }
3657
+
3658
+ /* Desktop split only (drawer keeps its own chrome). Non-!important so explicit
3659
+ paneBorder / paneShadow / paneBorderRadius overrides still win. */
3660
+ @media (min-width: 641px) {
3661
+ [data-persona-root]:not(.persona-artifact-narrow-host).persona-artifact-appearance-detached .persona-artifact-pane {
3662
+ border-radius: var(
3663
+ --persona-artifact-pane-radius,
3664
+ var(--persona-panel-radius, var(--persona-radius-xl, 0.75rem))
3665
+ );
3666
+ border: var(--persona-panel-border, 1px solid var(--persona-border, #e5e7eb));
3667
+ box-shadow: var(
3668
+ --persona-artifact-pane-shadow,
3669
+ var(--persona-panel-shadow, var(--persona-palette-shadows-xl, 0 25px 50px -12px rgba(0, 0, 0, 0.25)))
3670
+ );
3671
+ }
3672
+ }
3673
+
3674
+ /* chatSurface: 'flush': chat is flat flush background, only the pane floats. The
3675
+ chat has no perimeter inset, so inset the pane on 3 sides (left gap comes from
3676
+ the existing detached split flex gap). Keeps the pane's border/radius/shadow. */
3677
+ @media (min-width: 641px) {
3678
+ [data-persona-root]:not(.persona-artifact-narrow-host).persona-artifact-chat-flush .persona-artifact-pane {
3679
+ margin-top: var(--persona-panel-inset);
3680
+ margin-right: var(--persona-panel-inset);
3681
+ margin-bottom: var(--persona-panel-inset);
3682
+ }
3683
+ }
3684
+
3075
3685
  /*
3076
- * Unified split chrome: one visual shell (desktop split only; narrowed via :not(.narrow-host) + breakpoint).
3077
- * Main chat card (.persona-widget-container) loses inner-right rounding; artifact gains matching outer-right.
3686
+ * Welded split chrome (panel + seamless): one card. The outer panel carries the
3687
+ * border + radius + shadow (set in ui.ts); each column rounds only its outer
3688
+ * corners and clips, so the columns fill the panel's rounded rect and butt flush
3689
+ * at the seam. Desktop split only (narrowed via :not(.narrow-host) + breakpoint).
3690
+ * Toggled from ui.ts by splitChromeMode(); the pane's flex layout owns gap 0.
3078
3691
  */
3079
3692
  @media (min-width: 641px) {
3080
- [data-persona-root]:not(.persona-artifact-narrow-host).persona-artifact-unified-split .persona-widget-container {
3693
+ [data-persona-root]:not(.persona-artifact-narrow-host).persona-artifact-welded-split .persona-artifact-split-root {
3694
+ position: relative;
3695
+ }
3696
+
3697
+ [data-persona-root]:not(.persona-artifact-narrow-host).persona-artifact-welded-split .persona-widget-container {
3081
3698
  border-top-right-radius: 0 !important;
3082
3699
  border-bottom-right-radius: 0 !important;
3700
+ overflow: hidden;
3083
3701
  }
3084
3702
 
3085
- [data-persona-root]:not(.persona-artifact-narrow-host).persona-artifact-unified-split .persona-artifact-pane {
3703
+ [data-persona-root]:not(.persona-artifact-narrow-host).persona-artifact-welded-split .persona-artifact-pane {
3086
3704
  border-top-left-radius: 0 !important;
3087
3705
  border-bottom-left-radius: 0 !important;
3088
- border-top-right-radius: var(--persona-artifact-unified-outer-radius, var(--persona-radius-lg, 1rem)) !important;
3089
- border-bottom-right-radius: var(--persona-artifact-unified-outer-radius, var(--persona-radius-lg, 1rem)) !important;
3706
+ border-top-right-radius: var(--persona-artifact-welded-outer-radius, var(--persona-panel-radius, var(--persona-radius-xl, 0.75rem))) !important;
3707
+ border-bottom-right-radius: var(--persona-artifact-welded-outer-radius, var(--persona-panel-radius, var(--persona-radius-xl, 0.75rem))) !important;
3090
3708
  overflow: hidden;
3091
3709
  }
3710
+
3711
+ /* panel appearance: a hairline divider on the pane's chat-facing edge. seamless
3712
+ omits it (it already zeroes border-left). Non-!important so paneBorder /
3713
+ paneBorderLeft overrides win. */
3714
+ [data-persona-root]:not(.persona-artifact-narrow-host).persona-artifact-welded-split.persona-artifact-appearance-panel .persona-artifact-pane {
3715
+ border-left: 1px solid var(--persona-border, #e5e7eb);
3716
+ }
3092
3717
  }
3093
3718
 
3094
3719
  /* Narrow floating panel: drawer inside panel (class toggled by JS from panel width) */
@@ -3789,6 +4414,20 @@
3789
4414
  gap: 8px;
3790
4415
  }
3791
4416
 
4417
+ /* Configured / agent-pushed suggestion chips belong to the expanded
4418
+ * conversation surface, not the always-on collapsed composer. Keeping the
4419
+ * row in pillRoot lets it sit directly above the textarea without re-entering
4420
+ * the transcript's scrolling region. */
4421
+ .persona-widget-pill-root[data-state="collapsed"] [data-persona-composer-suggestions] {
4422
+ display: none;
4423
+ }
4424
+
4425
+ .persona-widget-pill-root[data-state="expanded"] [data-persona-composer-suggestions] {
4426
+ display: flex;
4427
+ justify-content: center;
4428
+ margin: 0 8px 4px;
4429
+ }
4430
+
3792
4431
  /* Attachment previews row (above the pill). AttachmentManager toggles
3793
4432
  * display:flex when items are added; default is display:none. */
3794
4433
  .persona-pill-composer__previews {
@@ -4039,3 +4678,214 @@
4039
4678
  display: none;
4040
4679
  }
4041
4680
  }
4681
+
4682
+ /* ===========================================================================
4683
+ * Context mentions (composer @-mentions): chip row, menu, affordance button.
4684
+ * Tokens default off the existing palette vars so themed widgets pick them up.
4685
+ * =========================================================================== */
4686
+
4687
+ /* Composer size-query container. Both the full and pill composers carry
4688
+ * `data-persona-composer-form`; establishing a container here lets a host
4689
+ * stylesheet make composer controls responsive to the PANEL width rather than
4690
+ * the viewport — correct across floating / docked / fullscreen, where the
4691
+ * composer is frequently narrower than the screen. Safe: the form is block-level
4692
+ * and already sized by its parent, so inline-size containment doesn't change its
4693
+ * width, and the ask sheet + mention menu mount OUTSIDE the form, so layout
4694
+ * containment can't capture them. Query with `@container persona-composer (...)`;
4695
+ * `.persona-mention-button` is a supported, stable hook. */
4696
+ [data-persona-composer-form] {
4697
+ container-type: inline-size;
4698
+ container-name: persona-composer;
4699
+ }
4700
+
4701
+ .persona-mention-context-row {
4702
+ display: none; /* toggled to flex by the manager when chips exist */
4703
+ flex-wrap: wrap;
4704
+ gap: var(--persona-mention-chip-gap, 6px);
4705
+ margin-bottom: var(--persona-mention-row-gap, 8px);
4706
+ }
4707
+
4708
+ .persona-mention-chip {
4709
+ display: inline-flex;
4710
+ align-items: center;
4711
+ gap: 5px;
4712
+ height: var(--persona-mention-chip-height, 26px);
4713
+ padding: 0 6px 0 8px;
4714
+ border-radius: var(--persona-mention-chip-radius, 6px);
4715
+ background: var(--persona-mention-chip-bg, var(--persona-container, #f3f4f6));
4716
+ border: 1px solid var(--persona-mention-chip-border, var(--persona-border, #e5e7eb));
4717
+ color: var(--persona-mention-chip-fg, var(--persona-text, #111827));
4718
+ font-size: var(--persona-mention-chip-font-size, 12px);
4719
+ line-height: 1;
4720
+ max-width: 220px;
4721
+ }
4722
+
4723
+ .persona-mention-chip[data-status="error"] {
4724
+ border-color: var(--persona-mention-error, #dc2626);
4725
+ color: var(--persona-mention-error, #dc2626);
4726
+ }
4727
+
4728
+ .persona-mention-chip-icon {
4729
+ display: inline-flex;
4730
+ align-items: center;
4731
+ flex: 0 0 auto;
4732
+ opacity: 0.8;
4733
+ }
4734
+
4735
+ .persona-mention-chip-label {
4736
+ overflow: hidden;
4737
+ text-overflow: ellipsis;
4738
+ white-space: nowrap;
4739
+ }
4740
+
4741
+ .persona-mention-chip-spinner {
4742
+ flex: 0 0 auto;
4743
+ width: 12px;
4744
+ height: 12px;
4745
+ border-radius: 50%;
4746
+ border: 2px solid var(--persona-mention-chip-border, var(--persona-border, #d1d5db));
4747
+ border-top-color: var(--persona-mention-accent, var(--persona-accent, #0f0f0f));
4748
+ animation: persona-mention-spin 0.7s linear infinite;
4749
+ }
4750
+
4751
+ @keyframes persona-mention-spin {
4752
+ to {
4753
+ transform: rotate(360deg);
4754
+ }
4755
+ }
4756
+
4757
+ .persona-mention-chip-remove {
4758
+ display: inline-flex;
4759
+ align-items: center;
4760
+ justify-content: center;
4761
+ flex: 0 0 auto;
4762
+ width: 16px;
4763
+ height: 16px;
4764
+ padding: 0;
4765
+ border: none;
4766
+ border-radius: 4px;
4767
+ background: transparent;
4768
+ color: inherit;
4769
+ opacity: 0.6;
4770
+ cursor: pointer;
4771
+ transition: opacity 0.15s ease, background-color 0.15s ease;
4772
+ }
4773
+
4774
+ .persona-mention-chip-remove:hover {
4775
+ opacity: 1;
4776
+ background: var(--persona-palette-colors-black-alpha-50, rgba(0, 0, 0, 0.06));
4777
+ }
4778
+
4779
+ /* The mention MENU CSS (.persona-mention-menu / -list / -search / -group /
4780
+ * -option / -status / -error / -retry and its coarse-pointer row sizing) lives
4781
+ * in the lazy context-mentions chunk (src/styles/context-mention-menu-css.ts)
4782
+ * and is injected on first menu open — so it also styles the body-mounted menu
4783
+ * inside a useShadowDom widget. Only the eagerly-rendered chip / context-row
4784
+ * pieces stay here. */
4785
+
4786
+ .persona-mention-chip-readonly {
4787
+ padding-right: 8px;
4788
+ cursor: default;
4789
+ }
4790
+
4791
+ /* Inline mention token (display: "inline"). Atomic styled chip that sits INSIDE
4792
+ * the sentence — in the contenteditable composer and in the read-only sent
4793
+ * bubble. Kept in the core stylesheet (tiny) so tokens are styled the instant a
4794
+ * segmented bubble renders, without waiting on the inline chunk's CSS. */
4795
+ .persona-mention-token {
4796
+ /* Per-token accent — set inline from `ref.color`, per source via
4797
+ * `.persona-mention-token[data-mention-source="…"]`, or globally by overriding
4798
+ * this var. Recolors the whole pill (tint bg + icon + label). */
4799
+ --persona-mention-token-accent: var(--persona-accent, #4338ca);
4800
+ display: inline-flex;
4801
+ align-items: center;
4802
+ gap: var(--persona-mention-token-gap, 4px);
4803
+ padding: var(--persona-mention-token-padding, 1px 6px);
4804
+ border-radius: var(--persona-mention-token-radius, 6px);
4805
+ /* Fallback flat tint first (older browsers), then a per-accent translucent
4806
+ * tint via color-mix so each type reads as its own color (Slack/Cursor). */
4807
+ background: var(--persona-mention-token-bg, #eef2ff);
4808
+ background: var(
4809
+ --persona-mention-token-bg,
4810
+ color-mix(in srgb, var(--persona-mention-token-accent) 14%, transparent)
4811
+ );
4812
+ color: var(--persona-mention-token-fg, var(--persona-mention-token-accent));
4813
+ font-size: var(--persona-mention-token-font-size, 0.95em);
4814
+ font-weight: var(--persona-mention-token-font-weight, 550);
4815
+ line-height: 1.5;
4816
+ white-space: nowrap;
4817
+ vertical-align: baseline;
4818
+ user-select: none;
4819
+ }
4820
+
4821
+ .persona-mention-token-icon {
4822
+ display: inline-flex;
4823
+ align-items: center;
4824
+ }
4825
+
4826
+ .persona-mention-token-label {
4827
+ white-space: nowrap;
4828
+ }
4829
+
4830
+ /* Read-only token in a sent bubble: no editable affordances (the composer's
4831
+ * atomic caret/select behavior doesn't apply), so it reads as inert prose. */
4832
+ .persona-mention-token-readonly {
4833
+ cursor: default;
4834
+ user-select: text;
4835
+ }
4836
+
4837
+ /* Resolve failure on an inline token: recolor the pill to the danger accent and
4838
+ * add a subtle inset border ring, reusing the same accent custom-property the
4839
+ * token already themes on (so bg tint + icon + label all shift together). */
4840
+ .persona-mention-token-error {
4841
+ --persona-mention-token-accent: var(--persona-mention-error, #dc2626);
4842
+ box-shadow: inset 0 0 0 1px
4843
+ color-mix(in srgb, var(--persona-mention-token-accent) 45%, transparent);
4844
+ }
4845
+
4846
+ /* The contenteditable composer surface: behaves like the textarea it replaced. */
4847
+ .persona-composer-contenteditable {
4848
+ white-space: pre-wrap;
4849
+ overflow-wrap: anywhere;
4850
+ outline: none;
4851
+ max-height: var(--persona-composer-max-height, 200px);
4852
+ overflow-y: auto;
4853
+ }
4854
+
4855
+ .persona-composer-contenteditable:empty::before {
4856
+ content: attr(data-placeholder);
4857
+ color: var(--persona-placeholder, var(--persona-muted, #9ca3af));
4858
+ pointer-events: none;
4859
+ }
4860
+
4861
+ .persona-message-mentions {
4862
+ margin-top: 6px;
4863
+ margin-bottom: 0;
4864
+ }
4865
+
4866
+ /* Touch targets: on coarse pointers (phones/tablets) grow the chip + its remove
4867
+ * control to the ~44px minimum. Desktop keeps the compact sizing above. (The
4868
+ * menu row / retry coarse sizing ships with the menu CSS in the lazy chunk.) */
4869
+ @media (pointer: coarse) {
4870
+ .persona-mention-chip {
4871
+ height: auto;
4872
+ min-height: 40px;
4873
+ }
4874
+ .persona-mention-chip-remove {
4875
+ width: 36px;
4876
+ height: 36px;
4877
+ margin-right: -6px; /* keep the visual gap tight while the hit area grows */
4878
+ }
4879
+ }
4880
+
4881
+ @media (prefers-reduced-motion: reduce) {
4882
+ /* No spin: show a static neutral ring instead of a rotating arc. */
4883
+ .persona-mention-chip-spinner {
4884
+ animation: none;
4885
+ border-top-color: var(
4886
+ --persona-mention-chip-border,
4887
+ var(--persona-border, #d1d5db)
4888
+ );
4889
+ opacity: 0.6;
4890
+ }
4891
+ }