| ... | ... | @@ -593,7 +593,11 @@ export function ProviderInstanceCard({ |
| 593 | 593 | compatibility.status !== "supported" && |
| 594 | 594 | compatibility.status !== "unknown"; |
| 595 | 595 | const VersionAdvisoryIcon = hasCompatibilityWarning ? AlertTriangleIcon : ArrowUpCircleIcon; |
| 596 | | const onRunVersionAction = versionAdvisory?.targetVersion ? onInstallRecommended : onRunUpdate; |
| 596 | const onRunVersionAction = readOnly |
| 597 | ? undefined |
| 598 | : versionAdvisory?.targetVersion |
| 599 | ? onInstallRecommended |
| 600 | : onRunUpdate; |
| 597 | 601 | const urlAuthAction = liveProvider?.auth.action; |
| 598 | 602 | const displayName = |
| 599 | 603 | instance.displayName?.trim() || driverOption?.label || String(instance.driver); |
| ... | ... | @@ -765,6 +769,101 @@ export function ProviderInstanceCard({ |
| 765 | 769 | ) : null} |
| 766 | 770 | </> |
| 767 | 771 | ); |
| 772 | const versionAdvisoryNode = versionAdvisory ? ( |
| 773 | <Popover> |
| 774 | <Tooltip> |
| 775 | <TooltipTrigger |
| 776 | render={ |
| 777 | <PopoverTrigger |
| 778 | render={ |
| 779 | <Button |
| 780 | type="button" |
| 781 | size={mode === "list" ? "icon-micro" : "icon-xs"} |
| 782 | variant="ghost-muted" |
| 783 | className={mode === "list" ? "pointer-events-auto relative shrink-0" : undefined} |
| 784 | aria-label={`${versionAdvisory.title} — view details`} |
| 785 | > |
| 786 | <VersionAdvisoryIcon |
| 787 | className={cn( |
| 788 | mode === "list" && "size-3.5", |
| 789 | hasCompatibilityWarning && "text-warning", |
| 790 | )} |
| 791 | /> |
| 792 | </Button> |
| 793 | } |
| 794 | /> |
| 795 | } |
| 796 | /> |
| 797 | <TooltipPopup side="top">{versionAdvisory.title}</TooltipPopup> |
| 798 | </Tooltip> |
| 799 | <PopoverPopup side="bottom" align="end" width="md" aria-label={versionAdvisory.title}> |
| 800 | <div className="grid min-w-0 gap-3"> |
| 801 | <div className="grid gap-0.5"> |
| 802 | <p className="text-sm font-semibold leading-tight text-foreground"> |
| 803 | {versionAdvisory.title} |
| 804 | </p> |
| 805 | <p |
| 806 | className={cn( |
| 807 | "text-xs leading-snug", |
| 808 | versionAdvisory.emphasis === "strong" ? "text-warning" : "text-muted-foreground", |
| 809 | )} |
| 810 | > |
| 811 | {versionAdvisory.detail} |
| 812 | </p> |
| 813 | </div> |
| 814 | {onRunVersionAction ? ( |
| 815 | <Button |
| 816 | type="button" |
| 817 | size="xs" |
| 818 | variant="outline" |
| 819 | className="w-full" |
| 820 | disabled={isUpdating} |
| 821 | onClick={onRunVersionAction} |
| 822 | > |
| 823 | {isUpdating ? <Spinner /> : <DownloadIcon />} |
| 824 | {isUpdating |
| 825 | ? "Updating" |
| 826 | : versionAdvisory.targetVersion |
| 827 | ? `Install ${getProviderVersionLabel(versionAdvisory.targetVersion)}` |
| 828 | : "Update now"} |
| 829 | </Button> |
| 830 | ) : null} |
| 831 | {onRunVersionAction && updateCommand ? ( |
| 832 | <div className="flex items-center gap-2 text-3xs font-medium uppercase tracking-wider text-muted-foreground"> |
| 833 | <span aria-hidden className="h-px flex-1 bg-border" /> |
| 834 | or, update manually using |
| 835 | <span aria-hidden className="h-px flex-1 bg-border" /> |
| 836 | </div> |
| 837 | ) : null} |
| 838 | {updateCommand ? ( |
| 839 | <div className="flex min-w-0 items-center gap-1 rounded-md border border-border/70 bg-muted/40 py-0.5 pr-0.5 pl-2"> |
| 840 | <code className="min-w-0 flex-1 truncate font-mono text-2xs text-foreground"> |
| 841 | {updateCommand} |
| 842 | </code> |
| 843 | <Tooltip> |
| 844 | <TooltipTrigger |
| 845 | render={ |
| 846 | <Button |
| 847 | type="button" |
| 848 | size="icon-xs" |
| 849 | variant="ghost-muted" |
| 850 | className="shrink-0" |
| 851 | onClick={() => copyToClipboard(updateCommand, { providerName: displayName })} |
| 852 | aria-label="Copy update command" |
| 853 | > |
| 854 | <CopyIcon className="size-3" /> |
| 855 | </Button> |
| 856 | } |
| 857 | /> |
| 858 | <TooltipPopup side="top">Copy command</TooltipPopup> |
| 859 | </Tooltip> |
| 860 | </div> |
| 861 | ) : null} |
| 862 | </div> |
| 863 | </PopoverPopup> |
| 864 | </Popover> |
| 865 | ) : null; |
| 866 | |
| 768 | 867 | if (mode === "list") { |
| 769 | 868 | return ( |
| 770 | 869 | <div |
| ... | ... | @@ -796,49 +895,7 @@ export function ProviderInstanceCard({ |
| 796 | 895 | {versionLabel} |
| 797 | 896 | </code> |
| 798 | 897 | ) : null} |
| 799 | | {versionAdvisory ? ( |
| 800 | | hasCompatibilityWarning ? ( |
| 801 | | <Tooltip> |
| 802 | | <TooltipTrigger |
| 803 | | render={ |
| 804 | | <span |
| 805 | | tabIndex={0} |
| 806 | | role="img" |
| 807 | | aria-label={versionAdvisory.title} |
| 808 | | className="pointer-events-auto relative inline-flex shrink-0 text-warning" |
| 809 | | > |
| 810 | | <VersionAdvisoryIcon className="size-3.5" /> |
| 811 | | </span> |
| 812 | | } |
| 813 | | /> |
| 814 | | <TooltipPopup side="top">{versionAdvisory.detail}</TooltipPopup> |
| 815 | | </Tooltip> |
| 816 | | ) : updateCommand ? ( |
| 817 | | <Tooltip> |
| 818 | | <TooltipTrigger |
| 819 | | render={ |
| 820 | | <Button |
| 821 | | type="button" |
| 822 | | size="icon-micro" |
| 823 | | variant="ghost-muted" |
| 824 | | className="pointer-events-auto relative shrink-0" |
| 825 | | aria-label={`Copy ${displayName} update command`} |
| 826 | | onClick={() => |
| 827 | | copyToClipboard(updateCommand, { providerName: displayName }) |
| 828 | | } |
| 829 | | > |
| 830 | | <ArrowUpCircleIcon className="size-3.5" /> |
| 831 | | </Button> |
| 832 | | } |
| 833 | | /> |
| 834 | | <TooltipPopup side="top">Copy update command</TooltipPopup> |
| 835 | | </Tooltip> |
| 836 | | ) : ( |
| 837 | | <span role="img" aria-label="Update available" className="inline-flex shrink-0"> |
| 838 | | <ArrowUpCircleIcon className="size-3.5 text-muted-foreground" /> |
| 839 | | </span> |
| 840 | | ) |
| 841 | | ) : null} |
| 898 | {versionAdvisoryNode} |
| 842 | 899 | </span> |
| 843 | 900 | <span className="mt-0.5 flex items-start gap-1.5 text-xs leading-normal text-muted-foreground/80"> |
| 844 | 901 | {statusDotNode ? ( |
| ... | ... | @@ -876,105 +933,12 @@ export function ProviderInstanceCard({ |
| 876 | 933 | </Badge> |
| 877 | 934 | ) : null} |
| 878 | 935 | {versionCodeNode} |
| 936 | {versionAdvisoryNode} |
| 879 | 937 | <span |
| 880 | 938 | inert={readOnly} |
| 881 | 939 | aria-disabled={readOnly || undefined} |
| 882 | 940 | className={cn("inline-flex items-center gap-1", readOnly && "opacity-50")} |
| 883 | 941 | > |
| 884 | | {versionAdvisory ? ( |
| 885 | | <Popover> |
| 886 | | <Tooltip> |
| 887 | | <TooltipTrigger |
| 888 | | render={ |
| 889 | | <PopoverTrigger |
| 890 | | render={ |
| 891 | | <Button |
| 892 | | type="button" |
| 893 | | size="icon-xs" |
| 894 | | variant="ghost-muted" |
| 895 | | aria-label={`${versionAdvisory.title} — view details`} |
| 896 | | > |
| 897 | | <VersionAdvisoryIcon |
| 898 | | className={cn(hasCompatibilityWarning && "text-warning")} |
| 899 | | /> |
| 900 | | </Button> |
| 901 | | } |
| 902 | | /> |
| 903 | | } |
| 904 | | /> |
| 905 | | <TooltipPopup side="top">{versionAdvisory.title}</TooltipPopup> |
| 906 | | </Tooltip> |
| 907 | | <PopoverPopup side="bottom" align="end" width="md"> |
| 908 | | <div className="grid min-w-0 gap-3"> |
| 909 | | <div className="grid gap-0.5"> |
| 910 | | <p className="text-sm font-semibold leading-tight text-foreground"> |
| 911 | | {versionAdvisory.title} |
| 912 | | </p> |
| 913 | | <p |
| 914 | | className={cn( |
| 915 | | "text-xs leading-snug", |
| 916 | | versionAdvisory.emphasis === "strong" |
| 917 | | ? "text-warning" |
| 918 | | : "text-muted-foreground", |
| 919 | | )} |
| 920 | | > |
| 921 | | {versionAdvisory.detail} |
| 922 | | </p> |
| 923 | | </div> |
| 924 | | {onRunVersionAction ? ( |
| 925 | | <Button |
| 926 | | type="button" |
| 927 | | size="xs" |
| 928 | | variant="outline" |
| 929 | | className="w-full" |
| 930 | | disabled={isUpdating} |
| 931 | | onClick={onRunVersionAction} |
| 932 | | > |
| 933 | | {isUpdating ? <Spinner /> : <DownloadIcon />} |
| 934 | | {isUpdating |
| 935 | | ? "Updating" |
| 936 | | : versionAdvisory.targetVersion |
| 937 | | ? `Install ${getProviderVersionLabel(versionAdvisory.targetVersion)}` |
| 938 | | : "Update now"} |
| 939 | | </Button> |
| 940 | | ) : null} |
| 941 | | {onRunVersionAction && updateCommand ? ( |
| 942 | | <div className="flex items-center gap-2 text-3xs font-medium uppercase tracking-wider text-muted-foreground"> |
| 943 | | <span aria-hidden className="h-px flex-1 bg-border" /> |
| 944 | | or, update manually using |
| 945 | | <span aria-hidden className="h-px flex-1 bg-border" /> |
| 946 | | </div> |
| 947 | | ) : null} |
| 948 | | {updateCommand ? ( |
| 949 | | <div className="flex min-w-0 items-center gap-1 rounded-md border border-border/70 bg-muted/40 py-0.5 pr-0.5 pl-2"> |
| 950 | | <code className="min-w-0 flex-1 truncate font-mono text-2xs text-foreground"> |
| 951 | | {updateCommand} |
| 952 | | </code> |
| 953 | | <Tooltip> |
| 954 | | <TooltipTrigger |
| 955 | | render={ |
| 956 | | <Button |
| 957 | | type="button" |
| 958 | | size="icon-xs" |
| 959 | | variant="ghost-muted" |
| 960 | | className="shrink-0" |
| 961 | | onClick={() => |
| 962 | | copyToClipboard(updateCommand, { providerName: displayName }) |
| 963 | | } |
| 964 | | aria-label="Copy update command" |
| 965 | | > |
| 966 | | <CopyIcon className="size-3" /> |
| 967 | | </Button> |
| 968 | | } |
| 969 | | /> |
| 970 | | <TooltipPopup side="top">Copy command</TooltipPopup> |
| 971 | | </Tooltip> |
| 972 | | </div> |
| 973 | | ) : null} |
| 974 | | </div> |
| 975 | | </PopoverPopup> |
| 976 | | </Popover> |
| 977 | | ) : null} |
| 978 | 942 | {titleTailNode} |
| 979 | 943 | {onDelete ? ( |
| 980 | 944 | <Button |