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

- 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:
Sepehr Ramezani
2026-05-01 16:14:07 +02:00
parent 1345403a31
commit dbd49d6fcb
64 changed files with 4124 additions and 1392 deletions

View File

@@ -19,7 +19,7 @@ import {
useSortable,
} from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities'
import { Note, NOTE_COLORS, NoteColor } from '@/lib/types'
import { Note, NOTE_COLORS, NoteColor, NoteType } from '@/lib/types'
import { cn } from '@/lib/utils'
import { NoteInlineEditor } from '@/components/note-inline-editor'
import { useLanguage } from '@/lib/i18n'
@@ -65,6 +65,7 @@ import {
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
@@ -737,11 +738,13 @@ export function NotesTabsView({
const selected = items.find((n) => n.id === selectedId) ?? null
const colorKey = selected ? getColorKey(selected) : 'default'
const handleCreateNote = () => {
const handleCreateNote = (noteType: NoteType = 'richtext') => {
startCreating(async () => {
try {
const newNote = await createNote({
content: '',
content: noteType === 'checklist' ? '' : '',
type: noteType,
checkItems: noteType === 'checklist' ? [{ id: Date.now().toString(), text: '', checked: false }] : undefined,
title: undefined,
notebookId: currentNotebookId || undefined,
skipRevalidation: true
@@ -843,19 +846,32 @@ export function NotesTabsView({
</DropdownMenuContent>
</DropdownMenu>
{/* New note button */}
<Button
variant="ghost"
size="sm"
className="h-7 w-7 p-0 text-muted-foreground/70 hover:bg-primary/8 hover:text-primary"
onClick={handleCreateNote}
disabled={isCreating}
title={t('notes.newNote')}
>
{isCreating
? <Loader2 className="h-3.5 w-3.5 animate-spin" />
: <Plus className="h-4 w-4" />}
</Button>
{/* New note button — dropdown to choose type */}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="sm"
className="h-7 w-7 p-0 text-muted-foreground/70 hover:bg-primary/8 hover:text-primary"
disabled={isCreating}
title={t('notes.newNote')}
>
{isCreating
? <Loader2 className="h-3.5 w-3.5 animate-spin" />
: <Plus className="h-4 w-4" />}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="min-w-[160px]">
<DropdownMenuItem onClick={() => handleCreateNote('richtext')}>
<FileText className="h-4 w-4 mr-2" />
{t('notes.newNote') || 'Note'}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleCreateNote('checklist')}>
<ListChecks className="h-4 w-4 mr-2" />
{t('notes.newChecklist') || 'Checklist'}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>