@agent-native/core 0.98.3 → 0.98.5

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 (230) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +29 -0
  3. package/corpus/core/docs/content/durable-background-runs.mdx +37 -0
  4. package/corpus/core/docs/content/locales/ar-SA/tracking.mdx +2 -0
  5. package/corpus/core/docs/content/locales/de-DE/tracking.mdx +2 -0
  6. package/corpus/core/docs/content/locales/es-ES/tracking.mdx +2 -0
  7. package/corpus/core/docs/content/locales/fr-FR/tracking.mdx +4 -2
  8. package/corpus/core/docs/content/locales/hi-IN/tracking.mdx +2 -0
  9. package/corpus/core/docs/content/locales/ja-JP/tracking.mdx +2 -0
  10. package/corpus/core/docs/content/locales/ko-KR/tracking.mdx +2 -0
  11. package/corpus/core/docs/content/locales/pt-BR/tracking.mdx +2 -0
  12. package/corpus/core/docs/content/locales/zh-CN/tracking.mdx +2 -0
  13. package/corpus/core/docs/content/locales/zh-TW/durable-background-runs.mdx +14 -0
  14. package/corpus/core/docs/content/locales/zh-TW/tracking.mdx +2 -0
  15. package/corpus/core/docs/content/tracking.mdx +2 -0
  16. package/corpus/core/package.json +1 -1
  17. package/corpus/core/src/agent/durable-background.ts +16 -16
  18. package/corpus/core/src/agent/engine/ai-sdk-engine.ts +25 -8
  19. package/corpus/core/src/agent/engine/anthropic-engine.ts +17 -3
  20. package/corpus/core/src/agent/engine/output-tokens.ts +6 -6
  21. package/corpus/core/src/agent/production-agent.ts +200 -27
  22. package/corpus/core/src/agent/run-manager.ts +81 -1
  23. package/corpus/core/src/agent/run-store.ts +301 -41
  24. package/corpus/core/src/client/AgentPanel.tsx +6 -2
  25. package/corpus/core/src/client/AssistantChat.tsx +364 -69
  26. package/corpus/core/src/client/agent-chat-adapter.ts +169 -1
  27. package/corpus/core/src/client/analytics.ts +12 -0
  28. package/corpus/core/src/client/chat/message-components.tsx +3 -0
  29. package/corpus/core/src/client/chat/tool-call-display.tsx +20 -36
  30. package/corpus/core/src/client/conversation/use-near-bottom-autoscroll.ts +7 -0
  31. package/corpus/core/src/client/extensions/AgentNativeExtensionFrame.tsx +2 -0
  32. package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +2 -0
  33. package/corpus/core/src/client/extensions/ExtensionEditor.tsx +9 -2
  34. package/corpus/core/src/client/extensions/ExtensionViewer.tsx +2 -0
  35. package/corpus/core/src/client/extensions/InlineExtensionFrame.tsx +2 -0
  36. package/corpus/core/src/client/extensions/portable-extension.ts +2 -0
  37. package/corpus/core/src/client/session-replay.ts +971 -53
  38. package/corpus/core/src/client/settings/DemoModeSection.tsx +3 -3
  39. package/corpus/core/src/client/sse-event-processor.ts +43 -1
  40. package/corpus/core/src/demo/actions/toggle-demo-mode.ts +2 -2
  41. package/corpus/core/src/demo/config.ts +6 -6
  42. package/corpus/core/src/demo/fetch-interceptor.ts +34 -2
  43. package/corpus/core/src/demo/redact.ts +71 -158
  44. package/corpus/core/src/extensions/html-shell.ts +3 -0
  45. package/corpus/core/src/extensions/session-replay-iframe.ts +131 -0
  46. package/corpus/core/src/scripts/agent-engines/list-agent-engines.ts +1 -20
  47. package/corpus/core/src/session-replay-iframe-protocol.ts +58 -0
  48. package/corpus/core/src/shared/reasoning-effort.ts +35 -0
  49. package/corpus/core/src/styles/agent-conversation.css +26 -0
  50. package/corpus/core/src/templates/workspace-core/.agents/skills/observability/SKILL.md +0 -15
  51. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +67 -16
  52. package/corpus/templates/analytics/AGENTS.md +12 -0
  53. package/corpus/templates/analytics/actions/get-error-issue.ts +1 -0
  54. package/corpus/templates/analytics/actions/get-session-replay-events.ts +1 -0
  55. package/corpus/templates/analytics/actions/get-session-replay-summary.ts +1 -0
  56. package/corpus/templates/analytics/actions/list-error-issues.ts +1 -0
  57. package/corpus/templates/analytics/actions/list-session-recordings.ts +1 -0
  58. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +2 -1
  59. package/corpus/templates/analytics/app/global.css +6 -64
  60. package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +41 -4
  61. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +143 -121
  62. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +34 -12
  63. package/corpus/templates/analytics/changelog/2026-07-12-analytics-chat-retries-before-showing-a-generic-no-data-mess.md +6 -0
  64. package/corpus/templates/analytics/changelog/2026-07-12-analytics-sessions-search-no-longer-loses-fast-keystrokes-an.md +6 -0
  65. package/corpus/templates/analytics/changelog/2026-07-12-chart-selection-respects-chat-state.md +6 -0
  66. package/corpus/templates/analytics/changelog/2026-07-12-demo-mode-anonymizes-error-reporting-emails.md +6 -0
  67. package/corpus/templates/analytics/changelog/2026-07-12-fixed-session-replays-that-appeared-ultra-wide-hid-recorded-.md +6 -0
  68. package/corpus/templates/analytics/changelog/2026-07-12-session-replays-load-faster-in-demo-mode-while-visitor-email.md +6 -0
  69. package/corpus/templates/analytics/changelog/2026-07-12-session-replays-show-iframe-content.md +6 -0
  70. package/corpus/templates/analytics/netlify.toml +0 -3
  71. package/corpus/templates/analytics/server/lib/analytics-connector-catalog.ts +14 -0
  72. package/corpus/templates/analytics/server/lib/error-capture.ts +59 -2
  73. package/corpus/templates/analytics/server/lib/real-data-actions.ts +24 -0
  74. package/corpus/templates/analytics/server/lib/session-replay.ts +2 -9
  75. package/corpus/templates/analytics/server/plugins/agent-chat.ts +39 -6
  76. package/corpus/templates/assets/app/global.css +1 -1
  77. package/corpus/templates/brain/app/global.css +2 -2
  78. package/corpus/templates/brain/changelog/2026-07-12-fix-long-response-progress.md +6 -0
  79. package/corpus/templates/calendar/app/components/calendar/DeleteEventDialog.tsx +11 -2
  80. package/corpus/templates/calendar/app/components/calendar/EventAttendeesSection.tsx +14 -2
  81. package/corpus/templates/calendar/changelog/2026-07-12-calendar-responses-can-now-be-saved-with-command-enter-or-ct.md +6 -0
  82. package/corpus/templates/forms/app/components/CloudUpgrade.tsx +1 -1
  83. package/corpus/templates/forms/app/components/ThemeToggle.tsx +22 -6
  84. package/corpus/templates/forms/app/components/builder/FieldPropertiesPanel.tsx +5 -5
  85. package/corpus/templates/forms/app/components/builder/FieldRenderer.tsx +3 -3
  86. package/corpus/templates/forms/app/components/layout/Sidebar.tsx +16 -16
  87. package/corpus/templates/forms/app/global.css +110 -5
  88. package/corpus/templates/forms/app/pages/AskPage.tsx +3 -3
  89. package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +134 -52
  90. package/corpus/templates/forms/app/pages/FormFillPage.tsx +44 -11
  91. package/corpus/templates/forms/app/pages/FormsListPage.tsx +42 -24
  92. package/corpus/templates/forms/app/pages/ResponseInsightsPage.tsx +28 -15
  93. package/corpus/templates/forms/app/pages/ResponsesPage.tsx +38 -15
  94. package/corpus/templates/forms/app/routes/_app.settings.tsx +1 -1
  95. package/corpus/templates/forms/changelog/2026-07-12-form-builder-surfaces-are-calmer-fields-stay-stable-on-hover.md +6 -0
  96. package/corpus/templates/forms/changelog/2026-07-12-smoother-more-tactile-controls-throughout-animated-icon-and-.md +6 -0
  97. package/corpus/templates/mail/app/components/email/EmailThread.tsx +21 -13
  98. package/corpus/templates/mail/app/components/email/email-iframe-document.ts +18 -0
  99. package/dist/agent/durable-background.d.ts +10 -10
  100. package/dist/agent/durable-background.d.ts.map +1 -1
  101. package/dist/agent/durable-background.js +16 -16
  102. package/dist/agent/durable-background.js.map +1 -1
  103. package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
  104. package/dist/agent/engine/ai-sdk-engine.js +19 -8
  105. package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
  106. package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
  107. package/dist/agent/engine/anthropic-engine.js +11 -3
  108. package/dist/agent/engine/anthropic-engine.js.map +1 -1
  109. package/dist/agent/engine/output-tokens.js +6 -6
  110. package/dist/agent/engine/output-tokens.js.map +1 -1
  111. package/dist/agent/production-agent.d.ts +52 -0
  112. package/dist/agent/production-agent.d.ts.map +1 -1
  113. package/dist/agent/production-agent.js +154 -26
  114. package/dist/agent/production-agent.js.map +1 -1
  115. package/dist/agent/run-manager.d.ts +9 -0
  116. package/dist/agent/run-manager.d.ts.map +1 -1
  117. package/dist/agent/run-manager.js +71 -1
  118. package/dist/agent/run-manager.js.map +1 -1
  119. package/dist/agent/run-store.d.ts +9 -0
  120. package/dist/agent/run-store.d.ts.map +1 -1
  121. package/dist/agent/run-store.js +243 -48
  122. package/dist/agent/run-store.js.map +1 -1
  123. package/dist/client/AgentPanel.d.ts.map +1 -1
  124. package/dist/client/AgentPanel.js +6 -2
  125. package/dist/client/AgentPanel.js.map +1 -1
  126. package/dist/client/AssistantChat.d.ts +21 -0
  127. package/dist/client/AssistantChat.d.ts.map +1 -1
  128. package/dist/client/AssistantChat.js +298 -67
  129. package/dist/client/AssistantChat.js.map +1 -1
  130. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  131. package/dist/client/agent-chat-adapter.js +146 -1
  132. package/dist/client/agent-chat-adapter.js.map +1 -1
  133. package/dist/client/analytics.d.ts.map +1 -1
  134. package/dist/client/analytics.js +11 -0
  135. package/dist/client/analytics.js.map +1 -1
  136. package/dist/client/chat/message-components.d.ts.map +1 -1
  137. package/dist/client/chat/message-components.js +2 -1
  138. package/dist/client/chat/message-components.js.map +1 -1
  139. package/dist/client/chat/tool-call-display.d.ts +3 -1
  140. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  141. package/dist/client/chat/tool-call-display.js +13 -37
  142. package/dist/client/chat/tool-call-display.js.map +1 -1
  143. package/dist/client/conversation/use-near-bottom-autoscroll.d.ts +1 -0
  144. package/dist/client/conversation/use-near-bottom-autoscroll.d.ts.map +1 -1
  145. package/dist/client/conversation/use-near-bottom-autoscroll.js +6 -0
  146. package/dist/client/conversation/use-near-bottom-autoscroll.js.map +1 -1
  147. package/dist/client/extensions/AgentNativeExtensionFrame.d.ts.map +1 -1
  148. package/dist/client/extensions/AgentNativeExtensionFrame.js +2 -1
  149. package/dist/client/extensions/AgentNativeExtensionFrame.js.map +1 -1
  150. package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
  151. package/dist/client/extensions/EmbeddedExtension.js +2 -1
  152. package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
  153. package/dist/client/extensions/ExtensionEditor.d.ts.map +1 -1
  154. package/dist/client/extensions/ExtensionEditor.js +5 -2
  155. package/dist/client/extensions/ExtensionEditor.js.map +1 -1
  156. package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
  157. package/dist/client/extensions/ExtensionViewer.js +2 -1
  158. package/dist/client/extensions/ExtensionViewer.js.map +1 -1
  159. package/dist/client/extensions/InlineExtensionFrame.d.ts.map +1 -1
  160. package/dist/client/extensions/InlineExtensionFrame.js +2 -1
  161. package/dist/client/extensions/InlineExtensionFrame.js.map +1 -1
  162. package/dist/client/extensions/portable-extension.d.ts.map +1 -1
  163. package/dist/client/extensions/portable-extension.js +2 -0
  164. package/dist/client/extensions/portable-extension.js.map +1 -1
  165. package/dist/client/session-replay.d.ts +12 -3
  166. package/dist/client/session-replay.d.ts.map +1 -1
  167. package/dist/client/session-replay.js +793 -49
  168. package/dist/client/session-replay.js.map +1 -1
  169. package/dist/client/settings/DemoModeSection.js +1 -1
  170. package/dist/client/settings/DemoModeSection.js.map +1 -1
  171. package/dist/client/sse-event-processor.d.ts +5 -0
  172. package/dist/client/sse-event-processor.d.ts.map +1 -1
  173. package/dist/client/sse-event-processor.js +37 -1
  174. package/dist/client/sse-event-processor.js.map +1 -1
  175. package/dist/demo/actions/toggle-demo-mode.js +2 -2
  176. package/dist/demo/actions/toggle-demo-mode.js.map +1 -1
  177. package/dist/demo/config.js +6 -6
  178. package/dist/demo/config.js.map +1 -1
  179. package/dist/demo/fetch-interceptor.d.ts +1 -0
  180. package/dist/demo/fetch-interceptor.d.ts.map +1 -1
  181. package/dist/demo/fetch-interceptor.js +28 -2
  182. package/dist/demo/fetch-interceptor.js.map +1 -1
  183. package/dist/demo/redact.d.ts +14 -7
  184. package/dist/demo/redact.d.ts.map +1 -1
  185. package/dist/demo/redact.js +27 -155
  186. package/dist/demo/redact.js.map +1 -1
  187. package/dist/extensions/html-shell.d.ts.map +1 -1
  188. package/dist/extensions/html-shell.js +2 -0
  189. package/dist/extensions/html-shell.js.map +1 -1
  190. package/dist/extensions/session-replay-iframe.d.ts +10 -0
  191. package/dist/extensions/session-replay-iframe.d.ts.map +1 -0
  192. package/dist/extensions/session-replay-iframe.js +121 -0
  193. package/dist/extensions/session-replay-iframe.js.map +1 -0
  194. package/dist/notifications/routes.d.ts +1 -1
  195. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  196. package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
  197. package/dist/scripts/agent-engines/list-agent-engines.js +1 -19
  198. package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
  199. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  200. package/dist/server/transcribe-voice.d.ts +1 -1
  201. package/dist/session-replay-iframe-protocol.d.ts +36 -0
  202. package/dist/session-replay-iframe-protocol.d.ts.map +1 -0
  203. package/dist/session-replay-iframe-protocol.js +24 -0
  204. package/dist/session-replay-iframe-protocol.js.map +1 -0
  205. package/dist/shared/reasoning-effort.d.ts +11 -0
  206. package/dist/shared/reasoning-effort.d.ts.map +1 -1
  207. package/dist/shared/reasoning-effort.js +36 -0
  208. package/dist/shared/reasoning-effort.js.map +1 -1
  209. package/dist/styles/agent-conversation.css +26 -0
  210. package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +0 -15
  211. package/docs/content/durable-background-runs.mdx +37 -0
  212. package/docs/content/locales/ar-SA/tracking.mdx +2 -0
  213. package/docs/content/locales/de-DE/tracking.mdx +2 -0
  214. package/docs/content/locales/es-ES/tracking.mdx +2 -0
  215. package/docs/content/locales/fr-FR/tracking.mdx +4 -2
  216. package/docs/content/locales/hi-IN/tracking.mdx +2 -0
  217. package/docs/content/locales/ja-JP/tracking.mdx +2 -0
  218. package/docs/content/locales/ko-KR/tracking.mdx +2 -0
  219. package/docs/content/locales/pt-BR/tracking.mdx +2 -0
  220. package/docs/content/locales/zh-CN/tracking.mdx +2 -0
  221. package/docs/content/locales/zh-TW/durable-background-runs.mdx +14 -0
  222. package/docs/content/locales/zh-TW/tracking.mdx +2 -0
  223. package/docs/content/tracking.mdx +2 -0
  224. package/package.json +2 -2
  225. package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +0 -15
  226. package/corpus/core/src/observability/hosted-model-experiment.ts +0 -118
  227. package/dist/observability/hosted-model-experiment.d.ts +0 -39
  228. package/dist/observability/hosted-model-experiment.d.ts.map +0 -1
  229. package/dist/observability/hosted-model-experiment.js +0 -90
  230. package/dist/observability/hosted-model-experiment.js.map +0 -1
@@ -187,8 +187,8 @@ export function FormFillPage() {
187
187
 
188
188
  if (isLoading) {
189
189
  return (
190
- <div className="min-h-screen bg-background py-10 px-4">
191
- <div className="max-w-xl mx-auto space-y-6">
190
+ <div className="min-h-screen bg-muted/30 px-4 py-10">
191
+ <div className="mx-auto max-w-xl space-y-6 rounded-2xl border border-border/80 bg-background p-5 shadow-sm sm:p-8">
192
192
  <div className="space-y-3">
193
193
  <Skeleton className="h-8 w-2/3" />
194
194
  <Skeleton className="h-4 w-full" />
@@ -210,8 +210,19 @@ export function FormFillPage() {
210
210
 
211
211
  if (error || !form) {
212
212
  return (
213
- <div className="min-h-screen bg-background flex items-center justify-center p-4">
214
- <div className="text-center">
213
+ <div
214
+ className={cn(
215
+ "flex min-h-screen items-center justify-center p-4",
216
+ embedded ? "bg-background" : "bg-muted/30",
217
+ )}
218
+ >
219
+ <div
220
+ className={cn(
221
+ "text-center",
222
+ !embedded &&
223
+ "max-w-md rounded-2xl border border-border/80 bg-background p-8 shadow-sm sm:p-10",
224
+ )}
225
+ >
215
226
  <h1 className="text-2xl font-semibold mb-2">
216
227
  {t("publicForm.formNotFound")}
217
228
  </h1>
@@ -222,7 +233,7 @@ export function FormFillPage() {
222
233
  variant="outline"
223
234
  size="sm"
224
235
  onClick={() => window.location.reload()}
225
- className="gap-2"
236
+ className="min-h-10 gap-2 transition-[scale,background-color,border-color,color,box-shadow] duration-150 active:scale-[0.96] motion-reduce:transition-none"
226
237
  >
227
238
  <IconRefresh className="h-3.5 w-3.5" />
228
239
  {t("publicForm.tryAgain")}
@@ -235,8 +246,19 @@ export function FormFillPage() {
235
246
 
236
247
  if (submitted) {
237
248
  return (
238
- <div className="min-h-screen bg-background flex items-center justify-center p-4">
239
- <div className="text-center max-w-md">
249
+ <div
250
+ className={cn(
251
+ "flex min-h-screen items-center justify-center p-4",
252
+ embedded ? "bg-background" : "bg-muted/30",
253
+ )}
254
+ >
255
+ <div
256
+ className={cn(
257
+ "max-w-md text-center",
258
+ !embedded &&
259
+ "rounded-2xl border border-border/80 bg-background p-8 shadow-sm sm:p-10",
260
+ )}
261
+ >
240
262
  <div className="mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-full bg-emerald-600/10">
241
263
  <IconCircleCheck className="h-7 w-7 text-emerald-600 dark:text-emerald-400" />
242
264
  </div>
@@ -254,11 +276,22 @@ export function FormFillPage() {
254
276
  }
255
277
 
256
278
  return (
257
- <div className="min-h-screen bg-background flex items-center justify-center p-3 sm:p-4 py-8 sm:py-12">
258
- <div className="w-full max-w-2xl">
279
+ <div
280
+ className={cn(
281
+ "flex min-h-screen items-center justify-center p-3 sm:p-4 py-8 sm:py-12",
282
+ embedded ? "bg-background" : "bg-muted/30",
283
+ )}
284
+ >
285
+ <div
286
+ className={cn(
287
+ "w-full max-w-2xl",
288
+ !embedded &&
289
+ "rounded-2xl border border-border/80 bg-background p-5 shadow-sm sm:p-8",
290
+ )}
291
+ >
259
292
  {!embedded && (
260
293
  <div className="mb-3 flex justify-end">
261
- <ThemeToggle className="h-8 w-8" />
294
+ <ThemeToggle className="h-10 w-10 transition-[scale,background-color,color,box-shadow] duration-150 active:scale-[0.96] motion-reduce:transition-none" />
262
295
  </div>
263
296
  )}
264
297
  {/* Form header */}
@@ -320,7 +353,7 @@ export function FormFillPage() {
320
353
 
321
354
  <Button
322
355
  type="submit"
323
- className="mt-4 w-full sm:w-auto"
356
+ className="mt-4 min-h-11 w-full transition-[scale,background-color,border-color,color,box-shadow] duration-150 active:scale-[0.96] motion-reduce:transition-none sm:w-auto"
324
357
  size="lg"
325
358
  disabled={submitForm.isPending}
326
359
  >
@@ -118,7 +118,7 @@ export function FormsListPage() {
118
118
  <Button
119
119
  onClick={handleCreate}
120
120
  size="sm"
121
- className="shrink-0 cursor-pointer"
121
+ className="min-h-10 shrink-0 cursor-pointer active:scale-[0.96] transition-[background-color,box-shadow,transform]"
122
122
  >
123
123
  <IconPlus className="h-3.5 w-3.5" />
124
124
  <span className="hidden sm:inline">{t("forms.newForm")}</span>
@@ -269,7 +269,7 @@ export function FormsListPage() {
269
269
  if (isLoading) {
270
270
  return (
271
271
  <div className="p-3 sm:p-6 max-w-5xl mx-auto">
272
- <div className="overflow-hidden rounded-lg border border-border bg-card">
272
+ <div className="forms-list-shell overflow-hidden bg-card">
273
273
  {Array.from({ length: 6 }).map((_, i) => (
274
274
  <div
275
275
  key={i}
@@ -280,8 +280,10 @@ export function FormsListPage() {
280
280
  <Skeleton className="h-3 w-1/2" />
281
281
  </div>
282
282
  <Skeleton className="h-5 w-20 rounded-full" />
283
+ <Skeleton className="h-5 w-20 rounded-full" />
284
+ <Skeleton className="h-4 w-24" />
283
285
  <Skeleton className="h-4 w-24" />
284
- <Skeleton className="h-8 w-8 rounded-md md:ms-auto" />
286
+ <Skeleton className="h-10 w-10 rounded-lg md:ms-auto" />
285
287
  </div>
286
288
  ))}
287
289
  </div>
@@ -300,6 +302,7 @@ export function FormsListPage() {
300
302
  <Button
301
303
  variant="outline"
302
304
  size="sm"
305
+ className="min-h-10 active:scale-[0.96] transition-[background-color,box-shadow,transform]"
303
306
  onClick={() => {
304
307
  const next = encodeURIComponent(
305
308
  window.location.pathname + window.location.search,
@@ -322,8 +325,8 @@ export function FormsListPage() {
322
325
  <Button
323
326
  variant="outline"
324
327
  size="sm"
328
+ className="min-h-10 gap-2 active:scale-[0.96] transition-[background-color,box-shadow,transform]"
325
329
  onClick={() => refetch()}
326
- className="gap-2"
327
330
  >
328
331
  <IconRefresh className="h-3.5 w-3.5" />
329
332
  {t("common.retry")}
@@ -344,11 +347,17 @@ export function FormsListPage() {
344
347
  value={view}
345
348
  onValueChange={(v) => setView(v as "active" | "archive")}
346
349
  >
347
- <TabsList>
348
- <TabsTrigger value="active" className="text-xs gap-1.5">
350
+ <TabsList className="h-12">
351
+ <TabsTrigger
352
+ value="active"
353
+ className="min-h-10 gap-1.5 text-xs active:scale-[0.96] transition-[background-color,box-shadow,color,transform]"
354
+ >
349
355
  {t("header.forms")}
350
356
  </TabsTrigger>
351
- <TabsTrigger value="archive" className="text-xs gap-1.5">
357
+ <TabsTrigger
358
+ value="archive"
359
+ className="min-h-10 gap-1.5 text-xs active:scale-[0.96] transition-[background-color,box-shadow,color,transform]"
360
+ >
352
361
  <IconArchive className="h-3.5 w-3.5" />
353
362
  {t("forms.archive")}
354
363
  </TabsTrigger>
@@ -359,7 +368,7 @@ export function FormsListPage() {
359
368
  <Button
360
369
  variant={selectionMode ? "secondary" : "ghost"}
361
370
  size="sm"
362
- className="h-8 gap-1.5 text-xs"
371
+ className="min-h-10 gap-1.5 text-xs active:scale-[0.96] transition-[background-color,box-shadow,transform]"
363
372
  onClick={() => {
364
373
  setSelectionMode((current) => {
365
374
  if (current) setSelectedIds(new Set());
@@ -374,7 +383,7 @@ export function FormsListPage() {
374
383
  </div>
375
384
 
376
385
  {selectionMode && forms.length > 0 && (
377
- <div className="mb-4 flex flex-wrap items-center gap-2 rounded-lg border border-border bg-muted/30 px-3 py-2">
386
+ <div className="forms-selection-toolbar mb-4 flex flex-wrap items-center gap-2 rounded-xl bg-muted/30 px-3 py-2">
378
387
  <span className="text-xs font-medium text-foreground">
379
388
  {t("forms.selectedCount", {
380
389
  count: selectedCount,
@@ -385,7 +394,7 @@ export function FormsListPage() {
385
394
  <Button
386
395
  variant="ghost"
387
396
  size="sm"
388
- className="h-8 text-xs"
397
+ className="min-h-10 text-xs active:scale-[0.96] transition-[background-color,box-shadow,transform]"
389
398
  onClick={toggleSelectAll}
390
399
  >
391
400
  {allFormsSelected ? t("common.clearAll") : t("common.selectAll")}
@@ -393,7 +402,7 @@ export function FormsListPage() {
393
402
  <Button
394
403
  variant="ghost"
395
404
  size="sm"
396
- className="h-8 gap-1.5 text-xs text-destructive hover:text-destructive"
405
+ className="min-h-10 gap-1.5 text-xs text-destructive hover:text-destructive active:scale-[0.96] transition-[background-color,box-shadow,transform]"
397
406
  onClick={() =>
398
407
  isArchive ? setBulkPurgeOpen(true) : handleBulkDelete(false)
399
408
  }
@@ -405,7 +414,7 @@ export function FormsListPage() {
405
414
  <Button
406
415
  variant="ghost"
407
416
  size="icon"
408
- className="ms-auto h-8 w-8"
417
+ className="relative ms-auto size-10 transition-[background-color,box-shadow,transform] active:scale-[0.96] sm:size-8 sm:before:absolute sm:before:-inset-1 sm:before:content-['']"
409
418
  onClick={clearSelection}
410
419
  aria-label={t("forms.exitSelectionMode")}
411
420
  >
@@ -415,7 +424,7 @@ export function FormsListPage() {
415
424
  )}
416
425
 
417
426
  {forms.length === 0 ? (
418
- <div className="flex flex-col items-center justify-center py-20 border border-dashed border-border rounded-xl">
427
+ <div className="forms-empty-state flex flex-col items-center justify-center rounded-xl border border-dashed border-border bg-card py-20">
419
428
  {isArchive ? (
420
429
  <>
421
430
  <h3 className="font-medium mb-1">
@@ -431,7 +440,11 @@ export function FormsListPage() {
431
440
  <p className="text-sm text-muted-foreground mb-4">
432
441
  {t("forms.emptyDescription")}
433
442
  </p>
434
- <Button onClick={handleCreate} size="sm" className="gap-2">
443
+ <Button
444
+ onClick={handleCreate}
445
+ size="sm"
446
+ className="min-h-10 gap-2 active:scale-[0.96] transition-[background-color,box-shadow,transform]"
447
+ >
435
448
  <IconPlus className="h-4 w-4" />
436
449
  {t("forms.createForm")}
437
450
  </Button>
@@ -439,7 +452,7 @@ export function FormsListPage() {
439
452
  )}
440
453
  </div>
441
454
  ) : (
442
- <div className="overflow-hidden rounded-lg border border-border bg-card">
455
+ <div className="forms-list-shell overflow-hidden bg-card">
443
456
  {forms.map((form: any) => {
444
457
  const selected = selectedIds.has(form.id);
445
458
  const dateLabel =
@@ -515,10 +528,6 @@ export function FormsListPage() {
515
528
  <h3 className="min-w-0 flex-1 truncate text-sm font-medium">
516
529
  {form.title}
517
530
  </h3>
518
- <VisibilityBadge
519
- visibility={(form as any).visibility}
520
- className="shrink-0"
521
- />
522
531
  </div>
523
532
  {form.description && (
524
533
  <p className="mt-0.5 truncate text-xs text-muted-foreground">
@@ -528,6 +537,10 @@ export function FormsListPage() {
528
537
  </div>
529
538
  </div>
530
539
 
540
+ <div className="flex min-w-0 items-center">
541
+ <VisibilityBadge visibility={(form as any).visibility} />
542
+ </div>
543
+
531
544
  <div className="flex items-center md:justify-start">
532
545
  <Badge
533
546
  variant="outline"
@@ -558,7 +571,7 @@ export function FormsListPage() {
558
571
  <Button
559
572
  variant="ghost"
560
573
  size="sm"
561
- className="h-10 w-10 sm:h-8 sm:w-8 p-0 sm:opacity-0 sm:group-hover:opacity-100 focus:opacity-100"
574
+ className="relative size-10 rounded-lg p-0 transition-[background-color,opacity,transform] duration-150 ease-out active:scale-[0.96] focus:opacity-100 sm:size-8 sm:opacity-0 sm:before:absolute sm:before:-inset-1 sm:before:content-[''] sm:group-hover:opacity-100"
562
575
  aria-label={t("forms.formActions")}
563
576
  >
564
577
  <IconDots className="h-4 w-4" />
@@ -695,10 +708,12 @@ export function FormsListPage() {
695
708
  </AlertDialogDescription>
696
709
  </AlertDialogHeader>
697
710
  <AlertDialogFooter>
698
- <AlertDialogCancel>{t("common.cancel")}</AlertDialogCancel>
711
+ <AlertDialogCancel className="min-h-10 active:scale-[0.96] transition-[background-color,box-shadow,transform]">
712
+ {t("common.cancel")}
713
+ </AlertDialogCancel>
699
714
  <AlertDialogAction
700
715
  onClick={handlePurge}
701
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
716
+ className="min-h-10 bg-destructive text-destructive-foreground hover:bg-destructive/90 active:scale-[0.96] transition-[background-color,box-shadow,transform]"
702
717
  >
703
718
  {t("forms.deleteForever")}
704
719
  </AlertDialogAction>
@@ -718,13 +733,16 @@ export function FormsListPage() {
718
733
  </AlertDialogDescription>
719
734
  </AlertDialogHeader>
720
735
  <AlertDialogFooter>
721
- <AlertDialogCancel disabled={bulkDeletePending}>
736
+ <AlertDialogCancel
737
+ disabled={bulkDeletePending}
738
+ className="min-h-10 active:scale-[0.96] transition-[background-color,box-shadow,transform]"
739
+ >
722
740
  {t("common.cancel")}
723
741
  </AlertDialogCancel>
724
742
  <AlertDialogAction
725
743
  onClick={() => handleBulkDelete(true)}
726
744
  disabled={bulkDeletePending}
727
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
745
+ className="min-h-10 bg-destructive text-destructive-foreground hover:bg-destructive/90 active:scale-[0.96] transition-[background-color,box-shadow,transform]"
728
746
  >
729
747
  {t("forms.deleteForever")}
730
748
  </AlertDialogAction>
@@ -52,7 +52,7 @@ function Metric({
52
52
  detail?: string;
53
53
  }) {
54
54
  return (
55
- <div className="min-w-0 rounded-md border border-border bg-background px-3 py-2">
55
+ <div className="min-w-0 rounded-xl border border-border/80 bg-background px-3 py-3 shadow-sm">
56
56
  <div className="text-[11px] font-medium uppercase tracking-normal text-muted-foreground">
57
57
  {label}
58
58
  </div>
@@ -97,7 +97,7 @@ function SubmissionBars({
97
97
  <div className="flex h-40 w-full items-end rounded-sm bg-muted/40 px-0.5">
98
98
  <div
99
99
  className={cn(
100
- "w-full rounded-sm bg-primary/80 transition-colors",
100
+ "w-full rounded-sm bg-primary/80 shadow-sm transition-[height,background-color,box-shadow] duration-300 ease-out hover:bg-primary hover:shadow-md motion-reduce:transition-none",
101
101
  point.submissions === 0 && "bg-muted-foreground/30",
102
102
  )}
103
103
  style={{ height }}
@@ -121,11 +121,11 @@ function LoadingState() {
121
121
  <Skeleton className="h-10 w-72" />
122
122
  <div className="forms-response-metrics-grid grid gap-3">
123
123
  {Array.from({ length: 4 }).map((_, index) => (
124
- <Skeleton key={index} className="h-20 rounded-md" />
124
+ <Skeleton key={index} className="h-20 rounded-xl" />
125
125
  ))}
126
126
  </div>
127
- <Skeleton className="h-64 rounded-md" />
128
- <Skeleton className="h-72 rounded-md" />
127
+ <Skeleton className="h-64 rounded-xl" />
128
+ <Skeleton className="h-72 rounded-xl" />
129
129
  </div>
130
130
  );
131
131
  }
@@ -160,7 +160,7 @@ export function ResponseInsightsPage() {
160
160
  type="button"
161
161
  variant="outline"
162
162
  size="sm"
163
- className="gap-1.5"
163
+ className="min-h-10 gap-1.5 transition-[scale,background-color,border-color,color,box-shadow] duration-150 active:scale-[0.96] motion-reduce:transition-none"
164
164
  onClick={() => refetch()}
165
165
  >
166
166
  <IconRefresh className="size-3.5" />
@@ -176,7 +176,12 @@ export function ResponseInsightsPage() {
176
176
  <div className="flex min-h-full flex-col bg-background">
177
177
  <header className="flex min-h-14 shrink-0 items-center justify-between gap-3 border-b border-border pl-12 pr-3 sm:px-4 md:pl-4">
178
178
  <div className="flex min-w-0 items-center gap-2">
179
- <Button variant="ghost" size="sm" asChild className="gap-1.5">
179
+ <Button
180
+ variant="ghost"
181
+ size="sm"
182
+ asChild
183
+ className="min-h-10 gap-1.5 transition-[scale,background-color,border-color,color,box-shadow] duration-150 active:scale-[0.96] motion-reduce:transition-none"
184
+ >
180
185
  <Link to="/forms">
181
186
  <IconArrowLeft className="size-3.5" />
182
187
  Forms
@@ -196,7 +201,12 @@ export function ResponseInsightsPage() {
196
201
  </div>
197
202
  </div>
198
203
  </div>
199
- <Button variant="outline" size="sm" asChild className="gap-1.5">
204
+ <Button
205
+ variant="outline"
206
+ size="sm"
207
+ asChild
208
+ className="min-h-10 gap-1.5 transition-[scale,background-color,border-color,color,box-shadow] duration-150 active:scale-[0.96] motion-reduce:transition-none"
209
+ >
200
210
  <Link to={display.primaryAction.href}>
201
211
  {display.primaryAction.label}
202
212
  <IconExternalLink className="size-3.5" />
@@ -204,7 +214,7 @@ export function ResponseInsightsPage() {
204
214
  </Button>
205
215
  </header>
206
216
 
207
- <main className="forms-response-insights-grid grid gap-4 p-4">
217
+ <main className="forms-response-insights-grid grid gap-4 bg-muted/20 p-4">
208
218
  <section className="space-y-4">
209
219
  <div className="forms-response-metrics-grid grid gap-3">
210
220
  <Metric
@@ -233,7 +243,7 @@ export function ResponseInsightsPage() {
233
243
  />
234
244
  </div>
235
245
 
236
- <div className="rounded-md border border-border bg-background p-4">
246
+ <div className="rounded-xl border border-border/80 bg-background p-4 shadow-sm">
237
247
  <div className="mb-3 flex items-center justify-between gap-3">
238
248
  <div>
239
249
  <h2 className="text-sm font-semibold">{chartSeries.title}</h2>
@@ -249,7 +259,7 @@ export function ResponseInsightsPage() {
249
259
  </div>
250
260
  </section>
251
261
 
252
- <section className="min-w-0 rounded-md border border-border bg-background">
262
+ <section className="min-w-0 overflow-hidden rounded-xl border border-border/80 bg-background shadow-sm">
253
263
  <div className="flex min-h-12 items-center justify-between gap-3 border-b border-border px-3">
254
264
  <div className="min-w-0">
255
265
  <h2 className="truncate text-sm font-semibold">{table.title}</h2>
@@ -268,13 +278,13 @@ export function ResponseInsightsPage() {
268
278
  ) : (
269
279
  <div className="max-h-[calc(100vh-15rem)] overflow-auto">
270
280
  <Table>
271
- <TableHeader className="sticky top-0 z-10 bg-background">
281
+ <TableHeader className="sticky top-0 z-10 bg-background shadow-sm">
272
282
  <TableRow>
273
283
  {table.columns.map((column) => (
274
284
  <TableHead
275
285
  key={column.key}
276
286
  className={cn(
277
- "h-9 whitespace-nowrap px-3 text-xs",
287
+ "h-11 whitespace-nowrap px-3 text-xs",
278
288
  column.align === "right" && "text-right",
279
289
  )}
280
290
  >
@@ -285,12 +295,15 @@ export function ResponseInsightsPage() {
285
295
  </TableHeader>
286
296
  <TableBody>
287
297
  {table.rows.map((row) => (
288
- <TableRow key={String(row.id)}>
298
+ <TableRow
299
+ key={String(row.id)}
300
+ className="transition-[background-color] duration-150 hover:bg-muted/30 motion-reduce:transition-none"
301
+ >
289
302
  {table.columns.map((column) => (
290
303
  <TableCell
291
304
  key={column.key}
292
305
  className={cn(
293
- "max-w-56 truncate px-3 py-2 text-xs",
306
+ "max-w-56 truncate px-3 py-3 text-xs",
294
307
  column.align === "right" && "text-right",
295
308
  )}
296
309
  title={formatCell(row[column.key])}
@@ -254,7 +254,7 @@ export function ResponsesPage() {
254
254
  variant="outline"
255
255
  size="sm"
256
256
  onClick={() => refetch()}
257
- className="gap-2"
257
+ className="min-h-10 gap-2 transition-[scale,background-color,border-color,color,box-shadow] duration-150 active:scale-[0.96] motion-reduce:transition-none"
258
258
  >
259
259
  <IconRefresh className="h-3.5 w-3.5" />
260
260
  {t("common.retry")}
@@ -272,7 +272,7 @@ export function ResponsesPage() {
272
272
  variant="ghost"
273
273
  size="sm"
274
274
  asChild
275
- className="gap-1.5 shrink-0"
275
+ className="min-h-10 shrink-0 gap-1.5 transition-[scale,background-color,border-color,color,box-shadow] duration-150 active:scale-[0.96] motion-reduce:transition-none"
276
276
  >
277
277
  <Link to={`/forms/${id}`}>
278
278
  <IconArrowLeft className="h-3.5 w-3.5" />
@@ -305,14 +305,14 @@ export function ResponsesPage() {
305
305
  value={search}
306
306
  onChange={(e) => setSearch(e.target.value)}
307
307
  placeholder={t("responses.filterPlaceholder")}
308
- className="h-8 pl-7 w-48 text-xs"
308
+ className="h-10 w-48 pl-7 text-xs transition-[background-color,border-color,box-shadow] duration-150 motion-reduce:transition-none"
309
309
  />
310
310
  </div>
311
311
  ) : null}
312
312
  <Button
313
313
  variant="outline"
314
314
  size="sm"
315
- className="gap-1.5 text-xs shrink-0"
315
+ className="min-h-10 shrink-0 gap-1.5 text-xs transition-[scale,background-color,border-color,color,box-shadow] duration-150 active:scale-[0.96] motion-reduce:transition-none"
316
316
  onClick={exportCsv}
317
317
  disabled={filteredSorted.length === 0}
318
318
  >
@@ -332,7 +332,7 @@ export function ResponsesPage() {
332
332
  value={search}
333
333
  onChange={(e) => setSearch(e.target.value)}
334
334
  placeholder={t("responses.filterPlaceholder")}
335
- className="h-8 pl-7 text-xs"
335
+ className="h-10 pl-7 text-xs transition-[background-color,border-color,box-shadow] duration-150 motion-reduce:transition-none"
336
336
  />
337
337
  </div>
338
338
  </div>
@@ -371,10 +371,10 @@ export function ResponsesPage() {
371
371
  ))}
372
372
  </colgroup>
373
373
  <thead>
374
- <tr className="border-b border-border bg-muted/30">
374
+ <tr className="border-b border-border bg-muted/40">
375
375
  <th
376
376
  scope="col"
377
- className="min-w-16 px-4 py-2.5 text-left text-xs font-medium text-muted-foreground whitespace-nowrap"
377
+ className="min-w-16 px-4 py-3 text-left text-xs font-medium text-muted-foreground whitespace-nowrap"
378
378
  >
379
379
  #
380
380
  </th>
@@ -423,7 +423,7 @@ export function ResponsesPage() {
423
423
  {filteredSorted.map((response, idx) => (
424
424
  <tr
425
425
  key={response.id}
426
- className="border-b border-border hover:bg-muted/20"
426
+ className="border-b border-border transition-[background-color] duration-150 hover:bg-muted/20 motion-reduce:transition-none"
427
427
  >
428
428
  <td className="px-4 py-2.5 text-xs text-muted-foreground">
429
429
  {filteredSorted.length - idx}
@@ -525,27 +525,50 @@ function SortableHeader(props: {
525
525
  }) {
526
526
  const t = useT();
527
527
  const { label, active, dir, onClick } = props;
528
- const Icon = !active
529
- ? IconArrowsSort
528
+ const sortState: "neutral" | "asc" | "desc" = !active
529
+ ? "neutral"
530
530
  : dir === "asc"
531
- ? IconArrowUp
532
- : IconArrowDown;
531
+ ? "asc"
532
+ : "desc";
533
+ const iconBase =
534
+ "absolute inset-0 transition-[opacity,scale,filter] duration-200 ease-[cubic-bezier(0.2,0,0,1)]";
535
+ const iconVisible = "scale-100 opacity-60 blur-none";
536
+ const iconHidden = "scale-[0.25] opacity-0 blur-[4px]";
533
537
  return (
534
538
  <th
535
539
  scope="col"
536
- className="min-w-40 px-4 py-2.5 text-left text-xs font-medium text-muted-foreground whitespace-nowrap"
540
+ className="min-w-40 px-4 py-1 text-left text-xs font-medium text-muted-foreground whitespace-nowrap"
537
541
  >
538
542
  <button
539
543
  type="button"
540
544
  onClick={onClick}
541
545
  className={cn(
542
- "inline-flex items-center gap-1 cursor-pointer hover:text-foreground",
546
+ "relative -mx-2 inline-flex min-h-10 cursor-pointer items-center gap-1 rounded-md px-2 transition-[scale,background-color,color,box-shadow] duration-150 ease-out hover:bg-accent hover:text-foreground active:scale-[0.96] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 motion-reduce:transition-none motion-reduce:active:scale-100 sm:min-h-0 sm:before:absolute sm:before:-inset-2.5 sm:before:content-['']",
543
547
  active && "text-foreground",
544
548
  )}
545
549
  aria-label={t("responses.sortBy", { label })}
546
550
  >
547
551
  {label}
548
- <Icon className="h-3 w-3 opacity-60" />
552
+ <span className="relative inline-block h-3 w-3 shrink-0">
553
+ <IconArrowsSort
554
+ className={cn(
555
+ iconBase,
556
+ sortState === "neutral" ? iconVisible : iconHidden,
557
+ )}
558
+ />
559
+ <IconArrowUp
560
+ className={cn(
561
+ iconBase,
562
+ sortState === "asc" ? iconVisible : iconHidden,
563
+ )}
564
+ />
565
+ <IconArrowDown
566
+ className={cn(
567
+ iconBase,
568
+ sortState === "desc" ? iconVisible : iconHidden,
569
+ )}
570
+ />
571
+ </span>
549
572
  </button>
550
573
  </th>
551
574
  );
@@ -54,7 +54,7 @@ export default function SettingsRoute() {
54
54
  {t("settings.description")}
55
55
  </p>
56
56
 
57
- <Card id="language" className="scroll-mt-16">
57
+ <Card id="language" className="forms-settings-card scroll-mt-16">
58
58
  <CardHeader>
59
59
  <CardTitle className="text-base">
60
60
  {t("settings.languageTitle")}
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-12
4
+ ---
5
+
6
+ Form builder surfaces are calmer, fields stay stable on hover, and integration icons load correctly
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-12
4
+ ---
5
+
6
+ Smoother, more tactile controls throughout: animated icon and sort toggles, press feedback on buttons and cards, and larger click targets