vipcare 0.3.25 → 0.3.26
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 +64 -49
- package/lib/synthesizer.js +1 -1
- package/package.json +1 -1
package/lib/card.js
CHANGED
|
@@ -24,9 +24,12 @@ export function generateCards(profiles, outputPath) {
|
|
|
24
24
|
const content = loadProfile(p.slug);
|
|
25
25
|
if (!content) continue;
|
|
26
26
|
|
|
27
|
+
// Extract twitter handle from content
|
|
28
|
+
const twMatch = content.match(/twitter\.com\/(\w+)/i) || content.match(/x\.com\/(\w+)/i);
|
|
29
|
+
const twitterHandle = twMatch ? twMatch[1] : null;
|
|
30
|
+
|
|
27
31
|
const data = extractVipData(content);
|
|
28
32
|
if (!data) {
|
|
29
|
-
// Fallback: parse what we can from markdown headers
|
|
30
33
|
const nameMatch = content.match(/^# (.+)$/m);
|
|
31
34
|
const summaryMatch = content.match(/^> (.+)$/m);
|
|
32
35
|
const titleMatch = content.match(/\*\*Title:\*\*\s*(.+)/);
|
|
@@ -36,11 +39,11 @@ export function generateCards(profiles, outputPath) {
|
|
|
36
39
|
|
|
37
40
|
cards.push({
|
|
38
41
|
slug: p.slug,
|
|
42
|
+
twitter_handle: twitterHandle,
|
|
39
43
|
name: nameMatch ? nameMatch[1] : p.name,
|
|
40
44
|
title: titleMatch ? titleMatch[1].trim() : '',
|
|
41
45
|
company: companyMatch ? companyMatch[1].trim() : '',
|
|
42
46
|
location: locationMatch ? locationMatch[1].trim() : '',
|
|
43
|
-
disc: '?',
|
|
44
47
|
mbti: '?',
|
|
45
48
|
scores: {},
|
|
46
49
|
tags: industryMatch ? [industryMatch[1].trim()] : [],
|
|
@@ -56,6 +59,7 @@ export function generateCards(profiles, outputPath) {
|
|
|
56
59
|
}
|
|
57
60
|
|
|
58
61
|
data.slug = p.slug;
|
|
62
|
+
data.twitter_handle = data.twitter_handle || twitterHandle;
|
|
59
63
|
cards.push(data);
|
|
60
64
|
}
|
|
61
65
|
|
|
@@ -96,18 +100,18 @@ function buildProfilePage(name, markdown) {
|
|
|
96
100
|
<title>${escapeHtml(name)} - VIPCare</title>
|
|
97
101
|
<style>
|
|
98
102
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
99
|
-
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #
|
|
100
|
-
a { color: #
|
|
103
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f8fafc; color: #1e293b; padding: 24px; max-width: 800px; margin: 0 auto; line-height: 1.7; }
|
|
104
|
+
a { color: #2563eb; text-decoration: none; }
|
|
101
105
|
a:hover { text-decoration: underline; }
|
|
102
106
|
.back { display: inline-block; margin-bottom: 20px; font-size: 0.9em; }
|
|
103
|
-
h1 { color: #
|
|
104
|
-
h2 { color: #
|
|
105
|
-
blockquote { color: #
|
|
106
|
-
p, li { color: #
|
|
107
|
-
strong { color: #
|
|
107
|
+
h1 { color: #2563eb; font-size: 2em; margin: 16px 0 8px; }
|
|
108
|
+
h2 { color: #7c3aed; font-size: 1.3em; margin: 24px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 6px; }
|
|
109
|
+
blockquote { color: #d97706; font-style: italic; padding: 8px 16px; border-left: 3px solid #e2e8f0; margin: 8px 0; }
|
|
110
|
+
p, li { color: #475569; margin: 4px 0; }
|
|
111
|
+
strong { color: #0f172a; }
|
|
108
112
|
li { margin-left: 20px; }
|
|
109
|
-
hr { border: none; border-top: 1px solid #
|
|
110
|
-
.meta { color: #
|
|
113
|
+
hr { border: none; border-top: 1px solid #e2e8f0; margin: 24px 0; }
|
|
114
|
+
.meta { color: #94a3b8; font-size: 0.85em; }
|
|
111
115
|
</style>
|
|
112
116
|
</head>
|
|
113
117
|
<body>
|
|
@@ -138,15 +142,15 @@ function buildHtml(cards) {
|
|
|
138
142
|
<style>
|
|
139
143
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
140
144
|
html { scroll-behavior: smooth; }
|
|
141
|
-
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #
|
|
142
|
-
h1 { text-align: center; font-size: 1.8em; margin: 20px 0 30px; color: #
|
|
145
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f8fafc; color: #1e293b; min-height: 100vh; padding: 20px; padding: max(20px, env(safe-area-inset-top)) max(20px, env(safe-area-inset-right)) max(20px, env(safe-area-inset-bottom)) max(20px, env(safe-area-inset-left)); }
|
|
146
|
+
h1 { text-align: center; font-size: 1.8em; margin: 20px 0 30px; color: #2563eb; }
|
|
143
147
|
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; max-width: 1200px; margin: 0 auto; }
|
|
144
148
|
|
|
145
149
|
.card {
|
|
146
|
-
background:
|
|
150
|
+
background: #ffffff;
|
|
147
151
|
border-radius: 16px;
|
|
148
152
|
padding: 24px;
|
|
149
|
-
border: 1px solid #
|
|
153
|
+
border: 1px solid #e2e8f0;
|
|
150
154
|
cursor: pointer;
|
|
151
155
|
transition: transform 0.2s, box-shadow 0.2s;
|
|
152
156
|
position: relative;
|
|
@@ -154,53 +158,53 @@ h1 { text-align: center; font-size: 1.8em; margin: 20px 0 30px; color: #38bdf8;
|
|
|
154
158
|
min-height: 44px;
|
|
155
159
|
-webkit-tap-highlight-color: transparent;
|
|
156
160
|
}
|
|
157
|
-
.card:hover { transform: translateY(-4px); box-shadow: 0 12px 40px rgba(
|
|
158
|
-
.card:active { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(
|
|
161
|
+
.card:hover { transform: translateY(-4px); box-shadow: 0 12px 40px rgba(37,99,235,0.12); }
|
|
162
|
+
.card:active { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(37,99,235,0.08); }
|
|
159
163
|
.card::before {
|
|
160
164
|
content: '';
|
|
161
165
|
position: absolute;
|
|
162
166
|
top: 0; left: 0; right: 0;
|
|
163
167
|
height: 4px;
|
|
164
|
-
background: linear-gradient(90deg, #
|
|
168
|
+
background: linear-gradient(90deg, #2563eb, #7c3aed, #db2777);
|
|
165
169
|
}
|
|
166
170
|
|
|
167
171
|
.card-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 12px; }
|
|
168
|
-
.card-name { font-size: 1.4em; font-weight: 700; color: #
|
|
169
|
-
.card-role { font-size: 0.85em; color: #
|
|
172
|
+
.card-name { font-size: 1.4em; font-weight: 700; color: #0f172a; }
|
|
173
|
+
.card-role { font-size: 0.85em; color: #64748b; margin-top: 2px; }
|
|
170
174
|
.card-badges { display: flex; gap: 6px; }
|
|
171
175
|
.badge { padding: 4px 10px; border-radius: 6px; font-size: 0.75em; font-weight: 700; min-height: 28px; display: inline-flex; align-items: center; }
|
|
172
|
-
.badge-disc { background: #
|
|
173
|
-
.badge-mbti { background: #
|
|
176
|
+
.badge-disc { background: #2563eb; color: #ffffff; }
|
|
177
|
+
.badge-mbti { background: #7c3aed; color: #ffffff; }
|
|
174
178
|
|
|
175
|
-
.card-quote { font-style: italic; color: #
|
|
179
|
+
.card-quote { font-style: italic; color: #64748b; font-size: 0.8em; margin: 10px 0; padding: 8px 12px; border-left: 3px solid #e2e8f0; }
|
|
176
180
|
|
|
177
181
|
.radar-container { display: flex; justify-content: center; margin: 16px 0; }
|
|
178
182
|
.radar { width: 200px; height: 200px; max-width: 100%; }
|
|
179
183
|
|
|
180
184
|
.tags { display: flex; flex-wrap: wrap; gap: 6px; margin: 12px 0; }
|
|
181
|
-
.tag { background: #
|
|
185
|
+
.tag { background: #eff6ff; color: #2563eb; padding: 4px 12px; border-radius: 12px; font-size: 0.75em; min-height: 28px; display: inline-flex; align-items: center; }
|
|
182
186
|
|
|
183
187
|
.expertise { margin: 10px 0; }
|
|
184
|
-
.expertise-title { font-size: 0.75em; color: #
|
|
185
|
-
.expertise-item { font-size: 0.8em; color: #
|
|
186
|
-
.superpower { color: #
|
|
188
|
+
.expertise-title { font-size: 0.75em; color: #94a3b8; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 6px; }
|
|
189
|
+
.expertise-item { font-size: 0.8em; color: #475569; padding: 2px 0; }
|
|
190
|
+
.superpower { color: #d97706; font-weight: 600; font-size: 0.85em; margin: 6px 0; }
|
|
187
191
|
|
|
188
|
-
.tips { margin-top: 12px; border-top: 1px solid #
|
|
189
|
-
.tip-row { display: flex; gap: 4px; font-size: 0.8em; margin: 4px 0; color: #
|
|
192
|
+
.tips { margin-top: 12px; border-top: 1px solid #e2e8f0; padding-top: 12px; }
|
|
193
|
+
.tip-row { display: flex; gap: 4px; font-size: 0.8em; margin: 4px 0; color: #475569; min-height: 44px; align-items: center; }
|
|
190
194
|
.tip-icon { width: 20px; text-align: center; }
|
|
191
|
-
.tip-label { color: #
|
|
195
|
+
.tip-label { color: #94a3b8; min-width: 55px; }
|
|
192
196
|
|
|
193
197
|
/* Modal */
|
|
194
|
-
.modal-overlay { display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.
|
|
198
|
+
.modal-overlay { display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.3); z-index: 100; justify-content: center; align-items: center; padding: 20px; }
|
|
195
199
|
.modal-overlay.active { display: flex; }
|
|
196
200
|
.modal {
|
|
197
|
-
background: #
|
|
198
|
-
border: 1px solid #
|
|
201
|
+
background: #ffffff; border-radius: 16px; max-width: 600px; width: 100%; max-height: 90vh; overflow-y: auto; padding: 32px;
|
|
202
|
+
border: 1px solid #e2e8f0; box-shadow: 0 25px 50px rgba(0,0,0,0.15);
|
|
199
203
|
-webkit-overflow-scrolling: touch;
|
|
200
204
|
}
|
|
201
205
|
.modal-close { float: right; background: none; border: none; color: #94a3b8; font-size: 1.5em; cursor: pointer; min-width: 44px; min-height: 44px; display: inline-flex; align-items: center; justify-content: center; }
|
|
202
|
-
.modal h2 { color: #
|
|
203
|
-
.modal p, .modal li { color: #
|
|
206
|
+
.modal h2 { color: #2563eb; margin: 16px 0 8px; font-size: 1.1em; }
|
|
207
|
+
.modal p, .modal li { color: #475569; font-size: 0.9em; line-height: 1.6; }
|
|
204
208
|
.modal ul { padding-left: 20px; }
|
|
205
209
|
|
|
206
210
|
/* Mobile: screens < 480px */
|
|
@@ -262,7 +266,7 @@ function radarSvg(scores, size = 200) {
|
|
|
262
266
|
const angle = (Math.PI * 2 * i / n) - Math.PI / 2;
|
|
263
267
|
pts.push(\`\${cx + lr * Math.cos(angle)},\${cy + lr * Math.sin(angle)}\`);
|
|
264
268
|
}
|
|
265
|
-
gridLines += \`<polygon points="\${pts.join(' ')}" fill="none" stroke="#
|
|
269
|
+
gridLines += \`<polygon points="\${pts.join(' ')}" fill="none" stroke="#e2e8f0" stroke-width="0.5"/>\`;
|
|
266
270
|
}
|
|
267
271
|
|
|
268
272
|
let axes = '', labels = '', dataPoints = [];
|
|
@@ -270,7 +274,7 @@ function radarSvg(scores, size = 200) {
|
|
|
270
274
|
const angle = (Math.PI * 2 * i / n) - Math.PI / 2;
|
|
271
275
|
const x = cx + r * Math.cos(angle);
|
|
272
276
|
const y = cy + r * Math.sin(angle);
|
|
273
|
-
axes += \`<line x1="\${cx}" y1="\${cy}" x2="\${x}" y2="\${y}" stroke="#
|
|
277
|
+
axes += \`<line x1="\${cx}" y1="\${cy}" x2="\${x}" y2="\${y}" stroke="#e2e8f0" stroke-width="0.5"/>\`;
|
|
274
278
|
|
|
275
279
|
const lx = cx + (r + 22) * Math.cos(angle);
|
|
276
280
|
const ly = cy + (r + 22) * Math.sin(angle);
|
|
@@ -283,7 +287,7 @@ function radarSvg(scores, size = 200) {
|
|
|
283
287
|
dataPoints.push(\`\${dx},\${dy}\`);
|
|
284
288
|
}
|
|
285
289
|
|
|
286
|
-
const dataPolygon = \`<polygon points="\${dataPoints.join(' ')}" fill="rgba(
|
|
290
|
+
const dataPolygon = \`<polygon points="\${dataPoints.join(' ')}" fill="rgba(37,99,235,0.15)" stroke="#2563eb" stroke-width="1.5"/>\`;
|
|
287
291
|
|
|
288
292
|
return \`<svg viewBox="0 0 \${size} \${size}" class="radar">\${gridLines}\${axes}\${dataPolygon}\${labels}</svg>\`;
|
|
289
293
|
}
|
|
@@ -292,17 +296,20 @@ function renderCard(card, index) {
|
|
|
292
296
|
const scores = card.scores || {};
|
|
293
297
|
const radar = radarSvg(scores);
|
|
294
298
|
|
|
299
|
+
const twitterUrl = card.twitter || (card.slug ? \`https://unavatar.io/twitter/\${card.slug}\` : '');
|
|
300
|
+
const avatarUrl = \`https://unavatar.io/twitter/\${card.twitter_handle || card.slug || 'unknown'}\`;
|
|
301
|
+
|
|
295
302
|
return \`
|
|
296
303
|
<div class="card" onclick="openModal(\${index})">
|
|
297
304
|
<div class="card-header">
|
|
298
|
-
<div>
|
|
299
|
-
<
|
|
300
|
-
<div
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
<span class="badge badge-mbti">\${card.mbti || '?'}</span>
|
|
305
|
+
<div style="display:flex;gap:12px;align-items:center">
|
|
306
|
+
<img src="\${avatarUrl}" alt="" style="width:48px;height:48px;border-radius:50%;object-fit:cover;border:2px solid #e2e8f0" onerror="this.style.display='none'">
|
|
307
|
+
<div>
|
|
308
|
+
<div class="card-name">\${card.name || 'Unknown'}</div>
|
|
309
|
+
<div class="card-role">\${card.title || ''}\${card.company ? ' @ ' + card.company : ''}</div>
|
|
310
|
+
</div>
|
|
305
311
|
</div>
|
|
312
|
+
<span class="badge badge-mbti">\${card.mbti || '?'}</span>
|
|
306
313
|
</div>
|
|
307
314
|
\${card.quote ? \`<div class="card-quote">"\${card.quote.slice(0, 120)}\${card.quote.length > 120 ? '...' : ''}"</div>\` : ''}
|
|
308
315
|
<div class="radar-container">\${radar}</div>
|
|
@@ -321,14 +328,22 @@ function openModal(index) {
|
|
|
321
328
|
const s = card.scores || {};
|
|
322
329
|
const modal = document.getElementById('modal-content');
|
|
323
330
|
|
|
331
|
+
const avatarUrl = \`https://unavatar.io/twitter/\${card.twitter_handle || card.slug || 'unknown'}\`;
|
|
332
|
+
|
|
324
333
|
modal.innerHTML = \`
|
|
325
334
|
<button class="modal-close" onclick="closeModal()">×</button>
|
|
326
|
-
<
|
|
327
|
-
|
|
335
|
+
<div style="display:flex;gap:16px;align-items:center;margin-bottom:12px">
|
|
336
|
+
<img src="\${avatarUrl}" alt="" style="width:64px;height:64px;border-radius:50%;object-fit:cover;border:2px solid #e2e8f0" onerror="this.style.display='none'">
|
|
337
|
+
<div>
|
|
338
|
+
<h1 style="color:#2563eb;margin-bottom:4px;font-size:1.5em">\${card.name}</h1>
|
|
339
|
+
<p style="color:#64748b">\${card.title || ''}\${card.company ? ' @ ' + card.company : ''}\${card.location ? ' · ' + card.location : ''}</p>
|
|
340
|
+
\${card.twitter_handle ? \`<a href="https://twitter.com/\${card.twitter_handle}" target="_blank" style="color:#2563eb;font-size:0.85em;text-decoration:none">@\${card.twitter_handle}</a>\` : ''}
|
|
341
|
+
</div>
|
|
342
|
+
</div>
|
|
328
343
|
\${card.quote ? \`<div class="card-quote" style="margin:16px 0">"\${card.quote}"</div>\` : ''}
|
|
329
344
|
|
|
330
345
|
<h2>Personality</h2>
|
|
331
|
-
<p><strong>
|
|
346
|
+
<p><strong>MBTI:</strong> \${card.mbti || '?'}</p>
|
|
332
347
|
<div style="display:flex;justify-content:center;margin:16px 0">\${radarSvg(s, 260)}</div>
|
|
333
348
|
|
|
334
349
|
\${card.expertise?.length ? \`<h2>Expertise</h2><ul>\${card.expertise.map(e => \`<li>\${e}</li>\`).join('')}</ul>\` : ''}
|
|
@@ -342,7 +357,7 @@ function openModal(index) {
|
|
|
342
357
|
|
|
343
358
|
\${card.tags?.length ? \`<h2>Tags</h2><div class="tags">\${card.tags.map(t => \`<span class="tag">\${t}</span>\`).join('')}</div>\` : ''}
|
|
344
359
|
|
|
345
|
-
\${card.slug ? \`<p style="margin-top:20px;text-align:center"><a href="\${card.slug}.html" style="color:#
|
|
360
|
+
\${card.slug ? \`<p style="margin-top:20px;text-align:center"><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></p>\` : ''}
|
|
346
361
|
\`;
|
|
347
362
|
|
|
348
363
|
document.getElementById('modal').classList.add('active');
|
package/lib/synthesizer.js
CHANGED
|
@@ -33,7 +33,7 @@ function copilotAvailable() {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
function callClaudeCli(prompt, timeout =
|
|
36
|
+
function callClaudeCli(prompt, timeout = 300000) {
|
|
37
37
|
// Write prompt to temp file, pass via -p flag reading from file
|
|
38
38
|
const tmpFile = path.join(os.tmpdir(), `vip-prompt-${Date.now()}.txt`);
|
|
39
39
|
try {
|