diff --git a/packages/frontend/component/src/ui/layout/wrapper.tsx b/packages/frontend/component/src/ui/layout/wrapper.tsx index 2a7e778683990..1b73b44b2d3c3 100644 --- a/packages/frontend/component/src/ui/layout/wrapper.tsx +++ b/packages/frontend/component/src/ui/layout/wrapper.tsx @@ -26,6 +26,7 @@ export type FlexWrapperProps = { wrap?: boolean; flexShrink?: CSSProperties['flexShrink']; flexGrow?: CSSProperties['flexGrow']; + gap?: CSSProperties['gap']; }; // Sometimes we just want to wrap a component with a div to set flex or other styles, but we don't want to create a new component for it. @@ -88,6 +89,7 @@ export const FlexWrapper = styled(Wrapper, { 'flexDirection', 'flexShrink', 'flexGrow', + 'gap', ].includes(prop as string); }, })(({ @@ -97,6 +99,7 @@ export const FlexWrapper = styled(Wrapper, { flexDirection, flexShrink, flexGrow, + gap, }) => { return { display: 'flex', @@ -106,6 +109,7 @@ export const FlexWrapper = styled(Wrapper, { flexDirection, flexShrink, flexGrow, + gap, }; }); diff --git a/packages/frontend/core/src/components/affine/ai-onboarding/edgeless.dialog.css.ts b/packages/frontend/core/src/components/affine/ai-onboarding/edgeless.dialog.css.ts index 0bfdd1b7da777..4f6f9170cc9dd 100644 --- a/packages/frontend/core/src/components/affine/ai-onboarding/edgeless.dialog.css.ts +++ b/packages/frontend/core/src/components/affine/ai-onboarding/edgeless.dialog.css.ts @@ -19,3 +19,15 @@ export const thumbContent = style({ width: 'calc(100% + 4px)', height: 'calc(100% + 4px)', }); + +export const actionButton = style({ + fontWeight: 500, + fontSize: cssVar('fontSm'), + lineHeight: '22px', +}); +export const getStartedButtonText = style({ + color: cssVar('textSecondaryColor'), +}); +export const purchaseButtonText = style({ + color: cssVar('textPrimaryColor'), +}); diff --git a/packages/frontend/core/src/components/affine/ai-onboarding/edgeless.dialog.tsx b/packages/frontend/core/src/components/affine/ai-onboarding/edgeless.dialog.tsx index 92de0c744cd46..67ef274aac6d7 100644 --- a/packages/frontend/core/src/components/affine/ai-onboarding/edgeless.dialog.tsx +++ b/packages/frontend/core/src/components/affine/ai-onboarding/edgeless.dialog.tsx @@ -1,5 +1,6 @@ -import { notify } from '@affine/component'; +import { Button, FlexWrapper, notify } from '@affine/component'; import { openSettingModalAtom } from '@affine/core/atoms'; +import { SubscriptionService } from '@affine/core/modules/cloud'; import { WorkspaceFlavour } from '@affine/env/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { AiIcon } from '@blocksuite/icons'; @@ -10,10 +11,10 @@ import { WorkspaceService, } from '@toeverything/infra'; import { cssVar } from '@toeverything/theme'; -import { useAtomValue } from 'jotai'; +import { useAtomValue, useSetAtom } from 'jotai'; import Lottie from 'lottie-react'; import { useTheme } from 'next-themes'; -import { useEffect, useMemo, useRef } from 'react'; +import { useCallback, useEffect, useMemo, useRef } from 'react'; import * as styles from './edgeless.dialog.css'; import mouseTrackDark from './lottie/edgeless/mouse-track-dark.json'; @@ -47,22 +48,34 @@ const EdgelessOnboardingAnimation = () => { export const AIOnboardingEdgeless = ({ onDismiss, }: BaseAIOnboardingDialogProps) => { - const { workspaceService, docService } = useServices({ + const { workspaceService, docService, subscriptionService } = useServices({ WorkspaceService, DocService, + SubscriptionService, }); const t = useAFFiNEI18N(); const notifyId = useLiveData(edgelessNotifyId$); const generalAIOnboardingOpened = useLiveData(showAIOnboardingGeneral$); + const aiSubscription = useLiveData(subscriptionService.subscription.ai$); const settingModalOpen = useAtomValue(openSettingModalAtom); const timeoutRef = useRef>(); const isCloud = workspaceService.workspace.flavour === WorkspaceFlavour.AFFINE_CLOUD; + const setSettingModal = useSetAtom(openSettingModalAtom); + const doc = docService.doc; const mode = useLiveData(doc.mode$); + const goToPricingPlans = useCallback(() => { + setSettingModal({ + open: true, + activeTab: 'plans', + scrollAnchor: 'aiPricingPlan', + }); + }, [setSettingModal]); + useEffect(() => { if (settingModalOpen.open) return; if (generalAIOnboardingOpened) return; @@ -83,13 +96,46 @@ export const AIOnboardingEdgeless = ({ thumb: , alignMessage: 'icon', onDismiss, + footer: ( + + + {aiSubscription ? null : ( + + )} + + ), }, { duration: 1000 * 60 * 10 } ); edgelessNotifyId$.next(id); }, 1000); }, [ + aiSubscription, generalAIOnboardingOpened, + goToPricingPlans, isCloud, mode, notifyId, diff --git a/packages/frontend/core/src/components/affine/ai-onboarding/general.dialog.tsx b/packages/frontend/core/src/components/affine/ai-onboarding/general.dialog.tsx index 97e80e72078ca..9a7c003ca419a 100644 --- a/packages/frontend/core/src/components/affine/ai-onboarding/general.dialog.tsx +++ b/packages/frontend/core/src/components/affine/ai-onboarding/general.dialog.tsx @@ -229,7 +229,7 @@ export const AIOnboardingGeneral = ({ a: ( ), }} diff --git a/packages/frontend/i18n/src/resources/en.json b/packages/frontend/i18n/src/resources/en.json index 0177c27e4747c..fb40f6c995e12 100644 --- a/packages/frontend/i18n/src/resources/en.json +++ b/packages/frontend/i18n/src/resources/en.json @@ -909,7 +909,7 @@ "com.affine.payment.billing-setting.upgrade": "Upgrade", "com.affine.payment.billing-setting.view-invoice": "View Invoice", "com.affine.payment.billing-setting.year": "year", - "com.affine.payment.billing-setting.ai.free-desc": "Yue are current on the Free plan.", + "com.affine.payment.billing-setting.ai.free-desc": "You are current on the Free plan.", "com.affine.payment.billing-setting.ai.purchase": "Purchase", "com.affine.payment.blob-limit.description.local": "The maximum file upload size for local workspaces is {{quota}}.", "com.affine.payment.blob-limit.description.member": "The maximum file upload size for this joined workspace is {{quota}}. You can contact the owner of this workspace.", @@ -1306,8 +1306,10 @@ "com.affine.ai-onboarding.local.message": "Lets you think bigger, create faster, work smarter and save time for every project.", "com.affine.ai-onboarding.local.action-dismiss": "Dismiss", "com.affine.ai-onboarding.local.action-learn-more": "Learn More", - "com.affine.ai-onboarding.edgeless.title": "Meet AFFiNE AI", + "com.affine.ai-onboarding.edgeless.title": "Right-clicking to select content AI", "com.affine.ai-onboarding.edgeless.message": "Lets you think bigger, create faster, work smarter and save time for every project.", + "com.affine.ai-onboarding.edgeless.get-started": "Get Started", + "com.affine.ai-onboarding.edgeless.purchase": "Upgrade to Unlimited Usage", "com.affine.ai.login-required.dialog-title": "Sign in to Continue", "com.affine.ai.login-required.dialog-content": "To use AFFiNE AI, please sign in to your AFFiNE Cloud account.", "com.affine.ai.login-required.dialog-confirm": "Sign in",