vipcare 0.5.1 โ 0.6.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 +142 -155
- package/lib/templates.js +10 -1
- package/package.json +1 -1
package/lib/card.js
CHANGED
|
@@ -72,6 +72,7 @@ export function generateCards(profiles, outputPath) {
|
|
|
72
72
|
expertise: [],
|
|
73
73
|
superpower: '',
|
|
74
74
|
quote: summaryMatch ? summaryMatch[1] : (p.summary || ''),
|
|
75
|
+
updated: p.updated,
|
|
75
76
|
annotations,
|
|
76
77
|
last_connection: lastConnection,
|
|
77
78
|
});
|
|
@@ -79,6 +80,7 @@ export function generateCards(profiles, outputPath) {
|
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
data.slug = p.slug;
|
|
83
|
+
data.updated = p.updated;
|
|
82
84
|
data.twitter_handle = data.twitter_handle || twitterHandle;
|
|
83
85
|
data.annotations = annotations;
|
|
84
86
|
data.last_connection = lastConnection;
|
|
@@ -160,103 +162,86 @@ function buildHtml(cards) {
|
|
|
160
162
|
<head>
|
|
161
163
|
<meta charset="UTF-8">
|
|
162
164
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
163
|
-
<title>VIPCare
|
|
165
|
+
<title>VIPCare</title>
|
|
164
166
|
<style>
|
|
165
167
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
166
168
|
html { scroll-behavior: smooth; }
|
|
167
|
-
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f8fafc; color: #1e293b; min-height: 100vh;
|
|
168
|
-
|
|
169
|
-
.
|
|
169
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f8fafc; color: #1e293b; min-height: 100vh; }
|
|
170
|
+
.layout { display: flex; min-height: 100vh; }
|
|
171
|
+
.main { flex: 1; padding: 20px; overflow-y: auto; transition: margin-right 0.3s; }
|
|
172
|
+
.main.panel-open { margin-right: 420px; }
|
|
173
|
+
header { display: flex; justify-content: space-between; align-items: center; max-width: 1200px; margin: 0 auto 24px; }
|
|
174
|
+
header h1 { font-size: 1.5em; color: #2563eb; }
|
|
175
|
+
header .count { color: #94a3b8; font-size: 0.85em; }
|
|
176
|
+
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 16px; max-width: 1200px; margin: 0 auto; }
|
|
170
177
|
|
|
171
178
|
.card {
|
|
172
179
|
background: #ffffff;
|
|
173
|
-
border-radius:
|
|
174
|
-
padding:
|
|
180
|
+
border-radius: 12px;
|
|
181
|
+
padding: 20px;
|
|
175
182
|
border: 1px solid #e2e8f0;
|
|
176
183
|
cursor: pointer;
|
|
177
|
-
transition:
|
|
184
|
+
transition: all 0.2s;
|
|
178
185
|
position: relative;
|
|
179
|
-
overflow: hidden;
|
|
180
|
-
min-height: 44px;
|
|
181
|
-
-webkit-tap-highlight-color: transparent;
|
|
182
186
|
}
|
|
183
|
-
.card:hover {
|
|
184
|
-
.card
|
|
185
|
-
.card
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
.
|
|
194
|
-
.
|
|
195
|
-
.
|
|
196
|
-
.
|
|
197
|
-
.
|
|
198
|
-
.
|
|
199
|
-
.
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
.expertise { margin: 10px 0; }
|
|
210
|
-
.expertise-title { font-size: 0.75em; color: #94a3b8; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 6px; }
|
|
211
|
-
.expertise-item { font-size: 0.8em; color: #475569; padding: 2px 0; }
|
|
212
|
-
.superpower { color: #d97706; font-weight: 600; font-size: 0.85em; margin: 6px 0; }
|
|
213
|
-
|
|
214
|
-
.tips { margin-top: 12px; border-top: 1px solid #e2e8f0; padding-top: 12px; }
|
|
215
|
-
.tip-row { display: flex; gap: 4px; font-size: 0.8em; margin: 4px 0; color: #475569; min-height: 44px; align-items: center; }
|
|
216
|
-
.tip-icon { width: 20px; text-align: center; }
|
|
217
|
-
.tip-label { color: #94a3b8; min-width: 55px; }
|
|
218
|
-
|
|
219
|
-
/* Modal */
|
|
220
|
-
.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; }
|
|
221
|
-
.modal-overlay.active { display: flex; }
|
|
222
|
-
.modal {
|
|
223
|
-
background: #ffffff; border-radius: 16px; max-width: 600px; width: 100%; max-height: 90vh; overflow-y: auto; padding: 32px;
|
|
224
|
-
border: 1px solid #e2e8f0; box-shadow: 0 25px 50px rgba(0,0,0,0.15);
|
|
187
|
+
.card:hover { box-shadow: 0 4px 20px rgba(0,0,0,0.08); border-color: #cbd5e1; }
|
|
188
|
+
.card.active { border-color: #2563eb; box-shadow: 0 0 0 2px rgba(37,99,235,0.2); }
|
|
189
|
+
.card-name { font-size: 1.1em; font-weight: 700; color: #0f172a; }
|
|
190
|
+
.card-role { font-size: 0.8em; color: #64748b; margin-top: 2px; }
|
|
191
|
+
.badge { padding: 3px 8px; border-radius: 4px; font-size: 0.7em; font-weight: 700; display: inline-flex; align-items: center; cursor: help; }
|
|
192
|
+
.badge-nt { background: #ede9fe; color: #7c3aed; }
|
|
193
|
+
.badge-nf { background: #d1fae5; color: #059669; }
|
|
194
|
+
.badge-sj { background: #dbeafe; color: #2563eb; }
|
|
195
|
+
.badge-sp { background: #fef3c7; color: #d97706; }
|
|
196
|
+
.badge-unknown { background: #f1f5f9; color: #94a3b8; }
|
|
197
|
+
.news-chip { background: #f0f9ff; padding: 6px 10px; border-radius: 6px; font-size: 0.75em; color: #1e40af; margin: 8px 0; }
|
|
198
|
+
.section-label { font-size: 0.65em; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.5px; margin: 10px 0 4px; }
|
|
199
|
+
.tags { display: flex; flex-wrap: wrap; gap: 4px; margin: 8px 0; }
|
|
200
|
+
.tag { background: #f1f5f9; color: #64748b; padding: 2px 8px; border-radius: 4px; font-size: 0.7em; }
|
|
201
|
+
.do-dont { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin: 8px 0; }
|
|
202
|
+
.do-box, .dont-box { padding: 8px; border-radius: 6px; font-size: 0.75em; }
|
|
203
|
+
.do-box { background: #f0fdf4; color: #166534; }
|
|
204
|
+
.dont-box { background: #fef2f2; color: #991b1b; }
|
|
205
|
+
|
|
206
|
+
/* Side Panel */
|
|
207
|
+
.panel {
|
|
208
|
+
position: fixed; top: 0; right: -440px; width: 420px; height: 100vh;
|
|
209
|
+
background: #ffffff; border-left: 1px solid #e2e8f0;
|
|
210
|
+
overflow-y: auto; transition: right 0.3s ease;
|
|
211
|
+
box-shadow: -4px 0 20px rgba(0,0,0,0.05);
|
|
212
|
+
z-index: 50; padding: 24px;
|
|
225
213
|
-webkit-overflow-scrolling: touch;
|
|
226
214
|
}
|
|
227
|
-
.
|
|
228
|
-
.
|
|
229
|
-
.
|
|
230
|
-
.
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
.
|
|
239
|
-
.
|
|
240
|
-
.
|
|
241
|
-
.tip-row { font-size: 0.85em; }
|
|
242
|
-
.radar { width: 180px; height: 180px; }
|
|
243
|
-
.badge { font-size: 0.8em; padding: 5px 12px; }
|
|
244
|
-
.tag { font-size: 0.8em; padding: 5px 14px; }
|
|
245
|
-
|
|
246
|
-
.modal-overlay { padding: 0; align-items: stretch; }
|
|
247
|
-
.modal { max-width: 100%; max-height: 100vh; height: 100%; border-radius: 0; padding: 20px; padding-top: max(20px, env(safe-area-inset-top)); padding-bottom: max(20px, env(safe-area-inset-bottom)); }
|
|
248
|
-
.modal h2 { font-size: 1.15em; }
|
|
249
|
-
.modal p, .modal li { font-size: 0.95em; line-height: 1.7; }
|
|
215
|
+
.panel.open { right: 0; }
|
|
216
|
+
.panel-close { position: absolute; top: 16px; right: 16px; background: none; border: none; color: #94a3b8; font-size: 1.2em; cursor: pointer; width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; border-radius: 6px; }
|
|
217
|
+
.panel-close:hover { background: #f1f5f9; color: #475569; }
|
|
218
|
+
.panel h2 { color: #2563eb; font-size: 0.9em; margin: 20px 0 8px; padding-bottom: 4px; border-bottom: 1px solid #f1f5f9; }
|
|
219
|
+
.panel p, .panel li { color: #475569; font-size: 0.85em; line-height: 1.6; }
|
|
220
|
+
.panel ul { padding-left: 16px; }
|
|
221
|
+
.panel blockquote { border-left: 3px solid #2563eb; padding: 4px 12px; margin: 6px 0; color: #475569; font-style: italic; font-size: 0.85em; }
|
|
222
|
+
.radar { max-width: 100%; }
|
|
223
|
+
|
|
224
|
+
/* Mobile */
|
|
225
|
+
@media (max-width: 768px) {
|
|
226
|
+
.main.panel-open { margin-right: 0; }
|
|
227
|
+
.panel { width: 100%; right: -100%; }
|
|
228
|
+
.grid { grid-template-columns: 1fr; }
|
|
250
229
|
}
|
|
251
230
|
</style>
|
|
252
231
|
</head>
|
|
253
232
|
<body>
|
|
254
|
-
|
|
255
|
-
<
|
|
256
|
-
<
|
|
257
|
-
|
|
258
|
-
<
|
|
259
|
-
|
|
233
|
+
<div class="layout">
|
|
234
|
+
<div class="main" id="main">
|
|
235
|
+
<header>
|
|
236
|
+
<h1>VIPCare</h1>
|
|
237
|
+
<span class="count" id="count"></span>
|
|
238
|
+
</header>
|
|
239
|
+
<div class="grid" id="grid"></div>
|
|
240
|
+
</div>
|
|
241
|
+
<div class="panel" id="panel">
|
|
242
|
+
<button class="panel-close" onclick="closePanel()">×</button>
|
|
243
|
+
<div id="panel-content"></div>
|
|
244
|
+
</div>
|
|
260
245
|
</div>
|
|
261
246
|
|
|
262
247
|
<script>
|
|
@@ -314,124 +299,126 @@ function radarSvg(scores, size = 200) {
|
|
|
314
299
|
return \`<svg viewBox="0 0 \${size} \${size}" class="radar">\${gridLines}\${axes}\${dataPolygon}\${labels}</svg>\`;
|
|
315
300
|
}
|
|
316
301
|
|
|
302
|
+
function mbtiClass(mbti) {
|
|
303
|
+
if (!mbti || mbti === '?') return 'badge-unknown';
|
|
304
|
+
const t = mbti.toUpperCase();
|
|
305
|
+
if (t.includes('NT')) return 'badge-nt';
|
|
306
|
+
if (t.includes('NF')) return 'badge-nf';
|
|
307
|
+
if (t[1] === 'S' && t[3] === 'J') return 'badge-sj';
|
|
308
|
+
if (t[1] === 'S' && t[3] === 'P') return 'badge-sp';
|
|
309
|
+
return 'badge-nt';
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
let activeCard = null;
|
|
313
|
+
|
|
317
314
|
function renderCard(card, index) {
|
|
318
315
|
const avatarUrl = \`https://unavatar.io/twitter/\${card.twitter_handle || card.slug || 'unknown'}\`;
|
|
319
|
-
|
|
320
316
|
return \`
|
|
321
|
-
<div class="card" onclick="
|
|
322
|
-
<div
|
|
323
|
-
<
|
|
324
|
-
|
|
325
|
-
<div>
|
|
326
|
-
|
|
327
|
-
<div class="card-role">\${card.one_liner || card.title || ''}</div>
|
|
328
|
-
</div>
|
|
317
|
+
<div class="card" id="card-\${index}" onclick="openPanel(\${index})">
|
|
318
|
+
<div style="display:flex;gap:10px;align-items:center;margin-bottom:8px">
|
|
319
|
+
<img src="\${avatarUrl}" alt="" style="width:40px;height:40px;border-radius:50%;object-fit:cover;border:2px solid #e2e8f0" onerror="this.style.display='none'">
|
|
320
|
+
<div style="flex:1;min-width:0">
|
|
321
|
+
<div class="card-name">\${card.name || 'Unknown'}</div>
|
|
322
|
+
<div class="card-role">\${card.one_liner || card.title || ''}</div>
|
|
329
323
|
</div>
|
|
330
|
-
<span class="badge
|
|
324
|
+
<span class="badge \${mbtiClass(card.mbti)}" title="\${card.mbti_reason || ''}">\${card.mbti || '?'}</span>
|
|
331
325
|
</div>
|
|
332
|
-
\${card.latest_news ? \`<div
|
|
333
|
-
\${card.current_focus ? \`<div style="font-size:0.
|
|
334
|
-
\${card.
|
|
335
|
-
\${card.
|
|
336
|
-
\${card.
|
|
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>
|
|
326
|
+
\${card.latest_news ? \`<div class="news-chip">๐ฐ \${card.latest_news.slice(0, 100)}\${card.latest_news.length > 100 ? '...' : ''}</div>\` : ''}
|
|
327
|
+
\${card.current_focus ? \`<div style="font-size:0.8em;color:#475569;margin:4px 0">๐ฏ \${card.current_focus}</div>\` : ''}
|
|
328
|
+
\${card.last_connection ? \`<div style="font-size:0.75em;color:#2563eb;margin:4px 0">๐ค \${card.last_connection}</div>\` : (card.icebreakers?.[0] ? \`<div style="font-size:0.75em;color:#94a3b8;margin:4px 0">๐ก \${card.icebreakers[0]}</div>\` : '')}
|
|
329
|
+
\${card.talking_points?.length ? \`<div class="section-label">Talking Points</div>\${card.talking_points.slice(0,2).map(t => \`<div style="font-size:0.75em;color:#475569">โข \${t}</div>\`).join('')}\` : ''}
|
|
330
|
+
\${card.tags?.length ? \`<div class="tags">\${card.tags.slice(0,3).map(t => \`<span class="tag">\${t}</span>\`).join('')}</div>\` : ''}
|
|
339
331
|
</div>\`;
|
|
340
332
|
}
|
|
341
333
|
|
|
342
|
-
function
|
|
334
|
+
function openPanel(index) {
|
|
335
|
+
// Highlight active card
|
|
336
|
+
if (activeCard !== null) document.getElementById('card-' + activeCard)?.classList.remove('active');
|
|
337
|
+
activeCard = index;
|
|
338
|
+
document.getElementById('card-' + index)?.classList.add('active');
|
|
339
|
+
|
|
343
340
|
const card = cards[index];
|
|
344
341
|
const s = card.scores || {};
|
|
345
|
-
const
|
|
346
|
-
|
|
342
|
+
const panel = document.getElementById('panel-content');
|
|
347
343
|
const avatarUrl = \`https://unavatar.io/twitter/\${card.twitter_handle || card.slug || 'unknown'}\`;
|
|
348
344
|
|
|
349
|
-
|
|
350
|
-
<
|
|
351
|
-
|
|
352
|
-
<img src="\${avatarUrl}" alt="" style="width:64px;height:64px;border-radius:50%;object-fit:cover;border:2px solid #e2e8f0" onerror="this.style.display='none'">
|
|
345
|
+
panel.innerHTML = \`
|
|
346
|
+
<div style="display:flex;gap:14px;align-items:center;margin-bottom:16px">
|
|
347
|
+
<img src="\${avatarUrl}" alt="" style="width:56px;height:56px;border-radius:50%;object-fit:cover;border:2px solid #e2e8f0" onerror="this.style.display='none'">
|
|
353
348
|
<div>
|
|
354
|
-
<
|
|
355
|
-
<
|
|
356
|
-
\${card.previous_role ? \`<
|
|
357
|
-
|
|
349
|
+
<div style="font-size:1.3em;font-weight:700;color:#0f172a">\${card.name}</div>
|
|
350
|
+
<div style="font-size:0.85em;color:#64748b">\${card.title || ''}\${card.company ? ' @ ' + card.company : ''}</div>
|
|
351
|
+
\${card.previous_role ? \`<div style="font-size:0.75em;color:#94a3b8">Previously: \${card.previous_role}</div>\` : ''}
|
|
352
|
+
<div style="margin-top:4px">
|
|
353
|
+
\${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>\` : ''}
|
|
354
|
+
<span class="badge \${mbtiClass(card.mbti)}" title="\${card.mbti_reason || ''}">\${card.mbti || '?'}</span>
|
|
355
|
+
</div>
|
|
358
356
|
</div>
|
|
359
357
|
</div>
|
|
360
358
|
|
|
361
|
-
\${card.latest_news ? \`<div style="
|
|
359
|
+
\${card.latest_news ? \`<div class="news-chip" style="margin-bottom:12px">๐ฐ \${card.latest_news}</div>\` : ''}
|
|
362
360
|
|
|
363
|
-
<h2>
|
|
364
|
-
\${card.current_focus ? \`<p
|
|
365
|
-
\${card.wants ? \`<p
|
|
361
|
+
<h2>Focus & Goals</h2>
|
|
362
|
+
\${card.current_focus ? \`<p>๐ฏ \${card.current_focus}</p>\` : ''}
|
|
363
|
+
\${card.wants ? \`<p>๐ \${card.wants}</p>\` : ''}
|
|
366
364
|
|
|
367
|
-
\${card.philosophy?.length ? \`<h2>
|
|
368
|
-
|
|
369
|
-
\${card.competition?.length ? \`<h2>Competition</h2><p>\${card.competition.join(', ')}</p>\` : ''}
|
|
365
|
+
\${card.philosophy?.length ? \`<h2>Philosophy</h2>\${card.philosophy.map(p => \`<blockquote>"\${p}"</blockquote>\`).join('')}\` : ''}
|
|
370
366
|
|
|
371
367
|
\${card.talking_points?.length ? \`<h2>Talking Points</h2><ul>\${card.talking_points.map(t => \`<li>\${t}</li>\`).join('')}</ul>\` : ''}
|
|
372
368
|
|
|
373
|
-
<h2>
|
|
374
|
-
\${card.last_connection ? \`<p
|
|
375
|
-
\${card.dos?.length ? \`<
|
|
376
|
-
\${card.
|
|
377
|
-
\${card.gifts?.length ? \`<p><strong>๐ Gifts:</strong> \${card.gifts.join(', ')}</p>\` : ''}
|
|
369
|
+
<h2>Approach</h2>
|
|
370
|
+
\${card.last_connection ? \`<p>๐ค <strong>Last:</strong> \${card.last_connection}</p>\` : '<p style="color:#94a3b8">No interactions yet</p>'}
|
|
371
|
+
\${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>\` : ''}
|
|
372
|
+
\${card.gifts?.length ? \`<p style="font-size:0.85em">๐ \${card.gifts.join(', ')}</p>\` : ''}
|
|
378
373
|
|
|
379
|
-
\${card.
|
|
374
|
+
\${card.competition?.length ? \`<h2>Competition</h2><p style="font-size:0.85em">\${card.competition.join(', ')}</p>\` : ''}
|
|
380
375
|
|
|
381
|
-
|
|
382
|
-
<p><strong>MBTI:</strong> <span style="cursor:help;border-bottom:1px dashed #94a3b8" title="\${card.mbti_reason || ''}">\${card.mbti || '?'}</span>\${card.mbti_reason ? \` โ \${card.mbti_reason}\` : ''}</p>
|
|
383
|
-
\${card.superpower ? \`<p><strong>โก Superpower:</strong> \${card.superpower}</p>\` : ''}
|
|
384
|
-
<div style="display:flex;justify-content:center;margin:16px 0">\${radarSvg(s, 240)}</div>
|
|
376
|
+
\${card.key_quotes?.length ? \`<h2>Quotes</h2>\${card.key_quotes.slice(0,4).map(q => \`<p style="font-size:0.8em;color:#475569;margin:3px 0">"\${q}"</p>\`).join('')}\` : ''}
|
|
385
377
|
|
|
386
|
-
|
|
378
|
+
<h2>Personality</h2>
|
|
379
|
+
<p style="font-size:0.85em"><strong>MBTI:</strong> \${card.mbti || '?'}\${card.mbti_reason ? \` โ \${card.mbti_reason}\` : ''}</p>
|
|
380
|
+
\${card.superpower ? \`<p style="font-size:0.85em">โก \${card.superpower}</p>\` : ''}
|
|
381
|
+
<div style="display:flex;justify-content:center;margin:12px 0">\${radarSvg(s, 220)}</div>
|
|
387
382
|
|
|
388
|
-
\${card.
|
|
383
|
+
\${card.annotations?.length ? \`<h2>Your Notes</h2>\${card.annotations.map(a => \`<p style="font-size:0.8em;color:#64748b">๐ \${a}</p>\`).join('')}\` : ''}
|
|
389
384
|
|
|
390
|
-
<div style="display:flex;gap:
|
|
391
|
-
\${card.slug ? \`<a href="\${card.slug}.html" style="color:#2563eb;text-decoration:none;padding:8px
|
|
392
|
-
\${card.slug ? \`<button onclick="regenerateProfile('\${card.slug}',this)" style="padding:8px
|
|
385
|
+
<div style="display:flex;gap:8px;margin-top:20px">
|
|
386
|
+
\${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>\` : ''}
|
|
387
|
+
\${card.slug ? \`<button onclick="regenerateProfile('\${card.slug}',this)" style="flex:1;padding:8px;border:1px solid #e2e8f0;border-radius:6px;background:#f8fafc;color:#64748b;cursor:pointer;font-size:0.85em">๐ Update</button>\` : ''}
|
|
393
388
|
</div>
|
|
394
389
|
\`;
|
|
395
390
|
|
|
396
|
-
document.getElementById('
|
|
391
|
+
document.getElementById('panel').classList.add('open');
|
|
392
|
+
document.getElementById('main').classList.add('panel-open');
|
|
397
393
|
}
|
|
398
394
|
|
|
399
|
-
function
|
|
400
|
-
document.getElementById('
|
|
395
|
+
function closePanel() {
|
|
396
|
+
document.getElementById('panel').classList.remove('open');
|
|
397
|
+
document.getElementById('main').classList.remove('panel-open');
|
|
398
|
+
if (activeCard !== null) document.getElementById('card-' + activeCard)?.classList.remove('active');
|
|
399
|
+
activeCard = null;
|
|
401
400
|
}
|
|
402
401
|
|
|
403
|
-
document.addEventListener('keydown', e => { if (e.key === 'Escape')
|
|
402
|
+
document.addEventListener('keydown', e => { if (e.key === 'Escape') closePanel(); });
|
|
404
403
|
|
|
405
404
|
async function regenerateProfile(slug, btn) {
|
|
406
|
-
const
|
|
405
|
+
const orig = btn.textContent;
|
|
407
406
|
btn.textContent = 'โณ Updating...';
|
|
408
407
|
btn.disabled = true;
|
|
409
|
-
btn.style.opacity = '0.6';
|
|
410
|
-
|
|
411
408
|
try {
|
|
412
409
|
const resp = await fetch(\`/api/regenerate/\${slug}\`);
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
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
|
-
}
|
|
410
|
+
if (resp.ok) { btn.textContent = 'โ
Done'; setTimeout(() => location.reload(), 1000); }
|
|
411
|
+
else { const d = await resp.json(); btn.textContent = 'โ ' + (d.error||'Failed'); setTimeout(() => { btn.textContent = orig; btn.disabled = false; }, 3000); }
|
|
412
|
+
} catch { btn.textContent = 'โ Error'; setTimeout(() => { btn.textContent = orig; btn.disabled = false; }, 3000); }
|
|
430
413
|
}
|
|
431
414
|
|
|
415
|
+
// Sort by most recently updated
|
|
416
|
+
cards.sort((a, b) => (b.updated || '').localeCompare(a.updated || ''));
|
|
417
|
+
|
|
432
418
|
// Render
|
|
433
419
|
const grid = document.getElementById('grid');
|
|
434
420
|
grid.innerHTML = cards.map((card, i) => renderCard(card, i)).join('');
|
|
421
|
+
document.getElementById('count').textContent = cards.length + ' contacts';
|
|
435
422
|
</script>
|
|
436
423
|
</body>
|
|
437
424
|
</html>`;
|
package/lib/templates.js
CHANGED
|
@@ -4,13 +4,22 @@ synthesize an actionable profile focused on HOW to work with this person.
|
|
|
4
4
|
|
|
5
5
|
## Rules
|
|
6
6
|
- Only include information supported by the provided data
|
|
7
|
-
- For personality/MBTI inferences, note these are estimates based on public behavior
|
|
8
7
|
- Focus on ACTIONABLE intelligence: what they care about NOW, what they want, how to approach them
|
|
9
8
|
- Include their own words whenever possible (direct quotes)
|
|
10
9
|
- If user annotations exist, incorporate them into interaction history
|
|
11
10
|
- If a section has no data, write "No data available."
|
|
12
11
|
- Output ONLY the Markdown profile followed by the JSON metadata block
|
|
13
12
|
|
|
13
|
+
## MBTI/Personality Rules (CRITICAL)
|
|
14
|
+
- Do NOT default to INTJ. Most people are NOT INTJ.
|
|
15
|
+
- Analyze ACTUAL behavior from the data: How do they communicate? Are they verbose or terse? Do they use humor? Do they share personal feelings? Do they focus on big picture or details? Do they seek consensus or decide alone?
|
|
16
|
+
- E vs I: Frequent public engagement, many tweets, social energy = E. Rare posts, private, reflective = I.
|
|
17
|
+
- S vs N: Focus on concrete details, practical matters = S. Focus on vision, future possibilities = N.
|
|
18
|
+
- T vs F: Decisions based on logic/data = T. Decisions mentioning people/values/impact = F.
|
|
19
|
+
- J vs P: Structured, plans, deadlines = J. Flexible, spontaneous, exploratory = P.
|
|
20
|
+
- Each letter MUST have a specific evidence citation from the data. If insufficient data, write "Insufficient data to estimate" instead of guessing.
|
|
21
|
+
- DISC type must also cite specific behavioral evidence.
|
|
22
|
+
|
|
14
23
|
## Output Format
|
|
15
24
|
|
|
16
25
|
# {Full Name} โ Profile
|