import { NoteEntity } from '../database/types'; export interface NoteListItemEntity extends NoteEntity { is_pinned?: number; } /** * 清理 Markdown 正文并提取前 80 字符的纯文本摘要(对标 Apple 备忘录副标题摘要) * * @param body 笔记正文内容 * @param title 笔记标题(若首行与标题相同则自动过滤跳过) */ export const cleanSnippet = (body: string, title?: string): string => { if (!body || !body.trim()) return ''; const lines = body.split('\n'); const cleanedSegments: string[] = []; const normalizedTitle = title ? title.trim().toLowerCase() : ''; for (const line of lines) { const trimmed = line.trim(); if (!trimmed) continue; // 如果当前行与标题相同或为标题的 Markdown 形式(如 # 标题),予以跳过 const headingStripped = trimmed.replace(/^#{1,6}\s+/, '').trim().toLowerCase(); if (normalizedTitle && (trimmed.toLowerCase() === normalizedTitle || headingStripped === normalizedTitle)) { continue; } // 移除各种 Markdown 格式控制符 const text = trimmed .replace(/^#{1,6}\s+/, '') .replace(/^[>*+-]\s+\[[ xX]\]\s*/, '') .replace(/^[>*+-]\s+/, '') .replace(/^\d+\.\s+\[[ xX]\]\s*/, '') .replace(/^\d+\.\s+/, '') .replace(/!\[([^\]]*)\]\([^)]*\)/g, '') // 移除图片 .replace(/\[([^\]]+)\]\([^)]*\)/g, '$1') // 链接仅保留描述文本 .replace(/```[\s\S]*?```/g, '') // 代码块标记 .replace(/[`*_~]/g, '') // 加粗、斜体、删除线、行内代码 .replace(/<[^>]+>/g, '') // HTML 标签 .trim(); if (text) { cleanedSegments.push(text); if (cleanedSegments.join(' ').length >= 90) break; } } const combined = cleanedSegments.join(' ').replace(/\s+/g, ' ').trim(); if (combined.length > 70) { return combined.slice(0, 70) + '…'; } return combined; }; /** * 苹果备忘录风格的时间格式化: * - 今天:仅展示时分(如 15:30) * - 本年:展示月/日(如 10/25) * - 往年:展示年/月/日(如 2025/10/25) */ export const formatNoteListDate = (updatedTimeMs: number): string => { if (!updatedTimeMs) return ''; const now = new Date(); const date = new Date(updatedTimeMs); const isToday = date.getFullYear() === now.getFullYear() && date.getMonth() === now.getMonth() && date.getDate() === now.getDate(); if (isToday) { const hours = date.getHours().toString().padStart(2, '0'); const minutes = date.getMinutes().toString().padStart(2, '0'); return `${hours}:${minutes}`; } const isThisYear = date.getFullYear() === now.getFullYear(); if (isThisYear) { return `${date.getMonth() + 1}/${date.getDate()}`; } return `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`; }; /** * 将置顶笔记重排至列表顶端,并注入 is_pinned 属性 */ export const reorderNotesWithPinned = (notes: NoteListItemEntity[], pinnedNoteIds: string[] = []): NoteListItemEntity[] => { if (!pinnedNoteIds || !pinnedNoteIds.length || !notes || !notes.length) { return notes; } const pinnedSet = new Set(pinnedNoteIds); const pinned: NoteListItemEntity[] = []; const unpinned: NoteListItemEntity[] = []; for (const note of notes) { if (pinnedSet.has(note.id)) { pinned.push({ ...note, is_pinned: 1 }); } else { unpinned.push(note); } } return [...pinned, ...unpinned]; };