Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
69b49b2
Integrate beUI motion primitives
hoangsvit Jul 2, 2026
c68e3de
style: format code with Prettier
deepsource-autofix[bot] Jul 2, 2026
8a96bec
fix: reorder dependencies in package.json for clarity
hoangsvit Jul 3, 2026
a2431fb
feat: integrate beUI motion into remaining UI components
hoangsvit Jul 3, 2026
af65fb8
refactor: align UI components with beUI design patterns
hoangsvit Jul 3, 2026
1142f71
refactor: enhance Select component styling and error handling
hoangsvit Jul 3, 2026
177ac9b
refactor: refine component styling for beUI consistency
hoangsvit Jul 3, 2026
2efd78a
refactor: improve Tooltip styling for beUI consistency
hoangsvit Jul 3, 2026
ddd9a6d
refactor: standardize border radius and opacity across components
hoangsvit Jul 3, 2026
6fb412d
feat: implement beUI design token system
hoangsvit Jul 3, 2026
2a1845a
refactor: migrate Badge and Toast to beUI design tokens
hoangsvit Jul 3, 2026
48dbec3
refactor: migrate remaining components to beUI design tokens
hoangsvit Jul 3, 2026
07eb3dd
style: format code with Prettier
deepsource-autofix[bot] Jul 3, 2026
61a2006
fix: adjust design token values for better beUI appearance
hoangsvit Jul 3, 2026
fbe568b
config: add beUI MCP server for project
hoangsvit Jul 3, 2026
2e73db8
fix: align Button component with beUI spec
hoangsvit Jul 3, 2026
dd6b93b
style: format code with Prettier
deepsource-autofix[bot] Jul 3, 2026
ec68ea9
feat: implement beUI Select compound component
hoangsvit Jul 3, 2026
cafdfcd
Merge branch 'codex/refactor-popup-ui-using-beui-design-bnuwpe' of ht…
hoangsvit Jul 3, 2026
06393fd
style: format code with Prettier
deepsource-autofix[bot] Jul 3, 2026
1831af4
config: add beUI MCP server configuration
hoangsvit Jul 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .claude.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"mcpServers": {
"beui": {
"type": "http",
"url": "https://mcp.beui.dev/mcp"
}
}
}
2 changes: 2 additions & 0 deletions .codex/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[mcp_servers.beui]
url = "https://mcp.beui.dev/mcp"
344 changes: 57 additions & 287 deletions entrypoints/popup/App.tsx

Large diffs are not rendered by default.

58 changes: 2 additions & 56 deletions entrypoints/popup/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,2 @@
import { ReactNode } from "react";

interface ButtonProps {
children: ReactNode;
onClick?: () => void;
variant?: "primary" | "secondary" | "danger" | "success";
size?: "sm" | "md" | "lg";
disabled?: boolean;
fullWidth?: boolean;
icon?: ReactNode;
}

/** Styled button with variant, size, and optional icon support. */
export default function Button({
children,
onClick,
variant = "primary",
size = "md",
disabled = false,
fullWidth = false,
icon,
}: ButtonProps) {
const baseClasses =
"font-medium rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2";

const variantClasses = {
primary:
"bg-gradient-to-r from-blue-600 to-purple-600 text-white hover:from-blue-700 hover:to-purple-700 focus:ring-blue-500",
secondary:
"bg-gray-100 text-gray-700 hover:bg-gray-200 focus:ring-gray-500 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600",
danger:
"bg-red-50 text-red-700 hover:bg-red-100 focus:ring-red-500 dark:bg-red-900/30 dark:text-red-400 dark:hover:bg-red-900/50",
success:
"bg-green-50 text-green-700 hover:bg-green-100 focus:ring-green-500 dark:bg-green-900/30 dark:text-green-400 dark:hover:bg-green-900/50",
};

const sizeClasses = {
sm: "px-3 py-1.5 text-xs",
md: "px-4 py-2 text-sm",
lg: "px-6 py-3 text-base",
};

const widthClass = fullWidth ? "w-full" : "";
const disabledClass = disabled ? "opacity-50 cursor-not-allowed" : "";

return (
<button
onClick={onClick}
disabled={disabled}
className={`${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]} ${widthClass} ${disabledClass} flex items-center justify-center gap-2`}
>
{icon && <span>{icon}</span>}
{children}
</button>
);
}
export { default } from "../../../src/components/ui/Button";
export type { ButtonProps } from "../../../src/components/ui/Button";
41 changes: 2 additions & 39 deletions entrypoints/popup/components/Input.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,2 @@
interface InputProps {
type?: "text" | "email" | "number";
value: string | number;
onChange: (value: string) => void;
placeholder?: string;
label?: string;
disabled?: boolean;
onKeyPress?: (e: React.KeyboardEvent) => void;
}

/** Styled text input with optional label. */
export default function Input({
type = "text",
value,
onChange,
placeholder,
label,
disabled = false,
onKeyPress,
}: InputProps) {
return (
<div>
{label && (
<label className="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-2">
{label}
</label>
)}
<input
type={type}
value={value}
onChange={(e) => onChange(e.target.value)}
onKeyPress={onKeyPress}
placeholder={placeholder}
disabled={disabled}
className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-full text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100"
/>
</div>
);
}
export { default } from "../../../src/components/ui/Input";
export type { InputProps } from "../../../src/components/ui/Input";
145 changes: 24 additions & 121 deletions entrypoints/popup/components/Statistics.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { useState, useEffect } from "react";
import { BarChart3, Check, Clock, Mail, X } from "lucide-react";
import { NumberAnimation, StatCard } from "../../../src/components/ui";
import { getAccountStorageKey } from "../utils";

interface Stats {
Expand Down Expand Up @@ -127,19 +129,7 @@ export default function Statistics() {
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">
View Statistics
</span>
<svg
className="w-4 h-4 text-gray-400 dark:text-gray-500"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
/>
</svg>
<BarChart3 className="h-4 w-4 text-gray-400 dark:text-gray-500" />
</button>
);
}
Expand All @@ -156,118 +146,31 @@ export default function Statistics() {
onClick={() => setIsOpen(false)}
className="text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300"
>
<svg
className="w-4 h-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
</svg>
<X className="h-4 w-4" />
</button>
</div>

<div className="grid grid-cols-2 gap-2">
<div className="border border-gray-200 dark:border-gray-700 rounded-xl p-3">
<div className="w-8 h-8 rounded-lg bg-blue-50 dark:bg-blue-950/40 flex items-center justify-center mb-2">
<svg
className="w-4 h-4 text-blue-600 dark:text-blue-400"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
/>
</svg>
</div>
<div className="text-xl font-bold text-gray-900 dark:text-gray-100">
{stats.totalGenerated}
</div>
<div className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
Total Generated
</div>
</div>

<div className="border border-gray-200 dark:border-gray-700 rounded-xl p-3">
<div className="w-8 h-8 rounded-lg bg-green-50 dark:bg-green-950/40 flex items-center justify-center mb-2">
<svg
className="w-4 h-4 text-green-600 dark:text-green-400"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M5 13l4 4L19 7"
/>
</svg>
</div>
<div className="text-xl font-bold text-gray-900 dark:text-gray-100">
{stats.createdToday}
</div>
<div className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
Created Today
</div>
</div>

<div className="border border-gray-200 dark:border-gray-700 rounded-xl p-3">
<div className="w-8 h-8 rounded-lg bg-purple-50 dark:bg-purple-950/40 flex items-center justify-center mb-2">
<svg
className="w-4 h-4 text-purple-600 dark:text-purple-400"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
</div>
<div className="text-xl font-bold text-gray-900 dark:text-gray-100">
{stats.createdThisWeek}
</div>
<div className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
This Week
</div>
</div>

<div className="border border-gray-200 dark:border-gray-700 rounded-xl p-3">
<div className="w-8 h-8 rounded-lg bg-orange-50 dark:bg-orange-950/40 flex items-center justify-center mb-2">
<svg
className="w-4 h-4 text-orange-600 dark:text-orange-400"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"
/>
</svg>
</div>
<div className="text-sm font-bold text-gray-900 dark:text-gray-100 truncate">
{stats.mostUsedTag}
</div>
<div className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
Most Used Tag
</div>
</div>
<StatCard
icon={<Mail className="h-4 w-4" />}
value={<NumberAnimation value={stats.totalGenerated} />}
label="Total Generated"
/>
<StatCard
icon={<Check className="h-4 w-4" />}
value={<NumberAnimation value={stats.createdToday} />}
label="Created Today"
/>
<StatCard
icon={<Clock className="h-4 w-4" />}
value={<NumberAnimation value={stats.createdThisWeek} />}
label="This Week"
/>
<StatCard
icon={<BarChart3 className="h-4 w-4" />}
value={stats.mostUsedTag}
label="Top Tag"
/>
</div>
</div>
);
Expand Down
45 changes: 2 additions & 43 deletions entrypoints/popup/components/Toggle.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,2 @@
interface ToggleProps {
enabled: boolean;
onChange: (enabled: boolean) => void;
label: string;
description?: string;
}

/** Accessible on/off switch with a label and optional description. */
export default function Toggle({
enabled,
onChange,
label,
description,
}: ToggleProps) {
return (
<div className="flex items-center justify-between">
<div>
<label className="text-sm font-medium text-gray-700 dark:text-gray-300">
{label}
</label>
{description && (
<p className="text-xs text-gray-500 dark:text-gray-400">
{description}
</p>
)}
</div>
<button
onClick={() => onChange(!enabled)}
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
enabled ? "bg-blue-600" : "bg-gray-300 dark:bg-gray-600"
}`}
aria-checked={enabled}
role="switch"
>
<span
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
enabled ? "translate-x-6" : "translate-x-1"
}`}
/>
</button>
</div>
);
}
export { default } from "../../../src/components/ui/ToggleSwitch";
export type { ToggleSwitchProps as ToggleProps } from "../../../src/components/ui/ToggleSwitch";
8 changes: 4 additions & 4 deletions entrypoints/popup/components/__tests__/Toggle.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ describe("Toggle", () => {
expect(screen.getByRole("switch")).toHaveAttribute("aria-checked", "false");
});

it("has bg-blue-600 when enabled", () => {
it("has bg-primary when enabled", () => {
render(<Toggle enabled onChange={vi.fn()} label="Test" />);
expect(screen.getByRole("switch")).toHaveClass("bg-blue-600");
expect(screen.getByRole("switch")).toHaveClass("bg-primary");
});

it("has bg-gray-300 when disabled", () => {
it("has bg-muted when disabled", () => {
render(<Toggle enabled={false} onChange={vi.fn()} label="Test" />);
expect(screen.getByRole("switch")).toHaveClass("bg-gray-300");
expect(screen.getByRole("switch")).toHaveClass("bg-muted");
});
});
42 changes: 42 additions & 0 deletions entrypoints/popup/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,48 @@
@tailwind components;
@tailwind utilities;

/* beUI Design Tokens - Light Mode */
:root {
--background: 0 0% 100%;
--foreground: 0 0% 3.6%;
--card: 0 0% 100%;
--card-foreground: 0 0% 3.6%;
--primary: 217 92% 59%;
--primary-foreground: 210 40% 98%;
--secondary: 217 32% 17%;
--secondary-foreground: 210 40% 98%;
--destructive: 0 84% 60%;
--destructive-foreground: 210 40% 98%;
--muted: 210 40% 96%;
--muted-foreground: 215 16% 47%;
--accent: 16 86% 67%;
--accent-foreground: 210 40% 98%;
--border: 214 32% 91%;
--input: 214 32% 91%;
--ring: 217 92% 59%;
}

/* beUI Design Tokens - Dark Mode */
.dark {
--background: 222 84% 5%;
--foreground: 210 40% 98%;
--card: 0 0% 13%;
--card-foreground: 210 40% 98%;
--primary: 217 92% 59%;
--primary-foreground: 222 47% 11%;
--secondary: 217 92% 59%;
--secondary-foreground: 222 47% 11%;
--destructive: 0 63% 31%;
--destructive-foreground: 210 40% 98%;
--muted: 0 0% 20%;
--muted-foreground: 215 20% 65%;
--accent: 16 86% 67%;
--accent-foreground: 222 47% 11%;
--border: 0 0% 20%;
--input: 0 0% 13%;
--ring: 217 92% 59%;
}

* {
margin: 0;
padding: 0;
Expand Down
Loading