feat: 8 AI providers, rich text editor, agent notifications, UI contrast & font settings
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 1m25s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 1m25s
- Add DeepSeek, OpenRouter, Mistral, Z.AI, LM Studio as AI providers with editable model names via Combobox in admin settings - Fix OpenRouter broken by normalizeProvider bug in config.ts - Convert agent-created notes from Markdown to HTML (TipTap rich text) - Add Notification model + in-app notifications for agent results - Agent notification click opens the created note directly - Add note count display on notebook and inbox headers - Fix checklist toggle in card view (persist state via localCheckItems) - Add checklist creation option in tabs/list view (dropdown on + button) - Fix image description ENOENT error with HTTP fallback - Improve UI contrast across all themes (input, border, checkbox visibility) - Add font family setting (Inter vs System Default) in Appearance settings - Fix CSS font-sans variable conflict (removed dead Geist references) - Update README with new features and 8 providers Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,6 @@
|
||||
import prisma from '@/lib/prisma'
|
||||
import { auth } from '@/auth'
|
||||
import { sendEmail } from '@/lib/mail'
|
||||
import { updateTag } from 'next/cache'
|
||||
|
||||
async function checkAdmin() {
|
||||
const session = await auth()
|
||||
@@ -62,9 +61,6 @@ export async function updateSystemConfig(data: Record<string, string>) {
|
||||
|
||||
await prisma.$transaction(operations)
|
||||
|
||||
// Invalidate cache after update
|
||||
updateTag('system-config')
|
||||
|
||||
return { success: true }
|
||||
} catch (error) {
|
||||
console.error('Failed to update settings:', error)
|
||||
|
||||
@@ -23,6 +23,7 @@ export type UserAISettingsData = {
|
||||
autoLabeling?: boolean
|
||||
noteHistory?: boolean
|
||||
noteHistoryMode?: 'manual' | 'auto'
|
||||
fontFamily?: 'inter' | 'system'
|
||||
}
|
||||
|
||||
/** Only fields that exist on `UserAISettings` in Prisma (excludes e.g. `theme`, which lives on `User`). */
|
||||
@@ -45,6 +46,7 @@ const USER_AI_SETTINGS_PRISMA_KEYS = [
|
||||
'autoLabeling',
|
||||
'noteHistory',
|
||||
'noteHistoryMode',
|
||||
'fontFamily',
|
||||
] as const
|
||||
|
||||
type UserAISettingsPrismaKey = (typeof USER_AI_SETTINGS_PRISMA_KEYS)[number]
|
||||
@@ -157,6 +159,7 @@ const getCachedAISettings = unstable_cache(
|
||||
autoLabeling: true,
|
||||
noteHistory: false,
|
||||
noteHistoryMode: 'manual' as const,
|
||||
fontFamily: 'inter' as const,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,6 +191,7 @@ const getCachedAISettings = unstable_cache(
|
||||
autoLabeling: settings.autoLabeling ?? true,
|
||||
noteHistory: settings.noteHistory ?? false,
|
||||
noteHistoryMode: (settings.noteHistoryMode ?? 'manual') as 'manual' | 'auto',
|
||||
fontFamily: (settings.fontFamily || 'inter') as 'inter' | 'system',
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error getting AI settings:', error)
|
||||
@@ -212,6 +216,7 @@ const getCachedAISettings = unstable_cache(
|
||||
autoLabeling: true,
|
||||
noteHistory: false,
|
||||
noteHistoryMode: 'manual' as const,
|
||||
fontFamily: 'inter' as const,
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -249,6 +254,7 @@ export async function getAISettings(userId?: string) {
|
||||
autoLabeling: true,
|
||||
noteHistory: false,
|
||||
noteHistoryMode: 'manual' as const,
|
||||
fontFamily: 'inter' as const,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use server'
|
||||
|
||||
import { detectUserLanguage } from '@/lib/i18n/detect-user-language'
|
||||
import { headers } from 'next/headers'
|
||||
import { detectUserLanguage, parseAcceptLanguage } from '@/lib/i18n/detect-user-language'
|
||||
import { SupportedLanguage } from '@/lib/i18n/load-translations'
|
||||
|
||||
/**
|
||||
@@ -8,5 +9,11 @@ import { SupportedLanguage } from '@/lib/i18n/load-translations'
|
||||
* Called on app load to set initial language
|
||||
*/
|
||||
export async function getInitialLanguage(): Promise<SupportedLanguage> {
|
||||
return await detectUserLanguage()
|
||||
try {
|
||||
const headersList = await headers()
|
||||
const browserLang = parseAcceptLanguage(headersList.get('accept-language'))
|
||||
return await detectUserLanguage(browserLang)
|
||||
} catch {
|
||||
return await detectUserLanguage()
|
||||
}
|
||||
}
|
||||
|
||||
76
memento-note/app/actions/notifications.ts
Normal file
76
memento-note/app/actions/notifications.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
'use server'
|
||||
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { auth } from '@/auth'
|
||||
|
||||
export interface AppNotification {
|
||||
id: string
|
||||
type: string
|
||||
title: string
|
||||
message: string | null
|
||||
read: boolean
|
||||
actionUrl: string | null
|
||||
relatedId: string | null
|
||||
createdAt: Date
|
||||
}
|
||||
|
||||
export async function getUnreadNotifications(): Promise<AppNotification[]> {
|
||||
const session = await auth()
|
||||
if (!session?.user?.id) return []
|
||||
|
||||
try {
|
||||
const notifications = await prisma.notification.findMany({
|
||||
where: { userId: session.user.id, read: false },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
take: 20,
|
||||
})
|
||||
return notifications
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export async function markNotificationRead(id: string): Promise<void> {
|
||||
const session = await auth()
|
||||
if (!session?.user?.id) return
|
||||
|
||||
await prisma.notification.updateMany({
|
||||
where: { id, userId: session.user.id },
|
||||
data: { read: true },
|
||||
})
|
||||
}
|
||||
|
||||
export async function markAllNotificationsRead(): Promise<void> {
|
||||
const session = await auth()
|
||||
if (!session?.user?.id) return
|
||||
|
||||
await prisma.notification.updateMany({
|
||||
where: { userId: session.user.id, read: false },
|
||||
data: { read: true },
|
||||
})
|
||||
}
|
||||
|
||||
/** Create a notification (called from server-side code, not exposed to client) */
|
||||
export async function createNotification(data: {
|
||||
userId: string
|
||||
type: string
|
||||
title: string
|
||||
message?: string
|
||||
actionUrl?: string
|
||||
relatedId?: string
|
||||
}): Promise<void> {
|
||||
try {
|
||||
await prisma.notification.create({
|
||||
data: {
|
||||
userId: data.userId,
|
||||
type: data.type,
|
||||
title: data.title,
|
||||
message: data.message || null,
|
||||
actionUrl: data.actionUrl || null,
|
||||
relatedId: data.relatedId || null,
|
||||
},
|
||||
})
|
||||
} catch (e) {
|
||||
console.error('[Notification] Failed to create:', e)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user