feat: add reminders page, BMad skills upgrade, MCP server refactor

- Add reminders page with navigation support
- Upgrade BMad builder module to skills-based architecture
- Refactor MCP server: extract tools and auth into separate modules
- Add connections cache, custom AI provider support
- Update prisma schema and generated client
- Various UI/UX improvements and i18n updates
- Add service worker for PWA support

Made-with: Cursor
This commit is contained in:
Sepehr Ramezani
2026-04-13 21:02:53 +02:00
parent 18ed116e0d
commit fa7e166f3e
3099 changed files with 397228 additions and 14584 deletions

View File

@@ -1,11 +1,55 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
import { LABEL_COLORS, LabelColorName, QueryType } from "./types"
import { LABEL_COLORS, LabelColorName, QueryType, Note } from "./types"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
/**
* Deep equality check for two values
* More performant than JSON.stringify for comparison
*/
export function deepEqual(a: unknown, b: unknown): boolean {
if (a === b) return true
if (a === null || b === null) return a === b
if (typeof a !== typeof b) return false
if (Array.isArray(a) && Array.isArray(b)) {
if (a.length !== b.length) return false
return a.every((item, index) => deepEqual(item, b[index]))
}
if (typeof a === 'object' && typeof b === 'object') {
const keysA = Object.keys(a as Record<string, unknown>)
const keysB = Object.keys(b as Record<string, unknown>)
if (keysA.length !== keysB.length) return false
return keysA.every(key => deepEqual(
(a as Record<string, unknown>)[key],
(b as Record<string, unknown>)[key]
))
}
return false
}
/**
* Parse a database note object into a typed Note
* Handles JSON string fields that are stored in the database
*/
export function parseNote(dbNote: any): Note {
return {
...dbNote,
checkItems: dbNote.checkItems ? JSON.parse(dbNote.checkItems) : null,
labels: dbNote.labels ? JSON.parse(dbNote.labels) : null,
images: dbNote.images ? JSON.parse(dbNote.images) : null,
links: dbNote.links ? JSON.parse(dbNote.links) : null,
embedding: dbNote.embedding ? JSON.parse(dbNote.embedding) : null,
sharedWith: dbNote.sharedWith ? JSON.parse(dbNote.sharedWith) : [],
size: dbNote.size || 'small',
}
}
export function getHashColor(name: string): LabelColorName {
let hash = 0;
for (let i = 0; i < name.length; i++) {