@artooi/ag-ui-web-component 0.44.0 → 0.46.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 (187) hide show
  1. package/CHANGELOG.md +182 -1
  2. package/README.md +245 -32
  3. package/dist/ag-ui-web-component.bundle.js +3696 -2798
  4. package/dist/ag-ui-web-component.bundle.js.map +4 -4
  5. package/dist/constants.d.ts +4 -1
  6. package/dist/constants.d.ts.map +1 -1
  7. package/dist/core/ag_ui_chat.d.ts +28 -8
  8. package/dist/core/ag_ui_chat.d.ts.map +1 -1
  9. package/dist/core/read_flag.d.ts +11 -0
  10. package/dist/core/read_flag.d.ts.map +1 -0
  11. package/dist/core/read_json_attribute.d.ts +15 -0
  12. package/dist/core/read_json_attribute.d.ts.map +1 -0
  13. package/dist/index.js +5084 -3481
  14. package/dist/index.js.map +4 -4
  15. package/dist/tools/tool_catalog.d.ts +5 -0
  16. package/dist/tools/tool_catalog.d.ts.map +1 -1
  17. package/dist/tools/tool_dispatch.d.ts.map +1 -1
  18. package/dist/ui/charts/chart_styles.d.ts +2 -0
  19. package/dist/ui/charts/chart_styles.d.ts.map +1 -0
  20. package/dist/ui/composer/attachment_styles.d.ts +2 -0
  21. package/dist/ui/composer/attachment_styles.d.ts.map +1 -0
  22. package/dist/ui/composer/build_composer.d.ts +30 -0
  23. package/dist/ui/composer/build_composer.d.ts.map +1 -0
  24. package/dist/ui/composer/composer_styles.d.ts +2 -0
  25. package/dist/ui/composer/composer_styles.d.ts.map +1 -0
  26. package/dist/ui/composer/skill_styles.d.ts +2 -0
  27. package/dist/ui/composer/skill_styles.d.ts.map +1 -0
  28. package/dist/ui/history/history_styles.d.ts +2 -0
  29. package/dist/ui/history/history_styles.d.ts.map +1 -0
  30. package/dist/ui/interrupts/approval_styles.d.ts +2 -0
  31. package/dist/ui/interrupts/approval_styles.d.ts.map +1 -0
  32. package/dist/ui/interrupts/confirmation_styles.d.ts +2 -0
  33. package/dist/ui/interrupts/confirmation_styles.d.ts.map +1 -0
  34. package/dist/ui/interrupts/question_styles.d.ts +2 -0
  35. package/dist/ui/interrupts/question_styles.d.ts.map +1 -0
  36. package/dist/ui/placement/collapse_styles.d.ts +2 -0
  37. package/dist/ui/placement/collapse_styles.d.ts.map +1 -0
  38. package/dist/ui/placement/embedded_styles.d.ts +2 -0
  39. package/dist/ui/placement/embedded_styles.d.ts.map +1 -0
  40. package/dist/ui/placement/launcher_styles.d.ts +2 -0
  41. package/dist/ui/placement/launcher_styles.d.ts.map +1 -0
  42. package/dist/ui/placement/page_styles.d.ts +2 -0
  43. package/dist/ui/placement/page_styles.d.ts.map +1 -0
  44. package/dist/ui/placement/panel_placement.d.ts.map +1 -1
  45. package/dist/ui/placement/preset_styles.d.ts +2 -0
  46. package/dist/ui/placement/preset_styles.d.ts.map +1 -0
  47. package/dist/ui/placement/resize_styles.d.ts +2 -0
  48. package/dist/ui/placement/resize_styles.d.ts.map +1 -0
  49. package/dist/ui/placement/sidebar_styles.d.ts +2 -0
  50. package/dist/ui/placement/sidebar_styles.d.ts.map +1 -0
  51. package/dist/ui/placement/small_viewport_styles.d.ts +2 -0
  52. package/dist/ui/placement/small_viewport_styles.d.ts.map +1 -0
  53. package/dist/ui/placement/surface_for.d.ts +74 -0
  54. package/dist/ui/placement/surface_for.d.ts.map +1 -0
  55. package/dist/ui/progress/run_notice_styles.d.ts +2 -0
  56. package/dist/ui/progress/run_notice_styles.d.ts.map +1 -0
  57. package/dist/ui/progress/thoughts_styles.d.ts +2 -0
  58. package/dist/ui/progress/thoughts_styles.d.ts.map +1 -0
  59. package/dist/ui/progress/tool_call_card.d.ts +8 -0
  60. package/dist/ui/progress/tool_call_card.d.ts.map +1 -1
  61. package/dist/ui/progress/tool_group_styles.d.ts +2 -0
  62. package/dist/ui/progress/tool_group_styles.d.ts.map +1 -0
  63. package/dist/ui/progress/tool_step_group.d.ts +47 -0
  64. package/dist/ui/progress/tool_step_group.d.ts.map +1 -0
  65. package/dist/ui/progress/tool_step_styles.d.ts +2 -0
  66. package/dist/ui/progress/tool_step_styles.d.ts.map +1 -0
  67. package/dist/ui/shell/adopt_styles.d.ts +9 -4
  68. package/dist/ui/shell/adopt_styles.d.ts.map +1 -1
  69. package/dist/ui/shell/build_header.d.ts +40 -0
  70. package/dist/ui/shell/build_header.d.ts.map +1 -0
  71. package/dist/ui/shell/build_launcher.d.ts +20 -0
  72. package/dist/ui/shell/build_launcher.d.ts.map +1 -0
  73. package/dist/ui/shell/frame_styles.d.ts +2 -0
  74. package/dist/ui/shell/frame_styles.d.ts.map +1 -0
  75. package/dist/ui/shell/theme_styles.d.ts +2 -0
  76. package/dist/ui/shell/theme_styles.d.ts.map +1 -0
  77. package/dist/ui/shell/token_styles.d.ts +2 -0
  78. package/dist/ui/shell/token_styles.d.ts.map +1 -0
  79. package/dist/ui/styles.d.ts +1 -1
  80. package/dist/ui/styles.d.ts.map +1 -1
  81. package/dist/ui/themes/classic_styles.d.ts +2 -0
  82. package/dist/ui/themes/classic_styles.d.ts.map +1 -0
  83. package/dist/ui/themes/classic_tokens.d.ts +2 -0
  84. package/dist/ui/themes/classic_tokens.d.ts.map +1 -0
  85. package/dist/ui/themes/code_tokens.d.ts +2 -0
  86. package/dist/ui/themes/code_tokens.d.ts.map +1 -0
  87. package/dist/ui/themes/ledger_styles.d.ts +2 -0
  88. package/dist/ui/themes/ledger_styles.d.ts.map +1 -0
  89. package/dist/ui/themes/ledger_tokens.d.ts +2 -0
  90. package/dist/ui/themes/ledger_tokens.d.ts.map +1 -0
  91. package/dist/ui/themes/modern_styles.d.ts +2 -0
  92. package/dist/ui/themes/modern_styles.d.ts.map +1 -0
  93. package/dist/ui/themes/quiet_styles.d.ts +2 -0
  94. package/dist/ui/themes/quiet_styles.d.ts.map +1 -0
  95. package/dist/ui/themes/scheme_styles.d.ts +2 -0
  96. package/dist/ui/themes/scheme_styles.d.ts.map +1 -0
  97. package/dist/ui/themes/soft_styles.d.ts +2 -0
  98. package/dist/ui/themes/soft_styles.d.ts.map +1 -0
  99. package/dist/ui/themes/soft_tokens.d.ts +2 -0
  100. package/dist/ui/themes/soft_tokens.d.ts.map +1 -0
  101. package/dist/ui/themes/theme_hosts.d.ts +15 -0
  102. package/dist/ui/themes/theme_hosts.d.ts.map +1 -0
  103. package/dist/ui/transcript/attachment_chip_styles.d.ts +2 -0
  104. package/dist/ui/transcript/attachment_chip_styles.d.ts.map +1 -0
  105. package/dist/ui/transcript/build_transcript.d.ts +21 -0
  106. package/dist/ui/transcript/build_transcript.d.ts.map +1 -0
  107. package/dist/ui/transcript/greeting_styles.d.ts +2 -0
  108. package/dist/ui/transcript/greeting_styles.d.ts.map +1 -0
  109. package/dist/ui/transcript/groups_tool_steps.d.ts +13 -0
  110. package/dist/ui/transcript/groups_tool_steps.d.ts.map +1 -0
  111. package/dist/ui/transcript/message_action_styles.d.ts +2 -0
  112. package/dist/ui/transcript/message_action_styles.d.ts.map +1 -0
  113. package/dist/ui/transcript/message_styles.d.ts +2 -0
  114. package/dist/ui/transcript/message_styles.d.ts.map +1 -0
  115. package/dist/ui/transcript/transcript.d.ts +16 -1
  116. package/dist/ui/transcript/transcript.d.ts.map +1 -1
  117. package/dist/ui/transcript/transcript_styles.d.ts +2 -0
  118. package/dist/ui/transcript/transcript_styles.d.ts.map +1 -0
  119. package/dist/ui/ui_strings.d.ts +18 -0
  120. package/dist/ui/ui_strings.d.ts.map +1 -1
  121. package/package.json +2 -2
  122. package/src/constants.ts +4 -1
  123. package/src/core/ag_ui_chat.ts +164 -299
  124. package/src/core/read_flag.ts +13 -0
  125. package/src/core/read_json_attribute.ts +30 -0
  126. package/src/tools/tool_catalog.ts +23 -10
  127. package/src/tools/tool_dispatch.ts +26 -11
  128. package/src/ui/charts/chart_block.ts +9 -9
  129. package/src/ui/charts/chart_styles.ts +63 -0
  130. package/src/ui/composer/attachment_styles.ts +71 -0
  131. package/src/ui/composer/build_composer.ts +112 -0
  132. package/src/ui/composer/composer_styles.ts +174 -0
  133. package/src/ui/composer/skill_styles.ts +120 -0
  134. package/src/ui/history/history_styles.ts +484 -0
  135. package/src/ui/interrupts/approval_styles.ts +52 -0
  136. package/src/ui/interrupts/confirmation_styles.ts +114 -0
  137. package/src/ui/interrupts/question_styles.ts +73 -0
  138. package/src/ui/placement/collapse_styles.ts +137 -0
  139. package/src/ui/placement/embedded_styles.ts +14 -0
  140. package/src/ui/placement/launcher_styles.ts +189 -0
  141. package/src/ui/placement/page_styles.ts +47 -0
  142. package/src/ui/placement/panel_placement.ts +3 -12
  143. package/src/ui/placement/preset_styles.ts +33 -0
  144. package/src/ui/placement/resize_styles.ts +186 -0
  145. package/src/ui/placement/sidebar_styles.ts +88 -0
  146. package/src/ui/placement/small_viewport_styles.ts +56 -0
  147. package/src/ui/placement/surface_for.ts +135 -0
  148. package/src/ui/progress/run_notice_styles.ts +52 -0
  149. package/src/ui/progress/thoughts_styles.ts +65 -0
  150. package/src/ui/progress/tool_call_card.ts +11 -0
  151. package/src/ui/progress/tool_group_styles.ts +232 -0
  152. package/src/ui/progress/tool_step_group.ts +269 -0
  153. package/src/ui/progress/tool_step_styles.ts +472 -0
  154. package/src/ui/shell/adopt_styles.ts +11 -5
  155. package/src/ui/shell/build_header.ts +108 -0
  156. package/src/ui/shell/build_launcher.ts +50 -0
  157. package/src/ui/shell/frame_styles.ts +119 -0
  158. package/src/ui/shell/theme_styles.ts +11 -0
  159. package/src/ui/shell/token_styles.ts +406 -0
  160. package/src/ui/styles.ts +104 -3774
  161. package/src/ui/themes/classic_styles.ts +32 -0
  162. package/src/ui/themes/classic_tokens.ts +50 -0
  163. package/src/ui/themes/code_tokens.ts +21 -0
  164. package/src/ui/themes/ledger_styles.ts +168 -0
  165. package/src/ui/themes/ledger_tokens.ts +32 -0
  166. package/src/ui/themes/modern_styles.ts +80 -0
  167. package/src/ui/themes/quiet_styles.ts +115 -0
  168. package/src/ui/themes/scheme_styles.ts +17 -0
  169. package/src/ui/themes/soft_styles.ts +148 -0
  170. package/src/ui/themes/soft_tokens.ts +28 -0
  171. package/src/ui/themes/theme_hosts.ts +14 -0
  172. package/src/ui/transcript/attachment_chip_styles.ts +116 -0
  173. package/src/ui/transcript/build_transcript.ts +67 -0
  174. package/src/ui/transcript/greeting_styles.ts +171 -0
  175. package/src/ui/transcript/groups_tool_steps.ts +16 -0
  176. package/src/ui/transcript/message_action_styles.ts +155 -0
  177. package/src/ui/transcript/message_styles.ts +262 -0
  178. package/src/ui/transcript/transcript.ts +109 -3
  179. package/src/ui/transcript/transcript_styles.ts +140 -0
  180. package/src/ui/ui_strings.ts +31 -0
  181. package/src/version.ts +1 -1
  182. package/dist/ui/placement/is_collapsible_placement.d.ts +0 -18
  183. package/dist/ui/placement/is_collapsible_placement.d.ts.map +0 -1
  184. package/dist/ui/placement/is_draggable_placement.d.ts +0 -9
  185. package/dist/ui/placement/is_draggable_placement.d.ts.map +0 -1
  186. package/src/ui/placement/is_collapsible_placement.ts +0 -19
  187. package/src/ui/placement/is_draggable_placement.ts +0 -23
@@ -0,0 +1,186 @@
1
+ // The resize grips on every edge and corner, and which placements keep them.
2
+ export const RESIZE_STYLES: string = `
3
+ /* Resize grips: every edge and every corner, so the panel can be dragged from
4
+ whichever side the reader is already near. Absent entirely where the
5
+ placement is full-bleed, since there is nothing to drag.
6
+
7
+ The whole set is always laid out. Which edges the *layout* pins is not a
8
+ question the stylesheet needs to answer any more -- it decided where the one
9
+ grip went, and there is no longer one grip. The element still measures and
10
+ stamps data-resize-anchor, because it decides what a drag on a pinned edge
11
+ costs in position and which grip carries the tab stop, but no rule here
12
+ reads it. */
13
+ .resize-handle {
14
+ position: absolute;
15
+ z-index: 2;
16
+ background: transparent;
17
+ touch-action: none;
18
+ }
19
+
20
+ .resize-handle:focus-visible {
21
+ outline: 2px solid var(--_accent);
22
+ outline-offset: -2px;
23
+ }
24
+
25
+ /* Corners first in the file and last in the DOM, so they take the pointer
26
+ where they overlap an edge strip. */
27
+ .resize-handle--top-left,
28
+ .resize-handle--top-right,
29
+ .resize-handle--bottom-left,
30
+ .resize-handle--bottom-right {
31
+ width: var(--_grip-corner);
32
+ height: var(--_grip-corner);
33
+ }
34
+
35
+ .resize-handle--top-left {
36
+ top: 0;
37
+ left: 0;
38
+ cursor: nwse-resize;
39
+ }
40
+
41
+ .resize-handle--top-right {
42
+ top: 0;
43
+ right: 0;
44
+ cursor: nesw-resize;
45
+ }
46
+
47
+ .resize-handle--bottom-left {
48
+ bottom: 0;
49
+ left: 0;
50
+ cursor: nesw-resize;
51
+ }
52
+
53
+ .resize-handle--bottom-right {
54
+ bottom: 0;
55
+ right: 0;
56
+ cursor: nwse-resize;
57
+ }
58
+
59
+ /* Edge strips, held clear of the corners at both ends so a corner drag is
60
+ never swallowed by the edge next to it. */
61
+ .resize-handle--left,
62
+ .resize-handle--right {
63
+ top: var(--_grip-corner);
64
+ bottom: var(--_grip-corner);
65
+ width: var(--_grip-edge);
66
+ cursor: ew-resize;
67
+ }
68
+
69
+ .resize-handle--left {
70
+ left: 0;
71
+ }
72
+
73
+ .resize-handle--right {
74
+ right: 0;
75
+ }
76
+
77
+ .resize-handle--top,
78
+ .resize-handle--bottom {
79
+ left: var(--_grip-corner);
80
+ right: var(--_grip-corner);
81
+ height: var(--_grip-edge);
82
+ cursor: ns-resize;
83
+ }
84
+
85
+ .resize-handle--top {
86
+ top: 0;
87
+ }
88
+
89
+ .resize-handle--bottom {
90
+ bottom: 0;
91
+ }
92
+
93
+ /* Docked: the placement owns the height, so the horizontal edges and every
94
+ corner are inert and must not advertise a drag that does nothing. The two
95
+ vertical edges remain, which is the same affordance these placements had
96
+ when there was one grip -- now on both sides, since either may be the inner
97
+ one depending on which side the rail is docked to. */
98
+ :host([placement="sidebar"]) .resize-handle--top,
99
+ :host([placement="sidebar"]) .resize-handle--bottom,
100
+ :host([placement="sidebar"]) .resize-handle--top-left,
101
+ :host([placement="sidebar"]) .resize-handle--top-right,
102
+ :host([placement="sidebar"]) .resize-handle--bottom-left,
103
+ :host([placement="sidebar"]) .resize-handle--bottom-right,
104
+ :host([placement="side"]) .resize-handle--top,
105
+ :host([placement="side"]) .resize-handle--bottom,
106
+ :host([placement="side"]) .resize-handle--top-left,
107
+ :host([placement="side"]) .resize-handle--top-right,
108
+ :host([placement="side"]) .resize-handle--bottom-left,
109
+ :host([placement="side"]) .resize-handle--bottom-right {
110
+ display: none;
111
+ }
112
+
113
+ /* The vertical edges run the full height once no corner shares them. */
114
+ :host([placement="sidebar"]) .resize-handle--left,
115
+ :host([placement="sidebar"]) .resize-handle--right,
116
+ :host([placement="side"]) .resize-handle--left,
117
+ :host([placement="side"]) .resize-handle--right {
118
+ top: 0;
119
+ bottom: 0;
120
+ width: var(--_grip-edge-docked);
121
+ }
122
+
123
+ /* Full-bleed: nothing to drag. */
124
+ :host([placement="full"]) .resize-handle,
125
+ :host([placement="page"]) .resize-handle {
126
+ display: none;
127
+ }
128
+
129
+ /* The visible mark on a grip.
130
+
131
+ Filling the whole strip was what the single corner grip did, and at 14px
132
+ square nobody ever saw it. On a strip running the length of an edge the same
133
+ fill reads as a border the panel grew -- square ended, stopping short of the
134
+ corner radius at both ends, and easily mistaken for a rendering fault rather
135
+ than for something to grab.
136
+
137
+ So the hit area stays the full strip and the mark is a short pill centred on
138
+ it, which cannot be read as an edge of anything and never meets the radius.
139
+
140
+ It shows on hover and focus as well as during the drag. With eight grips, a
141
+ mark that appears only once you are already dragging is a mark that never
142
+ told anyone the grips were there. */
143
+ .resize-handle::after {
144
+ content: "";
145
+ position: absolute;
146
+ top: 50%;
147
+ left: 50%;
148
+ transform: translate(-50%, -50%);
149
+ border-radius: 999px;
150
+ background: var(--_accent);
151
+ opacity: 0;
152
+ transition: opacity var(--_motion) var(--_ease);
153
+ }
154
+
155
+ .resize-handle--top::after,
156
+ .resize-handle--bottom::after {
157
+ width: var(--_grip-mark-length);
158
+ height: var(--_grip-mark-thickness);
159
+ }
160
+
161
+ .resize-handle--left::after,
162
+ .resize-handle--right::after {
163
+ width: var(--_grip-mark-thickness);
164
+ height: var(--_grip-mark-length);
165
+ }
166
+
167
+ /* A corner has no length to run along, so it gets a dot instead of a pill. */
168
+ .resize-handle--top-left::after,
169
+ .resize-handle--top-right::after,
170
+ .resize-handle--bottom-left::after,
171
+ .resize-handle--bottom-right::after {
172
+ width: var(--_grip-mark-thickness);
173
+ height: var(--_grip-mark-thickness);
174
+ }
175
+
176
+ .resize-handle:hover::after,
177
+ .resize-handle:focus-visible::after {
178
+ opacity: 0.5;
179
+ }
180
+
181
+ /* Equal specificity to the pair above, so source order is what makes the drag
182
+ the stronger of the two states. */
183
+ .resize-handle[data-dragging]::after {
184
+ opacity: 0.9;
185
+ }
186
+ `;
@@ -0,0 +1,88 @@
1
+ // The sidebar placement: a docked full-height panel that slides to a rail.
2
+ export const SIDEBAR_STYLES: string = `
3
+ /* Sidebar: a full-height docked panel that slides open/closed and
4
+ collapses to a slim icon rail (not the floating launcher). Docked right by
5
+ default; data-side="left" docks it left. Overlay by default — set
6
+ --ag-ui-position: static (and place this element in your own layout) for a
7
+ host-managed push instead. */
8
+ :host([placement="sidebar"]) {
9
+ --_inset: var(--ag-ui-inset, calc(var(--_viewport-inset-top) + var(--_keyboard-inset-top)) var(--_viewport-inset-right) var(--_viewport-inset-bottom) auto);
10
+ --_width: var(--ag-ui-width, 420px);
11
+ --_height: var(--ag-ui-height, var(--_viewport-height));
12
+ --_max-height: var(--ag-ui-max-height, var(--_viewport-height));
13
+ --_panel-radius: var(--ag-ui-panel-radius, 0);
14
+ transition: width var(--_motion) var(--_ease);
15
+ }
16
+
17
+ :host([placement="sidebar"][data-side="left"]) {
18
+ --_inset: var(
19
+ --ag-ui-inset,
20
+ calc(var(--_viewport-inset-top) + var(--_keyboard-inset-top)) auto var(--_viewport-inset-bottom)
21
+ var(--_viewport-inset-left)
22
+ );
23
+ }
24
+
25
+ /* The docked panel is pinned to the edge it docks against rather than filling
26
+ the host as a flex child. Collapsing shrinks the host to the rail width, and
27
+ a flex child would be squashed to 52px on the way out instead of sliding out
28
+ at full width. */
29
+ /* The panel is taken out of flow so the collapse can slide it out at full
30
+ width; see the note above. That makes the host its containing block, and the
31
+ host is only one by accident: it is position: fixed by default. A host that
32
+ takes the documented route to a pushed layout instead of an overlay sets
33
+ --ag-ui-position: static, and a static element establishes nothing -- so the
34
+ panel resolved against the initial containing block, landed at the document
35
+ origin, and scrolled away with the page. Docked left it pinned to the
36
+ document's left edge rather than the host's.
37
+
38
+ It looked correct wherever it was first tried, because a full-height column
39
+ at the top of an unscrolled document is exactly where those two answers
40
+ coincide. Containment makes the host a containing block whatever its
41
+ position, so the panel stays in the box the host was given. It is a no-op in
42
+ the default case, which is already positioned. */
43
+ :host([placement="sidebar"]) {
44
+ contain: layout;
45
+ }
46
+
47
+ :host([placement="sidebar"]) .chat {
48
+ position: absolute;
49
+ inset: 0 0 0 auto;
50
+ width: var(--_width);
51
+ transform-origin: right center;
52
+ }
53
+
54
+ :host([placement="sidebar"][data-side="left"]) .chat {
55
+ inset: 0 auto 0 0;
56
+ transform-origin: left center;
57
+ }
58
+
59
+ /* Collapsed sidebar: shrink the host to the rail width and slide the panel out
60
+ through the edge it docks against. Higher specificity than the generic
61
+ collapse rules, so it wins regardless of source order. */
62
+ /* The rail is the full height of what the host left, not of the screen. It
63
+ said 100vh and pinned its own bottom, which put it under any chrome the host
64
+ had reserved -- and the icon lives at the top of the rail, so the one control
65
+ that reopens the panel was the first thing to disappear behind a sticky
66
+ header. */
67
+ :host([placement="sidebar"][collapsed]) {
68
+ width: var(--_rail-width);
69
+ height: var(--_viewport-height);
70
+ max-height: var(--_viewport-height);
71
+ pointer-events: auto;
72
+ }
73
+
74
+ :host([placement="sidebar"][collapsed]) .chat {
75
+ transform: translateX(100%);
76
+ }
77
+
78
+ :host([placement="sidebar"][data-side="left"][collapsed]) .chat {
79
+ transform: translateX(-100%);
80
+ }
81
+
82
+ @media (prefers-reduced-motion: reduce) {
83
+ :host([placement="sidebar"]),
84
+ :host([placement="sidebar"]) .chat {
85
+ transition: none;
86
+ }
87
+ }
88
+ `;
@@ -0,0 +1,56 @@
1
+ // The one shape every placement takes on a phone-sized viewport.
2
+ export const SMALL_VIEWPORT_STYLES: string = `
3
+ /* Small viewports: one shape, reached from whichever placement the host chose.
4
+
5
+ A phone is not an eighth placement, it is an override that collapses the
6
+ others onto one of them. The host picked a placement for the desktop it was
7
+ designing; a 380x560 panel with a 24px margin is not a smaller version of
8
+ that decision, it is most of the screen with a frame drawn round it.
9
+
10
+ The breakpoint is a literal because a custom property cannot be read in a
11
+ media query -- which is exactly why there is an opt-out. Everything the block
12
+ sets is a token a host can re-state, but the *trigger* is unreachable, so a
13
+ host whose layout wants a different threshold, or none, sets
14
+ data-small-viewport="off" and keeps its desktop shape at every width. 600px is above every common phone in portrait and below every
15
+ tablet in landscape, and it is a width rather than a pointer test on purpose:
16
+ a touch laptop is coarse-pointered and wide, a narrow desktop window is
17
+ fine-pointered and small, and conflating the two gets both wrong. What the
18
+ pointer decides is which controls make sense, further down.
19
+
20
+ Two placements are left alone. "page" is already this shape. "embedded" sits
21
+ in a box the host sized and placed, and taking that over would break the app
22
+ shell it was embedded into -- the host is the only party that knows whether
23
+ its column should become the whole screen. */
24
+ @media (max-width: 600px) {
25
+ :host([placement="floating"]:not([data-small-viewport="off"])),
26
+ :host([placement="bottom-left"]:not([data-small-viewport="off"])),
27
+ :host([placement="sidebar"]:not([data-small-viewport="off"])),
28
+ :host([placement="side"]:not([data-small-viewport="off"])),
29
+ :host(:not([placement]):not([data-small-viewport="off"])),
30
+ :host([placement=""]:not([data-small-viewport="off"])) {
31
+ --_inset: var(
32
+ --ag-ui-inset,
33
+ calc(var(--_viewport-inset-top) + var(--_keyboard-inset-top)) var(--_viewport-inset-right)
34
+ calc(var(--_viewport-inset-bottom) + var(--_keyboard-inset))
35
+ var(--_viewport-inset-left)
36
+ );
37
+ --_width: var(--ag-ui-width, var(--_viewport-width));
38
+ --_height: var(--ag-ui-height, var(--_viewport-height));
39
+ --_max-width: var(--ag-ui-max-width, var(--_viewport-width));
40
+ --_max-height: var(--ag-ui-max-height, var(--_viewport-height));
41
+ --_panel-radius: var(--ag-ui-panel-radius, 0);
42
+ --_shadow: var(--ag-ui-shadow, none);
43
+ }
44
+
45
+ /* Nothing to resize once the panel is the screen, and the grips would sit
46
+ under the thumbs holding the phone. */
47
+ :host([placement="floating"]:not([data-small-viewport="off"])) .resize-handle,
48
+ :host([placement="bottom-left"]:not([data-small-viewport="off"])) .resize-handle,
49
+ :host([placement="sidebar"]:not([data-small-viewport="off"])) .resize-handle,
50
+ :host([placement="side"]:not([data-small-viewport="off"])) .resize-handle,
51
+ :host(:not([placement]):not([data-small-viewport="off"])) .resize-handle,
52
+ :host([placement=""]:not([data-small-viewport="off"])) .resize-handle {
53
+ display: none;
54
+ }
55
+ }
56
+ `;
@@ -0,0 +1,135 @@
1
+ import type { ResizeAxis } from "./resize_handle.js";
2
+
3
+ /**
4
+ * What a collapsed panel shrinks to.
5
+ *
6
+ * Only `null` versus not-`null` is read today: every caller asks whether the
7
+ * surface can collapse at all, never which target it collapses to. The three
8
+ * names below therefore describe the surface rather than drive it, and nothing
9
+ * relies on them (changing one fails only the test of this record). The
10
+ * bottom-bar collapse is expected to read or replace them, and a reader that
11
+ * does should come with a test outside this record's own.
12
+ *
13
+ * - `launcher` -- the round button in a corner.
14
+ * - `rail` -- the slim full-height edge the sidebar docks against; the same
15
+ * launcher element, shaped by the stylesheet.
16
+ * - `header` -- the in-flow collapse: the body hides and the header bar stays
17
+ * in the host's layout, so there is no launcher at all.
18
+ * - `null` -- no collapsed state. `page` is a dedicated route rather than a
19
+ * panel on someone else's page, so there is no "away" for it to go to:
20
+ * collapsing it left a strip of application chrome fixed over a route with
21
+ * no owner, and it hides the launcher, so the usual way back did not exist.
22
+ * The header drops its collapse control here, but a control removed from
23
+ * the UI is not a state removed from the model: the property, the attribute
24
+ * and a value restored from storage all still reach it, so every one of
25
+ * those paths asks this record; the stylesheet covers the one path that
26
+ * never does, an attribute written straight onto the element.
27
+ */
28
+ export type CollapseTarget = "launcher" | "rail" | "header" | null;
29
+
30
+ /**
31
+ * Everything the script needs to know about one display surface, in one
32
+ * record, so a placement's behaviour is read in one place rather than
33
+ * assembled from a table per question.
34
+ *
35
+ * The stylesheet still carries each surface's look (one module per placement
36
+ * under this directory); this is the part of a surface that code decides.
37
+ */
38
+ export interface Surface {
39
+ /**
40
+ * What collapsing shrinks the panel to; `null` when it cannot collapse. Only
41
+ * the `null` test is authoritative: the non-null names are descriptive (see
42
+ * {@link CollapseTarget}), and nothing branches on which one a surface has.
43
+ */
44
+ readonly collapseTo: CollapseTarget;
45
+ /**
46
+ * Whether a gesture may move the launcher. The rest have nowhere to put it:
47
+ * the rail is a docked edge, `header` and `null` have no launcher, and a
48
+ * full-bleed panel covers the screen it would be opening into.
49
+ */
50
+ readonly launcherDraggable: boolean;
51
+ /**
52
+ * Which axes the resize grips may change. A full-bleed layout is the whole
53
+ * viewport by definition and cannot be resized; a docked panel owns its
54
+ * height, leaving only its inner edge.
55
+ */
56
+ readonly resizeAxis: ResizeAxis;
57
+ /**
58
+ * Whether a first mount with nothing stored starts collapsed. Only the
59
+ * corner placements do: a launcher is the resting state of every corner chat
60
+ * in the field, and mounting open put a 380x560 panel over the host page's
61
+ * corner on a visitor's first load, uninvited. The placements that place
62
+ * themselves start open -- a host that docks a sidebar has already decided
63
+ * the widget belongs on screen, and one that embeds it has given it a box.
64
+ *
65
+ * Separate from {@link launcherDraggable} although the two agree today:
66
+ * "has a launcher to rest on" and "the launcher can be moved" are different
67
+ * questions, and a surface with a fixed launcher would answer them apart.
68
+ */
69
+ readonly startsCollapsed: boolean;
70
+ }
71
+
72
+ const CORNER: Surface = {
73
+ collapseTo: "launcher",
74
+ launcherDraggable: true,
75
+ resizeAxis: "both",
76
+ startsCollapsed: true,
77
+ };
78
+
79
+ const DOCKED: Surface = {
80
+ collapseTo: "launcher",
81
+ launcherDraggable: false,
82
+ resizeAxis: "width",
83
+ startsCollapsed: false,
84
+ };
85
+
86
+ /**
87
+ * One record per `placement` value. Absent (`null`) and empty are the default
88
+ * floating corner, as the stylesheet treats them.
89
+ */
90
+ const SURFACES: ReadonlyMap<string | null, Surface> = new Map<string | null, Surface>([
91
+ [null, CORNER],
92
+ ["", CORNER],
93
+ ["floating", CORNER],
94
+ ["bottom-left", CORNER],
95
+ ["side", DOCKED],
96
+ ["sidebar", { ...DOCKED, collapseTo: "rail" }],
97
+ [
98
+ "full",
99
+ {
100
+ collapseTo: "launcher",
101
+ launcherDraggable: false,
102
+ resizeAxis: "none",
103
+ startsCollapsed: false,
104
+ },
105
+ ],
106
+ [
107
+ "page",
108
+ { collapseTo: null, launcherDraggable: false, resizeAxis: "none", startsCollapsed: false },
109
+ ],
110
+ [
111
+ "embedded",
112
+ { collapseTo: "header", launcherDraggable: false, resizeAxis: "both", startsCollapsed: false },
113
+ ],
114
+ ]);
115
+
116
+ /**
117
+ * A value no record names. The stylesheet lays it out as the default corner,
118
+ * but the script has never treated an unknown value as one: its launcher stays
119
+ * put and it mounts open, so a typo in a host's markup does not start moving
120
+ * or hiding a panel the host meant to show.
121
+ */
122
+ const UNKNOWN: Surface = {
123
+ collapseTo: "launcher",
124
+ launcherDraggable: false,
125
+ resizeAxis: "both",
126
+ startsCollapsed: false,
127
+ };
128
+
129
+ /**
130
+ * The surface `placement` describes. Read per interaction by every caller,
131
+ * because `placement` is a live attribute.
132
+ */
133
+ export function surfaceFor(placement: string | null): Surface {
134
+ return SURFACES.get(placement) ?? UNKNOWN;
135
+ }
@@ -0,0 +1,52 @@
1
+ // The one-line notice a run leaves in the transcript, with its undo.
2
+ export const RUN_NOTICE_STYLES: string = `
3
+ .run-notice {
4
+ display: inline-flex;
5
+ align-items: center;
6
+ gap: 6px;
7
+ align-self: flex-start;
8
+ max-width: 100%;
9
+ margin: 2px 0;
10
+ padding: 3px 10px;
11
+ border: 1px dashed var(--_border);
12
+ border-radius: 999px;
13
+ background: transparent;
14
+ color: var(--_muted);
15
+ font-size: 0.8em;
16
+ line-height: 1.4;
17
+ }
18
+
19
+ .run-notice-icon {
20
+ flex: none;
21
+ opacity: 0.75;
22
+ }
23
+
24
+ /* The one control a notice may carry. Quiet, because it reports something
25
+ already done rather than asking for a decision. */
26
+ .run-notice-undo {
27
+ flex: 0 0 auto;
28
+ margin-inline-start: auto;
29
+ padding: 2px 8px;
30
+ border: 1px solid var(--_border);
31
+ border-radius: var(--_radius);
32
+ background: var(--_bg);
33
+ color: var(--_accent);
34
+ font: inherit;
35
+ font-size: 0.9em;
36
+ cursor: pointer;
37
+ }
38
+
39
+ .run-notice-undo:hover:not(:disabled) {
40
+ background: var(--_hover);
41
+ }
42
+
43
+ .run-notice-undo:disabled {
44
+ opacity: 0.5;
45
+ cursor: default;
46
+ }
47
+
48
+ .run-notice-text {
49
+ min-width: 0;
50
+ overflow-wrap: anywhere;
51
+ }
52
+ `;
@@ -0,0 +1,65 @@
1
+ // The collapsible reasoning region over an assistant turn.
2
+ export const THOUGHTS_STYLES: string = `
3
+ /* ── Thoughts region ────────────────────────────────────────────
4
+ A muted, collapsible chain-of-thought at the top of the answer group: open
5
+ while the model reasons, folded once the answer text starts. */
6
+ .thoughts {
7
+ align-self: stretch;
8
+ display: flex;
9
+ flex-direction: column;
10
+ gap: 4px;
11
+ font-size: 12px;
12
+ color: var(--_muted);
13
+ }
14
+
15
+ .thoughts-toggle {
16
+ align-self: flex-start;
17
+ border: none;
18
+ padding: 0;
19
+ background: none;
20
+ font: inherit;
21
+ font-size: 12px;
22
+ font-weight: 600;
23
+ color: var(--_muted);
24
+ cursor: pointer;
25
+ }
26
+
27
+ .thoughts-toggle::before {
28
+ content: var(--_disclosure-expanded) " ";
29
+ }
30
+
31
+ .thoughts-toggle[aria-expanded="false"]::before {
32
+ content: var(--_disclosure-collapsed) " ";
33
+ }
34
+
35
+ /* A gentle pulse on the label while reasoning is still streaming. */
36
+ .thoughts[data-streaming] .thoughts-label {
37
+ animation: ag-ui-thoughts-pulse 1.4s ease-in-out infinite;
38
+ }
39
+
40
+ @keyframes ag-ui-thoughts-pulse {
41
+ 0%, 100% { opacity: 0.55; }
42
+ 50% { opacity: 1; }
43
+ }
44
+
45
+ .thoughts-body {
46
+ margin: 0;
47
+ padding: 4px 0 4px 10px;
48
+ border-left: 2px solid var(--_border);
49
+ max-height: 220px;
50
+ overflow: auto;
51
+ white-space: pre-wrap;
52
+ word-break: break-word;
53
+ font-family: inherit;
54
+ }
55
+
56
+ .thoughts-body[hidden] {
57
+ display: none;
58
+ }
59
+
60
+ @media (prefers-reduced-motion: reduce) {
61
+ .thoughts[data-streaming] .thoughts-label {
62
+ animation: none;
63
+ }
64
+ }
65
+ `;
@@ -287,6 +287,17 @@ export class ToolCallCard {
287
287
  this.#status.textContent = statusLabels(this.#strings)[status];
288
288
  }
289
289
 
290
+ /**
291
+ * Say whether a prompt is open on this call, a question it asked or a
292
+ * confirmation it needs. A flag of its own beside the status, which stays
293
+ * `pending`: the call is still running as far as the status goes, and a card
294
+ * display mode that reads the status draws it exactly as before. The step
295
+ * group reads the flag to tell a call waiting on a person from one working.
296
+ */
297
+ setAsking(asking: boolean): void {
298
+ this.element.toggleAttribute("data-asking", asking);
299
+ }
300
+
290
301
  /**
291
302
  * Record that a human approved or declined this call, as a line in the card.
292
303
  * The prompt disappears once answered, so this is the only lasting trace that