Initial commit

This commit is contained in:
Aditya Gupta
2026-06-29 19:29:19 +05:30
committed by GitHub
commit aabb8986aa
292 changed files with 17976 additions and 0 deletions

336
packages/ui/README.md Normal file
View File

@@ -0,0 +1,336 @@
# UI Package
This package provides components that make up the UI.
## Installation
First, move to the page you want to use.
```shell
cd pages/options
```
Add the following to the dependencies in `package.json`.
```json
{
"dependencies": {
"@extension/ui": "workspace:*"
}
}
```
Then, run:
```shell
pnpm install
```
Add the following to the `tailwind.config.ts` file.
```ts
import baseConfig from '@extension/tailwindcss-config';
import { withUI } from '@extension/ui';
export default withUI({
...baseConfig,
content: ['./index.html', './src/**/*.tsx'],
});
```
Add the following to the first line of `index.css` file.
```css
@import '@extension/ui/global.css';
```
## Add Custom Component
Add the following to the `lib/components/index.ts` file.
```tsx
export * from './CustomComponent.js';
```
Add the following to the `lib/components/CustomComponent.tsx` file.
```tsx
import { cn } from '@/lib/utils.js';
import type { ComponentPropsWithoutRef } from 'react';
type CustomComponentProps = ComponentPropsWithoutRef<'section'>;
export const CustomComponent = ({ children, ...props }: CustomComponentProps) => {
return <section {...props}>{children}</section>;
}
```
## Usage
```tsx
import { CustomComponent, ErrorDisplay, LoadingSpinner } from '@extension/ui';
const Page = () => {
return <CustomComponent>Hi, I'm a custom component.</CustomComponent>;
}
export default withErrorBoundary(withSuspense(Page, <LoadingSpinner />), ErrorDisplay);
```
> [!TIP]
> You are able to set other size of the loading spinner by passing the `size` prop to the `<LoadingSpinner />`.
## Modifying the tailwind config of the UI library
Modify the `tailwind.config.ts` file to make global style changes to the package.
## Modifying the css variable of the UI library
Modify the css variable in the `ui/lib/global.css` code to change the css variable of all pages(with UI).
## Guide for Adding shadcn to the UI Package
You can refer to the this [manual guide](https://ui.shadcn.com/docs/installation/manual)
1. Create components.json in packages/ui
Create a file named `components.json` in the `packages/ui` directory with the following content:
```json
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "lib/global.css",
"baseColor": "zinc",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/lib/components",
"utils": "@/lib/utils",
"ui": "@/lib/components/ui",
"lib": "@/lib",
"hooks": "@/lib/hooks"
},
"iconLibrary": "lucide"
}
```
2. Install dependencies
Run the following command from the root of your project:
```shell
pnpm add tailwindcss-animate class-variance-authority tailwind-merge lucide-react -F ui
```
3. Edit `with-ui.ts` in `lib` folder
This configuration file is from the manual guide. You can refer to the manual guide to modify the configuration file. ([
`Configure tailwind.config.js`](https://ui.shadcn.com/docs/installation/manual))
```ts
import deepmerge from 'deepmerge';
import { fontFamily } from 'tailwindcss/defaultTheme';
import tailwindAnimate from 'tailwindcss-animate';
import type { Config } from 'tailwindcss';
export function withUI(tailwindConfig: Config): Config {
return deepmerge(
shadcnConfig,
deepmerge(tailwindConfig, {
content: ['./node_modules/@extension/ui/lib/**/*.{tsx,ts,js,jsx}'],
}),
);
}
const shadcnConfig = {
darkMode: ['class'],
theme: {
container: {
center: true,
padding: '2rem',
screens: {
'2xl': '1400px',
},
},
extend: {
colors: {
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))',
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))',
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))',
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))',
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))',
},
popover: {
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))',
},
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))',
},
},
borderRadius: {
lg: `var(--radius)`,
md: `calc(var(--radius) - 2px)`,
sm: 'calc(var(--radius) - 4px)',
},
fontFamily: {
sans: ['var(--font-sans)', ...fontFamily.sans],
},
keyframes: {
'accordion-down': {
from: { height: '0' },
to: { height: 'var(--radix-accordion-content-height)' },
},
'accordion-up': {
from: { height: 'var(--radix-accordion-content-height)' },
to: { height: '0' },
},
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out',
},
},
},
plugins: [tailwindAnimate],
};
```
4. Edit `global.css` in `lib` folder
This configuration also comes from the manual guide. You can refer to the manual guide to modify the configuration
file. ([`Configure styles`](https://ui.shadcn.com/docs/installation/manual))
```css
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:host, :root {
--background: 0 0% 100%;
--foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 47.4% 11.2%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--card: 0 0% 100%;
--card-foreground: 222.2 47.4% 11.2%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 100% 50%;
--destructive-foreground: 210 40% 98%;
--ring: 215 20.2% 65.1%;
--radius: 0.5rem;
}
.dark {
--background: 224 71% 4%;
--foreground: 213 31% 91%;
--muted: 223 47% 11%;
--muted-foreground: 215.4 16.3% 56.9%;
--accent: 216 34% 17%;
--accent-foreground: 210 40% 98%;
--popover: 224 71% 4%;
--popover-foreground: 215 20.2% 65.1%;
--border: 216 34% 17%;
--input: 216 34% 17%;
--card: 224 71% 4%;
--card-foreground: 213 31% 91%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 1.2%;
--secondary: 222.2 47.4% 11.2%;
--secondary-foreground: 210 40% 98%;
--destructive: 0 63% 31%;
--destructive-foreground: 210 40% 98%;
--ring: 216 34% 17%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply font-sans antialiased bg-background text-foreground;
}
}
```
5. Add shadcn components
Finally, run this command from the root of your project to add the button component:
```shell
pnpm dlx shadcn@latest add button -c ./packages/ui
```
This will add the shadcn button component to your UI package.
Remember to adjust any paths or package names if your project structure differs from the assumed layout in this guide.
6. Export components
Edit the `index.ts` file in the `packages/ui` directory to export the shadcn ui component:
```ts
//...
export * from './lib/components/ui/button';
```
7. Apply global.css
If you want to use shadcn components in content-ui ShadowDOM, you need to import ui package's global.css in the content-ui `tailwind-input.css`
```css
@import '@extension/ui/lib/global.css';
@tailwind base;
@tailwind components;
@tailwind utilities;
```
If you want to use shadcn components in other pages, you need to import ui package's global.css in the `src/index.css`
```css
@import '@extension/ui/lib/global.css';
@tailwind base;
@tailwind components;
@tailwind utilities;
```

20
packages/ui/global.css Normal file
View File

@@ -0,0 +1,20 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family:
-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans',
'Helvetica Neue', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
}

1
packages/ui/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from './lib/index';

View File

@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" width="400" fill="none" stroke="currentColor">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
fill="none"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>

After

Width:  |  Height:  |  Size: 409 B

View File

@@ -0,0 +1,11 @@
import { RingLoader } from 'react-spinners';
interface ILoadingSpinnerProps {
size?: number;
}
export const LoadingSpinner = ({ size }: ILoadingSpinnerProps) => (
<div className={'flex min-h-screen items-center justify-center'}>
<RingLoader size={size ?? 100} color={'aqua'} />
</div>
);

View File

@@ -0,0 +1,23 @@
import { cn } from '@/lib/utils';
import { useStorage } from '@extension/shared';
import { exampleThemeStorage } from '@extension/storage';
import type { ComponentPropsWithoutRef } from 'react';
type ToggleButtonProps = ComponentPropsWithoutRef<'button'>;
export const ToggleButton = ({ className, children, ...props }: ToggleButtonProps) => {
const { isLight } = useStorage(exampleThemeStorage);
return (
<button
className={cn(
'mt-4 rounded border-2 px-4 py-1 font-bold shadow hover:scale-105',
isLight ? 'border-black bg-white text-black' : 'border-white bg-black text-white',
className,
)}
onClick={exampleThemeStorage.toggle}
{...props}>
{children}
</button>
);
};

View File

@@ -0,0 +1,13 @@
import { ErrorHeader } from '@/lib/components/error-display/ErrorHeader';
import { ErrorResetButton } from '@/lib/components/error-display/ErrorResetButton';
import { ErrorStackTraceList } from '@/lib/components/error-display/ErrorStackTraceList';
export const ErrorDisplay = ({ error, resetErrorBoundary }: { error?: Error; resetErrorBoundary?: () => void }) => (
<div className="flex items-center justify-center bg-gray-50 px-4 py-6 sm:px-6 lg:px-8">
<div className="w-full max-w-md space-y-8">
<ErrorHeader />
<ErrorStackTraceList error={error} />
<ErrorResetButton resetErrorBoundary={resetErrorBoundary} />
</div>
</div>
);

View File

@@ -0,0 +1,21 @@
import { t } from '@extension/i18n';
// FIXME: IMPORT SVG ICON INSTEAD OF DEFINING INLINE IT HERE
const WarningIcon = ({ className }: { className: string }) => (
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" className={className}>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
);
export const ErrorHeader = () => (
<div className="text-center">
<WarningIcon className={'mx-auto h-24 w-24 text-red-500'} />
<h2 className="mt-6 text-3xl font-extrabold text-gray-900">{t('displayErrorInfo')}</h2>
<p className="mt-2 text-sm text-gray-600">{t('displayErrorDescription')}.</p>
</div>
);

View File

@@ -0,0 +1,11 @@
import { t } from '@extension/i18n';
export const ErrorResetButton = ({ resetErrorBoundary }: { resetErrorBoundary?: () => void }) => (
<div className="flex items-center justify-center">
<button
onClick={resetErrorBoundary}
className="inline-flex items-center rounded-md border border-transparent bg-red-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2">
{t('displayErrorReset')}
</button>
</div>
);

View File

@@ -0,0 +1,20 @@
import { t } from '@extension/i18n';
export const ErrorStackTraceList = ({ error }: { error?: Error }) => (
<div className="overflow-hidden rounded-lg bg-white shadow">
<div className="px-4 py-5 sm:p-6">
<div className="text-sm text-gray-500">
<p className="mb-2 font-medium text-gray-700">{t('displayErrorDetailsInfo')}</p>
<div className="overflow-auto rounded-md bg-red-50 p-4">
<p className="break-all font-mono text-red-700">{error?.message || t('displayErrorUnknownErrorInfo')}</p>
{error?.stack && (
<details className="mt-3">
<summary className="cursor-pointer text-sm text-red-700">Stack trace</summary>
<pre className="mt-2 overflow-auto p-2 text-xs text-red-800">{error?.stack}</pre>
</details>
)}
</div>
</div>
</div>
</div>
);

View File

@@ -0,0 +1,3 @@
export * from './ToggleButton';
export * from './LoadingSpinner';
export * from './error-display/ErrorDisplay';

3
packages/ui/lib/index.ts Normal file
View File

@@ -0,0 +1,3 @@
export * from './components/index';
export * from './utils';
export * from './with-ui';

5
packages/ui/lib/utils.ts Normal file
View File

@@ -0,0 +1,5 @@
import { clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
import type { ClassValue } from 'clsx';
export const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs));

View File

@@ -0,0 +1,7 @@
import deepmerge from 'deepmerge';
import type { Config } from 'tailwindcss';
export const withUI = (tailwindConfig: Config): Config =>
deepmerge(tailwindConfig, {
content: ['../../packages/ui/lib/**/*.tsx'],
});

37
packages/ui/package.json Normal file
View File

@@ -0,0 +1,37 @@
{
"name": "@extension/ui",
"version": "0.5.0",
"description": "chrome extension - ui components",
"type": "module",
"private": true,
"sideEffects": true,
"files": [
"dist/**"
],
"types": "index.ts",
"main": "dist/index.js",
"scripts": {
"clean:bundle": "rimraf dist",
"clean:node_modules": "pnpx rimraf node_modules",
"clean:turbo": "rimraf .turbo",
"clean": "pnpm clean:bundle && pnpm clean:node_modules && pnpm clean:turbo",
"ready": "tsc -b && tsc-alias -p tsconfig.json",
"lint": "eslint .",
"lint:fix": "pnpm lint --fix",
"format": "prettier . --write --ignore-path ../../.prettierignore",
"type-check": "tsc --noEmit"
},
"dependencies": {
"@extension/i18n": "workspace:*",
"@extension/shared": "workspace:*",
"@extension/storage": "workspace:*",
"clsx": "^2.1.1",
"tailwind-merge": "^3.3.0"
},
"devDependencies": {
"@extension/tailwindcss-config": "workspace:*",
"@extension/tsconfig": "workspace:*",
"react-spinners": "^0.17.0",
"tsc-alias": "^1.8.16"
}
}

View File

@@ -0,0 +1,7 @@
import globalConfig from '@extension/tailwindcss-config';
import type { Config } from 'tailwindcss';
export default {
content: ['lib/**/*.tsx'],
presets: [globalConfig],
} satisfies Config;

13
packages/ui/tsconfig.json Normal file
View File

@@ -0,0 +1,13 @@
{
"extends": "@extension/tsconfig/base",
"compilerOptions": {
"outDir": "dist",
"baseUrl": ".",
"paths": {
"@/*": ["./*"]
},
"noEmit": false,
"declaration": true
},
"include": ["index.ts", "lib", "tailwind.config.ts"]
}