| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,7 +1,10 @@ | |||
| 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. | ||
| 2 | 5 | // Loaded by the `--browser panes` data browser from /public/panes/. | |
| 3 | 6 | // 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 }. | ||
| 5 | 8 | ||
| 6 | 9 | export default { | |
| 7 | 10 | canHandle(node, h) { | |
@@ -14,18 +17,71 @@ export default { | |||
| 14 | 17 | const site = h.host(url); | |
| 15 | 18 | const date = h.fmtDate(h.first(h.prop(node, 'created'))); | |
| 16 | 19 | 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>` : ''} | ||
| 24 | 84 | </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>` : ''} | ||
| 29 | 85 | </div> | |
| 30 | 86 | </div>`; | |
| 31 | 87 | } | |
| Back | FazBrowse Home | New Git URL |
0 commit comments