@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,114 @@
1
+ // The confirmation card for a destructive client tool, and the editable
2
+ // arguments an approval shares with it.
3
+ export const CONFIRMATION_STYLES: string = `
4
+ /* Inline confirmation card — lives in the transcript, no focus-stealing overlay. */
5
+ .confirm {
6
+ align-self: stretch;
7
+ box-sizing: border-box;
8
+ display: flex;
9
+ flex-direction: column;
10
+ gap: 8px;
11
+ padding: 12px;
12
+ background: var(--_bg);
13
+ border: 1px solid var(--_accent);
14
+ border-radius: 10px;
15
+ }
16
+
17
+ .confirm[data-resolved] {
18
+ opacity: 0.7;
19
+ border-color: var(--_border);
20
+ }
21
+
22
+ .confirm-body {
23
+ font-weight: 600;
24
+ }
25
+
26
+ .confirm-args {
27
+ margin: 0;
28
+ padding: 8px 10px;
29
+ max-height: 140px;
30
+ overflow: auto;
31
+ font-size: 12px;
32
+ font-family: ui-monospace, "SF Mono", Menlo, monospace;
33
+ background: var(--_assistant-bg);
34
+ border-radius: 8px;
35
+ white-space: pre-wrap;
36
+ word-break: break-word;
37
+ }
38
+
39
+ .confirm-actions {
40
+ display: flex;
41
+ flex-wrap: wrap;
42
+ gap: 8px;
43
+ justify-content: flex-end;
44
+ }
45
+
46
+ .confirm-btn {
47
+ border: 1px solid var(--_border);
48
+ border-radius: 8px;
49
+ padding: 8px 14px;
50
+ font: inherit;
51
+ font-weight: 600;
52
+ cursor: pointer;
53
+ background: var(--_bg);
54
+ color: var(--_fg);
55
+ }
56
+
57
+ .confirm-btn:disabled {
58
+ cursor: default;
59
+ opacity: 0.6;
60
+ }
61
+
62
+ .confirm-btn--confirm {
63
+ border-color: var(--_accent);
64
+ background: var(--_accent);
65
+ color: var(--_on-accent);
66
+ }
67
+
68
+ /* The session waiver. Deliberately the quietest of the three: it is the widest
69
+ decision on the card, so it should be reachable without being the one the eye
70
+ lands on when the user means to say yes once. */
71
+ .confirm-btn--always {
72
+ font-weight: 500;
73
+ opacity: 0.85;
74
+ }
75
+
76
+ .confirm-btn--always:hover,
77
+ .confirm-btn--always:focus-visible {
78
+ opacity: 1;
79
+ }
80
+
81
+ /* Editable arguments on an approval card. A plain field rather than a code
82
+ editor: it holds the JSON a card already displays, and the only interaction
83
+ is correcting a value before letting the call run. */
84
+ .approval-edit {
85
+ display: flex;
86
+ flex-direction: column;
87
+ gap: 6px;
88
+ }
89
+
90
+ .approval-args {
91
+ box-sizing: border-box;
92
+ width: 100%;
93
+ resize: vertical;
94
+ border: 1px solid var(--_border);
95
+ border-radius: 8px;
96
+ padding: 8px;
97
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
98
+ font-size: 0.85em;
99
+ background: var(--_bg);
100
+ color: var(--_fg);
101
+ }
102
+
103
+ .approval-args:focus-visible {
104
+ border-color: var(--_accent);
105
+ outline: none;
106
+ }
107
+
108
+ .approval-error {
109
+ font-size: 0.85em;
110
+ color: var(--_danger);
111
+ }
112
+
113
+ /* Message action row. Sits under a finished assistant bubble.
114
+ `;
@@ -0,0 +1,73 @@
1
+ // The question card the agent asks the user through.
2
+ export const QUESTION_STYLES: string = `
3
+ /* Question card — the built-in ask_user prompt (radios and/or free text). */
4
+ .question {
5
+ align-self: stretch;
6
+ box-sizing: border-box;
7
+ display: flex;
8
+ flex-direction: column;
9
+ gap: 8px;
10
+ padding: 12px;
11
+ background: var(--_bg);
12
+ border: 1px solid var(--_accent);
13
+ border-radius: 10px;
14
+ }
15
+
16
+ .question[data-resolved] {
17
+ opacity: 0.7;
18
+ border-color: var(--_border);
19
+ }
20
+
21
+ .question-body {
22
+ font-weight: 600;
23
+ }
24
+
25
+ .question-options {
26
+ display: flex;
27
+ flex-direction: column;
28
+ gap: 6px;
29
+ }
30
+
31
+ .question-choice {
32
+ display: flex;
33
+ align-items: center;
34
+ gap: 8px;
35
+ cursor: pointer;
36
+ }
37
+
38
+ .question-input {
39
+ box-sizing: border-box;
40
+ width: 100%;
41
+ padding: 8px 10px;
42
+ font: inherit;
43
+ color: var(--_fg);
44
+ background: var(--_bg);
45
+ border: 1px solid var(--_border);
46
+ border-radius: 8px;
47
+ }
48
+
49
+ .question-input:disabled {
50
+ opacity: 0.6;
51
+ }
52
+
53
+ .question-actions {
54
+ display: flex;
55
+ justify-content: flex-end;
56
+ }
57
+
58
+ .question-btn {
59
+ border: 1px solid var(--_accent);
60
+ border-radius: 8px;
61
+ padding: 8px 14px;
62
+ font: inherit;
63
+ font-weight: 600;
64
+ cursor: pointer;
65
+ background: var(--_accent);
66
+ color: var(--_on-accent);
67
+ }
68
+
69
+ .question-btn:disabled {
70
+ cursor: default;
71
+ opacity: 0.6;
72
+ }
73
+ `;
@@ -0,0 +1,137 @@
1
+ // Collapsing and expanding: the animation toward the launcher, and the
2
+ // in-flow and page placements that collapse differently or not at all.
3
+ export const COLLAPSE_STYLES: string = `
4
+ /* ── Collapse ───────────────────────────────────────────────────────────────
5
+ Collapsing shrinks the widget to the round floating launcher: the panel
6
+ scales toward the launcher's corner and fades, the launcher pops in from the
7
+ same point. Both halves are transform and opacity only, so the morph runs on
8
+ the compositor and never reflows the host page.
9
+
10
+ The host box keeps its expanded size, since animating it would animate
11
+ layout and a dragged --ag-ui-width would fight the launcher's own size.
12
+ Nothing paints there once the panel is gone, so the box only has to stop
13
+ swallowing clicks: pointer events go to none and the launcher takes them.
14
+
15
+ Two placements do not use it: "sidebar" slides to its rail (below), and
16
+ "embedded" keeps its header bar, having no corner for a floating circle that
17
+ would escape the host's layout. "page" has no collapsed state at all. */
18
+ :host([collapsed]) {
19
+ pointer-events: none;
20
+ /* A collapsed host has to be allowed to shrink, and in the layout hosts
21
+ actually use it is not. Every collapse path here works by letting the host
22
+ size to its content -- the in-flow ones set height: auto, the floating one
23
+ leaves only the launcher -- and a flex or grid parent whose align-items is
24
+ the default stretch value overrides all of it. The panel then hides and the box
25
+ it occupied stays: a header bar over several hundred pixels of nothing.
26
+ Every known consumer hit this, because putting the element in a flex column
27
+ beside the page content is the obvious way to embed it. */
28
+ align-self: start;
29
+ }
30
+
31
+ :host([collapsed]) .chat {
32
+ opacity: 0;
33
+ transform: scale(0.94);
34
+ visibility: hidden;
35
+ }
36
+
37
+ /* visibility keeps the panel out of the tab order and the a11y tree at rest
38
+ without display:none killing the transition. It interpolates so any progress
39
+ below 1 still counts as visible: the panel stays on screen for the whole
40
+ collapse, flips hidden exactly at the end, and on expand is visible from the
41
+ first frame. */
42
+ .chat {
43
+ transform-origin: bottom right;
44
+ transition:
45
+ opacity var(--_motion) var(--_ease),
46
+ transform var(--_motion) var(--_ease),
47
+ visibility var(--_motion) var(--_ease);
48
+ }
49
+
50
+ :host([placement="bottom-left"]) .chat {
51
+ transform-origin: bottom left;
52
+ }
53
+
54
+ /* Once the launcher has been dragged the element places itself, and stamps the
55
+ corner it chose to open away from. The morph has to start at that same
56
+ corner: scaling out of the one the placement originally guessed reads as the
57
+ panel leaping across the screen before it opens.
58
+
59
+ Equal specificity to the placement rule above, so source order is what lets
60
+ the stamped value win -- the same arrangement the resize grip uses, and the
61
+ same trap. Write these with = on the whole hyphenated token; a ~= would match
62
+ whitespace-separated words and so could never match at all. */
63
+ :host([data-expand-corner="top-left"]) .chat {
64
+ transform-origin: top left;
65
+ }
66
+
67
+ :host([data-expand-corner="top-right"]) .chat {
68
+ transform-origin: top right;
69
+ }
70
+
71
+ :host([data-expand-corner="bottom-left"]) .chat {
72
+ transform-origin: bottom left;
73
+ }
74
+
75
+ :host([data-expand-corner="bottom-right"]) .chat {
76
+ transform-origin: bottom right;
77
+ }
78
+
79
+ /* The in-flow placement keeps the original collapse: hide the body, let the
80
+ host shrink to the header bar. */
81
+ :host([collapsed][placement="embedded"]) {
82
+ height: auto;
83
+ max-height: none;
84
+ pointer-events: auto;
85
+ }
86
+
87
+ :host([collapsed][placement="embedded"]) .chat {
88
+ opacity: 1;
89
+ transform: none;
90
+ visibility: visible;
91
+ }
92
+
93
+ /* It keeps the header bar, so the launcher must stay out of the way: an
94
+ embedded host is position: static, which would let an absolutely-positioned
95
+ circle escape the layout and land against whatever the page positions. */
96
+ :host([collapsed][placement="embedded"]) .launcher {
97
+ visibility: hidden;
98
+ opacity: 0;
99
+ }
100
+
101
+ :host([collapsed][placement="embedded"]) .messages-wrap,
102
+ :host([collapsed][placement="embedded"]) .messages,
103
+ :host([collapsed][placement="embedded"]) .input-row,
104
+ :host([collapsed][placement="embedded"]) .skill-chips,
105
+ :host([collapsed][placement="embedded"]) .skill-palette,
106
+ :host([collapsed][placement="embedded"]) .skill-hint {
107
+ display: none;
108
+ }
109
+
110
+ /* The page placement has no collapsed state, so it offers no control for one.
111
+ A dedicated route has no "away" to go to: shrinking it left a strip of
112
+ application chrome fixed over a route that no longer had an owner, and this
113
+ is the one placement where the launcher that would bring it back is hidden.
114
+
115
+ Hiding the control is only half of it. The state has another way in -- the
116
+ attribute can be written straight onto the element, and a value stored under
117
+ a different placement is restored on connect -- and the JS guards that catch
118
+ those cannot see an attribute set directly. So the placement neutralises the
119
+ state here as well: with the collapse rules above no longer naming "page", an
120
+ unguarded collapsed attribute would otherwise fall through to the generic
121
+ rule that scales the panel away and drops pointer events, leaving nothing on
122
+ screen and no launcher to press. */
123
+ :host([placement="page"]) .header-btn--collapse {
124
+ display: none;
125
+ }
126
+
127
+ :host([placement="page"][collapsed]) {
128
+ pointer-events: auto;
129
+ align-self: auto;
130
+ }
131
+
132
+ :host([placement="page"][collapsed]) .chat {
133
+ opacity: 1;
134
+ transform: none;
135
+ visibility: visible;
136
+ }
137
+ `;
@@ -0,0 +1,14 @@
1
+ // The embedded placement: no floating chrome, laid out by the host.
2
+ export const EMBEDDED_STYLES: string = `
3
+ /* Embedded: drop the floating chrome and the high z-index stacking context so
4
+ the widget lives in the host's own layout (fixes overlay/z-index clashes). */
5
+ :host([placement="embedded"]) {
6
+ --_position: var(--ag-ui-position, static);
7
+ --_width: var(--ag-ui-width, 100%);
8
+ --_height: var(--ag-ui-height, 100%);
9
+ --_max-width: var(--ag-ui-max-width, 100%);
10
+ --_max-height: var(--ag-ui-max-height, 100%);
11
+ --_shadow: var(--ag-ui-shadow, none);
12
+ --_z-index: var(--ag-ui-z-index, auto);
13
+ }
14
+ `;
@@ -0,0 +1,189 @@
1
+ // The launcher a collapsed widget shrinks to: the round button, its unread
2
+ // badge, and the sidebar's edge rail, which is the same element styled again.
3
+ export const LAUNCHER_STYLES: string = `
4
+ /* ── Launcher ───────────────────────────────────────────────────────────────
5
+ What a collapsed widget shrinks to: a round floating button by default, the
6
+ sidebar's slim edge rail under that placement. A sibling of the panel, so it
7
+ survives the panel being hidden, and the only part of a collapsed widget
8
+ that takes pointer events. */
9
+ .launcher {
10
+ display: flex;
11
+ position: absolute;
12
+ inset: var(--_launcher-inset);
13
+ align-items: center;
14
+ justify-content: center;
15
+ width: var(--_launcher-size);
16
+ height: var(--_launcher-size);
17
+ padding: 0;
18
+ border: none;
19
+ border-radius: var(--_launcher-radius);
20
+ background: var(--_launcher-bg);
21
+ color: var(--_launcher-fg);
22
+ box-shadow: var(--_shadow);
23
+ font: inherit;
24
+ cursor: pointer;
25
+ pointer-events: auto;
26
+ /* A touch drag on the launcher moves it; without this the page scrolls under
27
+ the finger instead and the launcher never moves at all. */
28
+ touch-action: none;
29
+ opacity: 0;
30
+ transform: scale(0.4);
31
+ visibility: hidden;
32
+ transition:
33
+ opacity var(--_motion) var(--_ease),
34
+ transform var(--_motion) var(--_ease-pop),
35
+ visibility var(--_motion) var(--_ease);
36
+ }
37
+
38
+ /* The launcher grows out of the corner the panel shrank into. It stays laid
39
+ out at rest rather than display:none, which is what lets it animate both in
40
+ and out: an unrendered element has no before-change style to transition
41
+ from, and one flipping display to none cannot transition at all. visibility
42
+ keeps it unpaintable, untabbable and unclickable in between, so the expanded
43
+ panel's own controls underneath stay reachable. */
44
+ :host([collapsed]) .launcher {
45
+ opacity: 1;
46
+ transform: none;
47
+ visibility: visible;
48
+ }
49
+
50
+ :host([collapsed]) .launcher:hover {
51
+ transform: scale(1.06);
52
+ }
53
+
54
+ :host([collapsed]) .launcher:active {
55
+ transform: scale(0.94);
56
+ }
57
+
58
+ /* A drag is a press and a hover at the same time, so both scale rules above are
59
+ live throughout it. Cancelling them is what keeps the launcher the size of
60
+ the thing under the pointer while it travels; the position itself comes from
61
+ inset, which nothing here transitions, so it tracks the pointer exactly. */
62
+ :host([collapsed]) .launcher[data-dragging] {
63
+ transform: none;
64
+ cursor: grabbing;
65
+ }
66
+
67
+ .launcher .icon-holder {
68
+ width: var(--_launcher-icon-size);
69
+ height: var(--_launcher-icon-size);
70
+ }
71
+
72
+ /* The unread badge rides the launcher's top-right corner. The ring in the
73
+ widget's own background is what separates it from the launcher underneath,
74
+ whatever colour the host themes either one. */
75
+ .launcher-badge {
76
+ position: absolute;
77
+ top: -2px;
78
+ right: -2px;
79
+ display: inline-flex;
80
+ align-items: center;
81
+ justify-content: center;
82
+ box-sizing: border-box;
83
+ min-width: var(--_badge-size);
84
+ height: var(--_badge-size);
85
+ padding: 0 5px;
86
+ border-radius: 999px;
87
+ background: var(--_badge-bg);
88
+ color: var(--_badge-fg);
89
+ font-size: var(--_badge-font-size);
90
+ font-weight: 600;
91
+ line-height: 1;
92
+ box-shadow: 0 0 0 2px var(--_bg);
93
+ }
94
+
95
+ .launcher-badge[hidden] {
96
+ display: none;
97
+ }
98
+
99
+ /* The sidebar collapses to an edge rail instead: full height, square, flush
100
+ against the dock. It slides in with the panel rather than popping. */
101
+ /* The edge rail. It reads as the docked edge of a panel rather than a coloured
102
+ stripe: the surface the panel is made of, a border on the side it docks
103
+ against, and the accent kept for the icon -- a full-height slab of accent is
104
+ the loudest thing on the page and says the least about itself.
105
+
106
+ Content sits at the top rather than centred, because a control floating in
107
+ the middle of a screen-high column has nothing to belong to. */
108
+ :host([placement="sidebar"][collapsed]) .launcher {
109
+ inset: 0;
110
+ width: auto;
111
+ height: auto;
112
+ flex-direction: column;
113
+ align-items: center;
114
+ justify-content: flex-start;
115
+ gap: 12px;
116
+ padding-top: 14px;
117
+ border: 0;
118
+ border-inline-start: 1px solid var(--_border);
119
+ border-radius: 0;
120
+ background: var(--_bg);
121
+ color: var(--_fg);
122
+ box-shadow: none;
123
+ transform: none;
124
+ }
125
+
126
+ :host([placement="sidebar"][data-side="left"][collapsed]) .launcher {
127
+ border-inline-start: 0;
128
+ border-inline-end: 1px solid var(--_border);
129
+ }
130
+
131
+ /* The icon keeps the accent, so there is one obvious thing to press.
132
+
133
+ The glyph is sized here rather than left to fill the holder. A glyph in an
134
+ icon holder takes the holder's whole box by default, which is right where the
135
+ holder is only a box and wrong the moment it becomes a filled circle: the
136
+ mark then runs edge to edge and reads as a square crammed into a disc. The
137
+ proportion is the floating launcher's own -- a 26px glyph in a 56px bubble --
138
+ so the two collapsed states look like the same widget. */
139
+ :host([placement="sidebar"][collapsed]) .launcher .icon-holder {
140
+ display: inline-flex;
141
+ align-items: center;
142
+ justify-content: center;
143
+ width: 34px;
144
+ height: 34px;
145
+ border-radius: 50%;
146
+ background: var(--_header-bg);
147
+ color: var(--_header-fg);
148
+ }
149
+
150
+ :host([placement="sidebar"][collapsed]) .launcher .icon-holder .glyph,
151
+ :host([placement="sidebar"][collapsed]) .launcher .icon-holder .icon-img {
152
+ width: 16px;
153
+ height: 16px;
154
+ }
155
+
156
+ /* Written down the rail, which is the only direction it fits. Reading upward
157
+ is the convention for a right-hand edge and matches how a docked panel's
158
+ label is set everywhere it appears. */
159
+ .rail-label {
160
+ display: none;
161
+ }
162
+
163
+ :host([placement="sidebar"][collapsed]) .rail-label {
164
+ display: block;
165
+ writing-mode: vertical-rl;
166
+ transform: rotate(180deg);
167
+ max-height: calc(100% - 96px);
168
+ overflow: hidden;
169
+ font-size: 0.9em;
170
+ font-weight: 600;
171
+ letter-spacing: 0.02em;
172
+ white-space: nowrap;
173
+ text-overflow: ellipsis;
174
+ }
175
+
176
+ :host([placement="sidebar"][data-side="left"][collapsed]) .rail-label {
177
+ writing-mode: vertical-rl;
178
+ transform: none;
179
+ }
180
+
181
+ /* Every transition in the stylesheet is timed by --_motion, so collapsing it to a
182
+ frame is the whole reduced-motion story: states still change (and display
183
+ still flips at the end of its discrete transition), nothing travels. */
184
+ @media (prefers-reduced-motion: reduce) {
185
+ :host {
186
+ --_motion: var(--ag-ui-motion, 0.001s);
187
+ }
188
+ }
189
+ `;
@@ -0,0 +1,47 @@
1
+ // The page and full placements: a full-bleed surface with a reading column.
2
+ export const PAGE_STYLES: string = `
3
+ /* Page: full-bleed background with a centred reading column capped at
4
+ --ag-ui-content-max-width, where "full" is edge-to-edge and left-aligned.
5
+ The column comes from symmetric auto padding on the scroll area and
6
+ composer rather than a per-row wrapper, so user pills still right-align and
7
+ the assistant well spans the column. */
8
+ :host([placement="page"]) {
9
+ --_inset: var(
10
+ --ag-ui-inset,
11
+ calc(var(--_viewport-inset-top) + var(--_keyboard-inset-top)) var(--_viewport-inset-right)
12
+ var(--_viewport-inset-bottom) var(--_viewport-inset-left)
13
+ );
14
+ --_width: var(--ag-ui-width, var(--_viewport-width));
15
+ --_height: var(--ag-ui-height, var(--_viewport-height));
16
+ --_max-width: var(--ag-ui-max-width, var(--_viewport-width));
17
+ --_max-height: var(--ag-ui-max-height, var(--_viewport-height));
18
+ --_panel-radius: var(--ag-ui-panel-radius, 0);
19
+ }
20
+
21
+ :host([placement="page"]) .messages {
22
+ padding-inline: max(var(--_pad), calc((100% - var(--_content-max-width)) / 2));
23
+ }
24
+
25
+ :host([placement="page"]) .input-row {
26
+ padding-inline: max(12px, calc((100% - var(--_content-max-width)) / 2));
27
+ }
28
+
29
+ /* The rows between the message list and the composer line up with the column
30
+ too. Chips and tray are padding-based while palette and hint are
31
+ margin-based, so each gets its own inline axis nudged by the same gutter. */
32
+ :host([placement="page"]) .skill-chips,
33
+ :host([placement="page"]) .attachment-tray {
34
+ padding-inline: max(12px, calc((100% - var(--_content-max-width)) / 2));
35
+ }
36
+
37
+ :host([placement="page"]) .skill-palette,
38
+ :host([placement="page"]) .skill-hint {
39
+ margin-inline: max(12px, calc((100% - var(--_content-max-width)) / 2));
40
+ }
41
+
42
+ /* In the reading column the assistant well uses the full width; the user
43
+ message stays a right-aligned pill (its default align-self + max-width). */
44
+ :host([placement="page"]) .message--assistant {
45
+ max-width: 100%;
46
+ }
47
+ `;
@@ -3,7 +3,6 @@ import type { ChatCorner, ChatSurfaceReport } from "../../tools/chat_surface_too
3
3
  import type { UiStrings } from "../ui_strings.js";
4
4
  import { clampLauncher } from "./clamp_launcher.js";
5
5
  import { clampPanel } from "./clamp_panel.js";
6
- import { isDraggablePlacement } from "./is_draggable_placement.js";
7
6
  import { enableLauncherDrag } from "./launcher_drag.js";
8
7
  import {
9
8
  type ExpandCorner,
@@ -23,6 +22,7 @@ import {
23
22
  type ResizeGrip,
24
23
  type ResizeSize,
25
24
  } from "./resize_handle.js";
25
+ import { surfaceFor } from "./surface_for.js";
26
26
 
27
27
  /** Per-tab persistence key for a dragged panel size. */
28
28
  const SIZE_KEY = "ag-ui-chat:size";
@@ -400,16 +400,7 @@ export class PanelPlacement {
400
400
  * per interaction, because `placement` is a live attribute.
401
401
  */
402
402
  #resizeAxis(): ResizeAxis {
403
- switch (this.#host.element.getAttribute("placement")) {
404
- case "full":
405
- case "page":
406
- return "none";
407
- case "sidebar":
408
- case "side":
409
- return "width";
410
- default:
411
- return "both";
412
- }
403
+ return surfaceFor(this.#host.element.getAttribute("placement")).resizeAxis;
413
404
  }
414
405
 
415
406
  /**
@@ -559,7 +550,7 @@ export class PanelPlacement {
559
550
  #launcherDraggable(): boolean {
560
551
  return (
561
552
  this.#host.element.getAttribute("data-launcher-drag") !== "false" &&
562
- isDraggablePlacement(this.#host.element.getAttribute("placement"))
553
+ surfaceFor(this.#host.element.getAttribute("placement")).launcherDraggable
563
554
  );
564
555
  }
565
556
 
@@ -0,0 +1,33 @@
1
+ // The token values the bottom-left, side and full placements start from;
2
+ // the default bottom-right corner is the token defaults themselves.
3
+ export const PRESET_STYLES: string = `
4
+ /* ── Placement presets ──────────────────────────────────────────────────── */
5
+ :host([placement="bottom-left"]) {
6
+ --_inset: var(
7
+ --ag-ui-inset,
8
+ auto auto calc(var(--_edge-gutter) + var(--_viewport-inset-bottom) + var(--_keyboard-inset))
9
+ calc(var(--_edge-gutter) + var(--_viewport-inset-left))
10
+ );
11
+ }
12
+
13
+ :host([placement="side"]) {
14
+ --_inset: var(--ag-ui-inset, calc(var(--_viewport-inset-top) + var(--_keyboard-inset-top)) var(--_viewport-inset-right) var(--_viewport-inset-bottom) auto);
15
+ --_width: var(--ag-ui-width, 420px);
16
+ --_height: var(--ag-ui-height, var(--_viewport-height));
17
+ --_max-height: var(--ag-ui-max-height, var(--_viewport-height));
18
+ --_panel-radius: var(--ag-ui-panel-radius, 0);
19
+ }
20
+
21
+ :host([placement="full"]) {
22
+ --_inset: var(
23
+ --ag-ui-inset,
24
+ calc(var(--_viewport-inset-top) + var(--_keyboard-inset-top)) var(--_viewport-inset-right)
25
+ var(--_viewport-inset-bottom) var(--_viewport-inset-left)
26
+ );
27
+ --_width: var(--ag-ui-width, var(--_viewport-width));
28
+ --_height: var(--ag-ui-height, var(--_viewport-height));
29
+ --_max-width: var(--ag-ui-max-width, var(--_viewport-width));
30
+ --_max-height: var(--ag-ui-max-height, var(--_viewport-height));
31
+ --_panel-radius: var(--ag-ui-panel-radius, 0);
32
+ }
33
+ `;