Skip to content

Commit c90cad7

Browse files
fix(browser): address Copilot review on #70
- start(): accept 'panes' in the library-API browser validation (not just the CLI path) so start({ browser: 'panes' }) no longer throws. - package.json: add data-browser-panes.{js,css} + examples/ to the files allowlist so the jsDelivr/npm asset exists after publish (was a guaranteed 404 for --browser panes). - panes: normalize multi-valued JSON-LD props — idOf() unwraps arrays and a new h.first helper takes the first value, so array-valued title/recalls/ created/summary render correctly instead of '' or [object Object].
1 parent e4da54f commit c90cad7

5 files changed

Lines changed: 15 additions & 9 deletions

File tree

data-browser-panes.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
//
1212
// Single file, no build, no framework. Local pane contract (ES module):
1313
// export default { canHandle(node, h) -> boolean, render(node, h) -> htmlString }
14-
// where h = { escape, prop, propAll, idOf, types, host, fmtDate, localName }.
14+
// where h = { escape, prop, propAll, first, idOf, types, host, fmtDate, localName }.
15+
// (prop may return an array for multi-valued JSON-LD; use h.first for single values.)
1516

1617
document.head.insertAdjacentHTML('beforeend', `<style>
1718
body{font:14px/1.55 system-ui,-apple-system,sans-serif;margin:0;color:#222;background:#f3eee5}
@@ -86,7 +87,7 @@ body{font:14px/1.55 system-ui,-apple-system,sans-serif;margin:0;color:#222;backg
8687
async function renderLocalPane(node) {
8788
const panes = await loadLocalPanes();
8889
if (!panes.length) return null;
89-
const h = { escape, prop, propAll, idOf, types: typesOf, host: hostOf, fmtDate: fmtDay, localName: localType };
90+
const h = { escape, prop, propAll, first: firstVal, idOf, types: typesOf, host: hostOf, fmtDate: fmtDay, localName: localType };
9091
for (const p of panes) {
9192
try { if (p.canHandle(node, h)) return p.render(node, h); } catch (e) { /* skip */ }
9293
}
@@ -135,7 +136,9 @@ body{font:14px/1.55 system-ui,-apple-system,sans-serif;margin:0;color:#222;backg
135136
return undefined;
136137
}
137138
function propAll(node, key) { const v = prop(node, key); return v == null ? [] : (Array.isArray(v) ? v : [v]); }
138-
function idOf(v) { return v == null ? '' : (typeof v === 'string' ? v : (v['@id'] || v.id || '')); }
139+
// First value of a possibly-multi-valued JSON-LD property.
140+
function firstVal(v) { return Array.isArray(v) ? v[0] : v; }
141+
function idOf(v) { if (Array.isArray(v)) v = v[0]; return v == null ? '' : (typeof v === 'string' ? v : (v['@id'] || v.id || '')); }
139142
function hostOf(u) { try { return new URL(u).host; } catch (e) { return u; } }
140143
function fmtDay(iso) {
141144
if (!iso) return '';

examples/panes/bookmark.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ export default {
1010

1111
render(node, h) {
1212
const url = h.idOf(h.prop(node, 'recalls'));
13-
const title = h.prop(node, 'title') || url || 'Untitled bookmark';
13+
const title = h.first(h.prop(node, 'title')) || url || 'Untitled bookmark';
1414
const site = h.host(url);
15-
const date = h.fmtDate(h.prop(node, 'created'));
15+
const date = h.fmtDate(h.first(h.prop(node, 'created')));
1616
const fav = url ? h.escape(new URL('/favicon.ico', url).href) : '';
1717
return `<div style="font-family:Inter,-apple-system,sans-serif;padding:24px 0 8px;">
1818
<div style="font-family:Georgia,serif;font-size:14px;font-style:italic;color:#999;margin-bottom:18px;">Bookmark</div>

examples/panes/tracker.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ export default {
1515
return s === 'COMPLETED' || s === 'CANCELLED';
1616
};
1717
const active = issues.filter(i => !done(i));
18-
const title = h.prop(node, 'title') || 'Tasks';
19-
const row = i => `<li style="padding:8px 0;border-bottom:1px solid rgba(127,127,127,0.12);${done(i) ? 'color:#aaa;text-decoration:line-through;' : ''}">${done(i) ? '☑' : '☐'} ${h.escape(h.prop(i, 'summary') || h.idOf(i))}</li>`;
18+
const title = h.first(h.prop(node, 'title')) || 'Tasks';
19+
const row = i => `<li style="padding:8px 0;border-bottom:1px solid rgba(127,127,127,0.12);${done(i) ? 'color:#aaa;text-decoration:line-through;' : ''}">${done(i) ? '☑' : '☐'} ${h.escape(h.first(h.prop(i, 'summary')) || h.idOf(i))}</li>`;
2020
return `<div style="font-family:Inter,-apple-system,sans-serif;padding:24px 0 8px;">
2121
<div style="font-family:Georgia,serif;font-size:14px;font-style:italic;color:#999;margin-bottom:6px;">Tasks</div>
2222
<div style="font-size:26px;font-weight:600;color:#1a1a1a;margin-bottom:4px;">${h.escape(title)}</div>

lib/start.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,8 @@ export async function start(userOptions = {}) {
206206
if (!Number.isInteger(options.port) || options.port < 1 || options.port > 65535) {
207207
throw new Error(`Invalid port: ${options.port}`);
208208
}
209-
if (options.browser !== 'folder' && options.browser !== 'json') {
210-
throw new Error(`Invalid browser: ${options.browser} (must be 'folder' or 'json')`);
209+
if (options.browser !== 'folder' && options.browser !== 'json' && options.browser !== 'panes') {
210+
throw new Error(`Invalid browser: ${options.browser} (must be 'folder', 'json', or 'panes')`);
211211
}
212212

213213
if (!existsSync(options.root)) {

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
"data-browser.css",
1818
"data-browser-folder.js",
1919
"data-browser-folder.css",
20+
"data-browser-panes.js",
21+
"data-browser-panes.css",
22+
"examples/",
2023
"welcome.html",
2124
"signin.html",
2225
"signin.html.acl",

0 commit comments

Comments
 (0)