From 92896ddf3f43cfa24d3b53d7b8b6c26f9b2b0c61 Mon Sep 17 00:00:00 2001 From: MananTank Date: Sat, 16 Aug 2025 12:58:24 +0000 Subject: [PATCH] Create shared UI library - @workspace/ui (#7855) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit created a @workspace/ui package - shared by dashboard and playground. for now it only has button component for testing --- ## PR-Codex overview This PR focuses on refactoring the `postcss` and `tailwind` configurations across multiple applications to centralize styles in the `@workspace/ui` package, along with various updates to components and layouts. ### Detailed summary - Deleted `postcss.config.js` files and replaced them with `postcss.config.mjs` referencing `@workspace/ui`. - Updated CSS imports to use `@workspace/ui/global.css`. - Added `tailwindcss` configuration in `@workspace/ui` and updated references. - Introduced new utility functions in `packages/ui/src/lib/utils.ts`. - Modified component imports to utilize centralized components from `@workspace/ui`. - Updated `package.json` files to include `@workspace/ui` as a dependency. - Refactored button components to use a centralized button definition in `@workspace/ui`. - Adjusted TypeScript configurations to align with the new structure. - Enhanced global styles and configurations across multiple applications to ensure consistency. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` ## Summary by CodeRabbit - New Features - Added a shared UI package and unified Button component used across apps. - Style - Apps now load shared global styles and animations from the UI package; global CSS and theme tokens consolidated. - Bug Fixes - AI chat header uses a safe fallback when the client ID is missing. - Refactor - Replaced local UI implementations with shared re-exports and composed Tailwind configs. - Chores - Added UI package dependencies, PostCSS delegation, and enabled Next.js transpilation for the UI package; lint/scan ignore lists updated. --- apps/dashboard/knip.json | 5 +- apps/dashboard/next.config.ts | 2 +- apps/dashboard/package.json | 1 + apps/dashboard/postcss.config.js | 6 - apps/dashboard/postcss.config.mjs | 1 + .../SponsoredTransactionsTable.tsx | 2 +- apps/dashboard/src/@/components/ui/button.tsx | 86 +--------- .../(marketplace)/components/types.ts | 3 +- .../claim-conditions-form/hooks.ts | 26 ++- apps/dashboard/src/app/(app)/layout.tsx | 2 +- .../tokens/create/_common/chain-overview.tsx | 2 +- .../create/token/create-token-page-impl.tsx | 2 +- apps/dashboard/src/app/bridge/layout.tsx | 2 +- apps/dashboard/src/app/login/layout.tsx | 2 +- apps/dashboard/src/app/pay/layout.tsx | 2 +- apps/dashboard/tailwind.config.ts | 13 ++ apps/dashboard/tsconfig.json | 8 +- apps/playground-web/knip.json | 2 +- apps/playground-web/next.config.mjs | 5 +- apps/playground-web/package.json | 1 + apps/playground-web/postcss.config.js | 6 - apps/playground-web/postcss.config.mjs | 1 + apps/playground-web/src/app/ai/api/chat.ts | 2 +- apps/playground-web/src/app/ai/api/types.ts | 16 -- .../src/app/ai/components/ChatPageContent.tsx | 41 +---- apps/playground-web/src/app/globals.css | 156 ----------------- apps/playground-web/src/app/layout.tsx | 2 +- .../src/components/ui/button.tsx | 66 +------- apps/playground-web/tailwind.config.ts | 120 ++----------- apps/portal/knip.json | 24 +-- apps/portal/next.config.mjs | 5 +- apps/portal/package.json | 1 + apps/portal/src/app/globals.css | 157 ------------------ apps/portal/src/app/layout.tsx | 1 + apps/portal/src/components/ui/button.tsx | 65 +------- apps/portal/tailwind.config.ts | 142 ++++------------ apps/wallet-ui/next.config.mjs | 4 - .../src/exports/extensions/marketplace.ts | 3 + packages/thirdweb/src/exports/utils.ts | 6 + packages/ui/package.json | 36 ++++ packages/ui/postcss.config.mjs | 9 + packages/ui/src/components/button.tsx | 82 +++++++++ .../dashboard => packages/ui}/src/global.css | 6 +- packages/ui/src/lib/utils.ts | 6 + .../ui/tailwind.config.ts | 15 +- packages/ui/tsconfig.json | 28 ++++ pnpm-lock.yaml | 58 ++++++- 47 files changed, 350 insertions(+), 881 deletions(-) delete mode 100644 apps/dashboard/postcss.config.js create mode 100644 apps/dashboard/postcss.config.mjs create mode 100644 apps/dashboard/tailwind.config.ts delete mode 100644 apps/playground-web/postcss.config.js create mode 100644 apps/playground-web/postcss.config.mjs delete mode 100644 apps/playground-web/src/app/globals.css create mode 100644 packages/ui/package.json create mode 100644 packages/ui/postcss.config.mjs create mode 100644 packages/ui/src/components/button.tsx rename {apps/dashboard => packages/ui}/src/global.css (98%) create mode 100644 packages/ui/src/lib/utils.ts rename apps/dashboard/tailwind.config.js => packages/ui/tailwind.config.ts (91%) create mode 100644 packages/ui/tsconfig.json diff --git a/apps/dashboard/knip.json b/apps/dashboard/knip.json index 022ab47085e..096ded6dc71 100644 --- a/apps/dashboard/knip.json +++ b/apps/dashboard/knip.json @@ -10,8 +10,11 @@ "ignoreDependencies": [ "@thirdweb-dev/service-utils", "@thirdweb-dev/vault-sdk", + "thirdweb", "@types/color", - "fast-xml-parser" + "fast-xml-parser", + "@workspace/ui", + "tailwindcss-animate" ], "next": true, "project": ["src/**"] diff --git a/apps/dashboard/next.config.ts b/apps/dashboard/next.config.ts index f316d4cffd4..abf6fb05c1f 100644 --- a/apps/dashboard/next.config.ts +++ b/apps/dashboard/next.config.ts @@ -115,6 +115,7 @@ const SENTRY_OPTIONS: SentryBuildOptions = { const FRAMER_ADDITIONAL_LANGUAGES = ["es"]; const baseNextConfig: NextConfig = { + transpilePackages: ["@workspace/ui"], eslint: { ignoreDuringBuilds: true, }, @@ -196,7 +197,6 @@ const baseNextConfig: NextConfig = { ]), ]; }, - serverExternalPackages: ["pino-pretty"], }; function getConfig(): NextConfig { diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index 88e7161bd96..555fea976a0 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -28,6 +28,7 @@ "@thirdweb-dev/vault-sdk": "workspace:*", "@vercel/functions": "2.2.2", "@vercel/og": "^0.6.8", + "@workspace/ui": "workspace:*", "abitype": "1.0.8", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", diff --git a/apps/dashboard/postcss.config.js b/apps/dashboard/postcss.config.js deleted file mode 100644 index 4df9d3ba36d..00000000000 --- a/apps/dashboard/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: { - autoprefixer: {}, - tailwindcss: {}, - }, -}; diff --git a/apps/dashboard/postcss.config.mjs b/apps/dashboard/postcss.config.mjs new file mode 100644 index 00000000000..1f1ca64b29b --- /dev/null +++ b/apps/dashboard/postcss.config.mjs @@ -0,0 +1 @@ +export { default } from "@workspace/ui/postcss.config"; diff --git a/apps/dashboard/src/@/components/sponsored-transactions-table/SponsoredTransactionsTable.tsx b/apps/dashboard/src/@/components/sponsored-transactions-table/SponsoredTransactionsTable.tsx index 5dd626d11d5..4c62235330f 100644 --- a/apps/dashboard/src/@/components/sponsored-transactions-table/SponsoredTransactionsTable.tsx +++ b/apps/dashboard/src/@/components/sponsored-transactions-table/SponsoredTransactionsTable.tsx @@ -1,7 +1,7 @@ "use client"; import { keepPreviousData, useQuery } from "@tanstack/react-query"; import { useState } from "react"; -import type { ThirdwebClient } from "thirdweb/dist/types/client/client"; +import type { ThirdwebClient } from "thirdweb"; import { analyticsServerProxy } from "@/actions/proxies"; import { type SponsoredTransaction, diff --git a/apps/dashboard/src/@/components/ui/button.tsx b/apps/dashboard/src/@/components/ui/button.tsx index 42e7d3e92b8..e212fca06f3 100644 --- a/apps/dashboard/src/@/components/ui/button.tsx +++ b/apps/dashboard/src/@/components/ui/button.tsx @@ -1,83 +1,3 @@ -import { Slot } from "@radix-ui/react-slot"; -import { cva, type VariantProps } from "class-variance-authority"; -import * as React from "react"; - -import { cn } from "@/lib/utils"; - -const buttonVariants = cva( - "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", - { - defaultVariants: { - size: "default", - variant: "default", - }, - variants: { - size: { - default: "h-10 px-4 py-2", - icon: "h-10 w-10", - lg: "h-11 rounded-md px-8", - sm: "h-9 rounded-md px-3", - }, - variant: { - default: "bg-foreground text-background hover:bg-foreground/90", - destructive: - "bg-destructive hover:bg-destructive/90 text-destructive-foreground ", - ghost: "hover:bg-accent hover:text-accent-foreground", - link: "underline-offset-4 hover:underline", - outline: - "border border-input bg-transparent hover:bg-accent hover:text-accent-foreground", - primary: "bg-primary hover:bg-primary/90 text-primary-foreground ", - secondary: - "bg-secondary hover:bg-secondary/80 text-secondary-foreground ", - upsell: - "bg-green-600 text-white hover:bg-green-700 shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 transition-all duration-200", - }, - }, - }, -); - -export interface ButtonProps - extends React.ButtonHTMLAttributes, - VariantProps { - asChild?: boolean; -} - -const Button = React.forwardRef( - ({ className, variant, size, asChild = false, disabled, ...props }, ref) => { - const Comp = asChild ? Slot : "button"; - - // "button" elements automatically handle the `disabled` attribute. - // For non-button elements rendered via `asChild` (e.g. ), we still want - // to visually convey the disabled state and prevent user interaction. - // We do that by conditionally adding the same utility classes that the - // `disabled:` pseudo-variant would normally apply and by setting - // `aria-disabled` for accessibility. - const disabledClass = disabled ? "pointer-events-none opacity-50" : ""; - - const btnOnlyProps = - Comp === "button" - ? { - type: - (props as React.ButtonHTMLAttributes).type || - ("button" as const), - } - : undefined; - - return ( - - ); - }, -); -Button.displayName = "Button"; - -export { Button, buttonVariants }; +"use client"; +export type { ButtonProps } from "@workspace/ui/components/button"; +export { Button, buttonVariants } from "@workspace/ui/components/button"; diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/types.ts b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/types.ts index 722a289c4d5..68e842d8feb 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/types.ts +++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/types.ts @@ -1,5 +1,4 @@ -// FIXME: export listing status type -import type { ListingStatus } from "thirdweb/dist/types/extensions/marketplace/types"; +import type { ListingStatus } from "thirdweb/extensions/marketplace"; export const LISTING_STATUS: Record = { ACTIVE: "Active", diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/hooks.ts b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/hooks.ts index 3c1e991731b..d308992eba8 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/hooks.ts +++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/hooks.ts @@ -5,13 +5,12 @@ import { toUnits, } from "thirdweb"; import { getContract } from "thirdweb/contract"; -import type { OverrideEntry } from "thirdweb/dist/types/utils/extensions/drops/types"; -import type { Prettify } from "thirdweb/dist/types/utils/type-utils"; import { getContractMetadata } from "thirdweb/extensions/common"; import * as ERC20Ext from "thirdweb/extensions/erc20"; import * as ERC721Ext from "thirdweb/extensions/erc721"; import * as ERC1155Ext from "thirdweb/extensions/erc1155"; import { download } from "thirdweb/storage"; +import type { OverrideEntry } from "thirdweb/utils"; import { maxUint256 } from "thirdweb/utils"; import type { z } from "zod"; import type { @@ -26,20 +25,15 @@ type SnapshotEntry = { currencyAddress?: string | undefined; }; -type CombinedClaimCondition = Prettify< - Omit< - LegacyClaimCondition, - "price" | "waitInSeconds" | "availableSupply" | "currencyMetadata" - > & { - price: bigint; - currencyMetadata: Omit< - LegacyClaimCondition["currencyMetadata"], - "value" - > & { - value: bigint; - }; - } ->; +type CombinedClaimCondition = Omit< + LegacyClaimCondition, + "price" | "waitInSeconds" | "availableSupply" | "currencyMetadata" +> & { + price: bigint; + currencyMetadata: Omit & { + value: bigint; + }; +}; type Options = | { diff --git a/apps/dashboard/src/app/(app)/layout.tsx b/apps/dashboard/src/app/(app)/layout.tsx index 70321bf96b3..6bdc11be91d 100644 --- a/apps/dashboard/src/app/(app)/layout.tsx +++ b/apps/dashboard/src/app/(app)/layout.tsx @@ -1,4 +1,4 @@ -import "../../global.css"; +import "@workspace/ui/global.css"; import type { Metadata } from "next"; import { Inter } from "next/font/google"; import NextTopLoader from "nextjs-toploader"; diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/_common/chain-overview.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/_common/chain-overview.tsx index 714a1c94434..82a8d1b72b4 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/_common/chain-overview.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/_common/chain-overview.tsx @@ -1,4 +1,4 @@ -import type { ThirdwebClient } from "thirdweb/dist/types/client/client"; +import type { ThirdwebClient } from "thirdweb"; import { useAllChainsData } from "@/hooks/chains/allChains"; import { ChainIconClient } from "@/icons/ChainIcon"; diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/token/create-token-page-impl.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/token/create-token-page-impl.tsx index 58fc8352f71..c4b0e2b1465 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/token/create-token-page-impl.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/token/create-token-page-impl.tsx @@ -11,7 +11,6 @@ import { toWei, } from "thirdweb"; import { deployERC20Contract } from "thirdweb/deploys"; -import type { ClaimConditionsInput } from "thirdweb/dist/types/utils/extensions/drops/types"; import { approve, claimTo, @@ -25,6 +24,7 @@ import { distributeToken, getDeployedEntrypointERC20, } from "thirdweb/tokens"; +import type { ClaimConditionsInput } from "thirdweb/utils"; import { create7702MinimalAccount } from "thirdweb/wallets/smart"; import { revalidatePathAction } from "@/actions/revalidate"; import { reportContractDeployed } from "@/analytics/report"; diff --git a/apps/dashboard/src/app/bridge/layout.tsx b/apps/dashboard/src/app/bridge/layout.tsx index 74495433229..bf13c0400a4 100644 --- a/apps/dashboard/src/app/bridge/layout.tsx +++ b/apps/dashboard/src/app/bridge/layout.tsx @@ -1,6 +1,6 @@ import { Inter } from "next/font/google"; import { cn } from "@/lib/utils"; -import "../../global.css"; +import "@workspace/ui/global.css"; import { BridgeProviders } from "./components/client/Providers.client"; const fontSans = Inter({ diff --git a/apps/dashboard/src/app/login/layout.tsx b/apps/dashboard/src/app/login/layout.tsx index 0dfcb195e34..b7a966ccef7 100644 --- a/apps/dashboard/src/app/login/layout.tsx +++ b/apps/dashboard/src/app/login/layout.tsx @@ -1,4 +1,4 @@ -import "../../global.css"; +import "@workspace/ui/global.css"; import type { Metadata } from "next"; import { Inter } from "next/font/google"; import NextTopLoader from "nextjs-toploader"; diff --git a/apps/dashboard/src/app/pay/layout.tsx b/apps/dashboard/src/app/pay/layout.tsx index 70a18849d42..109adb2818d 100644 --- a/apps/dashboard/src/app/pay/layout.tsx +++ b/apps/dashboard/src/app/pay/layout.tsx @@ -1,4 +1,4 @@ -import "../../global.css"; +import "@workspace/ui/global.css"; import { Inter } from "next/font/google"; import { ThemeProvider } from "next-themes"; import { cn } from "@/lib/utils"; diff --git a/apps/dashboard/tailwind.config.ts b/apps/dashboard/tailwind.config.ts new file mode 100644 index 00000000000..fc98fb15048 --- /dev/null +++ b/apps/dashboard/tailwind.config.ts @@ -0,0 +1,13 @@ +import tailwindConfig from "@workspace/ui/tailwind.config"; +import type { Config } from "tailwindcss"; + +const config: Config = { + ...tailwindConfig, + content: [ + "./src/**/*.{ts,tsx}", + // add contents of ui package + "../../packages/ui/src/**/*.{ts,tsx}", + ], +}; + +export default config; diff --git a/apps/dashboard/tsconfig.json b/apps/dashboard/tsconfig.json index f0d89427531..52872b08c43 100644 --- a/apps/dashboard/tsconfig.json +++ b/apps/dashboard/tsconfig.json @@ -9,11 +9,11 @@ "isolatedModules": true, "jsx": "preserve", "lib": ["dom", "dom.iterable", "esnext"], - "module": "esnext", - "moduleResolution": "node", + "module": "ESNext", + "moduleResolution": "Bundler", + "moduleDetection": "force", "noEmit": true, "noUncheckedIndexedAccess": true, - "plugins": [ { "name": "next" @@ -22,7 +22,7 @@ "resolveJsonModule": true, "skipLibCheck": true, "strict": true, - "target": "ES2020" + "target": "ES2022" }, "exclude": ["node_modules", ".next", "lib/__generated__"], "include": [ diff --git a/apps/playground-web/knip.json b/apps/playground-web/knip.json index 75ff819143e..5953eef3678 100644 --- a/apps/playground-web/knip.json +++ b/apps/playground-web/knip.json @@ -1,7 +1,7 @@ { "$schema": "https://unpkg.com/knip@5/schema.json", "ignore": ["src/components/ui/**", "src/app/insight/utils.ts"], - "ignoreDependencies": ["server-only"], + "ignoreDependencies": ["server-only", "@workspace/ui", "tailwindcss-animate"], "next": true, "project": ["src/**"] } diff --git a/apps/playground-web/next.config.mjs b/apps/playground-web/next.config.mjs index cb03b64c470..79d6a137e7a 100644 --- a/apps/playground-web/next.config.mjs +++ b/apps/playground-web/next.config.mjs @@ -37,6 +37,7 @@ const securityHeaders = [ /** @type {import('next').NextConfig} */ const nextConfig = { + transpilePackages: ["@workspace/ui"], eslint: { ignoreDuringBuilds: true, }, @@ -164,10 +165,6 @@ const nextConfig = { }, ]; }, - webpack: (config) => { - config.externals.push("pino-pretty", "lokijs", "encoding"); - return config; - }, }; export default nextConfig; diff --git a/apps/playground-web/package.json b/apps/playground-web/package.json index 4b2ccdc88e6..2488b74d5a7 100644 --- a/apps/playground-web/package.json +++ b/apps/playground-web/package.json @@ -15,6 +15,7 @@ "@radix-ui/react-switch": "^1.2.5", "@radix-ui/react-tooltip": "1.2.7", "@tanstack/react-query": "5.81.5", + "@workspace/ui": "workspace:*", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "date-fns": "4.1.0", diff --git a/apps/playground-web/postcss.config.js b/apps/playground-web/postcss.config.js deleted file mode 100644 index 4df9d3ba36d..00000000000 --- a/apps/playground-web/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: { - autoprefixer: {}, - tailwindcss: {}, - }, -}; diff --git a/apps/playground-web/postcss.config.mjs b/apps/playground-web/postcss.config.mjs new file mode 100644 index 00000000000..1f1ca64b29b --- /dev/null +++ b/apps/playground-web/postcss.config.mjs @@ -0,0 +1 @@ +export { default } from "@workspace/ui/postcss.config"; diff --git a/apps/playground-web/src/app/ai/api/chat.ts b/apps/playground-web/src/app/ai/api/chat.ts index 8a5e6c14028..05515833840 100644 --- a/apps/playground-web/src/app/ai/api/chat.ts +++ b/apps/playground-web/src/app/ai/api/chat.ts @@ -63,7 +63,7 @@ export async function promptNebula(params: { const events = await stream(`${API_URL}/ai/chat`, { body: JSON.stringify(body), headers: { - "x-client-id": process.env.NEXT_PUBLIC_THIRDWEB_CLIENT_ID!, + "x-client-id": process.env.NEXT_PUBLIC_THIRDWEB_CLIENT_ID || "", "Content-Type": "application/json", }, method: "POST", diff --git a/apps/playground-web/src/app/ai/api/types.ts b/apps/playground-web/src/app/ai/api/types.ts index 3ff482b787b..3f4402cb6de 100644 --- a/apps/playground-web/src/app/ai/api/types.ts +++ b/apps/playground-web/src/app/ai/api/types.ts @@ -1,8 +1,3 @@ -type SessionContextFilter = { - chain_ids: string[] | null; - wallet_address: string | null; -}; - type NebulaUserMessageContentItem = | { type: "image"; @@ -26,17 +21,6 @@ export type NebulaUserMessage = { content: NebulaUserMessageContent; }; -export type NebulaSessionHistoryMessage = - | { - role: "assistant" | "action" | "image"; - content: string; - timestamp: number; - } - | { - role: "user"; - content: NebulaUserMessageContent | string; - }; - export type NebulaTxData = { chainId: number; data: `0x${string}`; diff --git a/apps/playground-web/src/app/ai/components/ChatPageContent.tsx b/apps/playground-web/src/app/ai/components/ChatPageContent.tsx index c2869ff7a7f..a2338ba1e50 100644 --- a/apps/playground-web/src/app/ai/components/ChatPageContent.tsx +++ b/apps/playground-web/src/app/ai/components/ChatPageContent.tsx @@ -32,10 +32,7 @@ import { Img } from "../../../components/ui/Img"; import { Spinner } from "../../../components/ui/Spinner/Spinner"; import { THIRDWEB_CLIENT } from "../../../lib/client"; import { type NebulaContext, promptNebula } from "../api/chat"; -import type { - NebulaSessionHistoryMessage, - NebulaUserMessage, -} from "../api/types"; +import type { NebulaUserMessage } from "../api/types"; import { examplePrompts } from "../data/examplePrompts"; import { resolveSchemeWithErrorHandler } from "./resolveSchemeWithErrorHandler"; @@ -585,7 +582,7 @@ function SimpleChats(props: { enableAutoScroll: boolean; sendMessage: (message: NebulaUserMessage) => void; }) { - const { messages, setEnableAutoScroll, enableAutoScroll } = props; + const { messages, enableAutoScroll } = props; const scrollAnchorRef = useRef(null); // auto scroll to bottom when messages change @@ -1038,37 +1035,3 @@ function handleNebulaPromptError(params: { return newMessages; }); } - -function parseHistoryToMessages(history: NebulaSessionHistoryMessage[]) { - const messages: ChatMessage[] = []; - - for (const message of history) { - switch (message.role) { - case "user": { - messages.push({ - content: - typeof message.content === "string" - ? [ - { - text: message.content, - type: "text", - }, - ] - : message.content, - type: message.role, - }); - break; - } - - case "assistant": { - messages.push({ - request_id: undefined, - text: message.content, - type: message.role, - }); - } - } - } - - return messages; -} diff --git a/apps/playground-web/src/app/globals.css b/apps/playground-web/src/app/globals.css deleted file mode 100644 index 0dd345e81e7..00000000000 --- a/apps/playground-web/src/app/globals.css +++ /dev/null @@ -1,156 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -@layer base { - :root { - /* bg - neutral */ - --background: 0 0% 98%; - --popover: 0 0% 100%; - --card: 0 0% 100%; - --secondary: 0 0% 90%; - --muted: 0 0% 93%; - --accent: 0 0% 93%; - --inverted: 0 0% 4%; - - /* bg - colorful */ - --primary: 221 83% 54%; - --destructive: 360 72% 51%; - - /* Text */ - --foreground: 0 0% 4%; - --card-foreground: 0 0% 4%; - --popover-foreground: 240 10% 4%; - --primary-foreground: 0 0% 100%; - --secondary-foreground: 0 0% 4%; - --muted-foreground: 0 0% 40%; - --accent-foreground: 0 0% 9%; - --destructive-foreground: 0 0% 100%; - --inverted-foreground: 0 0% 100%; - --link-foreground: 221.21deg 83.19% 53.33%; - --success-text: 142.09 70.56% 35.29%; - --warning-text: 38 92% 40%; - --destructive-text: 360 72% 60%; - - /* Borders */ - --border: 0 0% 85%; - --active-border: 0 0% 70%; - --input: 0 0% 85%; - --ring: 0 0% 80%; - - /* Others */ - --radius: 0.5rem; - - /* Sidebar */ - --sidebar-background: 0 0% 98%; - --sidebar-foreground: 0 0% 4%; - --sidebar-primary: 221 83% 54%; - --sidebar-primary-foreground: 0 0% 100%; - --sidebar-accent: 0 0% 93%; - --sidebar-accent-foreground: 0 0% 9%; - --sidebar-border: 0 0% 85%; - --sidebar-ring: 0 0% 80%; - } - - .dark { - /* bg - neutral */ - --background: 0 0% 0%; - --card: 0 0% 3.92%; - --popover: 0 0% 0%; - --secondary: 0 0% 11%; - --muted: 0 0% 11%; - --accent: 0 0% 11%; - --inverted: 0 0% 100%; - - /* bg - colorful */ - --primary: 221 83% 54%; - --destructive: 360 72% 51%; - - /* Text */ - --foreground: 0 0% 98%; - --card-foreground: 0 0% 98%; - --popover-foreground: 0 0% 98%; - --primary-foreground: 0 0% 100%; - --secondary-foreground: 0 0% 98%; - --muted-foreground: 0 0% 63%; - --accent-foreground: 0 0% 98%; - --destructive-foreground: 0 0% 100%; - --link-foreground: 215.88 100% 65%; - --warning-text: 38 92% 50%; - --destructive-text: 360 72% 55%; - --success-text: 142 75% 50%; - --inverted-foreground: 0 0% 0%; - - /* Borders */ - --border: 0 0% 15%; - --active-border: 0 0% 22%; - --ring: 0 0% 30%; - --input: 0 0% 15%; - - /* sidebar */ - --sidebar-background: 0 0% 0%; - --sidebar-foreground: 0 0% 98%; - --sidebar-primary: 221 83% 54%; - --sidebar-primary-foreground: 0 0% 100%; - --sidebar-accent: 0 0% 11%; - --sidebar-accent-foreground: 0 0% 98%; - --sidebar-border: 0 0% 15%; - --sidebar-ring: 0 0% 30%; - } -} - -@layer base { - * { - @apply border-border; - } - body { - @apply bg-background text-foreground; - } -} - -.shiki, -.shiki span { - background-color: transparent !important; -} - -.dark .shiki, -.dark .shiki span { - color: var(--shiki-dark) !important; - /* Optional, if you also want font styles */ - font-style: var(--shiki-dark-font-style) !important; - font-weight: var(--shiki-dark-font-weight) !important; - text-decoration: var(--shiki-dark-text-decoration) !important; -} - -/* Fix colors on auto-filled inputs */ -input:-webkit-autofill, -input:-webkit-autofill:hover, -input:-webkit-autofill:focus, -input:-webkit-autofill:active { - /* Revert text color */ - -webkit-text-fill-color: hsl(var(--foreground)) !important; - color: hsl(var(--foreground)) !important; - caret-color: hsl(var(--foreground)) !important; - - /* Revert background color */ - transition: background-color 5000s ease-in-out 0s; -} - -@layer utilities { - /* Hide scrollbar for Chrome, Safari and Opera */ - .no-scrollbar::-webkit-scrollbar, - .no-scrollbar *::-webkit-scrollbar { - display: none; - } - /* Hide scrollbar for IE, Edge and Firefox */ - .no-scrollbar, - .no-scrollbar * { - -ms-overflow-style: none; /* IE and Edge */ - scrollbar-width: none; /* Firefox */ - } -} - -* { - scrollbar-width: thin; - scrollbar-color: hsl(var(--muted)) transparent; -} diff --git a/apps/playground-web/src/app/layout.tsx b/apps/playground-web/src/app/layout.tsx index c787f2631cb..1b45ae1fb21 100644 --- a/apps/playground-web/src/app/layout.tsx +++ b/apps/playground-web/src/app/layout.tsx @@ -4,7 +4,7 @@ import { metadataBase } from "@/lib/constants"; import { cn } from "@/lib/utils"; import { AppSidebarLayout } from "./AppSidebar"; import { Providers } from "./providers"; -import "./globals.css"; +import "@workspace/ui/global.css"; import { ThemeProvider } from "next-themes"; import NextTopLoader from "nextjs-toploader"; import { Toaster } from "sonner"; diff --git a/apps/playground-web/src/components/ui/button.tsx b/apps/playground-web/src/components/ui/button.tsx index abfefab7181..e212fca06f3 100644 --- a/apps/playground-web/src/components/ui/button.tsx +++ b/apps/playground-web/src/components/ui/button.tsx @@ -1,63 +1,3 @@ -import { Slot } from "@radix-ui/react-slot"; -import { cva, type VariantProps } from "class-variance-authority"; -import * as React from "react"; - -import { cn } from "@/lib/utils"; - -const buttonVariants = cva( - "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", - { - defaultVariants: { - size: "default", - variant: "default", - }, - variants: { - size: { - default: "h-10 px-4 py-2", - icon: "h-10 w-10", - lg: "h-11 rounded-md px-8", - sm: "h-9 rounded-md px-3", - }, - variant: { - default: "bg-foreground text-background hover:bg-foreground/90", - destructive: - "bg-destructive hover:bg-destructive/90 text-semibold text-destructive-foreground ", - ghost: "hover:bg-accent text-semibold hover:text-accent-foreground", - link: "text-primary underline-offset-4 hover:underline text-semibold", - outline: - "border border-input bg-transparent hover:bg-accent hover:text-accent-foreground text-semibold", - primary: - "bg-primary hover:bg-primary/90 text-semibold text-primary-foreground ", - secondary: - "bg-secondary hover:bg-secondary/80 text-semibold text-secondary-foreground ", - upsell: - "bg-gradient-to-r from-purple-500 to-pink-500 text-white hover:from-purple-600 hover:to-pink-600 shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 transition-all duration-200", - }, - }, - }, -); - -export interface ButtonProps - extends React.ButtonHTMLAttributes, - VariantProps { - asChild?: boolean; -} - -const Button = React.forwardRef( - ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : "button"; - const btnOnlyProps = - Comp === "button" ? { type: "button" as const } : undefined; - return ( - - ); - }, -); -Button.displayName = "Button"; - -export { Button, buttonVariants }; +"use client"; +export type { ButtonProps } from "@workspace/ui/components/button"; +export { Button, buttonVariants } from "@workspace/ui/components/button"; diff --git a/apps/playground-web/tailwind.config.ts b/apps/playground-web/tailwind.config.ts index f02b39cc456..fc98fb15048 100644 --- a/apps/playground-web/tailwind.config.ts +++ b/apps/playground-web/tailwind.config.ts @@ -1,111 +1,13 @@ -import { fontFamily } from "tailwindcss/defaultTheme"; +import tailwindConfig from "@workspace/ui/tailwind.config"; +import type { Config } from "tailwindcss"; -/** @type {import('tailwindcss').Config} */ -module.exports = { - content: ["./src/**/*.{ts,tsx}"], - darkMode: ["class"], - plugins: [require("tailwindcss-animate")], - prefix: "", - theme: { - container: { - center: true, - padding: { - // 16px - DEFAULT: "1rem", - // 24px - lg: "1.5rem", - }, - screens: { - "2xl": "1400px", - }, - }, - extend: { - animation: { - "accordion-down": "accordion-down 0.2s ease-out", - "accordion-up": "accordion-up 0.2s ease-out", - }, - borderRadius: { - lg: "var(--radius)", - md: "calc(var(--radius) - 2px)", - sm: "calc(var(--radius) - 4px)", - }, - colors: { - accent: { - DEFAULT: "hsl(var(--accent))", - foreground: "hsl(var(--accent-foreground))", - }, - "active-border": "hsl(var(--active-border))", - background: "hsl(var(--background))", - border: "hsl(var(--border))", - card: { - DEFAULT: "hsl(var(--card))", - foreground: "hsl(var(--card-foreground))", - }, - code: { - DEFAULT: "hsl(var(--code))", - }, - destructive: { - DEFAULT: "hsl(var(--destructive))", - foreground: "hsl(var(--destructive-foreground))", - text: "hsl(var(--destructive-text))", - }, - foreground: "hsl(var(--foreground))", - input: "hsl(var(--input))", - inverted: { - DEFAULT: "hsl(var(--inverted))", - foreground: "hsl(var(--inverted-foreground))", - }, - link: { - foreground: "hsl(var(--link-foreground))", - }, - muted: { - DEFAULT: "hsl(var(--muted))", - foreground: "hsl(var(--muted-foreground))", - }, - popover: { - DEFAULT: "hsl(var(--popover))", - foreground: "hsl(var(--popover-foreground))", - }, - primary: { - DEFAULT: "hsl(var(--primary))", - foreground: "hsl(var(--primary-foreground))", - }, - ring: "hsl(var(--ring))", - secondary: { - DEFAULT: "hsl(var(--secondary))", - foreground: "hsl(var(--secondary-foreground))", - }, - sidebar: { - accent: "hsl(var(--sidebar-accent))", - "accent-foreground": "hsl(var(--sidebar-accent-foreground))", - border: "hsl(var(--sidebar-border))", - DEFAULT: "hsl(var(--sidebar-background))", - foreground: "hsl(var(--sidebar-foreground))", - primary: "hsl(var(--sidebar-primary))", - "primary-foreground": "hsl(var(--sidebar-primary-foreground))", - ring: "hsl(var(--sidebar-ring))", - }, - success: { - text: "hsl(var(--success-text))", - }, - warning: { - text: "hsl(var(--warning-text))", - }, - }, - fontFamily: { - mono: ["var(--font-mono)", ...fontFamily.mono], - 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" }, - }, - }, - }, - }, +const config: Config = { + ...tailwindConfig, + content: [ + "./src/**/*.{ts,tsx}", + // add contents of ui package + "../../packages/ui/src/**/*.{ts,tsx}", + ], }; + +export default config; diff --git a/apps/portal/knip.json b/apps/portal/knip.json index 1f43b68d66c..9d3fe0e6cf1 100644 --- a/apps/portal/knip.json +++ b/apps/portal/knip.json @@ -2,31 +2,25 @@ "$schema": "https://unpkg.com/knip@5/schema.json", "entry": [ "next.config.{js,ts,cjs,mjs}", - "{instrumentation,middleware}.{js,ts}", - "app/global-error.{js,jsx,ts,tsx}", - "app/**/{error,layout,loading,not-found,page,template,default}.{js,jsx,ts,tsx,mdx}", "app/**/route.{js,jsx,ts,tsx}", - "app/{manifest,sitemap,robots}.{js,ts}", "app/**/{icon,apple-icon}.{js,jsx,ts,tsx}", - "app/**/{opengraph,twitter}-image.{js,jsx,ts,tsx}", - "pages/**/*.{js,jsx,ts,tsx}", - "src/{instrumentation,middleware}.{js,ts}", - "src/app/global-error.{js,jsx,ts,tsx}", "src/app/**/{error,layout,loading,not-found,page,template,default}.{js,jsx,ts,tsx,mdx}", - "src/app/**/route.{js,jsx,ts,tsx}", - "src/app/{manifest,sitemap,robots}.{js,ts}", - "src/app/**/{icon,apple-icon}.{js,jsx,ts,tsx}", - "src/app/**/{opengraph,twitter}-image.{js,jsx,ts,tsx}", - "src/pages/**/*.{js,jsx,ts,tsx}", "scripts/*.ts" ], - "ignore": ["src/components/ui/**", "src/icons/**", "src/components/others/Banner.tsx"], + "ignore": [ + "src/components/ui/**", + "src/icons/**", + "src/components/others/Banner.tsx" + ], "ignoreBinaries": ["only-allow"], "ignoreDependencies": [ "@thirdweb-dev/chains", "@thirdweb-dev/wallets", "thirdweb", - "@types/flexsearch" + "@workspace/ui", + "@types/flexsearch", + "@radix-ui/react-slot", + "tailwindcss-animate" ], "next": true, "project": ["src/**"] diff --git a/apps/portal/next.config.mjs b/apps/portal/next.config.mjs index 40f4ff35da5..0d078aea601 100644 --- a/apps/portal/next.config.mjs +++ b/apps/portal/next.config.mjs @@ -47,6 +47,7 @@ const withMDX = createMDX({ /** @type {import('next').NextConfig} */ const nextConfig = { + transpilePackages: ["@workspace/ui"], eslint: { ignoreDuringBuilds: true, }, @@ -82,10 +83,6 @@ const nextConfig = { }, ]; }, - webpack: (config) => { - config.externals.push("pino-pretty", "lokijs", "encoding"); - return config; - }, }; export default withMDX(nextConfig); diff --git a/apps/portal/package.json b/apps/portal/package.json index ca4879b1b84..a547d1b2085 100644 --- a/apps/portal/package.json +++ b/apps/portal/package.json @@ -55,6 +55,7 @@ "@types/tryghost__content-api": "^1.3.16", "@typescript-eslint/eslint-plugin": "7.14.1", "@typescript-eslint/parser": "7.14.1", + "@workspace/ui": "workspace:*", "autoprefixer": "^10.4.21", "eslint": "8.57.0", "eslint-config-biome": "1.9.4", diff --git a/apps/portal/src/app/globals.css b/apps/portal/src/app/globals.css index f30a76a1ea5..3529f267d17 100644 --- a/apps/portal/src/app/globals.css +++ b/apps/portal/src/app/globals.css @@ -1,123 +1,3 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -@layer base { - :root { - /* bg - neutral */ - --background: 0 0% 98%; - --popover: 0 0% 100%; - --card: 0 0% 100%; - --secondary: 0 0% 90%; - --muted: 0 0% 97%; - --accent: 0 0% 93%; - --inverted: 0 0% 4%; - - /* bg - colorful */ - --primary: 221 83% 54%; - --destructive: 360 72% 51%; - - /* Text */ - --foreground: 0 0% 4%; - --card-foreground: 0 0% 4%; - --popover-foreground: 240 10% 4%; - --primary-foreground: 0 0% 100%; - --secondary-foreground: 0 0% 4%; - --muted-foreground: 0 0% 40%; - --accent-foreground: 0 0% 9%; - --destructive-foreground: 0 0% 100%; - --inverted-foreground: 0 0% 100%; - --link-foreground: 221.21deg 83.19% 53.33%; - --success-text: 142.09 70.56% 35.29%; - --warning-text: 38 92% 40%; - --destructive-text: 360 72% 60%; - - /* Borders */ - --border: 0 0% 85%; - --active-border: 0 0% 70%; - --input: 0 0% 85%; - --ring: 0 0% 80%; - - /* Others */ - --radius: 0.5rem; - - /* Sidebar */ - --sidebar-background: 0 0% 98%; - --sidebar-foreground: 0 0% 4%; - --sidebar-primary: 221 83% 54%; - --sidebar-primary-foreground: 0 0% 100%; - --sidebar-accent: 0 0% 93%; - --sidebar-accent-foreground: 0 0% 9%; - --sidebar-border: 0 0% 85%; - --sidebar-ring: 0 0% 80%; - } - - .dark { - /* bg - neutral */ - --background: 0 0% 0%; - --card: 0 0% 3.92%; - --popover: 0 0% 0%; - --secondary: 0 0% 11%; - --muted: 0 0% 8%; - --accent: 0 0% 11%; - --inverted: 0 0% 100%; - - /* bg - colorful */ - --primary: 221 83% 54%; - --destructive: 360 72% 51%; - - /* Text */ - --foreground: 0 0% 98%; - --card-foreground: 0 0% 98%; - --popover-foreground: 0 0% 98%; - --primary-foreground: 0 0% 100%; - --secondary-foreground: 0 0% 98%; - --muted-foreground: 0 0% 63%; - --accent-foreground: 0 0% 98%; - --destructive-foreground: 0 0% 100%; - --link-foreground: 215.88 100% 65%; - --warning-text: 38 92% 50%; - --destructive-text: 360 72% 55%; - --success-text: 142 75% 50%; - --inverted-foreground: 0 0% 0%; - - /* Borders */ - --border: 0 0% 15%; - --active-border: 0 0% 22%; - --ring: 0 0% 30%; - --input: 0 0% 15%; - - /* sidebar */ - --sidebar-background: 0 0% 0%; - --sidebar-foreground: 0 0% 98%; - --sidebar-primary: 221 83% 54%; - --sidebar-primary-foreground: 0 0% 100%; - --sidebar-accent: 0 0% 11%; - --sidebar-accent-foreground: 0 0% 98%; - --sidebar-border: 0 0% 15%; - --sidebar-ring: 0 0% 30%; - } -} - -@layer base { - * { - @apply border-border; - } - body { - @apply bg-background text-foreground; - } -} - -@layer utilities { - .no-scrollbar::-webkit-scrollbar { - display: none; - } - .no-scrollbar { - -ms-overflow-style: none; - scrollbar-width: none; - } -} - code span { color: var(--code-light-color); } @@ -131,43 +11,6 @@ code span { background-color: transparent !important; } -.dark .shiki, -.dark .shiki span { - color: var(--shiki-dark) !important; - /* Optional, if you also want font styles */ - font-style: var(--shiki-dark-font-style) !important; - font-weight: var(--shiki-dark-font-weight) !important; - text-decoration: var(--shiki-dark-text-decoration) !important; -} - -/* Fix colors on auto-filled inputs */ -input:-webkit-autofill, -input:-webkit-autofill:hover, -input:-webkit-autofill:focus, -input:-webkit-autofill:active { - /* Revert text color */ - -webkit-text-fill-color: hsl(var(--foreground)) !important; - color: hsl(var(--foreground)) !important; - caret-color: hsl(var(--foreground)) !important; - - /* Revert background color */ - transition: background-color 5000s ease-in-out 0s; -} - -@layer utilities { - /* Hide scrollbar for Chrome, Safari and Opera */ - .no-scrollbar::-webkit-scrollbar, - .no-scrollbar *::-webkit-scrollbar { - display: none; - } - /* Hide scrollbar for IE, Edge and Firefox */ - .no-scrollbar, - .no-scrollbar * { - -ms-overflow-style: none; /* IE and Edge */ - scrollbar-width: none; /* Firefox */ - } -} - .styled-scrollbar::-webkit-scrollbar { width: 0.5rem; height: 0.5rem; diff --git a/apps/portal/src/app/layout.tsx b/apps/portal/src/app/layout.tsx index 705f9e4af29..847870e7d5a 100644 --- a/apps/portal/src/app/layout.tsx +++ b/apps/portal/src/app/layout.tsx @@ -1,3 +1,4 @@ +import "@workspace/ui/global.css"; import "./globals.css"; import { Fira_Code, Inter } from "next/font/google"; import { ThemeProvider } from "next-themes"; diff --git a/apps/portal/src/components/ui/button.tsx b/apps/portal/src/components/ui/button.tsx index 798c7eb334c..e212fca06f3 100644 --- a/apps/portal/src/components/ui/button.tsx +++ b/apps/portal/src/components/ui/button.tsx @@ -1,62 +1,3 @@ -import { Slot } from "@radix-ui/react-slot"; -import { cva, type VariantProps } from "class-variance-authority"; -import * as React from "react"; - -import { cn } from "@/lib/utils"; - -const buttonVariants = cva( - "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", - { - defaultVariants: { - size: "default", - variant: "default", - }, - variants: { - size: { - default: "h-8 px-4 py-2 text-sm", - icon: "size-10", - lg: "h-11 rounded-md px-8", - sm: "h-7 rounded-md px-3 text-sm", - }, - variant: { - default: "bg-foreground text-background hover:bg-foreground/90", - destructive: - "bg-destructive hover:bg-destructive/90 text-destructive-foreground ", - ghost: "hover:bg-accent hover:text-accent-foreground", - link: "text-primary underline-offset-4 hover:underline", - outline: - "border border-input bg-transparent hover:bg-accent hover:text-accent-foreground", - primary: "bg-primary hover:bg-primary/90 text-primary-foreground ", - secondary: - "bg-secondary hover:bg-secondary/80 text-secondary-foreground ", - upsell: - "bg-gradient-to-r from-purple-500 to-pink-500 text-white hover:from-purple-600 hover:to-pink-600 shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 transition-all duration-200", - }, - }, - }, -); - -export interface ButtonProps - extends React.ButtonHTMLAttributes, - VariantProps { - asChild?: boolean; -} - -const Button = React.forwardRef( - ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : "button"; - const btnOnlyProps = - Comp === "button" ? { type: "button" as const } : undefined; - return ( - - ); - }, -); -Button.displayName = "Button"; - -export { Button, buttonVariants }; +"use client"; +export type { ButtonProps } from "@workspace/ui/components/button"; +export { Button, buttonVariants } from "@workspace/ui/components/button"; diff --git a/apps/portal/tailwind.config.ts b/apps/portal/tailwind.config.ts index 9290818eb3e..fb784f424e9 100644 --- a/apps/portal/tailwind.config.ts +++ b/apps/portal/tailwind.config.ts @@ -1,130 +1,44 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { +import tailwindConfig from "@workspace/ui/tailwind.config"; +import type { Config } from "tailwindcss"; + +const config: Config = { + ...tailwindConfig, content: [ "./components/**/*.{ts,tsx}", "./app/**/*.{ts,tsx,mdx}", "./src/**/*.{ts,tsx,mdx}", + // add contents of ui package + "../../packages/ui/src/**/*.{ts,tsx}", ], - darkMode: ["class"], - plugins: [require("tailwindcss-animate")], theme: { - container: { - center: true, - padding: "1rem", - screens: { - "2xl": "1400px", - }, - }, + ...tailwindConfig.theme, extend: { - borderRadius: { - lg: "var(--radius)", - md: "calc(var(--radius) - 2px)", - sm: "calc(var(--radius) - 4px)", - }, - animation: { - "accordion-down": "accordion-down 0.2s ease-out", - "accordion-up": "accordion-up 0.2s ease-out", - "animate-in-slow": "animate-in 0.4s ease", - "text-shimmer": "text-shimmer 1.25s linear infinite", - }, - colors: { - accent: { - DEFAULT: "hsl(var(--accent))", - foreground: "hsl(var(--accent-foreground))", - }, - "active-border": "hsl(var(--active-border))", - background: "hsl(var(--background))", - border: "hsl(var(--border))", - card: { - DEFAULT: "hsl(var(--card))", - foreground: "hsl(var(--card-foreground))", - }, - current: "currentColor", - destructive: { - DEFAULT: "hsl(var(--destructive))", - // destructive-foreground should only be used on destructive bg - foreground: "hsl(var(--destructive-foreground))", - // destructive-text can be used on neutral bg - it's red-ish - text: "hsl(var(--destructive-text))", - }, - foreground: "hsl(var(--foreground))", - input: "hsl(var(--input))", - inverted: { - DEFAULT: "hsl(var(--inverted))", - foreground: "hsl(var(--inverted-foreground))", - }, - link: { - foreground: "hsl(var(--link-foreground))", - }, - muted: { - DEFAULT: "hsl(var(--muted))", - foreground: "hsl(var(--muted-foreground))", - }, - overlay: "hsl(var(--overlay))", - popover: { - DEFAULT: "hsl(var(--popover))", - foreground: "hsl(var(--popover-foreground))", - }, - primary: { - DEFAULT: "hsl(var(--primary))", - foreground: "hsl(var(--primary-foreground))", - }, - ring: "hsl(var(--ring))", - secondary: { - DEFAULT: "hsl(var(--secondary))", - foreground: "hsl(var(--secondary-foreground))", - }, - success: { - // success-text can be used on neutral bg, it's green-ish - text: "hsl(var(--success-text))", - }, - transparent: "transparent", - warning: { - text: "hsl(var(--warning-text))", - }, - }, - keyframes: { - "accordion-down": { - from: { height: 0, opacity: 0 }, - to: { height: "var(--radix-accordion-content-height)", opacity: 1 }, - }, - "accordion-up": { - from: { height: "var(--radix-accordion-content-height)", opacity: 1 }, - to: { height: 0, opacity: 0 }, - }, - "text-shimmer": { - "0%": { backgroundPosition: "100% 50%" }, - "100%": { backgroundPosition: "-100% 50%" }, - }, - }, + ...tailwindConfig.theme?.extend, spacing: { "offset-top": "calc(var(--sticky-top-height) + 18px)", "offset-top-mobile": "calc(var(--sticky-top-height) + 100px)", "sidebar-height": "calc(100vh - var(--sticky-top-height))", "sticky-top-height": "var(--sticky-top-height)", }, - }, - fontFamily: { - mono: ["var(--font-mono)", "monospace"], - sans: ["var(--font-sans)", "sans-serif"], - }, - - zIndex: { - // base - base: 0, - // - codeToken: 5, - codeTokenHighlight: 1, - // - copyCodeButton: 20, - dropdownMenu: 1200, - floatingButton: 1000, - menu: 1100, - modal: 1400, - modalOverlay: 1300, - stickyMobileSidebar: 500, - // - stickyTop: 1000, + zIndex: { + // base + base: "0", + // + codeToken: "5", + codeTokenHighlight: "1", + // + copyCodeButton: "20", + dropdownMenu: "1200", + floatingButton: "1000", + menu: "1100", + modal: "1400", + modalOverlay: "1300", + stickyMobileSidebar: "500", + // + stickyTop: "1000", + }, }, }, }; + +export default config; diff --git a/apps/wallet-ui/next.config.mjs b/apps/wallet-ui/next.config.mjs index 67e8badbfd2..ab38fa060bc 100644 --- a/apps/wallet-ui/next.config.mjs +++ b/apps/wallet-ui/next.config.mjs @@ -74,10 +74,6 @@ const nextConfig = { }, ]; }, - webpack: (config) => { - config.externals.push("pino-pretty", "lokijs", "encoding"); - return config; - }, }; export default nextConfig; diff --git a/packages/thirdweb/src/exports/extensions/marketplace.ts b/packages/thirdweb/src/exports/extensions/marketplace.ts index 78bd5b9ebac..81e7be948d7 100644 --- a/packages/thirdweb/src/exports/extensions/marketplace.ts +++ b/packages/thirdweb/src/exports/extensions/marketplace.ts @@ -208,3 +208,6 @@ export { type MakeOfferParams, makeOffer, } from "../../extensions/marketplace/offers/write/makeOffer.js"; + +// Types +export type { ListingStatus } from "../../extensions/marketplace/types.js"; diff --git a/packages/thirdweb/src/exports/utils.ts b/packages/thirdweb/src/exports/utils.ts index 11fdf212f96..8495ed8b688 100644 --- a/packages/thirdweb/src/exports/utils.ts +++ b/packages/thirdweb/src/exports/utils.ts @@ -170,6 +170,12 @@ export { type GetClaimParamsOptions, getClaimParams, } from "../utils/extensions/drops/get-claim-params.js"; +// types +export type { + ClaimConditionInput, + ClaimConditionsInput, + OverrideEntry, +} from "../utils/extensions/drops/types.js"; export { formatNumber } from "../utils/formatNumber.js"; // Ethereum Signed Message hashing export { hashMessage } from "../utils/hashing/hashMessage.js"; diff --git a/packages/ui/package.json b/packages/ui/package.json new file mode 100644 index 00000000000..49f63da0d0f --- /dev/null +++ b/packages/ui/package.json @@ -0,0 +1,36 @@ +{ + "name": "@workspace/ui", + "version": "0.0.1", + "private": true, + "exports": { + "./global.css": "./src/global.css", + "./postcss.config": "./postcss.config.mjs", + "./tailwind.config": "./tailwind.config.ts", + "./lib/*": "./src/lib/*.ts", + "./hooks/*": [ + "./src/hooks/*.ts", + "./src/hooks/*.tsx" + ], + "./components/*": "./src/components/*.tsx" + }, + "dependencies": { + "@radix-ui/react-label": "^2.1.7", + "@radix-ui/react-slot": "^1.2.3", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "tailwind-merge": "^2.6.0", + "tailwindcss-animate": "^1.0.7" + }, + "peerDependencies": { + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@types/react": "19.1.8", + "@types/react-dom": "19.1.6", + "autoprefixer": "^10.4.21", + "postcss": "8.5.6", + "tailwindcss": "3.4.17", + "typescript": "5.8.3" + } +} diff --git a/packages/ui/postcss.config.mjs b/packages/ui/postcss.config.mjs new file mode 100644 index 00000000000..2ef30fcf427 --- /dev/null +++ b/packages/ui/postcss.config.mjs @@ -0,0 +1,9 @@ +/** @type {import('postcss-load-config').Config} */ +const config = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; + +export default config; diff --git a/packages/ui/src/components/button.tsx b/packages/ui/src/components/button.tsx new file mode 100644 index 00000000000..bc6b1f07433 --- /dev/null +++ b/packages/ui/src/components/button.tsx @@ -0,0 +1,82 @@ +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; +import * as React from "react"; +import { cn } from "@/lib/utils"; + +const buttonVariants = cva( + "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", + { + defaultVariants: { + size: "default", + variant: "default", + }, + variants: { + size: { + default: "h-10 px-4 py-2", + icon: "h-10 w-10", + lg: "h-11 rounded-md px-8", + sm: "h-9 rounded-md px-3", + }, + variant: { + default: "bg-foreground text-background hover:bg-foreground/90", + destructive: + "bg-destructive hover:bg-destructive/90 text-destructive-foreground ", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "underline-offset-4 hover:underline", + outline: + "border border-input bg-transparent hover:bg-accent hover:text-accent-foreground", + primary: "bg-primary hover:bg-primary/90 text-primary-foreground ", + secondary: + "bg-secondary hover:bg-secondary/80 text-secondary-foreground ", + upsell: + "bg-green-600 text-white hover:bg-green-700 shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 transition-all duration-200", + }, + }, + }, +); + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, disabled, ...props }, ref) => { + const Comp = asChild ? Slot : "button"; + + // "button" elements automatically handle the `disabled` attribute. + // For non-button elements rendered via `asChild` (e.g. ), we still want + // to visually convey the disabled state and prevent user interaction. + // We do that by conditionally adding the same utility classes that the + // `disabled:` pseudo-variant would normally apply and by setting + // `aria-disabled` for accessibility. + const disabledClass = disabled ? "pointer-events-none opacity-50" : ""; + + const btnOnlyProps = + Comp === "button" + ? { + type: + (props as React.ButtonHTMLAttributes).type || + ("button" as const), + } + : undefined; + + return ( + + ); + }, +); +Button.displayName = "Button"; + +export { Button, buttonVariants }; diff --git a/apps/dashboard/src/global.css b/packages/ui/src/global.css similarity index 98% rename from apps/dashboard/src/global.css rename to packages/ui/src/global.css index c6361810549..f5793ed3134 100644 --- a/apps/dashboard/src/global.css +++ b/packages/ui/src/global.css @@ -3,8 +3,7 @@ @tailwind utilities; @layer base { - :root, - [data-theme="light"] { + :root { /* bg - neutral */ --background: 0 0% 98%; --popover: 0 0% 100%; @@ -54,8 +53,7 @@ --sidebar-ring: 0 0% 80%; } - .dark, - [data-theme="dark"] { + .dark { /* bg - neutral */ --background: 0 0% 0%; --card: 0 0% 3.92%; diff --git a/packages/ui/src/lib/utils.ts b/packages/ui/src/lib/utils.ts new file mode 100644 index 00000000000..365058cebd7 --- /dev/null +++ b/packages/ui/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/apps/dashboard/tailwind.config.js b/packages/ui/tailwind.config.ts similarity index 91% rename from apps/dashboard/tailwind.config.js rename to packages/ui/tailwind.config.ts index 67f7cb12354..62b5b045937 100644 --- a/apps/dashboard/tailwind.config.js +++ b/packages/ui/tailwind.config.ts @@ -1,10 +1,15 @@ +import type { Config } from "tailwindcss"; import { fontFamily } from "tailwindcss/defaultTheme"; +import tailwindcssAnimate from "tailwindcss-animate"; -/** @type {import('tailwindcss').Config} */ -module.exports = { - content: ["./src/**/*.{ts,tsx}"], +const config: Config = { + content: [ + "./src/**/*.{ts,tsx}", + // Note: when importing this config in website project, + // add a relative path to the ui package here + ], darkMode: ["class"], - plugins: [require("tailwindcss-animate")], + plugins: [tailwindcssAnimate], prefix: "", theme: { container: { @@ -122,3 +127,5 @@ module.exports = { }, }, }; + +export default config; diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json new file mode 100644 index 00000000000..59179be8a91 --- /dev/null +++ b/packages/ui/tsconfig.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "target": "ES2022", + "lib": ["es2022", "DOM", "DOM.Iterable"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "declaration": true, + "declarationMap": true, + "esModuleInterop": true, + "incremental": false, + "isolatedModules": true, + "module": "NodeNext", + "moduleDetection": "force", + "moduleResolution": "NodeNext", + "noUncheckedIndexedAccess": true, + "resolveJsonModule": true, + "baseUrl": ".", + "jsx": "preserve", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 529c9f94053..2bbe7fb5348 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -145,6 +145,9 @@ importers: '@vercel/og': specifier: ^0.6.8 version: 0.6.8 + '@workspace/ui': + specifier: workspace:* + version: link:../../packages/ui abitype: specifier: 1.0.8 version: 1.0.8(typescript@5.8.3)(zod@3.25.75) @@ -612,6 +615,9 @@ importers: '@tanstack/react-query': specifier: 5.81.5 version: 5.81.5(react@19.1.0) + '@workspace/ui': + specifier: workspace:* + version: link:../../packages/ui class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -893,6 +899,9 @@ importers: '@typescript-eslint/parser': specifier: 7.14.1 version: 7.14.1(eslint@8.57.0)(typescript@5.8.3) + '@workspace/ui': + specifier: workspace:* + version: link:../../packages/ui autoprefixer: specifier: ^10.4.21 version: 10.4.21(postcss@8.5.6) @@ -1472,6 +1481,52 @@ importers: specifier: 3.2.4 version: 3.2.4(@types/debug@4.1.12)(@types/node@24.0.10)(@vitest/ui@3.2.4)(happy-dom@17.4.4)(jiti@2.4.2)(lightningcss@1.30.1)(msw@2.7.5(@types/node@24.0.10)(typescript@5.8.3))(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) + packages/ui: + dependencies: + '@radix-ui/react-label': + specifier: ^2.1.7 + version: 2.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': + specifier: ^1.2.3 + version: 1.2.3(@types/react@19.1.8)(react@19.1.0) + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + react: + specifier: ^19.0.0 + version: 19.1.0 + react-dom: + specifier: ^19.0.0 + version: 19.1.0(react@19.1.0) + tailwind-merge: + specifier: ^2.6.0 + version: 2.6.0 + tailwindcss-animate: + specifier: ^1.0.7 + version: 1.0.7(tailwindcss@3.4.17) + devDependencies: + '@types/react': + specifier: 19.1.8 + version: 19.1.8 + '@types/react-dom': + specifier: 19.1.6 + version: 19.1.6(@types/react@19.1.8) + autoprefixer: + specifier: ^10.4.21 + version: 10.4.21(postcss@8.5.6) + postcss: + specifier: 8.5.6 + version: 8.5.6 + tailwindcss: + specifier: 3.4.17 + version: 3.4.17 + typescript: + specifier: 5.8.3 + version: 5.8.3 + packages/vault-sdk: dependencies: '@noble/ciphers': @@ -7297,6 +7352,7 @@ packages: '@walletconnect/modal@2.7.0': resolution: {integrity: sha512-RQVt58oJ+rwqnPcIvRFeMGKuXb9qkgSmwz4noF8JZGUym3gUAzVs+uW2NQ1Owm9XOJAV+sANrtJ+VoVq1ftElw==} + deprecated: Please follow the migration guide on https://docs.reown.com/appkit/upgrade/wcm '@walletconnect/react-native-compat@2.17.3': resolution: {integrity: sha512-lHKwXKoB0rdDH1ukxUx7o86xosWbttWIHYMZ8tgAQC1k9VH3CZZCoBcHOAAX8iBzyb0n0UP3/9zRrOcJE5nz7Q==} @@ -31311,7 +31367,7 @@ snapshots: '@babel/parser': 7.28.0 '@babel/template': 7.27.2 '@babel/traverse': 7.28.0 - '@babel/types': 7.28.0 + '@babel/types': 7.28.2 accepts: 1.3.8 chalk: 4.1.2 ci-info: 2.0.0