@stigmer/react 3.11.0 → 3.11.1-dev.20260812192248

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 (396) hide show
  1. package/agent-instance/AgentInstanceList.d.ts.map +1 -1
  2. package/agent-instance/AgentInstanceList.js +2 -1
  3. package/agent-instance/AgentInstanceList.js.map +1 -1
  4. package/api-key/ApiKeyListPanel.d.ts.map +1 -1
  5. package/api-key/ApiKeyListPanel.js +2 -1
  6. package/api-key/ApiKeyListPanel.js.map +1 -1
  7. package/attachment/AttachmentChipList.d.ts.map +1 -1
  8. package/attachment/AttachmentChipList.js +9 -8
  9. package/attachment/AttachmentChipList.js.map +1 -1
  10. package/attachment/AttachmentImageLightbox.d.ts.map +1 -1
  11. package/attachment/AttachmentImageLightbox.js +1 -1
  12. package/attachment/AttachmentImageLightbox.js.map +1 -1
  13. package/channel/AgentChannelsPanel.d.ts.map +1 -1
  14. package/channel/AgentChannelsPanel.js +2 -1
  15. package/channel/AgentChannelsPanel.js.map +1 -1
  16. package/channel/ChannelConversationsDialog.d.ts.map +1 -1
  17. package/channel/ChannelConversationsDialog.js +2 -1
  18. package/channel/ChannelConversationsDialog.js.map +1 -1
  19. package/channel/ChannelTemplatesDialog.d.ts.map +1 -1
  20. package/channel/ChannelTemplatesDialog.js +2 -1
  21. package/channel/ChannelTemplatesDialog.js.map +1 -1
  22. package/channel-app/internal.d.ts.map +1 -1
  23. package/channel-app/internal.js +2 -1
  24. package/channel-app/internal.js.map +1 -1
  25. package/composer/ComposerToolbar.d.ts.map +1 -1
  26. package/composer/ComposerToolbar.js +10 -7
  27. package/composer/ComposerToolbar.js.map +1 -1
  28. package/composer/ConfigureMenu.d.ts.map +1 -1
  29. package/composer/ConfigureMenu.js +2 -1
  30. package/composer/ConfigureMenu.js.map +1 -1
  31. package/composer/ContextChip.d.ts.map +1 -1
  32. package/composer/ContextChip.js +2 -1
  33. package/composer/ContextChip.js.map +1 -1
  34. package/composer/ContextPopover.d.ts.map +1 -1
  35. package/composer/ContextPopover.js +2 -1
  36. package/composer/ContextPopover.js.map +1 -1
  37. package/composer/SessionComposer.d.ts +13 -0
  38. package/composer/SessionComposer.d.ts.map +1 -1
  39. package/composer/SessionComposer.js +12 -2
  40. package/composer/SessionComposer.js.map +1 -1
  41. package/conversation/ConversationMediaAttachment.d.ts.map +1 -1
  42. package/conversation/ConversationMediaAttachment.js +2 -1
  43. package/conversation/ConversationMediaAttachment.js.map +1 -1
  44. package/conversation/ConversationTimelineView.d.ts.map +1 -1
  45. package/conversation/ConversationTimelineView.js +14 -4
  46. package/conversation/ConversationTimelineView.js.map +1 -1
  47. package/conversation/conversationPresentation.d.ts +28 -0
  48. package/conversation/conversationPresentation.d.ts.map +1 -1
  49. package/conversation/conversationPresentation.js +38 -1
  50. package/conversation/conversationPresentation.js.map +1 -1
  51. package/cursor-accounts/MemberCoverageTable.d.ts.map +1 -1
  52. package/cursor-accounts/MemberCoverageTable.js +6 -5
  53. package/cursor-accounts/MemberCoverageTable.js.map +1 -1
  54. package/dashboard/DashboardKPICards.d.ts.map +1 -1
  55. package/dashboard/DashboardKPICards.js +3 -1
  56. package/dashboard/DashboardKPICards.js.map +1 -1
  57. package/datastore/CollectionRecordsBrowser.d.ts.map +1 -1
  58. package/datastore/CollectionRecordsBrowser.js +5 -2
  59. package/datastore/CollectionRecordsBrowser.js.map +1 -1
  60. package/environment/EnvironmentVariableEditor.d.ts.map +1 -1
  61. package/environment/EnvironmentVariableEditor.js +2 -1
  62. package/environment/EnvironmentVariableEditor.js.map +1 -1
  63. package/execution/ApprovalCard.d.ts.map +1 -1
  64. package/execution/ApprovalCard.js +1 -1
  65. package/execution/ApprovalCard.js.map +1 -1
  66. package/execution/ArtifactRowView.d.ts.map +1 -1
  67. package/execution/ArtifactRowView.js +2 -1
  68. package/execution/ArtifactRowView.js.map +1 -1
  69. package/execution/FilePathLink.d.ts +4 -4
  70. package/execution/FilePathLink.d.ts.map +1 -1
  71. package/execution/FilePathLink.js +4 -4
  72. package/execution/FilePathLink.js.map +1 -1
  73. package/execution/McpToolDetail.d.ts.map +1 -1
  74. package/execution/McpToolDetail.js +2 -1
  75. package/execution/McpToolDetail.js.map +1 -1
  76. package/execution/MessageAttachments.d.ts.map +1 -1
  77. package/execution/MessageAttachments.js +10 -8
  78. package/execution/MessageAttachments.js.map +1 -1
  79. package/execution/MessageEntry.d.ts.map +1 -1
  80. package/execution/MessageEntry.js +2 -1
  81. package/execution/MessageEntry.js.map +1 -1
  82. package/execution/PlanArtifactCard.d.ts.map +1 -1
  83. package/execution/PlanArtifactCard.js +7 -4
  84. package/execution/PlanArtifactCard.js.map +1 -1
  85. package/execution/ToolCallDetail.d.ts.map +1 -1
  86. package/execution/ToolCallDetail.js +1 -1
  87. package/execution/ToolCallDetail.js.map +1 -1
  88. package/execution/ToolCallItem.d.ts.map +1 -1
  89. package/execution/ToolCallItem.js +5 -1
  90. package/execution/ToolCallItem.js.map +1 -1
  91. package/execution/WriteBackCard.d.ts.map +1 -1
  92. package/execution/WriteBackCard.js +6 -4
  93. package/execution/WriteBackCard.js.map +1 -1
  94. package/execution/artifact-row-item.d.ts +1 -1
  95. package/execution/artifact-row-item.d.ts.map +1 -1
  96. package/file-reference/FileReferenceChipList.d.ts.map +1 -1
  97. package/file-reference/FileReferenceChipList.js +6 -1
  98. package/file-reference/FileReferenceChipList.js.map +1 -1
  99. package/iam-policy/OrgMembersPanel.d.ts.map +1 -1
  100. package/iam-policy/OrgMembersPanel.js +6 -5
  101. package/iam-policy/OrgMembersPanel.js.map +1 -1
  102. package/iam-policy/ProviderBadge.d.ts.map +1 -1
  103. package/iam-policy/ProviderBadge.js +3 -2
  104. package/iam-policy/ProviderBadge.js.map +1 -1
  105. package/iam-policy/RoleSelector.d.ts.map +1 -1
  106. package/iam-policy/RoleSelector.js +1 -1
  107. package/iam-policy/RoleSelector.js.map +1 -1
  108. package/identity-provider/IdentityProviderListPanel.d.ts.map +1 -1
  109. package/identity-provider/IdentityProviderListPanel.js +2 -1
  110. package/identity-provider/IdentityProviderListPanel.js.map +1 -1
  111. package/internal/truncated-text.d.ts +17 -0
  112. package/internal/truncated-text.d.ts.map +1 -0
  113. package/internal/truncated-text.js +27 -0
  114. package/internal/truncated-text.js.map +1 -0
  115. package/invitation/InvitationManager.d.ts.map +1 -1
  116. package/invitation/InvitationManager.js +4 -3
  117. package/invitation/InvitationManager.js.map +1 -1
  118. package/library/ScopeToggle.d.ts.map +1 -1
  119. package/library/ScopeToggle.js +10 -2
  120. package/library/ScopeToggle.js.map +1 -1
  121. package/library/VisibilitySelector.d.ts.map +1 -1
  122. package/library/VisibilitySelector.js +2 -1
  123. package/library/VisibilitySelector.js.map +1 -1
  124. package/mcp-server/McpServerConfigPanel.d.ts +30 -1
  125. package/mcp-server/McpServerConfigPanel.d.ts.map +1 -1
  126. package/mcp-server/McpServerConfigPanel.js +18 -8
  127. package/mcp-server/McpServerConfigPanel.js.map +1 -1
  128. package/mcp-server/McpServerConnectDialog.d.ts +12 -1
  129. package/mcp-server/McpServerConnectDialog.d.ts.map +1 -1
  130. package/mcp-server/McpServerConnectDialog.js +8 -7
  131. package/mcp-server/McpServerConnectDialog.js.map +1 -1
  132. package/mcp-server/McpServerDetailView.d.ts.map +1 -1
  133. package/mcp-server/McpServerDetailView.js +4 -4
  134. package/mcp-server/McpServerDetailView.js.map +1 -1
  135. package/mcp-server/McpServerPicker.d.ts.map +1 -1
  136. package/mcp-server/McpServerPicker.js +86 -5
  137. package/mcp-server/McpServerPicker.js.map +1 -1
  138. package/mcp-server/McpToolSelector.d.ts.map +1 -1
  139. package/mcp-server/McpToolSelector.js +2 -1
  140. package/mcp-server/McpToolSelector.js.map +1 -1
  141. package/mcp-server/VendorApprovalBlockedNotice.d.ts +77 -0
  142. package/mcp-server/VendorApprovalBlockedNotice.d.ts.map +1 -0
  143. package/mcp-server/VendorApprovalBlockedNotice.js +62 -0
  144. package/mcp-server/VendorApprovalBlockedNotice.js.map +1 -0
  145. package/mcp-server/mcpServerSetupReducer.d.ts +18 -0
  146. package/mcp-server/mcpServerSetupReducer.d.ts.map +1 -1
  147. package/mcp-server/mcpServerSetupReducer.js +6 -0
  148. package/mcp-server/mcpServerSetupReducer.js.map +1 -1
  149. package/mcp-server/useMcpServerSetup.d.ts.map +1 -1
  150. package/mcp-server/useMcpServerSetup.js +7 -0
  151. package/mcp-server/useMcpServerSetup.js.map +1 -1
  152. package/oauth-app/OAuthAppListPanel.d.ts.map +1 -1
  153. package/oauth-app/OAuthAppListPanel.js +2 -1
  154. package/oauth-app/OAuthAppListPanel.js.map +1 -1
  155. package/package.json +4 -4
  156. package/platform-client/PlatformClientListPanel.d.ts.map +1 -1
  157. package/platform-client/PlatformClientListPanel.js +3 -2
  158. package/platform-client/PlatformClientListPanel.js.map +1 -1
  159. package/pricing-governance/BaselineEditor.d.ts.map +1 -1
  160. package/pricing-governance/BaselineEditor.js +7 -1
  161. package/pricing-governance/BaselineEditor.js.map +1 -1
  162. package/pricing-governance/ModelCatalogPanel.d.ts.map +1 -1
  163. package/pricing-governance/ModelCatalogPanel.js +2 -1
  164. package/pricing-governance/ModelCatalogPanel.js.map +1 -1
  165. package/pricing-governance/PricingGovernanceConsole.d.ts.map +1 -1
  166. package/pricing-governance/PricingGovernanceConsole.js +2 -1
  167. package/pricing-governance/PricingGovernanceConsole.js.map +1 -1
  168. package/pricing-governance/PricingGovernancePanel.d.ts.map +1 -1
  169. package/pricing-governance/PricingGovernancePanel.js +2 -1
  170. package/pricing-governance/PricingGovernancePanel.js.map +1 -1
  171. package/resource-workbench/components/StatusBadge.d.ts.map +1 -1
  172. package/resource-workbench/components/StatusBadge.js +2 -1
  173. package/resource-workbench/components/StatusBadge.js.map +1 -1
  174. package/schedule/CadenceField.d.ts.map +1 -1
  175. package/schedule/CadenceField.js +15 -8
  176. package/schedule/CadenceField.js.map +1 -1
  177. package/schedule/ScheduleRunsTable.d.ts.map +1 -1
  178. package/schedule/ScheduleRunsTable.js +3 -2
  179. package/schedule/ScheduleRunsTable.js.map +1 -1
  180. package/session/PlanEditor.d.ts.map +1 -1
  181. package/session/PlanEditor.js +3 -5
  182. package/session/PlanEditor.js.map +1 -1
  183. package/sharing/AgentShareList.d.ts.map +1 -1
  184. package/sharing/AgentShareList.js +3 -1
  185. package/sharing/AgentShareList.js.map +1 -1
  186. package/sharing/ShareAgentDialog.d.ts.map +1 -1
  187. package/sharing/ShareAgentDialog.js +2 -1
  188. package/sharing/ShareAgentDialog.js.map +1 -1
  189. package/skill/SkillDetailView.d.ts.map +1 -1
  190. package/skill/SkillDetailView.js +3 -2
  191. package/skill/SkillDetailView.js.map +1 -1
  192. package/src/__tests__/helpers/open-menu.ts +88 -0
  193. package/src/__tests__/portal-container.test.tsx +3 -3
  194. package/src/agent-instance/AgentInstanceList.tsx +5 -6
  195. package/src/agent-instance/__tests__/AgentInstanceList.test.tsx +5 -4
  196. package/src/api-key/ApiKeyListPanel.tsx +11 -3
  197. package/src/attachment/AttachmentChipList.tsx +74 -65
  198. package/src/attachment/AttachmentImageLightbox.tsx +5 -4
  199. package/src/attachment/__tests__/AttachmentChipList.test.tsx +4 -3
  200. package/src/channel/AgentChannelsPanel.tsx +5 -6
  201. package/src/channel/ChannelConversationsDialog.tsx +5 -3
  202. package/src/channel/ChannelTemplatesDialog.tsx +5 -6
  203. package/src/channel/__tests__/AgentChannelsPanel.test.tsx +14 -9
  204. package/src/channel-app/internal.tsx +5 -6
  205. package/src/composer/ComposerToolbar.tsx +122 -90
  206. package/src/composer/ConfigureMenu.tsx +33 -24
  207. package/src/composer/ContextChip.tsx +2 -3
  208. package/src/composer/ContextPopover.tsx +27 -18
  209. package/src/composer/SessionComposer.tsx +41 -9
  210. package/src/composer/__tests__/SessionComposer-agentSetupError.test.tsx +124 -0
  211. package/src/composer/__tests__/SessionComposer-lockAgent.test.tsx +4 -9
  212. package/src/conversation/ConversationMediaAttachment.tsx +2 -2
  213. package/src/conversation/ConversationTimelineView.tsx +19 -2
  214. package/src/conversation/__tests__/ConversationTimelineView.test.tsx +106 -3
  215. package/src/conversation/conversationPresentation.ts +52 -1
  216. package/src/cursor-accounts/MemberCoverageTable.tsx +15 -13
  217. package/src/cursor-accounts/__tests__/CursorAccountsConsole.test.tsx +4 -4
  218. package/src/dashboard/DashboardKPICards.tsx +2 -1
  219. package/src/datastore/CollectionRecordsBrowser.tsx +8 -7
  220. package/src/environment/EnvironmentVariableEditor.tsx +17 -6
  221. package/src/execution/ApprovalCard.tsx +1 -3
  222. package/src/execution/ArtifactRowView.tsx +68 -48
  223. package/src/execution/FilePathLink.tsx +43 -27
  224. package/src/execution/McpToolDetail.tsx +6 -2
  225. package/src/execution/MessageAttachments.tsx +57 -41
  226. package/src/execution/MessageEntry.tsx +22 -15
  227. package/src/execution/PlanArtifactCard.tsx +25 -18
  228. package/src/execution/ToolCallDetail.tsx +1 -4
  229. package/src/execution/ToolCallItem.tsx +18 -7
  230. package/src/execution/WriteBackCard.tsx +38 -29
  231. package/src/execution/__tests__/ArtifactRow.test.tsx +6 -2
  232. package/src/execution/__tests__/ArtifactRowView.test.tsx +5 -2
  233. package/src/execution/__tests__/FilePathLink.test.tsx +10 -6
  234. package/src/execution/__tests__/FileReviewCard.test.tsx +2 -1
  235. package/src/execution/__tests__/MessageEntry-attachments.test.tsx +3 -2
  236. package/src/execution/__tests__/MessageThread.test.tsx +6 -1
  237. package/src/execution/__tests__/artifact-row-tooltips.layout.test.tsx +115 -0
  238. package/src/execution/artifact-row-item.ts +1 -1
  239. package/src/file-reference/FileReferenceChipList.tsx +29 -17
  240. package/src/file-reference/__tests__/FileReferenceChipList.test.tsx +5 -2
  241. package/src/iam-policy/OrgMembersPanel.tsx +26 -19
  242. package/src/iam-policy/ProviderBadge.tsx +17 -10
  243. package/src/iam-policy/RoleSelector.tsx +0 -1
  244. package/src/identity-provider/IdentityProviderListPanel.tsx +11 -3
  245. package/src/internal/__tests__/truncated-text.layout.test.tsx +68 -0
  246. package/src/internal/__tests__/truncated-text.test.tsx +33 -0
  247. package/src/internal/truncated-text.tsx +81 -0
  248. package/src/invitation/InvitationManager.tsx +18 -5
  249. package/src/library/ScopeToggle.tsx +14 -2
  250. package/src/library/VisibilitySelector.tsx +21 -14
  251. package/src/mcp-server/McpServerConfigPanel.tsx +79 -37
  252. package/src/mcp-server/McpServerConnectDialog.tsx +41 -5
  253. package/src/mcp-server/McpServerDetailView.tsx +23 -39
  254. package/src/mcp-server/McpServerPicker.tsx +99 -7
  255. package/src/mcp-server/McpToolSelector.tsx +12 -7
  256. package/src/mcp-server/VendorApprovalBlockedNotice.tsx +221 -0
  257. package/src/mcp-server/__tests__/McpServerPicker.test.tsx +504 -0
  258. package/src/mcp-server/__tests__/VendorApprovalBlocked.surfaces.test.tsx +321 -0
  259. package/src/mcp-server/__tests__/VendorApprovalBlockedNotice.test.tsx +154 -0
  260. package/src/mcp-server/mcpServerSetupReducer.ts +24 -0
  261. package/src/mcp-server/useMcpServerSetup.ts +9 -0
  262. package/src/oauth-app/OAuthAppListPanel.tsx +11 -3
  263. package/src/platform-client/PlatformClientListPanel.tsx +35 -18
  264. package/src/pricing-governance/BaselineEditor.tsx +20 -0
  265. package/src/pricing-governance/ModelCatalogPanel.tsx +9 -5
  266. package/src/pricing-governance/PricingGovernanceConsole.tsx +9 -5
  267. package/src/pricing-governance/PricingGovernancePanel.tsx +13 -4
  268. package/src/pricing-governance/__tests__/ModelCatalogPanel.test.tsx +37 -0
  269. package/src/resource-workbench/components/StatusBadge.tsx +7 -3
  270. package/src/schedule/CadenceField.tsx +38 -27
  271. package/src/schedule/ScheduleRunsTable.tsx +23 -11
  272. package/src/schedule/__tests__/cadence-tooltips.layout.test.tsx +86 -0
  273. package/src/session/PlanEditor.tsx +9 -8
  274. package/src/sharing/AgentShareList.tsx +28 -22
  275. package/src/sharing/ShareAgentDialog.tsx +5 -6
  276. package/src/sharing/__tests__/AgentShareList.test.tsx +4 -4
  277. package/src/skill/SkillDetailView.tsx +23 -12
  278. package/src/version-history/VersionTimelineEntry.tsx +21 -12
  279. package/src/workflow/CanvasTransitionEdge.tsx +23 -16
  280. package/src/workflow/ExecutionActiveTaskIndicator.tsx +74 -59
  281. package/src/workflow/TaskPickerPopover.tsx +0 -1
  282. package/src/workflow/WorkflowCanvasEditor.tsx +95 -64
  283. package/src/workflow/WorkflowNode.tsx +13 -6
  284. package/src/workflow/WorkflowTaskApprovalSummary.tsx +18 -11
  285. package/src/workflow/WorkflowTaskList.tsx +2 -2
  286. package/src/workflow/WorkflowVersionBadge.tsx +33 -18
  287. package/src/workflow/WorkflowVersionTimeline.tsx +7 -6
  288. package/src/workflow/__tests__/WorkflowTaskApprovalSummary.test.tsx +3 -1
  289. package/src/workflow/__tests__/structured-data-viewer.test.tsx +5 -2
  290. package/src/workflow/execution-comparison/ExecutionComparisonView.tsx +5 -3
  291. package/src/workflow/execution-comparison/TaskComparisonTable.tsx +12 -5
  292. package/src/workflow/execution-history/ExecutionHistoryTable.tsx +24 -15
  293. package/src/workflow/inspector/InspectorHeader.tsx +20 -11
  294. package/src/workflow/inspector/NestedTaskList.tsx +18 -11
  295. package/src/workflow/inspector/WorkflowSummaryPanel.tsx +5 -3
  296. package/src/workflow/inspector/tabs/BranchesTab.tsx +61 -31
  297. package/src/workflow/instance/WorkflowInstanceList.tsx +5 -6
  298. package/src/workflow/instance/__tests__/WorkflowInstanceList.test.tsx +5 -4
  299. package/src/workflow/node-shell/DiffBadge.tsx +18 -11
  300. package/src/workflow/node-shell/ExecutionBadge.tsx +73 -46
  301. package/src/workflow/node-shell/NodeActions.tsx +128 -94
  302. package/src/workflow/task-detail/StructuredDataViewer.tsx +4 -2
  303. package/src/workspace/EditorTabs.tsx +102 -85
  304. package/src/workspace/ExplorerTree.tsx +47 -30
  305. package/src/workspace/FileViewer.tsx +11 -3
  306. package/src/workspace/PanelChip.tsx +32 -23
  307. package/src/workspace/WorkspaceEditor.tsx +21 -9
  308. package/src/workspace/WorkspaceSummary.tsx +21 -12
  309. package/src/workspace/WorkspaceSurface.tsx +86 -61
  310. package/src/workspace/__tests__/EditorTabs.test.tsx +5 -2
  311. package/src/workspace/__tests__/WorkspaceSurface.test.tsx +5 -2
  312. package/version-history/VersionTimelineEntry.d.ts.map +1 -1
  313. package/version-history/VersionTimelineEntry.js +2 -1
  314. package/version-history/VersionTimelineEntry.js.map +1 -1
  315. package/workflow/CanvasTransitionEdge.d.ts.map +1 -1
  316. package/workflow/CanvasTransitionEdge.js +3 -2
  317. package/workflow/CanvasTransitionEdge.js.map +1 -1
  318. package/workflow/ExecutionActiveTaskIndicator.d.ts.map +1 -1
  319. package/workflow/ExecutionActiveTaskIndicator.js +12 -11
  320. package/workflow/ExecutionActiveTaskIndicator.js.map +1 -1
  321. package/workflow/TaskPickerPopover.js +1 -1
  322. package/workflow/TaskPickerPopover.js.map +1 -1
  323. package/workflow/WorkflowCanvasEditor.d.ts.map +1 -1
  324. package/workflow/WorkflowCanvasEditor.js +9 -2
  325. package/workflow/WorkflowCanvasEditor.js.map +1 -1
  326. package/workflow/WorkflowNode.d.ts.map +1 -1
  327. package/workflow/WorkflowNode.js +2 -1
  328. package/workflow/WorkflowNode.js.map +1 -1
  329. package/workflow/WorkflowTaskApprovalSummary.d.ts.map +1 -1
  330. package/workflow/WorkflowTaskApprovalSummary.js +6 -4
  331. package/workflow/WorkflowTaskApprovalSummary.js.map +1 -1
  332. package/workflow/WorkflowTaskList.js +2 -2
  333. package/workflow/WorkflowTaskList.js.map +1 -1
  334. package/workflow/WorkflowVersionBadge.d.ts.map +1 -1
  335. package/workflow/WorkflowVersionBadge.js +2 -1
  336. package/workflow/WorkflowVersionBadge.js.map +1 -1
  337. package/workflow/WorkflowVersionTimeline.d.ts.map +1 -1
  338. package/workflow/WorkflowVersionTimeline.js +2 -1
  339. package/workflow/WorkflowVersionTimeline.js.map +1 -1
  340. package/workflow/execution-comparison/ExecutionComparisonView.d.ts.map +1 -1
  341. package/workflow/execution-comparison/ExecutionComparisonView.js +2 -1
  342. package/workflow/execution-comparison/ExecutionComparisonView.js.map +1 -1
  343. package/workflow/execution-comparison/TaskComparisonTable.d.ts.map +1 -1
  344. package/workflow/execution-comparison/TaskComparisonTable.js +2 -1
  345. package/workflow/execution-comparison/TaskComparisonTable.js.map +1 -1
  346. package/workflow/execution-history/ExecutionHistoryTable.d.ts.map +1 -1
  347. package/workflow/execution-history/ExecutionHistoryTable.js +6 -4
  348. package/workflow/execution-history/ExecutionHistoryTable.js.map +1 -1
  349. package/workflow/inspector/InspectorHeader.d.ts.map +1 -1
  350. package/workflow/inspector/InspectorHeader.js +4 -2
  351. package/workflow/inspector/InspectorHeader.js.map +1 -1
  352. package/workflow/inspector/NestedTaskList.d.ts.map +1 -1
  353. package/workflow/inspector/NestedTaskList.js +2 -1
  354. package/workflow/inspector/NestedTaskList.js.map +1 -1
  355. package/workflow/inspector/WorkflowSummaryPanel.d.ts.map +1 -1
  356. package/workflow/inspector/WorkflowSummaryPanel.js +2 -1
  357. package/workflow/inspector/WorkflowSummaryPanel.js.map +1 -1
  358. package/workflow/inspector/tabs/BranchesTab.d.ts.map +1 -1
  359. package/workflow/inspector/tabs/BranchesTab.js +5 -2
  360. package/workflow/inspector/tabs/BranchesTab.js.map +1 -1
  361. package/workflow/instance/WorkflowInstanceList.d.ts.map +1 -1
  362. package/workflow/instance/WorkflowInstanceList.js +2 -1
  363. package/workflow/instance/WorkflowInstanceList.js.map +1 -1
  364. package/workflow/node-shell/DiffBadge.d.ts.map +1 -1
  365. package/workflow/node-shell/DiffBadge.js +3 -2
  366. package/workflow/node-shell/DiffBadge.js.map +1 -1
  367. package/workflow/node-shell/ExecutionBadge.d.ts.map +1 -1
  368. package/workflow/node-shell/ExecutionBadge.js +5 -4
  369. package/workflow/node-shell/ExecutionBadge.js.map +1 -1
  370. package/workflow/node-shell/NodeActions.d.ts.map +1 -1
  371. package/workflow/node-shell/NodeActions.js +12 -11
  372. package/workflow/node-shell/NodeActions.js.map +1 -1
  373. package/workflow/task-detail/StructuredDataViewer.d.ts.map +1 -1
  374. package/workflow/task-detail/StructuredDataViewer.js +5 -1
  375. package/workflow/task-detail/StructuredDataViewer.js.map +1 -1
  376. package/workspace/EditorTabs.d.ts.map +1 -1
  377. package/workspace/EditorTabs.js +38 -37
  378. package/workspace/EditorTabs.js.map +1 -1
  379. package/workspace/ExplorerTree.d.ts.map +1 -1
  380. package/workspace/ExplorerTree.js +2 -1
  381. package/workspace/ExplorerTree.js.map +1 -1
  382. package/workspace/FileViewer.d.ts.map +1 -1
  383. package/workspace/FileViewer.js +2 -1
  384. package/workspace/FileViewer.js.map +1 -1
  385. package/workspace/PanelChip.d.ts.map +1 -1
  386. package/workspace/PanelChip.js +2 -1
  387. package/workspace/PanelChip.js.map +1 -1
  388. package/workspace/WorkspaceEditor.d.ts.map +1 -1
  389. package/workspace/WorkspaceEditor.js +2 -1
  390. package/workspace/WorkspaceEditor.js.map +1 -1
  391. package/workspace/WorkspaceSummary.d.ts.map +1 -1
  392. package/workspace/WorkspaceSummary.js +7 -2
  393. package/workspace/WorkspaceSummary.js.map +1 -1
  394. package/workspace/WorkspaceSurface.d.ts.map +1 -1
  395. package/workspace/WorkspaceSurface.js +12 -11
  396. package/workspace/WorkspaceSurface.js.map +1 -1
@@ -19,6 +19,7 @@ import {
19
19
  import { useAutoScroll } from "../internal/useAutoScroll.js";
20
20
  import { ConversationMediaAttachment } from "./ConversationMediaAttachment.js";
21
21
  import {
22
+ attemptExplanationOf,
22
23
  authorKindOf,
23
24
  inboundPlaceholderOf,
24
25
  isInternalItem,
@@ -228,8 +229,10 @@ const TimelineItemRow = memo(function TimelineItemRow({
228
229
  // relay, never pattern-matched; the numeric twin (receipt_error_code)
229
230
  // stays off the surface as machine vocabulary. Gated exactly like
230
231
  // ReceiptTicks' failed arm — attempt delivered AND receipt failed —
231
- // so the attempt-axis explanation (last_error, F-25's slice) can
232
- // structurally never leak in here.
232
+ // so the attempt-axis explanation (attemptExplanationOf, cloud#262's
233
+ // slice) can structurally never leak in here: its gate requires
234
+ // attempt FAILED, this one attempt delivered, so at most one of the
235
+ // two explanations ever renders.
233
236
  const receiptExplanation =
234
237
  sendAttemptOf(item) === "delivered" &&
235
238
  receiptOf(item) === "failed" &&
@@ -237,6 +240,15 @@ const TimelineItemRow = memo(function TimelineItemRow({
237
240
  ? item.receiptDetail
238
241
  : null;
239
242
 
243
+ // The attempt axis's explanation (cloud#262, closing F-25), the same
244
+ // R-1 visible-text treatment as the receipt explanation and for the
245
+ // same reason: why a send failed is decision-bearing (a closed
246
+ // 24h-window refusal says "the customer must write first"; a deleted
247
+ // channel says there is nothing to retry). The words and the gate
248
+ // live in attemptExplanationOf — presentation vocabulary, never
249
+ // computed in the view.
250
+ const attemptExplanation = attemptExplanationOf(item);
251
+
240
252
  return (
241
253
  <li className={cn("stg:flex", isCustomer ? "stg:justify-start" : "stg:justify-end")}>
242
254
  <div
@@ -274,6 +286,11 @@ const TimelineItemRow = memo(function TimelineItemRow({
274
286
  {receiptExplanation}
275
287
  </p>
276
288
  )}
289
+ {attemptExplanation !== null && (
290
+ <p className="stg:mt-1 stg:break-words stg:text-xs stg:text-destructive">
291
+ {attemptExplanation}
292
+ </p>
293
+ )}
277
294
  <ItemFooter item={item} />
278
295
  </div>
279
296
  </li>
@@ -10,7 +10,10 @@ import {
10
10
  ConversationLane,
11
11
  ConversationTimelineItemSchema,
12
12
  } from "@stigmer/protos/ai/stigmer/agentic/agentchannel/v1/conversation_io_pb";
13
- import { ChannelDeliveryStatus } from "@stigmer/protos/ai/stigmer/agentic/agentchannel/v1/delivery_pb";
13
+ import {
14
+ ChannelAttemptFailureKind,
15
+ ChannelDeliveryStatus,
16
+ } from "@stigmer/protos/ai/stigmer/agentic/agentchannel/v1/delivery_pb";
14
17
  import { ChannelReceiptState } from "@stigmer/protos/ai/stigmer/agentic/agentchannel/v1/outbound_pb";
15
18
  import { StigmerContext } from "../../context";
16
19
  import { ConversationTimelineView } from "../ConversationTimelineView";
@@ -174,8 +177,9 @@ describe("ConversationTimelineView", () => {
174
177
  text: "lost reply",
175
178
  deliveryStatus: ChannelDeliveryStatus.failed,
176
179
  // A malformed item carrying a detail on the wrong axis: the
177
- // attempt-axis explanation (last_error) is F-25's separate
178
- // slice, so this lane renders nothing extra.
180
+ // attempt-axis explanation has its own field and gate
181
+ // (attemptExplanationOf, cloud#262), so a stray receipt
182
+ // detail still renders nothing here.
179
183
  receiptDetail: "should never render",
180
184
  }),
181
185
  ]}
@@ -185,6 +189,105 @@ describe("ConversationTimelineView", () => {
185
189
  expect(screen.queryByText("should never render")).toBeNull();
186
190
  });
187
191
 
192
+ it("shows a refused send's platform-authored explanation verbatim (cloud#262)", () => {
193
+ render(
194
+ <ConversationTimelineView
195
+ {...baseProps()}
196
+ items={[
197
+ item("ob:1", {
198
+ author: ConversationItemAuthor.author_teammate,
199
+ text: "Following up on your case.",
200
+ deliveryStatus: ChannelDeliveryStatus.failed,
201
+ attemptFailureKind: ChannelAttemptFailureKind.attempt_refused,
202
+ attemptDetail: "the recipient's 24-hour customer-service window is closed",
203
+ }),
204
+ ]}
205
+ />,
206
+ );
207
+ expect(screen.getByText("Not delivered")).toBeDefined();
208
+ expect(
209
+ screen.getByText("the recipient's 24-hour customer-service window is closed"),
210
+ ).toBeDefined();
211
+ });
212
+
213
+ it("shows a withdrawn send's structural fact verbatim (cloud#262)", () => {
214
+ render(
215
+ <ConversationTimelineView
216
+ {...baseProps()}
217
+ items={[
218
+ item("dl:1", {
219
+ author: ConversationItemAuthor.author_agent,
220
+ text: "the reply that never landed",
221
+ deliveryStatus: ChannelDeliveryStatus.failed,
222
+ attemptFailureKind: ChannelAttemptFailureKind.attempt_withdrawn,
223
+ attemptDetail: "channel deleted",
224
+ }),
225
+ ]}
226
+ />,
227
+ );
228
+ expect(screen.getByText("channel deleted")).toBeDefined();
229
+ });
230
+
231
+ it("shows this surface's own generic copy for an errored send — the wire carries no detail (cloud#262)", () => {
232
+ render(
233
+ <ConversationTimelineView
234
+ {...baseProps()}
235
+ items={[
236
+ item("dl:1", {
237
+ author: ConversationItemAuthor.author_agent,
238
+ text: "lost reply",
239
+ deliveryStatus: ChannelDeliveryStatus.failed,
240
+ attemptFailureKind: ChannelAttemptFailureKind.attempt_errored,
241
+ // attemptDetail deliberately empty: the server's AttemptFailure
242
+ // construction keeps diagnostics off the wire entirely.
243
+ }),
244
+ ]}
245
+ />,
246
+ );
247
+ expect(screen.getByText("Not delivered")).toBeDefined();
248
+ expect(
249
+ screen.getByText("Delivery failed due to a technical problem on the platform's side."),
250
+ ).toBeDefined();
251
+ });
252
+
253
+ it("keeps the bare tick for a failure the platform never classified (pre-cloud#262 rows)", () => {
254
+ render(
255
+ <ConversationTimelineView
256
+ {...baseProps()}
257
+ items={[
258
+ item("ob:1", {
259
+ author: ConversationItemAuthor.author_teammate,
260
+ text: "an old failed send",
261
+ deliveryStatus: ChannelDeliveryStatus.failed,
262
+ }),
263
+ ]}
264
+ />,
265
+ );
266
+ expect(screen.getByText("Not delivered")).toBeDefined();
267
+ expect(screen.queryByText(/technical problem/)).toBeNull();
268
+ });
269
+
270
+ it("never shows the attempt explanation on a delivered item — the F-25 boundary's mirror", () => {
271
+ render(
272
+ <ConversationTimelineView
273
+ {...baseProps()}
274
+ items={[
275
+ item("ob:1", {
276
+ author: ConversationItemAuthor.author_teammate,
277
+ text: "a landed send",
278
+ deliveryStatus: ChannelDeliveryStatus.delivered,
279
+ // A malformed item carrying attempt copy on a success: the
280
+ // gate requires attempt FAILED, so nothing renders.
281
+ attemptFailureKind: ChannelAttemptFailureKind.attempt_refused,
282
+ attemptDetail: "should never render",
283
+ }),
284
+ ]}
285
+ />,
286
+ );
287
+ expect(screen.queryByText("should never render")).toBeNull();
288
+ expect(screen.queryByText("Not delivered")).toBeNull();
289
+ });
290
+
188
291
  it("renders a dead-lettered send as exactly that", () => {
189
292
  render(
190
293
  <ConversationTimelineView
@@ -6,7 +6,10 @@ import {
6
6
  ConversationLane,
7
7
  type ConversationTimelineItem,
8
8
  } from "@stigmer/protos/ai/stigmer/agentic/agentchannel/v1/conversation_io_pb";
9
- import { ChannelDeliveryStatus } from "@stigmer/protos/ai/stigmer/agentic/agentchannel/v1/delivery_pb";
9
+ import {
10
+ ChannelAttemptFailureKind,
11
+ ChannelDeliveryStatus,
12
+ } from "@stigmer/protos/ai/stigmer/agentic/agentchannel/v1/delivery_pb";
10
13
  import { ChannelReceiptState } from "@stigmer/protos/ai/stigmer/agentic/agentchannel/v1/outbound_pb";
11
14
  import type { ChannelProviderId } from "../channel/providers.js";
12
15
 
@@ -106,6 +109,54 @@ export function receiptOf(item: ConversationTimelineItem): ReceiptKind | null {
106
109
  }
107
110
  }
108
111
 
112
+ /**
113
+ * Why a FAILED send attempt failed, in render vocabulary. `null` until
114
+ * the platform classifies (rows failed before the classification
115
+ * existed, and every non-failed item).
116
+ *
117
+ * The structured twin of {@link attemptExplanationOf}'s prose
118
+ * (cloud#262): a renderer that branches on the failure keys on this,
119
+ * never on the explanation's words.
120
+ */
121
+ export type AttemptFailureKind = "refused" | "errored" | "withdrawn";
122
+
123
+ /** Map the wire attempt-failure kind onto the render vocabulary. */
124
+ export function attemptFailureOf(item: ConversationTimelineItem): AttemptFailureKind | null {
125
+ switch (item.attemptFailureKind) {
126
+ case ChannelAttemptFailureKind.attempt_refused:
127
+ return "refused";
128
+ case ChannelAttemptFailureKind.attempt_errored:
129
+ return "errored";
130
+ case ChannelAttemptFailureKind.attempt_withdrawn:
131
+ return "withdrawn";
132
+ default:
133
+ return null;
134
+ }
135
+ }
136
+
137
+ /**
138
+ * The visible explanation for a FAILED send attempt, or `null` when the
139
+ * item carries none to show (non-failed items, and rows failed before
140
+ * the platform classified failures).
141
+ *
142
+ * The attempt-axis sibling of the receipt explanation (cloud#262,
143
+ * closing F-25): `attempt_detail` is PLATFORM-authored thread-safe copy
144
+ * — the write side guarantees raw diagnostics never occupy it — so it
145
+ * renders verbatim when present (refusals and withdrawn sends). An
146
+ * `errored` failure deliberately carries no detail (its diagnostic is
147
+ * operator-only, in `last_error` server-side); the copy here is this
148
+ * surface's own generic for that arm, and it makes NO claim about
149
+ * attempt counts — the wire does not carry them, and `errored` covers
150
+ * first-try structural dead-letters as well as exhausted retries.
151
+ */
152
+ export function attemptExplanationOf(item: ConversationTimelineItem): string | null {
153
+ if (sendAttemptOf(item) !== "failed") return null;
154
+ if (item.attemptDetail !== "") return item.attemptDetail;
155
+ return attemptFailureOf(item) === "errored"
156
+ ? "Delivery failed due to a technical problem on the platform's side."
157
+ : null;
158
+ }
159
+
109
160
  /**
110
161
  * Placeholder copy for inbound items whose body the platform cannot
111
162
  * render (`text` empty, `provider_message_type` names the kind) — the
@@ -9,6 +9,7 @@ import {
9
9
  } from "@stigmer/protos/ai/stigmer/platform/cursoraccount/v1/io_pb";
10
10
  import type { CursorMemberSpend } from "@stigmer/protos/ai/stigmer/platform/cursoraccount/v1/cursor_account_pb";
11
11
  import { Button } from "../button/index.js";
12
+ import { TruncatedText } from "../internal/truncated-text.js";
12
13
  import { StateBadge } from "./badges.js";
13
14
  import { formatPoolPercent, formatSpendMicros } from "./cursor-account-format.js";
14
15
  import type { CursorAccountCoverage } from "./cursor-account-coverage.js";
@@ -225,9 +226,9 @@ function CoverageGroup({
225
226
  * The member-identity cell shared by key rows and gap rows. The email is
226
227
  * the row's identity, so it must be fully readable: it wraps onto more
227
228
  * lines when the column is narrow (emails are unbroken strings, so they
228
- * need explicit word-breaking) with a hover tooltip as backup, unlike
229
- * the other text cells which truncate. The secondary name line stays
230
- * truncated — it is display sugar, not identity.
229
+ * need explicit word-breaking), unlike the other text cells which
230
+ * truncate. The secondary name line stays truncated — it is display
231
+ * sugar, not identity.
231
232
  */
232
233
  function MemberCell({
233
234
  email,
@@ -238,7 +239,9 @@ function MemberCell({
238
239
  }) {
239
240
  return (
240
241
  <span role="cell" className="stg:min-w-0">
241
- <span className="stg:block stg:break-words stg:font-medium stg:text-foreground" title={email}>
242
+ {/* No tooltip: break-words shows the full email, so a hover hint
243
+ would only repeat what is already visible. */}
244
+ <span className="stg:block stg:break-words stg:font-medium stg:text-foreground">
242
245
  {email}
243
246
  </span>
244
247
  {name && (
@@ -273,16 +276,15 @@ function KeyRow({
273
276
  <div role="row" className={cn(ROW_GRID, "stg:border-t stg:border-border-muted stg:text-xs")}>
274
277
  <MemberCell email={key.boundEmail} />
275
278
  <span role="cell" className="stg:min-w-0">
276
- <span
277
- className="stg:block stg:truncate stg:text-muted-foreground"
278
- title={key.cursorKeyName || undefined}
279
- >
280
- {key.cursorKeyName || "unnamed key"}
281
- </span>
279
+ <TruncatedText
280
+ text={key.cursorKeyName || "unnamed key"}
281
+ className="stg:block stg:text-muted-foreground"
282
+ />
282
283
  {key.label && (
283
- <span className="stg:block stg:truncate stg:text-[11px] stg:text-muted-foreground" title={key.label}>
284
- {key.label}
285
- </span>
284
+ <TruncatedText
285
+ text={key.label}
286
+ className="stg:block stg:text-[11px] stg:text-muted-foreground"
287
+ />
286
288
  )}
287
289
  </span>
288
290
  <SpendCells spend={keyView.spend} />
@@ -237,10 +237,10 @@ describe("CursorAccountsConsole", () => {
237
237
  expect(screen.getByText(/On the team — key held/)).toBeTruthy();
238
238
  expect(screen.getByText("zane@scenar.ai")).toBeTruthy();
239
239
  expect(screen.getByText("stigmer-prod")).toBeTruthy();
240
- // The email is the row's identity: full value always hoverable on
241
- // both row kinds (the cell wraps rather than truncates).
242
- expect(screen.getByTitle("zane@scenar.ai")).toBeTruthy();
243
- expect(screen.getByTitle("uncovered@scenar.ai")).toBeTruthy();
240
+ // The email is the row's identity: the cell wraps rather than
241
+ // truncates, so the full value is always visible text on both row
242
+ // kinds — and never a native title (banned, stigmer-cloud#268).
243
+ expect(document.querySelector("[title]")).toBeNull();
244
244
  expect(screen.getByText("$0.17")).toBeTruthy(); // included, 169342 micro-USD
245
245
  expect(screen.getByText("Active")).toBeTruthy();
246
246
  // Category 2: on the team, no key — server-joined spend on the gap row.
@@ -103,11 +103,12 @@ export const DashboardKPICards = memo(function DashboardKPICards({
103
103
  <div className={cn("stg:grid stg:gap-3 stg:sm:grid-cols-2 stg:lg:grid-cols-4", className)}>
104
104
  {STAT_CARDS.map((card) => {
105
105
  const breakdown = card.getBreakdown(summary);
106
+ // No tooltip on the card: the breakdown is rendered visibly below
107
+ // the value, so the old title only repeated on-screen text.
106
108
  return (
107
109
  <div
108
110
  key={card.label}
109
111
  className="stg:rounded-lg stg:border stg:border-border stg:bg-card stg:px-4 stg:py-3"
110
- title={breakdown ?? undefined}
111
112
  >
112
113
  <p className="stg:text-xs stg:font-medium stg:text-muted-foreground">
113
114
  {card.label}
@@ -14,6 +14,7 @@ import type {
14
14
  CollectionDescription,
15
15
  RecordEnvelope,
16
16
  } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/record_io_pb";
17
+ import { TruncatedText } from "../internal/truncated-text.js";
17
18
  import { ResourceTable } from "../resource-workbench/components/ResourceTable.js";
18
19
  import { ConfirmDialog } from "../resource-detail/ConfirmDialog.js";
19
20
  import { useConfirmAction } from "../resource-detail/useConfirmAction.js";
@@ -505,18 +506,18 @@ function TypedCell({
505
506
  readonly numeric?: boolean;
506
507
  readonly mono?: boolean;
507
508
  }) {
508
- const truncated = value.length > 80 ? `${value.slice(0, 79)}…` : value;
509
+ // TruncatedText's overflow gating supersedes the old hand-rolled
510
+ // 80-character slice + conditional title: CSS truncates the cell and the
511
+ // tooltip opens only when the value is actually clipped.
509
512
  return (
510
- <span
511
- title={value.length > 80 ? value : undefined}
513
+ <TruncatedText
514
+ text={value}
512
515
  className={cn(
513
- "stg:block stg:max-w-xs stg:truncate",
516
+ "stg:block stg:max-w-xs",
514
517
  numeric && "stg:text-right stg:tabular-nums",
515
518
  mono && "stg:font-mono",
516
519
  )}
517
- >
518
- {truncated}
519
- </span>
520
+ />
520
521
  );
521
522
  }
522
523
 
@@ -12,6 +12,7 @@ import { getUserMessage, type EnvVarInput } from "@stigmer/sdk";
12
12
  import type { Environment } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/api_pb";
13
13
  import { useStigmer } from "../hooks.js";
14
14
  import { toError } from "../internal/toError.js";
15
+ import { Tooltip, TooltipContent, TooltipTrigger } from "../internal/tooltip.js";
15
16
  import { PermissionGate } from "../iam-policy/PermissionGate.js";
16
17
  import { useUpdateEnvironmentVariables } from "./useUpdateEnvironmentVariables.js";
17
18
  import { useRemoveEnvironmentVariables } from "./useRemoveEnvironmentVariables.js";
@@ -390,12 +391,22 @@ function VariableRow({
390
391
  <div className="stg:group stg:flex stg:items-center stg:gap-3 stg:py-2">
391
392
  {/* Key + badge */}
392
393
  <div className="stg:flex stg:shrink-0 stg:items-baseline stg:gap-1.5">
393
- <span
394
- className="stg:font-mono stg:text-xs stg:font-medium stg:text-foreground"
395
- title={variable.description || undefined}
396
- >
397
- {variable.key}
398
- </span>
394
+ {variable.description ? (
395
+ <Tooltip>
396
+ <TooltipTrigger
397
+ render={
398
+ <span className="stg:font-mono stg:text-xs stg:font-medium stg:text-foreground" />
399
+ }
400
+ >
401
+ {variable.key}
402
+ </TooltipTrigger>
403
+ <TooltipContent side="top">{variable.description}</TooltipContent>
404
+ </Tooltip>
405
+ ) : (
406
+ <span className="stg:font-mono stg:text-xs stg:font-medium stg:text-foreground">
407
+ {variable.key}
408
+ </span>
409
+ )}
399
410
  {variable.isSecret && (
400
411
  <span className="stg:text-[0.55rem] stg:uppercase stg:tracking-wider stg:text-muted-foreground-subtle">
401
412
  secret
@@ -407,12 +407,10 @@ export function ApprovalCardBody({
407
407
  </div>
408
408
 
409
409
  {/* De-emphasized provenance, trailing the decision it explains. Only the
410
- informative reasons reach here (see showGateReason); the full phrase is
411
- on hover. */}
410
+ informative reasons reach here (see showGateReason). */}
412
411
  {showGateReason && (
413
412
  <p
414
413
  className="stg:text-[11px] stg:italic stg:text-muted-foreground"
415
- title={gateReason ?? undefined}
416
414
  data-cursor-target="approval-gate-reason"
417
415
  >
418
416
  {gateReason}
@@ -5,6 +5,7 @@
5
5
 
6
6
  import { cn } from "@stigmer/theme";
7
7
  import { FileTypeIcon, FolderTypeIcon } from "../internal/file-icons/index.js";
8
+ import { Tooltip, TooltipContent, TooltipTrigger } from "../internal/tooltip.js";
8
9
  import type { ArtifactRowItem } from "./artifact-row-item.js";
9
10
  import { formatArtifactSize } from "./artifact-utils.js";
10
11
 
@@ -59,54 +60,73 @@ export function ArtifactRowView({
59
60
  }: ArtifactRowViewProps) {
60
61
  return (
61
62
  <li className={cn("stg:group stg:flex stg:items-stretch", className)}>
62
- <button
63
- type="button"
64
- onClick={onOpen}
65
- onDoubleClick={onActivate}
66
- title={item.tooltip}
67
- className={cn(
68
- "stg:flex stg:min-w-0 stg:flex-1 stg:items-center stg:gap-2 stg:px-2 stg:py-1 stg:text-left stg:text-xs stg:text-muted-foreground stg:transition-colors",
69
- "stg:hover:bg-muted stg:hover:text-foreground",
70
- "stg:focus-visible:outline-none stg:focus-visible:ring-2 stg:focus-visible:ring-inset stg:focus-visible:ring-ring",
71
- )}
72
- >
73
- <span className="stg:shrink-0 stg:text-muted-foreground">
74
- {item.isDirectory ? (
75
- <FolderTypeIcon open={false} />
76
- ) : (
77
- <FileTypeIcon fileName={item.name} />
78
- )}
79
- </span>
80
- <span className="stg:min-w-0 stg:flex-1 stg:truncate stg:text-foreground">
81
- {item.name}
82
- {item.isDirectory && "/"}
83
- {item.subtitlePath && (
84
- <span className="stg:ml-1.5 stg:text-[0.65rem] stg:text-muted-foreground">
85
- {item.subtitlePath}
86
- </span>
87
- )}
88
- </span>
89
- <span className="stg:shrink-0 stg:tabular-nums stg:text-[0.65rem] stg:text-muted-foreground-faint">
90
- {formatArtifactSize(item.sizeBytes)}
91
- </span>
92
- </button>
93
- <button
94
- type="button"
95
- onClick={onDownload}
96
- disabled={isDownloading}
97
- aria-label={
98
- isDownloading ? `Preparing ${item.name}` : `Download ${item.name}`
99
- }
100
- title={item.isDirectory ? "Download ZIP" : "Download"}
101
- className={cn(
102
- "stg:flex stg:shrink-0 stg:items-center stg:px-2 stg:text-muted-foreground stg:opacity-0 stg:transition-opacity",
103
- "stg:group-hover:opacity-100 stg:focus-visible:opacity-100",
104
- "stg:hover:text-foreground stg:disabled:opacity-50",
105
- "stg:focus-visible:outline-none stg:focus-visible:ring-2 stg:focus-visible:ring-inset stg:focus-visible:ring-ring",
106
- )}
107
- >
108
- <DownloadIcon />
109
- </button>
63
+ {/* The open button is its own tooltip trigger (hover and keyboard focus
64
+ both reveal the full path); the Download button's trigger is a
65
+ wrapper span so the hint survives the in-flight `disabled` state
66
+ (browsers suppress pointer events on disabled form controls, so a
67
+ disabled button can never open its own tooltip). */}
68
+ <Tooltip>
69
+ <TooltipTrigger
70
+ render={
71
+ <button
72
+ type="button"
73
+ onClick={onOpen}
74
+ onDoubleClick={onActivate}
75
+ className={cn(
76
+ "stg:flex stg:min-w-0 stg:flex-1 stg:items-center stg:gap-2 stg:px-2 stg:py-1 stg:text-left stg:text-xs stg:text-muted-foreground stg:transition-colors",
77
+ "stg:hover:bg-muted stg:hover:text-foreground",
78
+ "stg:focus-visible:outline-none stg:focus-visible:ring-2 stg:focus-visible:ring-inset stg:focus-visible:ring-ring",
79
+ )}
80
+ />
81
+ }
82
+ >
83
+ <span className="stg:shrink-0 stg:text-muted-foreground">
84
+ {item.isDirectory ? (
85
+ <FolderTypeIcon open={false} />
86
+ ) : (
87
+ <FileTypeIcon fileName={item.name} />
88
+ )}
89
+ </span>
90
+ <span className="stg:min-w-0 stg:flex-1 stg:truncate stg:text-foreground">
91
+ {item.name}
92
+ {item.isDirectory && "/"}
93
+ {item.subtitlePath && (
94
+ <span className="stg:ml-1.5 stg:text-[0.65rem] stg:text-muted-foreground">
95
+ {item.subtitlePath}
96
+ </span>
97
+ )}
98
+ </span>
99
+ <span className="stg:shrink-0 stg:tabular-nums stg:text-[0.65rem] stg:text-muted-foreground-faint">
100
+ {formatArtifactSize(item.sizeBytes)}
101
+ </span>
102
+ </TooltipTrigger>
103
+ <TooltipContent side="top" className="stg:break-all">
104
+ {item.tooltip}
105
+ </TooltipContent>
106
+ </Tooltip>
107
+ <Tooltip>
108
+ <TooltipTrigger render={<span className="stg:flex stg:shrink-0" />}>
109
+ <button
110
+ type="button"
111
+ onClick={onDownload}
112
+ disabled={isDownloading}
113
+ aria-label={
114
+ isDownloading ? `Preparing ${item.name}` : `Download ${item.name}`
115
+ }
116
+ className={cn(
117
+ "stg:flex stg:shrink-0 stg:items-center stg:px-2 stg:text-muted-foreground stg:opacity-0 stg:transition-opacity",
118
+ "stg:group-hover:opacity-100 stg:focus-visible:opacity-100",
119
+ "stg:hover:text-foreground stg:disabled:opacity-50",
120
+ "stg:focus-visible:outline-none stg:focus-visible:ring-2 stg:focus-visible:ring-inset stg:focus-visible:ring-ring",
121
+ )}
122
+ >
123
+ <DownloadIcon />
124
+ </button>
125
+ </TooltipTrigger>
126
+ <TooltipContent side="top">
127
+ {item.isDirectory ? "Download ZIP" : "Download"}
128
+ </TooltipContent>
129
+ </Tooltip>
110
130
  </li>
111
131
  );
112
132
  }