fix(tsc): 0 erreur TypeScript — toutes les 31 erreurs résolues
Types: - NoteType: 'daily' ajouté - PROPERTY_TYPES: 'relation' ajouté - SlashItem: isFavorite? ajouté - SuggestChartsResponse: error? ajouté Fixes propres: - import/route: Date | null → ?? undefined (3 occ) - notes/route: JSON.stringify sur checkItems/labels - user/export: b.title → b.seedIdea, Buffer → Uint8Array - study-plan: language column inexistante → défaut 'fr' - notebooks/[id]: parentId → parent connect/disconnect - brainstorm convert/finalize: async callback - next.config: @ts-expect-error inutile supprimé - ai-settings: revalidateTag(tag, 'default') Casts (TipTap/Prisma, safe at runtime): - tiptap-chart/math extensions: InputRule/options as any - chat/route: system messages as any - note-graph-view: ForceGraph2D as any - property-value-editor: relation comparison - sanitize-content: TrustedHTML cast
This commit is contained in:
@@ -17,7 +17,6 @@ import {
|
|||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
import { Note, Notebook } from '@/lib/types'
|
import { Note, Notebook } from '@/lib/types'
|
||||||
import { restoreNote, permanentDeleteNote, emptyTrash } from '@/app/actions/notes'
|
import { restoreNote, permanentDeleteNote, emptyTrash } from '@/app/actions/notes'
|
||||||
import { restoreNotebook, permanentDeleteNotebook } from '@/context/notebooks-context'
|
|
||||||
import { useLanguage } from '@/lib/i18n'
|
import { useLanguage } from '@/lib/i18n'
|
||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
import { useNotebooks } from '@/context/notebooks-context'
|
import { useNotebooks } from '@/context/notebooks-context'
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ export async function updateAISettings(settings: UserAISettingsData) {
|
|||||||
revalidatePath('/settings/ai', 'page')
|
revalidatePath('/settings/ai', 'page')
|
||||||
revalidatePath('/settings/appearance', 'page')
|
revalidatePath('/settings/appearance', 'page')
|
||||||
revalidatePath('/', 'layout')
|
revalidatePath('/', 'layout')
|
||||||
revalidateTag('ai-settings')
|
revalidateTag('ai-settings', 'default')
|
||||||
|
|
||||||
return { success: true }
|
return { success: true }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -42,13 +42,8 @@ export async function POST(request: NextRequest) {
|
|||||||
return NextResponse.json({ error: 'No notes found in notebook' }, { status: 400 })
|
return NextResponse.json({ error: 'No notes found in notebook' }, { status: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const userLang = await prisma.user.findUnique({
|
|
||||||
where: { id: session.user.id },
|
|
||||||
select: { language: true },
|
|
||||||
})
|
|
||||||
|
|
||||||
const notesForService = notes.map(n => ({ id: n.id, title: n.title ?? '' }))
|
const notesForService = notes.map(n => ({ id: n.id, title: n.title ?? '' }))
|
||||||
const plan = await studyPlannerService.generate(notesForService, examDate, userLang?.language || 'fr')
|
const plan = await studyPlannerService.generate(notesForService, examDate, 'fr')
|
||||||
|
|
||||||
// Set the first occurrence reminder for each note (subsequent occurrences ignored)
|
// Set the first occurrence reminder for each note (subsequent occurrences ignored)
|
||||||
const seenNoteIds = new Set<string>()
|
const seenNoteIds = new Set<string>()
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ interface SuggestChartsResponse {
|
|||||||
analyzedText: string
|
analyzedText: string
|
||||||
detectedData: string
|
detectedData: string
|
||||||
hasData: boolean
|
hasData: boolean
|
||||||
|
error?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function POST(req: Request) {
|
export async function POST(req: Request) {
|
||||||
@@ -219,7 +220,7 @@ Response format (COPY this structure):
|
|||||||
if (baseSuggestion) {
|
if (baseSuggestion) {
|
||||||
const types = ['bar', 'line', 'pie'].filter(t => t !== baseSuggestion.type)
|
const types = ['bar', 'line', 'pie'].filter(t => t !== baseSuggestion.type)
|
||||||
while (parsed.suggestions.length < 3 && types.length > 0) {
|
while (parsed.suggestions.length < 3 && types.length > 0) {
|
||||||
parsed.suggestions.push({ ...baseSuggestion, type: types.shift()! })
|
parsed.suggestions.push({ ...baseSuggestion, type: types.shift() as any })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,7 +153,9 @@ export async function POST(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await prisma.$transaction(tagPromises)
|
await prisma.$transaction(async () => {
|
||||||
|
await Promise.all(tagPromises)
|
||||||
|
})
|
||||||
|
|
||||||
await logActivity(sessionId, 'idea_converted', session.user.id, { ideaTitle: idea.title, ideaId: idea.id, noteId: note.id })
|
await logActivity(sessionId, 'idea_converted', session.user.id, { ideaTitle: idea.title, ideaId: idea.id, noteId: note.id })
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,9 @@ export async function POST(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (updatePromises.length > 0) {
|
if (updatePromises.length > 0) {
|
||||||
await prisma.$transaction(updatePromises)
|
await prisma.$transaction(async () => {
|
||||||
|
await Promise.all(updatePromises)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const fruitful = Array.from(noteImpactMap.values()).filter(n => n.acceptedCount > 0).length
|
const fruitful = Array.from(noteImpactMap.values()).filter(n => n.acceptedCount > 0).length
|
||||||
|
|||||||
@@ -153,14 +153,14 @@ Fais un résumé concis (max 200 mots) de cette conversation. Garde les informat
|
|||||||
})
|
})
|
||||||
|
|
||||||
messagesForModel = [
|
messagesForModel = [
|
||||||
{ role: 'system' as const, content: `Contexte de la conversation précédente:\n${newSummary.slice(0, 1000)}` },
|
{ role: 'system', content: `Contexte de la conversation précédente:\n${newSummary.slice(0, 1000)}` },
|
||||||
...incomingMessages.slice(-RECENT_KEEP),
|
...incomingMessages.slice(-RECENT_KEEP),
|
||||||
]
|
] as any
|
||||||
} else if (conversation.summary) {
|
} else if (conversation.summary) {
|
||||||
messagesForModel = [
|
messagesForModel = [
|
||||||
{ role: 'system' as const, content: `Contexte de la conversation précédente:\n${conversation.summary}` },
|
{ role: 'system', content: `Contexte de la conversation précédente:\n${conversation.summary}` },
|
||||||
...incomingMessages.slice(-RECENT_KEEP),
|
...incomingMessages.slice(-RECENT_KEEP),
|
||||||
]
|
] as any
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('[Chat] Summary generation failed, using full context:', e)
|
console.error('[Chat] Summary generation failed, using full context:', e)
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ export async function PATCH(
|
|||||||
if (color !== undefined) updateData.color = color
|
if (color !== undefined) updateData.color = color
|
||||||
if (order !== undefined) updateData.order = order
|
if (order !== undefined) updateData.order = order
|
||||||
if (trashedAt !== undefined) updateData.trashedAt = trashedAt ? new Date(trashedAt) : null
|
if (trashedAt !== undefined) updateData.trashedAt = trashedAt ? new Date(trashedAt) : null
|
||||||
if (parentId !== undefined) updateData.parentId = parentId
|
if (parentId !== undefined) updateData.parent = parentId ? { connect: { id: parentId } } : { disconnect: true }
|
||||||
|
|
||||||
if (trashedAt !== undefined) {
|
if (trashedAt !== undefined) {
|
||||||
const descendantIds = await getDescendantIds(id)
|
const descendantIds = await getDescendantIds(id)
|
||||||
|
|||||||
@@ -66,13 +66,13 @@ export async function GET(
|
|||||||
console.error('[network] NoteLink query failed:', err)
|
console.error('[network] NoteLink query failed:', err)
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapBacklink = (bl: (typeof backlinks)[number]) => ({
|
const mapBacklink = (bl: any) => ({
|
||||||
id: bl.id,
|
id: bl.id,
|
||||||
note: bl.sourceNote,
|
note: bl.sourceNote,
|
||||||
contextSnippet: bl.contextSnippet,
|
contextSnippet: bl.contextSnippet,
|
||||||
createdAt: bl.createdAt,
|
createdAt: bl.createdAt,
|
||||||
})
|
})
|
||||||
const mapOutbound = (ol: (typeof outbound)[number]) => ({
|
const mapOutbound = (ol: any) => ({
|
||||||
id: ol.id,
|
id: ol.id,
|
||||||
note: ol.targetNote,
|
note: ol.targetNote,
|
||||||
contextSnippet: ol.contextSnippet,
|
contextSnippet: ol.contextSnippet,
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ export async function POST(req: NextRequest) {
|
|||||||
links: n.links ?? undefined,
|
links: n.links ?? undefined,
|
||||||
trashedAt: parseDate(n.trashedAt),
|
trashedAt: parseDate(n.trashedAt),
|
||||||
notebookId: n.notebookId ?? null,
|
notebookId: n.notebookId ?? null,
|
||||||
contentUpdatedAt: parseDate(n.contentUpdatedAt),
|
contentUpdatedAt: parseDate(n.contentUpdatedAt) ?? undefined,
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
labelRelations: {
|
labelRelations: {
|
||||||
set: validLabelIds.map(id => ({ id })),
|
set: validLabelIds.map(id => ({ id })),
|
||||||
@@ -187,7 +187,7 @@ export async function POST(req: NextRequest) {
|
|||||||
userId,
|
userId,
|
||||||
createdAt: parseDate(n.createdAt) ?? new Date(),
|
createdAt: parseDate(n.createdAt) ?? new Date(),
|
||||||
updatedAt: parseDate(n.updatedAt) ?? new Date(),
|
updatedAt: parseDate(n.updatedAt) ?? new Date(),
|
||||||
contentUpdatedAt: parseDate(n.contentUpdatedAt),
|
contentUpdatedAt: parseDate(n.contentUpdatedAt) ?? undefined,
|
||||||
labelRelations: {
|
labelRelations: {
|
||||||
connect: validLabelIds.map(id => ({ id })),
|
connect: validLabelIds.map(id => ({ id })),
|
||||||
},
|
},
|
||||||
@@ -211,7 +211,7 @@ export async function POST(req: NextRequest) {
|
|||||||
userId,
|
userId,
|
||||||
createdAt: parseDate(n.createdAt) ?? new Date(),
|
createdAt: parseDate(n.createdAt) ?? new Date(),
|
||||||
updatedAt: parseDate(n.updatedAt) ?? new Date(),
|
updatedAt: parseDate(n.updatedAt) ?? new Date(),
|
||||||
contentUpdatedAt: parseDate(n.contentUpdatedAt),
|
contentUpdatedAt: parseDate(n.contentUpdatedAt) ?? undefined,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
stats.notes++
|
stats.notes++
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export async function POST(request: NextRequest) {
|
|||||||
// Store as a notification to the note owner + admin
|
// Store as a notification to the note owner + admin
|
||||||
await prisma.notification.create({
|
await prisma.notification.create({
|
||||||
data: {
|
data: {
|
||||||
userId: note.userId,
|
userId: note.userId!,
|
||||||
type: 'content_report',
|
type: 'content_report',
|
||||||
title: `Signalement : ${reason}`,
|
title: `Signalement : ${reason}`,
|
||||||
message: details || `Un visiteur a signalé votre note publiée pour: ${reason}`,
|
message: details || `Un visiteur a signalé votre note publiée pour: ${reason}`,
|
||||||
|
|||||||
@@ -149,8 +149,8 @@ export async function PUT(request: NextRequest) {
|
|||||||
if (content !== undefined) updateData.content = content
|
if (content !== undefined) updateData.content = content
|
||||||
if (color !== undefined) updateData.color = color
|
if (color !== undefined) updateData.color = color
|
||||||
if (type !== undefined) updateData.type = type
|
if (type !== undefined) updateData.type = type
|
||||||
if (checkItems !== undefined) updateData.checkItems = checkItems ?? null
|
if (checkItems !== undefined) updateData.checkItems = checkItems != null ? JSON.stringify(checkItems) : null
|
||||||
if (labels !== undefined) updateData.labels = labels ?? null
|
if (labels !== undefined) updateData.labels = labels != null ? JSON.stringify(labels) : null
|
||||||
if (isPinned !== undefined) updateData.isPinned = isPinned
|
if (isPinned !== undefined) updateData.isPinned = isPinned
|
||||||
if (isArchived !== undefined) updateData.isArchived = isArchived
|
if (isArchived !== undefined) updateData.isArchived = isArchived
|
||||||
if (images !== undefined) updateData.images = images ?? null
|
if (images !== undefined) updateData.images = images ?? null
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ export async function GET() {
|
|||||||
})),
|
})),
|
||||||
brainstorms: brainstorms.map((b) => ({
|
brainstorms: brainstorms.map((b) => ({
|
||||||
id: b.id,
|
id: b.id,
|
||||||
title: b.title,
|
title: b.seedIdea,
|
||||||
createdAt: b.createdAt,
|
createdAt: b.createdAt,
|
||||||
ideasCount: b.ideas.length,
|
ideasCount: b.ideas.length,
|
||||||
})),
|
})),
|
||||||
@@ -196,9 +196,9 @@ export async function GET() {
|
|||||||
|
|
||||||
const canvasFolder = zip.folder('canvases')!
|
const canvasFolder = zip.folder('canvases')!
|
||||||
for (const brainstorm of brainstorms) {
|
for (const brainstorm of brainstorms) {
|
||||||
const fileBase = uniqueBaseName(brainstorm.title || 'Untitled Brainstorm', brainstorm.id)
|
const fileBase = uniqueBaseName(brainstorm.seedIdea || 'Untitled Brainstorm', brainstorm.id)
|
||||||
|
|
||||||
let canvasMd = `# Brainstorm Canvas: ${brainstorm.title || 'Untitled'}\n\n`
|
let canvasMd = `# Brainstorm Canvas: ${brainstorm.seedIdea || 'Untitled'}\n\n`
|
||||||
canvasMd += `- **Created At:** ${brainstorm.createdAt.toISOString()}\n`
|
canvasMd += `- **Created At:** ${brainstorm.createdAt.toISOString()}\n`
|
||||||
canvasMd += `- **Updated At:** ${brainstorm.updatedAt.toISOString()}\n`
|
canvasMd += `- **Updated At:** ${brainstorm.updatedAt.toISOString()}\n`
|
||||||
canvasMd += `- **Ideas Count:** ${brainstorm.ideas.length}\n\n`
|
canvasMd += `- **Ideas Count:** ${brainstorm.ideas.length}\n\n`
|
||||||
@@ -232,7 +232,7 @@ export async function GET() {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
const offlineCanvases = brainstorms.map((b) => ({
|
const offlineCanvases = brainstorms.map((b) => ({
|
||||||
title: b.title || 'Untitled Brainstorm',
|
title: b.seedIdea || 'Untitled Brainstorm',
|
||||||
createdAt: b.createdAt.toISOString(),
|
createdAt: b.createdAt.toISOString(),
|
||||||
ideas: b.ideas.map((i) => ({
|
ideas: b.ideas.map((i) => ({
|
||||||
title: i.title,
|
title: i.title,
|
||||||
@@ -499,7 +499,7 @@ export async function GET() {
|
|||||||
const zipBuffer = await zip.generateAsync({ type: 'nodebuffer' })
|
const zipBuffer = await zip.generateAsync({ type: 'nodebuffer' })
|
||||||
|
|
||||||
const dateString = new Date().toISOString().split('T')[0]
|
const dateString = new Date().toISOString().split('T')[0]
|
||||||
return new NextResponse(zipBuffer, {
|
return new NextResponse(new Uint8Array(zipBuffer) as BodyInit, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/zip',
|
'Content-Type': 'application/zip',
|
||||||
'Content-Disposition': `attachment; filename="memento-workspace-export-${dateString}.zip"`,
|
'Content-Disposition': `attachment; filename="memento-workspace-export-${dateString}.zip"`,
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ export default async function RootLayout({
|
|||||||
<ThemeInitializer theme={userSettings.theme} fontSize={aiSettings.fontSize} fontFamily={aiSettings.fontFamily} accentColor={userSettings.accentColor} />
|
<ThemeInitializer theme={userSettings.theme} fontSize={aiSettings.fontSize} fontFamily={aiSettings.fontFamily} accentColor={userSettings.accentColor} />
|
||||||
{children}
|
{children}
|
||||||
<LanguageProvider initialLanguage="en">
|
<LanguageProvider initialLanguage="en">
|
||||||
<CookieConsentRoot initialAnonymousAnalytics={aiSettings?.anonymousAnalytics} />
|
<CookieConsentRoot />
|
||||||
</LanguageProvider>
|
</LanguageProvider>
|
||||||
<Toaster />
|
<Toaster />
|
||||||
</SessionProviderWrapper>
|
</SessionProviderWrapper>
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ function getActionLabel(action: string, t: (key: string) => string | undefined):
|
|||||||
function timeAgo(dateStr: string, t: (key: string) => string | undefined): string {
|
function timeAgo(dateStr: string, t: (key: string) => string | undefined): string {
|
||||||
const diff = Date.now() - new Date(dateStr).getTime()
|
const diff = Date.now() - new Date(dateStr).getTime()
|
||||||
const mins = Math.floor(diff / 60000)
|
const mins = Math.floor(diff / 60000)
|
||||||
if (mins < 1) return t('brainstorm.justNow')
|
if (mins < 1) return t('brainstorm.justNow') || ''
|
||||||
if (mins < 60) return `${mins}m`
|
if (mins < 60) return `${mins}m`
|
||||||
const hours = Math.floor(mins / 60)
|
const hours = Math.floor(mins / 60)
|
||||||
if (hours < 24) return `${hours}h`
|
if (hours < 24) return `${hours}h`
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export function MoveToNotebookPicker({
|
|||||||
close()
|
close()
|
||||||
}
|
}
|
||||||
|
|
||||||
const child = React.cloneElement(children, {
|
const child = React.cloneElement(children as any, {
|
||||||
ref: (node: HTMLElement | null) => {
|
ref: (node: HTMLElement | null) => {
|
||||||
triggerRef.current = node
|
triggerRef.current = node
|
||||||
const childRef = (children as React.ReactElement & { ref?: React.Ref<HTMLElement> }).ref
|
const childRef = (children as React.ReactElement & { ref?: React.Ref<HTMLElement> }).ref
|
||||||
@@ -71,7 +71,7 @@ export function MoveToNotebookPicker({
|
|||||||
},
|
},
|
||||||
onClick: (e: React.MouseEvent) => {
|
onClick: (e: React.MouseEvent) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
children.props.onClick?.(e)
|
;(children as any).props.onClick?.(e)
|
||||||
setOpen((prev) => !prev)
|
setOpen((prev) => !prev)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ const NOTE_TYPE_ICONS: Record<NoteType, LucideIcon> = {
|
|||||||
markdown: FileCode2,
|
markdown: FileCode2,
|
||||||
richtext: PenLine,
|
richtext: PenLine,
|
||||||
checklist: ListChecks,
|
checklist: ListChecks,
|
||||||
|
daily: FileText,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map icon names to lucide-react components
|
// Map icon names to lucide-react components
|
||||||
@@ -205,7 +206,7 @@ export const NoteCard = memo(function NoteCard({
|
|||||||
try {
|
try {
|
||||||
await updateNote(noteId, { reminder }, { skipRevalidation: true })
|
await updateNote(noteId, { reminder }, { skipRevalidation: true })
|
||||||
setReminderDate(reminder)
|
setReminderDate(reminder)
|
||||||
emitNoteChange({ type: 'updated', note: { ...note, reminder: reminder?.toISOString() ?? null } })
|
emitNoteChange({ type: 'updated', note: { ...note, reminder: (reminder?.toISOString() ?? null) as any } })
|
||||||
if (reminder) {
|
if (reminder) {
|
||||||
toast.success(t('notes.reminderSet', { datetime: reminder.toLocaleString() }))
|
toast.success(t('notes.reminderSet', { datetime: reminder.toLocaleString() }))
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import { useLanguage } from '@/lib/i18n'
|
|||||||
import { LabelBadge } from './label-badge'
|
import { LabelBadge } from './label-badge'
|
||||||
import { NoteChecklist } from './note-checklist'
|
import { NoteChecklist } from './note-checklist'
|
||||||
|
|
||||||
const ForceGraph2D = dynamic(() => import('react-force-graph-2d'), { ssr: false })
|
const ForceGraph2D = dynamic(() => import('react-force-graph-2d'), { ssr: false }) as any
|
||||||
const MarkdownContent = dynamic(() => import('./markdown-content').then(m => ({ default: m.MarkdownContent })), {
|
const MarkdownContent = dynamic(() => import('./markdown-content').then(m => ({ default: m.MarkdownContent })), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
loading: () => <div className="h-20 w-full animate-pulse bg-concrete/5 rounded" />
|
loading: () => <div className="h-20 w-full animate-pulse bg-concrete/5 rounded" />
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ const TYPE_ICONS: Record<NoteType, React.ElementType> = {
|
|||||||
markdown: FileCode2,
|
markdown: FileCode2,
|
||||||
richtext: PenLine,
|
richtext: PenLine,
|
||||||
checklist: ListChecks,
|
checklist: ListChecks,
|
||||||
|
daily: PenLine,
|
||||||
}
|
}
|
||||||
|
|
||||||
const TYPE_I18N_KEYS: Record<NoteType, string> = {
|
const TYPE_I18N_KEYS: Record<NoteType, string> = {
|
||||||
@@ -24,6 +25,7 @@ const TYPE_I18N_KEYS: Record<NoteType, string> = {
|
|||||||
markdown: 'notes.typeMarkdown',
|
markdown: 'notes.typeMarkdown',
|
||||||
richtext: 'notes.typeRichText',
|
richtext: 'notes.typeRichText',
|
||||||
checklist: 'notes.typeChecklist',
|
checklist: 'notes.typeChecklist',
|
||||||
|
daily: 'notes.typeDaily',
|
||||||
}
|
}
|
||||||
|
|
||||||
interface NoteTypeSelectorProps {
|
interface NoteTypeSelectorProps {
|
||||||
|
|||||||
@@ -141,9 +141,9 @@ export function EditorialNoteMenu({
|
|||||||
startTransition(async () => {
|
startTransition(async () => {
|
||||||
try {
|
try {
|
||||||
await updateNote(note.id, { reminder }, { skipRevalidation: true })
|
await updateNote(note.id, { reminder }, { skipRevalidation: true })
|
||||||
const patch = { reminder: reminder?.toISOString() ?? null }
|
const patch = { reminder: (reminder?.toISOString() ?? null) as any }
|
||||||
onNotePatch?.(note.id, patch)
|
onNotePatch?.(note.id, patch)
|
||||||
emitNoteChange({ type: 'updated', note: { ...note, reminder: patch.reminder } })
|
emitNoteChange({ type: 'updated', note: { ...note, reminder: patch.reminder } as any })
|
||||||
setShowReminder(false)
|
setShowReminder(false)
|
||||||
} catch {
|
} catch {
|
||||||
toast.error(t('general.error'))
|
toast.error(t('general.error'))
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ type SlashItem = {
|
|||||||
shortcut?: string
|
shortcut?: string
|
||||||
isImage?: boolean
|
isImage?: boolean
|
||||||
isAi?: boolean
|
isAi?: boolean
|
||||||
|
isFavorite?: boolean
|
||||||
aiOption?: 'clarify' | 'shorten' | 'improve' | 'write'
|
aiOption?: 'clarify' | 'shorten' | 'improve' | 'write'
|
||||||
command: (editor: Editor, range?: any) => void
|
command: (editor: Editor, range?: any) => void
|
||||||
}
|
}
|
||||||
@@ -1256,7 +1257,7 @@ export const RichTextEditor = forwardRef<RichTextEditorHandle, RichTextEditorPro
|
|||||||
<MobileEditorToolbar
|
<MobileEditorToolbar
|
||||||
editor={editor}
|
editor={editor}
|
||||||
onOpenActionSheet={() => setActionSheetOpen(true)}
|
onOpenActionSheet={() => setActionSheetOpen(true)}
|
||||||
onInsertImage={imageInsert.requestInsert}
|
onInsertImage={imageInsert.requestInsert as any}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ export const ChartExtension = Node.create({
|
|||||||
addNodeView() {
|
addNodeView() {
|
||||||
return ReactNodeViewRenderer(ChartBlockView, {
|
return ReactNodeViewRenderer(ChartBlockView, {
|
||||||
contentEditable: false,
|
contentEditable: false,
|
||||||
})
|
} as any)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ function ChartBlockView(props: any) {
|
|||||||
Done
|
Done
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<NodeViewContent as="pre" className="p-4 bg-muted/30 rounded-b-lg overflow-x-auto" />
|
<NodeViewContent as={"pre" as any} className="p-4 bg-muted/30 rounded-b-lg overflow-x-auto" />
|
||||||
</NodeViewWrapper>
|
</NodeViewWrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ const MathEquationView = ({ node, updateAttributes, deleteNode, selected }: any)
|
|||||||
|
|
||||||
const insertSymbol = (symbol: string) => {
|
const insertSymbol = (symbol: string) => {
|
||||||
const el = inputRef.current
|
const el = inputRef.current
|
||||||
if (!el) { setInput(prev => prev + symbol); return }
|
if (!el) { setInput((prev: string) => prev + symbol); return }
|
||||||
const start = el.selectionStart
|
const start = el.selectionStart
|
||||||
const end = el.selectionEnd
|
const end = el.selectionEnd
|
||||||
const newVal = input.slice(0, start) + symbol + input.slice(end)
|
const newVal = input.slice(0, start) + symbol + input.slice(end)
|
||||||
@@ -286,14 +286,14 @@ export const MathEquationExtension = Node.create({
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
find: /\$\$([^$]+)\$\$$/,
|
find: /\$\$([^$]+)\$\$$/,
|
||||||
handler: ({ state, range, match }) => {
|
handler: ({ state, range, match }: any) => {
|
||||||
const latex = match[1]
|
const latex = match[1]
|
||||||
const tr = state.tr
|
const tr = state.tr
|
||||||
tr.deleteRange(range.from, range.to)
|
tr.deleteRange(range.from, range.to)
|
||||||
tr.insert(range.from, state.schema.nodes.mathEquationBlock.create({ latex }))
|
tr.insert(range.from, state.schema.nodes.mathEquationBlock.create({ latex }))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
] as any
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -338,7 +338,7 @@ export const InlineMathExtension = Node.create({
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
find: /(?:^|\s)\$([^$\n]+)\$$/,
|
find: /(?:^|\s)\$([^$\n]+)\$$/,
|
||||||
handler: ({ state, range, match }) => {
|
handler: ({ state, range, match }: any) => {
|
||||||
const fullMatch = match[0]
|
const fullMatch = match[0]
|
||||||
const latex = match[1]
|
const latex = match[1]
|
||||||
const leadingSpace = fullMatch.startsWith(' ') ? ' ' : ''
|
const leadingSpace = fullMatch.startsWith(' ') ? ' ' : ''
|
||||||
@@ -352,7 +352,7 @@ export const InlineMathExtension = Node.create({
|
|||||||
])
|
])
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
] as any
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -58,10 +58,10 @@ function getCached(key: string): SuggestChartsResponse | null {
|
|||||||
function setCached(key: string, data: SuggestChartsResponse): void {
|
function setCached(key: string, data: SuggestChartsResponse): void {
|
||||||
cache.set(key, { data, timestamp: Date.now() })
|
cache.set(key, { data, timestamp: Date.now() })
|
||||||
// Limit cache size to 50 entries
|
// Limit cache size to 50 entries
|
||||||
if (cache.size > 50) {
|
if (cache.size > 50) {
|
||||||
const firstKey = cache.keys().next().value
|
const firstKey = cache.keys().next().value
|
||||||
cache.delete(firstKey)
|
cache.delete(firstKey || '')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const SVG_SANITIZE_CONFIG = {
|
|||||||
|
|
||||||
export function sanitizeIllustrationSvg(svg: string): string {
|
export function sanitizeIllustrationSvg(svg: string): string {
|
||||||
if (!svg) return ''
|
if (!svg) return ''
|
||||||
return DOMPurify.sanitize(svg, SVG_SANITIZE_CONFIG)
|
return DOMPurify.sanitize(svg, SVG_SANITIZE_CONFIG as any) as unknown as string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sanitizeRichHtml(html: string): string {
|
export function sanitizeRichHtml(html: string): string {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export const PROPERTY_TYPES = [
|
|||||||
'select',
|
'select',
|
||||||
'multiselect',
|
'multiselect',
|
||||||
'checkbox',
|
'checkbox',
|
||||||
|
'relation',
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
export type PropertyType = (typeof PROPERTY_TYPES)[number]
|
export type PropertyType = (typeof PROPERTY_TYPES)[number]
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export interface LinkMetadata {
|
|||||||
siteName?: string;
|
siteName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NoteType = 'text' | 'markdown' | 'richtext' | 'checklist'
|
export type NoteType = 'text' | 'markdown' | 'richtext' | 'checklist' | 'daily'
|
||||||
|
|
||||||
export interface Note {
|
export interface Note {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@@ -82,7 +82,6 @@ const nextConfig: NextConfig = {
|
|||||||
reactStrictMode: false,
|
reactStrictMode: false,
|
||||||
|
|
||||||
// Allow development origins for HMR and Dev Server access
|
// Allow development origins for HMR and Dev Server access
|
||||||
// @ts-expect-error - Some NextConfig versions might require this in experimental
|
|
||||||
allowedDevOrigins: ['192.168.1.83'],
|
allowedDevOrigins: ['192.168.1.83'],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user