README, docs and site: one story (the whole job, no avoidable rejections, the desktop app, your AI, install)
README is a short front page (about 120 lines) with one real visual per section; every former section moved unchanged into docs/*.md with a docs/README.md index (journey, rejections, getting-started, commands, growth, studios, store-art, listing-languages, api, skills, mcp, safety, faq, project). The site tells the same story on one page: the teaser in the hero, real CLI output as text, the desktop app as a section with light/dark captures of eight screens and the per-system downloads (desktop/latest.json); /desktop/ redirects to /#download. New capture tools: tools/site-desktop-shots.ts (fixture-mode desktop frames) and tools/site-term-shots.ts (real CLI output as text and WebP). tests/docs-links.test.ts checks README, docs and site links resolve.
This commit is contained in:
parent
deb218943c
commit
710c3a53d0
63 changed files with 2742 additions and 1782 deletions
|
|
@ -4,209 +4,17 @@
|
|||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>storebound desktop · download</title>
|
||||
<meta name="description" content="storebound desktop: every app and game on Google Play and the App Store in one window, driving the same storebound CLI. Downloads for macOS, Windows and Linux with checksums and signatures.">
|
||||
<link rel="canonical" href="https://storebound.dev/desktop/">
|
||||
<meta name="robots" content="noindex">
|
||||
<link rel="canonical" href="https://storebound.dev/#desktop">
|
||||
<meta http-equiv="refresh" content="0; url=../#download">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<meta name="theme-color" content="#f8fafc" media="(prefers-color-scheme: light)">
|
||||
<meta name="theme-color" content="#0b1120" media="(prefers-color-scheme: dark)">
|
||||
<link rel="icon" href="../favicon.ico?v=2" sizes="16x16 32x32 48x48">
|
||||
<link rel="manifest" href="../manifest.webmanifest?v=2">
|
||||
<link rel="apple-touch-icon" href="../apple-touch-icon.png?v=2">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:site_name" content="storebound">
|
||||
<meta property="og:url" content="https://storebound.dev/desktop/">
|
||||
<meta property="og:title" content="storebound desktop · download">
|
||||
<meta property="og:description" content="Every app and game on both stores in one window. macOS, Windows, Linux.">
|
||||
<meta property="og:image" content="https://storebound.dev/og.png?v=2">
|
||||
<link rel="stylesheet" href="../style.css?v=20261012">
|
||||
<script>document.documentElement.classList.add('js')</script>
|
||||
<style>
|
||||
/* page-only parts; colours and type come from ../style.css tokens (fallbacks keep the page readable alone) */
|
||||
.dl-hero { padding: 56px 0 40px; }
|
||||
.dl-hero h1 { font-size: clamp(30px, 5vw, 46px); font-weight: 760; }
|
||||
.dl-main { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 16px; margin-top: 28px; }
|
||||
.dl-main .meta { color: var(--muted, #64748b); font-size: 14.5px; }
|
||||
.dl-notice { margin-top: 22px; max-width: 46em; padding: 14px 16px; border-radius: 12px; border: 1px solid var(--ui-ink, #92400e); background: var(--ui-bg, #fef3c7); color: var(--ui-ink, #92400e); font-size: 15px; }
|
||||
.dl-notice b { font-weight: 700; }
|
||||
.dl-notice ol { margin: 8px 0 0; padding-left: 20px; }
|
||||
.dl-table { width: 100%; border-collapse: collapse; margin-top: 24px; font-size: 15px; }
|
||||
.dl-table th, .dl-table td { text-align: left; padding: 12px 10px; border-bottom: 1px solid var(--line, #e2e8f0); vertical-align: top; }
|
||||
.dl-table th { font: 600 12.5px var(--mono, monospace); text-transform: uppercase; letter-spacing: .08em; color: var(--muted, #64748b); }
|
||||
.dl-table tr.here td { background: var(--accent-soft, #eef2ff); }
|
||||
.dl-table .sha { font: 12.5px/1.5 var(--mono, monospace); color: var(--ink-2, #334155); overflow-wrap: anywhere; display: block; max-width: 34em; }
|
||||
.dl-table .fname { font-weight: 600; overflow-wrap: anywhere; }
|
||||
.dl-table .small { font-size: 13.5px; color: var(--muted, #64748b); }
|
||||
.dl-steps { margin: 16px 0 0; padding-left: 20px; color: var(--ink-2, #334155); max-width: 50em; }
|
||||
.dl-steps li { margin: 6px 0; }
|
||||
.dl-key { display: block; margin-top: 8px; font: 13px/1.6 var(--mono, monospace); overflow-wrap: anywhere; }
|
||||
@media (max-width: 720px) {
|
||||
.dl-table thead { display: none; }
|
||||
.dl-table, .dl-table tbody, .dl-table tr, .dl-table td { display: block; width: 100%; }
|
||||
.dl-table tr { border-bottom: 1px solid var(--line, #e2e8f0); padding: 8px 0; }
|
||||
.dl-table td { border: 0; padding: 4px 0; }
|
||||
}
|
||||
</style>
|
||||
<script>location.replace('../#download')</script>
|
||||
<style>body { font: 16px/1.6 ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; margin: 40px 16px; background: #f8fafc; color: #0f172a; } a { color: #4f46e5; } @media (prefers-color-scheme: dark) { body { background: #0b1120; color: #f1f5f9; } a { color: #818cf8; } }</style>
|
||||
</head>
|
||||
<body>
|
||||
<a class="skip" href="#main">Skip to content</a>
|
||||
|
||||
<header class="top">
|
||||
<div class="wrap top-in">
|
||||
<a class="brand" href="../" aria-label="storebound home">
|
||||
<img class="mark" src="../img/logo-mark.png?v=2" srcset="../img/logo-mark.png?v=2 1x, ../img/logo-mark@2x.png?v=2 2x" width="28" height="28" alt="storebound logo">
|
||||
<span>storebound</span>
|
||||
</a>
|
||||
<nav aria-label="Primary">
|
||||
<a href="../">Home</a>
|
||||
<a href="#all">All downloads</a>
|
||||
<a href="#verify">Verify</a>
|
||||
<a class="nav-git" href="https://git.crt.fyi/CRT/storebound" target="_blank" rel="noopener">git<span class="ext" aria-hidden="true">↗</span><span class="sr-only"> (opens in a new tab)</span></a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="main">
|
||||
<section class="dl-hero">
|
||||
<div class="wrap">
|
||||
<p class="kicker">Desktop app · preview</p>
|
||||
<h1>storebound desktop</h1>
|
||||
<p class="sub">Every app and game you ship, on Google Play and the App Store, in one window: releases on both stores, catalog parity, listings, crash health, reviews and TestFlight. It drives the same open source storebound CLI, keeps store keys in the system keychain, shows a dry run before every change, and never presses Submit.</p>
|
||||
|
||||
<div class="dl-main" id="primary" aria-live="polite">
|
||||
<p class="meta" id="status">Looking up the latest release…</p>
|
||||
</div>
|
||||
<noscript><p class="note">The download list needs JavaScript. Without it: <a href="latest.json">latest.json</a> lists every file with its SHA-256.</p></noscript>
|
||||
<div id="notice"></div>
|
||||
<p class="note">Already use the CLI? <code>storebound desktop</code> opens the app in your app folder, or prints the download for your system; <code>storebound desktop --download</code> saves the installer to ~/Downloads after checking its signature (it never installs anything).</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="sec alt" id="all">
|
||||
<div class="wrap">
|
||||
<p class="kicker">All downloads</p>
|
||||
<h2>Every system, with checksums</h2>
|
||||
<p class="sub" id="all-sub">The app updates itself from this site: each update is signed, and the app checks the signature before it replaces anything. It asks before installing.</p>
|
||||
<table class="dl-table" id="table" hidden>
|
||||
<thead><tr><th scope="col">System</th><th scope="col">File</th><th scope="col">SHA-256</th></tr></thead>
|
||||
<tbody id="rows"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="sec" id="verify">
|
||||
<div class="wrap">
|
||||
<p class="kicker">Verify</p>
|
||||
<h2>Check a download yourself</h2>
|
||||
<ol class="dl-steps">
|
||||
<li>Checksum: <code>shasum -a 256 <file></code> (macOS, Linux) or <code>Get-FileHash <file></code> (Windows) must print the SHA-256 above.</li>
|
||||
<li>Signature: every file has a <code>.sig</code> next to it (the app's update key, minisign). With <a href="https://jedisct1.github.io/minisign/" target="_blank" rel="noopener">minisign</a>: <code>base64 -d <file>.sig > f.minisig && minisign -Vm <file> -x f.minisig -P <key></code>, with the public key
|
||||
<code class="dl-key">RWQqOO/ZeIr3RBmolt/fx108m89QNEUndRTFrucjx0CBMoeFb7/xhrvW</code></li>
|
||||
<li>Or let the CLI do both: <code>storebound desktop --download</code>.</li>
|
||||
</ol>
|
||||
<p class="note">The npm package <code>storebound</code> stays the CLI, MCP server and agent skills; it does not contain this app. The app brings its own copy of the CLI.</p>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="foot">
|
||||
<div class="wrap foot-in">
|
||||
<div>
|
||||
<a class="brand" href="../" aria-label="storebound home">
|
||||
<img class="mark" src="../img/logo-mark.png?v=2" srcset="../img/logo-mark.png?v=2 1x, ../img/logo-mark@2x.png?v=2 2x" width="28" height="28" alt="storebound logo">
|
||||
<span>storebound</span>
|
||||
</a>
|
||||
<p class="fine">MIT · made by CRTkafa · not affiliated with Google or Apple.<br>App Store is a trademark of Apple Inc.; Google Play is a trademark of Google LLC.</p>
|
||||
</div>
|
||||
<nav aria-label="Footer">
|
||||
<a href="https://git.crt.fyi/CRT/storebound/src/branch/main/docs/desktop.md" target="_blank" rel="noopener">desktop docs<span class="ext" aria-hidden="true">↗</span><span class="sr-only"> (opens in a new tab)</span></a>
|
||||
<a href="https://git.crt.fyi/CRT/storebound/src/branch/main/CHANGELOG.md" target="_blank" rel="noopener">CHANGELOG<span class="ext" aria-hidden="true">↗</span><span class="sr-only"> (opens in a new tab)</span></a>
|
||||
<a href="https://git.crt.fyi/CRT/storebound/src/branch/main/SECURITY.md" target="_blank" rel="noopener">SECURITY<span class="ext" aria-hidden="true">↗</span><span class="sr-only"> (opens in a new tab)</span></a>
|
||||
</nav>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// Reads latest.json (same origin, published with the binaries) and fills the page. Everything from it is set as
|
||||
// text, never as HTML; only storebound.dev/desktop/dl/ links are shown.
|
||||
(function () {
|
||||
var LABEL = {
|
||||
'macos-universal-dmg': ['macOS 11 or later', 'Apple silicon and Intel, .dmg'],
|
||||
'windows-x86_64-nsis': ['Windows 10 / 11', 'x64 installer, installs for your user only'],
|
||||
'linux-x86_64-appimage': ['Linux', 'x64 AppImage (Ubuntu 22.04+, Debian 12+)'],
|
||||
'linux-x86_64-deb': ['Linux (Debian, Ubuntu)', 'x64 .deb package']
|
||||
};
|
||||
var ORDER = ['macos-universal-dmg', 'windows-x86_64-nsis', 'linux-x86_64-appimage', 'linux-x86_64-deb'];
|
||||
function el(tag, attrs, text) { var e = document.createElement(tag); for (var k in attrs || {}) e.setAttribute(k, attrs[k]); if (text != null) e.textContent = text; return e; }
|
||||
function mb(n) { return (n / 1e6).toFixed(1) + ' MB'; }
|
||||
function ours(u) { try { var x = new URL(u, location.href); return x.protocol === 'https:' && x.host === 'storebound.dev' && x.pathname.indexOf('/desktop/dl/') === 0; } catch (e) { return false; } }
|
||||
function system() {
|
||||
var p = ((navigator.userAgentData && navigator.userAgentData.platform) || navigator.platform || '') + ' ' + navigator.userAgent;
|
||||
if (/android|iphone|ipad|ipod/i.test(p)) return 'mobile';
|
||||
if (/mac/i.test(p)) return 'macos-universal-dmg';
|
||||
if (/win/i.test(p)) return 'windows-x86_64-nsis';
|
||||
if (/linux|x11/i.test(p)) return 'linux-x86_64-appimage';
|
||||
return null;
|
||||
}
|
||||
var status = document.getElementById('status');
|
||||
var primary = document.getElementById('primary');
|
||||
var notice = document.getElementById('notice');
|
||||
fetch('latest.json', { cache: 'no-cache' }).then(function (r) {
|
||||
if (r.status === 404) throw new Error('none');
|
||||
if (!r.ok) throw new Error('HTTP ' + r.status);
|
||||
return r.json();
|
||||
}).then(function (m) {
|
||||
var d = m.downloads || {};
|
||||
var keys = ORDER.filter(function (k) { return d[k] && ours(d[k].url); });
|
||||
if (!keys.length) throw new Error('none');
|
||||
var here = system();
|
||||
var pick = d[here] && keys.indexOf(here) >= 0 ? here : null;
|
||||
primary.textContent = '';
|
||||
if (pick) {
|
||||
var a = el('a', { 'class': 'btn btn-primary', href: d[pick].url }, 'Download for ' + LABEL[pick][0]);
|
||||
primary.appendChild(a);
|
||||
} else {
|
||||
primary.appendChild(el('a', { 'class': 'btn btn-primary', href: '#all' }, 'Choose your system'));
|
||||
}
|
||||
var meta = 'Version ' + m.version + (m.pub_date ? ' · ' + m.pub_date.slice(0, 10) : '') + (pick ? ' · ' + mb(d[pick].size) : '') + (here === 'mobile' ? ' · the desktop app runs on macOS, Windows and Linux' : '');
|
||||
primary.appendChild(el('p', { 'class': 'meta' }, meta));
|
||||
var unsigned = keys.filter(function (k) { return !d[k].codeSigned && (k === 'macos-universal-dmg' || k === 'windows-x86_64-nsis'); });
|
||||
if (unsigned.length) {
|
||||
var n = el('div', { 'class': 'dl-notice', role: 'note' });
|
||||
n.appendChild(el('b', null, 'Unsigned preview. '));
|
||||
n.appendChild(document.createTextNode('This early build is not yet signed by Apple or Microsoft, so your system asks once before opening it. Check the SHA-256 below first.'));
|
||||
var ol = el('ol');
|
||||
if (unsigned.indexOf('macos-universal-dmg') >= 0) ol.appendChild(el('li', null, 'macOS: drag storebound to Applications and open it; when macOS refuses, open System Settings > Privacy & Security and choose Open Anyway (once).'));
|
||||
if (unsigned.indexOf('windows-x86_64-nsis') >= 0) ol.appendChild(el('li', null, 'Windows: when SmartScreen shows "Windows protected your PC", choose More info > Run anyway.'));
|
||||
n.appendChild(ol);
|
||||
notice.appendChild(n);
|
||||
}
|
||||
var rows = document.getElementById('rows');
|
||||
keys.forEach(function (k) {
|
||||
var x = d[k];
|
||||
var tr = el('tr', k === pick ? { 'class': 'here' } : {});
|
||||
var sys = el('td');
|
||||
sys.appendChild(el('b', null, LABEL[k][0]));
|
||||
sys.appendChild(el('div', { 'class': 'small' }, LABEL[k][1] + ((k === 'macos-universal-dmg' || k === 'windows-x86_64-nsis') && !x.codeSigned ? ' · unsigned preview' : '')));
|
||||
var file = el('td');
|
||||
file.appendChild(el('a', { 'class': 'fname', href: x.url }, x.file));
|
||||
var sub = el('div', { 'class': 'small' }, mb(x.size) + ' · ');
|
||||
sub.appendChild(el('a', { href: x.url + '.sig' }, 'signature'));
|
||||
file.appendChild(sub);
|
||||
var sha = el('td');
|
||||
sha.appendChild(el('code', { 'class': 'sha' }, x.sha256));
|
||||
tr.appendChild(sys); tr.appendChild(file); tr.appendChild(sha);
|
||||
rows.appendChild(tr);
|
||||
});
|
||||
document.getElementById('table').hidden = false;
|
||||
}).catch(function (e) {
|
||||
var none = e && e.message === 'none';
|
||||
status.textContent = none
|
||||
? 'The first desktop release is not published yet. Until then the storebound CLI does everything the app will show.'
|
||||
: 'Could not read the release list (' + (e && e.message) + '). latest.json has every file and its SHA-256.';
|
||||
if (none) primary.appendChild(el('a', { 'class': 'btn btn-ghost', href: '../#start' }, 'Get the CLI'));
|
||||
else primary.appendChild(el('a', { 'class': 'btn btn-ghost', href: 'latest.json' }, 'latest.json'));
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<!-- The desktop app is a section of the home page now; this URL stays because the CLI (cli/lib/desktop-dist.ts PAGE)
|
||||
and older links point here. latest.json and dl/ next to it are served by the release server, not this file. -->
|
||||
<p>The desktop app moved to the home page: <a href="../#download">storebound.dev/#download</a>.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue