vipcare 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/vip.js +41 -1
- package/lib/card.js +32 -1
- package/package.json +1 -1
package/bin/vip.js
CHANGED
|
@@ -522,7 +522,47 @@ program.command('card')
|
|
|
522
522
|
const dir = path.dirname(outputPath);
|
|
523
523
|
const file = path.basename(outputPath);
|
|
524
524
|
|
|
525
|
-
const server = http.createServer((req, res) => {
|
|
525
|
+
const server = http.createServer(async (req, res) => {
|
|
526
|
+
// API: regenerate a profile
|
|
527
|
+
if (req.url.startsWith('/api/regenerate/')) {
|
|
528
|
+
const slug = req.url.replace('/api/regenerate/', '').replace(/\//g, '');
|
|
529
|
+
res.setHeader('Content-Type', 'application/json');
|
|
530
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
531
|
+
|
|
532
|
+
console.log(c.cyan(` Regenerating ${slug}...`));
|
|
533
|
+
try {
|
|
534
|
+
const content = loadProfile(slug);
|
|
535
|
+
if (!content) { res.writeHead(404); res.end(JSON.stringify({ error: 'Profile not found' })); return; }
|
|
536
|
+
|
|
537
|
+
const { extractMetadata } = await import('../lib/monitor.js');
|
|
538
|
+
const meta = extractMetadata(content);
|
|
539
|
+
const personName = meta.name || slug;
|
|
540
|
+
|
|
541
|
+
const person = resolveFromName(personName);
|
|
542
|
+
if (meta.twitterHandle) person.twitterHandle = person.twitterHandle || meta.twitterHandle;
|
|
543
|
+
if (meta.linkedinUrl) person.linkedinUrl = person.linkedinUrl || meta.linkedinUrl;
|
|
544
|
+
|
|
545
|
+
const [rawData, sources] = gatherData(person);
|
|
546
|
+
if (!rawData.trim()) { res.writeHead(400); res.end(JSON.stringify({ error: 'No data found' })); return; }
|
|
547
|
+
|
|
548
|
+
const profile = await synthesizeProfile(rawData, sources);
|
|
549
|
+
saveProfile(personName, profile);
|
|
550
|
+
|
|
551
|
+
// Regenerate cards
|
|
552
|
+
regenerate();
|
|
553
|
+
|
|
554
|
+
console.log(c.green(` ${personName} regenerated.`));
|
|
555
|
+
res.writeHead(200);
|
|
556
|
+
res.end(JSON.stringify({ ok: true, name: personName }));
|
|
557
|
+
} catch (e) {
|
|
558
|
+
console.error(c.red(` Error: ${e.message}`));
|
|
559
|
+
res.writeHead(500);
|
|
560
|
+
res.end(JSON.stringify({ error: e.message }));
|
|
561
|
+
}
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Static files
|
|
526
566
|
const filePath = req.url === '/' ? path.join(dir, file) : path.join(dir, req.url);
|
|
527
567
|
if (!fs.existsSync(filePath)) { res.writeHead(404); res.end('Not found'); return; }
|
|
528
568
|
const ext = path.extname(filePath);
|
package/lib/card.js
CHANGED
|
@@ -335,6 +335,7 @@ function renderCard(card, index) {
|
|
|
335
335
|
\${card.last_connection ? \`<div style="font-size:0.8em;color:#2563eb;margin:6px 0">🤝 \${card.last_connection}</div>\` : \`<div style="font-size:0.8em;color:#94a3b8;margin:6px 0">💡 \${card.icebreakers?.[0] || 'No connection yet'}</div>\`}
|
|
336
336
|
\${card.talking_points?.length ? \`<div style="margin:8px 0"><div style="font-size:0.75em;color:#94a3b8;text-transform:uppercase;margin-bottom:4px">Talking Points</div>\${card.talking_points.slice(0,2).map(t => \`<div style="font-size:0.8em;color:#475569;padding:2px 0">• \${t}</div>\`).join('')}</div>\` : ''}
|
|
337
337
|
\${card.annotations?.length ? \`<div style="border-top:1px solid #e2e8f0;margin-top:8px;padding-top:8px;font-size:0.75em;color:#64748b">📝 \${card.annotations[card.annotations.length-1]}</div>\` : ''}
|
|
338
|
+
<button onclick="event.stopPropagation();regenerateProfile('\${card.slug}',this)" style="margin-top:10px;width:100%;padding:6px;border:1px solid #e2e8f0;border-radius:6px;background:#f8fafc;color:#64748b;font-size:0.75em;cursor:pointer;transition:all 0.2s" onmouseover="this.style.borderColor='#2563eb';this.style.color='#2563eb'" onmouseout="this.style.borderColor='#e2e8f0';this.style.color='#64748b'">🔄 Update Profile</button>
|
|
338
339
|
</div>\`;
|
|
339
340
|
}
|
|
340
341
|
|
|
@@ -386,7 +387,10 @@ function openModal(index) {
|
|
|
386
387
|
|
|
387
388
|
\${card.tags?.length ? \`<div class="tags" style="margin-top:12px">\${card.tags.map(t => \`<span class="tag">\${t}</span>\`).join('')}</div>\` : ''}
|
|
388
389
|
|
|
389
|
-
|
|
390
|
+
<div style="display:flex;gap:10px;justify-content:center;margin-top:20px">
|
|
391
|
+
\${card.slug ? \`<a href="\${card.slug}.html" style="color:#2563eb;text-decoration:none;padding:8px 20px;border:1px solid #2563eb;border-radius:8px;display:inline-block">View Full Profile →</a>\` : ''}
|
|
392
|
+
\${card.slug ? \`<button onclick="regenerateProfile('\${card.slug}',this)" style="padding:8px 20px;border:1px solid #e2e8f0;border-radius:8px;background:#f8fafc;color:#64748b;cursor:pointer;font-size:0.9em">🔄 Update</button>\` : ''}
|
|
393
|
+
</div>
|
|
390
394
|
\`;
|
|
391
395
|
|
|
392
396
|
document.getElementById('modal').classList.add('active');
|
|
@@ -398,6 +402,33 @@ function closeModal() {
|
|
|
398
402
|
|
|
399
403
|
document.addEventListener('keydown', e => { if (e.key === 'Escape') closeModal(); });
|
|
400
404
|
|
|
405
|
+
async function regenerateProfile(slug, btn) {
|
|
406
|
+
const originalText = btn.textContent;
|
|
407
|
+
btn.textContent = '⏳ Updating...';
|
|
408
|
+
btn.disabled = true;
|
|
409
|
+
btn.style.opacity = '0.6';
|
|
410
|
+
|
|
411
|
+
try {
|
|
412
|
+
const resp = await fetch(\`/api/regenerate/\${slug}\`);
|
|
413
|
+
const data = await resp.json();
|
|
414
|
+
|
|
415
|
+
if (resp.ok) {
|
|
416
|
+
btn.textContent = '✅ Updated!';
|
|
417
|
+
btn.style.color = '#16a34a';
|
|
418
|
+
// Reload page after a short delay to show new data
|
|
419
|
+
setTimeout(() => location.reload(), 1500);
|
|
420
|
+
} else {
|
|
421
|
+
btn.textContent = '❌ ' + (data.error || 'Failed');
|
|
422
|
+
btn.style.color = '#dc2626';
|
|
423
|
+
setTimeout(() => { btn.textContent = originalText; btn.disabled = false; btn.style.opacity = '1'; btn.style.color = ''; }, 3000);
|
|
424
|
+
}
|
|
425
|
+
} catch (e) {
|
|
426
|
+
btn.textContent = '❌ Error';
|
|
427
|
+
btn.style.color = '#dc2626';
|
|
428
|
+
setTimeout(() => { btn.textContent = originalText; btn.disabled = false; btn.style.opacity = '1'; btn.style.color = ''; }, 3000);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
401
432
|
// Render
|
|
402
433
|
const grid = document.getElementById('grid');
|
|
403
434
|
grid.innerHTML = cards.map((card, i) => renderCard(card, i)).join('');
|