index.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. 'use client'
  2. import React from 'react'
  3. import type { Plugin } from '../types'
  4. import Icon from '../card/base/card-icon'
  5. import CornerMark from './base/corner-mark'
  6. import Title from './base/title'
  7. import OrgInfo from './base/org-info'
  8. import Description from './base/description'
  9. import Placeholder from './base/placeholder'
  10. import cn from '@/utils/classnames'
  11. import { useGetLanguage } from '@/context/i18n'
  12. import { getLanguage } from '@/i18n-config/language'
  13. import { useSingleCategories } from '../hooks'
  14. import { renderI18nObject } from '@/i18n-config'
  15. import { useMixedTranslation } from '@/app/components/plugins/marketplace/hooks'
  16. // import Partner from '../base/badges/partner'
  17. import Verified from '../base/badges/verified'
  18. import { RiAlertFill } from '@remixicon/react'
  19. export type Props = {
  20. className?: string
  21. payload: Plugin
  22. titleLeft?: React.ReactNode
  23. installed?: boolean
  24. installFailed?: boolean
  25. hideCornerMark?: boolean
  26. descriptionLineRows?: number
  27. footer?: React.ReactNode
  28. isLoading?: boolean
  29. loadingFileName?: string
  30. locale?: string
  31. limitedInstall?: boolean
  32. }
  33. const Card = ({
  34. className,
  35. payload,
  36. titleLeft,
  37. installed,
  38. installFailed,
  39. hideCornerMark,
  40. descriptionLineRows = 2,
  41. footer,
  42. isLoading = false,
  43. loadingFileName,
  44. locale: localeFromProps,
  45. limitedInstall = false,
  46. }: Props) => {
  47. const defaultLocale = useGetLanguage()
  48. const locale = localeFromProps ? getLanguage(localeFromProps) : defaultLocale
  49. const { t } = useMixedTranslation(localeFromProps)
  50. const { categoriesMap } = useSingleCategories(t)
  51. const { category, type, name, org, label, brief, icon, verified, badges = [] } = payload
  52. const isBundle = !['plugin', 'model', 'tool', 'extension', 'agent-strategy'].includes(type)
  53. const cornerMark = isBundle ? categoriesMap.bundle?.label : categoriesMap[category]?.label
  54. const getLocalizedText = (obj: Record<string, string> | undefined) =>
  55. obj ? renderI18nObject(obj, locale) : ''
  56. // zlwu12 2025-09-18 注释
  57. // const isPartner = badges.includes('partner')
  58. const wrapClassName = cn('hover-bg-components-panel-on-panel-item-bg relative overflow-hidden rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg shadow-xs', className)
  59. if (isLoading) {
  60. return (
  61. <Placeholder
  62. wrapClassName={wrapClassName}
  63. loadingFileName={loadingFileName!}
  64. />
  65. )
  66. }
  67. return (
  68. <div className={wrapClassName}>
  69. <div className={cn('p-4 pb-3', limitedInstall && 'pb-1')}>
  70. {!hideCornerMark && <CornerMark text={cornerMark} />}
  71. {/* Header */}
  72. <div className="flex">
  73. <Icon src={icon} installed={installed} installFailed={installFailed} />
  74. <div className="ml-3 w-0 grow">
  75. <div className="flex h-5 items-center">
  76. <Title title={getLocalizedText(label)} />
  77. {/* zlwu12 2025-09-18 注释 */}
  78. {/* {isPartner && <Partner className='ml-0.5 h-4 w-4' text={t('plugin.marketplace.partnerTip')} />} */}
  79. {verified && <Verified className='ml-0.5 h-4 w-4' text={t('plugin.marketplace.verifiedTip')} />}
  80. {titleLeft} {/* This can be version badge */}
  81. </div>
  82. <OrgInfo
  83. className="mt-0.5"
  84. orgName={org}
  85. packageName={name}
  86. />
  87. </div>
  88. </div>
  89. <Description
  90. className="mt-3"
  91. text={getLocalizedText(brief)}
  92. descriptionLineRows={descriptionLineRows}
  93. />
  94. {footer && <div>{footer}</div>}
  95. </div>
  96. {limitedInstall
  97. && <div className='relative flex h-8 items-center gap-x-2 px-3 after:absolute after:bottom-0 after:left-0 after:right-0 after:top-0 after:bg-toast-warning-bg after:opacity-40'>
  98. <RiAlertFill className='h-3 w-3 shrink-0 text-text-warning-secondary' />
  99. <p className='system-xs-regular z-10 grow text-text-secondary'>
  100. {t('plugin.installModal.installWarning')}
  101. </p>
  102. </div>}
  103. </div>
  104. )
  105. }
  106. export default React.memo(Card)