feat: support dark mode
This commit is contained in:
parent
4eba638a36
commit
010aa2f45e
18 changed files with 69 additions and 48 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -14,4 +14,8 @@ web-ext.config.ts
|
||||||
*.pem
|
*.pem
|
||||||
*.xpi
|
*.xpi
|
||||||
*.zip
|
*.zip
|
||||||
|
*.xml
|
||||||
|
*.gitignore
|
||||||
|
.idea
|
||||||
|
*.yaml
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ export default function App() {
|
||||||
const userInfoSetFinished = useRemeshQuery(userInfoDomain.query.UserInfoSetIsFinishedQuery())
|
const userInfoSetFinished = useRemeshQuery(userInfoDomain.query.UserInfoSetIsFinishedQuery())
|
||||||
const messageListLoadFinished = useRemeshQuery(messageListDomain.query.LoadIsFinishedQuery())
|
const messageListLoadFinished = useRemeshQuery(messageListDomain.query.LoadIsFinishedQuery())
|
||||||
const userInfoLoadFinished = useRemeshQuery(userInfoDomain.query.UserInfoLoadIsFinishedQuery())
|
const userInfoLoadFinished = useRemeshQuery(userInfoDomain.query.UserInfoLoadIsFinishedQuery())
|
||||||
|
const userInfo = useRemeshQuery(userInfoDomain.query.UserInfoQuery())
|
||||||
const notUserInfo = userInfoLoadFinished && !userInfoSetFinished
|
const notUserInfo = userInfoLoadFinished && !userInfoSetFinished
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -59,7 +59,7 @@ export default function App() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<AppMain>
|
<AppMain className={userInfo?.themeMode}>
|
||||||
<Header />
|
<Header />
|
||||||
<Main />
|
<Main />
|
||||||
<Footer />
|
<Footer />
|
||||||
|
@ -72,9 +72,10 @@ export default function App() {
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
<Toaster richColors offset="70px" visibleToasts={1} position="top-center"></Toaster>
|
<Toaster richColors offset="70px" visibleToasts={1} position="top-center"></Toaster>
|
||||||
</AppMain>
|
</AppMain>
|
||||||
<AppButton></AppButton>
|
|
||||||
|
|
||||||
<DanmakuContainer ref={danmakuContainerRef} />
|
<AppButton className={userInfo?.themeMode}></AppButton>
|
||||||
|
|
||||||
|
<DanmakuContainer className={userInfo?.themeMode} ref={danmakuContainerRef} />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ const EmojiButton: FC<EmojiButtonProps> = ({ onSelect }) => {
|
||||||
return (
|
return (
|
||||||
<Popover open={open} onOpenChange={setOpen}>
|
<Popover open={open} onOpenChange={setOpen}>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
<Button variant="ghost" size="icon">
|
<Button variant="ghost" size="icon" className="dark:text-white">
|
||||||
<SmileIcon size={20} />
|
<SmileIcon size={20} />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
|
|
|
@ -33,7 +33,7 @@ const LikeButton: FC<LikeButtonProps> & { Icon: FC<LikeButtonIconProps> } = ({
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className={cn(
|
className={cn(
|
||||||
'grid items-center overflow-hidden rounded-full leading-none transition-all select-none',
|
'grid items-center overflow-hidden rounded-full leading-none transition-all select-none dark:bg-slate-50',
|
||||||
checked ? 'text-orange-500' : 'text-slate-500',
|
checked ? 'text-orange-500' : 'text-slate-500',
|
||||||
count ? 'grid-cols-[auto_1fr] gap-x-1' : 'grid-cols-[auto_0fr] gap-x-0'
|
count ? 'grid-cols-[auto_1fr] gap-x-1' : 'grid-cols-[auto_0fr] gap-x-0'
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -52,7 +52,7 @@ const MessageInput = forwardRef<HTMLTextAreaElement, MessageInputProps>(
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
autoFocus={autoFocus}
|
autoFocus={autoFocus}
|
||||||
maxLength={maxLength}
|
maxLength={maxLength}
|
||||||
className="box-border resize-none whitespace-pre-wrap break-words border-none bg-gray-50 pb-5 [field-sizing:content] focus:ring-0 focus:ring-offset-0"
|
className="box-border resize-none whitespace-pre-wrap break-words border-none bg-gray-50 pb-5 [field-sizing:content] focus:ring-0 focus:ring-offset-0 dark:bg-slate-800 dark:text-white"
|
||||||
rows={2}
|
rows={2}
|
||||||
value={value}
|
value={value}
|
||||||
onCompositionStart={onCompositionStart}
|
onCompositionStart={onCompositionStart}
|
||||||
|
@ -63,7 +63,7 @@ const MessageInput = forwardRef<HTMLTextAreaElement, MessageInputProps>(
|
||||||
/>
|
/>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
)}
|
)}
|
||||||
<div className="absolute bottom-1 right-3 rounded-lg text-xs text-slate-400">
|
<div className="absolute bottom-1 right-3 rounded-lg text-xs text-slate-400 dark:text-slate-50">
|
||||||
{value?.length ?? 0}/{maxLength}
|
{value?.length ?? 0}/{maxLength}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -28,7 +28,10 @@ const MessageItem: FC<MessageItemProps> = (props) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-index={props.index}
|
data-index={props.index}
|
||||||
className={cn('box-border grid grid-cols-[auto_1fr] gap-x-2 px-4 first:pt-4 last:pb-4', props.className)}
|
className={cn(
|
||||||
|
'box-border grid grid-cols-[auto_1fr] gap-x-2 px-4 first:pt-4 last:pb-4 dark:text-slate-50',
|
||||||
|
props.className
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<Avatar>
|
<Avatar>
|
||||||
<AvatarImage src={props.data.userAvatar} className="size-full" alt="avatar" />
|
<AvatarImage src={props.data.userAvatar} className="size-full" alt="avatar" />
|
||||||
|
@ -36,14 +39,14 @@ const MessageItem: FC<MessageItemProps> = (props) => {
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<div className="overflow-hidden">
|
<div className="overflow-hidden">
|
||||||
<div className="grid grid-cols-[1fr_auto] items-center gap-x-2 leading-none">
|
<div className="grid grid-cols-[1fr_auto] items-center gap-x-2 leading-none">
|
||||||
<div className="truncate text-sm font-semibold text-slate-600">{props.data.username}</div>
|
<div className="truncate text-sm font-semibold text-slate-600 dark:text-slate-50">{props.data.username}</div>
|
||||||
<FormatDate className="text-xs text-slate-400" date={props.data.date}></FormatDate>
|
<FormatDate className="text-xs text-slate-400 dark:text-slate-100" date={props.data.date}></FormatDate>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="pb-2">
|
<div className="pb-2">
|
||||||
<Markdown>{props.data.body}</Markdown>
|
<Markdown>{props.data.body}</Markdown>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-flow-col justify-end gap-x-2 leading-none">
|
<div className="grid grid-flow-col justify-end gap-x-2 leading-none dark:text-slate-600">
|
||||||
<LikeButton
|
<LikeButton
|
||||||
checked={props.like}
|
checked={props.like}
|
||||||
onChange={(checked) => handleLikeChange(checked)}
|
onChange={(checked) => handleLikeChange(checked)}
|
||||||
|
|
|
@ -12,8 +12,11 @@ export interface PromptItemProps {
|
||||||
|
|
||||||
const PromptItem: FC<PromptItemProps> = ({ data, className }) => {
|
const PromptItem: FC<PromptItemProps> = ({ data, className }) => {
|
||||||
return (
|
return (
|
||||||
<div className={cn('flex justify-center py-1 px-4', className)}>
|
<div className={cn('flex justify-center py-1 px-4 ', className)}>
|
||||||
<Badge variant="secondary" className="gap-x-2 rounded-full px-2 font-medium text-slate-400">
|
<Badge
|
||||||
|
variant="secondary"
|
||||||
|
className="gap-x-2 rounded-full px-2 font-medium text-slate-400 dark:bg-slate-600 dark:text-slate-50"
|
||||||
|
>
|
||||||
<Avatar className="size-4">
|
<Avatar className="size-4">
|
||||||
<AvatarImage src={data.userAvatar} className="size-full" alt="avatar" />
|
<AvatarImage src={data.userAvatar} className="size-full" alt="avatar" />
|
||||||
<AvatarFallback>{data.username.at(0)}</AvatarFallback>
|
<AvatarFallback>{data.username.at(0)}</AvatarFallback>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { type FC, useState, type MouseEvent, useRef, useEffect, useLayoutEffect } from 'react'
|
import { type FC, useState, type MouseEvent, useRef, useEffect, useLayoutEffect, ReactNode } from 'react'
|
||||||
import { SettingsIcon, MoonIcon, SunIcon, HandIcon } from 'lucide-react'
|
import { SettingsIcon, MoonIcon, SunIcon, HandIcon } from 'lucide-react'
|
||||||
import { motion, AnimatePresence } from 'framer-motion'
|
import { motion, AnimatePresence } from 'framer-motion'
|
||||||
|
|
||||||
|
@ -22,7 +22,11 @@ import { messenger } from '@/messenger'
|
||||||
import useDarg from '@/hooks/useDarg'
|
import useDarg from '@/hooks/useDarg'
|
||||||
import { useWindowSize } from 'react-use'
|
import { useWindowSize } from 'react-use'
|
||||||
|
|
||||||
const AppButton: FC = () => {
|
export interface AppButtonProps {
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const AppButton: FC<AppButtonProps> = ({ className }) => {
|
||||||
const send = useRemeshSend()
|
const send = useRemeshSend()
|
||||||
const appStatusDomain = useRemeshDomain(AppStatusDomain())
|
const appStatusDomain = useRemeshDomain(AppStatusDomain())
|
||||||
const appOpenStatus = useRemeshQuery(appStatusDomain.query.OpenQuery())
|
const appOpenStatus = useRemeshQuery(appStatusDomain.query.OpenQuery())
|
||||||
|
@ -86,7 +90,7 @@ const AppButton: FC = () => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={menuRef}
|
ref={menuRef}
|
||||||
className="fixed bottom-5 right-5 z-infinity grid w-min select-none justify-center gap-y-3"
|
className={`fixed bottom-5 right-5 z-infinity grid w-min select-none justify-center gap-y-3 ${className}`}
|
||||||
style={{
|
style={{
|
||||||
left: `calc(${appPosition.x}px)`,
|
left: `calc(${appPosition.x}px)`,
|
||||||
bottom: `calc(100vh - ${appPosition.y}px)`,
|
bottom: `calc(100vh - ${appPosition.y}px)`,
|
||||||
|
|
|
@ -8,9 +8,10 @@ import { useWindowSize } from 'react-use'
|
||||||
|
|
||||||
export interface AppMainProps {
|
export interface AppMainProps {
|
||||||
children?: ReactNode
|
children?: ReactNode
|
||||||
|
className?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const AppMain: FC<AppMainProps> = ({ children }) => {
|
const AppMain: FC<AppMainProps> = ({ children, className }) => {
|
||||||
const appStatusDomain = useRemeshDomain(AppStatusDomain())
|
const appStatusDomain = useRemeshDomain(AppStatusDomain())
|
||||||
const appOpenStatus = useRemeshQuery(appStatusDomain.query.OpenQuery())
|
const appOpenStatus = useRemeshQuery(appStatusDomain.query.OpenQuery())
|
||||||
const { x, y } = useRemeshQuery(appStatusDomain.query.PositionQuery())
|
const { x, y } = useRemeshQuery(appStatusDomain.query.PositionQuery())
|
||||||
|
@ -44,7 +45,7 @@ const AppMain: FC<AppMainProps> = ({ children }) => {
|
||||||
bottom: `calc(100vh - ${y}px + 22px)`
|
bottom: `calc(100vh - ${y}px + 22px)`
|
||||||
}}
|
}}
|
||||||
className={cn(
|
className={cn(
|
||||||
'fixed inset-y-10 right-10 z-infinity mb-0 mt-auto box-border grid max-h-[min(calc(100vh_-60px),_1000px)] grid-flow-col grid-rows-[auto_1fr_auto] rounded-xl bg-slate-50 font-sans shadow-2xl',
|
`fixed inset-y-10 right-10 z-infinity dark:bg-slate-800 mb-0 mt-auto box-border grid max-h-[min(calc(100vh_-60px),_1000px)] grid-flow-col grid-rows-[auto_1fr_auto] rounded-xl bg-slate-50 font-sans shadow-2xl ${className}`,
|
||||||
{ 'transition-transform': isAnimationComplete }
|
{ 'transition-transform': isAnimationComplete }
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
@ -52,7 +53,7 @@ const AppMain: FC<AppMainProps> = ({ children }) => {
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'absolute inset-y-3 z-20 w-1 cursor-ew-resize rounded-xl bg-slate-100 opacity-0 shadow transition-opacity duration-200 ease-in hover:opacity-100',
|
'absolute inset-y-3 z-20 w-1 dark:bg-slate-800 cursor-ew-resize rounded-xl bg-slate-100 opacity-0 shadow transition-opacity duration-200 ease-in hover:opacity-100',
|
||||||
isOnRightSide ? '-left-0.5' : '-right-0.5'
|
isOnRightSide ? '-left-0.5' : '-right-0.5'
|
||||||
)}
|
)}
|
||||||
></div>
|
></div>
|
||||||
|
|
|
@ -34,7 +34,7 @@ const Footer: FC = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative z-10 grid gap-y-2 px-4 pb-4 pt-2 before:pointer-events-none before:absolute before:inset-x-4 before:-top-2 before:h-2 before:bg-gradient-to-t before:from-slate-50 before:from-30% before:to-transparent">
|
<div className="relative z-10 grid gap-y-2 px-4 pb-4 pt-2 before:pointer-events-none before:absolute before:inset-x-4 before:-top-2 before:h-2 before:bg-gradient-to-t before:from-slate-50 before:from-30% before:to-transparent dark:bg-slate-950 dark:text-slate-50">
|
||||||
<MessageInput
|
<MessageInput
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
value={message}
|
value={message}
|
||||||
|
@ -49,9 +49,9 @@ const Footer: FC = () => {
|
||||||
{/* <Button variant="ghost" size="icon">
|
{/* <Button variant="ghost" size="icon">
|
||||||
<ImageIcon size={20} />
|
<ImageIcon size={20} />
|
||||||
</Button> */}
|
</Button> */}
|
||||||
<Button className="ml-auto" size="sm" onClick={handleSend}>
|
<Button className="ml-auto dark:bg-white" size="sm" onClick={handleSend}>
|
||||||
<span className="mr-2">Send</span>
|
<span className="mr-2 text-slate-500">Send</span>
|
||||||
<CornerDownLeftIcon className="text-slate-400" size={12}></CornerDownLeftIcon>
|
<CornerDownLeftIcon className="text-slate-500" size={12}></CornerDownLeftIcon>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -15,8 +15,8 @@ const Header: FC = () => {
|
||||||
const onlineCount = userList.length
|
const onlineCount = userList.length
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="z-10 grid h-12 grid-flow-col grid-cols-[theme('spacing.20')_auto_theme('spacing.20')] items-center justify-between rounded-t-xl bg-white px-4 backdrop-blur-lg">
|
<div className="z-10 grid h-12 grid-flow-col grid-cols-[theme('spacing.20')_auto_theme('spacing.20')] items-center justify-between rounded-t-xl bg-white px-4 backdrop-blur-lg dark:bg-slate-950">
|
||||||
<Avatar className="size-8">
|
<Avatar className="size-8 dark:text-slate-50">
|
||||||
<AvatarImage src={siteInfo.icon} alt="favicon" />
|
<AvatarImage src={siteInfo.icon} alt="favicon" />
|
||||||
<AvatarFallback>
|
<AvatarFallback>
|
||||||
<Globe2Icon size="100%" className="text-gray-400" />
|
<Globe2Icon size="100%" className="text-gray-400" />
|
||||||
|
@ -25,7 +25,7 @@ const Header: FC = () => {
|
||||||
<HoverCard>
|
<HoverCard>
|
||||||
<HoverCardTrigger asChild>
|
<HoverCardTrigger asChild>
|
||||||
<Button className="overflow-hidden p-2" variant="link">
|
<Button className="overflow-hidden p-2" variant="link">
|
||||||
<span className="truncate text-lg font-semibold text-slate-600">
|
<span className="truncate text-lg font-semibold text-slate-600 dark:text-slate-50">
|
||||||
{siteInfo.hostname.replace(/^www\./i, '')}
|
{siteInfo.hostname.replace(/^www\./i, '')}
|
||||||
</span>
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -65,7 +65,7 @@ const Header: FC = () => {
|
||||||
)}
|
)}
|
||||||
></span>
|
></span>
|
||||||
</span>
|
</span>
|
||||||
<span>ONLINE {onlineCount > 99 ? '99+' : onlineCount}</span>
|
<span className="dark:text-slate-50">ONLINE {onlineCount > 99 ? '99+' : onlineCount}</span>
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
</HoverCardTrigger>
|
</HoverCardTrigger>
|
||||||
|
|
|
@ -4,17 +4,23 @@ import ProfileForm from './components/ProfileForm'
|
||||||
import BadgeList from './components/BadgeList'
|
import BadgeList from './components/BadgeList'
|
||||||
import Layout from './components/Layout'
|
import Layout from './components/Layout'
|
||||||
import VersionLink from './components/VersionLink'
|
import VersionLink from './components/VersionLink'
|
||||||
|
import { useRemeshDomain, useRemeshQuery } from 'remesh-react'
|
||||||
|
import UserInfoDomain from '@/domain/UserInfo'
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
const userInfoDomain = useRemeshDomain(UserInfoDomain())
|
||||||
|
const userInfo = useRemeshQuery(userInfoDomain.query.UserInfoQuery())
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<div className={userInfo?.themeMode}>
|
||||||
<VersionLink></VersionLink>
|
<Layout>
|
||||||
<Main>
|
<VersionLink></VersionLink>
|
||||||
<ProfileForm></ProfileForm>
|
<Main>
|
||||||
<Toaster richColors position="top-center" />
|
<ProfileForm></ProfileForm>
|
||||||
</Main>
|
<Toaster richColors position="top-center" />
|
||||||
<BadgeList></BadgeList>
|
</Main>
|
||||||
</Layout>
|
<BadgeList></BadgeList>
|
||||||
|
</Layout>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ export interface LayoutProps {
|
||||||
|
|
||||||
const Layout: FC<LayoutProps> = ({ children }) => {
|
const Layout: FC<LayoutProps> = ({ children }) => {
|
||||||
return (
|
return (
|
||||||
<div className="h-screen w-screen bg-gray-50 bg-[url(@/assets/images/texture.png)] font-sans">
|
<div className={`h-screen w-screen bg-gray-50 bg-[url(@/assets/images/texture.png)] font-sans dark:bg-slate-950`}>
|
||||||
<div className="fixed left-0 top-0 h-full w-screen overflow-hidden">
|
<div className="fixed left-0 top-0 h-full w-screen overflow-hidden">
|
||||||
<Meteors number={30} />
|
<Meteors number={30} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,7 +7,7 @@ export interface MainProps {
|
||||||
const Main: FC<MainProps> = ({ children }) => {
|
const Main: FC<MainProps> = ({ children }) => {
|
||||||
return (
|
return (
|
||||||
<main className="grid min-h-screen min-w-screen items-center justify-center">
|
<main className="grid min-h-screen min-w-screen items-center justify-center">
|
||||||
<div className="relative rounded-xl bg-slate-50 shadow-lg">{children}</div>
|
<div className="relative rounded-xl bg-slate-50 shadow-lg">{children}</div>
|
||||||
</main>
|
</main>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { useForm } from 'react-hook-form'
|
||||||
import { valibotResolver } from '@hookform/resolvers/valibot'
|
import { valibotResolver } from '@hookform/resolvers/valibot'
|
||||||
import { useRemeshDomain, useRemeshQuery, useRemeshSend } from 'remesh-react'
|
import { useRemeshDomain, useRemeshQuery, useRemeshSend } from 'remesh-react'
|
||||||
import { nanoid } from 'nanoid'
|
import { nanoid } from 'nanoid'
|
||||||
import { useEffect } from 'react'
|
import { ReactNode, useEffect, type FC } from 'react'
|
||||||
import AvatarSelect from './AvatarSelect'
|
import AvatarSelect from './AvatarSelect'
|
||||||
import { Button } from '@/components/ui/Button'
|
import { Button } from '@/components/ui/Button'
|
||||||
import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/Form'
|
import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/Form'
|
||||||
|
@ -56,8 +56,7 @@ const formSchema = v.object({
|
||||||
danmakuEnabled: v.boolean(),
|
danmakuEnabled: v.boolean(),
|
||||||
notificationEnabled: v.boolean()
|
notificationEnabled: v.boolean()
|
||||||
})
|
})
|
||||||
|
const ProfileForm: FC = () => {
|
||||||
const ProfileForm = () => {
|
|
||||||
const send = useRemeshSend()
|
const send = useRemeshSend()
|
||||||
const toast = ToastImpl.value
|
const toast = ToastImpl.value
|
||||||
|
|
||||||
|
@ -97,13 +96,13 @@ const ProfileForm = () => {
|
||||||
<form
|
<form
|
||||||
onSubmit={form.handleSubmit(handleSubmit)}
|
onSubmit={form.handleSubmit(handleSubmit)}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
className="relative w-[450px] space-y-8 p-14 pt-20"
|
className="relative w-[450px] space-y-8 p-14 pt-20 dark:bg-slate-900 dark:text-slate-50"
|
||||||
>
|
>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="avatar"
|
name="avatar"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="absolute inset-x-1 top-0 mx-auto grid w-fit -translate-y-1/3 justify-items-center">
|
<FormItem className="absolute inset-x-1 top-0 mx-auto grid w-fit -translate-y-1/3 justify-items-center">
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<div className="flex flex-col items-center gap-2">
|
<div className="flex flex-col items-center gap-2">
|
||||||
<BlurFade key={form.getValues().avatar} duration={0.1}>
|
<BlurFade key={form.getValues().avatar} duration={0.1}>
|
||||||
|
@ -135,7 +134,7 @@ const ProfileForm = () => {
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="name"
|
name="name"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem className="dark:text-slate-50">
|
||||||
<FormLabel>Username</FormLabel>
|
<FormLabel>Username</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="Please enter your username" {...field} />
|
<Input placeholder="Please enter your username" {...field} />
|
||||||
|
@ -227,7 +226,7 @@ const ProfileForm = () => {
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Button className="w-full" type="submit">
|
<Button className="w-full dark:bg-slate-800 dark:text-slate-50" type="submit">
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -113,7 +113,7 @@ const Markdown: FC<MarkdownProps> = ({ children = '', className }) => {
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
remarkPlugins={[remarkGfm, remarkBreaks]}
|
remarkPlugins={[remarkGfm, remarkBreaks]}
|
||||||
className={cn(className, 'prose prose-sm prose-slate break-words')}
|
className={cn(className, 'prose prose-sm prose-slate break-words dark:text-slate-400')}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</ReactMarkdown>
|
</ReactMarkdown>
|
||||||
|
|
|
@ -29,7 +29,7 @@ const AvatarFallback = React.forwardRef<
|
||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<AvatarPrimitive.Fallback
|
<AvatarPrimitive.Fallback
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn('flex h-full w-full items-center justify-center rounded-full bg-muted', className)}
|
className={cn('flex h-full w-full items-center justify-center rounded-full bg-muted dark:text-slate-400', className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
|
|
|
@ -7,7 +7,7 @@ const ScrollArea = React.forwardRef<
|
||||||
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
|
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
|
||||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> & { scrollLock?: boolean }
|
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> & { scrollLock?: boolean }
|
||||||
>(({ className, children, scrollLock = true, ...props }, ref) => (
|
>(({ className, children, scrollLock = true, ...props }, ref) => (
|
||||||
<ScrollAreaPrimitive.Root className={cn('relative grid grid-rows-[1fr] overflow-hidden', className)} {...props}>
|
<ScrollAreaPrimitive.Root className={cn('relative grid grid-rows-[1fr] overflow-hidden dark:bg-slate-900 z-50', className)} {...props}>
|
||||||
<ScrollAreaPrimitive.Viewport
|
<ScrollAreaPrimitive.Viewport
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn('size-full rounded-[inherit]', scrollLock ? 'overscroll-none' : 'overscroll-auto')}
|
className={cn('size-full rounded-[inherit]', scrollLock ? 'overscroll-none' : 'overscroll-auto')}
|
||||||
|
|
Loading…
Reference in a new issue