openvibers/openvibe.community Commits

Search: an untitled paste is named after its AI summary's first sentence or its first line (Search listed 'Untitled' as a result's title) (#93)

OP OpenVibers committed 27 days ago · 099e9b59a1 · 2 files, +24 −1

Changes

ddiff --git a/server/search/documents.js b/server/search/documents.js
iindex b2b5209..b25651b 100644
---- a/server/search/documents.js
++++ b/server/search/documents.js
@@@ -40,6 +40,18 @@ const plain = (md) => String(md || '')
.replace(/^[ \t]*(#{1,6}|>|[-*+]|\d+\.)[ \t]+/gm, '')
.replace(/[*_~`]+/g, '');
const AUTHORSHIP = { ai: 'ai_generated', imported: 'imported', discord: 'imported' };
++// A paste saved without a title is stored as 'Untitled' (pastes/store.js), and Search showed "Untitled" as a result's
++// title (a YouTube-downloader search, 2026-10-17). Name it after the first sentence of its AI summary, or its first
++// line, at most 90 characters; only a paste with neither stays Untitled.
++function pasteTitle(p, shot) {
++ const own = clean(p.title, 500);
++ if (own && own !== 'Untitled') return own;
++ const sentence = clean(p.ai_summary, 400).split(/(?<=[.!?])\s/)[0];
++ const line = shot ? '' : clean(String(p.content || '').split('\n').map((l) => l.trim()).find(Boolean), 400);
++ const pick = (sentence || line).replace(/[.!?]+$/, '');
++ if (!pick) return shot ? 'Screenshot' : 'Untitled';
++ return pick.length <= 90 ? pick : `${pick.slice(0, 89).replace(/\s+\S*$/, '')}…`;
++}
const hashOf = (doc) => crypto.createHash('sha256').update(JSON.stringify(doc)).digest('hex').slice(0, 32);
const sqlTime = (ms) => new Date(ms).toISOString().replace('T', ' ').slice(0, 19);
@@@ -91,7 +103,7 @@ function createSearchDocuments({ db }) {
const doc = {
owner: 'community', type: 'paste', id: pasteDocId(p), deleted: false, visibility: 'public',
canonical_url: `${config.baseUrl}/p/${encodeURIComponent(p.slug)}`,
-- title: clean(p.title, 500) || (shot ? 'Screenshot' : 'Untitled'),
++ title: pasteTitle(p, shot),
summary: clean(p.ai_summary, 4000) || clean(shot ? '' : p.content, 300) || `A ${shot ? 'screenshot' : 'paste'} on OpenVibe.Community.`,
body: shot ? clean(p.ai_summary, 8000) : String(p.content || '').slice(0, 40000),
facets, authorship: AUTHORSHIP[p.origin] || 'human', publication_state: 'published',
ddiff --git a/test/search-documents.test.js b/test/search-documents.test.js
iindex 680a687..e9440b1 100644
---- a/test/search-documents.test.js
++++ b/test/search-documents.test.js
@@@ -83,6 +83,17 @@ const { createSearchDocuments } = require('../server/search/documents');
ok((await index()).at(-1)); assert.deepStrictEqual((await index()).at(-1).payload, { type: 'paste', id: 'amber-fox-42', revision: 2 });
});
++ await check('an untitled paste is named after its AI summary, or its first line', async () => {
++ const row = async (id) => await db.prepare('SELECT * FROM pastes WHERE id = ?').get(id);
++ const a = await pastes.insertPaste(db, { slug: 'grand-river-59', owner_subject: USR, type: 'paste', title: '', content: 'yt mp3 limits', visibility: 'public' });
++ await db.prepare('UPDATE pastes SET ai_summary = ? WHERE id = ?').run('A note about a Tools tab offering YouTube MP3 downloaders with usage limits. It varies by level.', a.id);
++ const b = await pastes.insertPaste(db, { slug: 'quiet-moss-11', owner_subject: USR, type: 'paste', title: '', content: '\n\n npm install openvibe-sdk \nmore', visibility: 'public' });
++ assert.strictEqual(await docs.publishPaste(await row(a.id)), 'sent');
++ assert.strictEqual((await index()).at(-1).payload.title, 'A note about a Tools tab offering YouTube MP3 downloaders with usage limits');
++ assert.strictEqual(await docs.publishPaste(await row(b.id)), 'sent');
++ assert.strictEqual((await index()).at(-1).payload.title, 'npm install openvibe-sdk');
++ });
++
await check('the scan finds a paste changed since the last scan; the refresh removes one that is gone', async () => {
const now = Date.now();
await docs.scan({ now });