img2threejs / app /static /index.html
Mike0021's picture
Add persistent community gallery and v1.3 previews
37e3d5a verified
Raw
History Blame Contribute Delete
16.5 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#111319">
<title>img2threejs — image to procedural Three.js</title>
<meta name="description" content="Turn one reference image into an inspectable procedural Three.js draft, then explore results shared by the community.">
<link rel="icon" href="/static/logo.svg" type="image/svg+xml">
<link rel="stylesheet" href="/static/styles.css">
</head>
<body>
<a class="skip-link" href="#main-content">Skip to content</a>
<header class="site-header">
<a class="brand" href="/" aria-label="img2threejs home">
<img src="/static/logo.svg" alt="" width="38" height="38">
<span>
<strong>img2threejs</strong>
<small>Image → procedural Three.js</small>
</span>
</a>
<nav class="site-nav" aria-label="Primary navigation">
<button id="nav-create" class="nav-link is-active" type="button" aria-current="page">Create</button>
<button id="nav-gallery" class="nav-link" type="button">
Community gallery
<span class="nav-dot" aria-hidden="true"></span>
</button>
</nav>
<span id="llm-badge" class="badge badge-unknown" title="Checking model configuration" aria-live="polite">
Checking model
</span>
</header>
<main id="main-content">
<section id="panel-upload" class="panel upload-panel" aria-labelledby="upload-title">
<div class="panel-heading">
<div>
<p class="eyebrow">Create a procedural draft</p>
<h1 id="upload-title">Turn one image into editable Three.js</h1>
<p>Choose a clear, single-object reference. The model writes a structured sculpt spec and a procedural factory you can inspect, run, and refine.</p>
</div>
<span class="step-chip">01 · Reference</span>
</div>
<div id="dropzone" class="dropzone" tabindex="0" role="button"
aria-label="Upload a reference image" aria-describedby="drop-help">
<span class="upload-mark" aria-hidden="true"></span>
<p class="drop-title">Drop a reference image here</p>
<p id="drop-help" class="drop-sub">Click to browse or paste from clipboard · PNG, JPEG, WebP, GIF, BMP · ≤ 10 MiB</p>
<input id="file-input" type="file" accept="image/png,image/jpeg,image/webp,image/gif,image/bmp" hidden>
</div>
<div class="share-control">
<label class="switch-row" for="share-toggle">
<input id="share-toggle" type="checkbox" checked>
<span class="switch" aria-hidden="true"><span></span></span>
<span class="switch-copy">
<strong>Share this result in the public gallery</strong>
<small>On by default. Your uploaded reference, generated preview, title, and downloadable artifacts will be visible to anyone. Turn this off to keep the result out of the gallery; its hard-to-guess job links remain temporarily accessible to anyone who has them.</small>
</span>
</label>
</div>
<div id="preview-row" class="preview-row" hidden>
<div class="preview-image-wrap">
<img id="preview" alt="Uploaded reference preview">
<span class="reference-label">Reference</span>
</div>
<div class="preview-meta">
<div>
<div id="preview-name" class="preview-name"></div>
<div id="palette" class="palette" title="Approximate dominant colours sampled in your browser"></div>
</div>
<label class="hint-label" for="hint">What is the object? <span>Optional, but useful for ambiguous references</span></label>
<input id="hint" type="text" maxlength="80" autocomplete="off" placeholder="e.g. a red diesel locomotive">
<button id="run-btn" class="primary" type="button">
<span>Generate Three.js preview</span>
<span aria-hidden="true"></span>
</button>
</div>
</div>
<p class="honesty-note">
<strong>What to expect.</strong> This is an approximate, unreviewed procedural reconstruction from one image.
Hidden geometry is inferred—not observed or measured. If the reference cannot support a credible reconstruction,
the pipeline stops with an explanation instead of fabricating confidence.
</p>
</section>
<section id="panel-progress" class="panel progress-panel" aria-labelledby="progress-title" hidden>
<div class="panel-heading compact">
<div>
<p class="eyebrow">Generation in progress</p>
<h1 id="progress-title">Building your procedural draft</h1>
<p>Provider latency and validator repair rounds can take several minutes. Every update below comes from the running job—there are no estimated percentages.</p>
</div>
<span id="progress-connection" class="status-pill status-working" aria-live="polite">Connecting</span>
</div>
<div class="progress-facts" aria-label="Live generation details">
<div class="fact-card">
<span>Elapsed</span>
<strong id="progress-elapsed">0:00</strong>
<small>Since submission</small>
</div>
<div class="fact-card fact-wide">
<span>Latest activity</span>
<strong id="progress-activity">Uploading your reference…</strong>
<small id="progress-activity-time">Waiting for the first pipeline event</small>
</div>
<div id="progress-attempt-card" class="fact-card" hidden>
<span>Model attempt</span>
<strong id="progress-attempt"></strong>
<small>Reported by the validator loop</small>
</div>
<div class="fact-card">
<span>Gallery</span>
<strong id="progress-sharing">Public</strong>
<small>Choice locked for this run</small>
</div>
</div>
<ol id="stages" class="stages" aria-label="Pipeline stages" aria-live="polite"></ol>
<details class="log-drawer">
<summary>
<span>Live event log</span>
<small>Exact messages from the server</small>
</summary>
<pre id="log" class="log" aria-live="polite"></pre>
</details>
</section>
<section id="panel-result" class="panel result-panel" aria-labelledby="result-title" hidden>
<div class="result-topline">
<div>
<p class="eyebrow">Generation complete</p>
<h1 id="result-title">Result</h1>
<p id="result-sub" class="result-sub"></p>
</div>
<div class="result-actions">
<span id="result-share-status" class="status-pill"></span>
<button id="gallery-result-link" class="quiet-button" type="button" hidden>View in gallery →</button>
</div>
</div>
<div class="result-grid">
<div class="viewer-column">
<div id="viewer-frame-wrap" class="viewer-frame-wrap" aria-busy="true">
<iframe id="viewer-frame" title="Generated 3D model viewer"
sandbox="allow-scripts" referrerpolicy="no-referrer"></iframe>
<div id="viewer-overlay" class="viewer-overlay">
<div>
<span class="spinner" aria-hidden="true"></span>
<p id="viewer-overlay-copy">Starting the secure viewer…</p>
<button id="viewer-retry" class="overlay-action" type="button" hidden>Retry viewer</button>
</div>
</div>
</div>
<div class="viewer-toolbar" aria-label="Preview options">
<button id="viewer-wireframe" type="button" aria-pressed="false" disabled>Wireframe</button>
<button id="viewer-shadows" type="button" aria-pressed="true" disabled>Shadows</button>
<button id="viewer-reset-camera" type="button" disabled>Reset view</button>
<span id="viewer-control-status" role="status" aria-live="polite"></span>
</div>
<div class="viewer-caption">
<span>Drag to orbit · scroll to zoom · right-drag to pan</span>
<span id="viewer-stats" aria-live="polite">Preparing model</span>
</div>
</div>
<aside class="result-rail" aria-label="Result details and downloads">
<div class="rail-section">
<h2>Keep the useful parts</h2>
<p>The factory and spec are the editable source of truth. The viewer exports are convenient previews.</p>
<div class="downloads">
<a id="dl-ts" class="button download-button" download>
<span><strong>Factory</strong><small>Editable TypeScript</small></span><b>.ts</b>
</a>
<a id="dl-spec" class="button download-button" download>
<span><strong>Sculpt spec</strong><small>Structured source</small></span><b>.json</b>
</a>
<a id="dl-bundle" class="button download-button" download>
<span><strong>Model bundle</strong><small>Browser-ready module</small></span><b>.js</b>
</a>
<a id="dl-standalone" class="button download-button" download>
<span><strong>Standalone viewer</strong><small>Self-contained preview</small></span><b>.html</b>
</a>
<button id="dl-shot" class="button download-button" type="button" disabled>
<span><strong>Screenshot</strong><small id="shot-status" aria-live="polite">Available when the viewer is ready</small></span><b>.png</b>
</button>
</div>
</div>
<div id="result-publication-warning" class="publication-warning" role="status" hidden>
<strong>Gallery publication warning</strong>
<p id="result-publication-warning-copy"></p>
</div>
<details id="warnings-box" class="warnings-box" hidden>
<summary>Validator notes <span id="warnings-count"></span></summary>
<ul id="warnings-list"></ul>
</details>
<div class="honesty-box">
<h2>Honesty notes</h2>
<ul id="honesty-list"></ul>
</div>
<button id="again-btn" class="secondary" type="button">Create another model</button>
</aside>
</div>
</section>
<section id="panel-gallery" class="panel gallery-panel" aria-labelledby="gallery-title" hidden>
<div class="panel-heading">
<div>
<p class="eyebrow">Made by this community</p>
<h1 id="gallery-title">Community gallery</h1>
<p>Browse public results shared by creators. Every card opens the generated model in the same secure, interactive viewer and links to stable artifacts.</p>
</div>
<button id="gallery-refresh" class="quiet-button" type="button">Refresh</button>
</div>
<div class="gallery-disclosure">
<span aria-hidden="true"></span>
<p><strong>Public means public.</strong> Gallery references and generated artifacts can be viewed and downloaded by anyone. Turn sharing off to avoid publication; temporary job links are unlisted, not access-controlled.</p>
</div>
<p id="gallery-status" class="sr-only" role="status" aria-live="polite"></p>
<div id="gallery-loading" class="gallery-loading" aria-hidden="true">
<span></span><span></span><span></span>
</div>
<div id="gallery-error" class="inline-state inline-error" hidden>
<div>
<strong>Could not load the gallery</strong>
<p id="gallery-error-message"></p>
</div>
<button id="gallery-retry" class="quiet-button" type="button">Try again</button>
</div>
<div id="gallery-empty" class="inline-state gallery-empty" hidden>
<span aria-hidden="true"></span>
<div>
<strong>The gallery is ready for its first model</strong>
<p>Generate a result with sharing enabled and it will appear here.</p>
</div>
<button id="gallery-empty-create" class="primary compact-button" type="button">Create one</button>
</div>
<div id="gallery-grid" class="gallery-grid"></div>
<div class="gallery-more">
<button id="gallery-more" class="secondary inline-button" type="button" hidden>Load more</button>
</div>
</section>
<section id="panel-error" class="panel panel-error" aria-labelledby="error-title" hidden>
<div class="error-mark" aria-hidden="true">!</div>
<div>
<p class="eyebrow">Pipeline stopped</p>
<h1 id="error-title">Something went wrong</h1>
<p id="error-message"></p>
<pre id="error-detail" class="error-detail" hidden></pre>
<button id="error-again-btn" class="secondary inline-button" type="button">Start over</button>
</div>
</section>
</main>
<dialog id="gallery-dialog" class="gallery-dialog" aria-labelledby="gallery-detail-title">
<div class="dialog-shell">
<button id="gallery-detail-close" class="dialog-close" type="button" aria-label="Close gallery detail">×</button>
<div id="gallery-detail-loading" class="detail-loading">
<span class="spinner" aria-hidden="true"></span>
<p>Loading the shared model…</p>
</div>
<div id="gallery-detail-error" class="inline-state inline-error" hidden>
<div>
<strong>Could not open this gallery item</strong>
<p id="gallery-detail-error-message"></p>
</div>
</div>
<div id="gallery-detail-content" hidden>
<div class="detail-heading">
<div>
<p class="eyebrow">Community result</p>
<h1 id="gallery-detail-title">Shared model</h1>
<p id="gallery-detail-meta"></p>
</div>
<img id="gallery-detail-reference" alt="Shared reference image" loading="lazy">
</div>
<div class="detail-grid">
<div>
<div id="gallery-viewer-wrap" class="viewer-frame-wrap gallery-viewer-wrap" aria-busy="true">
<iframe id="gallery-viewer-frame" title="Shared 3D model viewer"
sandbox="allow-scripts" referrerpolicy="no-referrer"></iframe>
<div id="gallery-viewer-overlay" class="viewer-overlay">
<div>
<span class="spinner" aria-hidden="true"></span>
<p id="gallery-viewer-overlay-copy">Starting the secure viewer…</p>
<button id="gallery-viewer-retry" class="overlay-action" type="button" hidden>Retry viewer</button>
</div>
</div>
</div>
<div class="viewer-toolbar" aria-label="Shared preview options">
<button id="gallery-viewer-wireframe" type="button" aria-pressed="false" disabled>Wireframe</button>
<button id="gallery-viewer-shadows" type="button" aria-pressed="true" disabled>Shadows</button>
<button id="gallery-viewer-reset-camera" type="button" disabled>Reset view</button>
<span id="gallery-viewer-control-status" role="status" aria-live="polite"></span>
</div>
<div class="viewer-caption">
<span>Drag to orbit · scroll to zoom · right-drag to pan</span>
<span id="gallery-viewer-stats" aria-live="polite">Preparing model</span>
</div>
</div>
<aside class="detail-rail">
<h2>Shared artifacts</h2>
<p>These files are public copies with stable gallery URLs.</p>
<div class="downloads detail-downloads">
<a id="gallery-dl-reference" class="button">Reference image <b></b></a>
<a id="gallery-dl-ts" class="button" download>Factory <b>.ts</b></a>
<a id="gallery-dl-spec" class="button" download>Sculpt spec <b>.json</b></a>
<a id="gallery-dl-bundle" class="button" download>Model bundle <b>.js</b></a>
<a id="gallery-dl-standalone" class="button" download>Standalone viewer <b>.html</b></a>
<button id="gallery-dl-shot" class="button" type="button" disabled>
Screenshot <b>.png</b>
</button>
</div>
<p id="gallery-shot-status" class="action-status" aria-live="polite">Available when the viewer is ready</p>
<p class="detail-honesty">This is an approximate, unreviewed reconstruction from a single image. Public gallery inclusion is not a visual-quality approval.</p>
</aside>
</div>
</div>
</div>
</dialog>
<footer class="site-footer">
<p>
Built on
<a href="https://github.com/hoainho/img2threejs" target="_blank" rel="noopener noreferrer">hoainho/img2threejs</a>
(MIT) · reconstruction-by-code, not photogrammetry ·
<a href="https://huggingface.co/spaces/Mike0021/img2threejs" target="_blank" rel="noopener noreferrer">Hugging Face Space</a>
</p>
</footer>
<script type="module" src="/static/app.js"></script>
</body>
</html>