feat(site): wire landing to deployed app demo

- All CTAs (nav, hero, install note, gallery, catalog, footer) link to
  https://containr-app.vercel.app - the app frontend deployed as a
  public demo (VITE_PUBLIC_DEMO build).
- templates.html: per-template "Open in demo app" deep links via
  ?template=<id>; dead default API removed so the official snapshot
  renders instantly until an instance is configured with ?api=.
- Hero canvas screenshot updated to the fullscreen canvas build.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas DvorakandDevin committed 2026-09-23 17:41:43 +02:00
1 parent 117b469fae
commit e825eb0d7c
4 files changed
+46 -36

No files matched your search

+11 -9
View File
@@ -34,7 +34,7 @@
<a class="gh-link" href="https://github.com/Dvorinka/Containr" target="_blank" rel="noopener" aria-label="Containr on GitHub"> <a class="gh-link" href="https://github.com/Dvorinka/Containr" target="_blank" rel="noopener" aria-label="Containr on GitHub">
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z"/></svg> <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z"/></svg>
</a> </a>
<a class="btn btn-primary" href="#install">Get started</a> <a class="btn btn-primary" href="https://containr-app.vercel.app/projects">Live demo</a>
</div> </div>
</div> </div>
</nav> </nav>
@@ -48,16 +48,16 @@
<h1>Deploy containers on <em>your own cloud</em></h1> <h1>Deploy containers on <em>your own cloud</em></h1>
<p class="sub">Containr turns a Docker host into a full deployment platform. Git builds, managed databases, previews, failover.</p> <p class="sub">Containr turns a Docker host into a full deployment platform. Git builds, managed databases, previews, failover.</p>
<div class="hero-cta"> <div class="hero-cta">
<a class="btn btn-primary" href="#install"> <a class="btn btn-primary" href="https://containr-app.vercel.app/projects">
Get started Explore the live demo
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg> <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg>
</a> </a>
<a class="btn btn-ghost" href="https://github.com/Dvorinka/Containr" target="_blank" rel="noopener">View on GitHub</a> <a class="btn btn-ghost" href="#install">Get started</a>
</div> </div>
<div class="shot-frame reveal"> <div class="shot-frame reveal">
<div class="shot-bar"><span class="addr">localhost:3000/canvas</span></div> <div class="shot-bar"><span class="addr">containr-app.vercel.app/projects/demo-project-core</span></div>
<img src="screenshots/canvas.png" alt="Containr project canvas with two connected services" fetchpriority="high"> <img src="screenshots/canvas.png" alt="Containr fullscreen project canvas with connected services" fetchpriority="high">
</div> </div>
</div> </div>
</header> </header>
@@ -67,7 +67,7 @@
<div class="install-inner reveal"> <div class="install-inner reveal">
<div class="install-copy"> <div class="install-copy">
<h2>One command, running stack</h2> <h2>One command, running stack</h2>
<p>Requires Docker and openssl. Installs into ./containr, never overwrites your .env.</p> <p>Requires Docker and openssl. Installs into ./containr, never overwrites your .env. <a href="https://containr-app.vercel.app/projects" style="color: var(--accent);">Or explore the hosted demo first &rarr;</a></p>
</div> </div>
<div class="cmd"> <div class="cmd">
<code id="install-cmd">curl -fsSL https://raw.githubusercontent.com/Dvorinka/Containr/main/install.sh | bash</code> <code id="install-cmd">curl -fsSL https://raw.githubusercontent.com/Dvorinka/Containr/main/install.sh | bash</code>
@@ -154,6 +154,7 @@
</div> </div>
<img id="gallery-img" src="screenshots/dashboard.png" alt="Containr dashboard" loading="lazy"> <img id="gallery-img" src="screenshots/dashboard.png" alt="Containr dashboard" loading="lazy">
<p class="cap" id="gallery-cap">Projects dashboard with host CPU, memory, and disk telemetry.</p> <p class="cap" id="gallery-cap">Projects dashboard with host CPU, memory, and disk telemetry.</p>
<p class="cap"><a href="https://containr-app.vercel.app/projects/demo-project-core" style="color: var(--accent);">Try the canvas live &rarr;</a></p>
</div> </div>
</div> </div>
</section> </section>
@@ -162,7 +163,7 @@
<div class="wrap"> <div class="wrap">
<div class="sec-head reveal"> <div class="sec-head reveal">
<h2>A catalog worth browsing</h2> <h2>A catalog worth browsing</h2>
<p>Thirty official templates plus community presets. <a href="templates.html" style="color: var(--accent);">Browse the full catalog</a> or publish your own.</p> <p>Thirty official templates plus community presets. <a href="templates.html" style="color: var(--accent);">Browse the full catalog</a>, <a href="https://containr-app.vercel.app/templates" style="color: var(--accent);">open it in the app</a>, or publish your own.</p>
</div> </div>
<div class="marquee reveal" aria-label="Template catalog"> <div class="marquee reveal" aria-label="Template catalog">
<div class="marquee-track" id="marquee-track"> <div class="marquee-track" id="marquee-track">
@@ -231,7 +232,7 @@
Get started Get started
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg> <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg>
</a> </a>
<a class="btn btn-ghost" href="https://github.com/Dvorinka/Containr" target="_blank" rel="noopener">Star on GitHub</a> <a class="btn btn-ghost" href="https://containr-app.vercel.app/projects">Live demo</a>
</div> </div>
</div> </div>
</div> </div>
@@ -240,6 +241,7 @@
<div class="wrap foot"> <div class="wrap foot">
<span class="l"><img src="containr.svg" alt="">Containr - MIT License</span> <span class="l"><img src="containr.svg" alt="">Containr - MIT License</span>
<nav> <nav>
<a href="https://containr-app.vercel.app/projects">Live demo</a>
<a href="https://github.com/Dvorinka/Containr" target="_blank" rel="noopener">GitHub</a> <a href="https://github.com/Dvorinka/Containr" target="_blank" rel="noopener">GitHub</a>
<a href="https://github.com/Dvorinka/Containr/tree/main/docs" target="_blank" rel="noopener">Docs</a> <a href="https://github.com/Dvorinka/Containr/tree/main/docs" target="_blank" rel="noopener">Docs</a>
<a href="https://github.com/Dvorinka/Containr/blob/main/CONTRIBUTING.md" target="_blank" rel="noopener">Contribute</a> <a href="https://github.com/Dvorinka/Containr/blob/main/CONTRIBUTING.md" target="_blank" rel="noopener">Contribute</a>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 145 KiB

+29 -21
View File
@@ -317,7 +317,7 @@
<a class="gh-link" href="https://github.com/Dvorinka/Containr" target="_blank" rel="noopener" aria-label="Containr on GitHub"> <a class="gh-link" href="https://github.com/Dvorinka/Containr" target="_blank" rel="noopener" aria-label="Containr on GitHub">
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z"/></svg> <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z"/></svg>
</a> </a>
<a class="btn btn-primary" href="index.html#install">Get started</a> <a class="btn btn-primary" href="https://containr-app.vercel.app/projects">Live demo</a>
</div> </div>
</div> </div>
</nav> </nav>
@@ -370,6 +370,7 @@
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="12" height="12" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg> <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="12" height="12" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
Copy template JSON Copy template JSON
</button> </button>
<a class="btn btn-ghost" id="m-open" href="https://containr-app.vercel.app/templates" target="_blank" rel="noopener">Open in demo app</a>
<a class="btn btn-ghost" href="https://github.com/Dvorinka/Containr/tree/main/docs/guides" target="_blank" rel="noopener">Deploy guide</a> <a class="btn btn-ghost" href="https://github.com/Dvorinka/Containr/tree/main/docs/guides" target="_blank" rel="noopener">Deploy guide</a>
</div> </div>
<p class="hint">In your instance: <code>Templates &rarr; New template &rarr; Upload .json</code> (or paste the copied JSON). Deploy it to any project from there.</p> <p class="hint">In your instance: <code>Templates &rarr; New template &rarr; Upload .json</code> (or paste the copied JSON). Deploy it to any project from there.</p>
@@ -380,6 +381,7 @@
<div class="wrap foot"> <div class="wrap foot">
<span class="l"><img src="containr.svg" alt="">Containr - MIT License</span> <span class="l"><img src="containr.svg" alt="">Containr - MIT License</span>
<nav> <nav>
<a href="https://containr-app.vercel.app/projects">Live demo</a>
<a href="https://github.com/Dvorinka/Containr" target="_blank" rel="noopener">GitHub</a> <a href="https://github.com/Dvorinka/Containr" target="_blank" rel="noopener">GitHub</a>
<a href="https://github.com/Dvorinka/Containr/tree/main/docs" target="_blank" rel="noopener">Docs</a> <a href="https://github.com/Dvorinka/Containr/tree/main/docs" target="_blank" rel="noopener">Docs</a>
<a href="https://github.com/Dvorinka/Containr/blob/main/CONTRIBUTING.md" target="_blank" rel="noopener">Contribute</a> <a href="https://github.com/Dvorinka/Containr/blob/main/CONTRIBUTING.md" target="_blank" rel="noopener">Contribute</a>
@@ -389,14 +391,15 @@
</footer> </footer>
<script> <script>
// The catalog comes from a live Containr instance when one is reachable. // The catalog comes from a live Containr instance when one is configured.
// Default is the project's public API; override once with ?api=https://host // Point it at an instance once with ?api=https://host (persisted in
// (persisted in localStorage) or fall back to the bundled snapshot. // localStorage); with no instance configured the bundled official
const DEFAULT_API = 'https://containr-api.up.railway.app'; // snapshot renders immediately.
const APP_BASE = 'https://containr-app.vercel.app';
const params = new URLSearchParams(location.search); const params = new URLSearchParams(location.search);
const paramApi = params.get('api'); const paramApi = params.get('api');
if (paramApi) localStorage.setItem('containr-api', paramApi.replace(/\/$/, '')); if (paramApi) localStorage.setItem('containr-api', paramApi.replace(/\/$/, ''));
const API_BASE = (localStorage.getItem('containr-api') || DEFAULT_API).replace(/\/$/, ''); const API_BASE = (localStorage.getItem('containr-api') || '').replace(/\/$/, '');
const grid = document.getElementById('grid'); const grid = document.getElementById('grid');
const catsEl = document.getElementById('cats'); const catsEl = document.getElementById('cats');
@@ -412,22 +415,26 @@
const esc = (s) => String(s ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c])); const esc = (s) => String(s ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
async function loadTemplates() { async function loadTemplates() {
const ctrl = new AbortController(); if (API_BASE) {
const timer = setTimeout(() => ctrl.abort(), 4000); const ctrl = new AbortController();
try { const timer = setTimeout(() => ctrl.abort(), 4000);
const res = await fetch(API_BASE + '/api/v1/templates', { signal: ctrl.signal }); try {
if (!res.ok) throw new Error('api ' + res.status); const res = await fetch(API_BASE + '/api/v1/templates', { signal: ctrl.signal });
const data = await res.json(); if (!res.ok) throw new Error('api ' + res.status);
const list = Array.isArray(data.templates) ? data.templates : []; const data = await res.json();
if (!list.length) throw new Error('empty catalog'); const list = Array.isArray(data.templates) ? data.templates : [];
setTemplates(list, 'live: ' + API_BASE.replace(/^https?:\/\//, ''), true); if (!list.length) throw new Error('empty catalog');
} catch { setTemplates(list, 'live: ' + API_BASE.replace(/^https?:\/\//, ''), true);
const res = await fetch('templates.json'); return;
const data = await res.json(); } catch {
setTemplates(data.templates || [], 'offline snapshot - official catalog', false); // fall through to the bundled snapshot
} finally { } finally {
clearTimeout(timer); clearTimeout(timer);
}
} }
const res = await fetch('templates.json');
const data = await res.json();
setTemplates(data.templates || [], 'official snapshot - connect an instance with ?api=', false);
} }
function setTemplates(list, label, live) { function setTemplates(list, label, live) {
@@ -534,6 +541,7 @@
document.getElementById('m-badges').innerHTML = document.getElementById('m-badges').innerHTML =
badge(t) + `<span class="badge" style="background:var(--surface-muted);color:var(--text-tertiary)">${esc(t.category || 'custom')}</span>`; badge(t) + `<span class="badge" style="background:var(--surface-muted);color:var(--text-tertiary)">${esc(t.category || 'custom')}</span>`;
document.getElementById('m-desc').textContent = t.description || 'No description.'; document.getElementById('m-desc').textContent = t.description || 'No description.';
document.getElementById('m-open').href = `${APP_BASE}/templates?template=${encodeURIComponent(t.id)}`;
const c = cfg(t); const c = cfg(t);
const meta = []; const meta = [];
+6 -6
View File
@@ -6,7 +6,7 @@
"description": "Generic Node.js application with automatic dependency detection", "description": "Generic Node.js application with automatic dependency detection",
"category": "web", "category": "web",
"logo": "https://cdn.simpleicons.org/node.js", "logo": "https://cdn.simpleicons.org/node.js",
"config": "{\"type\":\"web\",\"runtime\":\"node\",\"build_command\":\"npm install \u0026\u0026 npm run build\",\"start_command\":\"npm start\",\"port\":3000,\"health_check\":\"/health\"}", "config": "{\"type\":\"web\",\"runtime\":\"node\",\"build_command\":\"npm install && npm run build\",\"start_command\":\"npm start\",\"port\":3000,\"health_check\":\"/health\"}",
"variables": "[{\"key\":\"NODE_ENV\",\"label\":\"Node Environment\",\"default\":\"production\",\"required\":false,\"secret\":false},{\"key\":\"NPM_TOKEN\",\"label\":\"NPM Token\",\"default\":\"\",\"required\":false,\"secret\":true}]", "variables": "[{\"key\":\"NODE_ENV\",\"label\":\"Node Environment\",\"default\":\"production\",\"required\":false,\"secret\":false},{\"key\":\"NPM_TOKEN\",\"label\":\"NPM Token\",\"default\":\"\",\"required\":false,\"secret\":true}]",
"is_official": true, "is_official": true,
"is_public": false, "is_public": false,
@@ -19,7 +19,7 @@
"description": "React single-page application with Vite", "description": "React single-page application with Vite",
"category": "frontend", "category": "frontend",
"logo": "https://cdn.simpleicons.org/react", "logo": "https://cdn.simpleicons.org/react",
"config": "{\"type\":\"web\",\"runtime\":\"node\",\"build_command\":\"npm install \u0026\u0026 npm run build\",\"start_command\":\"npx serve -s dist\",\"port\":3000}", "config": "{\"type\":\"web\",\"runtime\":\"node\",\"build_command\":\"npm install && npm run build\",\"start_command\":\"npx serve -s dist\",\"port\":3000}",
"variables": "[{\"key\":\"VITE_API_URL\",\"label\":\"API URL\",\"default\":\"\",\"required\":true,\"secret\":false}]", "variables": "[{\"key\":\"VITE_API_URL\",\"label\":\"API URL\",\"default\":\"\",\"required\":true,\"secret\":false}]",
"is_official": true, "is_official": true,
"is_public": false, "is_public": false,
@@ -148,7 +148,7 @@
"name": "Background Worker", "name": "Background Worker",
"description": "Background job processing service", "description": "Background job processing service",
"category": "worker", "category": "worker",
"logo": "https://cdn.simpleicons.org/terminal", "logo": "",
"config": "{\"type\":\"worker\",\"runtime\":\"node\",\"build_command\":\"npm install\",\"start_command\":\"npm run worker\"}", "config": "{\"type\":\"worker\",\"runtime\":\"node\",\"build_command\":\"npm install\",\"start_command\":\"npm run worker\"}",
"variables": "[{\"key\":\"WORKER_CONCURRENCY\",\"label\":\"Concurrency\",\"default\":\"4\",\"required\":false,\"secret\":false}]", "variables": "[{\"key\":\"WORKER_CONCURRENCY\",\"label\":\"Concurrency\",\"default\":\"4\",\"required\":false,\"secret\":false}]",
"is_official": true, "is_official": true,
@@ -161,7 +161,7 @@
"name": "Cron Job", "name": "Cron Job",
"description": "Scheduled task runner", "description": "Scheduled task runner",
"category": "cron", "category": "cron",
"logo": "https://cdn.simpleicons.org/clock", "logo": "",
"config": "{\"type\":\"cron\",\"runtime\":\"node\",\"build_command\":\"npm install\",\"start_command\":\"npm run cron\"}", "config": "{\"type\":\"cron\",\"runtime\":\"node\",\"build_command\":\"npm install\",\"start_command\":\"npm run cron\"}",
"variables": "[{\"key\":\"CRON_SCHEDULE\",\"label\":\"Schedule\",\"default\":\"0 * * * *\",\"required\":true,\"secret\":false}]", "variables": "[{\"key\":\"CRON_SCHEDULE\",\"label\":\"Schedule\",\"default\":\"0 * * * *\",\"required\":true,\"secret\":false}]",
"is_official": true, "is_official": true,
@@ -226,7 +226,7 @@
"name": "Uptime Kuma", "name": "Uptime Kuma",
"description": "Self-hosted monitoring tool for services and websites", "description": "Self-hosted monitoring tool for services and websites",
"category": "app", "category": "app",
"logo": "https://cdn.simpleicons.org/uptimerobot", "logo": "https://cdn.simpleicons.org/uptimekuma",
"config": "{\"type\":\"web\",\"runtime\":\"louislam/uptime-kuma:1\",\"port\":3001,\"health_check\":\"/\"}", "config": "{\"type\":\"web\",\"runtime\":\"louislam/uptime-kuma:1\",\"port\":3001,\"health_check\":\"/\"}",
"variables": "[]", "variables": "[]",
"is_official": true, "is_official": true,
@@ -391,4 +391,4 @@
"updated_at": "0001-01-01T00:00:00Z" "updated_at": "0001-01-01T00:00:00Z"
} }
] ]
} }