diff --git a/src/renderer/src/components/skills/SkillInstallDialog.tsx b/src/renderer/src/components/skills/SkillInstallDialog.tsx index cc0ee06b198..6ad6edcbedf 100644 --- a/src/renderer/src/components/skills/SkillInstallDialog.tsx +++ b/src/renderer/src/components/skills/SkillInstallDialog.tsx @@ -1,13 +1,6 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { Download, Loader2, ShieldCheck } from 'lucide-react' -import { Button } from '@/components/ui/button' -import { - Dialog, - DialogContent, - DialogFooter, - DialogHeader, - DialogTitle -} from '@/components/ui/dialog' +import { Loader2 } from 'lucide-react' +import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog' import { useAppStore } from '@/store' import type { SkillInstallDestination, @@ -32,6 +25,7 @@ import { useSkillInstallProgress } from './skill-install-progress-state' import { translate } from '@/i18n/i18n' import { resolveSkillShareForInstall } from './skill-warning-preview-gate' import { useSkillInstallRisk } from './use-skill-install-risk' +import { SkillInstallDialogFooter } from './SkillInstallDialogFooter' export function SkillInstallDialog({ open, @@ -374,65 +368,21 @@ export function SkillInstallDialog({ {installProgress.phaseLabel}

) : null} - {!bundleVersion ? ( - - - {!preview && !resolvingInitialLink ? ( - - ) : null} - {busy && installProgress.activeOperationId ? ( - - ) : null} - {preview && - (!result || ['conflict', 'partial', 'failed', 'cancelled'].includes(result.status)) ? ( - - ) : null} - - ) : null} + void cancelInstall()} + onClose={close} + onInspect={() => void inspect()} + onInstall={(discardLocal) => void install(discardLocal)} + /> ) diff --git a/src/renderer/src/components/skills/SkillInstallDialogFooter.tsx b/src/renderer/src/components/skills/SkillInstallDialogFooter.tsx new file mode 100644 index 00000000000..8d87da3046a --- /dev/null +++ b/src/renderer/src/components/skills/SkillInstallDialogFooter.tsx @@ -0,0 +1,81 @@ +import { Download, Loader2, ShieldCheck } from 'lucide-react' +import { Button } from '@/components/ui/button' +import { DialogFooter } from '@/components/ui/dialog' +import type { SkillInstallResult } from '../../../../shared/skill-install-contract' +import { translate } from '@/i18n/i18n' + +type SkillInstallDialogFooterProps = { + activeOperationId: string | null + busy: boolean + hasBundleVersion: boolean + hasPreview: boolean + link: string + resolvingInitialLink: boolean + result: SkillInstallResult | null + scope: 'global' | 'workspace' + workspace: string + onCancelInstall: () => void + onClose: () => void + onInspect: () => void + onInstall: (discardLocal?: boolean) => void +} + +export function SkillInstallDialogFooter({ + activeOperationId, + busy, + hasBundleVersion, + hasPreview, + link, + resolvingInitialLink, + result, + scope, + workspace, + onCancelInstall, + onClose, + onInspect, + onInstall +}: SkillInstallDialogFooterProps): React.JSX.Element | null { + if (hasBundleVersion) { + return null + } + + return ( + + + {!hasPreview && !resolvingInitialLink ? ( + + ) : null} + {busy && activeOperationId ? ( + + ) : null} + {hasPreview && + (!result || ['conflict', 'partial', 'failed', 'cancelled'].includes(result.status)) ? ( + + ) : null} + + ) +} diff --git a/src/renderer/src/components/skills/SkillInstallManagementDialog.tsx b/src/renderer/src/components/skills/SkillInstallManagementDialog.tsx index 0479aae20fd..d1a6016655f 100644 --- a/src/renderer/src/components/skills/SkillInstallManagementDialog.tsx +++ b/src/renderer/src/components/skills/SkillInstallManagementDialog.tsx @@ -1,14 +1,5 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { Loader2 } from 'lucide-react' -import { Button } from '@/components/ui/button' -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle -} from '@/components/ui/dialog' +import { Dialog } from '@/components/ui/dialog' import { useAppStore } from '@/store' import type { ManagedSkillInstall, @@ -17,18 +8,15 @@ import type { import type { SkillBundleInstallResult } from '../../../../shared/skill-bundle-install-contract' import type { SkillCloudPackageDetails } from '../../../../shared/skill-cloud-contract' import { notifyInstalledAgentSkillsChanged } from '@/hooks/useInstalledAgentSkills' -import { skillInstallResultLabel } from './skill-install-result-label' import { skillInstallManagementCopy } from './skill-install-management-copy' import { useSkillInstallProgress } from './skill-install-progress-state' -import { SkillManagedInstallRow } from './SkillManagedInstallRow' import { summarizeManagedSkillRemoval } from './skill-managed-removal-summary' import { groupManagedSkillInstalls, type SkillManagedInstallGroup } from './skill-managed-install-groups' -import { SkillInstallMachineSelect } from './SkillInstallMachineSelect' -import { SkillInstallManagementStatus } from './SkillInstallManagementStatus' import { translate } from '@/i18n/i18n' +import { SkillInstallManagementDialogContent } from './SkillInstallManagementDialogContent' export function SkillInstallManagementDialog({ open, @@ -351,77 +339,39 @@ export function SkillInstallManagementDialog({ onOpenChange(false) } - const destructiveConflict = - result?.status === 'conflict' || - Boolean(selected?.installs.some((install) => install.state === 'modified')) - return ( !next && !busy && close()}> - - - {copy.title} - {copy.description} - - ({ - id: `ssh:${id}`, - label, - connected: sshConnectionStates.get(id)?.status === 'connected' - }))} - /> - - {busy && installs.length === 0 ? : null} - {!busy && installs.length === 0 ? ( -

- {copy.noInstalls} -

- ) : null} - {groups.length > 0 ? ( -
    - {groups.map((group) => ( - (next ? void selectInstall(group) : collapse())} - onVersionChange={setVersionId} - onInstall={(discardLocal) => void installVersion(discardLocal)} - onCancelInstall={() => void cancelInstall()} - onSendToMachine={(shareId) => { - close() - useAppStore.getState().openSkillShare(shareId) - }} - onRemove={(discardLocal) => void remove(discardLocal)} - /> - ))} -
- ) : null} - - - - -
+ void cancelInstall()} + onClose={close} + onEnvironmentChange={setEnvironmentId} + onInstall={(discardLocal) => void installVersion(discardLocal)} + onOpenChange={(group, next) => (next ? void selectInstall(group) : collapse())} + onRemove={(discardLocal) => void remove(discardLocal)} + onSendToMachine={(shareId) => { + close() + useAppStore.getState().openSkillShare(shareId) + }} + onVersionChange={setVersionId} + />
) } diff --git a/src/renderer/src/components/skills/SkillInstallManagementDialogContent.tsx b/src/renderer/src/components/skills/SkillInstallManagementDialogContent.tsx new file mode 100644 index 00000000000..6d2abcc2263 --- /dev/null +++ b/src/renderer/src/components/skills/SkillInstallManagementDialogContent.tsx @@ -0,0 +1,151 @@ +import { Loader2 } from 'lucide-react' +import { Button } from '@/components/ui/button' +import { + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle +} from '@/components/ui/dialog' +import type { + ManagedSkillInstall, + SkillInstallResult +} from '../../../../shared/skill-install-contract' +import type { SkillBundleInstallResult } from '../../../../shared/skill-bundle-install-contract' +import type { SkillCloudPackageDetails } from '../../../../shared/skill-cloud-contract' +import type { skillInstallManagementCopy } from './skill-install-management-copy' +import { skillInstallResultLabel } from './skill-install-result-label' +import { SkillInstallMachineSelect } from './SkillInstallMachineSelect' +import { SkillInstallManagementStatus } from './SkillInstallManagementStatus' +import { SkillManagedInstallRow } from './SkillManagedInstallRow' +import type { SkillManagedInstallGroup } from './skill-managed-install-groups' + +type SkillInstallManagementDialogContentProps = { + bundleResult: SkillBundleInstallResult | null + busy: boolean + confirmRemove: boolean + copy: ReturnType + details: SkillCloudPackageDetails | null + environmentId: string + error: string | null + groups: SkillManagedInstallGroup[] + installs: ManagedSkillInstall[] + installActive: boolean + notice: string | null + result: SkillInstallResult | null + progressLabel: string | null + runtimeEnvironments: readonly { id: string; name: string }[] + selectedKey: string + sshConnectionStates: ReadonlyMap + sshTargetLabels: ReadonlyMap + versionId: string + onCancelInstall: () => void + onClose: () => void + onEnvironmentChange: (value: string) => void + onInstall: (discardLocal?: boolean) => void + onOpenChange: (group: SkillManagedInstallGroup, open: boolean) => void + onRemove: (discardLocal?: boolean) => void + onSendToMachine: (shareId: string) => void + onVersionChange: (versionId: string) => void +} + +export function SkillInstallManagementDialogContent({ + bundleResult, + busy, + confirmRemove, + copy, + details, + environmentId, + error, + groups, + installs, + installActive, + notice, + progressLabel, + result, + runtimeEnvironments, + selectedKey, + sshConnectionStates, + sshTargetLabels, + versionId, + onCancelInstall, + onClose, + onEnvironmentChange, + onInstall, + onOpenChange, + onRemove, + onSendToMachine, + onVersionChange +}: SkillInstallManagementDialogContentProps): React.JSX.Element { + const destructiveConflict = + result?.status === 'conflict' || + Boolean( + groups + .find((group) => group.key === selectedKey) + ?.installs.some((install) => install.state === 'modified') + ) + + return ( + + + {copy.title} + {copy.description} + + ({ + id: `ssh:${id}`, + label, + connected: sshConnectionStates.get(id)?.status === 'connected' + }))} + /> + {busy && installs.length === 0 ? : null} + {!busy && installs.length === 0 ? ( +

+ {copy.noInstalls} +

+ ) : null} + {groups.length > 0 ? ( +
    + {groups.map((group) => ( + onOpenChange(group, next)} + onVersionChange={onVersionChange} + onInstall={onInstall} + onCancelInstall={onCancelInstall} + onSendToMachine={onSendToMachine} + onRemove={onRemove} + /> + ))} +
+ ) : null} + + + + +
+ ) +}