@aparte/core 0.10.0 → 0.11.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 (82) hide show
  1. package/dist/client/aparte-client.d.ts +54 -33
  2. package/dist/client/aparte-client.d.ts.map +1 -1
  3. package/dist/client/stream-adapter.d.ts +6 -1
  4. package/dist/client/stream-adapter.d.ts.map +1 -1
  5. package/dist/components/bubble/aparte-chat-bubble.d.ts +25 -4
  6. package/dist/components/bubble/aparte-chat-bubble.d.ts.map +1 -1
  7. package/dist/components/chat/aparte-chat.d.ts +8 -4
  8. package/dist/components/chat/aparte-chat.d.ts.map +1 -1
  9. package/dist/components/composer/aparte-composer-action.d.ts +5 -5
  10. package/dist/components/composer/aparte-composer-add-attachment.d.ts +5 -4
  11. package/dist/components/composer/aparte-composer-add-attachment.d.ts.map +1 -1
  12. package/dist/components/composer/aparte-composer-attachments.d.ts +5 -3
  13. package/dist/components/composer/aparte-composer-attachments.d.ts.map +1 -1
  14. package/dist/components/composer/aparte-composer-cancel.d.ts +2 -2
  15. package/dist/components/composer/aparte-composer-input.d.ts +8 -5
  16. package/dist/components/composer/aparte-composer-input.d.ts.map +1 -1
  17. package/dist/components/composer/aparte-composer-send.d.ts +2 -2
  18. package/dist/components/composer/aparte-composer-toolbar.d.ts +3 -3
  19. package/dist/components/composer/aparte-composer.d.ts +54 -10
  20. package/dist/components/composer/aparte-composer.d.ts.map +1 -1
  21. package/dist/components/conversation-list/aparte-conversation-list.d.ts +19 -12
  22. package/dist/components/conversation-list/aparte-conversation-list.d.ts.map +1 -1
  23. package/dist/components/elicitation/aparte-elicitation.d.ts +12 -0
  24. package/dist/components/elicitation/aparte-elicitation.d.ts.map +1 -1
  25. package/dist/components/status/aparte-chat-status.d.ts +9 -0
  26. package/dist/components/status/aparte-chat-status.d.ts.map +1 -1
  27. package/dist/components/viewport/aparte-chat-viewport.d.ts +15 -4
  28. package/dist/components/viewport/aparte-chat-viewport.d.ts.map +1 -1
  29. package/dist/config/aparte-config.d.ts +20 -1
  30. package/dist/config/aparte-config.d.ts.map +1 -1
  31. package/dist/config/locale.d.ts +43 -0
  32. package/dist/config/locale.d.ts.map +1 -1
  33. package/dist/custom-elements.json +8517 -7601
  34. package/dist/elicitation/approval-panel.d.ts +42 -0
  35. package/dist/elicitation/approval-panel.d.ts.map +1 -0
  36. package/dist/elicitation/index.d.ts +4 -1
  37. package/dist/elicitation/index.d.ts.map +1 -1
  38. package/dist/elicitation/panel.d.ts +1 -1
  39. package/dist/elicitation/panel.d.ts.map +1 -1
  40. package/dist/elicitation/types.d.ts +116 -9
  41. package/dist/elicitation/types.d.ts.map +1 -1
  42. package/dist/generated/element-attributes.d.ts +173 -0
  43. package/dist/generated/element-attributes.d.ts.map +1 -0
  44. package/dist/index.css +615 -92
  45. package/dist/index.d.ts +3 -3
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +341 -254
  48. package/dist/index.js.map +1 -1
  49. package/dist/index.node.d.ts +3 -3
  50. package/dist/index.node.d.ts.map +1 -1
  51. package/dist/index.node.js +46 -44
  52. package/dist/index.node.js.map +1 -1
  53. package/dist/interop/element-props.d.ts +26 -1
  54. package/dist/interop/element-props.d.ts.map +1 -1
  55. package/dist/{index-BnKBciDG.js → is-awaiting-reply-Cb7H3YfN.js} +1448 -1544
  56. package/dist/is-awaiting-reply-Cb7H3YfN.js.map +1 -0
  57. package/dist/primitives/select/aparte-optgroup.d.ts +3 -0
  58. package/dist/primitives/select/aparte-optgroup.d.ts.map +1 -1
  59. package/dist/primitives/select/aparte-option.d.ts +1 -0
  60. package/dist/primitives/select/aparte-option.d.ts.map +1 -1
  61. package/dist/primitives/select/aparte-select.d.ts +22 -18
  62. package/dist/primitives/select/aparte-select.d.ts.map +1 -1
  63. package/dist/renderers/segment-renderers.d.ts.map +1 -1
  64. package/dist/renderers/segments/artifact/card.d.ts.map +1 -1
  65. package/dist/renderers/segments/pipeline-waiting.d.ts.map +1 -1
  66. package/dist/renderers/segments/tool-call.d.ts.map +1 -1
  67. package/dist/types/element-attributes.d.ts +42 -0
  68. package/dist/types/element-attributes.d.ts.map +1 -0
  69. package/dist/types/event-map.d.ts +1 -2
  70. package/dist/types/event-map.d.ts.map +1 -1
  71. package/dist/types/index.d.ts +3 -2
  72. package/dist/types/index.d.ts.map +1 -1
  73. package/dist/types/segments.d.ts +1 -8
  74. package/dist/types/segments.d.ts.map +1 -1
  75. package/dist/types/tools.d.ts +15 -21
  76. package/dist/types/tools.d.ts.map +1 -1
  77. package/dist/utils/segments.d.ts +8 -0
  78. package/dist/utils/segments.d.ts.map +1 -1
  79. package/package.json +3 -2
  80. package/dist/index-BnKBciDG.js.map +0 -1
  81. package/dist/renderers/segments/progress.d.ts +0 -3
  82. package/dist/renderers/segments/progress.d.ts.map +0 -1
package/dist/index.css CHANGED
@@ -192,6 +192,11 @@
192
192
  --aparte-elic-option-desc-size: 0.78rem;
193
193
  --aparte-elic-control-size: 15px;
194
194
  --aparte-elic-input-radius: 6px;
195
+ /* The tool-call row's corner. Declared here rather than left as an inline
196
+ fallback in the renderer: an undeclared custom property still WORKS, which is
197
+ what makes it dangerous — it is absent from the generated CSS reference, so a
198
+ consumer has no way to discover the knob exists. */
199
+ --aparte-tool-row-radius: 6px;
195
200
  --aparte-elic-textarea-min-height: 64px;
196
201
  --aparte-elic-skip-size: 0.8rem;
197
202
  --aparte-elic-step-size: 0.78rem;
@@ -229,7 +234,7 @@
229
234
  --aparte-error-icon-size: 20px;
230
235
  --aparte-error-rail-width: 4px;
231
236
 
232
- /* Prose (segment-content) */
237
+ /* Prose (aparte-segment-content) */
233
238
  --aparte-prose-p-margin: 0.4em 0;
234
239
  --aparte-prose-h1-size: 1.4em;
235
240
  --aparte-prose-h2-size: 1.25em;
@@ -412,10 +417,6 @@ aparte-chat {
412
417
  --aparte-code-border: var(--aparte-border);
413
418
  --aparte-code-header-text: var(--aparte-text-muted);
414
419
 
415
- /* Progress */
416
- --aparte-progress-bar-bg: var(--aparte-surface-3);
417
- --aparte-progress-bar-fill: var(--aparte-primary);
418
- --aparte-progress-label: var(--aparte-text);
419
420
 
420
421
  /* Scroll-to-bottom button */
421
422
  --aparte-scroll-btn-bg: var(--aparte-surface-1);
@@ -423,7 +424,7 @@ aparte-chat {
423
424
  --aparte-scroll-btn-border: var(--aparte-border);
424
425
  --aparte-scroll-btn-hover-bg: var(--aparte-surface-2);
425
426
 
426
- /* Prose (segment-content) */
427
+ /* Prose (aparte-segment-content) */
427
428
  --aparte-prose-blockquote-border: var(--aparte-border);
428
429
  --aparte-prose-code-bg: var(--aparte-surface-2);
429
430
 
@@ -670,6 +671,91 @@ textarea.aparte-elic-text {
670
671
  z-index: 1;
671
672
  }
672
673
 
674
+ /* ── An approval: a decision, not a value ───────────────────────────────────
675
+ The options are buttons, not radios, because a decision has two primary actions
676
+ of opposite polarity and the composer's one button cannot carry both. So they
677
+ settle on the first click, and approving costs one gesture rather than two.
678
+
679
+ The colours are the ones the inline Approve / Reject pair used, moved here from
680
+ the runtime-injected `getStyles()` string in the tool_call renderer. That move is
681
+ the point: injected CSS is invisible to a consumer's own rules, absent from the
682
+ generated CSS reference, and unreachable by `check:derived-vars`. The elicitation
683
+ panel made the same move for the same reason. */
684
+ /* A COLUMN, not a row of pills.
685
+ The panel lives in a composer, which is narrow — two or three labels side by side
686
+ either wrap into an unreadable jumble or get squeezed. And the labels are not short:
687
+ a scope option reads "Approve, and always for this tool". A stacked list is also what
688
+ every reference implementation of this shows, for the same reason: one target per
689
+ line, full width, so the choice is read rather than aimed at. */
690
+ .aparte-approval-options {
691
+ display: flex;
692
+ flex-direction: column;
693
+ gap: var(--aparte-approval-gap, 4px);
694
+ margin: 0 6px;
695
+ }
696
+
697
+ .aparte-approval-option {
698
+ font: inherit;
699
+ font-size: var(--aparte-approval-option-size, 0.85rem);
700
+ font-weight: var(--aparte-font-weight-medium, 500);
701
+ line-height: 1.35;
702
+ /* Left-aligned and full width: the eye reads down a list, and a centred label in a
703
+ wide target is harder to scan than a flush one. */
704
+ text-align: start;
705
+ width: 100%;
706
+ padding: var(--aparte-approval-option-padding, 8px 10px);
707
+ border-radius: var(--aparte-approval-option-radius, 8px);
708
+ cursor: pointer;
709
+ border: 1px solid var(--aparte-border, rgba(128, 128, 128, 0.3));
710
+ background: transparent;
711
+ color: var(--aparte-text);
712
+ }
713
+
714
+ .aparte-approval-option:hover {
715
+ background: var(--aparte-surface-2, rgba(128, 128, 128, 0.08));
716
+ }
717
+
718
+ /* Tone is carried by a leading rule, not by colouring the label or ringing the whole
719
+ button. Both of those were tried: a green word on a dark panel reads as a link, and a
720
+ coloured border on every option turns the list into a traffic light. A 2px edge says
721
+ which way an option goes while leaving the text plain. */
722
+ /* The SOLID colours, not the `-border` pair. Those carry ~40% alpha because they were
723
+ made for a 1px box outline over a surface; on a 2px rule against a light background
724
+ the deny edge came out a pale pink that read as decoration. A rule wants a colour. */
725
+ .aparte-approval-option--affirm {
726
+ border-inline-start: 2px solid var(--aparte-success, rgb(21, 128, 61));
727
+ }
728
+
729
+ .aparte-approval-option--deny {
730
+ border-inline-start: 2px solid var(--aparte-error, rgb(185, 28, 28));
731
+ }
732
+
733
+ /* The refusal's reason, in the user's words. Quiet by default and one line tall: it is
734
+ the exception, not the expected act, and a box sized for a paragraph invites one. */
735
+ .aparte-approval-instruction {
736
+ font: inherit;
737
+ font-size: var(--aparte-elic-input-size, 0.85rem);
738
+ color: var(--aparte-text);
739
+ background: transparent;
740
+ border: none;
741
+ border-top: 1px solid var(--aparte-border, rgba(128, 128, 128, 0.3));
742
+ border-radius: 0;
743
+ padding: 8px 10px 2px;
744
+ margin: 4px 6px 0;
745
+ resize: none;
746
+ overflow: hidden;
747
+ min-height: 1.6em;
748
+ }
749
+
750
+ .aparte-approval-instruction:focus {
751
+ outline: none;
752
+ border-top-color: var(--aparte-accent, rgba(128, 128, 128, 0.6));
753
+ }
754
+
755
+ .aparte-approval-instruction::placeholder {
756
+ color: var(--aparte-text-muted, rgba(128, 128, 128, 0.75));
757
+ }
758
+
673
759
  .aparte-elic-skip {
674
760
  border: none;
675
761
  background: transparent;
@@ -1474,7 +1560,7 @@ aparte-chat-bubble:last-of-type .aparte-message[data-role="assistant"]:not(.apar
1474
1560
  font-size: var(--aparte-font-size-md, 13px);
1475
1561
  }
1476
1562
 
1477
- .aparte-input-upper.has-content {
1563
+ .aparte-input-upper.aparte-has-content {
1478
1564
  display: flex;
1479
1565
  }
1480
1566
 
@@ -1701,7 +1787,7 @@ aparte-chat-status:not([visible]) {
1701
1787
  ========================================= */
1702
1788
 
1703
1789
  /* Text */
1704
- .segment-text {
1790
+ .aparte-segment-text {
1705
1791
  word-wrap: break-word;
1706
1792
  }
1707
1793
 
@@ -1712,60 +1798,60 @@ aparte-chat-status:not([visible]) {
1712
1798
  ========================================= */
1713
1799
 
1714
1800
  .aparte-content,
1715
- .segment-content {
1801
+ .aparte-segment-content {
1716
1802
  white-space: normal;
1717
1803
  }
1718
1804
 
1719
1805
  .aparte-content p,
1720
- .segment-content p,
1721
- .thinking-content p {
1806
+ .aparte-segment-content p,
1807
+ .aparte-thinking-content p {
1722
1808
  margin: var(--aparte-prose-p-margin, 0.4em 0);
1723
1809
  }
1724
1810
 
1725
1811
  .aparte-content p:first-child,
1726
- .segment-content p:first-child,
1727
- .thinking-content p:first-child {
1812
+ .aparte-segment-content p:first-child,
1813
+ .aparte-thinking-content p:first-child {
1728
1814
  margin-top: 0;
1729
1815
  }
1730
1816
 
1731
1817
  .aparte-content p:last-child,
1732
- .segment-content p:last-child,
1733
- .thinking-content p:last-child {
1818
+ .aparte-segment-content p:last-child,
1819
+ .aparte-thinking-content p:last-child {
1734
1820
  margin-bottom: 0;
1735
1821
  }
1736
1822
 
1737
- .aparte-content h1, .segment-content h1, .thinking-content h1 { font-size: var(--aparte-prose-h1-size, 1.4em); font-weight: var(--aparte-font-weight-semibold, 600); margin: 0.6em 0 0.2em; }
1738
- .aparte-content h2, .segment-content h2, .thinking-content h2 { font-size: var(--aparte-prose-h2-size, 1.25em); font-weight: var(--aparte-font-weight-semibold, 600); margin: 0.5em 0 0.2em; }
1739
- .aparte-content h3, .segment-content h3, .thinking-content h3 { font-size: var(--aparte-prose-h3-size, 1.1em); font-weight: var(--aparte-font-weight-semibold, 600); margin: 0.4em 0 0.15em; }
1740
- .aparte-content h4, .segment-content h4, .thinking-content h4,
1741
- .aparte-content h5, .segment-content h5, .thinking-content h5,
1742
- .aparte-content h6, .segment-content h6, .thinking-content h6 { font-size: 1em; font-weight: var(--aparte-font-weight-semibold, 600); margin: 0.3em 0 0.1em; }
1823
+ .aparte-content h1, .aparte-segment-content h1, .aparte-thinking-content h1 { font-size: var(--aparte-prose-h1-size, 1.4em); font-weight: var(--aparte-font-weight-semibold, 600); margin: 0.6em 0 0.2em; }
1824
+ .aparte-content h2, .aparte-segment-content h2, .aparte-thinking-content h2 { font-size: var(--aparte-prose-h2-size, 1.25em); font-weight: var(--aparte-font-weight-semibold, 600); margin: 0.5em 0 0.2em; }
1825
+ .aparte-content h3, .aparte-segment-content h3, .aparte-thinking-content h3 { font-size: var(--aparte-prose-h3-size, 1.1em); font-weight: var(--aparte-font-weight-semibold, 600); margin: 0.4em 0 0.15em; }
1826
+ .aparte-content h4, .aparte-segment-content h4, .aparte-thinking-content h4,
1827
+ .aparte-content h5, .aparte-segment-content h5, .aparte-thinking-content h5,
1828
+ .aparte-content h6, .aparte-segment-content h6, .aparte-thinking-content h6 { font-size: 1em; font-weight: var(--aparte-font-weight-semibold, 600); margin: 0.3em 0 0.1em; }
1743
1829
 
1744
1830
  .aparte-content ul,
1745
- .segment-content ul,
1746
- .thinking-content ul {
1831
+ .aparte-segment-content ul,
1832
+ .aparte-thinking-content ul {
1747
1833
  list-style: disc;
1748
1834
  padding-left: var(--aparte-prose-list-indent, 1.5em);
1749
1835
  margin: var(--aparte-prose-list-margin, 0.4em 0);
1750
1836
  }
1751
1837
 
1752
1838
  .aparte-content ol,
1753
- .segment-content ol,
1754
- .thinking-content ol {
1839
+ .aparte-segment-content ol,
1840
+ .aparte-thinking-content ol {
1755
1841
  list-style: decimal;
1756
1842
  padding-left: var(--aparte-prose-list-indent, 1.5em);
1757
1843
  margin: var(--aparte-prose-list-margin, 0.4em 0);
1758
1844
  }
1759
1845
 
1760
1846
  .aparte-content li,
1761
- .segment-content li,
1762
- .thinking-content li {
1847
+ .aparte-segment-content li,
1848
+ .aparte-thinking-content li {
1763
1849
  margin: var(--aparte-prose-li-margin, 0.15em 0);
1764
1850
  }
1765
1851
 
1766
1852
  .aparte-content blockquote,
1767
- .segment-content blockquote,
1768
- .thinking-content blockquote {
1853
+ .aparte-segment-content blockquote,
1854
+ .aparte-thinking-content blockquote {
1769
1855
  border-left: var(--aparte-prose-blockquote-border-width, 3px) solid var(--aparte-prose-blockquote-border, var(--aparte-border, #e5e7eb));
1770
1856
  padding-left: 1em;
1771
1857
  margin: 0.4em 0;
@@ -1774,20 +1860,20 @@ aparte-chat-status:not([visible]) {
1774
1860
  }
1775
1861
 
1776
1862
  .aparte-content strong,
1777
- .segment-content strong,
1778
- .thinking-content strong {
1863
+ .aparte-segment-content strong,
1864
+ .aparte-thinking-content strong {
1779
1865
  font-weight: var(--aparte-font-weight-semibold, 600);
1780
1866
  }
1781
1867
 
1782
1868
  .aparte-content em,
1783
- .segment-content em,
1784
- .thinking-content em {
1869
+ .aparte-segment-content em,
1870
+ .aparte-thinking-content em {
1785
1871
  font-style: italic;
1786
1872
  }
1787
1873
 
1788
1874
  .aparte-content code,
1789
- .segment-content code,
1790
- .thinking-content code {
1875
+ .aparte-segment-content code,
1876
+ .aparte-thinking-content code {
1791
1877
  font-family: var(--aparte-code-font-family, ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, monospace);
1792
1878
  font-size: var(--aparte-code-inline-font-size, 0.875em);
1793
1879
  background: var(--aparte-prose-code-bg, var(--aparte-code-bg, #f3f4f6));
@@ -1796,29 +1882,29 @@ aparte-chat-status:not([visible]) {
1796
1882
  }
1797
1883
 
1798
1884
  .aparte-content a,
1799
- .segment-content a,
1800
- .thinking-content a {
1885
+ .aparte-segment-content a,
1886
+ .aparte-thinking-content a {
1801
1887
  color: var(--aparte-primary, #6366f1);
1802
1888
  text-decoration: underline;
1803
1889
  }
1804
1890
 
1805
1891
  .aparte-content hr,
1806
- .segment-content hr,
1807
- .thinking-content hr {
1892
+ .aparte-segment-content hr,
1893
+ .aparte-thinking-content hr {
1808
1894
  border: none;
1809
1895
  border-top: var(--aparte-border-width, 1px) solid var(--aparte-border, #e5e7eb);
1810
1896
  margin: 0.75em 0;
1811
1897
  }
1812
1898
 
1813
1899
  /* Thinking */
1814
- .segment-thinking {
1900
+ .aparte-segment-thinking {
1815
1901
  /* Understated disclosure: a muted left rail, not a filled card. */
1816
1902
  border-left: var(--aparte-thinking-rail-width, 2px) solid var(--aparte-thinking-border, #e5e7eb);
1817
1903
  margin: var(--aparte-space-5, 10px) 0;
1818
1904
  background: transparent;
1819
1905
  }
1820
1906
 
1821
- .thinking-header {
1907
+ .aparte-thinking-header {
1822
1908
  display: flex;
1823
1909
  align-items: center;
1824
1910
  gap: var(--aparte-space-3, 6px);
@@ -1830,27 +1916,27 @@ aparte-chat-status:not([visible]) {
1830
1916
  list-style: none;
1831
1917
  }
1832
1918
 
1833
- .thinking-header::-webkit-details-marker {
1919
+ .aparte-thinking-header::-webkit-details-marker {
1834
1920
  display: none;
1835
1921
  }
1836
1922
 
1837
- .thinking-toggle {
1923
+ .aparte-thinking-toggle {
1838
1924
  margin-inline-start: auto;
1839
1925
  /* Logical property */
1840
1926
  transition: transform 0.2s;
1841
1927
  opacity: 0.55;
1842
1928
  }
1843
1929
 
1844
- details[open] .thinking-toggle {
1930
+ details[open] .aparte-thinking-toggle {
1845
1931
  transform: rotate(180deg);
1846
1932
  }
1847
1933
 
1848
- .thinking-toggle::after {
1934
+ .aparte-thinking-toggle::after {
1849
1935
  content: '▼';
1850
1936
  font-size: var(--aparte-thinking-toggle-size, 9px);
1851
1937
  }
1852
1938
 
1853
- .thinking-content {
1939
+ .aparte-thinking-content {
1854
1940
  padding: var(--aparte-space-2, 4px) 0 var(--aparte-space-1, 2px) var(--aparte-space-5, 10px);
1855
1941
  font-size: var(--aparte-font-size-md, 13px);
1856
1942
  line-height: var(--aparte-thinking-content-line-height, 1.55);
@@ -1861,14 +1947,14 @@ details[open] .thinking-toggle {
1861
1947
  }
1862
1948
 
1863
1949
  /* Code */
1864
- .segment-code {
1950
+ .aparte-segment-code {
1865
1951
  margin: var(--aparte-space-6, 12px) 0;
1866
1952
  border-radius: var(--aparte-radius-code);
1867
1953
  overflow: hidden;
1868
1954
  border: var(--aparte-border-width, 1px) solid var(--aparte-code-border, #e5e7eb);
1869
1955
  }
1870
1956
 
1871
- .code-header {
1957
+ .aparte-code-header {
1872
1958
  display: flex;
1873
1959
  align-items: center;
1874
1960
  padding: var(--aparte-space-4, 8px) var(--aparte-space-6, 12px);
@@ -1878,25 +1964,25 @@ details[open] .thinking-toggle {
1878
1964
  color: var(--aparte-code-header-text, #4b5563);
1879
1965
  }
1880
1966
 
1881
- .code-filename {
1967
+ .aparte-code-filename {
1882
1968
  font-weight: var(--aparte-font-weight-semibold, 600);
1883
1969
  margin-inline-end: auto;
1884
1970
  /* Logical property: pushes language + copy to the right when filename is present */
1885
1971
  }
1886
1972
 
1887
- .code-header-filler {
1973
+ .aparte-code-header-filler {
1888
1974
  flex: 1;
1889
1975
  /* No-filename placeholder — keeps language + copy right-aligned */
1890
1976
  }
1891
1977
 
1892
- .code-language {
1978
+ .aparte-code-language {
1893
1979
  margin-inline-end: var(--aparte-space-4, 8px);
1894
1980
  /* Logical property */
1895
1981
  text-transform: uppercase;
1896
1982
  opacity: 0.7;
1897
1983
  }
1898
1984
 
1899
- .code-copy {
1985
+ .aparte-code-copy {
1900
1986
  background: transparent;
1901
1987
  border: none;
1902
1988
  cursor: pointer;
@@ -1908,23 +1994,23 @@ details[open] .thinking-toggle {
1908
1994
  justify-content: center;
1909
1995
  }
1910
1996
 
1911
- .code-copy:hover {
1997
+ .aparte-code-copy:hover {
1912
1998
  background: var(--aparte-code-copy-hover-bg, rgba(0, 0, 0, 0.05));
1913
1999
  }
1914
2000
 
1915
- .code-copy svg {
2001
+ .aparte-code-copy svg {
1916
2002
  width: var(--aparte-code-copy-icon-size, 14px);
1917
2003
  height: var(--aparte-code-copy-icon-size, 14px);
1918
2004
  }
1919
2005
 
1920
- .code-content-wrapper pre {
2006
+ .aparte-code-content-wrapper pre {
1921
2007
  margin: 0;
1922
2008
  padding: var(--aparte-space-6, 12px);
1923
2009
  /* Wrap long lines instead of a horizontal scrollbar. `pre-wrap` honours
1924
2010
  newlines + indentation but soft-wraps overflowing lines; `overflow-wrap:
1925
2011
  anywhere` breaks unbreakable tokens (long URLs, hashes). Applies to both
1926
2012
  the plain <pre><code> initial render and the shiki <pre class="shiki">
1927
- replacement, since both sit inside .code-content-wrapper. */
2013
+ replacement, since both sit inside .aparte-code-content-wrapper. */
1928
2014
  white-space: pre-wrap;
1929
2015
  overflow-wrap: anywhere;
1930
2016
  overflow-x: hidden;
@@ -1940,7 +2026,7 @@ details[open] .thinking-toggle {
1940
2026
  }
1941
2027
 
1942
2028
  /* Error */
1943
- .segment-error {
2029
+ .aparte-segment-error {
1944
2030
  display: flex;
1945
2031
  gap: var(--aparte-space-6, 12px);
1946
2032
  padding: var(--aparte-space-6, 12px) var(--aparte-space-8, 16px);
@@ -1953,7 +2039,7 @@ details[open] .thinking-toggle {
1953
2039
  font-family: var(--aparte-font-family, system-ui, sans-serif);
1954
2040
  }
1955
2041
 
1956
- .error-icon-wrapper {
2042
+ .aparte-error-icon-wrapper {
1957
2043
  flex-shrink: 0;
1958
2044
  color: var(--aparte-error-solid, #dc2626);
1959
2045
  display: flex;
@@ -1961,29 +2047,29 @@ details[open] .thinking-toggle {
1961
2047
  padding-top: var(--aparte-space-1, 2px);
1962
2048
  }
1963
2049
 
1964
- .error-icon-wrapper svg {
2050
+ .aparte-error-icon-wrapper svg {
1965
2051
  width: var(--aparte-error-icon-size, 20px);
1966
2052
  height: var(--aparte-error-icon-size, 20px);
1967
2053
  }
1968
2054
 
1969
- .error-content {
2055
+ .aparte-error-content {
1970
2056
  flex: 1;
1971
2057
  min-width: 0;
1972
2058
  }
1973
2059
 
1974
- .error-title {
2060
+ .aparte-error-title {
1975
2061
  font-weight: var(--aparte-font-weight-semibold, 600);
1976
2062
  font-size: var(--aparte-font-size-lg, 14px);
1977
2063
  margin-bottom: var(--aparte-space-2, 4px);
1978
2064
  color: var(--aparte-error-title, #7f1d1d);
1979
2065
  }
1980
2066
 
1981
- .error-message {
2067
+ .aparte-error-message {
1982
2068
  font-size: var(--aparte-font-size-lg, 14px);
1983
2069
  line-height: var(--aparte-line-height-normal, 1.5);
1984
2070
  }
1985
2071
 
1986
- .error-details {
2072
+ .aparte-error-details {
1987
2073
  margin-top: var(--aparte-space-4, 8px);
1988
2074
  padding-top: var(--aparte-space-4, 8px);
1989
2075
  border-top: var(--aparte-border-width, 1px) solid var(--aparte-error-border, #fecaca);
@@ -1993,35 +2079,455 @@ details[open] .thinking-toggle {
1993
2079
  white-space: pre-wrap;
1994
2080
  }
1995
2081
 
1996
- /* Progress */
1997
- .segment-progress {
1998
- margin: var(--aparte-space-6, 12px) 0;
1999
- }
2000
2082
 
2001
- .progress-header {
2002
- display: flex;
2003
- justify-content: space-between;
2004
- align-items: center;
2005
- margin-bottom: var(--aparte-space-2, 4px);
2006
- color: var(--aparte-progress-label, #374151);
2007
- font-size: var(--aparte-font-size-lg, 14px);
2008
- }
2083
+ /* ─────────────────────────────────────────────────────────────────────────────
2084
+ Segment renderers — tool call · artifact card · pipeline waiting
2085
+
2086
+ These three shipped their CSS from `getStyles()` until now: a template literal
2087
+ in the renderer's own .ts file, injected into <head> at runtime. That seam is
2088
+ for a CONSUMER's renderer — something registered through registerSegmentRenderer
2089
+ or registerToolRenderer cannot edit this file, so `getStyles()` is its only way
2090
+ onto the page. A BUILT-IN has this file, and two reasons to use it:
2091
+
2092
+ - check:derived-vars reads this path and nothing else. A declaration whose
2093
+ value reads another variable has to sit on the 5-anchor layer or it cannot
2094
+ follow a palette a subtree overrides, and one hidden in a renderer was
2095
+ never checked at all.
2096
+ - it is read as CSS, by tools and by people. A backtick closes a template
2097
+ literal, and one did — the artifact card's own comment recorded it
2098
+ happening — then it happened three more times in one sitting, the worst of
2099
+ them rendering a `safe-text` source marker into an assistant's bubble as
2100
+ prose, because inside a template literal a `//` comment is just text.
2101
+
2102
+ NOT a reason, though it was the first one I wrote here: reaching the public CSS
2103
+ reference. `gen-css-vars.mjs` walks all of core's source for `var(--aparte-*)`
2104
+ reads, so a knob a renderer read was already listed with its fallback as the
2105
+ default — the move changed that file by zero lines. Worth recording, because the
2106
+ plausible version of this argument is the one that gets repeated.
2107
+
2108
+ Moving them changes no contract either: core's entry imports this file and
2109
+ package.json marks every .css a side effect, so importing @aparte/core has
2110
+ always brought this sheet along.
2111
+ ───────────────────────────────────────────────────────────────────────────── */
2009
2112
 
2010
- .progress-bar {
2011
- height: var(--aparte-space-2, 4px);
2012
- background: var(--aparte-progress-bar-bg, #e5e7eb);
2013
- border-radius: var(--aparte-radius-xs);
2014
- margin-top: var(--aparte-space-4, 8px);
2015
- overflow: hidden;
2113
+ /* Tool call */
2114
+ .aparte-segment-tool-call { display: block; padding: 2px 0; }
2115
+ /* The row is the summary. list-style:none plus the ::-webkit- pseudo is the
2116
+ pair that actually removes the native marker across engines. */
2117
+ /* A ROW, not a badge.
2118
+ It was a rounded chip, which is what a tag looks like — and a tool call is not
2119
+ a tag, it is a step of the turn. Every current implementation draws it as a
2120
+ line: the disclosure control, the call's identity, and its state at the far
2121
+ end. The chip also had to carry a border and a fill, so it competed with the
2122
+ reply text beside it for attention it did not deserve. */
2123
+ .aparte-tool-summary {
2124
+ display: flex; align-items: center; gap: 7px;
2125
+ padding: 3px 6px; margin-inline-start: -6px;
2126
+ border-radius: var(--aparte-tool-row-radius, 6px);
2127
+ cursor: pointer; list-style: none;
2128
+ }
2129
+ .aparte-tool-summary:hover { background: var(--aparte-surface-2, rgba(128,128,128,0.07)); }
2130
+ .aparte-tool-summary::-webkit-details-marker { display: none; }
2131
+ .aparte-tool-toggle {
2132
+ width: 0; height: 0; flex: none; margin-inline-start: -1px;
2133
+ border-inline-start: 5px solid currentColor;
2134
+ border-block: 4px solid transparent;
2135
+ opacity: 0.5; transition: transform 0.15s;
2136
+ }
2137
+ .aparte-tool-summary:hover .aparte-tool-toggle { opacity: 0.9; }
2138
+ details[open] > .aparte-tool-summary .aparte-tool-toggle { transform: rotate(90deg); }
2139
+ /* A rule down the leading edge, so the disclosure reads as belonging to the
2140
+ pill above it rather than continuing the message text below. Without it
2141
+ "Output" and the assistant's next sentence sat in one undifferentiated
2142
+ column. */
2143
+ .aparte-tool-detail {
2144
+ display: flex; flex-direction: column; gap: 8px;
2145
+ /* Aligned under the row's icon, so the rule reads as descending from the
2146
+ call rather than floating beside it. */
2147
+ margin: 4px 0 6px 3px; padding: 2px 0 2px 12px;
2148
+ border-inline-start: 1px solid var(--aparte-border, rgba(128,128,128,0.25));
2149
+ }
2150
+ .aparte-tool-part-label {
2151
+ display: block; font-size: 0.68rem; text-transform: uppercase;
2152
+ letter-spacing: 0.04em; opacity: 0.55; margin-bottom: 2px;
2153
+ }
2154
+ .aparte-tool-part-body pre { margin: 0; }
2155
+ /* Always in the DOM so update can leave the node alone and toggle hidden
2156
+ instead — an attribute a test can read, where a CSS-only rule is invisible
2157
+ to jsdom. */
2158
+ .aparte-tool-spinner[hidden] { display: none; }
2159
+ /* The call's identity: its icon and its name, and no decoration of its own.
2160
+ No border, no fill, no tint at any status — the row is the container now, and
2161
+ the colour lives on the state badge alone. A resolved call used to be filled
2162
+ green, which made a finished step shout louder than the reply it belongs to. */
2163
+ .aparte-tool-label {
2164
+ display: inline-flex;
2165
+ align-items: center;
2166
+ gap: 6px;
2167
+ min-width: 0;
2168
+ font-size: 0.8rem;
2169
+ font-weight: 500;
2170
+ color: var(--aparte-text, inherit);
2171
+ user-select: none;
2172
+ }
2173
+ /* A fixed square, and the glyph centred in it. The icon was baseline-aligned
2174
+ inside an inline box, so it sat low against the name and moved with whatever
2175
+ glyph the icon provider returned. */
2176
+ .aparte-tool-icon {
2177
+ display: grid;
2178
+ place-items: center;
2179
+ width: 1em; height: 1em;
2180
+ flex: none;
2181
+ opacity: 0.7;
2182
+ }
2183
+ .aparte-tool-icon svg { width: 1em; height: 1em; display: block; }
2184
+ .aparte-tool-name { font-family: var(--aparte-code-font-family, ui-monospace, monospace); }
2185
+ /* The state, as a word with its glyph. Colour lives HERE and nowhere else.
2186
+ Pushed to the far end by DOM order plus an auto inline-start margin, which is
2187
+ the placement rule this repo already settled on: no left/right in a name, so
2188
+ a right-to-left locale mirrors it for free. */
2189
+ .aparte-tool-state {
2190
+ display: inline-flex; align-items: center; gap: 4px;
2191
+ margin-inline-start: auto;
2192
+ font-size: 0.72rem; font-weight: 500;
2193
+ color: var(--aparte-text-muted, rgba(128,128,128,0.75));
2194
+ user-select: none;
2195
+ }
2196
+ .aparte-tool-state svg { width: 0.9em; height: 0.9em; display: block; }
2197
+ [data-status="resolved"] .aparte-tool-state { color: var(--aparte-success, rgb(21,128,61)); }
2198
+ [data-status="rejected"] .aparte-tool-state,
2199
+ [data-status="aborted"] .aparte-tool-state { color: var(--aparte-error, rgb(185,28,28)); }
2200
+ /* The Approve / Reject button rules were here. They moved with the buttons, to
2201
+ .aparte-approval-option in this same file - and what stayed behind was a
2202
+ leftover: a rule tinting .aparte-tool-label's border while a decision was pending,
2203
+ which stopped painting anything the moment the row redesign took that border
2204
+ off .aparte-tool-label. It reached for a variable that was never declared, and it
2205
+ survived because a template literal is not a stylesheet anyone greps. The
2206
+ state is named in words by .aparte-tool-state now, and the decision is at the
2207
+ composer. */
2208
+ .aparte-tool-spinner {
2209
+ width: 10px; height: 10px;
2210
+ border: 1.5px solid currentColor;
2211
+ border-top-color: transparent;
2212
+ border-radius: 50%;
2213
+ display: inline-block;
2214
+ animation: tool-spin 0.7s linear infinite;
2215
+ }
2216
+ .aparte-tool-state { font-size: 0.75rem; }
2217
+ @keyframes tool-spin { to { transform: rotate(360deg); } }
2218
+
2219
+ /* Artifact card */
2220
+ .aparte-segment-artifact-card {
2221
+ display: flex; flex-direction: column;
2222
+ margin: 8px 0;
2223
+ border: 1px solid var(--aparte-border, rgba(0,0,0,0.12));
2224
+ border-radius: 12px;
2225
+ background: var(--aparte-surface, #fff);
2226
+ overflow: hidden;
2227
+ font: inherit;
2228
+ color: var(--aparte-text, inherit);
2229
+ }
2230
+ .aparte-art-card__header {
2231
+ display: flex; align-items: center; justify-content: space-between;
2232
+ padding: 8px 10px;
2233
+ border-bottom: 1px solid var(--aparte-border, rgba(0,0,0,0.08));
2234
+ background: var(--aparte-surface-2, rgba(0,0,0,0.02));
2235
+ min-height: 36px;
2236
+ }
2237
+ .aparte-art-card__title-block { display: flex; align-items: center; gap: 8px; min-width: 0; }
2238
+ .aparte-art-card__kind {
2239
+ font-size: 0.7rem; font-weight: 600;
2240
+ text-transform: uppercase; letter-spacing: 0.04em;
2241
+ padding: 2px 6px; border-radius: 4px;
2242
+ background: var(--aparte-surface, #fff);
2243
+ border: 1px solid var(--aparte-border, rgba(0,0,0,0.1));
2244
+ color: var(--aparte-text-muted, rgba(0,0,0,0.6));
2245
+ }
2246
+ .aparte-art-card__title {
2247
+ font-size: 0.85rem; font-weight: 500;
2248
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
2249
+ }
2250
+ .aparte-art-card__pulse {
2251
+ width: 8px; height: 8px; border-radius: 50%;
2252
+ background: var(--aparte-accent, #3b82f6);
2253
+ animation: aparte-art-card-pulse 1.2s ease-in-out infinite;
2254
+ }
2255
+ @keyframes aparte-art-card-pulse {
2256
+ 0%, 100% { opacity: 0.35; transform: scale(0.85); }
2257
+ 50% { opacity: 1; transform: scale(1.1); }
2258
+ }
2259
+ .aparte-art-card__actions { display: flex; gap: 4px; }
2260
+ .aparte-art-card__btn {
2261
+ display: inline-flex; align-items: center; justify-content: center;
2262
+ width: 28px; height: 28px;
2263
+ background: transparent;
2264
+ border: 1px solid transparent;
2265
+ border-radius: 6px;
2266
+ color: var(--aparte-text-muted, rgba(0,0,0,0.55));
2267
+ cursor: pointer;
2268
+ transition: background 0.12s, border-color 0.12s, color 0.12s;
2269
+ }
2270
+ .aparte-art-card__btn:hover:not(:disabled) {
2271
+ background: var(--aparte-surface-hover, rgba(0,0,0,0.04));
2272
+ border-color: var(--aparte-border, rgba(0,0,0,0.1));
2273
+ color: var(--aparte-text, inherit);
2274
+ }
2275
+ .aparte-art-card__btn:disabled { opacity: 0.4; cursor: not-allowed; }
2276
+ .aparte-art-card__tabs {
2277
+ /* justify-content and the padding are DECLARED, not left to a default.
2278
+ Core is light DOM on purpose - no shadow root, no ::part(), any selector
2279
+ reaches in - and the corollary is that a component must state what its
2280
+ layout depends on, because an undeclared property has nothing to override
2281
+ a host rule with. A consuming page with a bare nav selector setting
2282
+ justify-content: space-between and padding-top (this library's own docs
2283
+ site had exactly that) otherwise pushes these two tabs to opposite ends of
2284
+ the card and pads the row out.
2285
+
2286
+ flex-end, and Code first in the DOM. The card OPENS on Code - mounting the
2287
+ preview would execute model-authored code with no gesture (ratified
2288
+ decision #8) - and a selected tab sitting second reads backwards. Right,
2289
+ because the header above puts the artifact's identity on the left and its
2290
+ copy/download buttons on the right, so this keeps every control in one
2291
+ column. DOM order is also keyboard order, so the tab a reader lands on
2292
+ first is the one already showing. */
2293
+ display: flex; justify-content: flex-end; align-items: stretch; gap: 2px;
2294
+ padding: 4px 8px 0;
2295
+ border-bottom: 1px solid var(--aparte-border, rgba(0,0,0,0.08));
2296
+ background: var(--aparte-surface-2, rgba(0,0,0,0.02));
2297
+ }
2298
+ .aparte-art-card__tabs button {
2299
+ padding: 6px 12px;
2300
+ font-size: 0.78rem;
2301
+ background: transparent;
2302
+ border: 1px solid transparent;
2303
+ border-bottom: none;
2304
+ border-radius: 6px 6px 0 0;
2305
+ color: var(--aparte-text-muted, rgba(0,0,0,0.6));
2306
+ cursor: pointer;
2307
+ transition: background 0.12s, color 0.12s;
2308
+ }
2309
+ .aparte-art-card__tabs button[aria-selected="true"] {
2310
+ background: var(--aparte-surface, #fff);
2311
+ border-color: var(--aparte-border, rgba(0,0,0,0.08));
2312
+ color: var(--aparte-text, inherit);
2313
+ font-weight: 500;
2314
+ }
2315
+ .aparte-art-card__tabs button:disabled { opacity: 0.4; cursor: not-allowed; }
2316
+ /* The card's heights, as variables with ONE owner each.
2317
+ They were four hardcoded numbers in the only part of this card that did not
2318
+ use a variable - everything else here already reads var(--aparte-code-bg) and
2319
+ friends - and two of them had to agree while a third contradicted a fourth:
2320
+ the code pane repeated the body's 600px, and the "press Preview" placeholder
2321
+ was 120px tall inside a body whose min-height said 80, so that minimum applied
2322
+ to nothing.
2323
+ The frame stays a FIXED height rather than an aspect ratio, which is what
2324
+ embeds of arbitrary HTML actually do - CodeSandbox documents 500px, StackBlitz
2325
+ takes a height parameter - because a frame with an opaque origin cannot be
2326
+ measured and a 16/10 ratio on a wide card is enormous. The vh cap is the part
2327
+ that was missing: a fixed 480px should not own a phone screen.
2328
+ Each default lives in its read, as var(--x, literal), the way every other
2329
+ value in this file already does - not in a declaration block on top of the
2330
+ fallbacks, which would be two owners of one number again. It also means the
2331
+ docs' CSS-variable generator finds them: its sweep looks for reads that
2332
+ carry a fallback, so a read without one is a public knob nobody documents. */
2333
+ .aparte-art-card__body {
2334
+ position: relative;
2335
+ /* The placeholder is the tallest thing this box can hold while empty, so it
2336
+ IS the minimum - one number instead of two that disagreed. */
2337
+ min-height: var(--aparte-artifact-pending-height, 120px);
2338
+ max-height: var(--aparte-artifact-body-max, 600px);
2339
+ overflow: hidden;
2016
2340
  }
2017
-
2018
- .progress-fill {
2019
- height: 100%;
2020
- background: var(--aparte-progress-bar-fill, #6366f1);
2021
- border-radius: var(--aparte-radius-xs);
2022
- transition: width 0.3s ease;
2341
+ .aparte-art-card__pane { display: none; height: 100%; }
2342
+ .aparte-segment-artifact-card[data-tab="code"] .aparte-art-card__pane[data-pane="code"] { display: block; }
2343
+ .aparte-segment-artifact-card[data-tab="preview"] .aparte-art-card__pane[data-pane="preview"] { display: block; }
2344
+ .aparte-art-card__pane[data-pane="code"] {
2345
+ /* The body already caps this; repeating the number was the second owner. */
2346
+ max-height: var(--aparte-artifact-body-max, 600px); overflow: auto;
2347
+ }
2348
+ .aparte-art-card__pane[data-pane="code"] pre {
2349
+ margin: 0; padding: 12px;
2350
+ font-size: 0.82rem;
2351
+ background: var(--aparte-code-bg, #fafafa);
2352
+ }
2353
+ .aparte-art-card__frame {
2354
+ display: block;
2355
+ width: 100%;
2356
+ height: min(var(--aparte-artifact-frame-height, 480px), var(--aparte-artifact-frame-max, 70vh));
2357
+ border: 0;
2358
+ background: #fff;
2359
+ }
2360
+ .aparte-art-card__pending {
2361
+ display: flex; align-items: center; justify-content: center;
2362
+ height: var(--aparte-artifact-pending-height, 120px);
2363
+ color: var(--aparte-text-muted, rgba(0,0,0,0.5));
2364
+ font-size: 0.85rem;
2365
+ font-style: italic;
2366
+ }
2367
+ /* ── Binary file artifact (xlsx/pdf/docx) ──────────────────── */
2368
+ .aparte-segment-artifact-file {
2369
+ display: flex; flex-direction: column;
2370
+ margin: 8px 0;
2371
+ border: 1px solid var(--aparte-border, rgba(0,0,0,0.12));
2372
+ border-radius: 12px;
2373
+ background: var(--aparte-surface, #fff);
2374
+ overflow: hidden;
2375
+ color: var(--aparte-text, inherit);
2376
+ font: inherit;
2377
+ }
2378
+ .aparte-art-file__card {
2379
+ display: flex; align-items: center; gap: 12px;
2380
+ padding: 12px 14px;
2381
+ background: var(--aparte-surface-2, rgba(0,0,0,0.02));
2382
+ border-bottom: 1px solid var(--aparte-border, rgba(0,0,0,0.08));
2383
+ }
2384
+ .aparte-art-file__body {
2385
+ position: relative;
2386
+ }
2387
+ .aparte-art-file__code-pane {
2388
+ max-height: var(--aparte-artifact-file-code-max, 360px); overflow: auto;
2389
+ background: var(--aparte-code-bg, #fafafa);
2390
+ }
2391
+ .aparte-art-file__code-pane pre {
2392
+ margin: 0; padding: 12px;
2393
+ font-size: 0.82rem;
2394
+ font-family: 'JetBrains Mono', 'Fira Code', Consolas, monospace;
2395
+ }
2396
+ .aparte-art-file__preview-pane {
2397
+ max-height: var(--aparte-artifact-file-preview-max, 460px);
2398
+ overflow: auto;
2399
+ background: #fff;
2400
+ /* Preview is a document view — force light scheme regardless of
2401
+ the app theme, with a dark text colour so cells stay readable. */
2402
+ color: #1f2937;
2403
+ }
2404
+ .aparte-art-file__icon {
2405
+ width: 40px; height: 40px;
2406
+ border-radius: 8px;
2407
+ display: flex; align-items: center; justify-content: center;
2408
+ background: linear-gradient(135deg, #1d6f42, #0f5132);
2409
+ color: #fff;
2410
+ font-weight: 700;
2411
+ font-size: 0.78rem;
2412
+ letter-spacing: 0.04em;
2413
+ flex-shrink: 0;
2414
+ }
2415
+ .aparte-art-file__icon[data-kind="pdf"] { background: linear-gradient(135deg, #c0392b, #7d1f17); }
2416
+ .aparte-art-file__icon[data-kind="docx"] { background: linear-gradient(135deg, #1e5288, #0f3060); }
2417
+ .aparte-art-file__meta { flex: 1 1 auto; min-width: 0; }
2418
+ .aparte-art-file__meta-name {
2419
+ font-weight: 600; font-size: 0.92rem;
2420
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
2421
+ }
2422
+ .aparte-art-file__meta-sub {
2423
+ font-size: 0.78rem;
2424
+ color: var(--aparte-text-muted, rgba(0,0,0,0.55));
2425
+ }
2426
+ .aparte-art-file__actions { display: flex; gap: 6px; flex-shrink: 0; }
2427
+ .aparte-art-file__btn {
2428
+ border: 1px solid var(--aparte-border, rgba(0,0,0,0.12));
2429
+ background: var(--aparte-surface, #fff);
2430
+ color: var(--aparte-text, inherit);
2431
+ padding: 6px 10px;
2432
+ border-radius: 6px;
2433
+ font-size: 0.8rem;
2434
+ cursor: pointer;
2435
+ transition: background 0.12s ease;
2436
+ }
2437
+ .aparte-art-file__btn:hover { background: var(--aparte-surface-2, rgba(0,0,0,0.05)); }
2438
+ .aparte-art-file__btn--primary {
2439
+ background: var(--aparte-primary, #6366f1);
2440
+ color: #fff;
2441
+ border-color: var(--aparte-primary, #6366f1);
2442
+ }
2443
+ .aparte-art-file__btn--primary:hover { filter: brightness(1.07); }
2444
+ /* Hide the download button until the sandbox has produced the buffer.
2445
+ Avoids confusing the user with a disabled-but-styled-primary button
2446
+ during streaming / compiling. */
2447
+ .aparte-segment-artifact-file:not([data-state="ready"]) .aparte-art-file__btn[data-action="download"] {
2448
+ display: none;
2449
+ }
2450
+ .aparte-art-file__preview-pane table {
2451
+ border-collapse: collapse;
2452
+ width: 100%;
2453
+ font-size: 0.82rem;
2454
+ font-family: inherit;
2455
+ }
2456
+ .aparte-art-file__preview-pane th,
2457
+ .aparte-art-file__preview-pane td {
2458
+ border: 1px solid rgba(0,0,0,0.10);
2459
+ padding: 6px 10px;
2460
+ text-align: left;
2461
+ white-space: nowrap;
2462
+ color: #1f2937;
2463
+ background: #fff;
2464
+ }
2465
+ .aparte-art-file__preview-pane tr:nth-child(odd) td { background: #f9fafb; }
2466
+ .aparte-art-file__preview-pane tr:first-child td {
2467
+ background: #f3f4f6;
2468
+ font-weight: 600;
2469
+ position: sticky; top: 0;
2470
+ color: #111827;
2471
+ }
2472
+ .aparte-art-file__preview-empty {
2473
+ padding: 20px; text-align: center;
2474
+ color: var(--aparte-text-muted, rgba(0,0,0,0.5));
2475
+ font-size: 0.85rem; font-style: italic;
2476
+ }
2477
+ .aparte-segment-artifact-file[data-state="error"] .aparte-art-file__icon {
2478
+ background: linear-gradient(135deg, #c0392b, #7d1f17);
2479
+ }
2480
+ .aparte-art-file__error {
2481
+ padding: 16px 18px;
2482
+ background: rgba(239, 68, 68, 0.05);
2483
+ border-top: 1px solid rgba(239, 68, 68, 0.2);
2484
+ color: var(--aparte-text, inherit);
2485
+ }
2486
+ .aparte-art-file__error-title {
2487
+ font-weight: 600;
2488
+ font-size: 0.9rem;
2489
+ margin-bottom: 6px;
2490
+ color: #b91c1c;
2491
+ }
2492
+ .aparte-art-file__error-msg {
2493
+ font-family: 'JetBrains Mono', 'Fira Code', Consolas, monospace;
2494
+ font-size: 0.78rem;
2495
+ padding: 6px 10px;
2496
+ background: rgba(0,0,0,0.04);
2497
+ border-radius: 4px;
2498
+ margin-bottom: 8px;
2499
+ color: #7f1d1d;
2500
+ word-break: break-word;
2501
+ }
2502
+ .aparte-art-file__error-hint {
2503
+ font-size: 0.78rem;
2504
+ color: var(--aparte-text-muted, rgba(0,0,0,0.55));
2505
+ font-style: italic;
2023
2506
  }
2024
2507
 
2508
+ /* Pipeline waiting */
2509
+ .aparte-segment-pipeline-waiting {
2510
+ display: flex;
2511
+ align-items: center;
2512
+ gap: 5px;
2513
+ padding: 6px 2px;
2514
+ min-height: 28px;
2515
+ }
2516
+ .aparte-pw-dot {
2517
+ display: inline-block;
2518
+ width: 8px;
2519
+ height: 8px;
2520
+ border-radius: 50%;
2521
+ background: var(--aparte-text-muted, #6b7280);
2522
+ opacity: 0.3;
2523
+ animation: pw-pulse 1.2s ease-in-out infinite;
2524
+ }
2525
+ .aparte-pw-dot:nth-child(2) { animation-delay: 0.2s; }
2526
+ .aparte-pw-dot:nth-child(3) { animation-delay: 0.4s; }
2527
+ @keyframes pw-pulse {
2528
+ 0%, 80%, 100% { opacity: 0.3; transform: scale(0.85); }
2529
+ 40% { opacity: 1; transform: scale(1.1); }
2530
+ }
2025
2531
 
2026
2532
  /* ─────────────────────────────────────────────────────────────────────────────
2027
2533
  Composer Primitives
@@ -2118,13 +2624,13 @@ aparte-composer-action {
2118
2624
  }
2119
2625
 
2120
2626
  /* ── Drag-over state on root ── */
2121
- aparte-composer.is-dragover .aparte-composer-shell {
2627
+ aparte-composer.aparte-is-dragover .aparte-composer-shell {
2122
2628
  outline: var(--aparte-focus-outline-width, 2px) dashed var(--aparte-primary);
2123
2629
  outline-offset: -2px;
2124
2630
  border-radius: var(--aparte-radius-input, 12px);
2125
2631
  }
2126
2632
  /* fallback: no shell */
2127
- aparte-composer.is-dragover:not(:has(.aparte-composer-shell)) {
2633
+ aparte-composer.aparte-is-dragover:not(:has(.aparte-composer-shell)) {
2128
2634
  outline: var(--aparte-focus-outline-width, 2px) dashed var(--aparte-primary);
2129
2635
  outline-offset: -2px;
2130
2636
  border-radius: var(--aparte-radius-input, 12px);
@@ -2186,6 +2692,23 @@ aparte-composer.is-dragover:not(:has(.aparte-composer-shell)) {
2186
2692
  justify-content: center;
2187
2693
  }
2188
2694
 
2695
+ /* A panel is mounted INSIDE that row, so its own buttons inherited the 44x44 sizing
2696
+ above and rendered as circles with their labels spilling out. The rule above means
2697
+ "the composer's own controls"; anything inside a panel is CONTENT, and a panel is
2698
+ free to contain a button — the approval options are the first that do, and they will
2699
+ not be the last. */
2700
+ [data-aparte-panel] button {
2701
+ width: auto;
2702
+ height: auto;
2703
+ flex-shrink: 1;
2704
+ /* The row also makes its controls centred flex boxes, which beats `text-align` — so a
2705
+ left-aligned label in a panel came out centred until this line existed. Undo the
2706
+ whole treatment, not the half of it that was visible. */
2707
+ display: block;
2708
+ justify-content: initial;
2709
+ align-items: initial;
2710
+ }
2711
+
2189
2712
  /* The composer's bottom row (mode picker, model selector, token counter…).
2190
2713
  `aparte-composer-toolbar` is the element consumers write — one name for vanilla and
2191
2714
  every wrapper. `.aparte-composer-footer` is the class the same row used to be built
@@ -2422,15 +2945,15 @@ aparte-conversation-list {
2422
2945
  Provider-HTML overflow guards
2423
2946
  ========================================= */
2424
2947
 
2425
- /* Markdown providers inject arbitrary HTML into .segment-text, and the bubble
2948
+ /* Markdown providers inject arbitrary HTML into .aparte-segment-text, and the bubble
2426
2949
  clips overflow (`aparte-chat-bubble { overflow: hidden }`) — without these
2427
2950
  guards a wide image or table is silently cut at the bubble edge. */
2428
- .segment-text img {
2951
+ .aparte-segment-text img {
2429
2952
  max-width: 100%;
2430
2953
  height: auto;
2431
2954
  }
2432
2955
 
2433
- .segment-text table {
2956
+ .aparte-segment-text table {
2434
2957
  display: block;
2435
2958
  max-width: 100%;
2436
2959
  overflow-x: auto;