perf: optimize theme styles

This commit is contained in:
molvqingtai 2024-10-28 09:03:57 +08:00
parent bcdd435e45
commit 2d051fedd7
22 changed files with 105 additions and 72 deletions

View file

@ -74,7 +74,6 @@
"idb-keyval": "^6.2.1", "idb-keyval": "^6.2.1",
"lucide-react": "^0.453.0", "lucide-react": "^0.453.0",
"nanoid": "^5.0.7", "nanoid": "^5.0.7",
"next-themes": "^0.3.0",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-hook-form": "^7.53.0", "react-hook-form": "^7.53.0",

View file

@ -98,9 +98,6 @@ importers:
nanoid: nanoid:
specifier: ^5.0.7 specifier: ^5.0.7
version: 5.0.7 version: 5.0.7
next-themes:
specifier: ^0.3.0
version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: react:
specifier: ^18.3.1 specifier: ^18.3.1
version: 18.3.1 version: 18.3.1
@ -4949,12 +4946,6 @@ packages:
resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==}
engines: {node: '>= 0.4.0'} engines: {node: '>= 0.4.0'}
next-themes@0.3.0:
resolution: {integrity: sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w==}
peerDependencies:
react: ^16.8 || ^17 || ^18
react-dom: ^16.8 || ^17 || ^18
nice-try@1.0.5: nice-try@1.0.5:
resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
@ -12482,11 +12473,6 @@ snapshots:
netmask@2.0.2: {} netmask@2.0.2: {}
next-themes@0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
nice-try@1.0.5: {} nice-try@1.0.5: {}
no-case@3.0.4: no-case@3.0.4:

View file

@ -15,6 +15,7 @@ import { AnimatePresence, motion } from 'framer-motion'
import DanmakuContainer from './components/DanmakuContainer' import DanmakuContainer from './components/DanmakuContainer'
import DanmakuDomain from '@/domain/Danmaku' import DanmakuDomain from '@/domain/Danmaku'
import AppStatusDomain from '@/domain/AppStatus' import AppStatusDomain from '@/domain/AppStatus'
import { cn } from '@/utils'
/** /**
* Fix requestAnimationFrame error in jest * Fix requestAnimationFrame error in jest
@ -63,24 +64,29 @@ export default function App() {
return ( return (
appStatusLoadIsFinished && ( appStatusLoadIsFinished && (
<> <div id="app" className={cn('contents', userInfo?.themeMode)}>
<AppMain className={userInfo?.themeMode}> <AppMain>
<Header /> <Header />
<Main /> <Main />
<Footer /> <Footer />
<AnimatePresence> <AnimatePresence>
{notUserInfo && ( {notUserInfo && (
<motion.div initial={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.3 }}> <motion.div
className="contents"
initial={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.3 }}
>
<Setup></Setup> <Setup></Setup>
</motion.div> </motion.div>
)} )}
</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 className={userInfo?.themeMode}></AppButton> <AppButton></AppButton>
<DanmakuContainer ref={danmakuContainerRef} className={userInfo?.themeMode} /> <DanmakuContainer ref={danmakuContainerRef} />
</> </div>
) )
) )
} }

View file

@ -10,7 +10,7 @@ export interface EmojiButtonProps {
onSelect?: (value: string) => void onSelect?: (value: string) => void
} }
const emojiGroups = chunk([...EMOJI_LIST], 8) const emojiGroups = chunk([...EMOJI_LIST], 6)
// BUG: https://github.com/radix-ui/primitives/pull/2433 // BUG: https://github.com/radix-ui/primitives/pull/2433
// BUG https://github.com/radix-ui/primitives/issues/1666 // BUG https://github.com/radix-ui/primitives/issues/1666
@ -34,16 +34,19 @@ const EmojiButton: FC<EmojiButtonProps> = ({ onSelect }) => {
<SmileIcon size={20} /> <SmileIcon size={20} />
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent className="z-infinity w-72 rounded-xl px-0" onCloseAutoFocus={handleCloseAutoFocus}> <PopoverContent
<ScrollArea className="size-72 px-3"> className="z-infinity w-64 overflow-hidden rounded-xl p-0 dark:bg-slate-900"
onCloseAutoFocus={handleCloseAutoFocus}
>
<ScrollArea className="size-64 p-1">
{emojiGroups.map((group, index) => { {emojiGroups.map((group, index) => {
return ( return (
<div key={index} className="grid grid-cols-8"> <div key={index} className="grid grid-cols-6">
{group.map((emoji, index) => ( {group.map((emoji, index) => (
<Button <Button
key={index} key={index}
size="sm" size="icon"
className="text-base" className="text-xl"
variant="ghost" variant="ghost"
onClick={() => handleSelect(emoji)} onClick={() => handleSelect(emoji)}
> >

View file

@ -33,8 +33,8 @@ 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 dark:bg-slate-200', 'grid items-center overflow-hidden rounded-full leading-none transition-all select-none dark:bg-slate-600',
checked ? 'text-orange-500' : 'text-slate-500', checked ? 'text-orange-500' : 'text-slate-500 dark:text-slate-100',
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'
)} )}
size="xs" size="xs"

View file

@ -12,7 +12,7 @@ const MessageList: FC<MessageListProps> = ({ children }) => {
const [scrollParentRef, setScrollParentRef] = useState<HTMLDivElement | null>(null) const [scrollParentRef, setScrollParentRef] = useState<HTMLDivElement | null>(null)
return ( return (
<ScrollArea ref={setScrollParentRef}> <ScrollArea ref={setScrollParentRef} className="dark:bg-slate-900">
<Virtuoso <Virtuoso
defaultItemHeight={108} defaultItemHeight={108}
followOutput={(isAtBottom: boolean) => (isAtBottom ? 'smooth' : 'auto')} followOutput={(isAtBottom: boolean) => (isAtBottom ? 'smooth' : 'auto')}

View file

@ -13,10 +13,7 @@ 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 <Badge variant="secondary" className="gap-x-2 rounded-full px-2 font-medium text-slate-400 dark:bg-slate-800">
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>

View file

@ -2,7 +2,7 @@ import React from 'react'
import { createRoot } from 'react-dom/client' import { createRoot } from 'react-dom/client'
import { Remesh } from 'remesh' import { Remesh } from 'remesh'
import { RemeshRoot, RemeshScope } from 'remesh-react' import { RemeshRoot, RemeshScope } from 'remesh-react'
import { RemeshLogger } from 'remesh-logger' // import { RemeshLogger } from 'remesh-logger'
import { defineContentScript } from 'wxt/sandbox' import { defineContentScript } from 'wxt/sandbox'
import { createShadowRootUi } from 'wxt/client' import { createShadowRootUi } from 'wxt/client'
@ -15,8 +15,8 @@ import { ToastImpl } from '@/domain/impls/Toast'
import { PeerRoomImpl } from '@/domain/impls/PeerRoom2' import { PeerRoomImpl } from '@/domain/impls/PeerRoom2'
import '@/assets/styles/tailwind.css' import '@/assets/styles/tailwind.css'
import '@/assets/styles/sonner.css' import '@/assets/styles/sonner.css'
import { createElement } from '@/utils'
import NotificationDomain from '@/domain/Notification' import NotificationDomain from '@/domain/Notification'
import { createElement } from '@/utils'
export default defineContentScript({ export default defineContentScript({
cssInjectionMode: 'ui', cssInjectionMode: 'ui',
@ -24,6 +24,13 @@ export default defineContentScript({
matches: ['https://*/*'], matches: ['https://*/*'],
excludeMatches: ['*://localhost/*', '*://127.0.0.1/*', '*://*.csdn.net/*', '*://*.csdn.com/*'], excludeMatches: ['*://localhost/*', '*://127.0.0.1/*', '*://*.csdn.net/*', '*://*.csdn.com/*'],
async main(ctx) { async main(ctx) {
window.CSS.registerProperty({
name: '--shimmer-angle',
syntax: '<angle>',
inherits: false,
initialValue: '0deg'
})
const store = Remesh.store({ const store = Remesh.store({
externs: [ externs: [
LocalStorageImpl, LocalStorageImpl,
@ -45,9 +52,8 @@ export default defineContentScript({
mode: 'open', mode: 'open',
isolateEvents: ['keyup', 'keydown', 'keypress'], isolateEvents: ['keyup', 'keydown', 'keypress'],
onMount: (container) => { onMount: (container) => {
const app = createElement('<div id="app"></div>') const app = createElement('<div id="root"></div>')
container.append(app) container.append(app)
const root = createRoot(app) const root = createRoot(app)
root.render( root.render(
<React.StrictMode> <React.StrictMode>

View file

@ -1,4 +1,4 @@
import { type FC, useState, type MouseEvent, useLayoutEffect, useEffect } from 'react' import { type FC, useState, type MouseEvent, useEffect } 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'
@ -19,6 +19,7 @@ import AppStatusDomain from '@/domain/AppStatus'
import { getDay } from 'date-fns' import { getDay } from 'date-fns'
import { messenger } from '@/messenger' import { messenger } from '@/messenger'
import useDarg from '@/hooks/useDarg' import useDarg from '@/hooks/useDarg'
import useWindowResize from '@/hooks/useWindowResize'
export interface AppButtonProps { export interface AppButtonProps {
className?: string className?: string
@ -47,21 +48,17 @@ const AppButton: FC<AppButtonProps> = ({ className }) => {
} = useDarg({ } = useDarg({
initX: appPosition.x, initX: appPosition.x,
initY: appPosition.y, initY: appPosition.y,
minX: 44, minX: 50,
maxX: window.innerWidth - 44, maxX: window.innerWidth - 50,
maxY: window.innerHeight - 22, maxY: window.innerHeight - 22,
minY: window.innerHeight / 2 minY: window.innerHeight / 2
}) })
useEffect(() => { useWindowResize(({ width, height }) => {
const handler = () => { send(appStatusDomain.command.UpdatePositionCommand({ x: width - 50, y: height - 22 }))
send(appStatusDomain.command.UpdatePositionCommand({ x: window.innerWidth - 44, y: window.innerHeight - 22 })) })
}
window.addEventListener('resize', handler)
return () => window.removeEventListener('resize', handler)
}, [])
useLayoutEffect(() => { useEffect(() => {
send(appStatusDomain.command.UpdatePositionCommand({ x, y })) send(appStatusDomain.command.UpdatePositionCommand({ x, y }))
}, [x, y]) }, [x, y])
@ -116,7 +113,7 @@ const AppButton: FC<AppButtonProps> = ({ className }) => {
className={cn( className={cn(
'absolute grid grid-rows-[repeat(2,minmax(0,2.5rem))] w-full justify-center items-center transition-all duration-500', 'absolute grid grid-rows-[repeat(2,minmax(0,2.5rem))] w-full justify-center items-center transition-all duration-500',
isDarkMode ? 'top-0' : '-top-10', isDarkMode ? 'top-0' : '-top-10',
isDarkMode ? 'bg-slate-800 text-white' : 'bg-white text-orange-400' isDarkMode ? 'bg-slate-950 text-white' : 'bg-white text-orange-400'
)} )}
> >
<MoonIcon size={20} /> <MoonIcon size={20} />
@ -136,7 +133,7 @@ const AppButton: FC<AppButtonProps> = ({ className }) => {
<Button <Button
onClick={handleToggleApp} onClick={handleToggleApp}
onContextMenu={handleToggleMenu} onContextMenu={handleToggleMenu}
className="relative z-20 size-11 rounded-full p-0 text-xs shadow-lg shadow-slate-500/50" className="relative z-20 size-11 rounded-full p-0 text-xs shadow-lg shadow-slate-500/50 after:absolute after:-inset-0.5 after:z-10 after:animate-[shimmer_2s_linear_infinite] after:rounded-full after:bg-[conic-gradient(from_var(--shimmer-angle),theme(colors.slate.500)_0%,theme(colors.white)_10%,theme(colors.slate.500)_20%)]"
> >
<AnimatePresence> <AnimatePresence>
{hasUnreadQuery && ( {hasUnreadQuery && (
@ -145,7 +142,7 @@ const AppButton: FC<AppButtonProps> = ({ className }) => {
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
exit={{ opacity: 0 }} exit={{ opacity: 0 }}
transition={{ duration: 0.1 }} transition={{ duration: 0.1 }}
className="absolute -right-1 -top-1 flex size-5 items-center justify-center" className="absolute -right-1 -top-1 z-30 flex size-5 items-center justify-center"
> >
<span <span
className={cn('absolute inline-flex size-full animate-ping rounded-full opacity-75', 'bg-orange-400')} className={cn('absolute inline-flex size-full animate-ping rounded-full opacity-75', 'bg-orange-400')}
@ -154,7 +151,8 @@ const AppButton: FC<AppButtonProps> = ({ className }) => {
</motion.div> </motion.div>
)} )}
</AnimatePresence> </AnimatePresence>
<DayLogo className="max-h-full max-w-full"></DayLogo>
<DayLogo className="relative z-20 max-h-full max-w-full overflow-hidden"></DayLogo>
</Button> </Button>
</div> </div>
) )

View file

@ -4,7 +4,7 @@ import { motion, AnimatePresence } from 'framer-motion'
import AppStatusDomain from '@/domain/AppStatus' import AppStatusDomain from '@/domain/AppStatus'
import { useRemeshDomain, useRemeshQuery } from 'remesh-react' import { useRemeshDomain, useRemeshQuery } from 'remesh-react'
import { cn } from '@/utils' import { cn } from '@/utils'
import { useWindowSize } from 'react-use' import useWindowResize from '@/hooks/useWindowResize'
export interface AppMainProps { export interface AppMainProps {
children?: ReactNode children?: ReactNode
@ -16,9 +16,9 @@ const AppMain: FC<AppMainProps> = ({ children, className }) => {
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())
const { width } = useWindowSize() const { width } = useWindowResize()
const isOnRightSide = x >= width / 2 + 44 const isOnRightSide = x >= width / 2 + 50
const { size, setRef } = useResizable({ const { size, setRef } = useResizable({
initSize: Math.max(375, width / 6), initSize: Math.max(375, width / 6),
@ -54,7 +54,7 @@ const AppMain: FC<AppMainProps> = ({ children, className }) => {
<div <div
ref={setRef} ref={setRef}
className={cn( className={cn(
'absolute inset-y-3 z-infinity w-1 dark:bg-slate-500 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-infinity w-1 dark:bg-slate-600 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>

View file

@ -15,7 +15,7 @@ import useTriggerAway from '@/hooks/useTriggerAway'
import { ScrollArea } from '@/components/ui/ScrollArea' import { ScrollArea } from '@/components/ui/ScrollArea'
import { Virtuoso, VirtuosoHandle } from 'react-virtuoso' import { Virtuoso, VirtuosoHandle } from 'react-virtuoso'
import UserInfoDomain from '@/domain/UserInfo' import UserInfoDomain from '@/domain/UserInfo'
import { cn, getTextSimilarity } from '@/utils' import { cn, getRootNode, getTextSimilarity } from '@/utils'
import { Avatar, AvatarFallback } from '@/components/ui/Avatar' import { Avatar, AvatarFallback } from '@/components/ui/Avatar'
import { AvatarImage } from '@radix-ui/react-avatar' import { AvatarImage } from '@radix-ui/react-avatar'
import ToastDomain from '@/domain/Toast' import ToastDomain from '@/domain/Toast'
@ -240,7 +240,7 @@ const Footer: FC = () => {
}) })
} }
const root = document.querySelector(__NAME__)?.shadowRoot const root = getRootNode()
return ( return (
<div className="relative z-10 grid gap-y-2 rounded-b-xl 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-900 before:dark:from-slate-900"> <div className="relative z-10 grid gap-y-2 rounded-b-xl 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-900 before:dark:from-slate-900">
@ -248,8 +248,8 @@ const Footer: FC = () => {
<Portal <Portal
container={root} container={root}
ref={shareAutoCompleteListRef} ref={shareAutoCompleteListRef}
className="fixed z-infinity w-36 -translate-y-full rounded-lg border bg-popover text-popover-foreground shadow-md duration-300 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95" className="fixed z-infinity w-36 -translate-y-full overflow-hidden rounded-lg border bg-popover text-popover-foreground shadow-md duration-300 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95"
style={{ left: `min(${x}px, 100vw - 212px)`, top: `${y}px` }} style={{ left: `min(${x}px, 100vw - 160px)`, top: `${y}px` }}
> >
<ScrollArea className="max-h-[204px] min-h-9 p-1" ref={setScrollParentRef}> <ScrollArea className="max-h-[204px] min-h-9 p-1" ref={setScrollParentRef}>
<Virtuoso <Virtuoso

View file

@ -19,7 +19,7 @@ const Header: FC = () => {
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 dark:bg-slate-950"> <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 dark:text-slate-50"> <Avatar className="size-8">
<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" />

View file

@ -16,7 +16,7 @@ function App() {
<VersionLink></VersionLink> <VersionLink></VersionLink>
<Main> <Main>
<ProfileForm></ProfileForm> <ProfileForm></ProfileForm>
<Toaster richColors position="top-center" /> <Toaster richColors position="top-center" duration={1000000} />
</Main> </Main>
<BadgeList></BadgeList> <BadgeList></BadgeList>
</Layout> </Layout>

View file

@ -82,3 +82,9 @@
direction: ltr !important; direction: ltr !important;
} }
} }
/* @property --shimmer-angle {
syntax: '<angle>';
inherits: false;
initial-value: 0deg;
} */

View file

@ -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 dark:text-slate-400')} className={cn(className, 'prose prose-sm prose-slate break-words dark:text-slate-50')}
> >
{children} {children}
</ReactMarkdown> </ReactMarkdown>

View file

@ -1,6 +1,6 @@
import * as React from 'react' import * as React from 'react'
import * as PopoverPrimitive from '@radix-ui/react-popover' import * as PopoverPrimitive from '@radix-ui/react-popover'
import { cn } from '@/utils/index' import { cn, getRootNode } from '@/utils'
const Popover = PopoverPrimitive.Root const Popover = PopoverPrimitive.Root
@ -10,8 +10,7 @@ const PopoverContent = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>, React.ElementRef<typeof PopoverPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
>(({ className, align = 'center', sideOffset = 4, ...props }, ref) => { >(({ className, align = 'center', sideOffset = 4, ...props }, ref) => {
const root = document.querySelector(__NAME__)?.shadowRoot const root = getRootNode()
return ( return (
<PopoverPrimitive.Portal container={root}> <PopoverPrimitive.Portal container={root}>
<PopoverPrimitive.Content <PopoverPrimitive.Content

View file

@ -7,10 +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 <ScrollAreaPrimitive.Root className={cn('relative grid grid-rows-[1fr] overflow-hidden', className)} {...props}>
className={cn('relative grid grid-rows-[1fr] overflow-hidden dark:bg-slate-900', 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')}

View file

@ -15,7 +15,7 @@ export interface AppStatus {
export const defaultStatusState = { export const defaultStatusState = {
open: false, open: false,
unread: 0, unread: 0,
position: { x: window.innerWidth - 44, y: window.innerHeight - 22 } position: { x: window.innerWidth - 50, y: window.innerHeight - 22 }
} }
const AppStatusDomain = Remesh.domain({ const AppStatusDomain = Remesh.domain({

View file

@ -0,0 +1,22 @@
import { useEffect, useState } from 'react'
const useWindowResize = (callback?: ({ width, height }: { width: number; height: number }) => void) => {
const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight })
useEffect(() => {
const handler = () => {
const width = window.innerWidth
const height = window.innerHeight
setSize({ width, height })
callback?.({ width, height })
}
window.addEventListener('resize', handler)
return () => {
window.removeEventListener('resize', handler)
}
}, [])
return size
}
export default useWindowResize

5
src/utils/getRootNode.ts Normal file
View file

@ -0,0 +1,5 @@
export const getRootNode = () => {
return document.querySelector(__NAME__)?.shadowRoot?.querySelector('#app') || document.body
}
export default getRootNode

View file

@ -13,3 +13,4 @@ export { default as generateRandomAvatar } from './generateRandomAvatar'
export { default as generateRandomName } from './generateRandomName' export { default as generateRandomName } from './generateRandomName'
export { default as getCursorPosition } from './getCursorPosition' export { default as getCursorPosition } from './getCursorPosition'
export { default as getTextSimilarity } from './getTextSimilarity' export { default as getTextSimilarity } from './getTextSimilarity'
export { default as getRootNode } from './getRootNode'

View file

@ -81,6 +81,14 @@ export default {
transform: 'rotate(215deg) translateX(-500px)', transform: 'rotate(215deg) translateX(-500px)',
opacity: '0' opacity: '0'
} }
},
shimmer: {
'0%': {
'--shimmer-angle': '0deg'
},
'100%': {
'--shimmer-angle': '360deg'
}
} }
}, },
animation: { animation: {