Polish memory UI and add auto tags
This commit is contained in:
@@ -83,6 +83,48 @@ function getEntryCount(content) {
|
||||
return content.split('\n').filter((line) => line.trim().startsWith('- [')).length
|
||||
}
|
||||
|
||||
const TAG_STOPWORDS = new Set([
|
||||
'about', 'after', 'again', 'also', 'been', 'being', 'between', 'could', 'daily', 'does', 'done', 'from', 'have', 'into', 'just', 'like', 'main', 'make', 'maybe', 'memory', 'more', 'most', 'much', 'need', 'note', 'notes', 'only', 'other', 'over', 'really', 'same', 'some', 'still', 'than', 'that', 'their', 'them', 'then', 'there', 'these', 'they', 'this', 'today', 'very', 'want', 'were', 'what', 'when', 'with', 'work', 'would', 'your', 'yours', 'jimmi', 'rook', 'said', 'todo', 'todos'
|
||||
])
|
||||
|
||||
function generateTags(content, filename) {
|
||||
const tags = []
|
||||
const pushTag = (value) => {
|
||||
const cleaned = String(value || '').toLowerCase().replace(/[^a-z0-9+-]+/g, '').trim()
|
||||
if (!cleaned || cleaned.length < 3 || TAG_STOPWORDS.has(cleaned) || tags.includes(cleaned)) return
|
||||
tags.push(cleaned)
|
||||
}
|
||||
|
||||
if (filename === 'MEMORY.md') pushTag('core')
|
||||
|
||||
for (const line of content.split('\n')) {
|
||||
const trimmed = line.trim()
|
||||
if (trimmed.startsWith('#')) {
|
||||
trimmed.replace(/^#+\s*/, '').split(/[^a-zA-Z0-9+-]+/).forEach(pushTag)
|
||||
}
|
||||
const hashtagMatches = trimmed.match(/#[a-zA-Z0-9+-]+/g) || []
|
||||
hashtagMatches.forEach((tag) => pushTag(tag.slice(1)))
|
||||
}
|
||||
|
||||
const frequency = new Map()
|
||||
const normalized = content
|
||||
.toLowerCase()
|
||||
.replace(/\[[^\]]*\]/g, ' ')
|
||||
.replace(/[^a-z0-9+\-\s]/g, ' ')
|
||||
|
||||
for (const token of normalized.split(/\s+/)) {
|
||||
if (!token || token.length < 4 || TAG_STOPWORDS.has(token)) continue
|
||||
frequency.set(token, (frequency.get(token) || 0) + 1)
|
||||
}
|
||||
|
||||
Array.from(frequency.entries())
|
||||
.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
|
||||
.slice(0, 8)
|
||||
.forEach(([token]) => pushTag(token))
|
||||
|
||||
return tags.slice(0, 4)
|
||||
}
|
||||
|
||||
function formatDateLabel(filename) {
|
||||
const match = filename.match(/^(\d{4})-(\d{2})-(\d{2})\.md$/)
|
||||
if (!match) return filename
|
||||
@@ -206,6 +248,7 @@ function buildFileMeta(doc) {
|
||||
wordCount: wordCount(content),
|
||||
entryCount: getEntryCount(content),
|
||||
dateLabel: formatDateLabel(doc.filename),
|
||||
tags: generateTags(content, doc.filename),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,6 +282,7 @@ app.get('/api/meta', async (req, res) => {
|
||||
size: Buffer.byteLength(mainDoc.content || '', 'utf8'),
|
||||
wordCount: wordCount(mainDoc.content || ''),
|
||||
entryCount: getEntryCount(mainDoc.content || ''),
|
||||
tags: generateTags(mainDoc.content || '', 'MEMORY.md'),
|
||||
} : null
|
||||
|
||||
const dailyFiles = dailyDocs.map(buildFileMeta)
|
||||
@@ -279,7 +323,7 @@ app.get('/api/memories/:filename', async (req, res) => {
|
||||
await ensureSync()
|
||||
const doc = await readMemoryDocument(filename)
|
||||
if (!doc) return res.status(404).json({ error: 'File not found' })
|
||||
return res.json({ content: doc.content || '' })
|
||||
return res.json({ content: doc.content || '', tags: generateTags(doc.content || '', filename) })
|
||||
} catch (error) {
|
||||
return res.status(500).json({ error: error.message })
|
||||
}
|
||||
@@ -290,7 +334,7 @@ app.get('/api/main-memory', async (req, res) => {
|
||||
await ensureSync()
|
||||
const doc = await readMemoryDocument('MEMORY.md')
|
||||
if (!doc) return res.status(404).json({ error: 'MEMORY.md not found' })
|
||||
res.json({ content: doc.content || '' })
|
||||
res.json({ content: doc.content || '', tags: generateTags(doc.content || '', 'MEMORY.md') })
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message })
|
||||
}
|
||||
@@ -369,6 +413,7 @@ app.get('/api/search', async (req, res) => {
|
||||
dateLabel: formatDateLabel(doc.filename),
|
||||
entryCount: getEntryCount(content),
|
||||
wordCount: wordCount(content),
|
||||
tags: generateTags(content, doc.filename),
|
||||
matchCount: (lower.match(new RegExp(query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')) || []).length,
|
||||
snippet: `${windowStart > 0 ? '…' : ''}${snippet}${windowEnd < content.length ? '…' : ''}`,
|
||||
matchingLines,
|
||||
|
||||
Reference in New Issue
Block a user