feat: initial commit
This commit is contained in:
205
.agents/skills/drizzle/SKILL.md
Normal file
205
.agents/skills/drizzle/SKILL.md
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
---
|
||||||
|
name: drizzle
|
||||||
|
description: Drizzle ORM schema and database guide. Use when working with database schemas (src/database/schemas/*), defining tables, creating migrations, or database model code. Triggers on Drizzle schema definition, database migrations, or ORM usage questions.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Drizzle ORM Schema Style Guide
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
- Config: `drizzle.config.ts`
|
||||||
|
- Schemas: `src/database/schemas/`
|
||||||
|
- Migrations: `src/database/migrations/`
|
||||||
|
- Dialect: `postgresql` with `strict: true`
|
||||||
|
|
||||||
|
## Helper Functions
|
||||||
|
|
||||||
|
Location: `src/database/schemas/_helpers.ts`
|
||||||
|
|
||||||
|
- `timestamptz(name)`: Timestamp with timezone
|
||||||
|
- `createdAt()`, `updatedAt()`, `accessedAt()`: Standard timestamp columns
|
||||||
|
- `timestamps`: Object with all three for easy spread
|
||||||
|
|
||||||
|
## Naming Conventions
|
||||||
|
|
||||||
|
- **Tables**: Plural snake_case (`users`, `session_groups`)
|
||||||
|
- **Columns**: snake_case (`user_id`, `created_at`)
|
||||||
|
|
||||||
|
## Column Definitions
|
||||||
|
|
||||||
|
### Primary Keys
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
id: text('id')
|
||||||
|
.primaryKey()
|
||||||
|
.$defaultFn(() => idGenerator('agents'))
|
||||||
|
.notNull(),
|
||||||
|
```
|
||||||
|
|
||||||
|
ID prefixes make entity types distinguishable. For internal tables, use `uuid`.
|
||||||
|
|
||||||
|
### Foreign Keys
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
userId: text('user_id')
|
||||||
|
.references(() => users.id, { onDelete: 'cascade' })
|
||||||
|
.notNull(),
|
||||||
|
```
|
||||||
|
|
||||||
|
### Timestamps
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
...timestamps, // Spread from _helpers.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
### Indexes
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Return array (object style deprecated)
|
||||||
|
(t) => [uniqueIndex('client_id_user_id_unique').on(t.clientId, t.userId)],
|
||||||
|
```
|
||||||
|
|
||||||
|
## Type Inference
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export const insertAgentSchema = createInsertSchema(agents);
|
||||||
|
export type NewAgent = typeof agents.$inferInsert;
|
||||||
|
export type AgentItem = typeof agents.$inferSelect;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Pattern
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export const agents = pgTable(
|
||||||
|
'agents',
|
||||||
|
{
|
||||||
|
id: text('id')
|
||||||
|
.primaryKey()
|
||||||
|
.$defaultFn(() => idGenerator('agents'))
|
||||||
|
.notNull(),
|
||||||
|
slug: varchar('slug', { length: 100 })
|
||||||
|
.$defaultFn(() => randomSlug(4))
|
||||||
|
.unique(),
|
||||||
|
userId: text('user_id')
|
||||||
|
.references(() => users.id, { onDelete: 'cascade' })
|
||||||
|
.notNull(),
|
||||||
|
clientId: text('client_id'),
|
||||||
|
chatConfig: jsonb('chat_config').$type<LobeAgentChatConfig>(),
|
||||||
|
...timestamps,
|
||||||
|
},
|
||||||
|
(t) => [uniqueIndex('client_id_user_id_unique').on(t.clientId, t.userId)],
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
### Junction Tables (Many-to-Many)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export const agentsKnowledgeBases = pgTable(
|
||||||
|
'agents_knowledge_bases',
|
||||||
|
{
|
||||||
|
agentId: text('agent_id')
|
||||||
|
.references(() => agents.id, { onDelete: 'cascade' })
|
||||||
|
.notNull(),
|
||||||
|
knowledgeBaseId: text('knowledge_base_id')
|
||||||
|
.references(() => knowledgeBases.id, { onDelete: 'cascade' })
|
||||||
|
.notNull(),
|
||||||
|
userId: text('user_id')
|
||||||
|
.references(() => users.id, { onDelete: 'cascade' })
|
||||||
|
.notNull(),
|
||||||
|
enabled: boolean('enabled').default(true),
|
||||||
|
...timestamps,
|
||||||
|
},
|
||||||
|
(t) => [primaryKey({ columns: [t.agentId, t.knowledgeBaseId] })],
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Query Style
|
||||||
|
|
||||||
|
**Always use `db.select()` builder API. Never use `db.query.*` relational API** (`findMany`, `findFirst`, `with:`).
|
||||||
|
|
||||||
|
The relational API generates complex lateral joins with `json_build_array` that are fragile and hard to debug.
|
||||||
|
|
||||||
|
### Select Single Row
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good
|
||||||
|
const [result] = await this.db
|
||||||
|
.select()
|
||||||
|
.from(agents)
|
||||||
|
.where(eq(agents.id, id))
|
||||||
|
.limit(1);
|
||||||
|
return result;
|
||||||
|
|
||||||
|
// ❌ Bad: relational API
|
||||||
|
return this.db.query.agents.findFirst({
|
||||||
|
where: eq(agents.id, id),
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Select with JOIN
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good: explicit select + leftJoin
|
||||||
|
const rows = await this.db
|
||||||
|
.select({
|
||||||
|
runId: agentEvalRunTopics.runId,
|
||||||
|
score: agentEvalRunTopics.score,
|
||||||
|
testCase: agentEvalTestCases,
|
||||||
|
topic: topics,
|
||||||
|
})
|
||||||
|
.from(agentEvalRunTopics)
|
||||||
|
.leftJoin(agentEvalTestCases, eq(agentEvalRunTopics.testCaseId, agentEvalTestCases.id))
|
||||||
|
.leftJoin(topics, eq(agentEvalRunTopics.topicId, topics.id))
|
||||||
|
.where(eq(agentEvalRunTopics.runId, runId))
|
||||||
|
.orderBy(asc(agentEvalRunTopics.createdAt));
|
||||||
|
|
||||||
|
// ❌ Bad: relational API with `with:`
|
||||||
|
return this.db.query.agentEvalRunTopics.findMany({
|
||||||
|
where: eq(agentEvalRunTopics.runId, runId),
|
||||||
|
with: { testCase: true, topic: true },
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Select with Aggregation
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good: select + leftJoin + groupBy
|
||||||
|
const rows = await this.db
|
||||||
|
.select({
|
||||||
|
id: agentEvalDatasets.id,
|
||||||
|
name: agentEvalDatasets.name,
|
||||||
|
testCaseCount: count(agentEvalTestCases.id).as('testCaseCount'),
|
||||||
|
})
|
||||||
|
.from(agentEvalDatasets)
|
||||||
|
.leftJoin(agentEvalTestCases, eq(agentEvalDatasets.id, agentEvalTestCases.datasetId))
|
||||||
|
.groupBy(agentEvalDatasets.id);
|
||||||
|
```
|
||||||
|
|
||||||
|
### One-to-Many (Separate Queries)
|
||||||
|
|
||||||
|
When you need a parent record with its children, use two queries instead of relational `with:`:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good: two simple queries
|
||||||
|
const [dataset] = await this.db
|
||||||
|
.select()
|
||||||
|
.from(agentEvalDatasets)
|
||||||
|
.where(eq(agentEvalDatasets.id, id))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!dataset) return undefined;
|
||||||
|
|
||||||
|
const testCases = await this.db
|
||||||
|
.select()
|
||||||
|
.from(agentEvalTestCases)
|
||||||
|
.where(eq(agentEvalTestCases.datasetId, id))
|
||||||
|
.orderBy(asc(agentEvalTestCases.sortOrder));
|
||||||
|
|
||||||
|
return { ...dataset, testCases };
|
||||||
|
```
|
||||||
|
|
||||||
|
## Database Migrations
|
||||||
|
|
||||||
|
See the `db-migrations` skill for the detailed migration guide.
|
||||||
205
.kilocode/skills/drizzle/SKILL.md
Normal file
205
.kilocode/skills/drizzle/SKILL.md
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
---
|
||||||
|
name: drizzle
|
||||||
|
description: Drizzle ORM schema and database guide. Use when working with database schemas (src/database/schemas/*), defining tables, creating migrations, or database model code. Triggers on Drizzle schema definition, database migrations, or ORM usage questions.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Drizzle ORM Schema Style Guide
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
- Config: `drizzle.config.ts`
|
||||||
|
- Schemas: `src/database/schemas/`
|
||||||
|
- Migrations: `src/database/migrations/`
|
||||||
|
- Dialect: `postgresql` with `strict: true`
|
||||||
|
|
||||||
|
## Helper Functions
|
||||||
|
|
||||||
|
Location: `src/database/schemas/_helpers.ts`
|
||||||
|
|
||||||
|
- `timestamptz(name)`: Timestamp with timezone
|
||||||
|
- `createdAt()`, `updatedAt()`, `accessedAt()`: Standard timestamp columns
|
||||||
|
- `timestamps`: Object with all three for easy spread
|
||||||
|
|
||||||
|
## Naming Conventions
|
||||||
|
|
||||||
|
- **Tables**: Plural snake_case (`users`, `session_groups`)
|
||||||
|
- **Columns**: snake_case (`user_id`, `created_at`)
|
||||||
|
|
||||||
|
## Column Definitions
|
||||||
|
|
||||||
|
### Primary Keys
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
id: text('id')
|
||||||
|
.primaryKey()
|
||||||
|
.$defaultFn(() => idGenerator('agents'))
|
||||||
|
.notNull(),
|
||||||
|
```
|
||||||
|
|
||||||
|
ID prefixes make entity types distinguishable. For internal tables, use `uuid`.
|
||||||
|
|
||||||
|
### Foreign Keys
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
userId: text('user_id')
|
||||||
|
.references(() => users.id, { onDelete: 'cascade' })
|
||||||
|
.notNull(),
|
||||||
|
```
|
||||||
|
|
||||||
|
### Timestamps
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
...timestamps, // Spread from _helpers.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
### Indexes
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Return array (object style deprecated)
|
||||||
|
(t) => [uniqueIndex('client_id_user_id_unique').on(t.clientId, t.userId)],
|
||||||
|
```
|
||||||
|
|
||||||
|
## Type Inference
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export const insertAgentSchema = createInsertSchema(agents);
|
||||||
|
export type NewAgent = typeof agents.$inferInsert;
|
||||||
|
export type AgentItem = typeof agents.$inferSelect;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Pattern
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export const agents = pgTable(
|
||||||
|
'agents',
|
||||||
|
{
|
||||||
|
id: text('id')
|
||||||
|
.primaryKey()
|
||||||
|
.$defaultFn(() => idGenerator('agents'))
|
||||||
|
.notNull(),
|
||||||
|
slug: varchar('slug', { length: 100 })
|
||||||
|
.$defaultFn(() => randomSlug(4))
|
||||||
|
.unique(),
|
||||||
|
userId: text('user_id')
|
||||||
|
.references(() => users.id, { onDelete: 'cascade' })
|
||||||
|
.notNull(),
|
||||||
|
clientId: text('client_id'),
|
||||||
|
chatConfig: jsonb('chat_config').$type<LobeAgentChatConfig>(),
|
||||||
|
...timestamps,
|
||||||
|
},
|
||||||
|
(t) => [uniqueIndex('client_id_user_id_unique').on(t.clientId, t.userId)],
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
### Junction Tables (Many-to-Many)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export const agentsKnowledgeBases = pgTable(
|
||||||
|
'agents_knowledge_bases',
|
||||||
|
{
|
||||||
|
agentId: text('agent_id')
|
||||||
|
.references(() => agents.id, { onDelete: 'cascade' })
|
||||||
|
.notNull(),
|
||||||
|
knowledgeBaseId: text('knowledge_base_id')
|
||||||
|
.references(() => knowledgeBases.id, { onDelete: 'cascade' })
|
||||||
|
.notNull(),
|
||||||
|
userId: text('user_id')
|
||||||
|
.references(() => users.id, { onDelete: 'cascade' })
|
||||||
|
.notNull(),
|
||||||
|
enabled: boolean('enabled').default(true),
|
||||||
|
...timestamps,
|
||||||
|
},
|
||||||
|
(t) => [primaryKey({ columns: [t.agentId, t.knowledgeBaseId] })],
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Query Style
|
||||||
|
|
||||||
|
**Always use `db.select()` builder API. Never use `db.query.*` relational API** (`findMany`, `findFirst`, `with:`).
|
||||||
|
|
||||||
|
The relational API generates complex lateral joins with `json_build_array` that are fragile and hard to debug.
|
||||||
|
|
||||||
|
### Select Single Row
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good
|
||||||
|
const [result] = await this.db
|
||||||
|
.select()
|
||||||
|
.from(agents)
|
||||||
|
.where(eq(agents.id, id))
|
||||||
|
.limit(1);
|
||||||
|
return result;
|
||||||
|
|
||||||
|
// ❌ Bad: relational API
|
||||||
|
return this.db.query.agents.findFirst({
|
||||||
|
where: eq(agents.id, id),
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Select with JOIN
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good: explicit select + leftJoin
|
||||||
|
const rows = await this.db
|
||||||
|
.select({
|
||||||
|
runId: agentEvalRunTopics.runId,
|
||||||
|
score: agentEvalRunTopics.score,
|
||||||
|
testCase: agentEvalTestCases,
|
||||||
|
topic: topics,
|
||||||
|
})
|
||||||
|
.from(agentEvalRunTopics)
|
||||||
|
.leftJoin(agentEvalTestCases, eq(agentEvalRunTopics.testCaseId, agentEvalTestCases.id))
|
||||||
|
.leftJoin(topics, eq(agentEvalRunTopics.topicId, topics.id))
|
||||||
|
.where(eq(agentEvalRunTopics.runId, runId))
|
||||||
|
.orderBy(asc(agentEvalRunTopics.createdAt));
|
||||||
|
|
||||||
|
// ❌ Bad: relational API with `with:`
|
||||||
|
return this.db.query.agentEvalRunTopics.findMany({
|
||||||
|
where: eq(agentEvalRunTopics.runId, runId),
|
||||||
|
with: { testCase: true, topic: true },
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Select with Aggregation
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good: select + leftJoin + groupBy
|
||||||
|
const rows = await this.db
|
||||||
|
.select({
|
||||||
|
id: agentEvalDatasets.id,
|
||||||
|
name: agentEvalDatasets.name,
|
||||||
|
testCaseCount: count(agentEvalTestCases.id).as('testCaseCount'),
|
||||||
|
})
|
||||||
|
.from(agentEvalDatasets)
|
||||||
|
.leftJoin(agentEvalTestCases, eq(agentEvalDatasets.id, agentEvalTestCases.datasetId))
|
||||||
|
.groupBy(agentEvalDatasets.id);
|
||||||
|
```
|
||||||
|
|
||||||
|
### One-to-Many (Separate Queries)
|
||||||
|
|
||||||
|
When you need a parent record with its children, use two queries instead of relational `with:`:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Good: two simple queries
|
||||||
|
const [dataset] = await this.db
|
||||||
|
.select()
|
||||||
|
.from(agentEvalDatasets)
|
||||||
|
.where(eq(agentEvalDatasets.id, id))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!dataset) return undefined;
|
||||||
|
|
||||||
|
const testCases = await this.db
|
||||||
|
.select()
|
||||||
|
.from(agentEvalTestCases)
|
||||||
|
.where(eq(agentEvalTestCases.datasetId, id))
|
||||||
|
.orderBy(asc(agentEvalTestCases.sortOrder));
|
||||||
|
|
||||||
|
return { ...dataset, testCases };
|
||||||
|
```
|
||||||
|
|
||||||
|
## Database Migrations
|
||||||
|
|
||||||
|
See the `db-migrations` skill for the detailed migration guide.
|
||||||
25
components.json.bak
Normal file
25
components.json.bak
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://ui.shadcn.com/schema.json",
|
||||||
|
"style": "radix-mira",
|
||||||
|
"rsc": true,
|
||||||
|
"tsx": true,
|
||||||
|
"tailwind": {
|
||||||
|
"config": "",
|
||||||
|
"css": "src/app/globals.css",
|
||||||
|
"baseColor": "neutral",
|
||||||
|
"cssVariables": true,
|
||||||
|
"prefix": ""
|
||||||
|
},
|
||||||
|
"iconLibrary": "hugeicons",
|
||||||
|
"rtl": false,
|
||||||
|
"aliases": {
|
||||||
|
"components": "@/components",
|
||||||
|
"utils": "@/lib/utils",
|
||||||
|
"ui": "@/components/ui",
|
||||||
|
"lib": "@/lib",
|
||||||
|
"hooks": "@/hooks"
|
||||||
|
},
|
||||||
|
"menuColor": "default",
|
||||||
|
"menuAccent": "subtle",
|
||||||
|
"registries": {}
|
||||||
|
}
|
||||||
2585
package-lock.json
generated
2585
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,9 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hugeicons/core-free-icons": "^4.1.1",
|
"@hugeicons/core-free-icons": "^4.1.1",
|
||||||
"@hugeicons/react": "^1.1.6",
|
"@hugeicons/react": "^1.1.6",
|
||||||
|
"@sentry/nextjs": "^10.49.0",
|
||||||
|
"@tabler/icons-react": "^3.41.1",
|
||||||
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
@@ -22,7 +25,7 @@
|
|||||||
"radix-ui": "^1.4.3",
|
"radix-ui": "^1.4.3",
|
||||||
"react": "19.2.4",
|
"react": "19.2.4",
|
||||||
"react-dom": "19.2.4",
|
"react-dom": "19.2.4",
|
||||||
"shadcn": "^4.2.0",
|
"shadcn": "^4.3.0",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.5.0",
|
"tailwind-merge": "^3.5.0",
|
||||||
"tw-animate-css": "^1.4.0",
|
"tw-animate-css": "^1.4.0",
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
{
|
{
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"skills": {
|
"skills": {
|
||||||
|
"drizzle": {
|
||||||
|
"source": "lobehub/lobehub",
|
||||||
|
"sourceType": "github",
|
||||||
|
"computedHash": "5cfce7f940d8d52863a0206a0afe7c5b5f04d610c1eac2b01274938abbddcd23"
|
||||||
|
},
|
||||||
"elysiajs": {
|
"elysiajs": {
|
||||||
"source": "elysiajs/skills",
|
"source": "elysiajs/skills",
|
||||||
"sourceType": "github",
|
"sourceType": "github",
|
||||||
|
|||||||
@@ -6,10 +6,12 @@
|
|||||||
|
|
||||||
@import './theme.css';
|
@import './theme.css';
|
||||||
|
|
||||||
|
@import "tw-animate-css";
|
||||||
|
|
||||||
|
@import "shadcn/tailwind.css";
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--radius: 0.625rem;
|
--radius: 0.625rem;
|
||||||
--background: oklch(1 0 0);
|
|
||||||
--foreground: oklch(0.145 0 0);
|
|
||||||
--card: oklch(1 0 0);
|
--card: oklch(1 0 0);
|
||||||
--card-foreground: oklch(0.145 0 0);
|
--card-foreground: oklch(0.145 0 0);
|
||||||
--popover: oklch(1 0 0);
|
--popover: oklch(1 0 0);
|
||||||
@@ -26,11 +28,11 @@
|
|||||||
--border: oklch(0.922 0 0);
|
--border: oklch(0.922 0 0);
|
||||||
--input: oklch(0.922 0 0);
|
--input: oklch(0.922 0 0);
|
||||||
--ring: oklch(0.708 0 0);
|
--ring: oklch(0.708 0 0);
|
||||||
--chart-1: oklch(0.646 0.222 41.116);
|
--chart-1: oklch(0.87 0 0);
|
||||||
--chart-2: oklch(0.6 0.118 184.704);
|
--chart-2: oklch(0.556 0 0);
|
||||||
--chart-3: oklch(0.398 0.07 227.392);
|
--chart-3: oklch(0.439 0 0);
|
||||||
--chart-4: oklch(0.828 0.189 84.429);
|
--chart-4: oklch(0.371 0 0);
|
||||||
--chart-5: oklch(0.769 0.188 70.08);
|
--chart-5: oklch(0.269 0 0);
|
||||||
--sidebar: oklch(0.985 0 0);
|
--sidebar: oklch(0.985 0 0);
|
||||||
--sidebar-foreground: oklch(0.145 0 0);
|
--sidebar-foreground: oklch(0.145 0 0);
|
||||||
--sidebar-primary: oklch(0.205 0 0);
|
--sidebar-primary: oklch(0.205 0 0);
|
||||||
@@ -39,6 +41,8 @@
|
|||||||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||||
--sidebar-border: oklch(0.922 0 0);
|
--sidebar-border: oklch(0.922 0 0);
|
||||||
--sidebar-ring: oklch(0.708 0 0);
|
--sidebar-ring: oklch(0.708 0 0);
|
||||||
|
--background: oklch(1 0 0);
|
||||||
|
--foreground: oklch(0.145 0 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
@@ -46,7 +50,7 @@
|
|||||||
--foreground: oklch(0.985 0 0);
|
--foreground: oklch(0.985 0 0);
|
||||||
--card: oklch(0.205 0 0);
|
--card: oklch(0.205 0 0);
|
||||||
--card-foreground: oklch(0.985 0 0);
|
--card-foreground: oklch(0.985 0 0);
|
||||||
--popover: oklch(0.269 0 0);
|
--popover: oklch(0.205 0 0);
|
||||||
--popover-foreground: oklch(0.985 0 0);
|
--popover-foreground: oklch(0.985 0 0);
|
||||||
--primary: oklch(0.922 0 0);
|
--primary: oklch(0.922 0 0);
|
||||||
--primary-foreground: oklch(0.205 0 0);
|
--primary-foreground: oklch(0.205 0 0);
|
||||||
@@ -54,17 +58,17 @@
|
|||||||
--secondary-foreground: oklch(0.985 0 0);
|
--secondary-foreground: oklch(0.985 0 0);
|
||||||
--muted: oklch(0.269 0 0);
|
--muted: oklch(0.269 0 0);
|
||||||
--muted-foreground: oklch(0.708 0 0);
|
--muted-foreground: oklch(0.708 0 0);
|
||||||
--accent: oklch(0.371 0 0);
|
--accent: oklch(0.269 0 0);
|
||||||
--accent-foreground: oklch(0.985 0 0);
|
--accent-foreground: oklch(0.985 0 0);
|
||||||
--destructive: oklch(0.704 0.191 22.216);
|
--destructive: oklch(0.704 0.191 22.216);
|
||||||
--border: oklch(1 0 0 / 10%);
|
--border: oklch(1 0 0 / 10%);
|
||||||
--input: oklch(1 0 0 / 15%);
|
--input: oklch(1 0 0 / 15%);
|
||||||
--ring: oklch(0.556 0 0);
|
--ring: oklch(0.556 0 0);
|
||||||
--chart-1: oklch(0.488 0.243 264.376);
|
--chart-1: oklch(0.87 0 0);
|
||||||
--chart-2: oklch(0.696 0.17 162.48);
|
--chart-2: oklch(0.556 0 0);
|
||||||
--chart-3: oklch(0.769 0.188 70.08);
|
--chart-3: oklch(0.439 0 0);
|
||||||
--chart-4: oklch(0.627 0.265 303.9);
|
--chart-4: oklch(0.371 0 0);
|
||||||
--chart-5: oklch(0.645 0.246 16.439);
|
--chart-5: oklch(0.269 0 0);
|
||||||
--sidebar: oklch(0.205 0 0);
|
--sidebar: oklch(0.205 0 0);
|
||||||
--sidebar-foreground: oklch(0.985 0 0);
|
--sidebar-foreground: oklch(0.985 0 0);
|
||||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||||
@@ -72,7 +76,7 @@
|
|||||||
--sidebar-accent: oklch(0.269 0 0);
|
--sidebar-accent: oklch(0.269 0 0);
|
||||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||||
--sidebar-border: oklch(1 0 0 / 10%);
|
--sidebar-border: oklch(1 0 0 / 10%);
|
||||||
--sidebar-ring: oklch(0.439 0 0);
|
--sidebar-ring: oklch(0.556 0 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
@@ -111,6 +115,11 @@
|
|||||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||||
--color-sidebar-border: var(--sidebar-border);
|
--color-sidebar-border: var(--sidebar-border);
|
||||||
--color-sidebar-ring: var(--sidebar-ring);
|
--color-sidebar-ring: var(--sidebar-ring);
|
||||||
|
--font-heading: var(--font-sans);
|
||||||
|
--font-sans: var(--font-sans);
|
||||||
|
--radius-2xl: calc(var(--radius) * 1.8);
|
||||||
|
--radius-3xl: calc(var(--radius) * 2.2);
|
||||||
|
--radius-4xl: calc(var(--radius) * 2.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
@@ -120,6 +129,9 @@
|
|||||||
body {
|
body {
|
||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground;
|
||||||
}
|
}
|
||||||
|
html {
|
||||||
|
@apply font-sans;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* View Transition Wave Effect */
|
/* View Transition Wave Effect */
|
||||||
@@ -155,4 +167,4 @@
|
|||||||
::view-transition-new(root) {
|
::view-transition-new(root) {
|
||||||
/* Apply the reveal animation */
|
/* Apply the reveal animation */
|
||||||
animation: reveal 0.4s ease-in-out forwards;
|
animation: reveal 0.4s ease-in-out forwards;
|
||||||
}
|
}
|
||||||
@@ -9,6 +9,9 @@ import NextTopLoader from 'nextjs-toploader';
|
|||||||
import { NuqsAdapter } from 'nuqs/adapters/next/app';
|
import { NuqsAdapter } from 'nuqs/adapters/next/app';
|
||||||
import './globals.css';
|
import './globals.css';
|
||||||
import './theme.css';
|
import './theme.css';
|
||||||
|
import { Inter } from "next/font/google";
|
||||||
|
|
||||||
|
const inter = Inter({subsets:['latin'],variable:'--font-sans'});
|
||||||
|
|
||||||
const META_THEME_COLORS = {
|
const META_THEME_COLORS = {
|
||||||
light: '#ffffff',
|
light: '#ffffff',
|
||||||
@@ -34,7 +37,7 @@ export default async function RootLayout({
|
|||||||
const isScaled = activeThemeValue?.endsWith('-scaled');
|
const isScaled = activeThemeValue?.endsWith('-scaled');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<html lang='en' suppressHydrationWarning>
|
<html lang='en' suppressHydrationWarning className={cn("font-sans", inter.variable)}>
|
||||||
<head>
|
<head>
|
||||||
<script
|
<script
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
|
|||||||
@@ -1,26 +1,6 @@
|
|||||||
import { type ClassValue, clsx } from "clsx";
|
import { clsx, type ClassValue } from "clsx"
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge"
|
||||||
|
|
||||||
export function cn(...inputs: ClassValue[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
return twMerge(clsx(inputs));
|
return twMerge(clsx(inputs))
|
||||||
}
|
|
||||||
|
|
||||||
export function formatBytes(
|
|
||||||
bytes: number,
|
|
||||||
opts: {
|
|
||||||
decimals?: number;
|
|
||||||
sizeType?: "accurate" | "normal";
|
|
||||||
} = {},
|
|
||||||
) {
|
|
||||||
const { decimals = 0, sizeType = "normal" } = opts;
|
|
||||||
|
|
||||||
const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
|
|
||||||
const accurateSizes = ["Bytes", "KiB", "MiB", "GiB", "TiB"];
|
|
||||||
if (bytes === 0) return "0 Byte";
|
|
||||||
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
||||||
return `${(bytes / Math.pow(1024, i)).toFixed(decimals)} ${
|
|
||||||
sizeType === "accurate"
|
|
||||||
? (accurateSizes[i] ?? "Bytest")
|
|
||||||
: (sizes[i] ?? "Bytes")
|
|
||||||
}`;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { DataTableConfig } from '@/config/data-table';
|
import type { DataTableConfig } from "@/config/data-table";
|
||||||
import type { FilterItemSchema } from '@/lib/parsers';
|
import type { FilterItemSchema } from "@/lib/parsers";
|
||||||
import type { ColumnSort, Row, RowData } from '@tanstack/react-table';
|
import type { ColumnSort, Row, RowData } from "@tanstack/react-table";
|
||||||
|
|
||||||
declare module '@tanstack/react-table' {
|
declare module "@tanstack/react-table" {
|
||||||
// biome-ignore lint/correctness/noUnusedVariables: Interface type parameters required by @tanstack/react-table
|
// biome-ignore lint/correctness/noUnusedVariables: Interface type parameters required by @tanstack/react-table
|
||||||
interface ColumnMeta<TData extends RowData, TValue> {
|
interface ColumnMeta<TData extends RowData, TValue> {
|
||||||
label?: string;
|
label?: string;
|
||||||
@@ -22,11 +22,11 @@ export interface Option {
|
|||||||
icon?: React.FC<React.SVGProps<SVGSVGElement>>;
|
icon?: React.FC<React.SVGProps<SVGSVGElement>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FilterOperator = DataTableConfig['operators'][number];
|
export type FilterOperator = DataTableConfig["operators"][number];
|
||||||
export type FilterVariant = DataTableConfig['filterVariants'][number];
|
export type FilterVariant = DataTableConfig["filterVariants"][number];
|
||||||
export type JoinOperator = DataTableConfig['joinOperators'][number];
|
export type JoinOperator = DataTableConfig["joinOperators"][number];
|
||||||
|
|
||||||
export interface ExtendedColumnSort<TData> extends Omit<ColumnSort, 'id'> {
|
export interface ExtendedColumnSort<TData> extends Omit<ColumnSort, "id"> {
|
||||||
id: Extract<keyof TData, string>;
|
id: Extract<keyof TData, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,5 +36,5 @@ export interface ExtendedColumnFilter<TData> extends FilterItemSchema {
|
|||||||
|
|
||||||
export interface DataTableRowAction<TData> {
|
export interface DataTableRowAction<TData> {
|
||||||
row: Row<TData>;
|
row: Row<TData>;
|
||||||
variant: 'update' | 'delete';
|
variant: "update" | "delete";
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user