openvibers/openvibe.shoes Commits
Developers: the API's routes as cards on a phone (#16)
Changes
@@@ -31,6 +31,15 @@.sh-empty { padding: 28px; margin: 20px 0; border: 1px dashed var(--border, rgba(255,255,255,.16)); border-radius: 16px; }.sh-notice { padding: 12px 14px; margin: 14px 0; border: 1px solid var(--room-accent, var(--accent)); border-radius: 12px; background: color-mix(in srgb, var(--room-accent, var(--accent)) 12%, transparent); }.sh-table-wrap { overflow-x: auto; margin: 10px 0; }++/* The API's routes, one card each: the method and the route on one line that wraps, what it does under it. A table++ squeezed the routes into a sliver on a phone (phone sweep, 2026-11-01). */++.sh-eps { display: grid; gap: 10px; margin: 10px 0 14px; grid-template-columns: repeat(auto-fill, minmax(min(100%, 440px), 1fr)); }++.sh-eps > div { min-width: 0; padding: 12px 14px; border-radius: 14px; border: 1px solid var(--border, #1f2d47); background: var(--bg-card, #0f1729); }++.sh-eps dt code { font-size: 13px; line-height: 1.5; color: var(--text-primary, #e6edf7); overflow-wrap: anywhere; }++.sh-eps .sh-m { margin-right: 6px; font-weight: 800; color: var(--success-text, #22c55e); }++.sh-eps .sh-m-post, .sh-eps .sh-m-put, .sh-eps .sh-m-patch { color: var(--warning-text, #f59e0b); }++.sh-eps .sh-m-delete { color: var(--danger-text, #ef4444); }++.sh-eps dd { margin: 6px 0 0; font-size: 14px; line-height: 1.5; color: var(--text-secondary, #96a7c2); }.sh-table { width: 100%; border-collapse: collapse; font-size: 14px; }.sh-table caption { text-align: left; padding: 0 0 8px; color: var(--text-muted, #7386a3); font-size: 13px; }.sh-table th, .sh-table td { padding: 9px 10px; border-bottom: 1px solid var(--border, color-mix(in srgb, var(--text-primary, #fff) 8%, transparent)); text-align: left; vertical-align: top; }@@@ -385,7 +385,7 @@ function developers({ baseUrl = 'https://open.shoes' } = {}) {];const body = `<div class="sh-wrap sh-page"><header class="sh-page-h"><p class="sh-kicker">Developers</p><h1>The Shoes API</h1><p class="sh-lede">Every model, offer, snapshot and drop is open JSON. The public reads take no key; a write is a signed-in person's own. There are no affiliate links and no Marketplace scraping.</p></header>--<section class="sh-sec" aria-labelledby="sh-api-h"><h2 id="sh-api-h">Routes</h2><div class="sh-table-wrap"><table class="sh-table"><thead><tr><th scope="col">Method</th><th scope="col">Route</th><th scope="col">What it does</th></tr></thead><tbody>${rows.map(([m, r, d]) => `<tr><td><code>${m}</code></td><td><code>${esc(r)}</code></td><td>${esc(d)}</td></tr>`).join('')}</tbody></table></div>++<section class="sh-sec" aria-labelledby="sh-api-h"><h2 id="sh-api-h">Routes</h2><dl class="sh-eps">${rows.map(([m, r, d]) => `<div><dt><code><b class="sh-m sh-m-${m.toLowerCase()}">${m}</b> ${esc(r)}</code></dt><dd>${esc(d)}</dd></div>`).join('')}</dl><p><a class="sh-btn" href="/api/openapi.json">OpenAPI 3.1</a> <a class="sh-btn sh-btn-quiet" href="/api/v1/shoes?q=air">Try a search</a></p></section><section class="sh-sec" aria-labelledby="sh-src-h"><h2 id="sh-src-h">Where the data comes from</h2><ul class="sh-list"><li><b>OpenVibe.Deals</b> is the network's offer aggregator: Shoes reads its public newest-offer pages and keeps the footwear, then reads each product's public record for its brand.</li><li><b>eBay</b> is read through the official Browse API, and only when the host sets a developer key pair.</li><li><b>Release pages</b> are the brand's or the store's own public release calendar (Nike SNKRS by default), read by a pure parser. A release with no date is not imported.</li><li><b>No scraping</b> of Facebook Marketplace; a Marketplace link is tracked by the person pasting it and Watch applying its own rules.</li></ul></section><section class="sh-sec" aria-labelledby="sh-ex-h"><h2 id="sh-ex-h">Examples</h2><pre class="sh-code"><code>curl "${api}/api/v1/shoes?q=air+max&size=10.5&sort=price"</code></pre>
@@ -31,6 +31,15 @@
@@ -31,6 +31,15 @@
.sh-empty { padding: 28px; margin: 20px 0; border: 1px dashed var(--border, rgba(255,255,255,.16)); border-radius: 16px; }
.sh-empty { padding: 28px; margin: 20px 0; border: 1px dashed var(--border, rgba(255,255,255,.16)); border-radius: 16px; }
.sh-notice { padding: 12px 14px; margin: 14px 0; border: 1px solid var(--room-accent, var(--accent)); border-radius: 12px; background: color-mix(in srgb, var(--room-accent, var(--accent)) 12%, transparent); }
.sh-notice { padding: 12px 14px; margin: 14px 0; border: 1px solid var(--room-accent, var(--accent)); border-radius: 12px; background: color-mix(in srgb, var(--room-accent, var(--accent)) 12%, transparent); }
.sh-table-wrap { overflow-x: auto; margin: 10px 0; }
.sh-table-wrap { overflow-x: auto; margin: 10px 0; }
+/* The API's routes, one card each: the method and the route on one line that wraps, what it does under it. A table
+ squeezed the routes into a sliver on a phone (phone sweep, 2026-11-01). */
+.sh-eps { display: grid; gap: 10px; margin: 10px 0 14px; grid-template-columns: repeat(auto-fill, minmax(min(100%, 440px), 1fr)); }
+.sh-eps > div { min-width: 0; padding: 12px 14px; border-radius: 14px; border: 1px solid var(--border, #1f2d47); background: var(--bg-card, #0f1729); }
+.sh-eps dt code { font-size: 13px; line-height: 1.5; color: var(--text-primary, #e6edf7); overflow-wrap: anywhere; }
+.sh-eps .sh-m { margin-right: 6px; font-weight: 800; color: var(--success-text, #22c55e); }
+.sh-eps .sh-m-post, .sh-eps .sh-m-put, .sh-eps .sh-m-patch { color: var(--warning-text, #f59e0b); }
+.sh-eps .sh-m-delete { color: var(--danger-text, #ef4444); }
+.sh-eps dd { margin: 6px 0 0; font-size: 14px; line-height: 1.5; color: var(--text-secondary, #96a7c2); }
.sh-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.sh-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.sh-table caption { text-align: left; padding: 0 0 8px; color: var(--text-muted, #7386a3); font-size: 13px; }
.sh-table caption { text-align: left; padding: 0 0 8px; color: var(--text-muted, #7386a3); font-size: 13px; }
.sh-table th, .sh-table td { padding: 9px 10px; border-bottom: 1px solid var(--border, color-mix(in srgb, var(--text-primary, #fff) 8%, transparent)); text-align: left; vertical-align: top; }
.sh-table th, .sh-table td { padding: 9px 10px; border-bottom: 1px solid var(--border, color-mix(in srgb, var(--text-primary, #fff) 8%, transparent)); text-align: left; vertical-align: top; }
@@ -385,7 +385,7 @@ function developers({ baseUrl = 'https://open.shoes' } = {}) {
@@ -385,7 +385,7 @@ function developers({ baseUrl = 'https://open.shoes' } = {}) {
];
];
const body = `<div class="sh-wrap sh-page">
const body = `<div class="sh-wrap sh-page">
<header class="sh-page-h"><p class="sh-kicker">Developers</p><h1>The Shoes API</h1><p class="sh-lede">Every model, offer, snapshot and drop is open JSON. The public reads take no key; a write is a signed-in person's own. There are no affiliate links and no Marketplace scraping.</p></header>
<header class="sh-page-h"><p class="sh-kicker">Developers</p><h1>The Shoes API</h1><p class="sh-lede">Every model, offer, snapshot and drop is open JSON. The public reads take no key; a write is a signed-in person's own. There are no affiliate links and no Marketplace scraping.</p></header>
-<section class="sh-sec" aria-labelledby="sh-api-h"><h2 id="sh-api-h">Routes</h2><div class="sh-table-wrap"><table class="sh-table"><thead><tr><th scope="col">Method</th><th scope="col">Route</th><th scope="col">What it does</th></tr></thead><tbody>${rows.map(([m, r, d]) => `<tr><td><code>${m}</code></td><td><code>${esc(r)}</code></td><td>${esc(d)}</td></tr>`).join('')}</tbody></table></div>
+<section class="sh-sec" aria-labelledby="sh-api-h"><h2 id="sh-api-h">Routes</h2><dl class="sh-eps">${rows.map(([m, r, d]) => `<div><dt><code><b class="sh-m sh-m-${m.toLowerCase()}">${m}</b> ${esc(r)}</code></dt><dd>${esc(d)}</dd></div>`).join('')}</dl>
<p><a class="sh-btn" href="/api/openapi.json">OpenAPI 3.1</a> <a class="sh-btn sh-btn-quiet" href="/api/v1/shoes?q=air">Try a search</a></p></section>
<p><a class="sh-btn" href="/api/openapi.json">OpenAPI 3.1</a> <a class="sh-btn sh-btn-quiet" href="/api/v1/shoes?q=air">Try a search</a></p></section>
<section class="sh-sec" aria-labelledby="sh-src-h"><h2 id="sh-src-h">Where the data comes from</h2><ul class="sh-list"><li><b>OpenVibe.Deals</b> is the network's offer aggregator: Shoes reads its public newest-offer pages and keeps the footwear, then reads each product's public record for its brand.</li><li><b>eBay</b> is read through the official Browse API, and only when the host sets a developer key pair.</li><li><b>Release pages</b> are the brand's or the store's own public release calendar (Nike SNKRS by default), read by a pure parser. A release with no date is not imported.</li><li><b>No scraping</b> of Facebook Marketplace; a Marketplace link is tracked by the person pasting it and Watch applying its own rules.</li></ul></section>
<section class="sh-sec" aria-labelledby="sh-src-h"><h2 id="sh-src-h">Where the data comes from</h2><ul class="sh-list"><li><b>OpenVibe.Deals</b> is the network's offer aggregator: Shoes reads its public newest-offer pages and keeps the footwear, then reads each product's public record for its brand.</li><li><b>eBay</b> is read through the official Browse API, and only when the host sets a developer key pair.</li><li><b>Release pages</b> are the brand's or the store's own public release calendar (Nike SNKRS by default), read by a pure parser. A release with no date is not imported.</li><li><b>No scraping</b> of Facebook Marketplace; a Marketplace link is tracked by the person pasting it and Watch applying its own rules.</li></ul></section>
<section class="sh-sec" aria-labelledby="sh-ex-h"><h2 id="sh-ex-h">Examples</h2><pre class="sh-code"><code>curl "${api}/api/v1/shoes?q=air+max&size=10.5&sort=price"</code></pre>
<section class="sh-sec" aria-labelledby="sh-ex-h"><h2 id="sh-ex-h">Examples</h2><pre class="sh-code"><code>curl "${api}/api/v1/shoes?q=air+max&size=10.5&sort=price"</code></pre>