format all files

This commit is contained in:
Maze Winther 2026-01-21 12:52:34 +01:00
parent 8500dd770b
commit afdf7d22cf
176 changed files with 2596 additions and 6480 deletions

View File

@ -6,7 +6,7 @@ alwaysApply: true
# Project Context # Project Context
Ultracite enforces strict type safety, accessibility standards, and consistent code quality for JavaScript/TypeScript projects using Biome's lightning-fast formatter and linter. Ultracite enforces strict type safety, accessibility standards, and consistent code quality for JavaScript/TypeScript projects using Prettier's formatter.
## Key Principles ## Key Principles

View File

@ -17,23 +17,23 @@ diverse, inclusive, and healthy community.
Examples of behavior that contributes to a positive environment for our Examples of behavior that contributes to a positive environment for our
community include: community include:
* Demonstrating empathy and kindness toward other people - Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences - Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback - Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes, - Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall - Focusing on what is best not just for us as individuals, but for the overall
community community
Examples of unacceptable behavior include: Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of - The use of sexualized language or imagery, and sexual attention or advances of
any kind any kind
* Trolling, insulting or derogatory comments, and personal or political attacks - Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment - Public or private harassment
* Publishing others' private information, such as a physical or email address, - Publishing others' private information, such as a physical or email address,
without their explicit permission without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a - Other conduct which could reasonably be considered inappropriate in a
professional setting professional setting
## Enforcement Responsibilities ## Enforcement Responsibilities

View File

@ -1,6 +1,6 @@
name: Bug report name: Bug report
description: Create a report to help us improve description: Create a report to help us improve
title: '[BUG] ' title: "[BUG] "
labels: bug labels: bug
body: body:
- type: input - type: input
@ -15,7 +15,7 @@ body:
id: Browser id: Browser
attributes: attributes:
label: Browser label: Browser
description: Please enter the browser on which you encountered the bug. description: Please enter the browser on which you encountered the bug.
placeholder: e.g. Chrome 137, Firefox 137, Safari 17 placeholder: e.g. Chrome 137, Firefox 137, Safari 17
validations: validations:
required: true required: true
@ -67,4 +67,4 @@ body:
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
validations: validations:
required: false required: false

View File

@ -1,6 +1,6 @@
name: Feature request name: Feature request
description: Suggest an idea for OpenCut description: Suggest an idea for OpenCut
title: '[FEATURE] ' title: "[FEATURE] "
labels: enhancement labels: enhancement
body: body:
- type: markdown - type: markdown
@ -39,4 +39,4 @@ body:
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
validations: validations:
required: false required: false

2
.github/SECURITY.md vendored
View File

@ -25,4 +25,4 @@ Please do not report security vulnerabilities through public GitHub issues.
- We will provide a detailed response within 5 business days - We will provide a detailed response within 5 business days
- We will keep you updated on our progress - We will keep you updated on our progress
Thank you for helping keep OpenCut secure! Thank you for helping keep OpenCut secure!

6
.github/SUPPORT.md vendored
View File

@ -3,21 +3,25 @@
Thanks for using OpenCut! If you need help, here are your options: Thanks for using OpenCut! If you need help, here are your options:
## Documentation ## Documentation
- Check our [README](../README.md) for basic setup instructions - Check our [README](../README.md) for basic setup instructions
- Review the [Contributing Guidelines](CONTRIBUTING.md) for development setup - Review the [Contributing Guidelines](CONTRIBUTING.md) for development setup
## Issues ## Issues
- **Bug reports**: Use the bug report template - **Bug reports**: Use the bug report template
- **Feature requests**: Use the feature request template - **Feature requests**: Use the feature request template
- **Questions**: Use GitHub Discussions for general questions - **Questions**: Use GitHub Discussions for general questions
## Community ## Community
- Join our discussions on GitHub - Join our discussions on GitHub
- Follow the [Code of Conduct](CODE_OF_CONDUCT.md) - Follow the [Code of Conduct](CODE_OF_CONDUCT.md)
## Response Times ## Response Times
- Issues are typically triaged within 2-3 business days - Issues are typically triaged within 2-3 business days
- Feature requests may take longer to evaluate - Feature requests may take longer to evaluate
- Security issues are handled with priority - Security issues are handled with priority
We appreciate your patience and contributions to making OpenCut better! We appreciate your patience and contributions to making OpenCut better!

View File

@ -3,15 +3,18 @@ applyTo: "**/*.{ts,tsx,js,jsx}"
--- ---
# Project Context # Project Context
Ultracite enforces strict type safety, accessibility standards, and consistent code quality for JavaScript/TypeScript projects using Biome's lightning-fast formatter and linter. Ultracite enforces strict type safety, accessibility standards, and consistent code quality for JavaScript/TypeScript projects using Biome's lightning-fast formatter and linter.
## Key Principles ## Key Principles
- Zero configuration required - Zero configuration required
- Subsecond performance - Subsecond performance
- Maximum type safety - Maximum type safety
- AI-friendly code generation - AI-friendly code generation
## Before Writing Code ## Before Writing Code
1. Analyze existing patterns in the codebase 1. Analyze existing patterns in the codebase
2. Consider edge cases and error scenarios 2. Consider edge cases and error scenarios
3. Follow the rules below strictly 3. Follow the rules below strictly
@ -20,6 +23,7 @@ Ultracite enforces strict type safety, accessibility standards, and consistent c
## Rules ## Rules
### Accessibility (a11y) ### Accessibility (a11y)
- Don't use `accessKey` attribute on any HTML element. - Don't use `accessKey` attribute on any HTML element.
- Don't set `aria-hidden="true"` on focusable elements. - Don't set `aria-hidden="true"` on focusable elements.
- Don't add ARIA roles, states, and properties to elements that don't support them. - Don't add ARIA roles, states, and properties to elements that don't support them.
@ -56,6 +60,7 @@ Ultracite enforces strict type safety, accessibility standards, and consistent c
- Use correct ISO language/country codes for the `lang` attribute. - Use correct ISO language/country codes for the `lang` attribute.
### Code Complexity and Quality ### Code Complexity and Quality
- Don't use consecutive spaces in regular expression literals. - Don't use consecutive spaces in regular expression literals.
- Don't use the `arguments` object. - Don't use the `arguments` object.
- Don't use primitive type aliases or misleading types. - Don't use primitive type aliases or misleading types.
@ -111,6 +116,7 @@ Ultracite enforces strict type safety, accessibility standards, and consistent c
- Don't use literal numbers that lose precision. - Don't use literal numbers that lose precision.
### React and JSX Best Practices ### React and JSX Best Practices
- Don't use the return value of React.render. - Don't use the return value of React.render.
- Make sure all dependencies are correctly specified in React hooks. - Make sure all dependencies are correctly specified in React hooks.
- Make sure all React hooks are called from the top level of component functions. - Make sure all React hooks are called from the top level of component functions.
@ -129,6 +135,7 @@ Ultracite enforces strict type safety, accessibility standards, and consistent c
- Watch out for possible "wrong" semicolons inside JSX elements. - Watch out for possible "wrong" semicolons inside JSX elements.
### Correctness and Safety ### Correctness and Safety
- Don't assign a value to itself. - Don't assign a value to itself.
- Don't return a value from a setter. - Don't return a value from a setter.
- Don't compare expressions that modify string case with non-compliant values. - Don't compare expressions that modify string case with non-compliant values.
@ -153,7 +160,7 @@ Ultracite enforces strict type safety, accessibility standards, and consistent c
- Don't use bitwise operators. - Don't use bitwise operators.
- Don't use expressions where the operation doesn't change the value. - Don't use expressions where the operation doesn't change the value.
- Make sure Promise-like statements are handled appropriately. - Make sure Promise-like statements are handled appropriately.
- Don't use __dirname and __filename in the global scope. - Don't use **dirname and **filename in the global scope.
- Prevent import cycles. - Prevent import cycles.
- Don't use configured elements. - Don't use configured elements.
- Don't hardcode sensitive data like API keys and tokens. - Don't hardcode sensitive data like API keys and tokens.
@ -184,6 +191,7 @@ Ultracite enforces strict type safety, accessibility standards, and consistent c
- Don't use `target="_blank"` without `rel="noopener"`. - Don't use `target="_blank"` without `rel="noopener"`.
### TypeScript Best Practices ### TypeScript Best Practices
- Don't use TypeScript enums. - Don't use TypeScript enums.
- Don't export imported variables. - Don't export imported variables.
- Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions. - Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
@ -208,6 +216,7 @@ Ultracite enforces strict type safety, accessibility standards, and consistent c
- Use the namespace keyword instead of the module keyword to declare TypeScript namespaces. - Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
### Style and Consistency ### Style and Consistency
- Don't use global `eval()`. - Don't use global `eval()`.
- Don't use callbacks in asynchronous tests and hooks. - Don't use callbacks in asynchronous tests and hooks.
- Don't use negation in `if` statements that have `else` clauses. - Don't use negation in `if` statements that have `else` clauses.
@ -295,30 +304,34 @@ Ultracite enforces strict type safety, accessibility standards, and consistent c
- Make sure to use the "use strict" directive in script files. - Make sure to use the "use strict" directive in script files.
### Next.js Specific Rules ### Next.js Specific Rules
- Don't use `<img>` elements in Next.js projects. - Don't use `<img>` elements in Next.js projects.
- Don't use `<head>` elements in Next.js projects. - Don't use `<head>` elements in Next.js projects.
- Don't import next/document outside of pages/_document.jsx in Next.js projects. - Don't import next/document outside of pages/\_document.jsx in Next.js projects.
- Don't use the next/head module in pages/_document.js on Next.js projects. - Don't use the next/head module in pages/\_document.js on Next.js projects.
### Testing Best Practices ### Testing Best Practices
- Don't use export or module.exports in test files. - Don't use export or module.exports in test files.
- Don't use focused tests. - Don't use focused tests.
- Make sure the assertion function, like expect, is placed inside an it() function call. - Make sure the assertion function, like expect, is placed inside an it() function call.
- Don't use disabled tests. - Don't use disabled tests.
## Common Tasks ## Common Tasks
- `npx ultracite init` - Initialize Ultracite in your project - `npx ultracite init` - Initialize Ultracite in your project
- `npx ultracite format` - Format and fix code automatically - `npx ultracite format` - Format and fix code automatically
- `npx ultracite lint` - Check for issues without fixing - `npx ultracite lint` - Check for issues without fixing
## Example: Error Handling ## Example: Error Handling
```typescript ```typescript
// ✅ Good: Comprehensive error handling // ✅ Good: Comprehensive error handling
try { try {
const result = await fetchData(); const result = await fetchData();
return { success: true, data: result }; return { success: true, data: result };
} catch (error) { } catch (error) {
console.error('API call failed:', error); console.error("API call failed:", error);
return { success: false, error: error.message }; return { success: false, error: error.message };
} }
@ -328,4 +341,4 @@ try {
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
``` ```

View File

@ -24,9 +24,10 @@ Please describe the tests that you ran to verify your changes. Provide instructi
- [ ] Test B - [ ] Test B
**Test Configuration**: **Test Configuration**:
* Node version:
* Browser (if applicable): - Node version:
* Operating System: - Browser (if applicable):
- Operating System:
## Screenshots (if applicable) ## Screenshots (if applicable)
@ -46,4 +47,4 @@ Add screenshots to help explain your changes.
## Additional context ## Additional context
Add any other context about the pull request here. Add any other context about the pull request here.

3
.prettierignore Normal file
View File

@ -0,0 +1,3 @@
.git
apps/web/.next/
apps/web/public/

View File

@ -1,3 +1,3 @@
{ {
"plugins": ["prettier-plugin-tailwindcss"] "plugins": ["prettier-plugin-tailwindcss"]
} }

29
.vscode/settings.json vendored
View File

@ -1,17 +1,16 @@
{ {
"editor.defaultFormatter": "esbenp.prettier-vscode", "editor.defaultFormatter": "esbenp.prettier-vscode",
"[javascript][typescript][javascriptreact][typescriptreact][json][jsonc][css][graphql]": { "[javascript][typescript][javascriptreact][typescriptreact][json][jsonc][css][graphql]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"typescript.tsdk": "node_modules/typescript/lib", "typescript.tsdk": "node_modules/typescript/lib",
"editor.formatOnSave": true, "editor.formatOnSave": true,
"editor.formatOnPaste": true, "editor.formatOnPaste": true,
"emmet.showExpandedAbbreviation": "never", "emmet.showExpandedAbbreviation": "never",
"[typescriptreact]": { "[typescriptreact]": {
"editor.defaultFormatter": "vscode.typescript-language-features" "editor.defaultFormatter": "vscode.typescript-language-features"
}, },
"[typescript]": { "[typescript]": {
"editor.defaultFormatter": "vscode.typescript-language-features" "editor.defaultFormatter": "esbenp.prettier-vscode"
}
} }
}

View File

@ -32,16 +32,17 @@ import { useEditor } from '@/hooks/use-editor';
function MyComponent() { function MyComponent() {
const editor = useEditor(); const editor = useEditor();
const tracks = editor.timeline.getTracks(); const tracks = editor.timeline.getTracks();
// Call methods // Call methods
editor.timeline.addTrack({ type: 'media' }); editor.timeline.addTrack({ type: 'media' });
// Display data (auto re-renders on changes) // Display data (auto re-renders on changes)
return <div>{tracks.length} tracks</div>; return <div>{tracks.length} tracks</div>;
} }
``` ```
The hook: The hook:
- Returns the singleton instance - Returns the singleton instance
- Subscribes to all manager changes - Subscribes to all manager changes
- Automatically re-renders when state changes - Automatically re-renders when state changes
@ -52,10 +53,10 @@ The hook:
```typescript ```typescript
// In utilities, event handlers, or non-React code // In utilities, event handlers, or non-React code
import { EditorCore } from '@/core'; import { EditorCore } from "@/core";
const editor = EditorCore.getInstance(); const editor = EditorCore.getInstance();
await editor.export({ format: 'mp4', quality: 'high' }); await editor.export({ format: "mp4", quality: "high" });
``` ```
## Actions System ## Actions System
@ -65,6 +66,7 @@ Actions are the trigger layer for user-initiated operations. The single source o
**To add a new action:** **To add a new action:**
1. Add it to `ACTIONS` in `@/lib/actions/definitions.ts`: 1. Add it to `ACTIONS` in `@/lib/actions/definitions.ts`:
```typescript ```typescript
export const ACTIONS = { export const ACTIONS = {
"my-action": { "my-action": {
@ -73,14 +75,19 @@ export const ACTIONS = {
defaultShortcuts: ["ctrl+m"], defaultShortcuts: ["ctrl+m"],
}, },
// ... // ...
} };
``` ```
2. Add handler in `@/hooks/use-editor-actions.ts`: 2. Add handler in `@/hooks/use-editor-actions.ts`:
```typescript ```typescript
useActionHandler("my-action", () => { useActionHandler(
// implementation "my-action",
}, undefined); () => {
// implementation
},
undefined,
);
``` ```
**In components, use `invokeAction()` for user-triggered operations:** **In components, use `invokeAction()` for user-triggered operations:**
@ -102,6 +109,7 @@ Direct `editor.xxx()` calls are for internal use (commands, tests, complex multi
Commands handle undo/redo. They live in `@/lib/commands/` organized by domain (timeline, media, scene). Commands handle undo/redo. They live in `@/lib/commands/` organized by domain (timeline, media, scene).
Each command extends `Command` from `@/lib/commands/base-command` and implements: Each command extends `Command` from `@/lib/commands/base-command` and implements:
- `execute()` - saves current state, then does the mutation - `execute()` - saves current state, then does the mutation
- `undo()` - restores the saved state - `undo()` - restores the saved state

View File

@ -1,6 +0,0 @@
[x] Delete all stores we can
[x] Create commands for each action
[x] Finish timeline store
[x] Migrate final timeline store to manager
[x] Delete timeline store
[ ] Update usage of all stores to use the new managers

View File

@ -96,7 +96,7 @@ function buildSortParameter({ query, sort }: { query?: string; sort: string }) {
function applyEffectsFilters({ function applyEffectsFilters({
params, params,
min_rating, min_rating,
commercial_only commercial_only,
}: { }: {
params: URLSearchParams; params: URLSearchParams;
min_rating: number; min_rating: number;
@ -108,17 +108,19 @@ function applyEffectsFilters({
if (commercial_only) { if (commercial_only) {
params.append( params.append(
"filter", "filter",
'license:("Attribution" OR "Creative Commons 0" OR "Attribution Noncommercial" OR "Attribution Commercial")' 'license:("Attribution" OR "Creative Commons 0" OR "Attribution Noncommercial" OR "Attribution Commercial")',
); );
} }
params.append( params.append(
"filter", "filter",
"tag:sound-effect OR tag:sfx OR tag:foley OR tag:ambient OR tag:nature OR tag:mechanical OR tag:electronic OR tag:impact OR tag:whoosh OR tag:explosion" "tag:sound-effect OR tag:sfx OR tag:foley OR tag:ambient OR tag:nature OR tag:mechanical OR tag:electronic OR tag:impact OR tag:whoosh OR tag:explosion",
); );
} }
function transformFreesoundResult(result: z.infer<typeof freesoundResultSchema>) { function transformFreesoundResult(
result: z.infer<typeof freesoundResultSchema>,
) {
return { return {
id: result.id, id: result.id,
name: result.name, name: result.name,
@ -169,7 +171,7 @@ export async function GET(request: NextRequest) {
error: "Invalid parameters", error: "Invalid parameters",
details: validationResult.error.flatten().fieldErrors, details: validationResult.error.flatten().fieldErrors,
}, },
{ status: 400 } { status: 400 },
); );
} }
@ -190,7 +192,7 @@ export async function GET(request: NextRequest) {
message: message:
"Song search functionality is coming soon. Try searching for sound effects instead.", "Song search functionality is coming soon. Try searching for sound effects instead.",
}, },
{ status: 501 } { status: 501 },
); );
} }
@ -220,7 +222,7 @@ export async function GET(request: NextRequest) {
console.error("Freesound API error:", response.status, errorText); console.error("Freesound API error:", response.status, errorText);
return NextResponse.json( return NextResponse.json(
{ error: "Failed to search sounds" }, { error: "Failed to search sounds" },
{ status: response.status } { status: response.status },
); );
} }
@ -230,11 +232,11 @@ export async function GET(request: NextRequest) {
if (!freesoundValidation.success) { if (!freesoundValidation.success) {
console.error( console.error(
"Invalid Freesound API response:", "Invalid Freesound API response:",
freesoundValidation.error freesoundValidation.error,
); );
return NextResponse.json( return NextResponse.json(
{ error: "Invalid response from Freesound API" }, { error: "Invalid response from Freesound API" },
{ status: 502 } { status: 502 },
); );
} }
@ -259,11 +261,11 @@ export async function GET(request: NextRequest) {
if (!responseValidation.success) { if (!responseValidation.success) {
console.error( console.error(
"Invalid API response structure:", "Invalid API response structure:",
responseValidation.error responseValidation.error,
); );
return NextResponse.json( return NextResponse.json(
{ error: "Internal response formatting error" }, { error: "Internal response formatting error" },
{ status: 500 } { status: 500 },
); );
} }
@ -272,7 +274,7 @@ export async function GET(request: NextRequest) {
console.error("Error searching sounds:", error); console.error("Error searching sounds:", error);
return NextResponse.json( return NextResponse.json(
{ error: "Internal server error" }, { error: "Internal server error" },
{ status: 500 } { status: 500 },
); );
} }
} }

View File

@ -30,7 +30,7 @@ export function BasePage({
<Header /> <Header />
<main <main
className={cn( className={cn(
"container relative mx-auto flex flex-col gap-12 px-6 pb-24 pt-12 md:pt-24", "relative container mx-auto flex flex-col gap-12 px-6 pt-12 pb-24 md:pt-24",
maxWidthClass, maxWidthClass,
mainClassName, mainClassName,
)} )}

View File

@ -42,7 +42,7 @@ export default async function BlogPage() {
function BlogPostItem({ post }: { post: Post }) { function BlogPostItem({ post }: { post: Post }) {
return ( return (
<Link href={`/blog/${post.slug}`}> <Link href={`/blog/${post.slug}`}>
<div className="h-auto w-full opacity-100 transition-opacity hover:opacity-75 flex items-center justify-between py-6"> <div className="flex h-auto w-full items-center justify-between py-6 opacity-100 transition-opacity hover:opacity-75">
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<h2 className="text-xl font-semibold">{post.title}</h2> <h2 className="text-xl font-semibold">{post.title}</h2>
<p className="text-muted-foreground">{post.description}</p> <p className="text-muted-foreground">{post.description}</p>

View File

@ -190,7 +190,7 @@ export default function ProjectsPage() {
)} )}
</div> </div>
</div> </div>
<main className="mx-auto max-w-6xl px-6 pb-6 pt-6"> <main className="mx-auto max-w-6xl px-6 pt-6 pb-6">
<div className="mb-8 flex items-center justify-between"> <div className="mb-8 flex items-center justify-between">
<div className="flex flex-col gap-3"> <div className="flex flex-col gap-3">
<h1 className="text-2xl font-bold tracking-tight md:text-3xl"> <h1 className="text-2xl font-bold tracking-tight md:text-3xl">
@ -419,16 +419,18 @@ function ProjectCard({
const cardContent = ( const cardContent = (
<Card <Card
className={`bg-background overflow-hidden border-none p-0 transition-all ${isSelectionMode && isSelected ? "ring-primary ring-2" : "" className={`bg-background overflow-hidden border-none p-0 transition-all ${
}`} isSelectionMode && isSelected ? "ring-primary ring-2" : ""
}`}
> >
<div <div
className={`bg-muted relative aspect-square transition-opacity ${isDropdownOpen ? "opacity-65" : "opacity-100 group-hover:opacity-65" className={`bg-muted relative aspect-square transition-opacity ${
}`} isDropdownOpen ? "opacity-65" : "opacity-100 group-hover:opacity-65"
}`}
> >
{isSelectionMode && ( {isSelectionMode && (
<div className="absolute left-3 top-3 z-10"> <div className="absolute top-3 left-3 z-10">
<div className="bg-background/80 backdrop-blur-xs flex size-5 items-center justify-center rounded-full border"> <div className="bg-background/80 flex size-5 items-center justify-center rounded-full border backdrop-blur-xs">
<Checkbox <Checkbox
checked={isSelected} checked={isSelected}
onCheckedChange={(checked) => onCheckedChange={(checked) =>
@ -462,7 +464,7 @@ function ProjectCard({
<CardContent className="flex flex-col gap-1 px-0 pt-5"> <CardContent className="flex flex-col gap-1 px-0 pt-5">
<div className="flex items-start justify-between"> <div className="flex items-start justify-between">
<h3 className="group-hover:text-foreground/90 line-clamp-2 text-sm font-medium leading-snug transition-colors"> <h3 className="group-hover:text-foreground/90 line-clamp-2 text-sm leading-snug font-medium transition-colors">
{project.name} {project.name}
</h3> </h3>
{!isSelectionMode && ( {!isSelectionMode && (
@ -475,10 +477,11 @@ function ProjectCard({
aria-label="project options" aria-label="project options"
variant="text" variant="text"
size="sm" size="sm"
className={`ml-2 size-6 shrink-0 p-0 transition-all ${isDropdownOpen className={`ml-2 size-6 shrink-0 p-0 transition-all ${
? "opacity-100" isDropdownOpen
: "opacity-0 group-hover:opacity-100" ? "opacity-100"
}`} : "opacity-0 group-hover:opacity-100"
}`}
onClick={(event) => event.preventDefault()} onClick={(event) => event.preventDefault()}
> >
<MoreHorizontal aria-hidden="true" /> <MoreHorizontal aria-hidden="true" />

View File

@ -156,7 +156,7 @@ function RoadmapItem({ item, index }: { item: RoadmapItem; index: number }) {
return ( return (
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<div className="flex items-center gap-2 text-lg font-medium"> <div className="flex items-center gap-2 text-lg font-medium">
<span className="select-none leading-normal">{index + 1}</span> <span className="leading-normal select-none">{index + 1}</span>
<h3>{item.title}</h3> <h3>{item.title}</h3>
<StatusBadge status={item.status} className="ml-1" /> <StatusBadge status={item.status} className="ml-1" />
</div> </div>

View File

@ -14,8 +14,9 @@ export async function GET() {
language: "en", language: "en",
image: `${SITE_INFO.openGraphImage}`, image: `${SITE_INFO.openGraphImage}`,
favicon: `${SITE_INFO.favicon}`, favicon: `${SITE_INFO.favicon}`,
copyright: `All rights reserved ${new Date().getFullYear()}, ${SITE_INFO.title copyright: `All rights reserved ${new Date().getFullYear()}, ${
}`, SITE_INFO.title
}`,
}); });
for (const post of posts) { for (const post of posts) {

View File

@ -27,15 +27,15 @@ export function MediaDragOverlay({
return ( return (
<div <div
className="flex flex-col items-center justify-center gap-4 h-full text-center rounded-lg bg-foreground/5 hover:bg-foreground/10 transition-all duration-200 p-8" className="bg-foreground/5 hover:bg-foreground/10 flex h-full flex-col items-center justify-center gap-4 rounded-lg p-8 text-center transition-all duration-200"
onClick={handleClick} onClick={handleClick}
> >
<div className="flex items-center justify-center"> <div className="flex items-center justify-center">
<Upload className="h-10 w-10 text-foreground" /> <Upload className="text-foreground h-10 w-10" />
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<p className="text-xs text-muted-foreground max-w-sm"> <p className="text-muted-foreground max-w-sm text-xs">
{isProcessing {isProcessing
? `Processing your files (${progress}%)` ? `Processing your files (${progress}%)`
: "Drag and drop videos, photos, and audio files here"} : "Drag and drop videos, photos, and audio files here"}
@ -44,7 +44,7 @@ export function MediaDragOverlay({
{isProcessing && ( {isProcessing && (
<div className="w-full max-w-xs"> <div className="w-full max-w-xs">
<div className="w-full bg-muted/50 rounded-full h-2"> <div className="bg-muted/50 h-2 w-full rounded-full">
<div <div
className="bg-primary h-2 rounded-full transition-all duration-300" className="bg-primary h-2 rounded-full transition-all duration-300"
style={{ width: `${progress}%` }} style={{ width: `${progress}%` }}

View File

@ -73,7 +73,7 @@ export function TabBar() {
variant="sidebar" variant="sidebar"
sideOffset={8} sideOffset={8}
> >
<div className="text-foreground text-sm font-medium leading-none"> <div className="text-foreground text-sm leading-none font-medium">
{tab.label} {tab.label}
</div> </div>
</TooltipContent> </TooltipContent>
@ -99,7 +99,7 @@ function FadeOverlay({
return ( return (
<div <div
className={cn( className={cn(
"pointer-events-none absolute left-0 right-0 h-6 transition-opacity duration-200", "pointer-events-none absolute right-0 left-0 h-6 transition-opacity duration-200",
direction === "top" && show direction === "top" && show
? "from-panel top-0 bg-gradient-to-b to-transparent" ? "from-panel top-0 bg-gradient-to-b to-transparent"
: "from-panel bottom-0 bg-gradient-to-t to-transparent", : "from-panel bottom-0 bg-gradient-to-t to-transparent",

View File

@ -23,9 +23,7 @@ export function Captions() {
const handleProgress = (progress: TranscriptionProgress) => { const handleProgress = (progress: TranscriptionProgress) => {
if (progress.status === "loading-model") { if (progress.status === "loading-model") {
setProcessingStep( setProcessingStep(`Loading model ${Math.round(progress.progress)}%`);
`Loading model ${Math.round(progress.progress)}%`,
);
} else if (progress.status === "transcribing") { } else if (progress.status === "transcribing") {
setProcessingStep("Transcribing..."); setProcessingStep("Transcribing...");
} }
@ -48,7 +46,8 @@ export function Captions() {
const result = await transcriptionService.transcribe({ const result = await transcriptionService.transcribe({
audioData: samples, audioData: samples,
language: selectedLanguage === "auto" ? "auto" : selectedLanguage.toLowerCase(), language:
selectedLanguage === "auto" ? "auto" : selectedLanguage.toLowerCase(),
onProgress: handleProgress, onProgress: handleProgress,
}); });

View File

@ -49,7 +49,8 @@ export function MediaView() {
const mediaFiles = editor.media.getAssets(); const mediaFiles = editor.media.getAssets();
const activeProject = editor.project.getActive(); const activeProject = editor.project.getActive();
const { mediaViewMode, setMediaViewMode, highlightMediaId, clearHighlight } = useAssetsPanelStore(); const { mediaViewMode, setMediaViewMode, highlightMediaId, clearHighlight } =
useAssetsPanelStore();
const { highlightedId, registerElement } = useRevealItem( const { highlightedId, registerElement } = useRevealItem(
highlightMediaId, highlightMediaId,
clearHighlight, clearHighlight,
@ -509,7 +510,7 @@ function MediaDurationBadge({ duration }: { duration?: number }) {
if (!duration) return null; if (!duration) return null;
return ( return (
<div className="absolute bottom-1 right-1 rounded bg-black/70 px-1 text-xs text-white"> <div className="absolute right-1 bottom-1 rounded bg-black/70 px-1 text-xs text-white">
{formatDuration({ duration })} {formatDuration({ duration })}
</div> </div>
); );
@ -551,7 +552,6 @@ function MediaTypePlaceholder({
} }
function MediaPreview({ item }: { item: MediaAsset }) { function MediaPreview({ item }: { item: MediaAsset }) {
if (item.type === "image") { if (item.type === "image") {
return ( return (
<div className="flex size-full items-center justify-center"> <div className="flex size-full items-center justify-center">
@ -604,13 +604,7 @@ function MediaPreview({ item }: { item: MediaAsset }) {
); );
} }
return ( return <MediaTypePlaceholder icon={Image} label="Unknown" variant="muted" />;
<MediaTypePlaceholder
icon={Image}
label="Unknown"
variant="muted"
/>
);
} }
function SortMenuItem({ function SortMenuItem({

View File

@ -82,11 +82,7 @@ function ProjectSettingsTabs() {
); );
} }
function getCurrentCanvasSize({ function getCurrentCanvasSize({ activeProject }: { activeProject: TProject }) {
activeProject,
}: {
activeProject: TProject;
}) {
const { canvasSize } = activeProject.settings; const { canvasSize } = activeProject.settings;
return { return {
@ -155,9 +151,7 @@ function ProjectInfoView() {
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
<PropertyItem direction="column"> <PropertyItem direction="column">
<PropertyItemLabel>Name</PropertyItemLabel> <PropertyItemLabel>Name</PropertyItemLabel>
<PropertyItemValue> <PropertyItemValue>{activeProject.metadata.name}</PropertyItemValue>
{activeProject.metadata.name}
</PropertyItemValue>
</PropertyItem> </PropertyItem>
<PropertyItem direction="column"> <PropertyItem direction="column">
@ -237,7 +231,7 @@ const BlurPreview = memo(
style={{ filter: `blur(${blur.value}px)` }} style={{ filter: `blur(${blur.value}px)` }}
loading="eager" loading="eager"
/> />
<div className="absolute bottom-1 left-1 right-1 text-center"> <div className="absolute right-1 bottom-1 left-1 text-center">
<span className="rounded bg-black/50 px-1 text-xs text-white"> <span className="rounded bg-black/50 px-1 text-xs text-white">
{blur.label} {blur.label}
</span> </span>
@ -270,18 +264,18 @@ const BackgroundPreviews = memo(
className={cn( className={cn(
"border-foreground/15 hover:border-primary aspect-square w-full cursor-pointer rounded-sm border", "border-foreground/15 hover:border-primary aspect-square w-full cursor-pointer rounded-sm border",
isColorBackground && isColorBackground &&
bg === currentBackgroundColor && bg === currentBackgroundColor &&
"border-primary border-2", "border-primary border-2",
)} )}
style={ style={
useBackgroundColor useBackgroundColor
? { backgroundColor: bg } ? { backgroundColor: bg }
: { : {
background: bg, background: bg,
backgroundSize: "cover", backgroundSize: "cover",
backgroundPosition: "center", backgroundPosition: "center",
backgroundRepeat: "no-repeat", backgroundRepeat: "no-repeat",
} }
} }
onClick={() => handleColorSelect({ bg })} onClick={() => handleColorSelect({ bg })}
/> />

View File

@ -36,8 +36,8 @@ import { useInfiniteScroll } from "@/hooks/use-infinite-scroll";
export function SoundsView() { export function SoundsView() {
return ( return (
<div className="h-full flex flex-col"> <div className="flex h-full flex-col">
<Tabs defaultValue="sound-effects" className="flex flex-col h-full"> <Tabs defaultValue="sound-effects" className="flex h-full flex-col">
<div className="px-3 pt-4 pb-0"> <div className="px-3 pt-4 pb-0">
<TabsList> <TabsList>
<TabsTrigger value="sound-effects">Sound effects</TabsTrigger> <TabsTrigger value="sound-effects">Sound effects</TabsTrigger>
@ -48,19 +48,19 @@ export function SoundsView() {
<Separator className="my-4" /> <Separator className="my-4" />
<TabsContent <TabsContent
value="sound-effects" value="sound-effects"
className="p-5 pt-0 mt-0 flex-1 flex flex-col min-h-0" className="mt-0 flex min-h-0 flex-1 flex-col p-5 pt-0"
> >
<SoundEffectsView /> <SoundEffectsView />
</TabsContent> </TabsContent>
<TabsContent <TabsContent
value="saved" value="saved"
className="p-5 pt-0 mt-0 flex-1 flex flex-col min-h-0" className="mt-0 flex min-h-0 flex-1 flex-col p-5 pt-0"
> >
<SavedSoundsView /> <SavedSoundsView />
</TabsContent> </TabsContent>
<TabsContent <TabsContent
value="songs" value="songs"
className="p-5 pt-0 mt-0 flex-1 flex flex-col min-h-0" className="mt-0 flex min-h-0 flex-1 flex-col p-5 pt-0"
> >
<SongsView /> <SongsView />
</TabsContent> </TabsContent>
@ -97,12 +97,15 @@ function SoundEffectsView() {
loadMore, loadMore,
hasNextPage, hasNextPage,
isLoadingMore, isLoadingMore,
} = useSoundSearch({ query: searchQuery, commercialOnly: showCommercialOnly }); } = useSoundSearch({
query: searchQuery,
commercialOnly: showCommercialOnly,
});
// Audio playback state // Audio playback state
const [playingId, setPlayingId] = useState<number | null>(null); const [playingId, setPlayingId] = useState<number | null>(null);
const [audioElement, setAudioElement] = useState<HTMLAudioElement | null>( const [audioElement, setAudioElement] = useState<HTMLAudioElement | null>(
null null,
); );
const { scrollAreaRef, handleScroll } = useInfiniteScroll({ const { scrollAreaRef, handleScroll } = useInfiniteScroll({
@ -125,7 +128,7 @@ function SoundEffectsView() {
} }
const response = await fetch( const response = await fetch(
"/api/sounds/search?page_size=50&sort=downloads" "/api/sounds/search?page_size=50&sort=downloads",
); );
if (!ignore) { if (!ignore) {
@ -146,7 +149,9 @@ function SoundEffectsView() {
console.error("Failed to fetch top sounds:", error); console.error("Failed to fetch top sounds:", error);
setError({ setError({
error: error:
error instanceof Error ? error.message : "Failed to load sounds", error instanceof Error
? error.message
: "Failed to load sounds",
}); });
} }
} finally { } finally {
@ -221,7 +226,7 @@ function SoundEffectsView() {
}; };
return ( return (
<div className="flex flex-col gap-5 mt-1 h-full"> <div className="mt-1 flex h-full flex-col gap-5">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Input <Input
placeholder="Search sound effects" placeholder="Search sound effects"
@ -249,7 +254,7 @@ function SoundEffectsView() {
> >
Show only commercially licensed Show only commercially licensed
</DropdownMenuCheckboxItem> </DropdownMenuCheckboxItem>
<div className="px-2 py-1.5 text-xs text-muted-foreground"> <div className="text-muted-foreground px-2 py-1.5 text-xs">
{showCommercialOnly {showCommercialOnly
? "Only showing sounds licensed for commercial use" ? "Only showing sounds licensed for commercial use"
: "Showing all sounds regardless of license"} : "Showing all sounds regardless of license"}
@ -260,7 +265,7 @@ function SoundEffectsView() {
<div className="relative h-full overflow-hidden"> <div className="relative h-full overflow-hidden">
<ScrollArea <ScrollArea
className="flex-1 h-full" className="h-full flex-1"
ref={scrollAreaRef} ref={scrollAreaRef}
onScrollCapture={handleScrollWithPosition} onScrollCapture={handleScrollWithPosition}
> >
@ -289,7 +294,7 @@ function SoundEffectsView() {
</div> </div>
)} )}
{isLoadingMore && ( {isLoadingMore && (
<div className="text-muted-foreground text-sm text-center py-4"> <div className="text-muted-foreground py-4 text-center text-sm">
Loading more sounds... Loading more sounds...
</div> </div>
)} )}
@ -314,7 +319,7 @@ function SavedSoundsView() {
// Audio playback state // Audio playback state
const [playingId, setPlayingId] = useState<number | null>(null); const [playingId, setPlayingId] = useState<number | null>(null);
const [audioElement, setAudioElement] = useState<HTMLAudioElement | null>( const [audioElement, setAudioElement] = useState<HTMLAudioElement | null>(
null null,
); );
// Clear confirmation dialog state // Clear confirmation dialog state
@ -378,7 +383,7 @@ function SavedSoundsView() {
if (isLoadingSavedSounds) { if (isLoadingSavedSounds) {
return ( return (
<div className="flex items-center justify-center h-full"> <div className="flex h-full items-center justify-center">
<div className="text-muted-foreground text-sm"> <div className="text-muted-foreground text-sm">
Loading saved sounds... Loading saved sounds...
</div> </div>
@ -388,7 +393,7 @@ function SavedSoundsView() {
if (savedSoundsError) { if (savedSoundsError) {
return ( return (
<div className="flex items-center justify-center h-full"> <div className="flex h-full items-center justify-center">
<div className="text-destructive text-sm"> <div className="text-destructive text-sm">
Error: {savedSoundsError} Error: {savedSoundsError}
</div> </div>
@ -398,14 +403,14 @@ function SavedSoundsView() {
if (savedSounds.length === 0) { if (savedSounds.length === 0) {
return ( return (
<div className="bg-panel h-full p-4 flex flex-col items-center justify-center gap-3"> <div className="bg-panel flex h-full flex-col items-center justify-center gap-3 p-4">
<HeartIcon <HeartIcon
className="w-10 h-10 text-muted-foreground" className="text-muted-foreground h-10 w-10"
strokeWidth={1.5} strokeWidth={1.5}
/> />
<div className="flex flex-col gap-2 text-center"> <div className="flex flex-col gap-2 text-center">
<p className="text-lg font-medium">No saved sounds</p> <p className="text-lg font-medium">No saved sounds</p>
<p className="text-sm text-muted-foreground text-balance"> <p className="text-muted-foreground text-sm text-balance">
Click the heart icon on any sound to save it here Click the heart icon on any sound to save it here
</p> </p>
</div> </div>
@ -414,9 +419,9 @@ function SavedSoundsView() {
} }
return ( return (
<div className="flex flex-col gap-5 mt-1 h-full"> <div className="mt-1 flex h-full flex-col gap-5">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<p className="text-sm text-muted-foreground"> <p className="text-muted-foreground text-sm">
{savedSounds.length} saved{" "} {savedSounds.length} saved{" "}
{savedSounds.length === 1 ? "sound" : "sounds"} {savedSounds.length === 1 ? "sound" : "sounds"}
</p> </p>
@ -425,7 +430,7 @@ function SavedSoundsView() {
<Button <Button
variant="text" variant="text"
size="sm" size="sm"
className="h-auto text-muted-foreground hover:text-destructive !opacity-100" className="text-muted-foreground hover:text-destructive h-auto !opacity-100"
> >
Clear all Clear all
</Button> </Button>
@ -457,7 +462,7 @@ function SavedSoundsView() {
</div> </div>
<div className="relative h-full overflow-hidden"> <div className="relative h-full overflow-hidden">
<ScrollArea className="flex-1 h-full"> <ScrollArea className="h-full flex-1">
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
{savedSounds.map((sound) => ( {savedSounds.map((sound) => (
<AudioItem <AudioItem
@ -515,11 +520,11 @@ function AudioItem({
return ( return (
<div <div
className="group flex items-center gap-3 opacity-100 hover:opacity-75 transition-opacity cursor-pointer" className="group flex cursor-pointer items-center gap-3 opacity-100 transition-opacity hover:opacity-75"
onClick={handleClick} onClick={handleClick}
> >
<div className="relative w-12 h-12 bg-accent rounded-md flex items-center justify-center overflow-hidden shrink-0"> <div className="bg-accent relative flex h-12 w-12 shrink-0 items-center justify-center overflow-hidden rounded-md">
<div className="absolute inset-0 bg-gradient-to-br from-primary/20 to-transparent" /> <div className="from-primary/20 absolute inset-0 bg-gradient-to-br to-transparent" />
{isPlaying ? ( {isPlaying ? (
<PauseIcon className="size-5" /> <PauseIcon className="size-5" />
) : ( ) : (
@ -527,9 +532,9 @@ function AudioItem({
)} )}
</div> </div>
<div className="flex-1 min-w-0 overflow-hidden"> <div className="min-w-0 flex-1 overflow-hidden">
<p className="font-medium truncate text-sm">{sound.name}</p> <p className="truncate text-sm font-medium">{sound.name}</p>
<span className="text-xs text-muted-foreground truncate block"> <span className="text-muted-foreground block truncate text-xs">
{sound.username} {sound.username}
</span> </span>
</div> </div>
@ -538,7 +543,7 @@ function AudioItem({
<Button <Button
variant="text" variant="text"
size="icon" size="icon"
className="text-muted-foreground hover:text-foreground !opacity-100 w-auto" className="text-muted-foreground hover:text-foreground w-auto !opacity-100"
onClick={handleAddToTimeline} onClick={handleAddToTimeline}
title="Add to timeline" title="Add to timeline"
> >
@ -547,10 +552,11 @@ function AudioItem({
<Button <Button
variant="text" variant="text"
size="icon" size="icon"
className={`hover:text-foreground !opacity-100 w-auto ${isSaved className={`hover:text-foreground w-auto !opacity-100 ${
? "text-red-500 hover:text-red-600" isSaved
: "text-muted-foreground" ? "text-red-500 hover:text-red-600"
}`} : "text-muted-foreground"
}`}
onClick={handleSaveClick} onClick={handleSaveClick}
title={isSaved ? "Remove from saved" : "Save sound"} title={isSaved ? "Remove from saved" : "Save sound"}
> >

View File

@ -108,10 +108,7 @@ function StickerGrid({
}; };
return ( return (
<div <div className="grid gap-2" style={gridStyle}>
className="grid gap-2"
style={gridStyle}
>
{icons.map((iconName) => ( {icons.map((iconName) => (
<StickerItem <StickerItem
key={iconName} key={iconName}
@ -160,7 +157,7 @@ function EmptyView({ message }: { message: string }) {
/> />
<div className="flex flex-col gap-2 text-center"> <div className="flex flex-col gap-2 text-center">
<p className="text-lg font-medium">No stickers found</p> <p className="text-lg font-medium">No stickers found</p>
<p className="text-muted-foreground text-balance text-sm">{message}</p> <p className="text-muted-foreground text-sm text-balance">{message}</p>
</div> </div>
</div> </div>
); );
@ -211,17 +208,17 @@ function StickersContentView({ category }: { category: StickerCategory }) {
const collection = collections[c.prefix]; const collection = collections[c.prefix];
return collection return collection
? { ? {
prefix: c.prefix, prefix: c.prefix,
name: c.name, name: c.name,
total: collection.total, total: collection.total,
} }
: null; : null;
}) })
.filter(Boolean) as Array<{ .filter(Boolean) as Array<{
prefix: string; prefix: string;
name: string; name: string;
total: number; total: number;
}>; }>;
}, [collections, category]); }, [collections, category]);
const { scrollAreaRef, handleScroll } = useInfiniteScroll({ const { scrollAreaRef, handleScroll } = useInfiniteScroll({
@ -485,7 +482,7 @@ function CollectionItem({ title, subtitle, onClick }: CollectionItemProps) {
return ( return (
<Button <Button
variant="outline" variant="outline"
className="h-auto justify-between py-2 rounded-md" className="h-auto justify-between rounded-md py-2"
onClick={onClick} onClick={onClick}
> >
<div className="text-left"> <div className="text-left">
@ -523,7 +520,7 @@ function StickerItem({
const preview = imageError ? ( const preview = imageError ? (
<div className="flex h-full w-full items-center justify-center p-2"> <div className="flex h-full w-full items-center justify-center p-2">
<span className="text-muted-foreground break-all text-center text-xs"> <span className="text-muted-foreground text-center text-xs break-all">
{displayName} {displayName}
</span> </span>
</div> </div>
@ -534,10 +531,10 @@ function StickerItem({
hostIndex === 0 hostIndex === 0
? getIconSvgUrl(iconName, { width: 64, height: 64 }) ? getIconSvgUrl(iconName, { width: 64, height: 64 })
: buildIconSvgUrl( : buildIconSvgUrl(
ICONIFY_HOSTS[Math.min(hostIndex, ICONIFY_HOSTS.length - 1)], ICONIFY_HOSTS[Math.min(hostIndex, ICONIFY_HOSTS.length - 1)],
iconName, iconName,
{ width: 64, height: 64 }, { width: 64, height: 64 },
) )
} }
alt={displayName} alt={displayName}
width={64} width={64}
@ -546,9 +543,9 @@ function StickerItem({
style={ style={
capSize capSize
? { ? {
maxWidth: "var(--sticker-max, 160px)", maxWidth: "var(--sticker-max, 160px)",
maxHeight: "var(--sticker-max, 160px)", maxHeight: "var(--sticker-max, 160px)",
} }
: undefined : undefined
} }
onError={() => { onError={() => {

View File

@ -28,7 +28,7 @@ export function TextView() {
name="Default text" name="Default text"
preview={ preview={
<div className="bg-panel-accent flex size-full items-center justify-center rounded"> <div className="bg-panel-accent flex size-full items-center justify-center rounded">
<span className="select-none text-xs">Default text</span> <span className="text-xs select-none">Default text</span>
</div> </div>
} }
dragData={{ dragData={{

View File

@ -52,11 +52,11 @@ export function ExportButton() {
} }
}} }}
> >
<div className="bg-linear-270 relative flex items-center gap-1.5 rounded-[0.8rem] from-[#2567EC] to-[#37B6F7] px-4 py-1 shadow-[0_1px_3px_0px_rgba(0,0,0,0.65)]"> <div className="relative flex items-center gap-1.5 rounded-[0.8rem] bg-linear-270 from-[#2567EC] to-[#37B6F7] px-4 py-1 shadow-[0_1px_3px_0px_rgba(0,0,0,0.65)]">
<TransitionUpIcon className="z-50" /> <TransitionUpIcon className="z-50" />
<span className="z-50 text-[0.875rem]">Export</span> <span className="z-50 text-[0.875rem]">Export</span>
<div className="bg-linear-to-t absolute left-0 top-0 z-10 flex h-full w-full items-center justify-center rounded-[0.8rem] from-white/0 to-white/50"> <div className="absolute top-0 left-0 z-10 flex h-full w-full items-center justify-center rounded-[0.8rem] bg-linear-to-t from-white/0 to-white/50">
<div className="bg-linear-270 absolute top-[0.08rem] z-50 h-[calc(100%-2px)] w-[calc(100%-2px)] rounded-[0.8rem] from-[#2567EC] to-[#37B6F7]"></div> <div className="absolute top-[0.08rem] z-50 h-[calc(100%-2px)] w-[calc(100%-2px)] rounded-[0.8rem] bg-linear-270 from-[#2567EC] to-[#37B6F7]"></div>
</div> </div>
</div> </div>
</button> </button>
@ -263,7 +263,7 @@ function ExportPopover({
<Button <Button
variant="outline" variant="outline"
className="w-full rounded-md" className="w-full rounded-md"
onClick={() => { }} onClick={() => {}}
> >
Cancel Cancel
</Button> </Button>

View File

@ -5,11 +5,11 @@ import Image from "next/image";
function TikTokGuide() { function TikTokGuide() {
return ( return (
<div className="absolute inset-0 pointer-events-none"> <div className="pointer-events-none absolute inset-0">
<Image <Image
src="/platform-guides/tiktok-blueprint.png" src="/platform-guides/tiktok-blueprint.png"
alt="TikTok layout guide" alt="TikTok layout guide"
className="absolute inset-0 w-full h-full object-contain" className="absolute inset-0 h-full w-full object-contain"
draggable={false} draggable={false}
fill fill
/> />

View File

@ -42,7 +42,7 @@ export function PanelBaseView({
ref, ref,
}: PanelBaseViewProps) { }: PanelBaseViewProps) {
return ( return (
<div className={cn("h-full flex flex-col", className)} ref={ref}> <div className={cn("flex h-full flex-col", className)} ref={ref}>
{!tabs || tabs.length === 0 ? ( {!tabs || tabs.length === 0 ? (
<ViewContent className={className}>{children}</ViewContent> <ViewContent className={className}>{children}</ViewContent>
) : ( ) : (
@ -50,15 +50,15 @@ export function PanelBaseView({
defaultValue={defaultTab} defaultValue={defaultTab}
value={value} value={value}
onValueChange={onValueChange} onValueChange={onValueChange}
className="flex flex-col h-full" className="flex h-full flex-col"
> >
<div className="sticky top-0 z-10 bg-panel"> <div className="bg-panel sticky top-0 z-10">
<div className="px-3 pt-3 pb-0"> <div className="px-3 pt-3 pb-0">
<TabsList> <TabsList>
{tabs.map((tab) => ( {tabs.map((tab) => (
<TabsTrigger key={tab.value} value={tab.value}> <TabsTrigger key={tab.value} value={tab.value}>
{tab.icon ? ( {tab.icon ? (
<span className="inline-flex items-center mr-1"> <span className="mr-1 inline-flex items-center">
{tab.icon} {tab.icon}
</span> </span>
) : null} ) : null}
@ -73,7 +73,7 @@ export function PanelBaseView({
<TabsContent <TabsContent
key={tab.value} key={tab.value}
value={tab.value} value={tab.value}
className="mt-0 flex-1 flex flex-col min-h-0" className="mt-0 flex min-h-0 flex-1 flex-col"
> >
<ViewContent className={className}>{tab.content}</ViewContent> <ViewContent className={className}>{tab.content}</ViewContent>
</TabsContent> </TabsContent>

View File

@ -9,7 +9,7 @@ import { useEditor } from "@/hooks/use-editor";
import { useElementSelection } from "@/hooks/timeline/element/use-element-selection"; import { useElementSelection } from "@/hooks/timeline/element/use-element-selection";
export function PropertiesPanel() { export function PropertiesPanel() {
const editor = useEditor(); const editor = useEditor();
const { selectedElements } = useElementSelection(); const { selectedElements } = useElementSelection();
const elementsWithTracks = editor.timeline.getElementsWithTracks({ const elementsWithTracks = editor.timeline.getElementsWithTracks({
@ -57,7 +57,7 @@ function EmptyView() {
/> />
<div className="flex flex-col gap-2 text-center"> <div className="flex flex-col gap-2 text-center">
<p className="text-lg font-medium">Its empty here</p> <p className="text-lg font-medium">Its empty here</p>
<p className="text-muted-foreground text-balance text-sm"> <p className="text-muted-foreground text-sm text-balance">
Click an element on the timeline to edit its properties Click an element on the timeline to edit its properties
</p> </p>
</div> </div>

View File

@ -20,7 +20,7 @@ export function PropertyItem({
direction === "row" direction === "row"
? "items-center justify-between gap-6" ? "items-center justify-between gap-6"
: "flex-col gap-1.5", : "flex-col gap-1.5",
className className,
)} )}
> >
{children} {children}
@ -36,7 +36,7 @@ export function PropertyItemLabel({
className?: string; className?: string;
}) { }) {
return ( return (
<label className={cn("text-xs text-muted-foreground", className)}> <label className={cn("text-muted-foreground text-xs", className)}>
{children} {children}
</label> </label>
); );
@ -72,7 +72,7 @@ export function PropertyGroup({
return ( return (
<PropertyItem direction="column" className={cn("gap-3", className)}> <PropertyItem direction="column" className={cn("gap-3", className)}>
<div <div
className="flex items-center gap-1.5 cursor-pointer" className="flex cursor-pointer items-center gap-1.5"
onClick={() => setIsExpanded(!isExpanded)} onClick={() => setIsExpanded(!isExpanded)}
> >
<PropertyItemLabel className={cn("cursor-pointer", titleClassName)}> <PropertyItemLabel className={cn("cursor-pointer", titleClassName)}>

View File

@ -118,7 +118,11 @@ export function TextProperties({
}); });
}; };
const handleTransparentToggle = ({ isTransparent }: { isTransparent: boolean }) => { const handleTransparentToggle = ({
isTransparent,
}: {
isTransparent: boolean;
}) => {
const newColor = isTransparent ? "transparent" : lastSelectedColor.current; const newColor = isTransparent ? "transparent" : lastSelectedColor.current;
editor.timeline.updateTextElement({ editor.timeline.updateTextElement({
trackId, trackId,
@ -146,7 +150,7 @@ export function TextProperties({
<Textarea <Textarea
placeholder="Name" placeholder="Name"
defaultValue={element.content} defaultValue={element.content}
className="min-h-18 bg-panel-accent resize-none" className="bg-panel-accent min-h-18 resize-none"
onChange={(e) => onChange={(e) =>
editor.timeline.updateTextElement({ editor.timeline.updateTextElement({
trackId, trackId,
@ -287,9 +291,11 @@ export function TextProperties({
value={fontSizeInput} value={fontSizeInput}
min={8} min={8}
max={300} max={300}
onChange={(e) => handleFontSizeChange({ value: e.target.value })} onChange={(e) =>
handleFontSizeChange({ value: e.target.value })
}
onBlur={handleFontSizeBlur} onBlur={handleFontSizeBlur}
className="bg-panel-accent h-7 w-12 rounded-sm px-2 text-center !text-xs [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none" className="bg-panel-accent h-7 w-12 [appearance:textfield] rounded-sm px-2 text-center !text-xs [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
/> />
</div> </div>
</PropertyItemValue> </PropertyItemValue>
@ -299,8 +305,7 @@ export function TextProperties({
<PropertyItemValue> <PropertyItemValue>
<ColorPicker <ColorPicker
value={uppercase({ value={uppercase({
string: string: (element.color || "FFFFFF").replace("#", ""),
(element.color || "FFFFFF").replace("#", "")
})} })}
onChange={(color) => { onChange={(color) => {
editor.timeline.updateTextElement({ editor.timeline.updateTextElement({
@ -337,9 +342,11 @@ export function TextProperties({
value={opacityInput} value={opacityInput}
min={0} min={0}
max={100} max={100}
onChange={(e) => handleOpacityChange({ value: e.target.value })} onChange={(e) =>
handleOpacityChange({ value: e.target.value })
}
onBlur={handleOpacityBlur} onBlur={handleOpacityBlur}
className="bg-panel-accent h-7 w-12 rounded-sm text-center !text-xs [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none" className="bg-panel-accent h-7 w-12 [appearance:textfield] rounded-sm text-center !text-xs [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
/> />
</div> </div>
</PropertyItemValue> </PropertyItemValue>
@ -353,12 +360,11 @@ export function TextProperties({
string: string:
element.backgroundColor === "transparent" element.backgroundColor === "transparent"
? lastSelectedColor.current.replace("#", "") ? lastSelectedColor.current.replace("#", "")
: (element.backgroundColor).replace( : element.backgroundColor.replace("#", ""),
"#",
"",
),
})} })}
onChange={(color) => handleColorChange({ color: `#${color}` })} onChange={(color) =>
handleColorChange({ color: `#${color}` })
}
containerRef={containerRef} containerRef={containerRef}
className={ className={
element.backgroundColor === "transparent" element.backgroundColor === "transparent"
@ -374,7 +380,8 @@ export function TextProperties({
size="icon" size="icon"
onClick={() => onClick={() =>
handleTransparentToggle({ handleTransparentToggle({
isTransparent: element.backgroundColor !== "transparent", isTransparent:
element.backgroundColor !== "transparent",
}) })
} }
className="bg-panel-accent size-9 overflow-hidden rounded-full p-0" className="bg-panel-accent size-9 overflow-hidden rounded-full p-0"
@ -383,7 +390,7 @@ export function TextProperties({
className={cn( className={cn(
"text-foreground", "text-foreground",
element.backgroundColor === "transparent" && element.backgroundColor === "transparent" &&
"text-primary", "text-primary",
)} )}
/> />
</Button> </Button>

View File

@ -147,11 +147,11 @@ export function ScenesView({ children }: { children: React.ReactNode }) {
className={cn( className={cn(
"w-full justify-between font-normal", "w-full justify-between font-normal",
currentScene?.id === scene.id && currentScene?.id === scene.id &&
!isSelectMode && !isSelectMode &&
"border-primary !text-primary", "border-primary !text-primary",
isSelectMode && isSelectMode &&
selectedScenes.has(scene.id) && selectedScenes.has(scene.id) &&
"bg-accent border-foreground/30", "bg-accent border-foreground/30",
)} )}
onClick={() => handleSceneSwitch(scene.id)} onClick={() => handleSceneSwitch(scene.id)}
> >
@ -159,8 +159,8 @@ export function ScenesView({ children }: { children: React.ReactNode }) {
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{((isSelectMode && selectedScenes.has(scene.id)) || {((isSelectMode && selectedScenes.has(scene.id)) ||
(!isSelectMode && currentScene?.id === scene.id)) && ( (!isSelectMode && currentScene?.id === scene.id)) && (
<Check className="h-4 w-4" /> <Check className="h-4 w-4" />
)} )}
</div> </div>
</Button> </Button>
))} ))}

View File

@ -79,8 +79,7 @@ export function AudioWaveform({
} else { } else {
try { try {
newWaveSurfer.destroy(); newWaveSurfer.destroy();
} catch { } catch {}
}
return; return;
} }
@ -121,8 +120,7 @@ export function AudioWaveform({
requestAnimationFrame(() => { requestAnimationFrame(() => {
try { try {
wsToDestroy.destroy(); wsToDestroy.destroy();
} catch { } catch {}
}
if (mounted) { if (mounted) {
initWaveSurfer(); initWaveSurfer();
} }
@ -142,8 +140,7 @@ export function AudioWaveform({
requestAnimationFrame(() => { requestAnimationFrame(() => {
try { try {
wsToDestroy.destroy(); wsToDestroy.destroy();
} catch { } catch {}
}
}); });
} }
}; };
@ -155,7 +152,7 @@ export function AudioWaveform({
className={`flex items-center justify-center ${className}`} className={`flex items-center justify-center ${className}`}
style={{ height }} style={{ height }}
> >
<span className="text-xs text-foreground/60">Audio unavailable</span> <span className="text-foreground/60 text-xs">Audio unavailable</span>
</div> </div>
); );
} }
@ -164,7 +161,7 @@ export function AudioWaveform({
<div className={`relative ${className}`}> <div className={`relative ${className}`}>
{isLoading && ( {isLoading && (
<div className="absolute inset-0 flex items-center justify-center"> <div className="absolute inset-0 flex items-center justify-center">
<span className="text-xs text-foreground/60">Loading...</span> <span className="text-foreground/60 text-xs">Loading...</span>
</div> </div>
)} )}
<div <div

View File

@ -79,7 +79,7 @@ export function TimelineBookmark({
}} }}
onClick={(event) => handleBookmarkClick({ event })} onClick={(event) => handleBookmarkClick({ event })}
> >
<div className="text-primary absolute left-[-5px] top-[-1px]"> <div className="text-primary absolute top-[-1px] left-[-5px]">
<Bookmark className="fill-primary size-3" /> <Bookmark className="fill-primary size-3" />
</div> </div>
</div> </div>

View File

@ -14,7 +14,7 @@ export function DragLine({ dropTarget, tracks, isVisible }: DragLineProps) {
return ( return (
<div <div
className="bg-primary pointer-events-none absolute left-0 right-0 z-50 h-0.5" className="bg-primary pointer-events-none absolute right-0 left-0 z-50 h-0.5"
style={{ top: `${y}px` }} style={{ top: `${y}px` }}
/> />
); );

View File

@ -1,7 +1,15 @@
"use client"; "use client";
import { ScrollArea } from "@/components/ui/scroll-area"; import { ScrollArea } from "@/components/ui/scroll-area";
import { Eye, EyeOff, VolumeOff, Volume2, LucideIcon, EyeIcon, Trash2 } from "lucide-react"; import {
Eye,
EyeOff,
VolumeOff,
Volume2,
LucideIcon,
EyeIcon,
Trash2,
} from "lucide-react";
import { import {
ContextMenu, ContextMenu,
ContextMenuContent, ContextMenuContent,
@ -249,7 +257,7 @@ export function Timeline() {
{tracks.length > 0 && ( {tracks.length > 0 && (
<div <div
ref={trackLabelsRef} ref={trackLabelsRef}
className="z-100 bg-panel w-28 shrink-0 overflow-y-auto border-r" className="bg-panel z-100 w-28 shrink-0 overflow-y-auto border-r"
style={{ paddingTop: TIMELINE_CONSTANTS.PADDING_TOP }} style={{ paddingTop: TIMELINE_CONSTANTS.PADDING_TOP }}
> >
<ScrollArea className="h-full w-full" ref={trackLabelsScrollRef}> <ScrollArea className="h-full w-full" ref={trackLabelsScrollRef}>
@ -378,7 +386,7 @@ export function Timeline() {
<ContextMenu key={track.id}> <ContextMenu key={track.id}>
<ContextMenuTrigger asChild> <ContextMenuTrigger asChild>
<div <div
className="absolute left-0 right-0" className="absolute right-0 left-0"
style={{ style={{
top: `${getCumulativeHeightBefore({ top: `${getCumulativeHeightBefore({
tracks, tracks,
@ -412,7 +420,6 @@ export function Timeline() {
> >
<Volume2 /> <Volume2 />
<span> <span>
{canTracktHaveAudio(track) && track.muted {canTracktHaveAudio(track) && track.muted
? "Unmute track" ? "Unmute track"
: "Mute track"} : "Mute track"}

View File

@ -38,7 +38,7 @@ export function SnapIndicator({
return ( return (
<div <div
className="z-90 pointer-events-none absolute" className="pointer-events-none absolute z-90"
style={{ style={{
left: `${leftPosition}px`, left: `${leftPosition}px`,
top: topPosition, top: topPosition,

View File

@ -82,18 +82,14 @@ export function TimelineElement({
const hasAudio = mediaSupportsAudio({ media: mediaAsset }); const hasAudio = mediaSupportsAudio({ media: mediaAsset });
const { const { handleResizeStart, isResizing, currentStartTime, currentDuration } =
handleResizeStart, useTimelineElementResize({
isResizing, element,
currentStartTime, track,
currentDuration, zoomLevel,
} = useTimelineElementResize({ onSnapPointChange,
element, onResizeStateChange,
track, });
zoomLevel,
onSnapPointChange,
onResizeStateChange,
});
const isCurrentElementSelected = selectedElements.some( const isCurrentElementSelected = selectedElements.some(
(selected) => (selected) =>
@ -139,8 +135,9 @@ export function TimelineElement({
<ContextMenu> <ContextMenu>
<ContextMenuTrigger asChild> <ContextMenuTrigger asChild>
<div <div
className={`absolute top-0 h-full select-none ${isBeingDragged ? "z-30" : "z-10" className={`absolute top-0 h-full select-none ${
}`} isBeingDragged ? "z-30" : "z-10"
}`}
style={{ style={{
left: `${elementLeft}px`, left: `${elementLeft}px`,
width: `${elementWidth}px`, width: `${elementWidth}px`,
@ -299,7 +296,7 @@ function ElementInner({
</div> </div>
{(hasAudio ? isMuted : canElementBeHidden(element) && element.hidden) && ( {(hasAudio ? isMuted : canElementBeHidden(element) && element.hidden) && (
<div className="pointer-events-none absolute inset-0 flex items-center justify-center bg-black bg-opacity-50"> <div className="bg-opacity-50 pointer-events-none absolute inset-0 flex items-center justify-center bg-black">
{hasAudio ? ( {hasAudio ? (
<VolumeX className="size-6 text-white" /> <VolumeX className="size-6 text-white" />
) : ( ) : (
@ -342,7 +339,7 @@ function ResizeHandle({
const isLeft = side === "left"; const isLeft = side === "left";
return ( return (
<div <div
className={`bg-primary absolute bottom-0 top-0 z-50 flex w-[0.6rem] items-center justify-center ${isLeft ? "left-0 cursor-w-resize" : "right-0 cursor-e-resize"}`} className={`bg-primary absolute top-0 bottom-0 z-50 flex w-[0.6rem] items-center justify-center ${isLeft ? "left-0 cursor-w-resize" : "right-0 cursor-e-resize"}`}
onMouseDown={(e) => handleResizeStart({ e, elementId, side })} onMouseDown={(e) => handleResizeStart({ e, elementId, side })}
> >
<div className="bg-foreground h-[1.5rem] w-[0.2rem] rounded-full" /> <div className="bg-foreground h-[1.5rem] w-[0.2rem] rounded-full" />
@ -383,7 +380,8 @@ function ElementContent({
} }
if (element.type === "audio") { if (element.type === "audio") {
const audioBuffer = element.sourceType === "library" ? element.buffer : undefined; const audioBuffer =
element.sourceType === "library" ? element.buffer : undefined;
const audioUrl = const audioUrl =
element.sourceType === "library" element.sourceType === "library"
@ -436,7 +434,7 @@ function ElementContent({
className={`relative size-full ${isSelected ? "bg-primary" : "bg-transparent"}`} className={`relative size-full ${isSelected ? "bg-primary" : "bg-transparent"}`}
> >
<div <div
className="absolute left-0 right-0" className="absolute right-0 left-0"
style={{ style={{
backgroundImage: imageUrl ? `url(${imageUrl})` : "none", backgroundImage: imageUrl ? `url(${imageUrl})` : "none",
backgroundRepeat: "repeat-x", backgroundRepeat: "repeat-x",

View File

@ -99,7 +99,7 @@ export function TimelinePlayhead({
<div className="bg-foreground absolute left-0 h-full w-0.5 cursor-col-resize" /> <div className="bg-foreground absolute left-0 h-full w-0.5 cursor-col-resize" />
<div <div
className={`shadow-xs absolute left-1/2 top-1 size-3 -translate-x-1/2 transform rounded-full border-2 ${isSnappingToPlayhead ? "bg-foreground border-foreground" : "bg-foreground border-foreground/50"}`} className={`absolute top-1 left-1/2 size-3 -translate-x-1/2 transform rounded-full border-2 shadow-xs ${isSnappingToPlayhead ? "bg-foreground border-foreground" : "bg-foreground border-foreground/50"}`}
/> />
</div> </div>
); );

View File

@ -52,13 +52,10 @@ export function TimelineToolbar({
const newZoomLevel = const newZoomLevel =
direction === "in" direction === "in"
? Math.min( ? Math.min(
TIMELINE_CONSTANTS.ZOOM_MAX, TIMELINE_CONSTANTS.ZOOM_MAX,
zoomLevel + TIMELINE_CONSTANTS.ZOOM_STEP, zoomLevel + TIMELINE_CONSTANTS.ZOOM_STEP,
) )
: Math.max( : Math.max(minZoom, zoomLevel - TIMELINE_CONSTANTS.ZOOM_STEP);
minZoom,
zoomLevel - TIMELINE_CONSTANTS.ZOOM_STEP,
);
setZoomLevel({ zoom: newZoomLevel }); setZoomLevel({ zoom: newZoomLevel });
}; };
@ -146,7 +143,7 @@ function ToolbarLeftSection() {
icon={<SplitSquareHorizontal />} icon={<SplitSquareHorizontal />}
tooltip="Coming soon" /* separate audio */ tooltip="Coming soon" /* separate audio */
disabled={true} disabled={true}
onClick={({ event }) => { }} onClick={({ event }) => {}}
/> />
<ToolbarButton <ToolbarButton
@ -161,7 +158,7 @@ function ToolbarLeftSection() {
icon={<Snowflake />} icon={<Snowflake />}
tooltip="Coming soon" /* freeze frame */ tooltip="Coming soon" /* freeze frame */
disabled={true} disabled={true}
onClick={({ event }) => { }} onClick={({ event }) => {}}
/> />
<ToolbarButton <ToolbarButton
@ -231,10 +228,7 @@ function SceneSelector() {
<SplitButtonLeft>{currentScene?.name || "No Scene"}</SplitButtonLeft> <SplitButtonLeft>{currentScene?.name || "No Scene"}</SplitButtonLeft>
<SplitButtonSeparator /> <SplitButtonSeparator />
<ScenesView> <ScenesView>
<SplitButtonRight <SplitButtonRight onClick={() => {}} type="button">
onClick={() => { }}
type="button"
>
<LayersIcon className="size-4" /> <LayersIcon className="size-4" />
</SplitButtonRight> </SplitButtonRight>
</ScenesView> </ScenesView>
@ -254,19 +248,28 @@ function ToolbarRightSection({
onZoomChange: (zoom: number) => void; onZoomChange: (zoom: number) => void;
onZoom: (options: { direction: "in" | "out" }) => void; onZoom: (options: { direction: "in" | "out" }) => void;
}) { }) {
const { snappingEnabled, rippleEditingEnabled, toggleSnapping, toggleRippleEditing } = useTimelineStore(); const {
snappingEnabled,
rippleEditingEnabled,
toggleSnapping,
toggleRippleEditing,
} = useTimelineStore();
return ( return (
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<TooltipProvider delayDuration={500}> <TooltipProvider delayDuration={500}>
<ToolbarButton <ToolbarButton
icon={<Magnet className={cn(snappingEnabled ? "text-primary" : "")} />} icon={
<Magnet className={cn(snappingEnabled ? "text-primary" : "")} />
}
tooltip="Auto snapping" tooltip="Auto snapping"
onClick={() => toggleSnapping()} onClick={() => toggleSnapping()}
/> />
<ToolbarButton <ToolbarButton
icon={<Link className={cn(rippleEditingEnabled ? "text-primary" : "")} />} icon={
<Link className={cn(rippleEditingEnabled ? "text-primary" : "")} />
}
tooltip="Ripple editing" tooltip="Ripple editing"
onClick={() => toggleRippleEditing()} onClick={() => toggleRippleEditing()}
/> />

View File

@ -58,7 +58,7 @@ export function TimelineTrackContent({
}); });
const hasSelectedElements = track.elements.some((element) => const hasSelectedElements = track.elements.some((element) =>
isElementSelected({ trackId: track.id, elementId: element.id }) isElementSelected({ trackId: track.id, elementId: element.id }),
); );
return ( return (

View File

@ -115,7 +115,7 @@ export function Header() {
))} ))}
</nav> </nav>
<ThemeToggle <ThemeToggle
className="absolute bottom-8 right-8 size-10" className="absolute right-8 bottom-8 size-10"
iconClassName="!size-[1.2rem]" iconClassName="!size-[1.2rem]"
onToggle={(e) => { onToggle={(e) => {
e.preventDefault(); e.preventDefault();

View File

@ -52,11 +52,11 @@ export function KeyboardShortcutsHelp() {
// Auto-save the new keybinding // Auto-save the new keybinding
const conflict = validateKeybinding( const conflict = validateKeybinding(
keyString, keyString,
recordingShortcut.action recordingShortcut.action,
); );
if (conflict) { if (conflict) {
toast.error( toast.error(
`Key "${keyString}" is already bound to "${conflict.existingAction}"` `Key "${keyString}" is already bound to "${conflict.existingAction}"`,
); );
setRecordingShortcut(null); setRecordingShortcut(null);
return; return;
@ -110,10 +110,10 @@ export function KeyboardShortcutsHelp() {
Shortcuts Shortcuts
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent className="max-w-2xl max-h-[80vh] flex flex-col p-0"> <DialogContent className="flex max-h-[80vh] max-w-2xl flex-col p-0">
<DialogHeader className="flex-shrink-0 p-6 pb-0"> <DialogHeader className="flex-shrink-0 p-6 pb-0">
<DialogTitle className="flex items-center gap-2"> <DialogTitle className="flex items-center gap-2">
<Keyboard className="w-5 h-5" /> <Keyboard className="h-5 w-5" />
Keyboard Shortcuts Keyboard Shortcuts
</DialogTitle> </DialogTitle>
<DialogDescription> <DialogDescription>
@ -122,11 +122,11 @@ export function KeyboardShortcutsHelp() {
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="overflow-y-auto flex-grow scrollbar-thin"> <div className="scrollbar-thin flex-grow overflow-y-auto">
<div className="space-y-6 p-6 pt-2"> <div className="space-y-6 p-6 pt-2">
{categories.map((category) => ( {categories.map((category) => (
<div key={category} className="flex flex-col gap-1"> <div key={category} className="flex flex-col gap-1">
<h3 className="text-xs text-muted-foreground uppercase tracking-wide font-medium"> <h3 className="text-muted-foreground text-xs font-medium tracking-wide uppercase">
{category} {category}
</h3> </h3>
<div className="space-y-1"> <div className="space-y-1">
@ -153,7 +153,6 @@ export function KeyboardShortcutsHelp() {
</Button> </Button>
</DialogFooter> </DialogFooter>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
); );
} }
@ -204,7 +203,7 @@ function ShortcutItem({
})} })}
</div> </div>
{index < displayKeys.length - 1 && ( {index < displayKeys.length - 1 && (
<span className="text-xs text-muted-foreground">or</span> <span className="text-muted-foreground text-xs">or</span>
)} )}
</div> </div>
))} ))}
@ -240,4 +239,4 @@ function EditableShortcutKey({
{children} {children}
</Button> </Button>
); );
} }

View File

@ -111,12 +111,12 @@ export function Handlebars({ children }: HandlebarsProps) {
width > 0 ? (rightHandle / (width + 10)) * 100 : 0; width > 0 ? (rightHandle / (width + 10)) * 100 : 0;
return ( return (
<div className="leading-16 flex justify-center gap-4"> <div className="flex justify-center gap-4 leading-16">
<div ref={containerRef} className="relative mt-0.5 -rotate-[2.76deg]"> <div ref={containerRef} className="relative mt-0.5 -rotate-[2.76deg]">
<div className="absolute inset-0 z-10 flex h-full w-full justify-between rounded-2xl border border-yellow-500"> <div className="absolute inset-0 z-10 flex h-full w-full justify-between rounded-2xl border border-yellow-500">
<div <div
ref={leftHandleRef} ref={leftHandleRef}
className="bg-background absolute left-0 z-20 flex h-full w-7 cursor-ew-resize touch-none select-none items-center justify-center rounded-full border border-yellow-500" className="bg-background absolute left-0 z-20 flex h-full w-7 cursor-ew-resize touch-none items-center justify-center rounded-full border border-yellow-500 select-none"
style={{ style={{
translate: `${leftHandle}px 0`, translate: `${leftHandle}px 0`,
}} }}
@ -135,7 +135,7 @@ export function Handlebars({ children }: HandlebarsProps) {
<div <div
ref={rightHandleRef} ref={rightHandleRef}
className="bg-background absolute -left-[30px] z-20 flex h-full w-7 cursor-ew-resize touch-none select-none items-center justify-center rounded-full border border-yellow-500" className="bg-background absolute -left-[30px] z-20 flex h-full w-7 cursor-ew-resize touch-none items-center justify-center rounded-full border border-yellow-500 select-none"
style={{ style={{
translate: `${rightHandle}px 0`, translate: `${rightHandle}px 0`,
}} }}

View File

@ -8,31 +8,31 @@ import Link from "next/link";
export function Hero() { export function Hero() {
return ( return (
<div className="min-h-[calc(100svh-4.5rem)] flex flex-col justify-between items-center text-center px-4"> <div className="flex min-h-[calc(100svh-4.5rem)] flex-col items-center justify-between px-4 text-center">
<Image <Image
className="absolute top-0 left-0 -z-50 size-full object-cover invert dark:invert-0 opacity-85" className="absolute top-0 left-0 -z-50 size-full object-cover opacity-85 invert dark:invert-0"
src="/landing-page-dark.png" src="/landing-page-dark.png"
height={1903.5} height={1903.5}
width={1269} width={1269}
alt="landing-page.bg" alt="landing-page.bg"
/> />
<div className="max-w-3xl mx-auto w-full flex-1 flex flex-col justify-center"> <div className="mx-auto flex w-full max-w-3xl flex-1 flex-col justify-center">
<div className="inline-block font-bold tracking-tighter text-4xl md:text-[4rem]"> <div className="inline-block text-4xl font-bold tracking-tighter md:text-[4rem]">
<h1>The open source</h1> <h1>The open source</h1>
<Handlebars>Video Editor</Handlebars> <Handlebars>Video Editor</Handlebars>
</div> </div>
<p className="mt-10 text-base sm:text-xl text-muted-foreground font-light tracking-wide max-w-xl mx-auto"> <p className="text-muted-foreground mx-auto mt-10 max-w-xl text-base font-light tracking-wide sm:text-xl">
A simple but powerful video editor that gets the job done. Works on A simple but powerful video editor that gets the job done. Works on
any platform. any platform.
</p> </p>
<div className="mt-8 flex gap-8 justify-center"> <div className="mt-8 flex justify-center gap-8">
<Link href="/projects"> <Link href="/projects">
<Button <Button
type="submit" type="submit"
size="lg" size="lg"
className="px-6 h-11 text-base bg-foreground" className="bg-foreground h-11 px-6 text-base"
> >
Try early beta Try early beta
<ArrowRight className="ml-0.5" /> <ArrowRight className="ml-0.5" />

View File

@ -19,7 +19,7 @@ interface LanguageSelectProps {
function FlagPreloader({ languages }: { languages: Language[] }) { function FlagPreloader({ languages }: { languages: Language[] }) {
return ( return (
<div className="absolute -top-[9999px] left-0 pointer-events-none"> <div className="pointer-events-none absolute -top-[9999px] left-0">
{languages.map((language) => ( {languages.map((language) => (
<ReactCountryFlag <ReactCountryFlag
key={language.code} key={language.code}
@ -74,7 +74,7 @@ export function LanguageSelect({
}, [expanded]); }, [expanded]);
const selectedLanguage = languages.find( const selectedLanguage = languages.find(
(lang) => lang.code === selectedCountry (lang) => lang.code === selectedCountry,
); );
const handleSelect = ({ const handleSelect = ({
@ -91,14 +91,14 @@ export function LanguageSelect({
}; };
return ( return (
<div className="relative w-full h-9"> <div className="relative h-9 w-full">
<FlagPreloader languages={languages} /> <FlagPreloader languages={languages} />
<motion.button <motion.button
type="button" type="button"
className={cn( className={cn(
"absolute w-full h-full flex flex-col overflow-hidden items-start justify-between z-10 rounded-lg px-3 cursor-pointer", "absolute z-10 flex h-full w-full cursor-pointer flex-col items-start justify-between overflow-hidden rounded-lg px-3",
"!bg-foreground/10 backdrop-blur-md text-foreground py-0", "!bg-foreground/10 text-foreground py-0 backdrop-blur-md",
"transition-[color,box-shadow] focus:border-ring focus:ring-ring/50 focus:ring-[1px]" "focus:border-ring focus:ring-ring/50 transition-[color,box-shadow] focus:ring-[1px]",
)} )}
initial={{ initial={{
height: collapsedHeight, height: collapsedHeight,
@ -117,7 +117,7 @@ export function LanguageSelect({
> >
{!expanded ? ( {!expanded ? (
<div <div
className="flex items-center justify-between w-full" className="flex w-full items-center justify-between"
style={{ style={{
height: collapsedHeight, height: collapsedHeight,
}} }}
@ -138,7 +138,7 @@ export function LanguageSelect({
</div> </div>
</div> </div>
) : ( ) : (
<div className="flex flex-col gap-2 my-2.5 w-full overflow-y-auto scrollbar-hidden"> <div className="scrollbar-hidden my-2.5 flex w-full flex-col gap-2 overflow-y-auto">
<LanguageButton <LanguageButton
language={{ code: "auto", name: "Auto" }} language={{ code: "auto", name: "Auto" }}
onSelect={handleSelect} onSelect={handleSelect}
@ -157,7 +157,7 @@ export function LanguageSelect({
</motion.button> </motion.button>
<motion.div <motion.div
className="absolute top-1/2 right-3 -translate-y-1/2 pointer-events-none z-20 mt-0.5" className="pointer-events-none absolute top-1/2 right-3 z-20 mt-0.5 -translate-y-1/2"
initial={{ opacity: 1 }} initial={{ opacity: 1 }}
animate={{ opacity: expanded ? 0 : 1 }} animate={{ opacity: expanded ? 0 : 1 }}
transition={{ duration: 0.2, ease: "easeInOut" }} transition={{ duration: 0.2, ease: "easeInOut" }}
@ -186,7 +186,7 @@ function LanguageButton({
return ( return (
<button <button
type="button" type="button"
className="flex items-center gap-2 cursor-pointer text-foreground hover:text-foreground/75" className="text-foreground hover:text-foreground/75 flex cursor-pointer items-center gap-2"
onClick={(e) => onSelect({ code: language.code, e })} onClick={(e) => onSelect({ code: language.code, e })}
> >
{language.code === "auto" ? ( {language.code === "auto" ? (

View File

@ -49,7 +49,7 @@ export function StorageProvider({ children }: StorageProviderProps) {
if (!hasSupport) { if (!hasSupport) {
toast.warning( toast.warning(
"Storage not fully supported. Some features may not work." "Storage not fully supported. Some features may not work.",
); );
} }

View File

@ -11,7 +11,11 @@ interface ThemeToggleProps {
onToggle?: (e: React.MouseEvent<HTMLButtonElement>) => void; onToggle?: (e: React.MouseEvent<HTMLButtonElement>) => void;
} }
export function ThemeToggle({ className, iconClassName, onToggle }: ThemeToggleProps) { export function ThemeToggle({
className,
iconClassName,
onToggle,
}: ThemeToggleProps) {
const { theme, setTheme } = useTheme(); const { theme, setTheme } = useTheme();
return ( return (

View File

@ -49,7 +49,7 @@ const AccordionContent = React.forwardRef<
className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm" className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm"
{...props} {...props}
> >
<div className={cn("pb-4 pt-0", className)}>{children}</div> <div className={cn("pt-0 pb-4", className)}>{children}</div>
</AccordionPrimitive.Content> </AccordionPrimitive.Content>
)); ));
AccordionContent.displayName = AccordionPrimitive.Content.displayName; AccordionContent.displayName = AccordionPrimitive.Content.displayName;

View File

@ -18,8 +18,8 @@ const AlertDialogOverlay = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Overlay <AlertDialogPrimitive.Overlay
className={cn( className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80",
className className,
)} )}
{...props} {...props}
ref={ref} ref={ref}
@ -36,8 +36,8 @@ const AlertDialogContent = React.forwardRef<
<AlertDialogPrimitive.Content <AlertDialogPrimitive.Content
ref={ref} ref={ref}
className={cn( className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg", "bg-background 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] fixed top-[50%] left-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg",
className className,
)} )}
{...props} {...props}
/> />
@ -52,7 +52,7 @@ const AlertDialogHeader = ({
<div <div
className={cn( className={cn(
"flex flex-col space-y-2 text-center sm:text-left", "flex flex-col space-y-2 text-center sm:text-left",
className className,
)} )}
{...props} {...props}
/> />
@ -66,7 +66,7 @@ const AlertDialogFooter = ({
<div <div
className={cn( className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className className,
)} )}
{...props} {...props}
/> />
@ -91,7 +91,7 @@ const AlertDialogDescription = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Description <AlertDialogPrimitive.Description
ref={ref} ref={ref}
className={cn("text-sm text-muted-foreground", className)} className={cn("text-muted-foreground text-sm", className)}
{...props} {...props}
/> />
)); ));
@ -119,7 +119,7 @@ const AlertDialogCancel = React.forwardRef<
className={cn( className={cn(
buttonVariants({ variant: "outline" }), buttonVariants({ variant: "outline" }),
"mt-2 sm:mt-0", "mt-2 sm:mt-0",
className className,
)} )}
{...props} {...props}
/> />

View File

@ -16,7 +16,7 @@ const alertVariants = cva(
defaultVariants: { defaultVariants: {
variant: "default", variant: "default",
}, },
} },
); );
const Alert = React.forwardRef< const Alert = React.forwardRef<
@ -38,7 +38,7 @@ const AlertTitle = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<h5 <h5
ref={ref} ref={ref}
className={cn("mb-1 font-medium leading-none tracking-tight", className)} className={cn("mb-1 leading-none font-medium tracking-tight", className)}
{...props} {...props}
/> />
)); ));

View File

@ -13,7 +13,7 @@ const Avatar = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full", "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
className className,
)} )}
{...props} {...props}
/> />
@ -39,8 +39,8 @@ const AvatarFallback = React.forwardRef<
<AvatarPrimitive.Fallback <AvatarPrimitive.Fallback
ref={ref} ref={ref}
className={cn( className={cn(
"flex h-full w-full items-center justify-center rounded-full bg-muted", "bg-muted flex h-full w-full items-center justify-center rounded-full",
className className,
)} )}
{...props} {...props}
/> />

View File

@ -20,12 +20,13 @@ const badgeVariants = cva(
defaultVariants: { defaultVariants: {
variant: "default", variant: "default",
}, },
} },
); );
export interface BadgeProps export interface BadgeProps
extends React.HTMLAttributes<HTMLDivElement>, extends
VariantProps<typeof badgeVariants> { } React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof badgeVariants> {}
function Badge({ className, variant, ...props }: BadgeProps) { function Badge({ className, variant, ...props }: BadgeProps) {
return ( return (

View File

@ -19,8 +19,8 @@ const BreadcrumbList = React.forwardRef<
<ol <ol
ref={ref} ref={ref}
className={cn( className={cn(
"flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5", "text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5",
className className,
)} )}
{...props} {...props}
/> />
@ -50,7 +50,7 @@ const BreadcrumbLink = React.forwardRef<
return ( return (
<Comp <Comp
ref={ref} ref={ref}
className={cn("transition-colors hover:text-foreground", className)} className={cn("hover:text-foreground transition-colors", className)}
{...props} {...props}
/> />
); );
@ -66,7 +66,7 @@ const BreadcrumbPage = React.forwardRef<
role="link" role="link"
aria-disabled="true" aria-disabled="true"
aria-current="page" aria-current="page"
className={cn("font-normal text-foreground", className)} className={cn("text-foreground font-normal", className)}
{...props} {...props}
/> />
)); ));
@ -80,7 +80,7 @@ const BreadcrumbSeparator = ({
<li <li
role="presentation" role="presentation"
aria-hidden="true" aria-hidden="true"
className={cn("[&>svg]:w-3.5 [&>svg]:h-3.5", className)} className={cn("[&>svg]:h-3.5 [&>svg]:w-3.5", className)}
{...props} {...props}
> >
{children ?? <ChevronRight />} {children ?? <ChevronRight />}

View File

@ -41,8 +41,9 @@ const buttonVariants = cva(
); );
export interface ButtonProps export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>, extends
VariantProps<typeof buttonVariants> { React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean; asChild?: boolean;
} }

View File

@ -27,7 +27,7 @@ function Calendar({
nav: "space-x-1 flex items-center", nav: "space-x-1 flex items-center",
nav_button: cn( nav_button: cn(
buttonVariants({ variant: "outline" }), buttonVariants({ variant: "outline" }),
"h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100" "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100",
), ),
nav_button_previous: "absolute left-1", nav_button_previous: "absolute left-1",
nav_button_next: "absolute right-1", nav_button_next: "absolute right-1",
@ -40,11 +40,11 @@ function Calendar({
"relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected].day-range-end)]:rounded-r-md", "relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected].day-range-end)]:rounded-r-md",
props.mode === "range" props.mode === "range"
? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md" ? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
: "[&:has([aria-selected])]:rounded-md" : "[&:has([aria-selected])]:rounded-md",
), ),
day: cn( day: cn(
buttonVariants({ variant: "text" }), buttonVariants({ variant: "text" }),
"h-8 w-8 p-0 font-normal aria-selected:opacity-100" "h-8 w-8 p-0 font-normal aria-selected:opacity-100",
), ),
day_range_start: "day-range-start", day_range_start: "day-range-start",
day_range_end: "day-range-end", day_range_end: "day-range-end",

View File

@ -8,7 +8,7 @@ const Card = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<div <div
ref={ref} ref={ref}
className={cn("rounded-2xl border bg-card text-card-foreground", className)} className={cn("bg-card text-card-foreground rounded-2xl border", className)}
{...props} {...props}
/> />
)); ));
@ -32,7 +32,7 @@ const CardTitle = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<div <div
ref={ref} ref={ref}
className={cn("font-semibold leading-none tracking-tight", className)} className={cn("leading-none font-semibold tracking-tight", className)}
{...props} {...props}
/> />
)); ));
@ -44,7 +44,7 @@ const CardDescription = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<div <div
ref={ref} ref={ref}
className={cn("text-sm text-muted-foreground", className)} className={cn("text-muted-foreground text-sm", className)}
{...props} {...props}
/> />
)); ));

View File

@ -56,14 +56,14 @@ const Carousel = React.forwardRef<
children, children,
...props ...props
}, },
ref ref,
) => { ) => {
const [carouselRef, api] = useEmblaCarousel( const [carouselRef, api] = useEmblaCarousel(
{ {
...opts, ...opts,
axis: orientation === "horizontal" ? "x" : "y", axis: orientation === "horizontal" ? "x" : "y",
}, },
plugins plugins,
); );
const [canScrollPrev, setCanScrollPrev] = React.useState(false); const [canScrollPrev, setCanScrollPrev] = React.useState(false);
const [canScrollNext, setCanScrollNext] = React.useState(false); const [canScrollNext, setCanScrollNext] = React.useState(false);
@ -95,7 +95,7 @@ const Carousel = React.forwardRef<
scrollNext(); scrollNext();
} }
}, },
[scrollPrev, scrollNext] [scrollPrev, scrollNext],
); );
React.useEffect(() => { React.useEffect(() => {
@ -146,7 +146,7 @@ const Carousel = React.forwardRef<
</div> </div>
</CarouselContext.Provider> </CarouselContext.Provider>
); );
} },
); );
Carousel.displayName = "Carousel"; Carousel.displayName = "Carousel";
@ -163,7 +163,7 @@ const CarouselContent = React.forwardRef<
className={cn( className={cn(
"flex", "flex",
orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col", orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
className className,
)} )}
{...props} {...props}
/> />
@ -186,7 +186,7 @@ const CarouselItem = React.forwardRef<
className={cn( className={cn(
"min-w-0 shrink-0 grow-0 basis-full", "min-w-0 shrink-0 grow-0 basis-full",
orientation === "horizontal" ? "pl-4" : "pt-4", orientation === "horizontal" ? "pl-4" : "pt-4",
className className,
)} )}
{...props} {...props}
/> />
@ -206,11 +206,11 @@ const CarouselPrevious = React.forwardRef<
variant={variant} variant={variant}
size={size} size={size}
className={cn( className={cn(
"absolute h-8 w-8 rounded-full", "absolute h-8 w-8 rounded-full",
orientation === "horizontal" orientation === "horizontal"
? "-left-12 top-1/2 -translate-y-1/2" ? "top-1/2 -left-12 -translate-y-1/2"
: "-top-12 left-1/2 -translate-x-1/2 rotate-90", : "-top-12 left-1/2 -translate-x-1/2 rotate-90",
className className,
)} )}
disabled={!canScrollPrev} disabled={!canScrollPrev}
onClick={scrollPrev} onClick={scrollPrev}
@ -237,9 +237,9 @@ const CarouselNext = React.forwardRef<
className={cn( className={cn(
"absolute h-8 w-8 rounded-full", "absolute h-8 w-8 rounded-full",
orientation === "horizontal" orientation === "horizontal"
? "-right-12 top-1/2 -translate-y-1/2" ? "top-1/2 -right-12 -translate-y-1/2"
: "-bottom-12 left-1/2 -translate-x-1/2 rotate-90", : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
className className,
)} )}
disabled={!canScrollNext} disabled={!canScrollNext}
onClick={scrollNext} onClick={scrollNext}

View File

@ -57,8 +57,8 @@ const ChartContainer = React.forwardRef<
data-chart={chartId} data-chart={chartId}
ref={ref} ref={ref}
className={cn( className={cn(
"flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-hidden [&_.recharts-surface]:outline-hidden", "[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden",
className className,
)} )}
{...props} {...props}
> >
@ -74,7 +74,7 @@ ChartContainer.displayName = "Chart";
const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
const colorConfig = Object.entries(config).filter( const colorConfig = Object.entries(config).filter(
([_, config]) => config.theme || config.color ([_, config]) => config.theme || config.color,
); );
if (!colorConfig.length) { if (!colorConfig.length) {
@ -89,15 +89,15 @@ const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
([theme, prefix]) => ` ([theme, prefix]) => `
${prefix} [data-chart=${id}] { ${prefix} [data-chart=${id}] {
${colorConfig ${colorConfig
.map(([key, itemConfig]) => { .map(([key, itemConfig]) => {
const color = const color =
itemConfig.theme?.[theme as keyof typeof itemConfig.theme] || itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
itemConfig.color; itemConfig.color;
return color ? ` --color-${key}: ${color};` : null; return color ? ` --color-${key}: ${color};` : null;
}) })
.join("\n")} .join("\n")}
} }
` `,
) )
.join("\n"), .join("\n"),
}} }}
@ -110,13 +110,13 @@ const ChartTooltip = RechartsPrimitive.Tooltip;
const ChartTooltipContent = React.forwardRef< const ChartTooltipContent = React.forwardRef<
HTMLDivElement, HTMLDivElement,
React.ComponentProps<typeof RechartsPrimitive.Tooltip> & React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
React.ComponentProps<"div"> & { React.ComponentProps<"div"> & {
hideLabel?: boolean; hideLabel?: boolean;
hideIndicator?: boolean; hideIndicator?: boolean;
indicator?: "line" | "dot" | "dashed"; indicator?: "line" | "dot" | "dashed";
nameKey?: string; nameKey?: string;
labelKey?: string; labelKey?: string;
} }
>( >(
( (
{ {
@ -134,7 +134,7 @@ const ChartTooltipContent = React.forwardRef<
nameKey, nameKey,
labelKey, labelKey,
}, },
ref ref,
) => { ) => {
const { config } = useChart(); const { config } = useChart();
@ -184,8 +184,8 @@ const ChartTooltipContent = React.forwardRef<
<div <div
ref={ref} ref={ref}
className={cn( className={cn(
"grid min-w-32 items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl", "border-border/50 bg-background grid min-w-32 items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl",
className className,
)} )}
> >
{nestLabel ? null : tooltipLabel} {nestLabel ? null : tooltipLabel}
@ -199,8 +199,8 @@ const ChartTooltipContent = React.forwardRef<
<div <div
key={item.dataKey} key={item.dataKey}
className={cn( className={cn(
"flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground", "[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5",
indicator === "dot" && "items-center" indicator === "dot" && "items-center",
)} )}
> >
{formatter && item?.value !== undefined && item.name ? ( {formatter && item?.value !== undefined && item.name ? (
@ -220,7 +220,7 @@ const ChartTooltipContent = React.forwardRef<
"w-0 border-[1.5px] border-dashed bg-transparent": "w-0 border-[1.5px] border-dashed bg-transparent":
indicator === "dashed", indicator === "dashed",
"my-0.5": nestLabel && indicator === "dashed", "my-0.5": nestLabel && indicator === "dashed",
} },
)} )}
style={ style={
{ {
@ -234,7 +234,7 @@ const ChartTooltipContent = React.forwardRef<
<div <div
className={cn( className={cn(
"flex flex-1 justify-between leading-none", "flex flex-1 justify-between leading-none",
nestLabel ? "items-end" : "items-center" nestLabel ? "items-end" : "items-center",
)} )}
> >
<div className="grid gap-1.5"> <div className="grid gap-1.5">
@ -244,7 +244,7 @@ const ChartTooltipContent = React.forwardRef<
</span> </span>
</div> </div>
{item.value && ( {item.value && (
<span className="font-mono font-medium tabular-nums text-foreground"> <span className="text-foreground font-mono font-medium tabular-nums">
{item.value.toLocaleString()} {item.value.toLocaleString()}
</span> </span>
)} )}
@ -257,7 +257,7 @@ const ChartTooltipContent = React.forwardRef<
</div> </div>
</div> </div>
); );
} },
); );
ChartTooltipContent.displayName = "ChartTooltip"; ChartTooltipContent.displayName = "ChartTooltip";
@ -266,14 +266,14 @@ const ChartLegend = RechartsPrimitive.Legend;
const ChartLegendContent = React.forwardRef< const ChartLegendContent = React.forwardRef<
HTMLDivElement, HTMLDivElement,
React.ComponentProps<"div"> & React.ComponentProps<"div"> &
Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & { Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
hideIcon?: boolean; hideIcon?: boolean;
nameKey?: string; nameKey?: string;
} }
>( >(
( (
{ className, hideIcon = false, payload, verticalAlign = "bottom", nameKey }, { className, hideIcon = false, payload, verticalAlign = "bottom", nameKey },
ref ref,
) => { ) => {
const { config } = useChart(); const { config } = useChart();
@ -287,7 +287,7 @@ const ChartLegendContent = React.forwardRef<
className={cn( className={cn(
"flex items-center justify-center gap-4", "flex items-center justify-center gap-4",
verticalAlign === "top" ? "pb-3" : "pt-3", verticalAlign === "top" ? "pb-3" : "pt-3",
className className,
)} )}
> >
{payload.map((item) => { {payload.map((item) => {
@ -298,7 +298,7 @@ const ChartLegendContent = React.forwardRef<
<div <div
key={item.value} key={item.value}
className={cn( className={cn(
"flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground" "[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3",
)} )}
> >
{itemConfig?.icon && !hideIcon ? ( {itemConfig?.icon && !hideIcon ? (
@ -317,7 +317,7 @@ const ChartLegendContent = React.forwardRef<
})} })}
</div> </div>
); );
} },
); );
ChartLegendContent.displayName = "ChartLegend"; ChartLegendContent.displayName = "ChartLegend";
@ -325,7 +325,7 @@ ChartLegendContent.displayName = "ChartLegend";
function getPayloadConfigFromPayload( function getPayloadConfigFromPayload(
config: ChartConfig, config: ChartConfig,
payload: unknown, payload: unknown,
key: string key: string,
) { ) {
if (typeof payload !== "object" || payload === null) { if (typeof payload !== "object" || payload === null) {
return; return;
@ -333,8 +333,8 @@ function getPayloadConfigFromPayload(
const payloadPayload = const payloadPayload =
"payload" in payload && "payload" in payload &&
typeof payload.payload === "object" && typeof payload.payload === "object" &&
payload.payload !== null payload.payload !== null
? payload.payload ? payload.payload
: undefined; : undefined;

View File

@ -13,8 +13,8 @@ const Checkbox = React.forwardRef<
<CheckboxPrimitive.Root <CheckboxPrimitive.Root
ref={ref} ref={ref}
className={cn( className={cn(
"peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow-sm focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground", "peer border-primary focus-visible:ring-ring data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground h-4 w-4 shrink-0 rounded-sm border shadow-sm focus-visible:ring-1 focus-visible:outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
className className,
)} )}
{...props} {...props}
> >

View File

@ -135,11 +135,11 @@ const ColorPicker = React.forwardRef<HTMLDivElement, ColorPickerProps>(
const rect = saturationRef.current.getBoundingClientRect(); const rect = saturationRef.current.getBoundingClientRect();
const x = Math.max( const x = Math.max(
0, 0,
Math.min(1, (e.clientX - rect.left) / rect.width) Math.min(1, (e.clientX - rect.left) / rect.width),
); );
const y = Math.max( const y = Math.max(
0, 0,
Math.min(1, (e.clientY - rect.top) / rect.height) Math.min(1, (e.clientY - rect.top) / rect.height),
); );
const newS = x; const newS = x;
const newV = 1 - y; const newV = 1 - y;
@ -151,7 +151,7 @@ const ColorPicker = React.forwardRef<HTMLDivElement, ColorPickerProps>(
const rect = hueRef.current.getBoundingClientRect(); const rect = hueRef.current.getBoundingClientRect();
const x = Math.max( const x = Math.max(
0, 0,
Math.min(1, (e.clientX - rect.left) / rect.width) Math.min(1, (e.clientX - rect.left) / rect.width),
); );
const newH = x * 360; const newH = x * 360;
setInternalHue(newH); setInternalHue(newH);
@ -231,14 +231,14 @@ const ColorPicker = React.forwardRef<HTMLDivElement, ColorPickerProps>(
<div <div
ref={ref} ref={ref}
className={cn( className={cn(
"bg-panel-accent h-9 rounded-full flex items-center gap-2 px-[0.45rem]", "bg-panel-accent flex h-9 items-center gap-2 rounded-full px-[0.45rem]",
className className,
)} )}
{...props} {...props}
> >
<div <div
ref={triggerRef} ref={triggerRef}
className="size-6 rounded-full cursor-pointer hover:ring-2 hover:ring-white/20 transition-all" className="size-6 cursor-pointer rounded-full transition-all hover:ring-2 hover:ring-white/20"
style={{ backgroundColor: `#${value}` }} style={{ backgroundColor: `#${value}` }}
onClick={() => { onClick={() => {
if (!isOpen && triggerRef.current && containerRef?.current) { if (!isOpen && triggerRef.current && containerRef?.current) {
@ -252,9 +252,9 @@ const ColorPicker = React.forwardRef<HTMLDivElement, ColorPickerProps>(
setIsOpen(!isOpen); setIsOpen(!isOpen);
}} }}
/> />
<div className="flex-1 flex items-center"> <div className="flex flex-1 items-center">
<Input <Input
className="bg-transparent p-0 !ring-0 !ring-offset-0 !border-0" className="!border-0 bg-transparent p-0 !ring-0 !ring-offset-0"
containerClassName="w-full" containerClassName="w-full"
value={inputValue} value={inputValue}
onChange={handleInputChange} onChange={handleInputChange}
@ -268,7 +268,7 @@ const ColorPicker = React.forwardRef<HTMLDivElement, ColorPickerProps>(
createPortal( createPortal(
<div <div
ref={pickerRef} ref={pickerRef}
className="fixed z-50 p-4 bg-popover border border-border rounded-lg shadow-lg select-none" className="bg-popover border-border fixed z-50 rounded-lg border p-4 shadow-lg select-none"
style={{ style={{
right: pickerPosition.right, right: pickerPosition.right,
bottom: pickerPosition.bottom, bottom: pickerPosition.bottom,
@ -276,7 +276,7 @@ const ColorPicker = React.forwardRef<HTMLDivElement, ColorPickerProps>(
> >
<div <div
ref={saturationRef} ref={saturationRef}
className="relative w-48 h-32 cursor-crosshair mb-3" className="relative mb-3 h-32 w-48 cursor-crosshair"
style={saturationStyle} style={saturationStyle}
onMouseDown={handleSaturationMouseDown} onMouseDown={handleSaturationMouseDown}
> >
@ -289,7 +289,7 @@ const ColorPicker = React.forwardRef<HTMLDivElement, ColorPickerProps>(
<div <div
ref={hueRef} ref={hueRef}
className="relative w-48 h-4 rounded-lg cursor-pointer" className="relative h-4 w-48 cursor-pointer rounded-lg"
style={hueStyle} style={hueStyle}
onMouseDown={handleHueMouseDown} onMouseDown={handleHueMouseDown}
> >
@ -303,11 +303,11 @@ const ColorPicker = React.forwardRef<HTMLDivElement, ColorPickerProps>(
/> />
</div> </div>
</div>, </div>,
document.body document.body,
)} )}
</div> </div>
); );
} },
); );
ColorPicker.displayName = "ColorPicker"; ColorPicker.displayName = "ColorPicker";
@ -321,8 +321,9 @@ const ColorCircle = ({
color: string; color: string;
}) => ( }) => (
<div <div
className={`absolute border-3 border-white rounded-full shadow-lg pointer-events-none ${size === "sm" ? "size-3" : "size-4" className={`pointer-events-none absolute rounded-full border-3 border-white shadow-lg ${
}`} size === "sm" ? "size-3" : "size-4"
}`}
style={{ style={{
left: position.left, left: position.left,
top: position.top, top: position.top,

View File

@ -14,8 +14,8 @@ const Command = React.forwardRef<
<CommandPrimitive <CommandPrimitive
ref={ref} ref={ref}
className={cn( className={cn(
"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground", "bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md",
className className,
)} )}
{...props} {...props}
/> />
@ -26,7 +26,7 @@ const CommandDialog = ({ children, ...props }: DialogProps) => {
return ( return (
<Dialog {...props}> <Dialog {...props}>
<DialogContent className="overflow-hidden p-0"> <DialogContent className="overflow-hidden p-0">
<Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5"> <Command className="[&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
{children} {children}
</Command> </Command>
</DialogContent> </DialogContent>
@ -43,8 +43,8 @@ const CommandInput = React.forwardRef<
<CommandPrimitive.Input <CommandPrimitive.Input
ref={ref} ref={ref}
className={cn( className={cn(
"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50", "placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
className className,
)} )}
{...props} {...props}
/> />
@ -59,7 +59,7 @@ const CommandList = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<CommandPrimitive.List <CommandPrimitive.List
ref={ref} ref={ref}
className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)} className={cn("max-h-[300px] overflow-x-hidden overflow-y-auto", className)}
{...props} {...props}
/> />
)); ));
@ -86,8 +86,8 @@ const CommandGroup = React.forwardRef<
<CommandPrimitive.Group <CommandPrimitive.Group
ref={ref} ref={ref}
className={cn( className={cn(
"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground", "text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium",
className className,
)} )}
{...props} {...props}
/> />
@ -101,7 +101,7 @@ const CommandSeparator = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<CommandPrimitive.Separator <CommandPrimitive.Separator
ref={ref} ref={ref}
className={cn("-mx-1 h-px bg-border", className)} className={cn("bg-border -mx-1 h-px", className)}
{...props} {...props}
/> />
)); ));
@ -114,8 +114,8 @@ const CommandItem = React.forwardRef<
<CommandPrimitive.Item <CommandPrimitive.Item
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", "data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
className className,
)} )}
{...props} {...props}
/> />
@ -130,8 +130,8 @@ const CommandShortcut = ({
return ( return (
<span <span
className={cn( className={cn(
"ml-auto text-xs tracking-widest text-muted-foreground", "text-muted-foreground ml-auto text-xs tracking-widest",
className className,
)} )}
{...props} {...props}
/> />

View File

@ -31,7 +31,7 @@ const contextMenuItemVariants = cva(
defaultVariants: { defaultVariants: {
variant: "default", variant: "default",
}, },
} },
); );
const ContextMenuSubTrigger = React.forwardRef< const ContextMenuSubTrigger = React.forwardRef<
@ -47,7 +47,7 @@ const ContextMenuSubTrigger = React.forwardRef<
contextMenuItemVariants({ variant }), contextMenuItemVariants({ variant }),
"data-[state=open]:bg-accent data-[state=open]:opacity-65", "data-[state=open]:bg-accent data-[state=open]:opacity-65",
inset && "pl-8", inset && "pl-8",
className className,
)} )}
{...props} {...props}
> >
@ -64,8 +64,8 @@ const ContextMenuSubContent = React.forwardRef<
<ContextMenuPrimitive.SubContent <ContextMenuPrimitive.SubContent
ref={ref} ref={ref}
className={cn( className={cn(
"z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg 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", "bg-popover text-popover-foreground 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 min-w-32 overflow-hidden rounded-md border p-1 shadow-lg",
className className,
)} )}
{...props} {...props}
/> />
@ -80,9 +80,9 @@ const ContextMenuContent = React.forwardRef<
<ContextMenuPrimitive.Content <ContextMenuPrimitive.Content
ref={ref} ref={ref}
className={cn( className={cn(
"z-50 min-w-32 overflow-hidden rounded-md border p-1 bg-popover text-popover-foreground shadow-md", "bg-popover text-popover-foreground z-50 min-w-32 overflow-hidden rounded-md border p-1 shadow-md",
"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", "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",
className className,
)} )}
{...props} {...props}
/> />
@ -102,7 +102,7 @@ const ContextMenuItem = React.forwardRef<
className={cn( className={cn(
contextMenuItemVariants({ variant }), contextMenuItemVariants({ variant }),
inset && "pl-8", inset && "pl-8",
className className,
)} )}
{...props} {...props}
/> />
@ -117,7 +117,7 @@ const ContextMenuCheckboxItem = React.forwardRef<
>(({ className, children, checked, variant = "default", ...props }, ref) => ( >(({ className, children, checked, variant = "default", ...props }, ref) => (
<ContextMenuPrimitive.CheckboxItem <ContextMenuPrimitive.CheckboxItem
ref={ref} ref={ref}
className={cn(contextMenuItemVariants({ variant }), "pl-8 pr-2", className)} className={cn(contextMenuItemVariants({ variant }), "pr-2 pl-8", className)}
checked={checked} checked={checked}
{...props} {...props}
> >
@ -140,7 +140,7 @@ const ContextMenuRadioItem = React.forwardRef<
>(({ className, children, variant = "default", ...props }, ref) => ( >(({ className, children, variant = "default", ...props }, ref) => (
<ContextMenuPrimitive.RadioItem <ContextMenuPrimitive.RadioItem
ref={ref} ref={ref}
className={cn(contextMenuItemVariants({ variant }), "pl-8 pr-2", className)} className={cn(contextMenuItemVariants({ variant }), "pr-2 pl-8", className)}
{...props} {...props}
> >
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
@ -164,7 +164,7 @@ const ContextMenuLabel = React.forwardRef<
className={cn( className={cn(
"px-2 py-1.5 text-sm font-semibold", "px-2 py-1.5 text-sm font-semibold",
inset && "pl-8", inset && "pl-8",
className className,
)} )}
{...props} {...props}
/> />
@ -177,7 +177,7 @@ const ContextMenuSeparator = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<ContextMenuPrimitive.Separator <ContextMenuPrimitive.Separator
ref={ref} ref={ref}
className={cn("-mx-1 my-1 h-px bg-foreground/10", className)} className={cn("bg-foreground/10 -mx-1 my-1 h-px", className)}
{...props} {...props}
/> />
)); ));

View File

@ -22,8 +22,8 @@ const DialogOverlay = React.forwardRef<
<DialogPrimitive.Overlay <DialogPrimitive.Overlay
ref={ref} ref={ref}
className={cn( className={cn(
"fixed inset-0 z-250 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-250 bg-black/50",
className className,
)} )}
{...props} {...props}
/> />
@ -39,8 +39,8 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content <DialogPrimitive.Content
ref={ref} ref={ref}
className={cn( className={cn(
"fixed left-[50%] top-[50%] p-6 z-250 grid w-[calc(100%-2rem)] max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-popover shadow-lg duration-200 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 rounded-lg", "bg-popover 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 fixed top-[50%] left-[50%] z-250 grid w-[calc(100%-2rem)] max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200",
className className,
)} )}
onCloseAutoFocus={(e) => { onCloseAutoFocus={(e) => {
e.stopPropagation(); e.stopPropagation();
@ -49,7 +49,7 @@ const DialogContent = React.forwardRef<
{...props} {...props}
> >
{children} {children}
<DialogPrimitive.Close className="absolute right-4 top-4 opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground cursor-pointer"> <DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 cursor-pointer opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
<X className="h-4 w-4" /> <X className="h-4 w-4" />
<span className="sr-only">Close</span> <span className="sr-only">Close</span>
</DialogPrimitive.Close> </DialogPrimitive.Close>
@ -63,10 +63,7 @@ const DialogHeader = ({
...props ...props
}: React.HTMLAttributes<HTMLDivElement>) => ( }: React.HTMLAttributes<HTMLDivElement>) => (
<div <div
className={cn( className={cn("flex flex-col space-y-2 text-left", className)}
"flex flex-col space-y-2 text-left",
className
)}
{...props} {...props}
/> />
); );
@ -79,7 +76,7 @@ const DialogFooter = ({
<div <div
className={cn( className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className className,
)} )}
{...props} {...props}
/> />
@ -93,8 +90,8 @@ const DialogTitle = React.forwardRef<
<DialogPrimitive.Title <DialogPrimitive.Title
ref={ref} ref={ref}
className={cn( className={cn(
"text-lg font-semibold leading-none tracking-tight", "text-lg leading-none font-semibold tracking-tight",
className className,
)} )}
{...props} {...props}
/> />
@ -107,7 +104,7 @@ const DialogDescription = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<DialogPrimitive.Description <DialogPrimitive.Description
ref={ref} ref={ref}
className={cn("text-sm text-muted-foreground", className)} className={cn("text-muted-foreground text-sm", className)}
{...props} {...props}
/> />
)); ));

View File

@ -170,7 +170,7 @@ export function DraggableItem({
typeof document !== "undefined" && typeof document !== "undefined" &&
createPortal( createPortal(
<div <div
className="z-9999 pointer-events-none fixed" className="pointer-events-none fixed z-9999"
style={{ style={{
left: dragPosition.x - 40, left: dragPosition.x - 40,
top: dragPosition.y - 40, top: dragPosition.y - 40,
@ -179,7 +179,7 @@ export function DraggableItem({
<div className="w-[80px]"> <div className="w-[80px]">
<AspectRatio <AspectRatio
ratio={1} ratio={1}
className="ring-3 ring-primary relative overflow-hidden rounded-md shadow-2xl" className="ring-primary relative overflow-hidden rounded-md shadow-2xl ring-3"
> >
<div className="h-full w-full [&_img]:h-full [&_img]:w-full [&_img]:rounded-none [&_img]:object-cover"> <div className="h-full w-full [&_img]:h-full [&_img]:w-full [&_img]:rounded-none [&_img]:object-cover">
{preview} {preview}
@ -212,7 +212,7 @@ function PlusButton({
<Button <Button
size="icon" size="icon"
className={cn( className={cn(
"bg-background hover:bg-panel text-foreground absolute bottom-2 right-2 size-5", "bg-background hover:bg-panel text-foreground absolute right-2 bottom-2 size-5",
className, className,
)} )}
onClick={(e) => { onClick={(e) => {

View File

@ -43,12 +43,12 @@ const DrawerContent = React.forwardRef<
<DrawerPrimitive.Content <DrawerPrimitive.Content
ref={ref} ref={ref}
className={cn( className={cn(
"fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background", "bg-background fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border",
className className,
)} )}
{...props} {...props}
> >
<div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" /> <div className="bg-muted mx-auto mt-4 h-2 w-[100px] rounded-full" />
{children} {children}
</DrawerPrimitive.Content> </DrawerPrimitive.Content>
</DrawerPortal> </DrawerPortal>
@ -84,8 +84,8 @@ const DrawerTitle = React.forwardRef<
<DrawerPrimitive.Title <DrawerPrimitive.Title
ref={ref} ref={ref}
className={cn( className={cn(
"text-lg font-semibold leading-none tracking-tight", "text-lg leading-none font-semibold tracking-tight",
className className,
)} )}
{...props} {...props}
/> />
@ -98,7 +98,7 @@ const DrawerDescription = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<DrawerPrimitive.Description <DrawerPrimitive.Description
ref={ref} ref={ref}
className={cn("text-sm text-muted-foreground", className)} className={cn("text-muted-foreground text-sm", className)}
{...props} {...props}
/> />
)); ));

View File

@ -31,7 +31,7 @@ const dropdownMenuItemVariants = cva(
defaultVariants: { defaultVariants: {
variant: "default", variant: "default",
}, },
} },
); );
const DropdownMenuSubTrigger = React.forwardRef< const DropdownMenuSubTrigger = React.forwardRef<
@ -47,7 +47,7 @@ const DropdownMenuSubTrigger = React.forwardRef<
dropdownMenuItemVariants({ variant }), dropdownMenuItemVariants({ variant }),
"data-[state=open]:bg-accent data-[state=open]:opacity-65", "data-[state=open]:bg-accent data-[state=open]:opacity-65",
inset && "pl-8", inset && "pl-8",
className className,
)} )}
{...props} {...props}
> >
@ -65,8 +65,8 @@ const DropdownMenuSubContent = React.forwardRef<
<DropdownMenuPrimitive.SubContent <DropdownMenuPrimitive.SubContent
ref={ref} ref={ref}
className={cn( className={cn(
"z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg 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", "bg-popover text-popover-foreground 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 min-w-32 overflow-hidden rounded-md border p-1 shadow-lg",
className className,
)} )}
{...props} {...props}
/> />
@ -87,9 +87,9 @@ const DropdownMenuContent = React.forwardRef<
e.preventDefault(); e.preventDefault();
}} }}
className={cn( className={cn(
"z-50 min-w-32 overflow-hidden rounded-md border p-1 bg-popover text-popover-foreground shadow-md", "bg-popover text-popover-foreground z-50 min-w-32 overflow-hidden rounded-md border p-1 shadow-md",
"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", "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",
className className,
)} )}
{...props} {...props}
/> />
@ -109,7 +109,7 @@ const DropdownMenuItem = React.forwardRef<
className={cn( className={cn(
dropdownMenuItemVariants({ variant }), dropdownMenuItemVariants({ variant }),
inset && "pl-8", inset && "pl-8",
className className,
)} )}
{...props} {...props}
/> />
@ -126,8 +126,8 @@ const DropdownMenuCheckboxItem = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
dropdownMenuItemVariants({ variant }), dropdownMenuItemVariants({ variant }),
"pl-2 pr-8", "pr-8 pl-2",
className className,
)} )}
checked={checked} checked={checked}
onSelect={(e) => { onSelect={(e) => {
@ -157,8 +157,8 @@ const DropdownMenuRadioItem = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
dropdownMenuItemVariants({ variant }), dropdownMenuItemVariants({ variant }),
"pl-8 pr-2", "pr-2 pl-8",
className className,
)} )}
{...props} {...props}
> >
@ -183,7 +183,7 @@ const DropdownMenuLabel = React.forwardRef<
className={cn( className={cn(
"px-2 py-1.5 text-sm font-semibold", "px-2 py-1.5 text-sm font-semibold",
inset && "pl-8", inset && "pl-8",
className className,
)} )}
{...props} {...props}
/> />
@ -196,7 +196,7 @@ const DropdownMenuSeparator = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.Separator <DropdownMenuPrimitive.Separator
ref={ref} ref={ref}
className={cn("-mx-1 my-1 h-px bg-foreground/10", className)} className={cn("bg-foreground/10 -mx-1 my-1 h-px", className)}
{...props} {...props}
/> />
)); ));

View File

@ -20,7 +20,9 @@ export function FontPicker({
}: FontPickerProps) { }: FontPickerProps) {
return ( return (
<Select defaultValue={defaultValue} onValueChange={onValueChange}> <Select defaultValue={defaultValue} onValueChange={onValueChange}>
<SelectTrigger className={`w-full bg-panel-accent h-8 text-xs ${className || ""}`}> <SelectTrigger
className={`bg-panel-accent h-8 w-full text-xs ${className || ""}`}
>
<SelectValue placeholder="Select a font" /> <SelectValue placeholder="Select a font" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>

View File

@ -25,7 +25,7 @@ type FormFieldContextValue<
}; };
const FormFieldContext = React.createContext<FormFieldContextValue>( const FormFieldContext = React.createContext<FormFieldContextValue>(
{} as FormFieldContextValue {} as FormFieldContextValue,
); );
const FormField = < const FormField = <
@ -69,7 +69,7 @@ type FormItemContextValue = {
}; };
const FormItemContext = React.createContext<FormItemContextValue>( const FormItemContext = React.createContext<FormItemContextValue>(
{} as FormItemContextValue {} as FormItemContextValue,
); );
const FormItem = React.forwardRef< const FormItem = React.forwardRef<
@ -134,7 +134,7 @@ const FormDescription = React.forwardRef<
<p <p
ref={ref} ref={ref}
id={formDescriptionId} id={formDescriptionId}
className={cn("text-[0.8rem] text-muted-foreground", className)} className={cn("text-muted-foreground text-[0.8rem]", className)}
{...props} {...props}
/> />
); );
@ -156,7 +156,7 @@ const FormMessage = React.forwardRef<
<p <p
ref={ref} ref={ref}
id={formMessageId} id={formMessageId}
className={cn("text-[0.8rem] font-medium text-destructive", className)} className={cn("text-destructive text-[0.8rem] font-medium", className)}
{...props} {...props}
> >
{body} {body}

View File

@ -18,8 +18,8 @@ const HoverCardContent = React.forwardRef<
align={align} align={align}
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
"z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden 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", "bg-popover text-popover-foreground 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 p-4 shadow-md outline-hidden",
className className,
)} )}
{...props} {...props}
/> />

View File

@ -14,7 +14,7 @@ const InputOTP = React.forwardRef<
ref={ref} ref={ref}
containerClassName={cn( containerClassName={cn(
"flex items-center justify-between gap-2 w-full has-disabled:opacity-50", "flex items-center justify-between gap-2 w-full has-disabled:opacity-50",
containerClassName containerClassName,
)} )}
className={cn("disabled:cursor-not-allowed", className)} className={cn("disabled:cursor-not-allowed", className)}
{...props} {...props}
@ -28,7 +28,7 @@ const InputOTPGroup = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<div <div
ref={ref} ref={ref}
className={cn("flex items-center flex-1 gap-2", className)} className={cn("flex flex-1 items-center gap-2", className)}
{...props} {...props}
/> />
)); ));
@ -45,16 +45,16 @@ const InputOTPSlot = React.forwardRef<
<div <div
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex aspect-square flex-1 min-w-[36px] items-center justify-center border border-input text-lg shadow-xs transition-all first:rounded-l-md first:border-l last:rounded-r-md", "border-input relative flex aspect-square min-w-[36px] flex-1 items-center justify-center border text-lg shadow-xs transition-all first:rounded-l-md first:border-l last:rounded-r-md",
isActive && "z-10 ring-1 ring-ring", isActive && "ring-ring z-10 ring-1",
className className,
)} )}
{...props} {...props}
> >
{char} {char}
{hasFakeCaret && ( {hasFakeCaret && (
<div className="pointer-events-none absolute inset-0 flex items-center justify-center"> <div className="pointer-events-none absolute inset-0 flex items-center justify-center">
<div className="h-4 w-px animate-caret-blink bg-foreground duration-1000" /> <div className="animate-caret-blink bg-foreground h-4 w-px duration-1000" />
</div> </div>
)} )}
</div> </div>

View File

@ -41,7 +41,7 @@ export function InputWithBack({
return ( return (
<div ref={setContainerRef} className="relative w-full"> <div ref={setContainerRef} className="relative w-full">
<motion.div <motion.div
className="absolute left-0 top-1/2 -translate-y-1/2 cursor-pointer hover:opacity-75 transition-opacity z-10" className="absolute top-1/2 left-0 z-10 -translate-y-1/2 cursor-pointer transition-opacity hover:opacity-75"
initial={{ initial={{
x: isExpanded ? 0 : buttonOffset, x: isExpanded ? 0 : buttonOffset,
opacity: isExpanded ? 1 : 0.5, opacity: isExpanded ? 1 : 0.5,
@ -55,7 +55,7 @@ export function InputWithBack({
> >
<Button <Button
variant="outline" variant="outline"
className="!size-9 rounded-full bg-panel-accent" className="bg-panel-accent !size-9 rounded-full"
> >
<ArrowLeft /> <ArrowLeft />
</Button> </Button>
@ -74,10 +74,10 @@ export function InputWithBack({
}} }}
transition={smoothTransition} transition={smoothTransition}
> >
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" /> <Search className="text-muted-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2" />
<Input <Input
placeholder={placeholder} placeholder={placeholder}
className="pl-9 bg-panel-accent w-full" className="bg-panel-accent w-full pl-9"
value={value} value={value}
onChange={(e) => onChange?.(e.target.value)} onChange={(e) => onChange?.(e.target.value)}
/> />

View File

@ -29,7 +29,7 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
onBlur, onBlur,
...props ...props
}, },
ref ref,
) => { ) => {
const [isFocused, setIsFocused] = useState(false); const [isFocused, setIsFocused] = useState(false);
@ -49,15 +49,17 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
iconCount === 2 ? "pr-20" : iconCount === 1 ? "pr-10" : ""; iconCount === 2 ? "pr-20" : iconCount === 1 ? "pr-10" : "";
return ( return (
<div className={cn(hasIcons ? "relative w-full" : "", containerClassName)}> <div
className={cn(hasIcons ? "relative w-full" : "", containerClassName)}
>
<input <input
type={inputType} type={inputType}
className={cn( className={cn(
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground border-input flex h-9 w-full min-w-0 rounded-md border bg-background px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground border-input bg-background flex h-9 w-full min-w-0 rounded-md border px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[2px]", "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[2px]",
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
paddingRight, paddingRight,
className className,
)} )}
ref={ref} ref={ref}
value={value} value={value}
@ -80,7 +82,7 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
e.preventDefault(); e.preventDefault();
onClear?.(); onClear?.();
}} }}
className="absolute right-0 top-0 h-full px-3 text-muted-foreground !opacity-100" className="text-muted-foreground absolute top-0 right-0 h-full px-3 !opacity-100"
aria-label="Clear input" aria-label="Clear input"
> >
<X className="!size-[0.85]" /> <X className="!size-[0.85]" />
@ -93,8 +95,8 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
size="icon" size="icon"
onClick={() => onShowPasswordChange?.(!showPassword)} onClick={() => onShowPasswordChange?.(!showPassword)}
className={cn( className={cn(
"absolute top-0 h-full px-3 text-muted-foreground hover:text-foreground", "text-muted-foreground hover:text-foreground absolute top-0 h-full px-3",
showClear ? "right-10" : "right-0" showClear ? "right-10" : "right-0",
)} )}
aria-label={showPassword ? "Hide password" : "Show password"} aria-label={showPassword ? "Hide password" : "Show password"}
> >
@ -107,7 +109,7 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
)} )}
</div> </div>
); );
} },
); );
Input.displayName = "Input"; Input.displayName = "Input";

View File

@ -7,13 +7,13 @@ import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/utils/ui"; import { cn } from "@/utils/ui";
const labelVariants = cva( const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
); );
const Label = React.forwardRef< const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>, React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants> VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<LabelPrimitive.Root <LabelPrimitive.Root
ref={ref} ref={ref}

View File

@ -23,8 +23,8 @@ const Menubar = React.forwardRef<
<MenubarPrimitive.Root <MenubarPrimitive.Root
ref={ref} ref={ref}
className={cn( className={cn(
"flex h-9 items-center space-x-1 rounded-md border bg-background p-1 shadow-xs", "bg-background flex h-9 items-center space-x-1 rounded-md border p-1 shadow-xs",
className className,
)} )}
{...props} {...props}
/> />
@ -38,8 +38,8 @@ const MenubarTrigger = React.forwardRef<
<MenubarPrimitive.Trigger <MenubarPrimitive.Trigger
ref={ref} ref={ref}
className={cn( className={cn(
"flex cursor-default select-none items-center rounded-sm px-3 py-1 text-sm font-medium outline-hidden focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground", "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-3 py-1 text-sm font-medium outline-hidden select-none",
className className,
)} )}
{...props} {...props}
/> />
@ -55,9 +55,9 @@ const MenubarSubTrigger = React.forwardRef<
<MenubarPrimitive.SubTrigger <MenubarPrimitive.SubTrigger
ref={ref} ref={ref}
className={cn( className={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground", "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none",
inset && "pl-8", inset && "pl-8",
className className,
)} )}
{...props} {...props}
> >
@ -74,8 +74,8 @@ const MenubarSubContent = React.forwardRef<
<MenubarPrimitive.SubContent <MenubarPrimitive.SubContent
ref={ref} ref={ref}
className={cn( className={cn(
"z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg 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", "bg-popover text-popover-foreground 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 min-w-32 overflow-hidden rounded-md border p-1 shadow-lg",
className className,
)} )}
{...props} {...props}
/> />
@ -88,7 +88,7 @@ const MenubarContent = React.forwardRef<
>( >(
( (
{ className, align = "start", alignOffset = -4, sideOffset = 8, ...props }, { className, align = "start", alignOffset = -4, sideOffset = 8, ...props },
ref ref,
) => ( ) => (
<MenubarPrimitive.Portal> <MenubarPrimitive.Portal>
<MenubarPrimitive.Content <MenubarPrimitive.Content
@ -97,13 +97,13 @@ const MenubarContent = React.forwardRef<
alignOffset={alignOffset} alignOffset={alignOffset}
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
"z-50 min-w-48 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in 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", "bg-popover text-popover-foreground data-[state=open]:animate-in 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 min-w-48 overflow-hidden rounded-md border p-1 shadow-md",
className className,
)} )}
{...props} {...props}
/> />
</MenubarPrimitive.Portal> </MenubarPrimitive.Portal>
) ),
); );
MenubarContent.displayName = MenubarPrimitive.Content.displayName; MenubarContent.displayName = MenubarPrimitive.Content.displayName;
@ -116,9 +116,9 @@ const MenubarItem = React.forwardRef<
<MenubarPrimitive.Item <MenubarPrimitive.Item
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50", "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50",
inset && "pl-8", inset && "pl-8",
className className,
)} )}
{...props} {...props}
/> />
@ -132,8 +132,8 @@ const MenubarCheckboxItem = React.forwardRef<
<MenubarPrimitive.CheckboxItem <MenubarPrimitive.CheckboxItem
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50", "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50",
className className,
)} )}
checked={checked} checked={checked}
{...props} {...props}
@ -155,8 +155,8 @@ const MenubarRadioItem = React.forwardRef<
<MenubarPrimitive.RadioItem <MenubarPrimitive.RadioItem
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50", "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50",
className className,
)} )}
{...props} {...props}
> >
@ -181,7 +181,7 @@ const MenubarLabel = React.forwardRef<
className={cn( className={cn(
"px-2 py-1.5 text-sm font-semibold", "px-2 py-1.5 text-sm font-semibold",
inset && "pl-8", inset && "pl-8",
className className,
)} )}
{...props} {...props}
/> />
@ -194,7 +194,7 @@ const MenubarSeparator = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<MenubarPrimitive.Separator <MenubarPrimitive.Separator
ref={ref} ref={ref}
className={cn("-mx-1 my-1 h-px bg-muted", className)} className={cn("bg-muted -mx-1 my-1 h-px", className)}
{...props} {...props}
/> />
)); ));
@ -207,8 +207,8 @@ const MenubarShortcut = ({
return ( return (
<span <span
className={cn( className={cn(
"ml-auto text-xs tracking-widest text-muted-foreground", "text-muted-foreground ml-auto text-xs tracking-widest",
className className,
)} )}
{...props} {...props}
/> />

View File

@ -13,7 +13,7 @@ const NavigationMenu = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
"relative z-10 flex max-w-max flex-1 items-center justify-center", "relative z-10 flex max-w-max flex-1 items-center justify-center",
className className,
)} )}
{...props} {...props}
> >
@ -31,7 +31,7 @@ const NavigationMenuList = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
"group flex flex-1 list-none items-center justify-center space-x-1", "group flex flex-1 list-none items-center justify-center space-x-1",
className className,
)} )}
{...props} {...props}
/> />
@ -41,7 +41,7 @@ NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName;
const NavigationMenuItem = NavigationMenuPrimitive.Item; const NavigationMenuItem = NavigationMenuPrimitive.Item;
const navigationMenuTriggerStyle = cva( const navigationMenuTriggerStyle = cva(
"group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-hidden disabled:pointer-events-none disabled:opacity-50 data-active:bg-accent/50 data-[state=open]:bg-accent/50" "group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-hidden disabled:pointer-events-none disabled:opacity-50 data-active:bg-accent/50 data-[state=open]:bg-accent/50",
); );
const NavigationMenuTrigger = React.forwardRef< const NavigationMenuTrigger = React.forwardRef<
@ -69,8 +69,8 @@ const NavigationMenuContent = React.forwardRef<
<NavigationMenuPrimitive.Content <NavigationMenuPrimitive.Content
ref={ref} ref={ref}
className={cn( className={cn(
"left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto ", "data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 top-0 left-0 w-full md:absolute md:w-auto",
className className,
)} )}
{...props} {...props}
/> />
@ -83,11 +83,11 @@ const NavigationMenuViewport = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Viewport>, React.ElementRef<typeof NavigationMenuPrimitive.Viewport>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport> React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport>
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<div className={cn("absolute left-0 top-full flex justify-center")}> <div className={cn("absolute top-full left-0 flex justify-center")}>
<NavigationMenuPrimitive.Viewport <NavigationMenuPrimitive.Viewport
className={cn( className={cn(
"origin-top-center relative mt-1.5 h-(--radix-navigation-menu-viewport-height) w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-(--radix-navigation-menu-viewport-width)", "origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-(--radix-navigation-menu-viewport-height) w-full overflow-hidden rounded-md border shadow-sm md:w-(--radix-navigation-menu-viewport-width)",
className className,
)} )}
ref={ref} ref={ref}
{...props} {...props}
@ -104,12 +104,12 @@ const NavigationMenuIndicator = React.forwardRef<
<NavigationMenuPrimitive.Indicator <NavigationMenuPrimitive.Indicator
ref={ref} ref={ref}
className={cn( className={cn(
"top-full z-1 flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in", "data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-1 flex h-1.5 items-end justify-center overflow-hidden",
className className,
)} )}
{...props} {...props}
> >
<div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" /> <div className="bg-border relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm shadow-md" />
</NavigationMenuPrimitive.Indicator> </NavigationMenuPrimitive.Indicator>
)); ));
NavigationMenuIndicator.displayName = NavigationMenuIndicator.displayName =

View File

@ -52,7 +52,7 @@ const PaginationLink = ({
variant: isActive ? "outline" : "text", variant: isActive ? "outline" : "text",
size, size,
}), }),
className className,
)} )}
{...props} {...props}
/> />

View File

@ -52,7 +52,7 @@ const PhoneInput: React.ForwardRefExoticComponent<PhoneInputProps> =
{...props} {...props}
/> />
); );
} },
); );
PhoneInput.displayName = "PhoneInput"; PhoneInput.displayName = "PhoneInput";
@ -61,7 +61,7 @@ const InputComponent = React.forwardRef<
React.ComponentProps<"input"> React.ComponentProps<"input">
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<Input <Input
className={cn("rounded-e-lg rounded-s-none", className)} className={cn("rounded-s-none rounded-e-lg", className)}
{...props} {...props}
ref={ref} ref={ref}
/> />
@ -89,7 +89,7 @@ const CountrySelect = ({
<Button <Button
type="button" type="button"
variant="outline" variant="outline"
className="flex gap-1 rounded-e-none rounded-s-lg border-r-0 px-3 focus:z-10" className="flex gap-1 rounded-s-lg rounded-e-none border-r-0 px-3 focus:z-10"
disabled={disabled} disabled={disabled}
> >
<FlagComponent <FlagComponent
@ -99,7 +99,7 @@ const CountrySelect = ({
<ChevronsUpDown <ChevronsUpDown
className={cn( className={cn(
"-mr-2 size-4 opacity-50", "-mr-2 size-4 opacity-50",
disabled ? "hidden" : "opacity-100" disabled ? "hidden" : "opacity-100",
)} )}
/> />
</Button> </Button>
@ -120,7 +120,7 @@ const CountrySelect = ({
selectedCountry={selectedCountry} selectedCountry={selectedCountry}
onChange={onChange} onChange={onChange}
/> />
) : null ) : null,
)} )}
</CommandGroup> </CommandGroup>
</ScrollArea> </ScrollArea>
@ -146,7 +146,7 @@ const CountrySelectOption = ({
<CommandItem className="gap-2" onSelect={() => onChange(country)}> <CommandItem className="gap-2" onSelect={() => onChange(country)}>
<FlagComponent country={country} countryName={countryName} /> <FlagComponent country={country} countryName={countryName} />
<span className="flex-1 text-sm">{countryName}</span> <span className="flex-1 text-sm">{countryName}</span>
<span className="text-sm text-foreground/50">{`+${RPNInput.getCountryCallingCode(country)}`}</span> <span className="text-foreground/50 text-sm">{`+${RPNInput.getCountryCallingCode(country)}`}</span>
<CheckIcon <CheckIcon
className={`ml-auto size-4 ${country === selectedCountry ? "opacity-100" : "opacity-0"}`} className={`ml-auto size-4 ${country === selectedCountry ? "opacity-100" : "opacity-0"}`}
/> />
@ -158,7 +158,7 @@ const FlagComponent = ({ country, countryName }: RPNInput.FlagProps) => {
const Flag = flags[country]; const Flag = flags[country];
return ( return (
<span className="flex h-4 w-6 overflow-hidden rounded-sm bg-foreground/20 [&_svg]:size-full"> <span className="bg-foreground/20 flex h-4 w-6 overflow-hidden rounded-sm [&_svg]:size-full">
{Flag && <Flag title={countryName} />} {Flag && <Flag title={countryName} />}
</span> </span>
); );

View File

@ -21,8 +21,8 @@ const PopoverContent = React.forwardRef<
align={align} align={align}
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-[0_0_10px_rgba(0,0,0,0.15)] outline-hidden 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", "bg-popover text-popover-foreground 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-72 rounded-md border p-4 shadow-[0_0_10px_rgba(0,0,0,0.15)] outline-hidden",
className className,
)} )}
{...props} {...props}
/> />

View File

@ -12,13 +12,13 @@ const Progress = React.forwardRef<
<ProgressPrimitive.Root <ProgressPrimitive.Root
ref={ref} ref={ref}
className={cn( className={cn(
"relative h-2 w-full overflow-hidden rounded-full bg-accent", "bg-accent relative h-2 w-full overflow-hidden rounded-full",
className className,
)} )}
{...props} {...props}
> >
<ProgressPrimitive.Indicator <ProgressPrimitive.Indicator
className="h-full w-full flex-1 bg-foreground transition-all" className="bg-foreground h-full w-full flex-1 transition-all"
style={{ transform: `translateX(-${100 - (value || 0)}%)` }} style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
/> />
</ProgressPrimitive.Root> </ProgressPrimitive.Root>

View File

@ -10,8 +10,8 @@ function Prose({ children, html, className }: ProseProps) {
return ( return (
<article <article
className={cn( className={cn(
"prose prose-h2:font-semibold max-w-none prose-h1:text-xl prose-a:text-blue-600 prose-p:text-justify dark:prose-invert mx-auto", "prose prose-h2:font-semibold prose-h1:text-xl prose-a:text-blue-600 prose-p:text-justify dark:prose-invert mx-auto max-w-none",
className className,
)} )}
> >
{html ? <div dangerouslySetInnerHTML={{ __html: html }} /> : children} {html ? <div dangerouslySetInnerHTML={{ __html: html }} /> : children}

View File

@ -28,13 +28,13 @@ const RadioGroupItem = React.forwardRef<
<RadioGroupPrimitive.Item <RadioGroupPrimitive.Item
ref={ref} ref={ref}
className={cn( className={cn(
"aspect-square h-4 w-4 rounded-full border border-primary text-primary shadow-sm focus:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50", "border-primary text-primary focus-visible:ring-ring aspect-square h-4 w-4 rounded-full border shadow-sm focus:outline-hidden focus-visible:ring-1 disabled:cursor-not-allowed disabled:opacity-50",
className className,
)} )}
{...props} {...props}
> >
<RadioGroupPrimitive.Indicator className="flex items-center justify-center"> <RadioGroupPrimitive.Indicator className="flex items-center justify-center">
<Circle className="h-3.5 w-3.5 fill-primary" /> <Circle className="fill-primary h-3.5 w-3.5" />
</RadioGroupPrimitive.Indicator> </RadioGroupPrimitive.Indicator>
</RadioGroupPrimitive.Item> </RadioGroupPrimitive.Item>
); );

View File

@ -11,7 +11,7 @@ const ResizablePanelGroup = ({
<ResizablePrimitive.PanelGroup <ResizablePrimitive.PanelGroup
className={cn( className={cn(
"flex h-full w-full data-[panel-group-direction=vertical]:flex-col", "flex h-full w-full data-[panel-group-direction=vertical]:flex-col",
className className,
)} )}
{...props} {...props}
/> />
@ -28,8 +28,8 @@ const ResizableHandle = ({
}) => ( }) => (
<ResizablePrimitive.PanelResizeHandle <ResizablePrimitive.PanelResizeHandle
className={cn( className={cn(
"relative flex w-px items-center justify-center bg-transparent after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90", "focus-visible:ring-ring relative flex w-px items-center justify-center bg-transparent after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:ring-1 focus-visible:ring-offset-1 focus-visible:outline-hidden data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:translate-x-0 data-[panel-group-direction=vertical]:after:-translate-y-1/2 [&[data-panel-group-direction=vertical]>div]:rotate-90",
className className,
)} )}
{...props} {...props}
/> />

View File

@ -7,7 +7,7 @@ const ScrollArea = React.forwardRef<
>(({ className, children, ...props }, ref) => ( >(({ className, children, ...props }, ref) => (
<div <div
ref={ref} ref={ref}
className={cn("overflow-auto scrollbar-thin", className)} className={cn("scrollbar-thin overflow-auto", className)}
{...props} {...props}
> >
{children} {children}

View File

@ -25,7 +25,7 @@ const selectItemVariants = cva(
defaultVariants: { defaultVariants: {
variant: "default", variant: "default",
}, },
} },
); );
const SelectTrigger = React.forwardRef< const SelectTrigger = React.forwardRef<
@ -35,8 +35,8 @@ const SelectTrigger = React.forwardRef<
<SelectPrimitive.Trigger <SelectPrimitive.Trigger
ref={ref} ref={ref}
className={cn( className={cn(
"flex gap-1 h-7 cursor-pointer w-auto items-center justify-between whitespace-nowrap rounded-md bg-accent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-hidden focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1", "bg-accent ring-offset-background placeholder:text-muted-foreground focus:ring-ring flex h-7 w-auto cursor-pointer items-center justify-between gap-1 rounded-md px-3 py-2 text-sm whitespace-nowrap focus:ring-1 focus:outline-hidden disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
className className,
)} )}
{...props} {...props}
> >
@ -56,7 +56,7 @@ const SelectScrollUpButton = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
"flex cursor-default items-center justify-center py-1", "flex cursor-default items-center justify-center py-1",
className className,
)} )}
{...props} {...props}
> >
@ -73,7 +73,7 @@ const SelectScrollDownButton = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
"flex cursor-default items-center justify-center py-1", "flex cursor-default items-center justify-center py-1",
className className,
)} )}
{...props} {...props}
> >
@ -91,10 +91,10 @@ const SelectContent = React.forwardRef<
<SelectPrimitive.Content <SelectPrimitive.Content
ref={ref} ref={ref}
className={cn( className={cn(
"relative z-50 max-h-96 min-w-32 overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md 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", "bg-popover text-popover-foreground 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 relative z-50 max-h-96 min-w-32 overflow-hidden rounded-md border shadow-md",
position === "popper" && position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className className,
)} )}
position={position} position={position}
onCloseAutoFocus={(e) => { onCloseAutoFocus={(e) => {
@ -108,7 +108,7 @@ const SelectContent = React.forwardRef<
className={cn( className={cn(
"p-1", "p-1",
position === "popper" && position === "popper" &&
"h-(--radix-select-trigger-height) w-full min-w-(--radix-select-trigger-width)" "h-(--radix-select-trigger-height) w-full min-w-(--radix-select-trigger-width)",
)} )}
> >
{children} {children}
@ -158,7 +158,7 @@ const SelectSeparator = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<SelectPrimitive.Separator <SelectPrimitive.Separator
ref={ref} ref={ref}
className={cn("-mx-1 my-1 h-px bg-foreground/10", className)} className={cn("bg-foreground/10 -mx-1 my-1 h-px", className)}
{...props} {...props}
/> />
)); ));

View File

@ -11,20 +11,20 @@ const Separator = React.forwardRef<
>( >(
( (
{ className, orientation = "horizontal", decorative = true, ...props }, { className, orientation = "horizontal", decorative = true, ...props },
ref ref,
) => ( ) => (
<SeparatorPrimitive.Root <SeparatorPrimitive.Root
ref={ref} ref={ref}
decorative={decorative} decorative={decorative}
orientation={orientation} orientation={orientation}
className={cn( className={cn(
"shrink-0 bg-border", "bg-border shrink-0",
orientation === "horizontal" ? "h-px w-full" : "h-full w-px", orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
className className,
)} )}
{...props} {...props}
/> />
) ),
); );
Separator.displayName = SeparatorPrimitive.Root.displayName; Separator.displayName = SeparatorPrimitive.Root.displayName;

View File

@ -21,8 +21,8 @@ const SheetOverlay = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<SheetPrimitive.Overlay <SheetPrimitive.Overlay
className={cn( className={cn(
"fixed inset-0 z-200 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-200 bg-black/50",
className className,
)} )}
{...props} {...props}
ref={ref} ref={ref}
@ -46,12 +46,13 @@ const sheetVariants = cva(
defaultVariants: { defaultVariants: {
side: "right", side: "right",
}, },
} },
); );
interface SheetContentProps interface SheetContentProps
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>, extends
VariantProps<typeof sheetVariants> { } React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
VariantProps<typeof sheetVariants> {}
const SheetContent = React.forwardRef< const SheetContent = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Content>, React.ElementRef<typeof SheetPrimitive.Content>,
@ -68,7 +69,7 @@ const SheetContent = React.forwardRef<
}} }}
{...props} {...props}
> >
<SheetPrimitive.Close className="absolute cursor-pointer right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary"> <SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 cursor-pointer rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
<X className="size-5" /> <X className="size-5" />
<span className="sr-only">Close</span> <span className="sr-only">Close</span>
</SheetPrimitive.Close> </SheetPrimitive.Close>
@ -85,7 +86,7 @@ const SheetHeader = ({
<div <div
className={cn( className={cn(
"flex flex-col space-y-2 text-center sm:text-left", "flex flex-col space-y-2 text-center sm:text-left",
className className,
)} )}
{...props} {...props}
/> />
@ -99,7 +100,7 @@ const SheetFooter = ({
<div <div
className={cn( className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className className,
)} )}
{...props} {...props}
/> />
@ -112,7 +113,7 @@ const SheetTitle = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<SheetPrimitive.Title <SheetPrimitive.Title
ref={ref} ref={ref}
className={cn("text-lg font-semibold text-foreground", className)} className={cn("text-foreground text-lg font-semibold", className)}
{...props} {...props}
/> />
)); ));
@ -124,7 +125,7 @@ const SheetDescription = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<SheetPrimitive.Description <SheetPrimitive.Description
ref={ref} ref={ref}
className={cn("text-sm text-muted-foreground", className)} className={cn("text-muted-foreground text-sm", className)}
{...props} {...props}
/> />
)); ));

View File

@ -6,7 +6,7 @@ function Skeleton({
}: React.HTMLAttributes<HTMLDivElement>) { }: React.HTMLAttributes<HTMLDivElement>) {
return ( return (
<div <div
className={cn("animate-pulse rounded-md bg-primary/10", className)} className={cn("bg-primary/10 animate-pulse rounded-md", className)}
{...props} {...props}
/> />
); );

View File

@ -12,15 +12,15 @@ const Slider = React.forwardRef<
<SliderPrimitive.Root <SliderPrimitive.Root
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex w-full touch-none select-none items-center", "relative flex w-full touch-none items-center select-none",
className className,
)} )}
{...props} {...props}
> >
<SliderPrimitive.Track className="relative h-1.5 w-full grow overflow-hidden rounded-full bg-primary/20"> <SliderPrimitive.Track className="bg-primary/20 relative h-1.5 w-full grow overflow-hidden rounded-full">
<SliderPrimitive.Range className="absolute h-full bg-primary" /> <SliderPrimitive.Range className="bg-primary absolute h-full" />
</SliderPrimitive.Track> </SliderPrimitive.Track>
<SliderPrimitive.Thumb className="block h-4 w-4 rounded-full border border-primary/50 bg-background shadow-sm transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50" /> <SliderPrimitive.Thumb className="border-primary/50 bg-background focus-visible:ring-ring block h-4 w-4 rounded-full border shadow-sm transition-colors focus-visible:ring-1 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50" />
</SliderPrimitive.Root> </SliderPrimitive.Root>
)); ));
Slider.displayName = SliderPrimitive.Root.displayName; Slider.displayName = SliderPrimitive.Root.displayName;

View File

@ -18,15 +18,15 @@ const SplitButton = forwardRef<HTMLDivElement, SplitButtonProps>(
<div <div
ref={ref} ref={ref}
className={cn( className={cn(
"inline-flex rounded-full h-7 border border-input bg-panel-accent overflow-hidden", "border-input bg-panel-accent inline-flex h-7 overflow-hidden rounded-full border",
className className,
)} )}
{...props} {...props}
> >
{children} {children}
</div> </div>
); );
} },
); );
SplitButton.displayName = "SplitButton"; SplitButton.displayName = "SplitButton";
@ -39,18 +39,18 @@ const SplitButtonSide = forwardRef<
ref={ref} ref={ref}
variant="text" variant="text"
className={cn( className={cn(
"h-full rounded-none bg-panel-accent !opacity-100 border-0 gap-0 font-normal transition-colors disabled:text-muted-foreground", "bg-panel-accent disabled:text-muted-foreground h-full gap-0 rounded-none border-0 font-normal !opacity-100 transition-colors",
onClick onClick
? "hover:bg-foreground/10 hover:opacity-100 cursor-pointer" ? "hover:bg-foreground/10 cursor-pointer hover:opacity-100"
: "cursor-default select-text", : "cursor-default select-text",
paddingClass, paddingClass,
className className,
)} )}
onClick={onClick} onClick={onClick}
{...props} {...props}
> >
{typeof children === "string" ? ( {typeof children === "string" ? (
<span className="font-normal cursor-text">{children}</span> <span className="cursor-text font-normal">{children}</span>
) : ( ) : (
children children
)} )}
@ -62,14 +62,14 @@ SplitButtonSide.displayName = "SplitButtonSide";
const SplitButtonLeft = forwardRef<HTMLButtonElement, SplitButtonSideProps>( const SplitButtonLeft = forwardRef<HTMLButtonElement, SplitButtonSideProps>(
({ ...props }, ref) => { ({ ...props }, ref) => {
return <SplitButtonSide ref={ref} paddingClass="pl-3 pr-2" {...props} />; return <SplitButtonSide ref={ref} paddingClass="pl-3 pr-2" {...props} />;
} },
); );
SplitButtonLeft.displayName = "SplitButtonLeft"; SplitButtonLeft.displayName = "SplitButtonLeft";
const SplitButtonRight = forwardRef<HTMLButtonElement, SplitButtonSideProps>( const SplitButtonRight = forwardRef<HTMLButtonElement, SplitButtonSideProps>(
({ ...props }, ref) => { ({ ...props }, ref) => {
return <SplitButtonSide ref={ref} paddingClass="pl-2 pr-3" {...props} />; return <SplitButtonSide ref={ref} paddingClass="pl-2 pr-3" {...props} />;
} },
); );
SplitButtonRight.displayName = "SplitButtonRight"; SplitButtonRight.displayName = "SplitButtonRight";
@ -79,11 +79,11 @@ const SplitButtonSeparator = forwardRef<HTMLDivElement, { className?: string }>(
<Separator <Separator
ref={ref} ref={ref}
orientation="vertical" orientation="vertical"
className={cn("h-full bg-foreground/15", className)} className={cn("bg-foreground/15 h-full", className)}
{...props} {...props}
/> />
); );
} },
); );
SplitButtonSeparator.displayName = "SplitButtonSeparator"; SplitButtonSeparator.displayName = "SplitButtonSeparator";

View File

@ -11,15 +11,15 @@ const Switch = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<SwitchPrimitives.Root <SwitchPrimitives.Root
className={cn( className={cn(
"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-xs transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input", "peer focus-visible:ring-ring focus-visible:ring-offset-background data-[state=checked]:bg-primary data-[state=unchecked]:bg-input inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-xs transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
className className,
)} )}
{...props} {...props}
ref={ref} ref={ref}
> >
<SwitchPrimitives.Thumb <SwitchPrimitives.Thumb
className={cn( className={cn(
"pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0" "bg-background pointer-events-none block h-4 w-4 rounded-full shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0",
)} )}
/> />
</SwitchPrimitives.Root> </SwitchPrimitives.Root>

View File

@ -43,8 +43,8 @@ const TableFooter = React.forwardRef<
<tfoot <tfoot
ref={ref} ref={ref}
className={cn( className={cn(
"border-t bg-muted/50 font-medium last:[&>tr]:border-b-0", "bg-muted/50 border-t font-medium last:[&>tr]:border-b-0",
className className,
)} )}
{...props} {...props}
/> />
@ -58,8 +58,8 @@ const TableRow = React.forwardRef<
<tr <tr
ref={ref} ref={ref}
className={cn( className={cn(
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", "hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",
className className,
)} )}
{...props} {...props}
/> />
@ -73,8 +73,8 @@ const TableHead = React.forwardRef<
<th <th
ref={ref} ref={ref}
className={cn( className={cn(
"h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", "text-muted-foreground h-10 px-2 text-left align-middle font-medium [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
className className,
)} )}
{...props} {...props}
/> />
@ -89,7 +89,7 @@ const TableCell = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
"p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", "p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
className className,
)} )}
{...props} {...props}
/> />
@ -102,7 +102,7 @@ const TableCaption = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<caption <caption
ref={ref} ref={ref}
className={cn("mt-4 text-sm text-muted-foreground", className)} className={cn("text-muted-foreground mt-4 text-sm", className)}
{...props} {...props}
/> />
)); ));

View File

@ -14,8 +14,8 @@ const TabsList = React.forwardRef<
<TabsPrimitive.List <TabsPrimitive.List
ref={ref} ref={ref}
className={cn( className={cn(
"inline-flex h-auto items-center justify-center gap-2 rounded-lg bg-transparent p-0 text-muted-foreground", "text-muted-foreground inline-flex h-auto items-center justify-center gap-2 rounded-lg bg-transparent p-0",
className className,
)} )}
{...props} {...props}
/> />
@ -29,8 +29,8 @@ const TabsTrigger = React.forwardRef<
<TabsPrimitive.Trigger <TabsPrimitive.Trigger
ref={ref} ref={ref}
className={cn( className={cn(
"inline-flex items-center cursor-pointer justify-center whitespace-nowrap rounded-lg px-3 py-1 text-sm font-medium ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-panel-accent data-[state=active]:text-foreground", "ring-offset-background focus-visible:ring-ring data-[state=active]:bg-panel-accent data-[state=active]:text-foreground inline-flex cursor-pointer items-center justify-center rounded-lg px-3 py-1 text-sm font-medium whitespace-nowrap focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50",
className className,
)} )}
{...props} {...props}
/> />
@ -44,8 +44,8 @@ const TabsContent = React.forwardRef<
<TabsPrimitive.Content <TabsPrimitive.Content
ref={ref} ref={ref}
className={cn( className={cn(
"mt-2 ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", "ring-offset-background focus-visible:ring-ring mt-2 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden",
className className,
)} )}
{...props} {...props}
/> />

View File

@ -9,10 +9,10 @@ const Textarea = React.forwardRef<
return ( return (
<textarea <textarea
className={cn( className={cn(
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex min-h-[60px] w-full rounded-md border bg-accent/50 px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input bg-accent/50 flex min-h-[60px] w-full rounded-md border px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[2px]", "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[2px]",
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
className className,
)} )}
ref={ref} ref={ref}
{...props} {...props}

View File

@ -16,8 +16,8 @@ const ToastViewport = React.forwardRef<
<ToastPrimitives.Viewport <ToastPrimitives.Viewport
ref={ref} ref={ref}
className={cn( className={cn(
"fixed top-0 z-100 flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]", "fixed top-0 z-100 flex max-h-screen w-full flex-col-reverse p-4 sm:top-auto sm:right-0 sm:bottom-0 sm:flex-col md:max-w-[420px]",
className className,
)} )}
{...props} {...props}
/> />
@ -37,13 +37,13 @@ const toastVariants = cva(
defaultVariants: { defaultVariants: {
variant: "default", variant: "default",
}, },
} },
); );
const Toast = React.forwardRef< const Toast = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Root>, React.ElementRef<typeof ToastPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> & React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
VariantProps<typeof toastVariants> VariantProps<typeof toastVariants>
>(({ className, variant, ...props }, ref) => { >(({ className, variant, ...props }, ref) => {
return ( return (
<ToastPrimitives.Root <ToastPrimitives.Root
@ -62,8 +62,8 @@ const ToastAction = React.forwardRef<
<ToastPrimitives.Action <ToastPrimitives.Action
ref={ref} ref={ref}
className={cn( className={cn(
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors hover:bg-secondary focus-visible:outline-hidden focus-visible:ring-1 focus:ring-ring disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 hover:group-[.destructive]:border-destructive/30 hover:group-[.destructive]:bg-destructive hover:group-[.destructive]:text-destructive-foreground group-[.destructive]:focus-visible::ring-destructive", "hover:bg-secondary focus:ring-ring group-[.destructive]:border-muted/40 hover:group-[.destructive]:border-destructive/30 hover:group-[.destructive]:bg-destructive hover:group-[.destructive]:text-destructive-foreground group-[.destructive]:focus-visible::ring-destructive inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors focus-visible:ring-1 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50",
className className,
)} )}
{...props} {...props}
/> />
@ -77,8 +77,8 @@ const ToastClose = React.forwardRef<
<ToastPrimitives.Close <ToastPrimitives.Close
ref={ref} ref={ref}
className={cn( className={cn(
"absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-hidden focus-visible::ring-1 group-hover:opacity-100 group-[.destructive]:text-muted-foreground hover:group-[.destructive]:text-red-50 focus-visible:group-[.destructive]:ring-red-400 focus-visible:group-[.destructive]:ring-offset-red-600", "text-foreground/50 hover:text-foreground focus-visible::ring-1 group-[.destructive]:text-muted-foreground absolute top-1 right-1 rounded-md p-1 opacity-0 transition-opacity group-hover:opacity-100 hover:group-[.destructive]:text-red-50 focus:opacity-100 focus:outline-hidden focus-visible:group-[.destructive]:ring-red-400 focus-visible:group-[.destructive]:ring-offset-red-600",
className className,
)} )}
toast-close="" toast-close=""
{...props} {...props}

View File

@ -17,7 +17,7 @@ const ToggleGroupContext = React.createContext<
const ToggleGroup = React.forwardRef< const ToggleGroup = React.forwardRef<
React.ElementRef<typeof ToggleGroupPrimitive.Root>, React.ElementRef<typeof ToggleGroupPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> & React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> &
VariantProps<typeof toggleVariants> VariantProps<typeof toggleVariants>
>(({ className, variant, size, children, ...props }, ref) => ( >(({ className, variant, size, children, ...props }, ref) => (
<ToggleGroupPrimitive.Root <ToggleGroupPrimitive.Root
ref={ref} ref={ref}
@ -35,7 +35,7 @@ ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName;
const ToggleGroupItem = React.forwardRef< const ToggleGroupItem = React.forwardRef<
React.ElementRef<typeof ToggleGroupPrimitive.Item>, React.ElementRef<typeof ToggleGroupPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> & React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> &
VariantProps<typeof toggleVariants> VariantProps<typeof toggleVariants>
>(({ className, children, variant, size, ...props }, ref) => { >(({ className, children, variant, size, ...props }, ref) => {
const context = React.useContext(ToggleGroupContext); const context = React.useContext(ToggleGroupContext);
@ -47,7 +47,7 @@ const ToggleGroupItem = React.forwardRef<
variant: context.variant || variant, variant: context.variant || variant,
size: context.size || size, size: context.size || size,
}), }),
className className,
)} )}
{...props} {...props}
> >

View File

@ -25,13 +25,13 @@ const toggleVariants = cva(
variant: "default", variant: "default",
size: "default", size: "default",
}, },
} },
); );
const Toggle = React.forwardRef< const Toggle = React.forwardRef<
React.ElementRef<typeof TogglePrimitive.Root>, React.ElementRef<typeof TogglePrimitive.Root>,
React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> & React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &
VariantProps<typeof toggleVariants> VariantProps<typeof toggleVariants>
>(({ className, variant, size, ...props }, ref) => ( >(({ className, variant, size, ...props }, ref) => (
<TogglePrimitive.Root <TogglePrimitive.Root
ref={ref} ref={ref}

View File

@ -1,8 +1,8 @@
import { cva, type VariantProps } from 'class-variance-authority'; import { cva, type VariantProps } from "class-variance-authority";
import { Tooltip as TooltipPrimitive } from 'radix-ui'; import { Tooltip as TooltipPrimitive } from "radix-ui";
import * as React from 'react'; import * as React from "react";
import { cn } from '@/utils/ui'; import { cn } from "@/utils/ui";
const TooltipProvider = TooltipPrimitive.Provider; const TooltipProvider = TooltipPrimitive.Provider;
@ -11,36 +11,37 @@ const Tooltip = TooltipPrimitive.Root;
const TooltipTrigger = TooltipPrimitive.Trigger; const TooltipTrigger = TooltipPrimitive.Trigger;
const tooltipVariants = cva( const tooltipVariants = cva(
'z-50 overflow-visible rounded-sm text-sm shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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 overflow-visible rounded-sm text-sm shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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",
{ {
variants: { variants: {
variant: { variant: {
default: 'bg-popover text-popover-foreground border px-3 py-1.5', default: "bg-popover text-popover-foreground border px-3 py-1.5",
destructive: destructive:
'bg-destructive/10 text-destructive dark:bg-destructive/20 border-destructive [border-width:0.5px]', "bg-destructive/10 text-destructive dark:bg-destructive/20 border-destructive [border-width:0.5px]",
outline: 'border-border', outline: "border-border",
important: important:
'bg-amber-100/90 text-amber-900 dark:bg-amber-900/20 dark:text-amber-300 border-amber-900 [border-width:0.5px]', "bg-amber-100/90 text-amber-900 dark:bg-amber-900/20 dark:text-amber-300 border-amber-900 [border-width:0.5px]",
promotions: promotions:
'bg-red-100/90 text-red-900 dark:bg-red-900/20 dark:text-red-300 border-red-900 [border-width:0.5px]', "bg-red-100/90 text-red-900 dark:bg-red-900/20 dark:text-red-300 border-red-900 [border-width:0.5px]",
personal: personal:
'bg-green-100/90 text-green-900 dark:bg-green-900/20 dark:text-green-300 border-green-900 [border-width:0.5px]', "bg-green-100/90 text-green-900 dark:bg-green-900/20 dark:text-green-300 border-green-900 [border-width:0.5px]",
updates: updates:
'bg-purple-100/90 text-purple-900 dark:bg-purple-900/20 dark:text-purple-300 border-purple-900 [border-width:0.5px]', "bg-purple-100/90 text-purple-900 dark:bg-purple-900/20 dark:text-purple-300 border-purple-900 [border-width:0.5px]",
forums: forums:
'bg-blue-100/90 text-blue-900 dark:bg-blue-900/20 dark:text-blue-300 border-blue-900 [border-width:0.5px]', "bg-blue-100/90 text-blue-900 dark:bg-blue-900/20 dark:text-blue-300 border-blue-900 [border-width:0.5px]",
sidebar: 'bg-white dark:bg-[#413F3E] p-2.5 flex flex-col gap-2', sidebar: "bg-white dark:bg-[#413F3E] p-2.5 flex flex-col gap-2",
}, },
}, },
defaultVariants: { defaultVariants: {
variant: 'default', variant: "default",
}, },
}, },
); );
interface TooltipContentProps interface TooltipContentProps
extends React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>, extends
VariantProps<typeof tooltipVariants> { } React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>,
VariantProps<typeof tooltipVariants> {}
const TooltipContent = React.forwardRef< const TooltipContent = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Content>, React.ElementRef<typeof TooltipPrimitive.Content>,
@ -52,16 +53,19 @@ const TooltipContent = React.forwardRef<
className={cn(tooltipVariants({ variant }), className)} className={cn(tooltipVariants({ variant }), className)}
{...props} {...props}
> >
{variant === 'sidebar' && ( {variant === "sidebar" && (
<svg <svg
width="6" width="6"
height="10" height="10"
viewBox="0 0 6 10" viewBox="0 0 6 10"
fill="none" fill="none"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
className="absolute left-[-6px] top-1/2 -translate-y-1/2" className="absolute top-1/2 left-[-6px] -translate-y-1/2"
> >
<path d="M6 0L0 5L6 10V0Z" className="fill-white/80 dark:fill-[#413F3E]" /> <path
d="M6 0L0 5L6 10V0Z"
className="fill-white/80 dark:fill-[#413F3E]"
/>
</svg> </svg>
)} )}
{props.children} {props.children}
@ -69,4 +73,4 @@ const TooltipContent = React.forwardRef<
)); ));
TooltipContent.displayName = TooltipPrimitive.Content.displayName; TooltipContent.displayName = TooltipPrimitive.Content.displayName;
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }; export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };

Some files were not shown because too many files have changed in this diff Show More