chore: build basic layout framework
This commit is contained in:
parent
1a8d2ec675
commit
5bb773c0e3
31 changed files with 999 additions and 190 deletions
|
@ -31,6 +31,7 @@
|
|||
},
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"react/prop-types": "off",
|
||||
"@typescript-eslint/naming-convention": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
|
|
14
.vscode/launch.json
vendored
Normal file
14
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "debug vite",
|
||||
"request": "launch",
|
||||
"runtimeArgs": ["run-script", "dev"],
|
||||
"runtimeExecutable": "npm",
|
||||
"skipFiles": ["<node_internals>/**"],
|
||||
"type": "node",
|
||||
"sourceMaps": true
|
||||
}
|
||||
]
|
||||
}
|
15
index.html
Normal file
15
index.html
Normal file
|
@ -0,0 +1,15 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content="Chatting Anonymously with People on the Same Website." />
|
||||
<link rel="shortcut icon" href="https://github.com/shadcn.png" type="image/x-icon" />
|
||||
<title>WebChat</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<h1>WebChat Dev</h1>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -10,7 +10,7 @@ export default defineManifest({
|
|||
content_scripts: [
|
||||
{
|
||||
js: ['src/main.tsx'],
|
||||
matches: [isDev ? `*://localhost/*` : '<all_urls>']
|
||||
matches: isDev ? ['*://localhost/*', 'https://www.example.com/*'] : ['https://*/*']
|
||||
}
|
||||
]
|
||||
})
|
||||
|
|
10
package.json
10
package.json
|
@ -5,13 +5,14 @@
|
|||
"description": "Chatting Anonymously with People on the Same Website.",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"dev": "vite --force",
|
||||
"dev:web": "vite -c vite.config.web.ts --force",
|
||||
"build": "vite build",
|
||||
"pack": "cross-env NODE_ENV=production run-p pack:*",
|
||||
"pack:zip": "rimraf dist.zip && jszip-cli add dist/* -o ./dist.zip",
|
||||
"pack:crx": "crx pack dist -o ./dist.crx",
|
||||
"pack:xpi": "cross-env WEB_EXT_ARTIFACTS_DIR=./ web-ext build --source-dir ./dist --filename dist.xpi --overwrite-dest",
|
||||
"lint": "npx eslint . --ext .js,.jsx,.ts,.tsx --cache --fix",
|
||||
"lint": "eslint . --ext .js,.jsx,.ts,.tsx --cache --fix",
|
||||
"clear": "rimraf dist dist.*",
|
||||
"tsc:check": "tsc --noEmit",
|
||||
"prepare": "husky install"
|
||||
|
@ -86,13 +87,16 @@
|
|||
"*.{js,jsx,ts,tsx}": "eslint --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-avatar": "^1.0.3",
|
||||
"@radix-ui/react-hover-card": "^1.0.6",
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"class-variance-authority": "^0.6.1",
|
||||
"clsx": "^1.2.1",
|
||||
"lucide-react": "^0.263.0",
|
||||
"peerjs": "^1.4.7",
|
||||
"react-use": "^17.4.0",
|
||||
"tailwind-merge": "^1.13.2",
|
||||
"tailwindcss-animate": "^1.0.6",
|
||||
"type-fest": "^3.13.0"
|
||||
},
|
||||
"engines": {
|
||||
|
|
746
pnpm-lock.yaml
746
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -1,18 +1,17 @@
|
|||
import Header from '@/components/Header'
|
||||
import Footer from '@/components/Footer'
|
||||
import Main from '@/components/Main'
|
||||
import Sidebar from '@/components/Sidebar'
|
||||
import AppButton from './components/AppButton'
|
||||
import AppButton from '@/components/AppButton'
|
||||
import AppContainer from '@/components/AppContainer'
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<>
|
||||
<main className="main">
|
||||
<AppContainer>
|
||||
<Header />
|
||||
<Main />
|
||||
<Sidebar />
|
||||
<Footer />
|
||||
</main>
|
||||
</AppContainer>
|
||||
<AppButton></AppButton>
|
||||
</>
|
||||
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
import { type FC } from 'react'
|
||||
import { Button } from '@/components/ui/Button'
|
||||
|
||||
const AppButton: FC = () => {
|
||||
return (
|
||||
<Button className="fixed bottom-20 right-10 z-top h-10 w-10 select-none rounded-full text-red-300 ">ICON</Button>
|
||||
)
|
||||
}
|
||||
|
||||
export default AppButton
|
18
src/components/AppButton/index.tsx
Normal file
18
src/components/AppButton/index.tsx
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { type ReactNode, type FC } from 'react'
|
||||
import { Button } from '@/components/ui/Button'
|
||||
|
||||
export interface AppButtonProps {
|
||||
children?: ReactNode
|
||||
}
|
||||
|
||||
const AppButton: FC<AppButtonProps> = ({ children }) => {
|
||||
return (
|
||||
<Button className="fixed bottom-5 right-5 z-top h-10 w-10 select-none rounded-full bg-yellow-400 text-xs">
|
||||
{children}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
AppButton.displayName = 'AppButton'
|
||||
|
||||
export default AppButton
|
17
src/components/AppContainer/index.tsx
Normal file
17
src/components/AppContainer/index.tsx
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { type ReactNode, type FC } from 'react'
|
||||
|
||||
export interface AppContainerProps {
|
||||
children?: ReactNode
|
||||
}
|
||||
|
||||
const AppContainer: FC<AppContainerProps> = ({ children }) => {
|
||||
return (
|
||||
<div className="fixed bottom-10 right-10 top-10 z-top box-border grid w-1/4 grid-flow-col grid-rows-[auto_1fr_auto] overflow-hidden rounded-xl bg-white shadow-2xl transition-transform">
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
AppContainer.displayName = 'AppContainer'
|
||||
|
||||
export default AppContainer
|
|
@ -1,7 +0,0 @@
|
|||
import { type FC } from 'react'
|
||||
|
||||
const Footer: FC = () => {
|
||||
return <div></div>
|
||||
}
|
||||
|
||||
export default Footer
|
39
src/components/Footer/index.tsx
Normal file
39
src/components/Footer/index.tsx
Normal file
|
@ -0,0 +1,39 @@
|
|||
import { useState, type FC, type ChangeEvent } from 'react'
|
||||
import { Textarea } from '@/components/ui/Textarea'
|
||||
import { Button } from '@/components/ui/Button'
|
||||
import { Smile, Command, CornerDownLeft } from 'lucide-react'
|
||||
import { useBreakpoint } from '@/hooks/useBreakpoint'
|
||||
|
||||
const Footer: FC = () => {
|
||||
const { is2XL } = useBreakpoint()
|
||||
const [message, setMessage] = useState('')
|
||||
|
||||
const handleInput = (e: ChangeEvent<HTMLTextAreaElement>) => {
|
||||
setMessage(e.target.value)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-2 gap-y-2 p-4">
|
||||
<Textarea
|
||||
className="col-span-2"
|
||||
rows={is2XL ? 3 : 2}
|
||||
value={message}
|
||||
placeholder="Type your message here."
|
||||
onInput={handleInput}
|
||||
/>
|
||||
|
||||
<Button variant="ghost" size="sm" className="place-self-start">
|
||||
<Smile size={20} />
|
||||
</Button>
|
||||
<Button size="sm" className="place-self-end">
|
||||
<span className="mr-2">Send</span>
|
||||
<Command className="text-slate-400" size={12}></Command>
|
||||
<CornerDownLeft className="text-slate-400" size={12}></CornerDownLeft>
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Footer.displayName = 'Footer'
|
||||
|
||||
export default Footer
|
|
@ -1,7 +0,0 @@
|
|||
import { type FC } from 'react'
|
||||
|
||||
const Header: FC = ({ ...props }) => {
|
||||
return <div></div>
|
||||
}
|
||||
|
||||
export default Header
|
35
src/components/Header/index.tsx
Normal file
35
src/components/Header/index.tsx
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { useState, type FC } from 'react'
|
||||
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/HoverCard'
|
||||
import { Button } from '@/components/ui/Button'
|
||||
import getWebSiteInfo from '@/utils/getWebsiteInfo'
|
||||
|
||||
const Header: FC = ({ ...props }) => {
|
||||
const [websiteInfo] = useState(getWebSiteInfo())
|
||||
|
||||
return (
|
||||
<div className="flex h-12 items-center px-4 shadow-sm 2xl:h-14">
|
||||
<img className="h-8 w-8 overflow-hidden rounded-full" src={websiteInfo.icon} />
|
||||
<HoverCard>
|
||||
<HoverCardTrigger asChild>
|
||||
<Button className="overflow-hidden text-xl" variant="link">
|
||||
<h1 className="truncate">{websiteInfo.hostname}</h1>
|
||||
</Button>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent className="w-80">
|
||||
<div className="flex justify-between space-x-4">
|
||||
<img className="h-14 w-14 flex-shrink-0 overflow-hidden rounded-full" src={websiteInfo.icon} />
|
||||
<div className="space-y-1">
|
||||
<h4 className="text-sm font-semibold">{websiteInfo.title}</h4>
|
||||
<p className="text-xs text-slate-500">{websiteInfo.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
<div className="ml-auto flex-shrink-0 text-sm text-slate-500">Online 99</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Header.displayName = 'Header'
|
||||
|
||||
export default Header
|
|
@ -1,15 +0,0 @@
|
|||
import IconPower from '~icons/pixelarticons/power'
|
||||
|
||||
export default function Logo() {
|
||||
return (
|
||||
<a
|
||||
className="icon-btn mx-2 text-2xl"
|
||||
rel="noreferrer"
|
||||
href="https://github.com/antfu/vitesse-webext"
|
||||
target="_blank"
|
||||
title="GitHub"
|
||||
>
|
||||
<IconPower />
|
||||
</a>
|
||||
)
|
||||
}
|
9
src/components/Main/Message.tsx
Normal file
9
src/components/Main/Message.tsx
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { type FC } from 'react'
|
||||
|
||||
const Message: FC = () => {
|
||||
return <div>Message</div>
|
||||
}
|
||||
|
||||
Message.displayName = 'Message'
|
||||
|
||||
export default Message
|
|
@ -1,7 +1,9 @@
|
|||
import { type FC } from 'react'
|
||||
|
||||
const Main: FC = () => {
|
||||
return <div></div>
|
||||
return <div>Main</div>
|
||||
}
|
||||
|
||||
Main.displayName = 'Main'
|
||||
|
||||
export default Main
|
|
@ -1,7 +0,0 @@
|
|||
import { type FC } from 'react'
|
||||
|
||||
const Sidebar: FC = () => {
|
||||
return <div></div>
|
||||
}
|
||||
|
||||
export default Sidebar
|
38
src/components/ui/Avatar.tsx
Normal file
38
src/components/ui/Avatar.tsx
Normal file
|
@ -0,0 +1,38 @@
|
|||
import * as React from 'react'
|
||||
import * as AvatarPrimitive from '@radix-ui/react-avatar'
|
||||
|
||||
import { cn } from '@/utils/index'
|
||||
|
||||
const Avatar = React.forwardRef<
|
||||
React.ElementRef<typeof AvatarPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AvatarPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn('relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
Avatar.displayName = AvatarPrimitive.Root.displayName
|
||||
|
||||
const AvatarImage = React.forwardRef<
|
||||
React.ElementRef<typeof AvatarPrimitive.Image>,
|
||||
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AvatarPrimitive.Image ref={ref} className={cn('aspect-square h-full w-full', className)} {...props} />
|
||||
))
|
||||
AvatarImage.displayName = AvatarPrimitive.Image.displayName
|
||||
|
||||
const AvatarFallback = React.forwardRef<
|
||||
React.ElementRef<typeof AvatarPrimitive.Fallback>,
|
||||
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AvatarPrimitive.Fallback
|
||||
ref={ref}
|
||||
className={cn('flex h-full w-full items-center justify-center rounded-full bg-muted', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
|
||||
|
||||
export { Avatar, AvatarImage, AvatarFallback }
|
29
src/components/ui/Badge.tsx
Normal file
29
src/components/ui/Badge.tsx
Normal file
|
@ -0,0 +1,29 @@
|
|||
import type * as React from 'react'
|
||||
import { cva, type VariantProps } from 'class-variance-authority'
|
||||
|
||||
import { cn } from '@/utils/index'
|
||||
|
||||
const badgeVariants = cva(
|
||||
'inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80',
|
||||
secondary: 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
||||
destructive: 'border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80',
|
||||
outline: 'text-foreground'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {}
|
||||
|
||||
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||
return <div className={cn(badgeVariants({ variant }), className)} {...props} />
|
||||
}
|
||||
|
||||
export { Badge, badgeVariants }
|
27
src/components/ui/HoverCard.tsx
Normal file
27
src/components/ui/HoverCard.tsx
Normal file
|
@ -0,0 +1,27 @@
|
|||
import * as React from 'react'
|
||||
import * as HoverCardPrimitive from '@radix-ui/react-hover-card'
|
||||
|
||||
import { cn } from '@/utils/index'
|
||||
|
||||
const HoverCard = HoverCardPrimitive.Root
|
||||
|
||||
const HoverCardTrigger = HoverCardPrimitive.Trigger
|
||||
|
||||
const HoverCardContent = React.forwardRef<
|
||||
React.ElementRef<typeof HoverCardPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>
|
||||
>(({ className, align = 'center', sideOffset = 4, ...props }, ref) => (
|
||||
<HoverCardPrimitive.Content
|
||||
ref={ref}
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
'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 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
HoverCardContent.displayName = HoverCardPrimitive.Content.displayName
|
||||
|
||||
export { HoverCard, HoverCardTrigger, HoverCardContent }
|
|
@ -11,6 +11,8 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(({ classNa
|
|||
'flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
|
17
src/constants/index.ts
Normal file
17
src/constants/index.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
// https://night-tailwindcss.vercel.app/docs/breakpoints
|
||||
export const BREAKPOINTS = {
|
||||
sm: '640px',
|
||||
// => @media (min-width: 640px) { ... }
|
||||
|
||||
md: '768px',
|
||||
// => @media (min-width: 768px) { ... }
|
||||
|
||||
lg: '1024px',
|
||||
// => @media (min-width: 1024px) { ... }
|
||||
|
||||
xl: '1280px',
|
||||
// => @media (min-width: 1280px) { ... }
|
||||
|
||||
'2xl': '1536px'
|
||||
// => @media (min-width: 1536px) { ... }
|
||||
} as const
|
|
@ -6,10 +6,11 @@ export interface RootOptions {
|
|||
mode?: ShadowRootMode
|
||||
style?: string
|
||||
script?: string
|
||||
element?: Element
|
||||
}
|
||||
|
||||
const createShadowRoot = (name: string, options: RootOptions): Root => {
|
||||
const { mode = 'open', style = '', script = '' } = options ?? {}
|
||||
const { mode = 'open', style = '', script = '', element = '' } = options ?? {}
|
||||
const shadowHost = createElement(`<${name}></${name}>`)
|
||||
const shadowRoot = shadowHost.attachShadow({ mode })
|
||||
const appRoot = createElement(`<div id="app"></div>`)
|
||||
|
@ -17,7 +18,7 @@ const createShadowRoot = (name: string, options: RootOptions): Root => {
|
|||
const appScript = script && createElement(`<script type="application/javascript">${script}</script>`)
|
||||
const reactRoot = createRoot(appRoot)
|
||||
|
||||
shadowRoot.append(appStyle, appRoot, appScript)
|
||||
shadowRoot.append(appStyle, appRoot, appScript, element)
|
||||
|
||||
return {
|
||||
...reactRoot,
|
||||
|
|
17
src/hooks/useBreakpoint.ts
Normal file
17
src/hooks/useBreakpoint.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { useMedia } from 'react-use'
|
||||
import { BREAKPOINTS } from '@/constants'
|
||||
|
||||
export function useBreakpoint() {
|
||||
const isSM = useMedia(`(min-width: ${BREAKPOINTS.sm})`)
|
||||
const isMD = useMedia(`(min-width: ${BREAKPOINTS.md})`)
|
||||
const isLG = useMedia(`(min-width: ${BREAKPOINTS.lg})`)
|
||||
const isXL = useMedia(`(min-width: ${BREAKPOINTS.xl})`)
|
||||
const is2XL = useMedia(`(min-width: ${BREAKPOINTS['2xl']})`)
|
||||
return {
|
||||
isSM,
|
||||
isMD,
|
||||
isLG,
|
||||
isXL,
|
||||
is2XL
|
||||
}
|
||||
}
|
15
src/main.tsx
15
src/main.tsx
|
@ -3,15 +3,22 @@ import App from './App'
|
|||
import createShadowRoot from './createShadowRoot'
|
||||
import style from './index.css?inline'
|
||||
|
||||
// TODO: css hmr not work
|
||||
// https://github.com/crxjs/chrome-extension-tools/issues/671
|
||||
void (() => {
|
||||
void (async () => {
|
||||
createShadowRoot(__NAME__, {
|
||||
style,
|
||||
style: __DEV__ ? '' : style,
|
||||
mode: __DEV__ ? 'open' : 'closed'
|
||||
}).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
)
|
||||
|
||||
// HMR Hack
|
||||
// https://github.com/crxjs/chrome-extension-tools/issues/600
|
||||
if (__DEV__) {
|
||||
await import('./index.css')
|
||||
const styleElement = document.querySelector('[data-vite-dev-id]')!
|
||||
const shadowRoot = document.querySelector(__NAME__)!.shadowRoot!
|
||||
shadowRoot.insertBefore(styleElement, shadowRoot.firstChild)
|
||||
}
|
||||
})()
|
||||
|
|
24
src/utils/getWebsiteInfo.ts
Normal file
24
src/utils/getWebsiteInfo.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
const getWebSiteInfo = () => {
|
||||
return {
|
||||
host: document.location.host,
|
||||
hostname: document.location.hostname,
|
||||
href: document.location.href,
|
||||
origin: document.location.origin,
|
||||
title:
|
||||
document.querySelector('meta[rel="og:title i"]')?.getAttribute('content') ??
|
||||
document.querySelector('meta[rel="og:title i"]')?.getAttribute('content') ??
|
||||
document.querySelector('meta[rel="og:site_name i"]')?.getAttribute('content') ??
|
||||
document.title,
|
||||
icon:
|
||||
document.querySelector('meta[property="og:image" i]')?.getAttribute('href') ??
|
||||
document.querySelector('link[rel="icon" i]')?.getAttribute('href') ??
|
||||
document.querySelector('link[rel="shortcut icon" i]')?.getAttribute('href') ??
|
||||
`${document.location.origin}/favicon.ico}`,
|
||||
description:
|
||||
document.querySelector('meta[property="og:description i"]')?.getAttribute('content') ??
|
||||
document.querySelector('meta[name="description" i]')?.getAttribute('content') ??
|
||||
''
|
||||
}
|
||||
}
|
||||
|
||||
export default getWebSiteInfo
|
|
@ -1,14 +1,12 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
import { type Config } from 'tailwindcss'
|
||||
|
||||
export default {
|
||||
darkMode: ['class'],
|
||||
content: ['./src/**/*.{ts,tsx}'],
|
||||
content: ['./src/**/*.{ts,tsx,css}', './index.html'],
|
||||
theme: {
|
||||
container: {
|
||||
center: true,
|
||||
padding: '2rem',
|
||||
screens: {
|
||||
'2xl': '1400px'
|
||||
}
|
||||
padding: '2rem'
|
||||
},
|
||||
extend: {
|
||||
zIndex: {
|
||||
|
@ -56,12 +54,12 @@ module.exports = {
|
|||
},
|
||||
keyframes: {
|
||||
'accordion-down': {
|
||||
from: { height: 0 },
|
||||
from: { height: '0' },
|
||||
to: { height: 'var(--radix-accordion-content-height)' }
|
||||
},
|
||||
'accordion-up': {
|
||||
from: { height: 'var(--radix-accordion-content-height)' },
|
||||
to: { height: 0 }
|
||||
to: { height: '0' }
|
||||
}
|
||||
},
|
||||
animation: {
|
||||
|
@ -69,6 +67,5 @@ module.exports = {
|
|||
'accordion-up': 'accordion-up 0.2s ease-out'
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [require('tailwindcss-animate')]
|
||||
}
|
||||
} satisfies Config
|
||||
|
|
|
@ -22,6 +22,9 @@ export default defineConfig({
|
|||
react(),
|
||||
// https://github.com/antfu/unplugin-icons
|
||||
Icons({ compiler: 'jsx', jsx: 'react' }),
|
||||
crx({ manifest })
|
||||
// @ts-expect-error use local package
|
||||
crx({
|
||||
manifest
|
||||
})
|
||||
]
|
||||
})
|
||||
|
|
24
vite.config.web.ts
Normal file
24
vite.config.web.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import path from 'node:path'
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import Icons from 'unplugin-icons/vite'
|
||||
import packageJson from './package.json'
|
||||
|
||||
const isDev = process.env.NODE_ENV === 'development'
|
||||
|
||||
export default defineConfig({
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src')
|
||||
}
|
||||
},
|
||||
define: {
|
||||
__DEV__: isDev,
|
||||
__NAME__: JSON.stringify(packageJson.name)
|
||||
},
|
||||
plugins: [
|
||||
react(),
|
||||
// https://github.com/antfu/unplugin-icons
|
||||
Icons({ compiler: 'jsx', jsx: 'react' })
|
||||
]
|
||||
})
|
Loading…
Reference in a new issue