vipcare 0.6.3 โ 0.7.0
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/lib/card.js +110 -19
- package/lib/templates.js +14 -3
- package/package.json +1 -1
package/lib/card.js
CHANGED
|
@@ -49,6 +49,25 @@ export function generateCards(profiles, outputPath) {
|
|
|
49
49
|
if (noteLines.length) lastConnection = noteLines[noteLines.length - 1];
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
// Extract Recent Activity section
|
|
53
|
+
const recentMatch = content.match(/## Recent Activity\n([\s\S]*?)(?=\n---|\n## [A-Z])/);
|
|
54
|
+
const recentActivity = recentMatch ? recentMatch[1].trim() : '';
|
|
55
|
+
|
|
56
|
+
// Extract Key Quotes section
|
|
57
|
+
const quotesMatch = content.match(/## Key Quotes\n([\s\S]*?)(?=\n---|\n## [A-Z])/);
|
|
58
|
+
const rawQuotes = quotesMatch ? quotesMatch[1].trim() : '';
|
|
59
|
+
|
|
60
|
+
// Extract Core Philosophy section
|
|
61
|
+
const philoMatch = content.match(/## Core Philosophy\n([\s\S]*?)(?=\n---|\n## [A-Z])/);
|
|
62
|
+
const rawPhilosophy = philoMatch ? philoMatch[1].trim() : '';
|
|
63
|
+
|
|
64
|
+
// Load raw Twitter data if available
|
|
65
|
+
const twitterRawFile = path.join(path.dirname(p.path), '.raw', p.slug, `twitter_${twitterHandle || p.slug}.md`);
|
|
66
|
+
let rawTweets = '';
|
|
67
|
+
if (fs.existsSync(twitterRawFile)) {
|
|
68
|
+
rawTweets = fs.readFileSync(twitterRawFile, 'utf-8');
|
|
69
|
+
}
|
|
70
|
+
|
|
52
71
|
const data = extractVipData(content);
|
|
53
72
|
if (!data) {
|
|
54
73
|
const nameMatch = content.match(/^# (.+)$/m);
|
|
@@ -78,6 +97,10 @@ export function generateCards(profiles, outputPath) {
|
|
|
78
97
|
updated: p.updated,
|
|
79
98
|
annotations,
|
|
80
99
|
last_connection: lastConnection,
|
|
100
|
+
recent_activity: recentActivity,
|
|
101
|
+
raw_quotes: rawQuotes,
|
|
102
|
+
raw_philosophy: rawPhilosophy,
|
|
103
|
+
raw_tweets: rawTweets,
|
|
81
104
|
});
|
|
82
105
|
continue;
|
|
83
106
|
}
|
|
@@ -87,6 +110,10 @@ export function generateCards(profiles, outputPath) {
|
|
|
87
110
|
data.twitter_handle = data.twitter_handle || twitterHandle;
|
|
88
111
|
data.annotations = annotations;
|
|
89
112
|
data.last_connection = lastConnection;
|
|
113
|
+
data.recent_activity = recentActivity;
|
|
114
|
+
data.raw_quotes = rawQuotes;
|
|
115
|
+
data.raw_philosophy = rawPhilosophy;
|
|
116
|
+
data.raw_tweets = rawTweets;
|
|
90
117
|
cards.push(data);
|
|
91
118
|
}
|
|
92
119
|
|
|
@@ -222,6 +249,16 @@ header .count { color: #94a3b8; font-size: 0.85em; }
|
|
|
222
249
|
.panel p, .panel li { color: #475569; font-size: 0.85em; line-height: 1.6; }
|
|
223
250
|
.panel ul { padding-left: 16px; }
|
|
224
251
|
.panel blockquote { border-left: 3px solid #2563eb; padding: 4px 12px; margin: 6px 0; color: #475569; font-style: italic; font-size: 0.85em; }
|
|
252
|
+
.tabs { display: flex; border-bottom: 2px solid #e2e8f0; margin: 0 -24px 16px; padding: 0 24px; }
|
|
253
|
+
.tab { padding: 8px 16px; font-size: 0.85em; font-weight: 600; color: #94a3b8; cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -2px; transition: all 0.2s; }
|
|
254
|
+
.tab:hover { color: #475569; }
|
|
255
|
+
.tab.active { color: #2563eb; border-bottom-color: #2563eb; }
|
|
256
|
+
.tab-content { display: none; }
|
|
257
|
+
.tab-content.active { display: block; }
|
|
258
|
+
.tweet { background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 10px 12px; margin: 8px 0; font-size: 0.8em; color: #475569; line-height: 1.5; }
|
|
259
|
+
.tweet-date { font-size: 0.7em; color: #94a3b8; margin-top: 4px; }
|
|
260
|
+
.activity-item { padding: 8px 0; border-bottom: 1px solid #f1f5f9; font-size: 0.85em; }
|
|
261
|
+
.activity-date { font-size: 0.75em; color: #94a3b8; }
|
|
225
262
|
.radar { max-width: 100%; }
|
|
226
263
|
|
|
227
264
|
/* Mobile */
|
|
@@ -346,13 +383,46 @@ function openPanel(index) {
|
|
|
346
383
|
const panel = document.getElementById('panel-content');
|
|
347
384
|
const avatarUrl = \`https://unavatar.io/twitter/\${card.twitter_handle || card.slug || 'unknown'}\`;
|
|
348
385
|
|
|
386
|
+
// Parse raw tweets into individual tweet objects
|
|
387
|
+
const tweets = [];
|
|
388
|
+
if (card.raw_tweets) {
|
|
389
|
+
const tweetBlocks = card.raw_tweets.split(/โ{5,}/);
|
|
390
|
+
for (const block of tweetBlocks) {
|
|
391
|
+
const textMatch = block.match(/:\n([\s\S]*?)(?=๐
|$)/);
|
|
392
|
+
const dateMatch = block.match(/๐
\s*(.+)/);
|
|
393
|
+
const linkMatch = block.match(/๐\s*(https?:\/\/\S+)/);
|
|
394
|
+
if (textMatch && textMatch[1].trim()) {
|
|
395
|
+
tweets.push({
|
|
396
|
+
text: textMatch[1].trim().substring(0, 280),
|
|
397
|
+
date: dateMatch ? dateMatch[1].trim() : '',
|
|
398
|
+
link: linkMatch ? linkMatch[1] : '',
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Parse recent activity into items
|
|
405
|
+
const activityItems = [];
|
|
406
|
+
if (card.recent_activity) {
|
|
407
|
+
const lines = card.recent_activity.split('\n').filter(l => l.trim());
|
|
408
|
+
for (const line of lines) {
|
|
409
|
+
const dateMatch = line.match(/###?\s*(\d{4}-\d{2}-\d{2}|\w+\s+\d+)/);
|
|
410
|
+
if (dateMatch) {
|
|
411
|
+
activityItems.push({ date: dateMatch[1], text: '' });
|
|
412
|
+
} else if (activityItems.length) {
|
|
413
|
+
activityItems[activityItems.length - 1].text += line.replace(/^[-โข*]\s*/, '').trim() + ' ';
|
|
414
|
+
} else {
|
|
415
|
+
activityItems.push({ date: '', text: line.replace(/^[-โข*]\s*/, '').trim() });
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
349
420
|
panel.innerHTML = \`
|
|
350
|
-
<div style="display:flex;gap:14px;align-items:center;margin-bottom:
|
|
421
|
+
<div style="display:flex;gap:14px;align-items:center;margin-bottom:12px">
|
|
351
422
|
<img src="\${avatarUrl}" alt="" style="width:56px;height:56px;border-radius:50%;object-fit:cover;border:2px solid #e2e8f0" onerror="this.style.display='none'">
|
|
352
423
|
<div>
|
|
353
424
|
<div style="font-size:1.3em;font-weight:700;color:#0f172a">\${card.name}</div>
|
|
354
425
|
<div style="font-size:0.85em;color:#64748b">\${card.title || ''}\${card.company ? ' @ ' + card.company : ''}</div>
|
|
355
|
-
\${card.previous_role ? \`<div style="font-size:0.75em;color:#94a3b8">Previously: \${card.previous_role}</div>\` : ''}
|
|
356
426
|
<div style="margin-top:4px">
|
|
357
427
|
\${card.twitter_handle ? \`<a href="https://twitter.com/\${card.twitter_handle}" target="_blank" style="color:#2563eb;font-size:0.8em;text-decoration:none;margin-right:8px">@\${card.twitter_handle}</a>\` : ''}
|
|
358
428
|
<span class="badge \${mbtiClass(card.mbti)}" title="\${card.mbti_reason || ''}">\${card.mbti || '?'}</span>
|
|
@@ -360,31 +430,45 @@ function openPanel(index) {
|
|
|
360
430
|
</div>
|
|
361
431
|
</div>
|
|
362
432
|
|
|
363
|
-
|
|
433
|
+
<div class="tabs">
|
|
434
|
+
<div class="tab active" onclick="switchTab('recent',this)">Latest</div>
|
|
435
|
+
<div class="tab" onclick="switchTab('facts',this)">Facts & Approach</div>
|
|
436
|
+
</div>
|
|
437
|
+
|
|
438
|
+
<!-- RECENT TAB -->
|
|
439
|
+
<div class="tab-content active" id="tab-recent">
|
|
440
|
+
\${card.latest_news ? \`<div class="news-chip" style="margin-bottom:12px">๐ฐ \${card.latest_news}</div>\` : ''}
|
|
441
|
+
|
|
442
|
+
\${tweets.length ? \`<h2>Latest Tweets</h2>\${tweets.slice(0,5).map(t => \`<div class="tweet">\${t.text}\${t.date ? \`<div class="tweet-date">\${t.date}\${t.link ? \` ยท <a href="\${t.link}" target="_blank" style="color:#2563eb">link</a>\` : ''}</div>\` : ''}</div>\`).join('')}\` : ''}
|
|
443
|
+
|
|
444
|
+
\${activityItems.length ? \`<h2>Recent Moves</h2>\${activityItems.slice(0,8).map(a => \`<div class="activity-item">\${a.date ? \`<span class="activity-date">\${a.date}</span> ยท \` : ''}\${a.text.trim()}</div>\`).join('')}\` : ''}
|
|
364
445
|
|
|
365
|
-
|
|
366
|
-
\${card.current_focus ? \`<p>๐ฏ \${card.current_focus}</p>\` : ''}
|
|
367
|
-
\${card.wants ? \`<p>๐ \${card.wants}</p>\` : ''}
|
|
446
|
+
\${card.key_quotes?.length ? \`<h2>In Their Words</h2>\${card.key_quotes.map(q => \`<blockquote>"\${q}"</blockquote>\`).join('')}\` : ''}
|
|
368
447
|
|
|
369
|
-
|
|
448
|
+
\${card.annotations?.length ? \`<h2>Your Notes</h2>\${card.annotations.map(a => \`<p style="font-size:0.8em;color:#64748b">๐ \${a}</p>\`).join('')}\` : ''}
|
|
449
|
+
</div>
|
|
370
450
|
|
|
371
|
-
|
|
451
|
+
<!-- FACTS TAB -->
|
|
452
|
+
<div class="tab-content" id="tab-facts">
|
|
453
|
+
<h2>Focus & Goals</h2>
|
|
454
|
+
\${card.current_focus ? \`<p>๐ฏ \${card.current_focus}</p>\` : ''}
|
|
455
|
+
\${card.wants ? \`<p>๐ \${card.wants}</p>\` : ''}
|
|
372
456
|
|
|
373
|
-
|
|
374
|
-
\${card.last_connection ? \`<p>๐ค <strong>Last:</strong> \${card.last_connection}</p>\` : '<p style="color:#94a3b8">No interactions yet</p>'}
|
|
375
|
-
\${card.dos?.length || card.donts?.length ? \`<div class="do-dont">\${card.dos?.length ? \`<div class="do-box"><strong>โ
Do</strong><br>\${card.dos.join('<br>')}</div>\` : ''}\${card.donts?.length ? \`<div class="dont-box"><strong>โ Don't</strong><br>\${card.donts.join('<br>')}</div>\` : ''}</div>\` : ''}
|
|
376
|
-
\${card.gifts?.length ? \`<p style="font-size:0.85em">๐ \${card.gifts.join(', ')}</p>\` : ''}
|
|
457
|
+
\${card.philosophy?.length ? \`<h2>Philosophy</h2>\${card.philosophy.map(p => \`<blockquote>"\${p}"</blockquote>\`).join('')}\` : ''}
|
|
377
458
|
|
|
378
|
-
|
|
459
|
+
\${card.talking_points?.length ? \`<h2>Talking Points</h2><ul>\${card.talking_points.map(t => \`<li>\${t}</li>\`).join('')}</ul>\` : ''}
|
|
379
460
|
|
|
380
|
-
|
|
461
|
+
<h2>Approach</h2>
|
|
462
|
+
\${card.last_connection ? \`<p>๐ค <strong>Last:</strong> \${card.last_connection}</p>\` : '<p style="color:#94a3b8">No interactions yet</p>'}
|
|
463
|
+
\${card.dos?.length || card.donts?.length ? \`<div class="do-dont">\${card.dos?.length ? \`<div class="do-box"><strong>โ
Do</strong><br>\${card.dos.join('<br>')}</div>\` : ''}\${card.donts?.length ? \`<div class="dont-box"><strong>โ Don't</strong><br>\${card.donts.join('<br>')}</div>\` : ''}</div>\` : ''}
|
|
464
|
+
\${card.gifts?.length ? \`<p style="font-size:0.85em">๐ \${card.gifts.join(', ')}</p>\` : ''}
|
|
381
465
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
466
|
+
\${card.allies?.length || card.rivals?.length ? \`<h2>Relationships</h2>
|
|
467
|
+
\${card.allies?.length ? \`<div style="margin-bottom:8px">\${card.allies.map(a => \`<p style="font-size:0.85em;color:#166534">๐ <strong>\${a.name || a}</strong>\${a.reason ? \` โ \${a.reason}\` : ''}</p>\`).join('')}</div>\` : ''}
|
|
468
|
+
\${card.rivals?.length ? \`<div>\${card.rivals.map(r => \`<p style="font-size:0.85em;color:#991b1b">๐ <strong>\${r.name || r}</strong>\${r.reason ? \` โ \${r.reason}\` : ''}</p>\`).join('')}</div>\` : ''}
|
|
469
|
+
\` : ''}
|
|
386
470
|
|
|
387
|
-
|
|
471
|
+
</div>
|
|
388
472
|
|
|
389
473
|
<div style="display:flex;gap:8px;margin-top:20px">
|
|
390
474
|
\${card.slug ? \`<a href="\${card.slug}.html" style="flex:1;text-align:center;color:#2563eb;text-decoration:none;padding:8px;border:1px solid #2563eb;border-radius:6px;font-size:0.85em">Full Profile</a>\` : ''}
|
|
@@ -396,6 +480,13 @@ function openPanel(index) {
|
|
|
396
480
|
document.getElementById('main').classList.add('panel-open');
|
|
397
481
|
}
|
|
398
482
|
|
|
483
|
+
function switchTab(tabId, el) {
|
|
484
|
+
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
|
485
|
+
document.querySelectorAll('.tab-content').forEach(t => t.classList.remove('active'));
|
|
486
|
+
el.classList.add('active');
|
|
487
|
+
document.getElementById('tab-' + tabId).classList.add('active');
|
|
488
|
+
}
|
|
489
|
+
|
|
399
490
|
function closePanel() {
|
|
400
491
|
document.getElementById('panel').classList.remove('open');
|
|
401
492
|
document.getElementById('main').classList.remove('panel-open');
|
package/lib/templates.js
CHANGED
|
@@ -65,8 +65,18 @@ synthesize an actionable profile focused on HOW to work with this person.
|
|
|
65
65
|
|
|
66
66
|
---
|
|
67
67
|
|
|
68
|
-
##
|
|
69
|
-
|
|
68
|
+
## Relationships & Alliances
|
|
69
|
+
|
|
70
|
+
**Allies / People they like:**
|
|
71
|
+
- {Person 1} โ {why: shared values, mutual support, public praise, partnership, etc.}
|
|
72
|
+
- {Person 2} โ {why}
|
|
73
|
+
|
|
74
|
+
**Rivals / People they clash with:**
|
|
75
|
+
- {Person 1} โ {why: public disagreement, competitive tension, ideological difference, etc.}
|
|
76
|
+
- {Person 2} โ {why}
|
|
77
|
+
|
|
78
|
+
**Key relationships:**
|
|
79
|
+
- {Notable relationship with context โ mentor, investor, partner, adversary}
|
|
70
80
|
|
|
71
81
|
---
|
|
72
82
|
|
|
@@ -165,7 +175,8 @@ IMPORTANT: After the profile, output a JSON metadata block in EXACTLY this forma
|
|
|
165
175
|
"dos": ["{do1}", "{do2}", "{do3}"],
|
|
166
176
|
"donts": ["{dont1}", "{dont2}", "{dont3}"],
|
|
167
177
|
"gifts": ["{gift idea 1}", "{gift idea 2}"],
|
|
168
|
-
"
|
|
178
|
+
"allies": [{"name": "{person}", "reason": "{why they like them}"}],
|
|
179
|
+
"rivals": [{"name": "{person}", "reason": "{why they clash}"}],
|
|
169
180
|
"twitter_handle": "{handle without @}"
|
|
170
181
|
}
|
|
171
182
|
-->`;
|