@agent-native/toolkit 0.201.1 → 0.202.0-nightly-20261006033736

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 (229) hide show
  1. package/agent-native.eject.json +39 -12
  2. package/dist/app/agentkit/react/components.d.ts.map +1 -1
  3. package/dist/app/agentkit/react/components.js +292 -23
  4. package/dist/app/agentkit/react/components.js.map +1 -1
  5. package/dist/app/agentkit/react/context.d.ts +5 -0
  6. package/dist/app/agentkit/react/context.d.ts.map +1 -1
  7. package/dist/app/agentkit/react/context.js +5 -0
  8. package/dist/app/agentkit/react/context.js.map +1 -1
  9. package/dist/app/agentkit/react/styles.css +78 -3
  10. package/dist/app/auth/AuthPage.d.ts.map +1 -1
  11. package/dist/app/auth/AuthPage.js +7 -14
  12. package/dist/app/auth/AuthPage.js.map +1 -1
  13. package/dist/app/auth/server.d.ts +6 -0
  14. package/dist/app/auth/server.d.ts.map +1 -0
  15. package/dist/app/auth/server.js +19 -0
  16. package/dist/app/auth/server.js.map +1 -0
  17. package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
  18. package/dist/app/chat/AgentKitAssistantChat.js +164 -83
  19. package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
  20. package/dist/app/chat/AgentSidebar.d.ts +2 -1
  21. package/dist/app/chat/AgentSidebar.d.ts.map +1 -1
  22. package/dist/app/chat/AgentSidebar.js +2 -2
  23. package/dist/app/chat/AgentSidebar.js.map +1 -1
  24. package/dist/app/chat/agentkit-chat/guided-questions.d.ts.map +1 -1
  25. package/dist/app/chat/agentkit-chat/guided-questions.js +13 -3
  26. package/dist/app/chat/agentkit-chat/guided-questions.js.map +1 -1
  27. package/dist/app/chat/chat/surface-types.d.ts +2 -0
  28. package/dist/app/chat/chat/surface-types.d.ts.map +1 -1
  29. package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
  30. package/dist/app/i18n/catalogs/ar-SA.js +41 -0
  31. package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
  32. package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
  33. package/dist/app/i18n/catalogs/de-DE.js +41 -0
  34. package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
  35. package/dist/app/i18n/catalogs/en-US.d.ts +41 -0
  36. package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
  37. package/dist/app/i18n/catalogs/en-US.js +41 -0
  38. package/dist/app/i18n/catalogs/en-US.js.map +1 -1
  39. package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
  40. package/dist/app/i18n/catalogs/es-ES.js +41 -0
  41. package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
  42. package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
  43. package/dist/app/i18n/catalogs/fr-FR.js +41 -0
  44. package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
  45. package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
  46. package/dist/app/i18n/catalogs/hi-IN.js +41 -0
  47. package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
  48. package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
  49. package/dist/app/i18n/catalogs/ja-JP.js +41 -0
  50. package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
  51. package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
  52. package/dist/app/i18n/catalogs/ko-KR.js +41 -0
  53. package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
  54. package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
  55. package/dist/app/i18n/catalogs/pt-BR.js +41 -0
  56. package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
  57. package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
  58. package/dist/app/i18n/catalogs/zh-CN.js +41 -0
  59. package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
  60. package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
  61. package/dist/app/i18n/catalogs/zh-TW.js +41 -0
  62. package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
  63. package/dist/app/shared/StarfieldBackground.d.ts +1 -6
  64. package/dist/app/shared/StarfieldBackground.d.ts.map +1 -1
  65. package/dist/app/shared/StarfieldBackground.js +1 -368
  66. package/dist/app/shared/StarfieldBackground.js.map +1 -1
  67. package/dist/app/shared/WaveBackground.d.ts +5 -0
  68. package/dist/app/shared/WaveBackground.d.ts.map +1 -0
  69. package/dist/app/shared/WaveBackground.js +34 -0
  70. package/dist/app/shared/WaveBackground.js.map +1 -0
  71. package/dist/app/shared/WebGlWaveBackground.d.ts +9 -0
  72. package/dist/app/shared/WebGlWaveBackground.d.ts.map +1 -0
  73. package/dist/app/shared/WebGlWaveBackground.js +433 -0
  74. package/dist/app/shared/WebGlWaveBackground.js.map +1 -0
  75. package/dist/app/shared/index.d.ts +1 -0
  76. package/dist/app/shared/index.d.ts.map +1 -1
  77. package/dist/app/shared/index.js +1 -0
  78. package/dist/app/shared/index.js.map +1 -1
  79. package/dist/app/shared/ocean/bloom-blur.wgsl +43 -0
  80. package/dist/app/shared/ocean/bloom-blur.wgsl.js +1 -0
  81. package/dist/app/shared/ocean/bloom-bright.wgsl +21 -0
  82. package/dist/app/shared/ocean/bloom-bright.wgsl.js +1 -0
  83. package/dist/app/shared/ocean/bloom-composite.wgsl +40 -0
  84. package/dist/app/shared/ocean/bloom-composite.wgsl.js +1 -0
  85. package/dist/app/shared/ocean/brand-colors.d.ts +9 -0
  86. package/dist/app/shared/ocean/brand-colors.d.ts.map +1 -0
  87. package/dist/app/shared/ocean/brand-colors.js +45 -0
  88. package/dist/app/shared/ocean/brand-colors.js.map +1 -0
  89. package/dist/app/shared/ocean/camera.d.ts +5 -0
  90. package/dist/app/shared/ocean/camera.d.ts.map +1 -0
  91. package/dist/app/shared/ocean/camera.js +24 -0
  92. package/dist/app/shared/ocean/camera.js.map +1 -0
  93. package/dist/app/shared/ocean/hero-ocean-background.d.ts +8 -0
  94. package/dist/app/shared/ocean/hero-ocean-background.d.ts.map +1 -0
  95. package/dist/app/shared/ocean/hero-ocean-background.js +186 -0
  96. package/dist/app/shared/ocean/hero-ocean-background.js.map +1 -0
  97. package/dist/app/shared/ocean/ifft-stage.wgsl +36 -0
  98. package/dist/app/shared/ocean/ifft-stage.wgsl.js +1 -0
  99. package/dist/app/shared/ocean/initial-spectrum.wgsl +36 -0
  100. package/dist/app/shared/ocean/initial-spectrum.wgsl.js +1 -0
  101. package/dist/app/shared/ocean/noise.wgsl +24 -0
  102. package/dist/app/shared/ocean/noise.wgsl.js +1 -0
  103. package/dist/app/shared/ocean/normal-foam.wgsl +32 -0
  104. package/dist/app/shared/ocean/normal-foam.wgsl.js +1 -0
  105. package/dist/app/shared/ocean/ocean-colors.d.ts +6 -0
  106. package/dist/app/shared/ocean/ocean-colors.d.ts.map +1 -0
  107. package/dist/app/shared/ocean/ocean-colors.js +6 -0
  108. package/dist/app/shared/ocean/ocean-colors.js.map +1 -0
  109. package/dist/app/shared/ocean/ocean-common.wgsl +15 -0
  110. package/dist/app/shared/ocean/ocean-common.wgsl.js +1 -0
  111. package/dist/app/shared/ocean/ocean-graph.d.ts +12 -0
  112. package/dist/app/shared/ocean/ocean-graph.d.ts.map +1 -0
  113. package/dist/app/shared/ocean/ocean-graph.js +16 -0
  114. package/dist/app/shared/ocean/ocean-graph.js.map +1 -0
  115. package/dist/app/shared/ocean/particles.wgsl +139 -0
  116. package/dist/app/shared/ocean/particles.wgsl.js +1 -0
  117. package/dist/app/shared/ocean/present.wgsl +40 -0
  118. package/dist/app/shared/ocean/present.wgsl.js +1 -0
  119. package/dist/app/shared/ocean/renderer.d.ts +66 -0
  120. package/dist/app/shared/ocean/renderer.d.ts.map +1 -0
  121. package/dist/app/shared/ocean/renderer.js +535 -0
  122. package/dist/app/shared/ocean/renderer.js.map +1 -0
  123. package/dist/app/shared/ocean/spectrum.wgsl +35 -0
  124. package/dist/app/shared/ocean/spectrum.wgsl.js +1 -0
  125. package/dist/app/shared/ocean/tuning.d.ts +48 -0
  126. package/dist/app/shared/ocean/tuning.d.ts.map +1 -0
  127. package/dist/app/shared/ocean/tuning.js +74 -0
  128. package/dist/app/shared/ocean/tuning.js.map +1 -0
  129. package/dist/app/shared/ocean/webgpu-support.d.ts +3 -0
  130. package/dist/app/shared/ocean/webgpu-support.d.ts.map +1 -0
  131. package/dist/app/shared/ocean/webgpu-support.js +13 -0
  132. package/dist/app/shared/ocean/webgpu-support.js.map +1 -0
  133. package/dist/app/sharing/AccessRequestApprovalPage.d.ts +23 -0
  134. package/dist/app/sharing/AccessRequestApprovalPage.d.ts.map +1 -0
  135. package/dist/app/sharing/AccessRequestApprovalPage.js +95 -0
  136. package/dist/app/sharing/AccessRequestApprovalPage.js.map +1 -0
  137. package/dist/app/sharing/AccessRequestsSection.d.ts +5 -0
  138. package/dist/app/sharing/AccessRequestsSection.d.ts.map +1 -0
  139. package/dist/app/sharing/AccessRequestsSection.js +47 -0
  140. package/dist/app/sharing/AccessRequestsSection.js.map +1 -0
  141. package/dist/app/sharing/ResourceAccessScreen.d.ts +53 -0
  142. package/dist/app/sharing/ResourceAccessScreen.d.ts.map +1 -0
  143. package/dist/app/sharing/ResourceAccessScreen.js +132 -0
  144. package/dist/app/sharing/ResourceAccessScreen.js.map +1 -0
  145. package/dist/app/sharing/ShareButton.d.ts.map +1 -1
  146. package/dist/app/sharing/ShareButton.js +4 -2
  147. package/dist/app/sharing/ShareButton.js.map +1 -1
  148. package/dist/app/sharing/ShareDialog.d.ts.map +1 -1
  149. package/dist/app/sharing/ShareDialog.js +7 -5
  150. package/dist/app/sharing/ShareDialog.js.map +1 -1
  151. package/dist/app/sharing/index.d.ts +2 -0
  152. package/dist/app/sharing/index.d.ts.map +1 -1
  153. package/dist/app/sharing/index.js +2 -0
  154. package/dist/app/sharing/index.js.map +1 -1
  155. package/dist/app/styles/agent-native.css +21 -1
  156. package/dist/app/styles/tokens/agent-kit.css +3 -1
  157. package/dist/composer/AgentSuggestionBar.d.ts +1 -1
  158. package/dist/composer/AgentSuggestionBar.d.ts.map +1 -1
  159. package/dist/composer/AgentSuggestionBar.js +75 -6
  160. package/dist/composer/AgentSuggestionBar.js.map +1 -1
  161. package/package.json +14 -4
  162. package/src/app/agentkit/react/components.tsx +492 -60
  163. package/src/app/agentkit/react/context.tsx +10 -0
  164. package/src/app/agentkit/react/lifecycle.spec.tsx +487 -1
  165. package/src/app/agentkit/react/styles.css +78 -3
  166. package/src/app/auth/AuthPage.spec.tsx +11 -7
  167. package/src/app/auth/AuthPage.tsx +16 -35
  168. package/src/app/auth/server.spec.ts +68 -0
  169. package/src/app/auth/server.ts +29 -0
  170. package/src/app/chat/AgentKitAssistantChat.spec.tsx +287 -290
  171. package/src/app/chat/AgentKitAssistantChat.tsx +219 -116
  172. package/src/app/chat/AgentSidebar.tsx +3 -0
  173. package/src/app/chat/agentkit-chat/guided-questions.flow.spec.tsx +85 -0
  174. package/src/app/chat/agentkit-chat/guided-questions.tsx +12 -5
  175. package/src/app/chat/chat/surface-types.ts +2 -0
  176. package/src/app/i18n/catalogs/ar-SA.ts +44 -0
  177. package/src/app/i18n/catalogs/de-DE.ts +55 -0
  178. package/src/app/i18n/catalogs/en-US.ts +46 -0
  179. package/src/app/i18n/catalogs/es-ES.ts +53 -0
  180. package/src/app/i18n/catalogs/fr-FR.ts +53 -0
  181. package/src/app/i18n/catalogs/hi-IN.ts +47 -0
  182. package/src/app/i18n/catalogs/ja-JP.ts +54 -0
  183. package/src/app/i18n/catalogs/ko-KR.ts +50 -0
  184. package/src/app/i18n/catalogs/pt-BR.ts +52 -0
  185. package/src/app/i18n/catalogs/zh-CN.ts +42 -0
  186. package/src/app/i18n/catalogs/zh-TW.ts +43 -0
  187. package/src/app/shared/StarfieldBackground.tsx +4 -423
  188. package/src/app/shared/WaveBackground.spec.tsx +104 -0
  189. package/src/app/shared/WaveBackground.tsx +60 -0
  190. package/src/app/shared/WebGlWaveBackground.spec.tsx +106 -0
  191. package/src/app/shared/WebGlWaveBackground.tsx +499 -0
  192. package/src/app/shared/index.ts +1 -0
  193. package/src/app/shared/ocean/bloom-blur.wgsl +43 -0
  194. package/src/app/shared/ocean/bloom-bright.wgsl +21 -0
  195. package/src/app/shared/ocean/bloom-composite.wgsl +40 -0
  196. package/src/app/shared/ocean/brand-colors.ts +49 -0
  197. package/src/app/shared/ocean/camera.ts +28 -0
  198. package/src/app/shared/ocean/hero-ocean-background.spec.tsx +369 -0
  199. package/src/app/shared/ocean/hero-ocean-background.tsx +238 -0
  200. package/src/app/shared/ocean/ifft-stage.wgsl +36 -0
  201. package/src/app/shared/ocean/initial-spectrum.wgsl +36 -0
  202. package/src/app/shared/ocean/noise.wgsl +24 -0
  203. package/src/app/shared/ocean/normal-foam.wgsl +32 -0
  204. package/src/app/shared/ocean/ocean-colors.ts +11 -0
  205. package/src/app/shared/ocean/ocean-common.wgsl +15 -0
  206. package/src/app/shared/ocean/ocean-graph.ts +29 -0
  207. package/src/app/shared/ocean/particles.wgsl +139 -0
  208. package/src/app/shared/ocean/present.wgsl +40 -0
  209. package/src/app/shared/ocean/renderer.spec.ts +100 -0
  210. package/src/app/shared/ocean/renderer.ts +693 -0
  211. package/src/app/shared/ocean/spectrum.wgsl +35 -0
  212. package/src/app/shared/ocean/tuning.ts +131 -0
  213. package/src/app/shared/ocean/webgpu-support.ts +12 -0
  214. package/src/app/sharing/AccessRequestApprovalPage.spec.tsx +276 -0
  215. package/src/app/sharing/AccessRequestApprovalPage.tsx +307 -0
  216. package/src/app/sharing/AccessRequestsSection.tsx +163 -0
  217. package/src/app/sharing/ResourceAccessScreen.spec.tsx +235 -0
  218. package/src/app/sharing/ResourceAccessScreen.tsx +331 -0
  219. package/src/app/sharing/ShareButton.spec.tsx +171 -5
  220. package/src/app/sharing/ShareButton.tsx +15 -2
  221. package/src/app/sharing/ShareDialog.spec.tsx +71 -2
  222. package/src/app/sharing/ShareDialog.tsx +24 -2
  223. package/src/app/sharing/index.ts +2 -0
  224. package/src/app/styles/agent-native.css +21 -1
  225. package/src/app/styles/agent-native.spec.ts +16 -3
  226. package/src/app/styles/tokens/agent-kit.css +3 -1
  227. package/src/composer/AgentSuggestionBar.overflow.spec.tsx +277 -0
  228. package/src/composer/AgentSuggestionBar.tsx +113 -8
  229. package/src/wgsl-env.d.ts +6 -0
@@ -27,6 +27,7 @@ import {
27
27
  } from "@tabler/icons-react";
28
28
  import { type ReactNode } from "react";
29
29
 
30
+ import { AccessRequestsSection } from "./AccessRequestsSection.js";
30
31
  import { AgentShareSection } from "./AgentShareSection.js";
31
32
 
32
33
  export interface ShareDialogProps {
@@ -83,6 +84,12 @@ export function ShareDialog({
83
84
  });
84
85
  if (!open) return null;
85
86
  const dialogTitle = title ?? controller.title;
87
+ const accessRequests = controller.canManage ? (
88
+ <AccessRequestsSection
89
+ resourceType={resourceType}
90
+ resourceId={resourceId}
91
+ />
92
+ ) : null;
86
93
  const showTabList =
87
94
  controller.tabs.length > 1 || controller.tabs[0]?.value !== "link";
88
95
 
@@ -132,12 +139,17 @@ export function ShareDialog({
132
139
 
133
140
  <div className="px-5 py-4">
134
141
  {controller.tabsEnabled && controller.activeTab === "link" ? (
135
- <LinkTab controller={controller} extras={linkTabExtras} />
142
+ <LinkTab
143
+ controller={controller}
144
+ extras={linkTabExtras}
145
+ accessRequests={accessRequests}
146
+ />
136
147
  ) : null}
137
148
  {!controller.tabsEnabled || controller.activeTab === "invite" ? (
138
149
  <InviteTab
139
150
  controller={controller}
140
151
  showVisibility={!controller.tabsEnabled}
152
+ accessRequests={accessRequests}
141
153
  />
142
154
  ) : null}
143
155
  {controller.tabsEnabled && controller.activeTab === "embed"
@@ -184,9 +196,11 @@ function TabTrigger({
184
196
  function LinkTab({
185
197
  controller,
186
198
  extras,
199
+ accessRequests,
187
200
  }: {
188
201
  controller: ShareDialogController;
189
202
  extras?: ReactNode;
203
+ accessRequests: ReactNode;
190
204
  }) {
191
205
  const Icon = VIS_ICONS[controller.visibility.value];
192
206
  return (
@@ -218,7 +232,11 @@ function LinkTab({
218
232
  </div>
219
233
  </div>
220
234
  </div>
221
- <InviteTab controller={controller} showVisibility={false} />
235
+ <InviteTab
236
+ controller={controller}
237
+ showVisibility={false}
238
+ accessRequests={accessRequests}
239
+ />
222
240
  {extras}
223
241
  </div>
224
242
  );
@@ -227,9 +245,11 @@ function LinkTab({
227
245
  function InviteTab({
228
246
  controller,
229
247
  showVisibility,
248
+ accessRequests,
230
249
  }: {
231
250
  controller: ShareDialogController;
232
251
  showVisibility: boolean;
252
+ accessRequests: ReactNode;
233
253
  }) {
234
254
  const Icon = VIS_ICONS[controller.visibility.value];
235
255
  return (
@@ -256,6 +276,8 @@ function InviteTab({
256
276
  </div>
257
277
  ) : null}
258
278
 
279
+ {accessRequests}
280
+
259
281
  <div className="space-y-4">
260
282
  <div className="text-sm font-semibold">
261
283
  {controller.labels.peopleWithAccess}
@@ -1,4 +1,6 @@
1
+ export * from "./AccessRequestApprovalPage.js";
1
2
  export * from "./AgentShareSection.js";
3
+ export * from "./ResourceAccessScreen.js";
2
4
  export * from "./ShareButton.js";
3
5
  export * from "./ShareDialog.js";
4
6
  export * from "./VisibilityBadge.js";
@@ -1065,6 +1065,19 @@
1065
1065
  z-index: 1;
1066
1066
  margin-bottom: -1.875rem;
1067
1067
  padding-bottom: 0 !important;
1068
+ animation: agent-builder-setup-card-enter 220ms var(--ease-out-strong) both;
1069
+ }
1070
+
1071
+ @keyframes agent-builder-setup-card-enter {
1072
+ from {
1073
+ opacity: 0;
1074
+ transform: translateY(0.75rem);
1075
+ }
1076
+
1077
+ to {
1078
+ opacity: 1;
1079
+ transform: translateY(0);
1080
+ }
1068
1081
  }
1069
1082
 
1070
1083
  .agent-builder-setup-card--attached .agent-builder-setup-card__panel {
@@ -1241,6 +1254,10 @@
1241
1254
  }
1242
1255
 
1243
1256
  @media (prefers-reduced-motion: reduce) {
1257
+ .agent-builder-setup-card--attached {
1258
+ animation: none;
1259
+ }
1260
+
1244
1261
  .agent-builder-setup-card--attached.animate-bounce-once {
1245
1262
  animation: none;
1246
1263
  }
@@ -1250,7 +1267,10 @@
1250
1267
  min-height: var(--agent-kit-composer-min-height);
1251
1268
  border-radius: var(--agent-kit-composer-radius);
1252
1269
  border-color: var(--agent-kit-composer-border-color);
1253
- background: var(--agent-kit-raised-surface);
1270
+ background: var(
1271
+ --agent-kit-composer-surface,
1272
+ var(--agent-kit-raised-surface)
1273
+ );
1254
1274
  box-shadow: var(--agent-kit-composer-elevation);
1255
1275
  }
1256
1276
 
@@ -275,17 +275,30 @@ describe("agent-native shell surface tokens", () => {
275
275
  );
276
276
  });
277
277
 
278
- it("gives the composer a quiet boundary in both themes", () => {
278
+ it("uses a subtle dark composer surface and a focused boundary", () => {
279
+ const css = readFileSync(new URL("./agent-native.css", import.meta.url), {
280
+ encoding: "utf8",
281
+ });
282
+ const agentkitCss = readFileSync(
283
+ new URL("../agentkit/react/styles.css", import.meta.url),
284
+ { encoding: "utf8" },
285
+ );
279
286
  const tokens = readFileSync(
280
287
  new URL("./tokens/agent-kit.css", import.meta.url),
281
288
  { encoding: "utf8" },
282
289
  );
283
290
 
284
291
  expect(tokens).toMatch(
285
- /:root\s*\{[\s\S]*?--agent-kit-composer-border-mix: 82%;[\s\S]*?--agent-kit-composer-focus-border-mix: 100%;[\s\S]*?--agent-kit-composer-border-color: color-mix\([\s\S]*?--agent-kit-composer-focus-border-color: color-mix\(/s,
292
+ /:root\s*\{[\s\S]*?--agent-kit-composer-surface: var\(--agent-kit-raised-surface\);[\s\S]*?--agent-kit-composer-border-mix: 82%;[\s\S]*?--agent-kit-composer-focus-border-mix: 100%;[\s\S]*?--agent-kit-composer-border-color: color-mix\([\s\S]*?--agent-kit-composer-focus-border-color: color-mix\(/s,
286
293
  );
287
294
  expect(tokens).toMatch(
288
- /:is\(\.dark, :root\[data-theme="dark"\]:not\(\.light\)\)\s*\{[\s\S]*?--agent-kit-composer-border-mix: 40%;[\s\S]*?--agent-kit-composer-focus-border-mix: 65%;[\s\S]*?--agent-kit-composer-border-color: color-mix\([\s\S]*?--agent-kit-composer-focus-border-color: color-mix\(/s,
295
+ /:is\(\.dark, :root\[data-theme="dark"\]:not\(\.light\)\)\s*\{[\s\S]*?--agent-kit-composer-surface: var\(--agent-kit-subtle-surface\);[\s\S]*?--agent-kit-composer-border-mix: 0%;[\s\S]*?--agent-kit-composer-focus-border-mix: 65%;[\s\S]*?--agent-kit-composer-border-color: color-mix\([\s\S]*?--agent-kit-composer-focus-border-color: color-mix\(/s,
296
+ );
297
+ expect(css).toMatch(
298
+ /background:\s*var\(\s*--agent-kit-composer-surface,\s*var\(--agent-kit-raised-surface\)\s*\);/,
299
+ );
300
+ expect(agentkitCss).toMatch(
301
+ /--agent-kit-composer-surface,\s*var\(--agentkit-surface\)/,
289
302
  );
290
303
  });
291
304
 
@@ -43,6 +43,7 @@
43
43
  --agent-kit-composer-block-padding-end: 1rem;
44
44
  --agent-kit-composer-min-height: 4.75rem;
45
45
  --agent-kit-composer-radius: 1.25rem;
46
+ --agent-kit-composer-surface: var(--agent-kit-raised-surface);
46
47
  --agent-kit-composer-border-mix: 82%;
47
48
  --agent-kit-composer-focus-border-mix: 100%;
48
49
  --agent-kit-composer-border-color: color-mix(
@@ -101,7 +102,8 @@
101
102
  }
102
103
 
103
104
  :is(.dark, :root[data-theme="dark"]:not(.light)) {
104
- --agent-kit-composer-border-mix: 40%;
105
+ --agent-kit-composer-surface: var(--agent-kit-subtle-surface);
106
+ --agent-kit-composer-border-mix: 0%;
105
107
  --agent-kit-composer-focus-border-mix: 65%;
106
108
  --agent-kit-composer-border-color: color-mix(
107
109
  in srgb,
@@ -0,0 +1,277 @@
1
+ // @vitest-environment happy-dom
2
+
3
+ import { readFileSync } from "node:fs";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ import React, { act } from "react";
8
+ import { createRoot, type Root } from "react-dom/client";
9
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
10
+
11
+ import type { ActionButtonProps } from "../design-system/types.js";
12
+ import { ToolkitProvider } from "../provider.js";
13
+ import { AgentSuggestionBar } from "./AgentSuggestionBar.js";
14
+
15
+ const DOCUMENT_SUGGESTIONS = [
16
+ "Summarize this document",
17
+ "Improve this document",
18
+ "Find the action items",
19
+ ];
20
+ const CHIP_WIDTHS: Record<string, number> = {
21
+ "Summarize this document": 155,
22
+ "Improve this document": 144,
23
+ "Find the action items": 144,
24
+ };
25
+ // A 280px agent panel minus the bar's and scroller's inline padding.
26
+ const NARROWEST_SCROLLER_WIDTH = 252;
27
+
28
+ describe("AgentSuggestionBar overflow", () => {
29
+ let container: HTMLDivElement;
30
+ let root: Root;
31
+ let gapStyle: HTMLStyleElement;
32
+ let scrollerWidth: number;
33
+ let scrollOffset: number;
34
+ let resizeCallbacks: Array<() => void>;
35
+ let disconnectObserver: ReturnType<typeof vi.fn>;
36
+ const descriptors = new Map<string, PropertyDescriptor | undefined>();
37
+
38
+ const scroller = () =>
39
+ container.querySelector<HTMLElement>(
40
+ '[data-agent-suggestion-scroller="true"]',
41
+ )!;
42
+ const track = () =>
43
+ container.querySelector<HTMLElement>(
44
+ '[data-agent-suggestion-track="true"]',
45
+ )!;
46
+ const trackWidth = () =>
47
+ Number.parseFloat(
48
+ track().style.getPropertyValue("--agent-suggestion-track-width"),
49
+ );
50
+ const fadeEdges = () => ({
51
+ start: scroller().getAttribute("data-overflow-start"),
52
+ end: scroller().getAttribute("data-overflow-end"),
53
+ });
54
+
55
+ function defineLayout(name: string, descriptor: PropertyDescriptor) {
56
+ descriptors.set(
57
+ name,
58
+ Object.getOwnPropertyDescriptor(HTMLElement.prototype, name),
59
+ );
60
+ Object.defineProperty(HTMLElement.prototype, name, {
61
+ configurable: true,
62
+ ...descriptor,
63
+ });
64
+ }
65
+
66
+ function render(suggestions: string[]) {
67
+ act(() => {
68
+ root.render(
69
+ <AgentSuggestionBar
70
+ ariaLabel="Suggested prompts"
71
+ suggestions={suggestions}
72
+ onSelect={() => {}}
73
+ />,
74
+ );
75
+ });
76
+ }
77
+
78
+ function resizeScroller(width: number) {
79
+ scrollerWidth = width;
80
+ act(() => resizeCallbacks.forEach((callback) => callback()));
81
+ }
82
+
83
+ function scrollTo(offset: number) {
84
+ scrollOffset = offset;
85
+ act(() => {
86
+ scroller().dispatchEvent(new Event("scroll"));
87
+ });
88
+ }
89
+
90
+ beforeEach(() => {
91
+ vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
92
+ scrollerWidth = NARROWEST_SCROLLER_WIDTH;
93
+ scrollOffset = 0;
94
+ resizeCallbacks = [];
95
+ disconnectObserver = vi.fn();
96
+ vi.stubGlobal(
97
+ "ResizeObserver",
98
+ class {
99
+ constructor(callback: () => void) {
100
+ resizeCallbacks.push(callback);
101
+ }
102
+ observe() {}
103
+ disconnect = disconnectObserver;
104
+ },
105
+ );
106
+ vi.stubGlobal("requestAnimationFrame", (callback: FrameRequestCallback) => {
107
+ callback(0);
108
+ return 1;
109
+ });
110
+ vi.stubGlobal("cancelAnimationFrame", () => {});
111
+
112
+ gapStyle = document.createElement("style");
113
+ gapStyle.textContent =
114
+ '[data-agent-suggestion-track="true"] { column-gap: 4px; }';
115
+ document.head.appendChild(gapStyle);
116
+
117
+ defineLayout("getBoundingClientRect", {
118
+ value(this: HTMLElement) {
119
+ const width = CHIP_WIDTHS[this.textContent ?? ""] ?? 0;
120
+ return { width, height: 28, top: 0, left: 0, right: width, bottom: 28 };
121
+ },
122
+ });
123
+ defineLayout("clientWidth", {
124
+ get(this: HTMLElement) {
125
+ return this.dataset.agentSuggestionScroller ? scrollerWidth : 0;
126
+ },
127
+ });
128
+ defineLayout("scrollWidth", {
129
+ get(this: HTMLElement) {
130
+ if (!this.dataset.agentSuggestionScroller) return 0;
131
+ return Math.max(scrollerWidth, trackWidth());
132
+ },
133
+ });
134
+ defineLayout("scrollLeft", {
135
+ get() {
136
+ return scrollOffset;
137
+ },
138
+ set(value: number) {
139
+ scrollOffset = value;
140
+ },
141
+ });
142
+
143
+ container = document.createElement("div");
144
+ document.body.appendChild(container);
145
+ root = createRoot(container);
146
+ });
147
+
148
+ afterEach(() => {
149
+ act(() => root.unmount());
150
+ container.remove();
151
+ gapStyle.remove();
152
+ for (const [name, descriptor] of descriptors) {
153
+ if (descriptor) {
154
+ Object.defineProperty(HTMLElement.prototype, name, descriptor);
155
+ } else {
156
+ delete (HTMLElement.prototype as unknown as Record<string, unknown>)[
157
+ name
158
+ ];
159
+ }
160
+ }
161
+ descriptors.clear();
162
+ vi.unstubAllGlobals();
163
+ });
164
+
165
+ it("wraps document suggestions onto two lines at the narrowest panel", () => {
166
+ render(DOCUMENT_SUGGESTIONS);
167
+
168
+ expect(
169
+ [...container.querySelectorAll("button")].map(
170
+ (button) => button.textContent,
171
+ ),
172
+ ).toEqual(DOCUMENT_SUGGESTIONS);
173
+ expect(track().className).toContain("flex-wrap");
174
+ expect(track().className).toContain("min-w-full");
175
+ // The measured width only lays the chips out in two lines once the track
176
+ // is sized by it; the mocked geometry here cannot show that on its own.
177
+ expect(track().className).toContain("w-(--agent-suggestion-track-width)");
178
+ expect(trackWidth()).toBe(144 + 4 + 144);
179
+ expect(fadeEdges()).toEqual({ start: null, end: "true" });
180
+ });
181
+
182
+ it("fades only the edges that still hide suggestions", () => {
183
+ render(DOCUMENT_SUGGESTIONS);
184
+ const hidden = trackWidth() - NARROWEST_SCROLLER_WIDTH;
185
+
186
+ scrollTo(hidden / 2);
187
+ expect(fadeEdges()).toEqual({ start: "true", end: "true" });
188
+
189
+ scrollTo(hidden);
190
+ expect(fadeEdges()).toEqual({ start: "true", end: null });
191
+ });
192
+
193
+ it("drops the fade once a resize lets every suggestion fit", () => {
194
+ render(DOCUMENT_SUGGESTIONS);
195
+ expect(fadeEdges().end).toBe("true");
196
+
197
+ resizeScroller(480);
198
+ expect(fadeEdges()).toEqual({ start: null, end: null });
199
+
200
+ resizeScroller(NARROWEST_SCROLLER_WIDTH);
201
+ expect(fadeEdges()).toEqual({ start: null, end: "true" });
202
+
203
+ act(() => root.unmount());
204
+ expect(disconnectObserver).toHaveBeenCalled();
205
+ root = createRoot(container);
206
+ });
207
+
208
+ it("scrolls a keyboard-focused suggestion out from under the fade", () => {
209
+ const scrollIntoView = vi.fn();
210
+ defineLayout("scrollIntoView", { value: scrollIntoView });
211
+ render(DOCUMENT_SUGGESTIONS);
212
+ const lastChip = [...container.querySelectorAll("button")].at(-1)!;
213
+
214
+ act(() => lastChip.focus());
215
+
216
+ expect(scrollIntoView).toHaveBeenCalledWith({
217
+ block: "nearest",
218
+ inline: "nearest",
219
+ });
220
+ });
221
+
222
+ it("reveals a keyboard-focused suggestion rendered by a host's ActionButton", () => {
223
+ const scrollIntoView = vi.fn();
224
+ defineLayout("scrollIntoView", { value: scrollIntoView });
225
+ // Adapters receive only the props Button forwards, which omit onFocus.
226
+ const HostActionButton = ({
227
+ label,
228
+ onClick,
229
+ elementRef,
230
+ }: ActionButtonProps) => (
231
+ <button ref={elementRef} type="button" onClick={onClick}>
232
+ {label}
233
+ </button>
234
+ );
235
+ act(() => {
236
+ root.render(
237
+ <ToolkitProvider
238
+ designSystem={{ components: { ActionButton: HostActionButton } }}
239
+ >
240
+ <AgentSuggestionBar
241
+ ariaLabel="Suggested prompts"
242
+ suggestions={DOCUMENT_SUGGESTIONS}
243
+ onSelect={() => {}}
244
+ />
245
+ </ToolkitProvider>,
246
+ );
247
+ });
248
+ const lastChip = [...container.querySelectorAll("button")].at(-1)!;
249
+
250
+ act(() => lastChip.focus());
251
+
252
+ expect(scrollIntoView).toHaveBeenCalledWith({
253
+ block: "nearest",
254
+ inline: "nearest",
255
+ });
256
+ });
257
+
258
+ it("ships the fade and wrap styles for every host", () => {
259
+ const styles = readFileSync(
260
+ path.resolve(
261
+ path.dirname(fileURLToPath(import.meta.url)),
262
+ "../app/agentkit/react/styles.css",
263
+ ),
264
+ "utf8",
265
+ ).replace(/\r\n/g, "\n");
266
+
267
+ expect(styles).toMatch(
268
+ /\[data-agent-suggestion-scroller="true"\]:is\(\s*\[data-overflow-start\],\s*\[data-overflow-end\]\s*\) \{[\s\S]*?mask-image: linear-gradient\(/,
269
+ );
270
+ expect(styles).toMatch(
271
+ /\[data-agent-suggestion-scroller="true"\]\[data-overflow-end\] \{\n --agent-suggestion-fade-end: 1\.5rem;/,
272
+ );
273
+ expect(styles).toMatch(
274
+ /\.agentkit-suggestions \[data-agent-suggestion-track="true"\] \{[\s\S]*?flex-wrap: wrap;[\s\S]*?inline-size: var\(--agent-suggestion-track-width\);[\s\S]*?min-inline-size: 100%;/,
275
+ );
276
+ });
277
+ });
@@ -1,8 +1,18 @@
1
- import type { ReactNode } from "react";
1
+ import {
2
+ useEffect,
3
+ useLayoutEffect,
4
+ useRef,
5
+ useState,
6
+ type FocusEvent,
7
+ type ReactNode,
8
+ } from "react";
2
9
 
3
10
  import { Button } from "../ui/button.js";
4
11
  import { cn } from "../utils.js";
5
12
 
13
+ const useBrowserLayoutEffect =
14
+ typeof window === "undefined" ? useEffect : useLayoutEffect;
15
+
6
16
  export interface AgentSuggestionItem {
7
17
  id: string;
8
18
  label: string;
@@ -41,6 +51,90 @@ export function agentSuggestionPrompt(
41
51
  : (suggestion.prompt ?? suggestion.label);
42
52
  }
43
53
 
54
+ function twoLineTrackWidth(widths: readonly number[], gap: number): number {
55
+ const total =
56
+ widths.reduce((sum, width) => sum + width, 0) +
57
+ gap * Math.max(widths.length - 1, 0);
58
+ let narrowest = total;
59
+ let firstLine = -gap;
60
+ for (const width of widths.slice(0, -1)) {
61
+ firstLine += gap + width;
62
+ narrowest = Math.min(
63
+ narrowest,
64
+ Math.max(firstLine, total - firstLine - gap),
65
+ );
66
+ }
67
+ return narrowest;
68
+ }
69
+
70
+ // Chrome does not scroll a partly visible chip into view when it takes
71
+ // keyboard focus, which leaves it under the edge fade. Listen on the track:
72
+ // a host's ActionButton adapter receives only the props Button forwards, and
73
+ // onFocus is not one of them.
74
+ function revealKeyboardFocusedChip(event: FocusEvent<HTMLDivElement>) {
75
+ const chip = event.target;
76
+ if (!(chip instanceof HTMLElement) || !chip.matches(":focus-visible")) return;
77
+ chip.scrollIntoView({ block: "nearest", inline: "nearest" });
78
+ }
79
+
80
+ function useTwoLineSuggestionTrack(layoutKey: string) {
81
+ const scrollerRef = useRef<HTMLDivElement>(null);
82
+ const trackRef = useRef<HTMLDivElement>(null);
83
+ const [overflow, setOverflow] = useState({ start: false, end: false });
84
+
85
+ useBrowserLayoutEffect(() => {
86
+ const scroller = scrollerRef.current;
87
+ const track = trackRef.current;
88
+ if (!scroller || !track) return;
89
+ const chips = Array.from(track.children);
90
+ const updateOverflow = () => {
91
+ const offset = Math.abs(scroller.scrollLeft);
92
+ const start = offset > 1;
93
+ const end = offset + scroller.clientWidth < scroller.scrollWidth - 1;
94
+ setOverflow((current) =>
95
+ current.start === start && current.end === end
96
+ ? current
97
+ : { start, end },
98
+ );
99
+ };
100
+ const update = () => {
101
+ const gap = Number.parseFloat(getComputedStyle(track).columnGap);
102
+ const width = twoLineTrackWidth(
103
+ chips.map((chip) => chip.getBoundingClientRect().width),
104
+ Number.isNaN(gap) ? 0 : gap,
105
+ );
106
+ track.style.setProperty(
107
+ "--agent-suggestion-track-width",
108
+ `${Math.ceil(width)}px`,
109
+ );
110
+ updateOverflow();
111
+ };
112
+ update();
113
+
114
+ // Resizing the track inside the observer callback resizes the observed
115
+ // scroller in the same frame, which the browser reports as a
116
+ // ResizeObserver loop error. Measure on the next frame instead.
117
+ let frame = 0;
118
+ const observer =
119
+ typeof ResizeObserver === "undefined"
120
+ ? null
121
+ : new ResizeObserver(() => {
122
+ cancelAnimationFrame(frame);
123
+ frame = requestAnimationFrame(update);
124
+ });
125
+ observer?.observe(scroller);
126
+ for (const chip of chips) observer?.observe(chip);
127
+ scroller.addEventListener("scroll", updateOverflow, { passive: true });
128
+ return () => {
129
+ cancelAnimationFrame(frame);
130
+ observer?.disconnect();
131
+ scroller.removeEventListener("scroll", updateOverflow);
132
+ };
133
+ }, [layoutKey]);
134
+
135
+ return { scrollerRef, trackRef, overflow };
136
+ }
137
+
44
138
  export function AgentSuggestionBar({
45
139
  suggestions,
46
140
  ariaLabel,
@@ -48,7 +142,11 @@ export function AgentSuggestionBar({
48
142
  renderSuggestion,
49
143
  className,
50
144
  }: AgentSuggestionBarProps) {
51
- if (suggestions.length === 0) return null;
145
+ const items = suggestions.map(normalizeAgentSuggestion);
146
+ const { scrollerRef, trackRef, overflow } = useTwoLineSuggestionTrack(
147
+ items.map((item) => item.id).join("\n"),
148
+ );
149
+ if (items.length === 0) return null;
52
150
 
53
151
  return (
54
152
  <section
@@ -57,12 +155,19 @@ export function AgentSuggestionBar({
57
155
  className={cn("w-full min-w-0 overflow-hidden px-3 py-2", className)}
58
156
  >
59
157
  <div
158
+ ref={scrollerRef}
60
159
  data-agent-suggestion-scroller="true"
61
- className="flex w-full min-w-0 snap-x snap-proximity gap-1 overflow-x-auto overscroll-x-contain px-0.5 py-px [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
160
+ data-overflow-start={overflow.start || undefined}
161
+ data-overflow-end={overflow.end || undefined}
162
+ className="w-full min-w-0 snap-x snap-proximity scroll-px-6 overflow-x-auto overscroll-x-contain px-0.5 py-px [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
62
163
  >
63
- {suggestions.map((input, index) => {
64
- const suggestion = normalizeAgentSuggestion(input, index);
65
- return (
164
+ <div
165
+ ref={trackRef}
166
+ data-agent-suggestion-track="true"
167
+ className="flex w-(--agent-suggestion-track-width) min-w-full flex-wrap gap-1"
168
+ onFocus={revealKeyboardFocusedChip}
169
+ >
170
+ {items.map((suggestion) => (
66
171
  <Button
67
172
  key={suggestion.id}
68
173
  type="button"
@@ -78,8 +183,8 @@ export function AgentSuggestionBar({
78
183
  : suggestion.label}
79
184
  </span>
80
185
  </Button>
81
- );
82
- })}
186
+ ))}
187
+ </div>
83
188
  </div>
84
189
  </section>
85
190
  );
@@ -0,0 +1,6 @@
1
+ /// <reference types="@webgpu/types" />
2
+
3
+ declare module "*.wgsl?raw" {
4
+ const source: string;
5
+ export default source;
6
+ }