@stigmer/react 3.12.3 → 3.12.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.
- package/agent/steps/IdentityStep.d.ts.map +1 -1
- package/agent/steps/IdentityStep.js +2 -1
- package/agent/steps/IdentityStep.js.map +1 -1
- package/agent-instance/AgentInstanceDetailPanel.d.ts.map +1 -1
- package/agent-instance/AgentInstanceDetailPanel.js +2 -1
- package/agent-instance/AgentInstanceDetailPanel.js.map +1 -1
- package/api-key/CreateApiKeyForm.d.ts.map +1 -1
- package/api-key/CreateApiKeyForm.js +2 -1
- package/api-key/CreateApiKeyForm.js.map +1 -1
- package/attachment/AttachmentChipList.js +16 -6
- package/attachment/AttachmentChipList.js.map +1 -1
- package/attachment/index.d.ts +1 -1
- package/attachment/index.d.ts.map +1 -1
- package/attachment/index.js.map +1 -1
- package/attachment/useAttachments.d.ts +51 -8
- package/attachment/useAttachments.d.ts.map +1 -1
- package/attachment/useAttachments.js +74 -3
- package/attachment/useAttachments.js.map +1 -1
- package/channel/ChannelConversationsDialog.d.ts.map +1 -1
- package/channel/ChannelConversationsDialog.js +2 -1
- package/channel/ChannelConversationsDialog.js.map +1 -1
- package/channel/ChannelTemplatesDialog.d.ts.map +1 -1
- package/channel/ChannelTemplatesDialog.js +2 -1
- package/channel/ChannelTemplatesDialog.js.map +1 -1
- package/channel/ConnectSlackDialog.d.ts.map +1 -1
- package/channel/ConnectSlackDialog.js +2 -1
- package/channel/ConnectSlackDialog.js.map +1 -1
- package/channel/connect/ServingAppSection.d.ts.map +1 -1
- package/channel/connect/ServingAppSection.js +2 -1
- package/channel/connect/ServingAppSection.js.map +1 -1
- package/channel-app/CreateChannelAppForm.d.ts.map +1 -1
- package/channel-app/CreateChannelAppForm.js +2 -1
- package/channel-app/CreateChannelAppForm.js.map +1 -1
- package/composer/SessionComposer.d.ts.map +1 -1
- package/composer/SessionComposer.js +17 -10
- package/composer/SessionComposer.js.map +1 -1
- package/conversation/ConversationComposer.d.ts +19 -3
- package/conversation/ConversationComposer.d.ts.map +1 -1
- package/conversation/ConversationComposer.js +4 -4
- package/conversation/ConversationComposer.js.map +1 -1
- package/conversation/ConversationListPane.d.ts.map +1 -1
- package/conversation/ConversationListPane.js +2 -1
- package/conversation/ConversationListPane.js.map +1 -1
- package/conversation/ConversationTemplatePickerDialog.d.ts +63 -0
- package/conversation/ConversationTemplatePickerDialog.d.ts.map +1 -0
- package/conversation/ConversationTemplatePickerDialog.js +151 -0
- package/conversation/ConversationTemplatePickerDialog.js.map +1 -0
- package/conversation/ConversationTimelineView.d.ts.map +1 -1
- package/conversation/ConversationTimelineView.js +2 -1
- package/conversation/ConversationTimelineView.js.map +1 -1
- package/conversation/ConversationsWorkbench.d.ts.map +1 -1
- package/conversation/ConversationsWorkbench.js +16 -5
- package/conversation/ConversationsWorkbench.js.map +1 -1
- package/conversation/index.d.ts +3 -1
- package/conversation/index.d.ts.map +1 -1
- package/conversation/index.js +1 -0
- package/conversation/index.js.map +1 -1
- package/conversation/useConversationParticipation.d.ts +41 -4
- package/conversation/useConversationParticipation.d.ts.map +1 -1
- package/conversation/useConversationParticipation.js +13 -2
- package/conversation/useConversationParticipation.js.map +1 -1
- package/cursor-accounts/CursorAccountsConsole.d.ts.map +1 -1
- package/cursor-accounts/CursorAccountsConsole.js +2 -1
- package/cursor-accounts/CursorAccountsConsole.js.map +1 -1
- package/cursor-accounts/MemberKeysPanel.d.ts.map +1 -1
- package/cursor-accounts/MemberKeysPanel.js +2 -1
- package/cursor-accounts/MemberKeysPanel.js.map +1 -1
- package/dashboard/DashboardFailedRuns.d.ts.map +1 -1
- package/dashboard/DashboardFailedRuns.js +2 -1
- package/dashboard/DashboardFailedRuns.js.map +1 -1
- package/dependency-graph/DependencyGraph.d.ts.map +1 -1
- package/dependency-graph/DependencyGraph.js +2 -1
- package/dependency-graph/DependencyGraph.js.map +1 -1
- package/dependency-graph/DependencyTreeNode.d.ts.map +1 -1
- package/dependency-graph/DependencyTreeNode.js +2 -1
- package/dependency-graph/DependencyTreeNode.js.map +1 -1
- package/environment/EnvironmentPicker.d.ts.map +1 -1
- package/environment/EnvironmentPicker.js +2 -1
- package/environment/EnvironmentPicker.js.map +1 -1
- package/error/SecretFlowErrorGuide.d.ts.map +1 -1
- package/error/SecretFlowErrorGuide.js +2 -1
- package/error/SecretFlowErrorGuide.js.map +1 -1
- package/execution/ArtifactContentBody.d.ts.map +1 -1
- package/execution/ArtifactContentBody.js +2 -1
- package/execution/ArtifactContentBody.js.map +1 -1
- package/execution/ArtifactsWidget.d.ts.map +1 -1
- package/execution/ArtifactsWidget.js +2 -1
- package/execution/ArtifactsWidget.js.map +1 -1
- package/execution/FileChangeProgressBar.d.ts.map +1 -1
- package/execution/FileChangeProgressBar.js +2 -1
- package/execution/FileChangeProgressBar.js.map +1 -1
- package/execution/TodoList.d.ts.map +1 -1
- package/execution/TodoList.js +2 -1
- package/execution/TodoList.js.map +1 -1
- package/execution/WriteBacksWidget.d.ts.map +1 -1
- package/execution/WriteBacksWidget.js +2 -1
- package/execution/WriteBacksWidget.js.map +1 -1
- package/execution/tool-categories.d.ts +5 -22
- package/execution/tool-categories.d.ts.map +1 -1
- package/execution/tool-categories.js +8 -38
- package/execution/tool-categories.js.map +1 -1
- package/iam-policy/PeopleWithAccess.d.ts.map +1 -1
- package/iam-policy/PeopleWithAccess.js +2 -1
- package/iam-policy/PeopleWithAccess.js.map +1 -1
- package/iam-policy/PrincipalPicker.d.ts.map +1 -1
- package/iam-policy/PrincipalPicker.js +2 -1
- package/iam-policy/PrincipalPicker.js.map +1 -1
- package/iam-policy/RoleSelector.d.ts.map +1 -1
- package/iam-policy/RoleSelector.js +2 -1
- package/iam-policy/RoleSelector.js.map +1 -1
- package/identity-account/AccountPreferencesPanel.d.ts +35 -0
- package/identity-account/AccountPreferencesPanel.d.ts.map +1 -0
- package/identity-account/AccountPreferencesPanel.js +187 -0
- package/identity-account/AccountPreferencesPanel.js.map +1 -0
- package/identity-account/index.d.ts +8 -0
- package/identity-account/index.d.ts.map +1 -1
- package/identity-account/index.js +4 -0
- package/identity-account/index.js.map +1 -1
- package/identity-account/useAccountExecutionDefaults.d.ts +36 -0
- package/identity-account/useAccountExecutionDefaults.d.ts.map +1 -0
- package/identity-account/useAccountExecutionDefaults.js +46 -0
- package/identity-account/useAccountExecutionDefaults.js.map +1 -0
- package/identity-account/useMyIdentityAccount.d.ts +55 -0
- package/identity-account/useMyIdentityAccount.d.ts.map +1 -0
- package/identity-account/useMyIdentityAccount.js +37 -0
- package/identity-account/useMyIdentityAccount.js.map +1 -0
- package/identity-account/useUpdateIdentityAccount.d.ts +42 -0
- package/identity-account/useUpdateIdentityAccount.d.ts.map +1 -0
- package/identity-account/useUpdateIdentityAccount.js +53 -0
- package/identity-account/useUpdateIdentityAccount.js.map +1 -0
- package/identity-provider/CreateIdentityProviderForm.d.ts.map +1 -1
- package/identity-provider/CreateIdentityProviderForm.js +2 -1
- package/identity-provider/CreateIdentityProviderForm.js.map +1 -1
- package/identity-provider/IdentityProviderDetailPanel.d.ts.map +1 -1
- package/identity-provider/IdentityProviderDetailPanel.js +2 -1
- package/identity-provider/IdentityProviderDetailPanel.js.map +1 -1
- package/identity-provider/IdentityProviderWizard.d.ts.map +1 -1
- package/identity-provider/IdentityProviderWizard.js +2 -1
- package/identity-provider/IdentityProviderWizard.js.map +1 -1
- package/index.d.ts +14 -10
- package/index.d.ts.map +1 -1
- package/index.js +11 -5
- package/index.js.map +1 -1
- package/internal/StandingContextField.d.ts +37 -0
- package/internal/StandingContextField.d.ts.map +1 -0
- package/internal/StandingContextField.js +24 -0
- package/internal/StandingContextField.js.map +1 -0
- package/internal/element-resets.d.ts +17 -0
- package/internal/element-resets.d.ts.map +1 -0
- package/internal/element-resets.js +32 -0
- package/internal/element-resets.js.map +1 -0
- package/internal/file-tree/FileTreeNode.d.ts.map +1 -1
- package/internal/file-tree/FileTreeNode.js +2 -1
- package/internal/file-tree/FileTreeNode.js.map +1 -1
- package/internal/markdown-components.d.ts +7 -0
- package/internal/markdown-components.d.ts.map +1 -1
- package/internal/markdown-components.js +15 -5
- package/internal/markdown-components.js.map +1 -1
- package/invitation/InvitationManager.d.ts.map +1 -1
- package/invitation/InvitationManager.js +2 -1
- package/invitation/InvitationManager.js.map +1 -1
- package/library/InstanceVisibilitySelector.d.ts.map +1 -1
- package/library/InstanceVisibilitySelector.js +9 -2
- package/library/InstanceVisibilitySelector.js.map +1 -1
- package/library/ResourceVisibilityControl.d.ts +13 -3
- package/library/ResourceVisibilityControl.d.ts.map +1 -1
- package/library/ResourceVisibilityControl.js +33 -5
- package/library/ResourceVisibilityControl.js.map +1 -1
- package/library/VisibilityOptionRow.d.ts +6 -0
- package/library/VisibilityOptionRow.d.ts.map +1 -1
- package/library/VisibilityOptionRow.js +11 -1
- package/library/VisibilityOptionRow.js.map +1 -1
- package/library/VisibilitySelector.d.ts.map +1 -1
- package/library/VisibilitySelector.js +14 -2
- package/library/VisibilitySelector.js.map +1 -1
- package/library/index.d.ts +4 -2
- package/library/index.d.ts.map +1 -1
- package/library/index.js +2 -1
- package/library/index.js.map +1 -1
- package/library/useCanSetPublicVisibility.d.ts +30 -0
- package/library/useCanSetPublicVisibility.d.ts.map +1 -0
- package/library/useCanSetPublicVisibility.js +41 -0
- package/library/useCanSetPublicVisibility.js.map +1 -0
- package/library/visibilityLevels.d.ts +32 -1
- package/library/visibilityLevels.d.ts.map +1 -1
- package/library/visibilityLevels.js +34 -14
- package/library/visibilityLevels.js.map +1 -1
- package/manifest/ApplyManifestDialog.d.ts.map +1 -1
- package/manifest/ApplyManifestDialog.js +2 -1
- package/manifest/ApplyManifestDialog.js.map +1 -1
- package/mcp-server/steps/IdentityTransportStep.d.ts.map +1 -1
- package/mcp-server/steps/IdentityTransportStep.js +2 -1
- package/mcp-server/steps/IdentityTransportStep.js.map +1 -1
- package/mcp-server/useMcpServerConnect.d.ts +8 -5
- package/mcp-server/useMcpServerConnect.d.ts.map +1 -1
- package/mcp-server/useMcpServerConnect.js +2 -1
- package/mcp-server/useMcpServerConnect.js.map +1 -1
- package/mcp-server/useMcpServerOAuthConnect.d.ts.map +1 -1
- package/mcp-server/useMcpServerOAuthConnect.js +7 -2
- package/mcp-server/useMcpServerOAuthConnect.js.map +1 -1
- package/oauth-app/CreateOAuthAppForm.d.ts +2 -2
- package/oauth-app/CreateOAuthAppForm.d.ts.map +1 -1
- package/oauth-app/CreateOAuthAppForm.js +13 -4
- package/oauth-app/CreateOAuthAppForm.js.map +1 -1
- package/oauth-app/OAuthAppDetailPanel.d.ts.map +1 -1
- package/oauth-app/OAuthAppDetailPanel.js +32 -5
- package/oauth-app/OAuthAppDetailPanel.js.map +1 -1
- package/organization/OrgPreferencesPanel.d.ts +32 -0
- package/organization/OrgPreferencesPanel.d.ts.map +1 -0
- package/organization/OrgPreferencesPanel.js +90 -0
- package/organization/OrgPreferencesPanel.js.map +1 -0
- package/organization/OrgProfilePanel.d.ts.map +1 -1
- package/organization/OrgProfilePanel.js +5 -4
- package/organization/OrgProfilePanel.js.map +1 -1
- package/organization/index.d.ts +2 -0
- package/organization/index.d.ts.map +1 -1
- package/organization/index.js +1 -0
- package/organization/index.js.map +1 -1
- package/package.json +4 -4
- package/platform-client/CreatePlatformClientForm.d.ts.map +1 -1
- package/platform-client/CreatePlatformClientForm.js +2 -1
- package/platform-client/CreatePlatformClientForm.js.map +1 -1
- package/platform-client/PlatformClientDetailPanel.d.ts.map +1 -1
- package/platform-client/PlatformClientDetailPanel.js +2 -1
- package/platform-client/PlatformClientDetailPanel.js.map +1 -1
- package/pricing-governance/BaselineEditor.d.ts.map +1 -1
- package/pricing-governance/BaselineEditor.js +2 -1
- package/pricing-governance/BaselineEditor.js.map +1 -1
- package/pricing-governance/PricingGovernanceConsole.d.ts.map +1 -1
- package/pricing-governance/PricingGovernanceConsole.js +2 -1
- package/pricing-governance/PricingGovernanceConsole.js.map +1 -1
- package/provider-standing/ProviderStandingAccessNotice.d.ts +11 -0
- package/provider-standing/ProviderStandingAccessNotice.d.ts.map +1 -0
- package/provider-standing/ProviderStandingAccessNotice.js +14 -0
- package/provider-standing/ProviderStandingAccessNotice.js.map +1 -0
- package/provider-standing/ProviderStandingConsole.d.ts +24 -0
- package/provider-standing/ProviderStandingConsole.d.ts.map +1 -0
- package/provider-standing/ProviderStandingConsole.js +98 -0
- package/provider-standing/ProviderStandingConsole.js.map +1 -0
- package/provider-standing/index.d.ts +6 -0
- package/provider-standing/index.d.ts.map +1 -0
- package/provider-standing/index.js +6 -0
- package/provider-standing/index.js.map +1 -0
- package/provider-standing/useProviderStanding.d.ts +34 -0
- package/provider-standing/useProviderStanding.d.ts.map +1 -0
- package/provider-standing/useProviderStanding.js +26 -0
- package/provider-standing/useProviderStanding.js.map +1 -0
- package/provider.d.ts +12 -0
- package/provider.d.ts.map +1 -1
- package/provider.js +12 -0
- package/provider.js.map +1 -1
- package/resource-creation/StepIndicator.d.ts.map +1 -1
- package/resource-creation/StepIndicator.js +2 -1
- package/resource-creation/StepIndicator.js.map +1 -1
- package/schedule/ScheduleDetailView.d.ts.map +1 -1
- package/schedule/ScheduleDetailView.js +3 -2
- package/schedule/ScheduleDetailView.js.map +1 -1
- package/schedule/ScheduleRunsTable.d.ts.map +1 -1
- package/schedule/ScheduleRunsTable.js +2 -1
- package/schedule/ScheduleRunsTable.js.map +1 -1
- package/session/NewSessionViewer.d.ts +10 -1
- package/session/NewSessionViewer.d.ts.map +1 -1
- package/session/NewSessionViewer.js +2 -1
- package/session/NewSessionViewer.js.map +1 -1
- package/session/facets/ArtifactsTab.d.ts.map +1 -1
- package/session/facets/ArtifactsTab.js +2 -1
- package/session/facets/ArtifactsTab.js.map +1 -1
- package/session/facets/ChangesTab.d.ts.map +1 -1
- package/session/facets/ChangesTab.js +2 -1
- package/session/facets/ChangesTab.js.map +1 -1
- package/session/facets/UsageTab.d.ts +10 -3
- package/session/facets/UsageTab.d.ts.map +1 -1
- package/session/facets/UsageTab.js +42 -6
- package/session/facets/UsageTab.js.map +1 -1
- package/session/index.d.ts +1 -1
- package/session/index.d.ts.map +1 -1
- package/session/index.js.map +1 -1
- package/session/useNewSessionFlow.d.ts +27 -1
- package/session/useNewSessionFlow.d.ts.map +1 -1
- package/session/useNewSessionFlow.js +45 -8
- package/session/useNewSessionFlow.js.map +1 -1
- package/session/useSessionUsage.d.ts +25 -0
- package/session/useSessionUsage.d.ts.map +1 -1
- package/session/useSessionUsage.js +11 -0
- package/session/useSessionUsage.js.map +1 -1
- package/settings/AccountPreferencesSection.d.ts +3 -0
- package/settings/AccountPreferencesSection.d.ts.map +1 -0
- package/settings/AccountPreferencesSection.js +10 -0
- package/settings/AccountPreferencesSection.js.map +1 -0
- package/settings/OrgPreferencesSection.d.ts +3 -0
- package/settings/OrgPreferencesSection.d.ts.map +1 -0
- package/settings/OrgPreferencesSection.js +13 -0
- package/settings/OrgPreferencesSection.js.map +1 -0
- package/settings/index.d.ts +2 -0
- package/settings/index.d.ts.map +1 -1
- package/settings/index.js +2 -0
- package/settings/index.js.map +1 -1
- package/settings/settings-nav.d.ts.map +1 -1
- package/settings/settings-nav.js +23 -1
- package/settings/settings-nav.js.map +1 -1
- package/settings/useSettingsNavGroups.d.ts.map +1 -1
- package/settings/useSettingsNavGroups.js +4 -1
- package/settings/useSettingsNavGroups.js.map +1 -1
- package/sharing/ShareAgentDialog.d.ts.map +1 -1
- package/sharing/ShareAgentDialog.js +2 -1
- package/sharing/ShareAgentDialog.js.map +1 -1
- package/sidebar/WorkspaceSidebar.d.ts.map +1 -1
- package/sidebar/WorkspaceSidebar.js +2 -1
- package/sidebar/WorkspaceSidebar.js.map +1 -1
- package/skill/SkillFileBrowser.d.ts.map +1 -1
- package/skill/SkillFileBrowser.js +2 -1
- package/skill/SkillFileBrowser.js.map +1 -1
- package/skill/internal/fetchAndUnpackArtifact.d.ts +1 -1
- package/skill/internal/fetchAndUnpackArtifact.d.ts.map +1 -1
- package/skill/internal/fetchAndUnpackArtifact.js +47 -3
- package/skill/internal/fetchAndUnpackArtifact.js.map +1 -1
- package/skill/useSkillDiff.d.ts.map +1 -1
- package/skill/useSkillDiff.js +7 -1
- package/skill/useSkillDiff.js.map +1 -1
- package/skill/useSkillUpload.d.ts.map +1 -1
- package/skill/useSkillUpload.js +22 -4
- package/skill/useSkillUpload.js.map +1 -1
- package/src/__tests__/bare-host-resets.layout.test.tsx +184 -0
- package/src/agent/steps/IdentityStep.tsx +2 -1
- package/src/agent-instance/AgentInstanceDetailPanel.tsx +2 -1
- package/src/api-key/CreateApiKeyForm.tsx +2 -1
- package/src/attachment/AttachmentChipList.tsx +19 -8
- package/src/attachment/__tests__/AttachmentChipList.test.tsx +12 -0
- package/src/attachment/__tests__/useAttachments.test.ts +293 -0
- package/src/attachment/index.ts +1 -0
- package/src/attachment/useAttachments.ts +143 -11
- package/src/channel/ChannelConversationsDialog.tsx +2 -1
- package/src/channel/ChannelTemplatesDialog.tsx +2 -1
- package/src/channel/ConnectSlackDialog.tsx +2 -1
- package/src/channel/connect/ServingAppSection.tsx +2 -1
- package/src/channel-app/CreateChannelAppForm.tsx +2 -1
- package/src/composer/SessionComposer.tsx +23 -14
- package/src/conversation/ConversationComposer.tsx +38 -4
- package/src/conversation/ConversationListPane.tsx +2 -5
- package/src/conversation/ConversationTemplatePickerDialog.tsx +441 -0
- package/src/conversation/ConversationTimelineView.tsx +2 -1
- package/src/conversation/ConversationsWorkbench.tsx +40 -4
- package/src/conversation/__tests__/ConversationComposer.test.tsx +56 -1
- package/src/conversation/__tests__/ConversationTemplatePickerDialog.test.tsx +224 -0
- package/src/conversation/__tests__/useConversationParticipation.test.tsx +45 -4
- package/src/conversation/index.ts +4 -0
- package/src/conversation/useConversationParticipation.ts +58 -6
- package/src/cursor-accounts/CursorAccountsConsole.tsx +2 -1
- package/src/cursor-accounts/MemberKeysPanel.tsx +2 -1
- package/src/dashboard/DashboardFailedRuns.tsx +2 -1
- package/src/dependency-graph/DependencyGraph.tsx +2 -1
- package/src/dependency-graph/DependencyTreeNode.tsx +2 -1
- package/src/environment/EnvironmentPicker.tsx +2 -1
- package/src/error/SecretFlowErrorGuide.tsx +3 -2
- package/src/execution/ArtifactContentBody.tsx +2 -1
- package/src/execution/ArtifactsWidget.tsx +2 -1
- package/src/execution/FileChangeProgressBar.tsx +2 -1
- package/src/execution/TodoList.tsx +2 -1
- package/src/execution/WriteBacksWidget.tsx +2 -1
- package/src/execution/tool-categories.ts +17 -41
- package/src/iam-policy/PeopleWithAccess.tsx +2 -1
- package/src/iam-policy/PrincipalPicker.tsx +2 -0
- package/src/iam-policy/RoleSelector.tsx +2 -1
- package/src/identity-account/AccountPreferencesPanel.tsx +446 -0
- package/src/identity-account/__tests__/AccountPreferencesPanel.test.tsx +233 -0
- package/src/identity-account/__tests__/useAccountExecutionDefaults.test.tsx +116 -0
- package/src/identity-account/__tests__/useMyIdentityAccount.test.tsx +121 -0
- package/src/identity-account/index.ts +11 -0
- package/src/identity-account/useAccountExecutionDefaults.ts +63 -0
- package/src/identity-account/useMyIdentityAccount.ts +75 -0
- package/src/identity-account/useUpdateIdentityAccount.ts +73 -0
- package/src/identity-provider/CreateIdentityProviderForm.tsx +2 -1
- package/src/identity-provider/IdentityProviderDetailPanel.tsx +2 -1
- package/src/identity-provider/IdentityProviderWizard.tsx +2 -1
- package/src/index.ts +37 -2
- package/src/internal/StandingContextField.tsx +92 -0
- package/src/internal/element-resets.ts +33 -0
- package/src/internal/file-tree/FileTreeNode.tsx +2 -1
- package/src/internal/markdown-components.tsx +15 -5
- package/src/invitation/InvitationManager.tsx +3 -2
- package/src/library/InstanceVisibilitySelector.tsx +9 -2
- package/src/library/ResourceVisibilityControl.tsx +46 -13
- package/src/library/VisibilityOptionRow.tsx +28 -6
- package/src/library/VisibilitySelector.tsx +14 -2
- package/src/library/__tests__/VisibilitySelector.test.tsx +48 -1
- package/src/library/__tests__/visibilityLevels.test.ts +67 -1
- package/src/library/index.ts +6 -1
- package/src/library/useCanSetPublicVisibility.ts +56 -0
- package/src/library/visibilityLevels.ts +65 -14
- package/src/manifest/ApplyManifestDialog.tsx +2 -1
- package/src/mcp-server/__tests__/useMcpServerConnect.asyncLane.test.tsx +117 -0
- package/src/mcp-server/steps/IdentityTransportStep.tsx +3 -2
- package/src/mcp-server/useMcpServerConnect.ts +9 -6
- package/src/mcp-server/useMcpServerOAuthConnect.ts +7 -2
- package/src/oauth-app/CreateOAuthAppForm.tsx +58 -3
- package/src/oauth-app/OAuthAppDetailPanel.tsx +84 -3
- package/src/organization/OrgPreferencesPanel.tsx +221 -0
- package/src/organization/OrgProfilePanel.tsx +5 -4
- package/src/organization/__tests__/OrgPreferencesPanel.test.tsx +145 -0
- package/src/organization/__tests__/OrgProfilePanel.test.tsx +108 -0
- package/src/organization/index.ts +2 -0
- package/src/platform-client/CreatePlatformClientForm.tsx +4 -3
- package/src/platform-client/PlatformClientDetailPanel.tsx +4 -3
- package/src/pricing-governance/BaselineEditor.tsx +3 -2
- package/src/pricing-governance/PricingGovernanceConsole.tsx +2 -1
- package/src/provider-standing/ProviderStandingAccessNotice.tsx +36 -0
- package/src/provider-standing/ProviderStandingConsole.tsx +216 -0
- package/src/provider-standing/__tests__/ProviderStandingConsole.test.tsx +143 -0
- package/src/provider-standing/index.ts +8 -0
- package/src/provider-standing/useProviderStanding.ts +50 -0
- package/src/provider.tsx +12 -0
- package/src/resource-creation/StepIndicator.tsx +2 -1
- package/src/schedule/ScheduleDetailView.tsx +3 -2
- package/src/schedule/ScheduleRunsTable.tsx +2 -1
- package/src/session/NewSessionViewer.tsx +12 -0
- package/src/session/__tests__/UsageTab.test.tsx +136 -0
- package/src/session/__tests__/useNewSessionFlow.test.tsx +151 -0
- package/src/session/__tests__/useSessionUsage.test.ts +19 -0
- package/src/session/facets/ArtifactsTab.tsx +2 -1
- package/src/session/facets/ChangesTab.tsx +2 -1
- package/src/session/facets/UsageTab.tsx +98 -6
- package/src/session/index.ts +1 -0
- package/src/session/useNewSessionFlow.ts +76 -9
- package/src/session/useSessionUsage.ts +37 -0
- package/src/settings/AccountPreferencesSection.tsx +25 -0
- package/src/settings/OrgPreferencesSection.tsx +35 -0
- package/src/settings/__tests__/settings-nav.test.ts +27 -0
- package/src/settings/__tests__/useSettingsNavGroups.test.tsx +11 -1
- package/src/settings/index.ts +2 -0
- package/src/settings/settings-nav.ts +25 -0
- package/src/settings/useSettingsNavGroups.ts +8 -1
- package/src/sharing/ShareAgentDialog.tsx +2 -1
- package/src/sidebar/WorkspaceSidebar.tsx +2 -1
- package/src/skill/SkillFileBrowser.tsx +2 -1
- package/src/skill/__tests__/SkillFileBrowser.test.tsx +10 -2
- package/src/skill/__tests__/fetchAndUnpackArtifact.test.ts +90 -0
- package/src/skill/__tests__/useSkillUpload.test.ts +69 -0
- package/src/skill/internal/fetchAndUnpackArtifact.ts +54 -4
- package/src/skill/useSkillDiff.ts +7 -1
- package/src/skill/useSkillUpload.ts +25 -6
- package/src/workflow/ApprovalFormBuilder.tsx +5 -27
- package/src/workflow/FailedRunsWidget.tsx +2 -1
- package/src/workflow/PendingApprovalsWidget.tsx +2 -1
- package/src/workflow/facets/WorkflowArtifactsTab.tsx +2 -1
- package/src/workflow/facets/WorkflowUsageTab.tsx +2 -1
- package/src/workflow/inspector/WorkflowSummaryPanel.tsx +2 -1
- package/src/workflow/inspector/tabs/BranchesTab.tsx +2 -1
- package/src/workflow/inspector/tabs/IterationTab.tsx +2 -1
- package/src/workflow/instance/WorkflowInstanceDetailPanel.tsx +2 -1
- package/src/workspace/ExplorerTree.tsx +2 -1
- package/src/workspace/WorkspaceContentSearch.tsx +2 -1
- package/src/workspace/WorkspaceFileSearch.tsx +2 -1
- package/src/workspace/WorkspaceSummary.tsx +2 -1
- package/styles.css +1 -1
- package/workflow/ApprovalFormBuilder.d.ts.map +1 -1
- package/workflow/ApprovalFormBuilder.js +8 -5
- package/workflow/ApprovalFormBuilder.js.map +1 -1
- package/workflow/FailedRunsWidget.d.ts.map +1 -1
- package/workflow/FailedRunsWidget.js +2 -1
- package/workflow/FailedRunsWidget.js.map +1 -1
- package/workflow/PendingApprovalsWidget.d.ts.map +1 -1
- package/workflow/PendingApprovalsWidget.js +2 -1
- package/workflow/PendingApprovalsWidget.js.map +1 -1
- package/workflow/facets/WorkflowArtifactsTab.d.ts.map +1 -1
- package/workflow/facets/WorkflowArtifactsTab.js +2 -1
- package/workflow/facets/WorkflowArtifactsTab.js.map +1 -1
- package/workflow/facets/WorkflowUsageTab.d.ts.map +1 -1
- package/workflow/facets/WorkflowUsageTab.js +2 -1
- package/workflow/facets/WorkflowUsageTab.js.map +1 -1
- package/workflow/inspector/WorkflowSummaryPanel.d.ts.map +1 -1
- package/workflow/inspector/WorkflowSummaryPanel.js +2 -1
- package/workflow/inspector/WorkflowSummaryPanel.js.map +1 -1
- package/workflow/inspector/tabs/BranchesTab.d.ts.map +1 -1
- package/workflow/inspector/tabs/BranchesTab.js +2 -1
- package/workflow/inspector/tabs/BranchesTab.js.map +1 -1
- package/workflow/inspector/tabs/IterationTab.d.ts.map +1 -1
- package/workflow/inspector/tabs/IterationTab.js +2 -1
- package/workflow/inspector/tabs/IterationTab.js.map +1 -1
- package/workflow/instance/WorkflowInstanceDetailPanel.d.ts.map +1 -1
- package/workflow/instance/WorkflowInstanceDetailPanel.js +2 -1
- package/workflow/instance/WorkflowInstanceDetailPanel.js.map +1 -1
- package/workspace/ExplorerTree.d.ts.map +1 -1
- package/workspace/ExplorerTree.js +2 -1
- package/workspace/ExplorerTree.js.map +1 -1
- package/workspace/WorkspaceContentSearch.d.ts.map +1 -1
- package/workspace/WorkspaceContentSearch.js +2 -1
- package/workspace/WorkspaceContentSearch.js.map +1 -1
- package/workspace/WorkspaceFileSearch.d.ts.map +1 -1
- package/workspace/WorkspaceFileSearch.js +2 -1
- package/workspace/WorkspaceFileSearch.js.map +1 -1
- package/workspace/WorkspaceSummary.d.ts.map +1 -1
- package/workspace/WorkspaceSummary.js +2 -1
- package/workspace/WorkspaceSummary.js.map +1 -1
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
// Bare-host regression suite for the per-element UA resets (#695).
|
|
2
|
+
//
|
|
3
|
+
// Runs in a real Chromium via `vitest.a11y.config.ts` — the defect under
|
|
4
|
+
// guard is the user agent's OWN default list/fieldset/flow-content styling
|
|
5
|
+
// (bullets, 40px indents, stacked margins), which only a real browser
|
|
6
|
+
// applies. happy-dom has no UA stylesheet, so it would pass vacuously.
|
|
7
|
+
//
|
|
8
|
+
// The contract: every SDK component is authored against a zeroed-margin
|
|
9
|
+
// baseline (our consoles ship a global reset). In a host WITHOUT one —
|
|
10
|
+
// third-party embeds, the SDK's stated audience — UI lists and fieldsets
|
|
11
|
+
// carry the `UNSTYLED_LIST`/`UNSTYLED_FIELDSET` per-component resets
|
|
12
|
+
// (`internal/element-resets.ts` records why this class of reset cannot live
|
|
13
|
+
// in styles.css), and the markdown map declares its own top/inline margins.
|
|
14
|
+
// Like the sibling #374 suite (preflight-parity.layout.test.tsx), this
|
|
15
|
+
// renders against the SHIPPED stylesheet (`dist/styles.css`, rebuilt by
|
|
16
|
+
// `npm run build:css`) and nothing else — the page IS the bare host.
|
|
17
|
+
//
|
|
18
|
+
// Three directions, all load-bearing:
|
|
19
|
+
// 1. A control proving the harness is genuinely bare (a raw <ul> DOES show
|
|
20
|
+
// UA bullets — if this fails, every other assertion here is vacuous).
|
|
21
|
+
// 2. The resets neutralize UA defaults on real, swept components.
|
|
22
|
+
// 3. Utilities and deliberate styling still win (markdown lists keep their
|
|
23
|
+
// explicit bullets; spacing utilities compose on top of the reset).
|
|
24
|
+
|
|
25
|
+
import "../../dist/styles.css";
|
|
26
|
+
|
|
27
|
+
import { describe, it, expect, afterEach } from "vitest";
|
|
28
|
+
import { render, cleanup } from "@testing-library/react";
|
|
29
|
+
import { create } from "@bufbuild/protobuf";
|
|
30
|
+
import Markdown from "react-markdown";
|
|
31
|
+
import type { Stigmer } from "@stigmer/sdk";
|
|
32
|
+
import { TodoItemSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/todo_pb";
|
|
33
|
+
import { TodoStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
34
|
+
import { StigmerProvider } from "../provider";
|
|
35
|
+
import { TodoList } from "../execution/TodoList.js";
|
|
36
|
+
import { UNSTYLED_FIELDSET, UNSTYLED_LIST } from "../internal/element-resets.js";
|
|
37
|
+
import { MARKDOWN_COMPONENTS } from "../internal/markdown-components.js";
|
|
38
|
+
|
|
39
|
+
afterEach(cleanup);
|
|
40
|
+
|
|
41
|
+
// A minimal client (the preflight-parity suite's idiom): a null credential
|
|
42
|
+
// keeps the provider's registry fetches non-blocking so rendering is
|
|
43
|
+
// synchronous and never touches the network.
|
|
44
|
+
function makeClient(): Stigmer {
|
|
45
|
+
return {
|
|
46
|
+
baseUrl: "https://example.test",
|
|
47
|
+
getAuthCredential: async () => null,
|
|
48
|
+
fetch: (async () => {
|
|
49
|
+
throw new Error("network disabled in test");
|
|
50
|
+
}) as unknown as typeof globalThis.fetch,
|
|
51
|
+
} as unknown as Stigmer;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function renderScoped(ui: React.ReactNode): HTMLElement {
|
|
55
|
+
render(
|
|
56
|
+
<StigmerProvider client={makeClient()}>
|
|
57
|
+
<div data-testid="probe">{ui}</div>
|
|
58
|
+
</StigmerProvider>,
|
|
59
|
+
);
|
|
60
|
+
return document.querySelector('[data-testid="probe"]') as HTMLElement;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function computed(el: Element): CSSStyleDeclaration {
|
|
64
|
+
return getComputedStyle(el);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const ZERO_MARGINS = ["0px", "0px", "0px", "0px"];
|
|
68
|
+
|
|
69
|
+
function margins(style: CSSStyleDeclaration): string[] {
|
|
70
|
+
return [style.marginTop, style.marginRight, style.marginBottom, style.marginLeft];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
describe("bare-host per-element resets (#695)", () => {
|
|
74
|
+
it("CONTROL: the harness is genuinely bare — a raw <ul> wears UA bullets and indent", () => {
|
|
75
|
+
const probe = renderScoped(
|
|
76
|
+
<ul>
|
|
77
|
+
<li>raw</li>
|
|
78
|
+
</ul>,
|
|
79
|
+
);
|
|
80
|
+
const style = computed(probe.querySelector("ul")!);
|
|
81
|
+
|
|
82
|
+
expect(style.listStyleType, "no global reset may leak into this page").toBe("disc");
|
|
83
|
+
expect(style.paddingLeft, "UA padding-inline-start must be present").toBe("40px");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe("UNSTYLED_LIST", () => {
|
|
87
|
+
it("neutralizes bullets, indent, and margins", () => {
|
|
88
|
+
const probe = renderScoped(
|
|
89
|
+
<ul className={UNSTYLED_LIST}>
|
|
90
|
+
<li>item</li>
|
|
91
|
+
</ul>,
|
|
92
|
+
);
|
|
93
|
+
const style = computed(probe.querySelector("ul")!);
|
|
94
|
+
|
|
95
|
+
expect(style.listStyleType).toBe("none");
|
|
96
|
+
expect(style.paddingLeft).toBe("0px");
|
|
97
|
+
expect(margins(style)).toEqual(ZERO_MARGINS);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("spacing utilities still win over the reset (tailwind-merge + layer order)", () => {
|
|
101
|
+
const probe = renderScoped(
|
|
102
|
+
<ul className={`${UNSTYLED_LIST} stg:p-2`}>
|
|
103
|
+
<li>item</li>
|
|
104
|
+
</ul>,
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
expect(computed(probe.querySelector("ul")!).paddingLeft).toBe("8px");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("a real swept organism (TodoList) renders without UA list styling", () => {
|
|
111
|
+
const probe = renderScoped(
|
|
112
|
+
<TodoList
|
|
113
|
+
todos={{
|
|
114
|
+
t1: create(TodoItemSchema, {
|
|
115
|
+
id: "t1",
|
|
116
|
+
content: "Scaffold the component",
|
|
117
|
+
status: TodoStatus.TODO_IN_PROGRESS,
|
|
118
|
+
}),
|
|
119
|
+
}}
|
|
120
|
+
/>,
|
|
121
|
+
);
|
|
122
|
+
const style = computed(probe.querySelector('ul[aria-label="Tasks"]')!);
|
|
123
|
+
|
|
124
|
+
expect(style.listStyleType).toBe("none");
|
|
125
|
+
expect(style.paddingLeft).toBe("0px");
|
|
126
|
+
expect(margins(style)).toEqual(ZERO_MARGINS);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe("UNSTYLED_FIELDSET", () => {
|
|
131
|
+
it("neutralizes UA fieldset margins, padding, and min-inline-size", () => {
|
|
132
|
+
const probe = renderScoped(
|
|
133
|
+
<>
|
|
134
|
+
<fieldset data-testid="raw">
|
|
135
|
+
<legend>raw</legend>
|
|
136
|
+
</fieldset>
|
|
137
|
+
<fieldset data-testid="reset" className={UNSTYLED_FIELDSET}>
|
|
138
|
+
<legend>reset</legend>
|
|
139
|
+
</fieldset>
|
|
140
|
+
</>,
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
// Control half: UA fieldset padding really is present in this page.
|
|
144
|
+
expect(computed(probe.querySelector('[data-testid="raw"]')!).paddingLeft).not.toBe("0px");
|
|
145
|
+
|
|
146
|
+
const style = computed(probe.querySelector('[data-testid="reset"]')!);
|
|
147
|
+
expect(margins(style)).toEqual(ZERO_MARGINS);
|
|
148
|
+
expect(style.paddingLeft).toBe("0px");
|
|
149
|
+
expect(style.minWidth, "min-inline-size: min-content breaks flex shrinking").toBe("0px");
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe("markdown map (content elements declare their own margins)", () => {
|
|
154
|
+
function renderMarkdown(md: string): HTMLElement {
|
|
155
|
+
return renderScoped(<Markdown components={MARKDOWN_COMPONENTS}>{md}</Markdown>);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
it("paragraphs carry no UA top margin, so mb-* spacing is the whole rhythm", () => {
|
|
159
|
+
const probe = renderMarkdown("first\n\nsecond");
|
|
160
|
+
const [, second] = Array.from(probe.querySelectorAll("p"));
|
|
161
|
+
|
|
162
|
+
expect(computed(second).marginTop).toBe("0px");
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("blockquotes lose the UA 1em/40px box but keep the authored border+padding treatment", () => {
|
|
166
|
+
const probe = renderMarkdown("> quoted");
|
|
167
|
+
const style = computed(probe.querySelector("blockquote")!);
|
|
168
|
+
|
|
169
|
+
expect(style.marginTop).toBe("0px");
|
|
170
|
+
expect(style.marginLeft, "UA margin-inline: 40px must be cleared").toBe("0px");
|
|
171
|
+
expect(style.marginRight).toBe("0px");
|
|
172
|
+
expect(style.paddingLeft, "the authored pl-4 treatment stays").toBe("16px");
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("content lists KEEP their bullets — the reset is for UI lists only", () => {
|
|
176
|
+
const probe = renderMarkdown("- one\n- two");
|
|
177
|
+
const style = computed(probe.querySelector("ul")!);
|
|
178
|
+
|
|
179
|
+
expect(style.listStyleType).toBe("disc");
|
|
180
|
+
expect(style.marginTop, "stacked UA margin-top must be cleared").toBe("0px");
|
|
181
|
+
expect(style.paddingLeft, "authored pl-5 replaces the UA 40px").toBe("20px");
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
});
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useCallback, useId } from "react";
|
|
4
4
|
import { cn } from "@stigmer/theme";
|
|
5
|
+
import { UNSTYLED_FIELDSET } from "../../internal/element-resets.js";
|
|
5
6
|
import { generateSlug } from "../../internal/slug.js";
|
|
6
7
|
import type { AgentWizardData } from "./types.js";
|
|
7
8
|
|
|
@@ -173,7 +174,7 @@ export function IdentityStep({
|
|
|
173
174
|
/>
|
|
174
175
|
</div>
|
|
175
176
|
|
|
176
|
-
<fieldset className="stg:space-y-1.5">
|
|
177
|
+
<fieldset className={cn(UNSTYLED_FIELDSET, "stg:space-y-1.5")}>
|
|
177
178
|
<legend className="stg:text-sm stg:font-medium stg:text-foreground">
|
|
178
179
|
Visibility
|
|
179
180
|
</legend>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useCallback, useState } from "react";
|
|
4
4
|
import { cn } from "@stigmer/theme";
|
|
5
|
+
import { UNSTYLED_LIST } from "../internal/element-resets.js";
|
|
5
6
|
import { timestampDate } from "@bufbuild/protobuf/wkt";
|
|
6
7
|
import type { AgentInstance } from "@stigmer/protos/ai/stigmer/agentic/agentinstance/v1/api_pb";
|
|
7
8
|
import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
|
|
@@ -233,7 +234,7 @@ export function AgentInstanceDetailPanel({
|
|
|
233
234
|
{(spec?.environmentRefs?.length ?? 0) === 0 ? (
|
|
234
235
|
<p className="stg:text-xs stg:text-muted-foreground">No environments bound.</p>
|
|
235
236
|
) : (
|
|
236
|
-
<ol className="stg:space-y-1">
|
|
237
|
+
<ol className={cn(UNSTYLED_LIST, "stg:space-y-1")}>
|
|
237
238
|
{spec!.environmentRefs.map((ref, idx) => (
|
|
238
239
|
<li key={`${ref.slug}-${idx}`} className="stg:flex stg:items-center stg:gap-2 stg:text-sm">
|
|
239
240
|
<span className="stg:text-xs stg:text-muted-foreground stg:w-4 stg:text-right">{idx + 1}.</span>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useCallback, useId, useState, type FormEvent } from "react";
|
|
4
4
|
import { cn } from "@stigmer/theme";
|
|
5
|
+
import { UNSTYLED_FIELDSET } from "../internal/element-resets.js";
|
|
5
6
|
import { getUserMessage } from "@stigmer/sdk";
|
|
6
7
|
import type { ApiKey } from "@stigmer/protos/ai/stigmer/iam/apikey/v1/api_pb";
|
|
7
8
|
import { useCreateApiKey } from "./useCreateApiKey.js";
|
|
@@ -122,7 +123,7 @@ export function CreateApiKeyForm({
|
|
|
122
123
|
</div>
|
|
123
124
|
|
|
124
125
|
{/* Expiry */}
|
|
125
|
-
<fieldset className="stg:space-y-1.5">
|
|
126
|
+
<fieldset className={cn(UNSTYLED_FIELDSET, "stg:space-y-1.5")}>
|
|
126
127
|
<legend className="stg:text-xs stg:font-medium stg:text-foreground">
|
|
127
128
|
Expiration
|
|
128
129
|
</legend>
|
|
@@ -154,9 +154,20 @@ function AttachmentChip({
|
|
|
154
154
|
|
|
155
155
|
/** Shared accessible name: filename, size, and upload status. */
|
|
156
156
|
function chipAriaLabel(entry: AttachmentEntry): string {
|
|
157
|
+
const isPreparing = entry.phase === "preparing";
|
|
157
158
|
const isUploading = entry.phase === "uploading";
|
|
158
159
|
const isError = entry.phase === "error";
|
|
159
|
-
return `${entry.file.name}, ${formatFileSize(entry.file.size)}${isUploading ? ", uploading" : ""}${isError ? ", upload failed" : ""}`;
|
|
160
|
+
return `${entry.file.name}, ${formatFileSize(entry.file.size)}${isPreparing ? ", preparing" : ""}${isUploading ? ", uploading" : ""}${isError ? ", upload failed" : ""}`;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* True while the entry has work in flight (`preparing` or `uploading`) —
|
|
165
|
+
* the chips render both identically (dimmed + spinner): to the user each
|
|
166
|
+
* means "your file is on its way", and the split is audible in the
|
|
167
|
+
* accessible label where it actually informs.
|
|
168
|
+
*/
|
|
169
|
+
function isChipBusy(entry: AttachmentEntry): boolean {
|
|
170
|
+
return entry.phase === "preparing" || entry.phase === "uploading";
|
|
160
171
|
}
|
|
161
172
|
|
|
162
173
|
// ---------------------------------------------------------------------------
|
|
@@ -212,7 +223,7 @@ function ImageAttachmentChip({
|
|
|
212
223
|
}
|
|
213
224
|
|
|
214
225
|
const isError = entry.phase === "error";
|
|
215
|
-
const
|
|
226
|
+
const isBusy = isChipBusy(entry);
|
|
216
227
|
|
|
217
228
|
return (
|
|
218
229
|
// The remove badge and retry pill are SIBLINGS of the preview button,
|
|
@@ -250,7 +261,7 @@ function ImageAttachmentChip({
|
|
|
250
261
|
onError={() => setLoadFailed(true)}
|
|
251
262
|
className={cn(
|
|
252
263
|
"stg:h-full stg:w-full stg:object-cover",
|
|
253
|
-
(
|
|
264
|
+
(isBusy || isError) && "stg:opacity-50",
|
|
254
265
|
)}
|
|
255
266
|
/>
|
|
256
267
|
) : (
|
|
@@ -261,7 +272,7 @@ function ImageAttachmentChip({
|
|
|
261
272
|
aria-hidden="true"
|
|
262
273
|
/>
|
|
263
274
|
)}
|
|
264
|
-
{
|
|
275
|
+
{isBusy && (
|
|
265
276
|
<span className="stg:absolute stg:inset-0 stg:flex stg:items-center stg:justify-center">
|
|
266
277
|
<ChipSpinner size={16} />
|
|
267
278
|
</span>
|
|
@@ -322,7 +333,7 @@ function FileAttachmentChip({
|
|
|
322
333
|
readonly disabled?: boolean;
|
|
323
334
|
}) {
|
|
324
335
|
const isError = entry.phase === "error";
|
|
325
|
-
const
|
|
336
|
+
const isBusy = isChipBusy(entry);
|
|
326
337
|
|
|
327
338
|
return (
|
|
328
339
|
<span
|
|
@@ -333,12 +344,12 @@ function FileAttachmentChip({
|
|
|
333
344
|
isError
|
|
334
345
|
? "stg:border stg:border-destructive/30 stg:bg-destructive-subtle stg:text-destructive"
|
|
335
346
|
: "stg:bg-muted-subtle stg:text-foreground",
|
|
336
|
-
|
|
347
|
+
isBusy && "stg:opacity-70",
|
|
337
348
|
)}
|
|
338
349
|
>
|
|
339
|
-
{
|
|
350
|
+
{isBusy && <ChipSpinner />}
|
|
340
351
|
{isError && <ErrorDot />}
|
|
341
|
-
{!
|
|
352
|
+
{!isBusy && !isError && <FileIcon />}
|
|
342
353
|
|
|
343
354
|
<span className="stg:truncate">{entry.file.name}</span>
|
|
344
355
|
|
|
@@ -101,6 +101,18 @@ describe("AttachmentChipList — image miniature", () => {
|
|
|
101
101
|
expect(container.querySelector("svg.stg\\:animate-spin")).toBeTruthy();
|
|
102
102
|
});
|
|
103
103
|
|
|
104
|
+
it("treats the preparing phase as busy: spinner over the miniature, 'preparing' in the accessible label (#369)", async () => {
|
|
105
|
+
const preparing = entry({ id: "prep", phase: "preparing", storageKey: null });
|
|
106
|
+
|
|
107
|
+
const { container } = renderChips([preparing]);
|
|
108
|
+
|
|
109
|
+
await waitFor(() => expect(chipImage(container)).toBeTruthy());
|
|
110
|
+
expect(container.querySelector("svg.stg\\:animate-spin")).toBeTruthy();
|
|
111
|
+
expect(
|
|
112
|
+
screen.getByRole("listitem", { name: /shot\.png.*preparing/ }),
|
|
113
|
+
).toBeTruthy();
|
|
114
|
+
});
|
|
115
|
+
|
|
104
116
|
it("renders image chips preview-only: no visible filename or size text", async () => {
|
|
105
117
|
const { container } = renderChips([entry()]);
|
|
106
118
|
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useAttachments lifecycle tests, centered on the `preparing` phase
|
|
3
|
+
* (stigmer/stigmer#369): entries must exist from the instant a
|
|
4
|
+
* prepare-flagged file is added, transition through preparation to
|
|
5
|
+
* upload, and honor the deferred-validation and cancellation contracts
|
|
6
|
+
* documented on {@link AddFilesOptions.prepareImages}.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
9
|
+
import { renderHook, act, waitFor } from "@testing-library/react";
|
|
10
|
+
|
|
11
|
+
vi.mock("../../hooks", () => ({
|
|
12
|
+
useStigmer: vi.fn(),
|
|
13
|
+
}));
|
|
14
|
+
vi.mock("../prepare-image.js", () => ({
|
|
15
|
+
prepareImageForVision: vi.fn(),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
import { useStigmer } from "../../hooks";
|
|
19
|
+
import { prepareImageForVision } from "../prepare-image.js";
|
|
20
|
+
import { useAttachments } from "../useAttachments.js";
|
|
21
|
+
import { MAX_ATTACHMENT_BYTES } from "../attachment-utils.js";
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Helpers
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
function deferred<T>() {
|
|
28
|
+
let resolve!: (value: T) => void;
|
|
29
|
+
const promise = new Promise<T>((r) => {
|
|
30
|
+
resolve = r;
|
|
31
|
+
});
|
|
32
|
+
return { promise, resolve };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function makeFile(name: string, sizeBytes: number, type = "image/png"): File {
|
|
36
|
+
return new File([new Uint8Array(sizeBytes)], name, { type });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Wires useStigmer to an uploadAttachment mock that resolves immediately. */
|
|
40
|
+
function mockStigmer() {
|
|
41
|
+
const uploadAttachment = vi
|
|
42
|
+
.fn()
|
|
43
|
+
.mockResolvedValue({ storageKey: "attachments/test/key" });
|
|
44
|
+
vi.mocked(useStigmer).mockReturnValue({
|
|
45
|
+
agentExecution: { uploadAttachment },
|
|
46
|
+
} as unknown as ReturnType<typeof useStigmer>);
|
|
47
|
+
return { uploadAttachment };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const prepareMock = vi.mocked(prepareImageForVision);
|
|
51
|
+
|
|
52
|
+
afterEach(() => {
|
|
53
|
+
vi.restoreAllMocks();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
// The preparing phase
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
|
|
60
|
+
describe("useAttachments — prepareImages lifecycle", () => {
|
|
61
|
+
it("shows the entry immediately in the preparing phase, gating submit", () => {
|
|
62
|
+
mockStigmer();
|
|
63
|
+
const gap = deferred<File>();
|
|
64
|
+
prepareMock.mockReturnValue(gap.promise);
|
|
65
|
+
|
|
66
|
+
const { result } = renderHook(() => useAttachments());
|
|
67
|
+
|
|
68
|
+
act(() => {
|
|
69
|
+
result.current.addFiles([makeFile("shot.png", 1024)], {
|
|
70
|
+
prepareImages: true,
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
expect(result.current.entries).toHaveLength(1);
|
|
75
|
+
expect(result.current.entries[0].phase).toBe("preparing");
|
|
76
|
+
expect(result.current.entries[0].file.name).toBe("shot.png");
|
|
77
|
+
expect(result.current.isPreparing).toBe(true);
|
|
78
|
+
// preparing gates exactly like uploading — the silent-drop guard.
|
|
79
|
+
expect(result.current.isUploading).toBe(true);
|
|
80
|
+
expect(result.current.allReady).toBe(false);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("swaps to the prepared file (name, type) and proceeds to upload → ready", async () => {
|
|
84
|
+
const { uploadAttachment } = mockStigmer();
|
|
85
|
+
const gap = deferred<File>();
|
|
86
|
+
prepareMock.mockReturnValue(gap.promise);
|
|
87
|
+
|
|
88
|
+
const { result } = renderHook(() => useAttachments());
|
|
89
|
+
|
|
90
|
+
act(() => {
|
|
91
|
+
result.current.addFiles([makeFile("shot.png", 4096)], {
|
|
92
|
+
prepareImages: true,
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Preparation re-encoded to JPEG: name and MIME follow.
|
|
97
|
+
act(() => {
|
|
98
|
+
gap.resolve(makeFile("shot.jpg", 2048, "image/jpeg"));
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
await waitFor(() => {
|
|
102
|
+
expect(result.current.entries[0].phase).toBe("ready");
|
|
103
|
+
});
|
|
104
|
+
expect(result.current.entries[0].file.name).toBe("shot.jpg");
|
|
105
|
+
expect(result.current.entries[0].contentType).toBe("image/jpeg");
|
|
106
|
+
expect(uploadAttachment).toHaveBeenCalledTimes(1);
|
|
107
|
+
expect(result.current.toAttachmentInputs()).toEqual([
|
|
108
|
+
{
|
|
109
|
+
filename: "shot.jpg",
|
|
110
|
+
storageKey: "attachments/test/key",
|
|
111
|
+
contentType: "image/jpeg",
|
|
112
|
+
},
|
|
113
|
+
]);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("accepts a raw file over the size limit when preparation brings it under (the #369 paste contract)", async () => {
|
|
117
|
+
const { uploadAttachment } = mockStigmer();
|
|
118
|
+
const onValidationError = vi.fn();
|
|
119
|
+
const gap = deferred<File>();
|
|
120
|
+
prepareMock.mockReturnValue(gap.promise);
|
|
121
|
+
|
|
122
|
+
const { result } = renderHook(() =>
|
|
123
|
+
useAttachments({ onValidationError }),
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
// 10 MB + 1 raw screenshot — would be rejected by up-front validation.
|
|
127
|
+
act(() => {
|
|
128
|
+
result.current.addFiles(
|
|
129
|
+
[makeFile("huge-screenshot.png", MAX_ATTACHMENT_BYTES + 1)],
|
|
130
|
+
{ prepareImages: true },
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
expect(result.current.entries[0].phase).toBe("preparing");
|
|
134
|
+
|
|
135
|
+
act(() => {
|
|
136
|
+
gap.resolve(makeFile("huge-screenshot.png", 512 * 1024));
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
await waitFor(() => {
|
|
140
|
+
expect(result.current.entries[0].phase).toBe("ready");
|
|
141
|
+
});
|
|
142
|
+
expect(onValidationError).not.toHaveBeenCalled();
|
|
143
|
+
expect(uploadAttachment).toHaveBeenCalledTimes(1);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("removes the entry and reports via onValidationError when still oversized after preparation", async () => {
|
|
147
|
+
const { uploadAttachment } = mockStigmer();
|
|
148
|
+
const onValidationError = vi.fn();
|
|
149
|
+
const oversized = makeFile("giant.zip", MAX_ATTACHMENT_BYTES + 1, "application/zip");
|
|
150
|
+
// Non-image passthrough: preparation returns the original untouched.
|
|
151
|
+
prepareMock.mockResolvedValue(oversized);
|
|
152
|
+
|
|
153
|
+
const { result } = renderHook(() =>
|
|
154
|
+
useAttachments({ onValidationError }),
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
act(() => {
|
|
158
|
+
result.current.addFiles([oversized], { prepareImages: true });
|
|
159
|
+
});
|
|
160
|
+
expect(result.current.entries).toHaveLength(1);
|
|
161
|
+
|
|
162
|
+
await waitFor(() => {
|
|
163
|
+
expect(result.current.entries).toHaveLength(0);
|
|
164
|
+
});
|
|
165
|
+
expect(onValidationError).toHaveBeenCalledWith(
|
|
166
|
+
expect.stringContaining("giant.zip"),
|
|
167
|
+
);
|
|
168
|
+
expect(uploadAttachment).not.toHaveBeenCalled();
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("discards the preparation result when the entry is removed mid-preparation", async () => {
|
|
172
|
+
const { uploadAttachment } = mockStigmer();
|
|
173
|
+
const gap = deferred<File>();
|
|
174
|
+
prepareMock.mockReturnValue(gap.promise);
|
|
175
|
+
|
|
176
|
+
const { result } = renderHook(() => useAttachments());
|
|
177
|
+
|
|
178
|
+
act(() => {
|
|
179
|
+
result.current.addFiles([makeFile("shot.png", 1024)], {
|
|
180
|
+
prepareImages: true,
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
const id = result.current.entries[0].id;
|
|
184
|
+
|
|
185
|
+
act(() => {
|
|
186
|
+
result.current.removeEntry(id);
|
|
187
|
+
});
|
|
188
|
+
expect(result.current.entries).toHaveLength(0);
|
|
189
|
+
|
|
190
|
+
// Late completion must not resurrect the entry or start an upload.
|
|
191
|
+
await act(async () => {
|
|
192
|
+
gap.resolve(makeFile("shot.jpg", 512, "image/jpeg"));
|
|
193
|
+
await gap.promise;
|
|
194
|
+
});
|
|
195
|
+
expect(result.current.entries).toHaveLength(0);
|
|
196
|
+
expect(uploadAttachment).not.toHaveBeenCalled();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("discards the preparation result on clear()", async () => {
|
|
200
|
+
const { uploadAttachment } = mockStigmer();
|
|
201
|
+
const gap = deferred<File>();
|
|
202
|
+
prepareMock.mockReturnValue(gap.promise);
|
|
203
|
+
|
|
204
|
+
const { result } = renderHook(() => useAttachments());
|
|
205
|
+
|
|
206
|
+
act(() => {
|
|
207
|
+
result.current.addFiles([makeFile("shot.png", 1024)], {
|
|
208
|
+
prepareImages: true,
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
act(() => {
|
|
213
|
+
result.current.clear();
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
await act(async () => {
|
|
217
|
+
gap.resolve(makeFile("shot.jpg", 512, "image/jpeg"));
|
|
218
|
+
await gap.promise;
|
|
219
|
+
});
|
|
220
|
+
expect(result.current.entries).toHaveLength(0);
|
|
221
|
+
expect(uploadAttachment).not.toHaveBeenCalled();
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it("uniquifies the prepared name against other entries' settled names", async () => {
|
|
225
|
+
mockStigmer();
|
|
226
|
+
const gap = deferred<File>();
|
|
227
|
+
prepareMock.mockReturnValue(gap.promise);
|
|
228
|
+
|
|
229
|
+
const { result } = renderHook(() => useAttachments());
|
|
230
|
+
|
|
231
|
+
// A settled entry already owns "pasted-image.png".
|
|
232
|
+
act(() => {
|
|
233
|
+
result.current.addFiles([makeFile("pasted-image.png", 256)]);
|
|
234
|
+
});
|
|
235
|
+
// The prepared file lands on the same name.
|
|
236
|
+
act(() => {
|
|
237
|
+
result.current.addFiles([makeFile("other.png", 256)], {
|
|
238
|
+
prepareImages: true,
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
act(() => {
|
|
242
|
+
gap.resolve(makeFile("pasted-image.png", 128));
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
await waitFor(() => {
|
|
246
|
+
expect(result.current.entries[1].phase).toBe("ready");
|
|
247
|
+
});
|
|
248
|
+
expect(result.current.entries[1].file.name).toBe("pasted-image-2.png");
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// ---------------------------------------------------------------------------
|
|
253
|
+
// The unflagged path — byte-identical to the pre-#369 behavior
|
|
254
|
+
// ---------------------------------------------------------------------------
|
|
255
|
+
|
|
256
|
+
describe("useAttachments — unflagged addFiles", () => {
|
|
257
|
+
it("never enters the preparing phase and never calls prepareImageForVision", async () => {
|
|
258
|
+
mockStigmer();
|
|
259
|
+
|
|
260
|
+
const { result } = renderHook(() => useAttachments());
|
|
261
|
+
|
|
262
|
+
act(() => {
|
|
263
|
+
result.current.addFiles([makeFile("picked.png", 1024)]);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
expect(result.current.entries[0].phase).toBe("uploading");
|
|
267
|
+
expect(result.current.isPreparing).toBe(false);
|
|
268
|
+
expect(prepareMock).not.toHaveBeenCalled();
|
|
269
|
+
|
|
270
|
+
await waitFor(() => {
|
|
271
|
+
expect(result.current.entries[0].phase).toBe("ready");
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it("still validates size up front and rejects without creating an entry", () => {
|
|
276
|
+
const { uploadAttachment } = mockStigmer();
|
|
277
|
+
const onValidationError = vi.fn();
|
|
278
|
+
|
|
279
|
+
const { result } = renderHook(() =>
|
|
280
|
+
useAttachments({ onValidationError }),
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
act(() => {
|
|
284
|
+
result.current.addFiles([
|
|
285
|
+
makeFile("too-big.png", MAX_ATTACHMENT_BYTES + 1),
|
|
286
|
+
]);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
expect(result.current.entries).toHaveLength(0);
|
|
290
|
+
expect(onValidationError).toHaveBeenCalledTimes(1);
|
|
291
|
+
expect(uploadAttachment).not.toHaveBeenCalled();
|
|
292
|
+
});
|
|
293
|
+
});
|