1- // Pod-local pane for bookmark:Bookmark (dc:title / bookmark:recalls / dcterms:created).
1+ // Pod-local pane for bookmark:Bookmark.
2+ // Core: dc:title / bookmark:recalls / dcterms:created.
3+ // Enrichment (optional, rendered when present): dc:description (summary),
4+ // schema:keywords (tags), schema:image (hero), schema:datePublished.
25// Loaded by the `--browser panes` data browser from /public/panes/.
36// Contract: export default { canHandle(node, h) -> bool, render(node, h) -> htmlString }
4- // h = { escape, prop, propAll, idOf, types, host, fmtDate, localName }.
7+ // h = { escape, prop, propAll, first, idOf, types, host, fmtDate, localName }.
58
69export default {
710 canHandle ( node , h ) {
@@ -14,18 +17,71 @@ export default {
1417 const site = h . host ( url ) ;
1518 const date = h . fmtDate ( h . first ( h . prop ( node , 'created' ) ) ) ;
1619 const fav = url ? h . escape ( new URL ( '/favicon.ico' , url ) . href ) : '' ;
17- return `<div style="font-family:Inter,-apple-system,sans-serif;padding:24px 0 8px;">
18- <div style="font-family:Georgia,serif;font-size:14px;font-style:italic;color:#999;margin-bottom:18px;">Bookmark</div>
19- <a href="${ h . escape ( url ) } " target="_blank" rel="noopener" style="display:flex;gap:18px;align-items:flex-start;text-decoration:none;color:inherit;border:1px solid rgba(127,127,127,0.18);border-radius:14px;padding:24px;background:#fff;">
20- <img src="${ fav } " alt="" width="44" height="44" onerror="this.replaceWith(Object.assign(document.createElement('div'),{textContent:'🔖',style:'font-size:32px;line-height:44px;width:44px;text-align:center'}))" style="width:44px;height:44px;border-radius:9px;flex:0 0 auto;background:rgba(127,127,127,0.08);object-fit:contain;" />
21- <div style="min-width:0;">
22- <div style="font-size:21px;font-weight:600;color:#1a1a1a;line-height:1.3;margin-bottom:6px;overflow-wrap:anywhere;">${ h . escape ( title ) } </div>
23- <div style="font-size:13px;color:#7c3aed;font-family:monospace;overflow-wrap:anywhere;">${ h . escape ( site ) } </div>
20+
21+ // Enrichment — all optional.
22+ const desc = h . first ( h . prop ( node , 'description' ) ) ;
23+ const tags = h . propAll ( node , 'keywords' ) . map ( t => h . first ( t ) ) . filter ( Boolean ) ;
24+ const img = h . idOf ( h . prop ( node , 'image' ) ) ;
25+ const published = h . fmtDate ( h . first ( h . prop ( node , 'datePublished' ) ) ) ;
26+
27+ const hero = img
28+ ? `<img class="bm-hero" src="${ h . escape ( img ) } " alt="" onerror="this.style.display='none'">`
29+ : '' ;
30+ const rule = desc ? `<div class="bm-rule"></div>` : '' ;
31+ // Render the summary as paragraphs (split on blank lines); first is the lead.
32+ const paras = desc ? String ( desc ) . split ( / \n \s * \n / ) . map ( p => p . trim ( ) ) . filter ( Boolean ) : [ ] ;
33+ const summary = paras . length
34+ ? `<div class="bm-body">${ paras . map ( ( p , i ) =>
35+ `<p class="${ i === 0 ? 'bm-lead' : 'bm-para' } ">${ h . escape ( p ) } </p>` ) . join ( '' ) } </div>`
36+ : '' ;
37+ const chips = tags . length
38+ ? `<div class="bm-tags">${ tags . map ( t => `<span class="bm-tag">${ h . escape ( t ) } </span>` ) . join ( '' ) } </div>`
39+ : '' ;
40+ const meta = [ published ? `published ${ h . escape ( published ) } ` : '' , date ? `saved ${ h . escape ( date ) } ` : '' ]
41+ . filter ( Boolean ) . join ( ' · ' ) ;
42+
43+ return `<style>
44+ .bm-wrap{max-width:680px;margin:0 auto;padding:8px 0 28px;font-family:Inter,-apple-system,system-ui,sans-serif;}
45+ .bm-eyebrow{font-family:Georgia,serif;font-size:13px;font-style:italic;color:#a1a1aa;margin-bottom:18px;}
46+ .bm-card{background:#fff;border:1px solid rgba(24,24,27,.07);border-radius:18px;box-shadow:0 1px 3px rgba(24,24,27,.04),0 10px 40px rgba(24,24,27,.07);padding:32px 34px;}
47+ .bm-head{display:flex;gap:16px;align-items:flex-start;}
48+ .bm-fav{width:40px;height:40px;border-radius:10px;flex:0 0 auto;background:rgba(127,127,127,.06);object-fit:contain;}
49+ .bm-titlewrap{min-width:0;flex:1;}
50+ .bm-title{font-size:22px;font-weight:650;color:#18181b;line-height:1.3;text-decoration:none;overflow-wrap:anywhere;}
51+ .bm-title:hover{color:#7c3aed;}
52+ .bm-host{display:block;font-size:12.5px;color:#7c3aed;font-family:ui-monospace,SFMono-Regular,monospace;margin-top:7px;text-decoration:none;overflow-wrap:anywhere;}
53+ .bm-host:hover{text-decoration:underline;}
54+ .bm-hero{width:88px;height:88px;border-radius:12px;object-fit:cover;flex:0 0 auto;margin-left:auto;border:1px solid rgba(24,24,27,.06);background:#fafafa;}
55+ .bm-rule{height:1px;background:rgba(24,24,27,.07);margin:22px 0;}
56+ .bm-body{display:flex;flex-direction:column;gap:15px;}
57+ .bm-lead{font-size:16px;line-height:1.7;color:#27272a;margin:0;}
58+ .bm-para{font-size:15px;line-height:1.72;color:#52525b;margin:0;}
59+ .bm-tags{display:flex;flex-wrap:wrap;gap:7px;margin-top:24px;}
60+ .bm-tag{font-size:12px;color:#6d28d9;background:rgba(124,58,237,.07);border:1px solid rgba(124,58,237,.13);padding:4px 11px;border-radius:999px;}
61+ .bm-foot{display:flex;align-items:center;margin-top:26px;}
62+ .bm-open{font-size:13px;font-weight:600;color:#fff;background:#7c3aed;padding:10px 18px;border-radius:10px;text-decoration:none;box-shadow:0 2px 10px rgba(124,58,237,.28);transition:background .15s,transform .05s;}
63+ .bm-open:hover{background:#6d28d9;}
64+ .bm-open:active{transform:translateY(1px);}
65+ .bm-meta{margin-left:auto;font-size:12px;color:#a1a1aa;}
66+ </style>
67+ <div class="bm-wrap">
68+ <div class="bm-eyebrow">Bookmark</div>
69+ <div class="bm-card">
70+ <div class="bm-head">
71+ <img class="bm-fav" src="${ fav } " alt="" onerror="this.replaceWith(Object.assign(document.createElement('div'),{textContent:'🔖',style:'font-size:30px;line-height:40px;width:40px;text-align:center'}))">
72+ <div class="bm-titlewrap">
73+ <a class="bm-title" href="${ h . escape ( url ) } " target="_blank" rel="noopener">${ h . escape ( title ) } </a>
74+ <a class="bm-host" href="${ h . escape ( url ) } " target="_blank" rel="noopener">${ h . escape ( site ) } </a>
75+ </div>
76+ ${ hero }
77+ </div>
78+ ${ rule }
79+ ${ summary }
80+ ${ chips }
81+ <div class="bm-foot">
82+ <a class="bm-open" href="${ h . escape ( url ) } " target="_blank" rel="noopener">Open ↗</a>
83+ ${ meta ? `<span class="bm-meta">${ meta } </span>` : '' }
2484 </div>
25- </a>
26- <div style="display:flex;gap:10px;align-items:center;margin-top:18px;">
27- <a href="${ h . escape ( url ) } " target="_blank" rel="noopener" style="font-size:13px;font-weight:600;color:#fff;background:#7c3aed;padding:9px 16px;border-radius:8px;text-decoration:none;">Open ↗</a>
28- ${ date ? `<span style="margin-left:auto;font-size:12px;color:#aaa;">saved ${ h . escape ( date ) } </span>` : '' }
2985 </div>
3086 </div>` ;
3187 }
0 commit comments