fix(ui): sidebar icon centering and local dev setup (#68)

This commit is contained in:
aaf2tbz 2026-02-12 08:12:35 -07:00 committed by GitHub
parent 337117f895
commit 89b0f8e88e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 492 additions and 153 deletions

View File

@ -1,104 +1,112 @@
# Local Development Setup # Local Development Setup
This directory contains patches and scripts to enable local development without WorkOS authentication. This directory contains files for running Compass locally without Cloudflare or WorkOS credentials.
## What This Does ## What This Does
These patches modify Compass to run in development mode without requiring WorkOS SSO authentication: 1. **Bypasses WorkOS auth** - Middleware skips auth when API keys aren't configured
2. **Local SQLite database** - Uses sql.js (pure JS SQLite) instead of Cloudflare D1
3. **Dev user** - `dev@compass.io` with admin role
4. **Full database support** - Real queries work, not just mocks
1. **Bypasses WorkOS auth checks** - Middleware redirects `/` to `/dashboard` when WorkOS isn't configured ## Quick Start
2. **Mock D1 database** - Returns empty arrays for queries instead of throwing errors
3. **Wraps Cloudflare context** - Returns mock `{ env: { DB: null }, ctx: {} }` when WorkOS isn't configured
4. **Skips OpenNext initialization** - Only runs when WorkOS API keys are properly set
## How to Use
### Quick Start
From the compass directory: From the compass directory:
```bash ```bash
.dev-setup/apply-dev.sh .dev-setup/apply-dev.sh
bun dev
``` ```
This will apply all necessary patches to enable local development. ## Manual Setup
### Manual Application If the script fails, apply manually:
If the automated script fails, you can apply patches manually:
1. **middleware.ts** - Redirects root to dashboard, allows all requests without auth
2. **lib/auth.ts** - Checks for "your_" and "placeholder" in WorkOS keys
3. **lib/cloudflare-context.ts** - New file that wraps `getCloudflareContext()`
4. **db/index.ts** - Returns mock DB when `d1` parameter is `null`
5. **next.config.ts** - Only initializes OpenNext Cloudflare when WorkOS is configured
6. **.gitignore** - Ignores `src/lib/cloudflare-context.ts` so it's not committed
### Undoing Changes
To remove dev setup and restore original behavior:
```bash ```bash
git restore src/middleware.ts # 1. Install sql.js
git restore src/lib/auth.ts bun add sql.js
git restore src/db/index.ts
git restore next.config.ts # 2. Copy dev files
git restore .gitignore cp .dev-setup/files/middleware.ts src/middleware.ts
rm src/lib/cloudflare-context.ts cp .dev-setup/files/next.config.ts next.config.ts
cp .dev-setup/files/cloudflare-context.ts src/lib/cloudflare-context.ts
cp .dev-setup/files/db.ts src/lib/db.ts
mkdir -p scripts
cp .dev-setup/files/init-local-db.ts scripts/init-local-db.ts
# 3. Replace imports
find src -name "*.ts" -o -name "*.tsx" | xargs sed -i '' 's|from "@opennextjs/cloudflare"|from "@/lib/db"|g'
# 4. Add script to package.json
node -e 'const fs=require("fs");const p=JSON.parse(fs.readFileSync("package.json"));p.scripts["db:init-local"]="bun scripts/init-local-db.ts";fs.writeFileSync("package.json",JSON.stringify(p,null,2)+"\n")'
# 5. Initialize database
bun run db:init-local
# 6. Start dev server
bun dev
``` ```
## Reverting
```bash
.dev-setup/restore-dev.sh
```
Or manually:
```bash
git checkout HEAD -- src/middleware.ts next.config.ts package.json bun.lock
rm -f src/lib/cloudflare-context.ts src/lib/db.ts scripts/init-local-db.ts
find src -name "*.ts" -o -name "*.tsx" | xargs sed -i '' 's|from "@/lib/db"|from "@opennextjs/cloudflare"|g'
bun remove sql.js
rm -f local.db local.db-wal local.db-shm
```
## Files
| File | Purpose |
|------|---------|
| `files/middleware.ts` | Bypasses WorkOS when not configured |
| `files/next.config.ts` | Removes Cloudflare dev proxy |
| `files/cloudflare-context.ts` | sql.js wrapper mimicking D1 |
| `files/db.ts` | Conditional import wrapper |
| `files/init-local-db.ts` | Migration runner for local DB |
## Environment Variables ## Environment Variables
To configure WorkOS auth properly (to disable dev mode): To use real WorkOS auth (disables dev mode):
```env ```env
WORKOS_API_KEY=sk_dev_xxxxx WORKOS_API_KEY=sk_dev_xxxxx
WORKOS_CLIENT_ID=client_xxxxx WORKOS_CLIENT_ID=client_xxxxx
WORKOS_REDIRECT_URI=http://localhost:3000
``` ```
With these set, the dev patches will automatically skip and use real WorkOS authentication. Values containing "placeholder" trigger dev mode.
## Dev Mode Indicators ## Database
When in dev mode (WorkOS not configured): - Stored in `local.db` at repo root
- Dashboard loads directly without login redirect - Migrations applied from `drizzle/` directory
- Database queries return empty arrays instead of errors - Persisted between sessions
- Cloudflare context returns null DB instead of throwing
## Files Created/Modified ## Limitations
### New Files (dev only) Features requiring external APIs won't work offline:
- `src/lib/cloudflare-context.ts` - Wraps `getCloudflareContext()` with dev bypass - NetSuite sync
- Google Drive integration
### Modified Files - AI agent (needs OPENROUTER_API_KEY)
- `src/middleware.ts` - Added WorkOS detection and dev bypass - Push notifications
- `src/lib/auth.ts` - Enhanced WorkOS configuration detection
- `src/db/index.ts` - Added mock DB support for null D1 parameter
- `next.config.ts` - Conditional OpenNext initialization
- `.gitignore` - Added `cloudflare-context.ts` to prevent commits
## Future Development
Place any new dev-only components or patches in this directory following the same pattern:
1. **Patch files** - Store in `patches/` with `.patch` extension
2. **New files** - Store in `files/` directory
3. **Update apply-dev.sh** - Add your patches to the apply script
4. **Document here** - Update this README with changes
## Troubleshooting ## Troubleshooting
**Build errors after applying patches:** **"Cannot find module 'sql.js'"**
- Check that `src/lib/cloudflare-context.ts` exists - Run `bun add sql.js`
- Verify all patches applied cleanly
- Try manual patch application if automated script fails
**Auth still required:** **Database errors**
- Verify `.env.local` or `.dev.vars` doesn't have placeholder values - Re-run `bun run db:init-local`
- Check that WorkOS environment variables aren't set (if you want dev mode) - Check `local.db` exists
- Restart dev server after applying patches
**Database errors:** **Auth still required**
- Ensure `src/db/index.ts` patch was applied - Verify no WORKOS_API_KEY set
- Check that mock DB is being returned when `d1` is null - Check middleware.ts was copied

View File

@ -1,9 +1,8 @@
#!/bin/bash #!/bin/bash
set -e set -e
echo "🔧 Applying local development setup patches..." echo "🔧 Applying local development setup..."
# Check if we're in the compass directory
if [ ! -f "package.json" ] || [ ! -d "src" ]; then if [ ! -f "package.json" ] || [ ! -d "src" ]; then
echo "❌ Error: Please run this script from the compass directory" echo "❌ Error: Please run this script from the compass directory"
exit 1 exit 1
@ -11,55 +10,45 @@ fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Apply middleware patch # 1. Install sql.js
echo "📦 Applying middleware.ts patch..." echo "📦 Installing sql.js..."
patch -p1 < "$SCRIPT_DIR/patches/middleware.patch" || { bun add sql.js
echo "⚠️ middleware.ts patch failed, applying manually..."
cat "$SCRIPT_DIR/patches/middleware.patch"
}
# Apply auth patch # 2. Copy dev files
echo "📦 Applying auth.ts patch..." echo "📦 Copying dev files..."
patch -p1 < "$SCRIPT_DIR/patches/auth.patch" || { cp "$SCRIPT_DIR/files/middleware.ts" src/middleware.ts
echo "⚠️ auth.ts patch failed, applying manually..." cp "$SCRIPT_DIR/files/next.config.ts" next.config.ts
cat "$SCRIPT_DIR/patches/auth.patch" cp "$SCRIPT_DIR/files/cloudflare-context.ts" src/lib/cloudflare-context.ts
} cp "$SCRIPT_DIR/files/db.ts" src/lib/db.ts
mkdir -p scripts
cp "$SCRIPT_DIR/files/init-local-db.ts" scripts/init-local-db.ts
# Apply cloudflare-context (create the wrapper file) # 3. Replace all @opennextjs/cloudflare imports with local wrapper
echo "📦 Applying cloudflare-context.ts..." echo "📦 Updating imports..."
if [ ! -f "src/lib/cloudflare-context.ts" ]; then find src -name "*.ts" -o -name "*.tsx" | xargs sed -i '' 's|from "@opennextjs/cloudflare"|from "@/lib/db"|g'
mkdir -p src/lib
cp "$SCRIPT_DIR/files/cloudflare-context.ts" src/lib/ # 4. Add db:init-local script to package.json if not present
echo "✓ Created src/lib/cloudflare-context.ts" if ! grep -q '"db:init-local"' package.json; then
else echo "📦 Adding db:init-local script..."
echo "⚠️ cloudflare-context.ts already exists, skipping" # Use node to update package.json
node -e '
const fs = require("fs");
const pkg = JSON.parse(fs.readFileSync("package.json"));
pkg.scripts["db:init-local"] = "bun scripts/init-local-db.ts";
fs.writeFileSync("package.json", JSON.stringify(pkg, null, 2) + "\n");
'
fi fi
# Apply db-index patch # 5. Initialize local database
echo "📦 Applying db/index.ts patch..." echo "📦 Initializing local database..."
patch -p1 < "$SCRIPT_DIR/patches/db-index.patch" || { bun run db:init-local
echo "⚠️ db/index.ts patch failed, applying manually..."
cat "$SCRIPT_DIR/patches/db-index.patch"
}
# Apply next-config patch
echo "📦 Applying next.config.ts patch..."
patch -p1 < "$SCRIPT_DIR/patches/next-config.patch" || {
echo "⚠️ next.config.ts patch failed, applying manually..."
cat "$SCRIPT_DIR/patches/next-config.patch"
}
# Update .gitignore
echo "📦 Updating .gitignore..."
patch -p1 < "$SCRIPT_DIR/patches/gitignore.patch" || {
echo "⚠️ .gitignore patch failed, applying manually..."
cat "$SCRIPT_DIR/patches/gitignore.patch"
}
echo "" echo ""
echo "✅ Development setup complete!" echo "✅ Development setup complete!"
echo "" echo ""
echo "📝 Notes:" echo "📝 Notes:"
echo " - These changes allow local development without WorkOS authentication" echo " - Local database stored in local.db"
echo " - To use WorkOS auth, remove these changes or revert the patches" echo " - Dev user: dev@compass.io (admin role)"
echo " - Modified files: src/lib/cloudflare-context.ts, src/middleware.ts, src/lib/auth.ts, src/db/index.ts, next.config.ts, .gitignore" echo " - Run 'bun dev' to start"
echo ""
echo "🔄 To revert: run .dev-setup/restore-dev.sh"

View File

@ -1,20 +1,189 @@
import { getCloudflareContext as originalGetCloudflareContext } from "@opennextjs/cloudflare" import initSqlJs, { Database as SqlJsDatabase } from "sql.js"
import { readFileSync, writeFileSync, existsSync } from "fs"
import { createRequire } from "module"
import { getDb } from "@/db"
const isWorkOSConfigured = const require = createRequire(import.meta.url)
process.env.WORKOS_API_KEY && const sqlJsWasm = require.resolve("sql.js/dist/sql-wasm.wasm")
process.env.WORKOS_CLIENT_ID &&
!process.env.WORKOS_API_KEY.includes("your_") &&
!process.env.WORKOS_API_KEY.includes("placeholder")
export async function getCloudflareContext() { const DB_PATH = process.env.LOCAL_DB_PATH || "local.db"
if (!isWorkOSConfigured) {
return {
env: {
DB: null,
},
ctx: {},
} as Awaited<ReturnType<typeof originalGetCloudflareContext>>
}
return originalGetCloudflareContext() let sqlJs: Awaited<ReturnType<typeof initSqlJs>> | null = null
let db: SqlJsDatabase | null = null
async function getLocalDb(): Promise<SqlJsDatabase> {
if (!sqlJs) {
sqlJs = await initSqlJs({
locateFile: (file: string) => {
if (file.endsWith(".wasm")) {
return sqlJsWasm
}
return file
},
})
}
if (!db) {
if (existsSync(DB_PATH)) {
const buffer = readFileSync(DB_PATH)
db = new sqlJs.Database(buffer)
} else {
db = new sqlJs.Database()
}
}
return db
}
function saveDb(db: SqlJsDatabase) {
const data = db.export()
const buffer = Buffer.from(data)
writeFileSync(DB_PATH, buffer)
}
interface D1Result<T = unknown> {
results: T[]
success: boolean
meta: {
duration: number
changes: number
last_row_id: number
rows_read: number
rows_written: number
}
}
class LocalPreparedStatement {
private db: SqlJsDatabase
private query: string
private boundValues: unknown[] = []
constructor(db: SqlJsDatabase, query: string) {
this.db = db
this.query = query
}
bind(...values: unknown[]): this {
this.boundValues = values
return this
}
async first<T = unknown>(): Promise<T | null> {
const stmt = this.db.prepare(this.query)
if (this.boundValues.length > 0) {
stmt.bind(this.boundValues as number[])
}
if (stmt.step()) {
const row = stmt.getAsObject() as T
stmt.free()
return row
}
stmt.free()
return null
}
async run(): Promise<D1Result> {
this.db.run(this.query, this.boundValues as number[])
saveDb(this.db)
return {
results: [],
success: true,
meta: {
duration: 0,
changes: this.db.getRowsModified(),
last_row_id: Number(this.db.exec("SELECT last_insert_rowid()")[0]?.values[0]?.[0] || 0),
rows_read: 0,
rows_written: this.db.getRowsModified(),
},
}
}
async all<T = unknown>(): Promise<D1Result<T>> {
const stmt = this.db.prepare(this.query)
if (this.boundValues.length > 0) {
stmt.bind(this.boundValues as number[])
}
const results: T[] = []
while (stmt.step()) {
results.push(stmt.getAsObject() as T)
}
stmt.free()
return {
results,
success: true,
meta: {
duration: 0,
changes: 0,
last_row_id: 0,
rows_read: results.length,
rows_written: 0,
},
}
}
async raw<T = unknown>(): Promise<T[]> {
const results = this.db.exec(this.query, this.boundValues as number[])
return results[0]?.values as T[] ?? []
}
}
class LocalD1Database {
constructor(private db: SqlJsDatabase) {}
prepare(query: string): LocalPreparedStatement {
return new LocalPreparedStatement(this.db, query)
}
async batch<T = unknown>(
statements: LocalPreparedStatement[]
): Promise<D1Result<T>[]> {
const results: D1Result<T>[] = []
for (const stmt of statements) {
results.push(await stmt.all<T>())
}
return results
}
async exec(query: string): Promise<D1Result> {
this.db.run(query)
saveDb(this.db)
return {
results: [],
success: true,
meta: {
duration: 0,
changes: this.db.getRowsModified(),
last_row_id: 0,
rows_read: 0,
rows_written: this.db.getRowsModified(),
},
}
}
async dump(): Promise<ArrayBuffer> {
const data = this.db.export()
return data.buffer as ArrayBuffer
}
}
export async function getCloudflareContext(): Promise<{
env: {
DB: D1Database
[key: string]: unknown
}
ctx: {
waitUntil: (promise: Promise<unknown>) => void
}
cf: unknown
}> {
const localDb = await getLocalDb()
const d1 = new LocalD1Database(localDb) as unknown as D1Database
return {
env: {
DB: d1,
},
ctx: {
waitUntil: () => {},
},
cf: {},
}
} }

27
.dev-setup/files/db.ts Normal file
View File

@ -0,0 +1,27 @@
export async function getCloudflareContext(): Promise<{
env: {
DB: D1Database
[key: string]: unknown
}
ctx: {
waitUntil: (promise: Promise<unknown>) => void
}
cf: unknown
}> {
const isLocalDev =
process.env.NODE_ENV === "development" &&
(!process.env.WORKOS_API_KEY ||
process.env.WORKOS_API_KEY.includes("placeholder"))
if (isLocalDev) {
const { getCloudflareContext: getLocalContext } = await import(
"./cloudflare-context"
)
return getLocalContext()
}
const { getCloudflareContext: getCfContext } = await import(
"@opennextjs/cloudflare"
)
return getCfContext()
}

View File

@ -0,0 +1,51 @@
import initSqlJs from "sql.js"
import { readFileSync, readdirSync, writeFileSync, existsSync } from "fs"
import { join, dirname } from "path"
import { createRequire } from "module"
import { fileURLToPath } from "url"
const require = createRequire(import.meta.url)
const __dirname = dirname(fileURLToPath(import.meta.url))
const sqlJsWasm = require.resolve("sql.js/dist/sql-wasm.wasm")
const DB_PATH = process.env.LOCAL_DB_PATH || "local.db"
const MIGRATIONS_DIR = join(process.cwd(), "drizzle")
async function main() {
const sqlJs = await initSqlJs({
locateFile: (file: string) => {
if (file.endsWith(".wasm")) {
return sqlJsWasm
}
return file
},
})
let db: ReturnType<typeof sqlJs.Database>
if (existsSync(DB_PATH)) {
const buffer = readFileSync(DB_PATH)
db = new sqlJs.Database(buffer)
} else {
db = new sqlJs.Database()
}
const migrations = readdirSync(MIGRATIONS_DIR)
.filter((f) => f.endsWith(".sql") && !f.includes("seed"))
.sort()
console.log(`Running ${migrations.length} migrations on ${DB_PATH}...`)
for (const migration of migrations) {
const sql = readFileSync(join(MIGRATIONS_DIR, migration), "utf-8")
console.log(` ${migration}`)
db.run(sql)
}
const data = db.export()
const buffer = Buffer.from(data)
writeFileSync(DB_PATH, buffer)
console.log("Done!")
}
main().catch(console.error)

View File

@ -0,0 +1,61 @@
import { NextRequest, NextResponse } from "next/server"
import { authkit, handleAuthkitHeaders } from "@workos-inc/authkit-nextjs"
const publicPaths = [
"/",
"/login",
"/signup",
"/reset-password",
"/verify-email",
"/invite",
"/callback",
]
const bridgePaths = [
"/api/bridge/register",
"/api/bridge/tools",
"/api/bridge/context",
]
function isPublicPath(pathname: string): boolean {
return (
publicPaths.includes(pathname) ||
bridgePaths.includes(pathname) ||
pathname.startsWith("/api/auth/") ||
pathname.startsWith("/api/netsuite/") ||
pathname.startsWith("/api/google/")
)
}
const isWorkOSConfigured =
process.env.WORKOS_API_KEY &&
process.env.WORKOS_CLIENT_ID &&
!process.env.WORKOS_API_KEY.includes("placeholder")
export default async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl
if (!isWorkOSConfigured) {
return NextResponse.next()
}
const { session, headers } = await authkit(request)
if (isPublicPath(pathname)) {
return handleAuthkitHeaders(request, headers)
}
if (!session.user) {
const loginUrl = new URL("/login", request.url)
loginUrl.searchParams.set("from", pathname)
return handleAuthkitHeaders(request, headers, { redirect: loginUrl.toString() })
}
return handleAuthkitHeaders(request, headers)
}
export const config = {
matcher: [
"/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",
],
}

View File

@ -0,0 +1,23 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
eslint: {
ignoreDuringBuilds: true,
},
experimental: {
optimizePackageImports: [
"@tabler/icons-react",
"lucide-react",
"@radix-ui/react-icons",
"recharts",
"@workos-inc/node",
"date-fns",
"remeda",
"framer-motion",
],
},
};
export default nextConfig;
// Cloudflare dev proxy removed for local dev - uses sql.js instead

View File

@ -1,36 +1,39 @@
#!/bin/bash #!/bin/bash
set -e set -e
echo "🔄 Removing local development setup patches..." echo "🔄 Removing local development setup..."
# Check if we're in the compass directory
if [ ! -f "package.json" ] || [ ! -d "src" ]; then if [ ! -f "package.json" ] || [ ! -d "src" ]; then
echo "❌ Error: Please run this script from the compass directory" echo "❌ Error: Please run this script from the compass directory"
exit 1 exit 1
fi fi
# Restore modified files # Revert tracked files
echo "📦 Restoring modified files..." echo "📦 Restoring modified files..."
git restore src/middleware.ts git checkout HEAD -- src/middleware.ts next.config.ts package.json bun.lock
git restore src/lib/auth.ts
git restore src/db/index.ts
git restore next.config.ts
git restore .gitignore
# Remove dev-only new file # Remove dev-only files
echo "📦 Removing dev-only files..." echo "📦 Removing dev-only files..."
if [ -f "src/lib/cloudflare-context.ts" ]; then rm -f src/lib/cloudflare-context.ts
rm src/lib/cloudflare-context.ts rm -f src/lib/db.ts
echo "✓ Removed src/lib/cloudflare-context.ts" rm -f scripts/init-local-db.ts
else echo "✓ Removed dev files"
echo "⚠️ src/lib/cloudflare-context.ts not found, skipping"
fi # Restore original imports
echo "📦 Restoring original imports..."
find src -name "*.ts" -o -name "*.tsx" | xargs sed -i '' 's|from "@/lib/db"|from "@opennextjs/cloudflare"|g'
# Remove sql.js
echo "📦 Removing sql.js..."
bun remove sql.js
# Remove local database
rm -f local.db local.db-wal local.db-shm
echo "" echo ""
echo "✅ Development setup removed!" echo "✅ Development setup removed!"
echo "" echo ""
echo "📝 Notes:" echo "📝 Notes:"
echo " - Original code has been restored from git" echo " - Original code restored from git"
echo " - Dev mode is now disabled" echo " - WorkOS/Cloudflare auth will be required"
echo " - WorkOS authentication will be required (if configured)" echo " - To re-apply: .dev-setup/apply-dev.sh"
echo " - To re-apply dev setup, run: .dev-setup/apply-dev.sh"

7
.gitignore vendored
View File

@ -41,3 +41,10 @@ android/app/build/
# Local auth bypass (dev only) # Local auth bypass (dev only)
src/lib/auth-bypass.ts src/lib/auth-bypass.ts
src/lib/cloudflare-context.ts src/lib/cloudflare-context.ts
src/lib/db.ts
scripts/init-local-db.ts
# local dev database
local.db
local.db-wal
local.db-shm

View File

@ -31,6 +31,7 @@ export function NavSecondary({
<SidebarMenuButton <SidebarMenuButton
asChild={!item.onClick} asChild={!item.onClick}
onClick={item.onClick} onClick={item.onClick}
tooltip={item.title}
> >
{item.onClick ? ( {item.onClick ? (
<> <>

View File

@ -469,7 +469,7 @@ function SidebarMenuItem({ className, ...props }: React.ComponentProps<"li">) {
} }
const sidebarMenuButtonVariants = cva( const sidebarMenuButtonVariants = cva(
"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-9! group-data-[collapsible=icon]:justify-center! group-data-[collapsible=icon]:p-2.5! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0", "peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:justify-center! group-data-[collapsible=icon]:gap-0! group-data-[collapsible=icon]:[&>*:nth-child(n+2)]:translate-x-10 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
{ {
variants: { variants: {
variant: { variant: {
@ -478,9 +478,9 @@ const sidebarMenuButtonVariants = cva(
"bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]", "bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]",
}, },
size: { size: {
default: "h-8 text-sm", default: "h-8 text-sm group-data-[collapsible=icon]:size-9! group-data-[collapsible=icon]:p-2!",
sm: "h-7 text-xs", sm: "h-7 text-xs group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-1.5!",
lg: "h-12 text-sm group-data-[collapsible=icon]:p-0!", lg: "h-12 text-sm group-data-[collapsible=icon]:size-11! group-data-[collapsible=icon]:p-1.5! group-data-[collapsible=icon]:-translate-x-[0.3rem] group-data-[collapsible=icon]:[&>*:first-child]:translate-x-[0.5rem]",
}, },
}, },
defaultVariants: { defaultVariants: {