diff --git a/.eslintrc.js b/.eslintrc.js index 8ae91f34..92834fe6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,5 +1,3 @@ -const bskyEslint = require('./eslint') - module.exports = { root: true, extends: [ @@ -27,29 +25,18 @@ module.exports = { { impliedTextComponents: [ 'Button', // TODO: Not always safe. - 'ButtonText', - 'DateField.Label', - 'Description', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', - 'InlineLink', - 'Label', 'P', - 'Prompt.Title', - 'Prompt.Description', 'Prompt.Cancel', // TODO: Not always safe. 'Prompt.Action', // TODO: Not always safe. - 'TextField.Label', - 'TextField.Suffix', - 'Title', - 'Toggle.Label', 'ToggleButton.Button', // TODO: Not always safe. ], - impliedTextProps: ['FormContainer title'], + impliedTextProps: [], }, ], 'simple-import-sort/imports': [ diff --git a/eslint/__tests__/avoid-unwrapped-text.test.js b/eslint/__tests__/avoid-unwrapped-text.test.js index 0fbc0112..7c667b4a 100644 --- a/eslint/__tests__/avoid-unwrapped-text.test.js +++ b/eslint/__tests__/avoid-unwrapped-text.test.js @@ -246,6 +246,41 @@ describe('avoid-unwrapped-text', () => { `, }, + + { + code: ` +function Stuff() { + return foo +} + `, + }, + + { + code: ` +function Stuff({ foo }) { + return {foo} +} + `, + }, + + { + code: ` +function MyText() { + return foo +} + `, + }, + + { + code: ` +function MyText({ foo }) { + if (foo) { + return foo + } + return foo +} + `, + }, ], invalid: [ @@ -390,6 +425,36 @@ describe('avoid-unwrapped-text', () => { `, errors: 1, }, + + { + code: ` +function MyText() { + return +} + `, + errors: 1, + }, + + { + code: ` +function MyText({ foo }) { + return {foo} +} + `, + errors: 1, + }, + + { + code: ` +function MyText({ foo }) { + if (foo) { + return {foo} + } + return foo +} + `, + errors: 1, + }, ], } diff --git a/eslint/avoid-unwrapped-text.js b/eslint/avoid-unwrapped-text.js index c9e72386..79d099f0 100644 --- a/eslint/avoid-unwrapped-text.js +++ b/eslint/avoid-unwrapped-text.js @@ -35,6 +35,11 @@ exports.create = function create(context) { const impliedTextComponents = options.impliedTextComponents ?? [] const textProps = [...impliedTextProps] const textComponents = ['Text', ...impliedTextComponents] + + function isTextComponent(tagName) { + return textComponents.includes(tagName) || tagName.endsWith('Text') + } + return { JSXText(node) { if (typeof node.value !== 'string' || hasOnlyLineBreak(node.value)) { @@ -44,7 +49,7 @@ exports.create = function create(context) { while (parent) { if (parent.type === 'JSXElement') { const tagName = getTagName(parent) - if (textComponents.includes(tagName) || tagName.endsWith('Text')) { + if (isTextComponent(tagName)) { // We're good. return } @@ -107,5 +112,36 @@ exports.create = function create(context) { continue } }, + ReturnStatement(node) { + let fnScope = context.getScope() + while (fnScope && fnScope.type !== 'function') { + fnScope = fnScope.upper + } + if (!fnScope) { + return + } + const fn = fnScope.block + if (!fn.id || fn.id.type !== 'Identifier' || !fn.id.name) { + return + } + if (!/^[A-Z]\w*Text$/.test(fn.id.name)) { + return + } + if (!node.argument || node.argument.type !== 'JSXElement') { + return + } + const openingEl = node.argument.openingElement + if (openingEl.name.type !== 'JSXIdentifier') { + return + } + const returnedComponentName = openingEl.name.name + if (!isTextComponent(returnedComponentName)) { + context.report({ + node, + message: + 'Components ending with *Text must return or .', + }) + } + }, } } diff --git a/src/components/Link.tsx b/src/components/Link.tsx index 1a494626..65a015ba 100644 --- a/src/components/Link.tsx +++ b/src/components/Link.tsx @@ -250,7 +250,7 @@ export type InlineLinkProps = React.PropsWithChildren< BaseLinkProps & TextStyleProp & Pick > -export function InlineLink({ +export function InlineLinkText({ children, to, action = 'push', diff --git a/src/components/Prompt.tsx b/src/components/Prompt.tsx index 37d1b700..000d2a3c 100644 --- a/src/components/Prompt.tsx +++ b/src/components/Prompt.tsx @@ -51,7 +51,7 @@ export function Outer({ ) } -export function Title({children}: React.PropsWithChildren<{}>) { +export function TitleText({children}: React.PropsWithChildren<{}>) { const {titleId} = React.useContext(Context) return ( @@ -60,7 +60,7 @@ export function Title({children}: React.PropsWithChildren<{}>) { ) } -export function Description({children}: React.PropsWithChildren<{}>) { +export function DescriptionText({children}: React.PropsWithChildren<{}>) { const t = useTheme() const {descriptionId} = React.useContext(Context) return ( @@ -175,8 +175,8 @@ export function Basic({ }>) { return ( - {title} - {description} + {title} + {description} {segment.text} - , + , ) } else if (link && AppBskyRichtextFacet.validateLink(link).success) { if (disableLinks) { els.push(toShortUrl(segment.text)) } else { els.push( - {toShortUrl(segment.text)} - , + , ) } } else if ( diff --git a/src/components/dialogs/MutedWords.tsx b/src/components/dialogs/MutedWords.tsx index 46f319ad..0eced11e 100644 --- a/src/components/dialogs/MutedWords.tsx +++ b/src/components/dialogs/MutedWords.tsx @@ -1,37 +1,36 @@ import React from 'react' import {Keyboard, View} from 'react-native' +import {AppBskyActorDefs, sanitizeMutedWordValue} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {AppBskyActorDefs, sanitizeMutedWordValue} from '@atproto/api' -import { - usePreferencesQuery, - useUpsertMutedWordsMutation, - useRemoveMutedWordMutation, -} from '#/state/queries/preferences' +import {logger} from '#/logger' import {isNative} from '#/platform/detection' +import { + usePreferencesQuery, + useRemoveMutedWordMutation, + useUpsertMutedWordsMutation, +} from '#/state/queries/preferences' import { atoms as a, - useTheme, + native, useBreakpoints, + useTheme, ViewStyleProp, web, - native, } from '#/alf' -import {Text} from '#/components/Typography' import {Button, ButtonIcon, ButtonText} from '#/components/Button' -import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' -import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' +import * as Dialog from '#/components/Dialog' +import {useGlobalDialogsControlContext} from '#/components/dialogs/Context' +import {Divider} from '#/components/Divider' +import * as Toggle from '#/components/forms/Toggle' import {Hashtag_Stroke2_Corner0_Rounded as Hashtag} from '#/components/icons/Hashtag' import {PageText_Stroke2_Corner0_Rounded as PageText} from '#/components/icons/PageText' -import {Divider} from '#/components/Divider' +import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' +import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' import {Loader} from '#/components/Loader' -import {logger} from '#/logger' -import * as Dialog from '#/components/Dialog' -import * as Toggle from '#/components/forms/Toggle' import * as Prompt from '#/components/Prompt' - -import {useGlobalDialogsControlContext} from '#/components/dialogs/Context' +import {Text} from '#/components/Typography' export function MutedWordsDialog() { const {mutedWordsDialogControl: control} = useGlobalDialogsControlContext() @@ -130,9 +129,9 @@ function MutedWordsInner({}: {control: Dialog.DialogOuterProps['control']}) { - + Mute in text & tags - + @@ -145,9 +144,9 @@ function MutedWordsInner({}: {control: Dialog.DialogOuterProps['control']}) { - + Mute in tags only - + diff --git a/src/components/forms/DateField/index.android.tsx b/src/components/forms/DateField/index.android.tsx index 700d15e6..1830ca4b 100644 --- a/src/components/forms/DateField/index.android.tsx +++ b/src/components/forms/DateField/index.android.tsx @@ -8,7 +8,7 @@ import * as TextField from '#/components/forms/TextField' import {DateFieldButton} from './index.shared' export * as utils from '#/components/forms/DateField/utils' -export const Label = TextField.Label +export const LabelText = TextField.LabelText export function DateField({ value, diff --git a/src/components/forms/DateField/index.tsx b/src/components/forms/DateField/index.tsx index 5662bb59..e231ac5b 100644 --- a/src/components/forms/DateField/index.tsx +++ b/src/components/forms/DateField/index.tsx @@ -13,7 +13,7 @@ import * as TextField from '#/components/forms/TextField' import {DateFieldButton} from './index.shared' export * as utils from '#/components/forms/DateField/utils' -export const Label = TextField.Label +export const LabelText = TextField.LabelText /** * Date-only input. Accepts a date in the format YYYY-MM-DD, and reports date diff --git a/src/components/forms/DateField/index.web.tsx b/src/components/forms/DateField/index.web.tsx index 982d3271..b764620e 100644 --- a/src/components/forms/DateField/index.web.tsx +++ b/src/components/forms/DateField/index.web.tsx @@ -9,7 +9,7 @@ import * as TextField from '#/components/forms/TextField' import {CalendarDays_Stroke2_Corner0_Rounded as CalendarDays} from '#/components/icons/CalendarDays' export * as utils from '#/components/forms/DateField/utils' -export const Label = TextField.Label +export const LabelText = TextField.LabelText const InputBase = React.forwardRef( ({style, ...props}, ref) => { diff --git a/src/components/forms/TextField.tsx b/src/components/forms/TextField.tsx index 0bdeca64..73a660ea 100644 --- a/src/components/forms/TextField.tsx +++ b/src/components/forms/TextField.tsx @@ -225,7 +225,7 @@ export function createInput(Component: typeof TextInput) { export const Input = createInput(TextInput) -export function Label({ +export function LabelText({ nativeID, children, }: React.PropsWithChildren<{nativeID?: string}>) { @@ -288,7 +288,7 @@ export function Icon({icon: Comp}: {icon: React.ComponentType}) { ) } -export function Suffix({ +export function SuffixText({ children, label, accessibilityHint, diff --git a/src/components/forms/Toggle.tsx b/src/components/forms/Toggle.tsx index 7a4b5ac9..7285e5fa 100644 --- a/src/components/forms/Toggle.tsx +++ b/src/components/forms/Toggle.tsx @@ -3,16 +3,16 @@ import {Pressable, View, ViewStyle} from 'react-native' import {HITSLOP_10} from 'lib/constants' import { - useTheme, atoms as a, - native, flatten, - ViewStyleProp, + native, TextStyleProp, + useTheme, + ViewStyleProp, } from '#/alf' -import {Text} from '#/components/Typography' import {useInteractionState} from '#/components/hooks/useInteractionState' import {CheckThick_Stroke2_Corner0_Rounded as Checkmark} from '#/components/icons/Check' +import {Text} from '#/components/Typography' export type ItemState = { name: string @@ -234,7 +234,7 @@ export function Item({ ) } -export function Label({ +export function LabelText({ children, style, }: React.PropsWithChildren) { diff --git a/src/components/moderation/LabelPreference.tsx b/src/components/moderation/LabelPreference.tsx index 028bd1a3..990e7362 100644 --- a/src/components/moderation/LabelPreference.tsx +++ b/src/components/moderation/LabelPreference.tsx @@ -13,7 +13,7 @@ import { } from '#/state/queries/preferences' import {atoms as a, useBreakpoints, useTheme} from '#/alf' import * as ToggleButton from '#/components/forms/ToggleButton' -import {InlineLink} from '#/components/Link' +import {InlineLinkText} from '#/components/Link' import {Text} from '#/components/Typography' import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '../icons/CircleInfo' @@ -243,9 +243,9 @@ export function LabelerLabelPreference({ ) : isGlobalLabel ? ( Configured in{' '} - + moderation settings - + . ) : null} diff --git a/src/components/moderation/LabelsOnMeDialog.tsx b/src/components/moderation/LabelsOnMeDialog.tsx index 6eddbc7c..95e3d242 100644 --- a/src/components/moderation/LabelsOnMeDialog.tsx +++ b/src/components/moderation/LabelsOnMeDialog.tsx @@ -1,20 +1,19 @@ import React from 'react' import {View} from 'react-native' +import {ComAtprotoLabelDefs, ComAtprotoModerationDefs} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {ComAtprotoLabelDefs, ComAtprotoModerationDefs} from '@atproto/api' import {useLabelInfo} from '#/lib/moderation/useLabelInfo' import {makeProfileLink} from '#/lib/routes/links' import {sanitizeHandle} from '#/lib/strings/handles' import {getAgent} from '#/state/session' - -import {atoms as a, useBreakpoints, useTheme} from '#/alf' -import {Text} from '#/components/Typography' -import * as Dialog from '#/components/Dialog' -import {Button, ButtonText} from '#/components/Button' -import {InlineLink} from '#/components/Link' import * as Toast from '#/view/com/util/Toast' +import {atoms as a, useBreakpoints, useTheme} from '#/alf' +import {Button, ButtonText} from '#/components/Button' +import * as Dialog from '#/components/Dialog' +import {InlineLinkText} from '#/components/Link' +import {Text} from '#/components/Typography' import {Divider} from '../Divider' export {useDialogControl as useLabelsOnMeDialogControl} from '#/components/Dialog' @@ -145,13 +144,13 @@ function Label({ Source:{' '} - control.close()}> {labeler ? sanitizeHandle(labeler.creator.handle, '@') : label.src} - + @@ -204,14 +203,14 @@ function AppealForm({ This appeal will be sent to{' '} - control.close()} style={[a.text_md, a.leading_snug]}> {labeler ? sanitizeHandle(labeler.creator.handle, '@') : label.src} - + . diff --git a/src/components/moderation/ModerationDetailsDialog.tsx b/src/components/moderation/ModerationDetailsDialog.tsx index da490cb4..da57de4d 100644 --- a/src/components/moderation/ModerationDetailsDialog.tsx +++ b/src/components/moderation/ModerationDetailsDialog.tsx @@ -1,19 +1,18 @@ import React from 'react' import {View} from 'react-native' +import {ModerationCause} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {ModerationCause} from '@atproto/api' -import {listUriToHref} from '#/lib/strings/url-helpers' import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription' import {makeProfileLink} from '#/lib/routes/links' - +import {listUriToHref} from '#/lib/strings/url-helpers' import {isNative} from '#/platform/detection' -import {useTheme, atoms as a} from '#/alf' -import {Text} from '#/components/Typography' +import {atoms as a, useTheme} from '#/alf' import * as Dialog from '#/components/Dialog' -import {InlineLink} from '#/components/Link' import {Divider} from '#/components/Divider' +import {InlineLinkText} from '#/components/Link' +import {Text} from '#/components/Typography' export {useDialogControl as useModerationDetailsDialogControl} from '#/components/Dialog' @@ -55,9 +54,9 @@ function ModerationDetailsDialogInner({ description = ( This user is included in the{' '} - + {list.name} - {' '} + {' '} list which you have blocked. ) @@ -84,9 +83,9 @@ function ModerationDetailsDialogInner({ description = ( This user is included in the{' '} - + {list.name} - {' '} + {' '} list which you have muted. ) @@ -127,12 +126,12 @@ function ModerationDetailsDialogInner({ {modcause.source.type === 'user' ? ( the author ) : ( - control.close()} style={a.text_md}> {desc.source} - + )} . diff --git a/src/screens/Login/ChooseAccountForm.tsx b/src/screens/Login/ChooseAccountForm.tsx index 15c06dbe..01eca187 100644 --- a/src/screens/Login/ChooseAccountForm.tsx +++ b/src/screens/Login/ChooseAccountForm.tsx @@ -58,11 +58,11 @@ export const ChooseAccountForm = ({ return ( Select account}> + titleText={Select account}> - + Sign in as... - + onSelectAccount()} diff --git a/src/screens/Login/ForgotPasswordForm.tsx b/src/screens/Login/ForgotPasswordForm.tsx index 580452e7..ec30bab4 100644 --- a/src/screens/Login/ForgotPasswordForm.tsx +++ b/src/screens/Login/ForgotPasswordForm.tsx @@ -83,11 +83,11 @@ export const ForgotPasswordForm = ({ return ( Reset password}> + titleText={Reset password}> - + Hosting provider - + - + Email address - + }) { @@ -21,9 +21,9 @@ export function FormContainer({ - {title && !gtMobile && ( + {titleText && !gtMobile && ( - {title} + {titleText} )} {children} diff --git a/src/screens/Login/LoginForm.tsx b/src/screens/Login/LoginForm.tsx index 6bf215ee..6b1340b9 100644 --- a/src/screens/Login/LoginForm.tsx +++ b/src/screens/Login/LoginForm.tsx @@ -128,11 +128,11 @@ export const LoginForm = ({ const isReady = !!serviceDescription && !!identifier && !!password return ( - Sign in}> + Sign in}> - + Hosting provider - + - + Account - + diff --git a/src/screens/Login/SetNewPasswordForm.tsx b/src/screens/Login/SetNewPasswordForm.tsx index e7b48865..88f7ec54 100644 --- a/src/screens/Login/SetNewPasswordForm.tsx +++ b/src/screens/Login/SetNewPasswordForm.tsx @@ -99,7 +99,7 @@ export const SetNewPasswordForm = ({ return ( Set new password}> + titleText={Set new password}> You will receive an email with a "reset code." Enter that code here, @@ -108,7 +108,7 @@ export const SetNewPasswordForm = ({ - Reset code + Reset code - New password + New password - + Discourage apps from showing my account to logged-out users - + {updateProfile.isPending && } @@ -545,9 +545,9 @@ function PwiOptOut() { - + Learn more about what is public on Bluesky. - + ) diff --git a/src/screens/Onboarding/Layout.tsx b/src/screens/Onboarding/Layout.tsx index 6337cee0..cfaf20ff 100644 --- a/src/screens/Onboarding/Layout.tsx +++ b/src/screens/Onboarding/Layout.tsx @@ -1,29 +1,27 @@ import React from 'react' import {View} from 'react-native' import {useSafeAreaInsets} from 'react-native-safe-area-context' -import {useLingui} from '@lingui/react' import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' -import {IS_DEV} from '#/env' import {isWeb} from '#/platform/detection' import {useOnboardingDispatch} from '#/state/shell' - -import { - useTheme, - atoms as a, - useBreakpoints, - web, - native, - flatten, - TextStyleProp, -} from '#/alf' -import {P, leading, Text} from '#/components/Typography' -import {ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft} from '#/components/icons/Chevron' -import {Button, ButtonIcon} from '#/components/Button' import {ScrollView} from '#/view/com/util/Views' -import {createPortalGroup} from '#/components/Portal' - import {Context} from '#/screens/Onboarding/state' +import { + atoms as a, + flatten, + native, + TextStyleProp, + useBreakpoints, + useTheme, + web, +} from '#/alf' +import {Button, ButtonIcon} from '#/components/Button' +import {ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft} from '#/components/icons/Chevron' +import {createPortalGroup} from '#/components/Portal' +import {leading, P, Text} from '#/components/Typography' +import {IS_DEV} from '#/env' const COL_WIDTH = 500 @@ -204,7 +202,7 @@ export function Layout({children}: React.PropsWithChildren<{}>) { ) } -export function Title({ +export function TitleText({ children, style, }: React.PropsWithChildren) { @@ -224,7 +222,7 @@ export function Title({ ) } -export function Description({ +export function DescriptionText({ children, style, }: React.PropsWithChildren) { diff --git a/src/screens/Onboarding/StepAlgoFeeds/index.tsx b/src/screens/Onboarding/StepAlgoFeeds/index.tsx index 35f525ef..4ba61696 100644 --- a/src/screens/Onboarding/StepAlgoFeeds/index.tsx +++ b/src/screens/Onboarding/StepAlgoFeeds/index.tsx @@ -6,9 +6,9 @@ import {useLingui} from '@lingui/react' import {useAnalytics} from '#/lib/analytics/analytics' import {logEvent} from '#/lib/statsig/statsig' import { - Description, + DescriptionText, OnboardingControls, - Title, + TitleText, } from '#/screens/Onboarding/Layout' import {Context} from '#/screens/Onboarding/state' import {FeedCard} from '#/screens/Onboarding/StepAlgoFeeds/FeedCard' @@ -105,15 +105,15 @@ export function StepAlgoFeeds() { - + <TitleText> <Trans>Choose your main feeds</Trans> - - + + Custom feeds built by the community bring you new experiences and help you find the content you love. - + - + <TitleText> <Trans>You're ready to go!</Trans> - - + + We hope you have a wonderful time. Remember, Bluesky is: - + diff --git a/src/screens/Onboarding/StepFollowingFeed.tsx b/src/screens/Onboarding/StepFollowingFeed.tsx index e886a089..a1c7299f 100644 --- a/src/screens/Onboarding/StepFollowingFeed.tsx +++ b/src/screens/Onboarding/StepFollowingFeed.tsx @@ -10,9 +10,9 @@ import { useSetFeedViewPreferencesMutation, } from 'state/queries/preferences' import { - Description, + DescriptionText, OnboardingControls, - Title, + TitleText, } from '#/screens/Onboarding/Layout' import {Context} from '#/screens/Onboarding/state' import {atoms as a} from '#/alf' @@ -58,12 +58,12 @@ export function StepFollowingFeed() { - + <TitleText> <Trans>Your default feed is "Following"</Trans> - - + + It shows posts from the people you follow as they happen. - + - + You can change these settings later. - + - This is a prompt - + This is a prompt + This is a generic prompt component. It accepts a title and a description, as well as two actions. - + Cancel {}}>Confirm diff --git a/src/view/screens/Storybook/Forms.tsx b/src/view/screens/Storybook/Forms.tsx index 2d5495d7..182eacfd 100644 --- a/src/view/screens/Storybook/Forms.tsx +++ b/src/view/screens/Storybook/Forms.tsx @@ -2,13 +2,13 @@ import React from 'react' import {View} from 'react-native' import {atoms as a} from '#/alf' -import {H1, H3} from '#/components/Typography' +import {Button} from '#/components/Button' +import {DateField, LabelText} from '#/components/forms/DateField' import * as TextField from '#/components/forms/TextField' -import {DateField, Label} from '#/components/forms/DateField' import * as Toggle from '#/components/forms/Toggle' import * as ToggleButton from '#/components/forms/ToggleButton' -import {Button} from '#/components/Button' import {Globe_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe' +import {H1, H3} from '#/components/Typography' export function Forms() { const [toggleGroupAValues, setToggleGroupAValues] = React.useState(['a']) @@ -42,7 +42,7 @@ export function Forms() { - Text field + Text field - @gmail.com + + @gmail.com + - Textarea + Textarea DateField - + Date - Uncontrolled toggle + Uncontrolled toggle - Click me + Click me - Click me + Click me - Click me + Click me - Click me + Click me - Click me + Click me @@ -128,23 +130,23 @@ export function Forms() { - Click me + Click me - Click me + Click me - Click me + Click me - Click me + Click me - Click me + Click me @@ -157,23 +159,23 @@ export function Forms() { - Click me + Click me - Click me + Click me - Click me + Click me - Click me + Click me - Click me + Click me diff --git a/src/view/screens/Storybook/Links.tsx b/src/view/screens/Storybook/Links.tsx index f9ecfba5..d35db79b 100644 --- a/src/view/screens/Storybook/Links.tsx +++ b/src/view/screens/Storybook/Links.tsx @@ -1,9 +1,9 @@ import React from 'react' import {View} from 'react-native' -import {useTheme, atoms as a} from '#/alf' +import {atoms as a, useTheme} from '#/alf' import {ButtonText} from '#/components/Button' -import {InlineLink, Link} from '#/components/Link' +import {InlineLinkText, Link} from '#/components/Link' import {H1, Text} from '#/components/Typography' export function Links() { @@ -13,20 +13,22 @@ export function Links() {

Links

- + https://google.com - - + + External with custom children (google.com) - - + Internal (bsky.social) - - + + Internal (bsky.app) - +