Spaces:
Runtime error
Runtime error
| #!/usr/bin/env python3 | |
| # flake8: noqa: E501 | |
| # Copyright (c) 2025 ByteDance Ltd. and/or its affiliates | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| """ | |
| Depth Anything 3 Gallery Server (two-level, single-file) | |
| Now supports paginated depth preview (4 per page). | |
| """ | |
| import argparse | |
| import json | |
| import mimetypes | |
| import os | |
| import posixpath | |
| import sys | |
| from functools import partial | |
| from http import HTTPStatus | |
| from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer | |
| from urllib.parse import quote, unquote | |
| # ------------------------------ Embedded HTML ------------------------------ # | |
| HTML_PAGE = r"""<!doctype html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <title>Depth Anything 3 Gallery</title> | |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| <link rel="icon" href="https://i.postimg.cc/rFSzGJ7J/light-icon.jpg" media="(prefers-color-scheme: light)"> | |
| <link rel="icon" href="https://i.postimg.cc/P5gZfJsf/dark-icon.jpg" media="(prefers-color-scheme: dark)"> | |
| <script type="module" src="https://unpkg.com/@google/model-viewer/dist/model-viewer.min.js"></script> | |
| <style> | |
| :root { | |
| --gap:16px; --card-radius:16px; --shadow:0 8px 24px rgba(0,0,0,.12); | |
| --maxW:1036px; --maxH:518px; | |
| --tech-blue: #00d4ff; | |
| --tech-cyan: #00ffcc; | |
| --tech-purple: #7877c6; | |
| } | |
| *{ box-sizing:border-box } | |
| /* Dark mode tech theme */ | |
| @media (prefers-color-scheme: dark) { | |
| body{ | |
| margin:0; font:16px/1.5 system-ui,-apple-system,Segoe UI,Roboto,sans-serif; | |
| background: linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 50%, #16213e 100%); | |
| color:#e8eaed; | |
| position: relative; | |
| overflow-x: hidden; | |
| } | |
| body::before { | |
| content: ''; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background: | |
| radial-gradient(circle at 20% 80%, rgba(120, 119, 198, 0.3) 0%, transparent 50%), | |
| radial-gradient(circle at 80% 20%, rgba(255, 119, 198, 0.3) 0%, transparent 50%), | |
| radial-gradient(circle at 40% 40%, rgba(120, 219, 255, 0.2) 0%, transparent 50%); | |
| animation: techPulse 8s ease-in-out infinite; | |
| z-index: -1; | |
| } | |
| } | |
| /* Light mode tech theme */ | |
| @media (prefers-color-scheme: light) { | |
| body{ | |
| margin:0; font:16px/1.5 system-ui,-apple-system,Segoe UI,Roboto,sans-serif; | |
| background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 50%, #cbd5e1 100%); | |
| color:#1e293b; | |
| position: relative; | |
| overflow-x: hidden; | |
| } | |
| body::before { | |
| content: ''; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background: | |
| radial-gradient(circle at 20% 80%, rgba(0, 212, 255, 0.1) 0%, transparent 50%), | |
| radial-gradient(circle at 80% 20%, rgba(0, 102, 255, 0.1) 0%, transparent 50%), | |
| radial-gradient(circle at 40% 40%, rgba(0, 255, 204, 0.08) 0%, transparent 50%); | |
| animation: techPulse 8s ease-in-out infinite; | |
| z-index: -1; | |
| } | |
| } | |
| @keyframes techPulse { | |
| 0%, 100% { opacity: 0.5; } | |
| 50% { opacity: 0.8; } | |
| } | |
| @keyframes techGradient { | |
| 0% { background-position: 0% 50%; } | |
| 50% { background-position: 100% 50%; } | |
| 100% { background-position: 0% 50%; } | |
| } | |
| /* Dark mode header */ | |
| @media (prefers-color-scheme: dark) { | |
| header{ | |
| padding:20px 24px; position:sticky; top:0; | |
| background:linear-gradient(180deg,rgba(10,10,10,0.9) 60%,rgba(10,10,10,0)); | |
| z-index:2; border-bottom:1px solid rgba(0, 212, 255, 0.2); | |
| backdrop-filter: blur(10px); | |
| } | |
| h1{ | |
| margin:0; font-size:22px; | |
| background: linear-gradient(45deg, var(--tech-blue), var(--tech-cyan), var(--tech-purple)); | |
| background-size: 400% 400%; | |
| -webkit-background-clip: text; | |
| background-clip: text; | |
| color: transparent; | |
| animation: techGradient 3s ease infinite; | |
| text-shadow: 0 0 30px rgba(0, 212, 255, 0.5); | |
| } | |
| .muted{ opacity:.7; font-size:13px; color: #a0a0a0; } | |
| #backBtn{ | |
| display:none; padding:6px 10px; border-radius:10px; | |
| border:1px solid rgba(0, 212, 255, 0.3); | |
| background:rgba(0, 0, 0, 0.3); | |
| color:#e8eaed; cursor:pointer; | |
| transition: all 0.3s ease; | |
| } | |
| #backBtn:hover { | |
| border-color: var(--tech-blue); | |
| box-shadow: 0 0 10px rgba(0, 212, 255, 0.3); | |
| } | |
| #search{ | |
| flex:1 1 260px; min-width:240px; max-width:520px; | |
| padding:10px 14px; border-radius:12px; | |
| border:1px solid rgba(0, 212, 255, 0.3); | |
| background:rgba(0, 0, 0, 0.3); | |
| color:#e8eaed; outline:none; | |
| transition: all 0.3s ease; | |
| } | |
| #search:focus { | |
| border-color: var(--tech-blue); | |
| box-shadow: 0 0 10px rgba(0, 212, 255, 0.3); | |
| } | |
| } | |
| /* Light mode header */ | |
| @media (prefers-color-scheme: light) { | |
| header{ | |
| padding:20px 24px; position:sticky; top:0; | |
| background:linear-gradient(180deg,rgba(248,250,252,0.9) 60%,rgba(248,250,252,0)); | |
| z-index:2; border-bottom:1px solid rgba(0, 212, 255, 0.3); | |
| backdrop-filter: blur(10px); | |
| } | |
| h1{ | |
| margin:0; font-size:22px; | |
| background: linear-gradient(45deg, #0066ff, #00d4ff, #00ffcc); | |
| background-size: 400% 400%; | |
| -webkit-background-clip: text; | |
| background-clip: text; | |
| color: transparent; | |
| animation: techGradient 3s ease infinite; | |
| text-shadow: 0 0 20px rgba(0, 102, 255, 0.3); | |
| } | |
| .muted{ opacity:.7; font-size:13px; color: #64748b; } | |
| #backBtn{ | |
| display:none; padding:6px 10px; border-radius:10px; | |
| border:1px solid rgba(0, 212, 255, 0.4); | |
| background:rgba(255, 255, 255, 0.8); | |
| color:#1e293b; cursor:pointer; | |
| transition: all 0.3s ease; | |
| } | |
| #backBtn:hover { | |
| border-color: #0066ff; | |
| box-shadow: 0 0 10px rgba(0, 102, 255, 0.3); | |
| } | |
| #search{ | |
| flex:1 1 260px; min-width:240px; max-width:520px; | |
| padding:10px 14px; border-radius:12px; | |
| border:1px solid rgba(0, 212, 255, 0.4); | |
| background:rgba(255, 255, 255, 0.8); | |
| color:#1e293b; outline:none; | |
| transition: all 0.3s ease; | |
| } | |
| #search:focus { | |
| border-color: #0066ff; | |
| box-shadow: 0 0 10px rgba(0, 102, 255, 0.3); | |
| } | |
| } | |
| .row{ display:flex; gap:12px; align-items:center; flex-wrap:wrap; justify-content:center; } | |
| main{ padding:16px 24px 24px; display:grid; place-items:center; } | |
| .group-wrap{ width:min(900px,100%); } | |
| .group-list{ list-style:none; margin:0; padding:0; display:grid; gap:10px; } | |
| /* Dark mode cards */ | |
| @media (prefers-color-scheme: dark) { | |
| .group-item{ | |
| display:flex; align-items:center; gap:12px; padding:12px 14px; | |
| background:rgba(0, 0, 0, 0.3); border:1px solid rgba(0, 212, 255, 0.2); border-radius:14px; cursor:pointer; | |
| transition: all 0.3s ease; | |
| backdrop-filter: blur(10px); | |
| } | |
| .group-item:hover{ | |
| transform: translateY(-1px); | |
| border-color:var(--tech-blue); | |
| box-shadow: 0 4px 15px rgba(0, 212, 255, 0.2); | |
| } | |
| .card{ | |
| background:rgba(0, 0, 0, 0.3); border:1px solid rgba(0, 212, 255, 0.2); border-radius:var(--card-radius); | |
| overflow:hidden; box-shadow:var(--shadow); | |
| transition:all 0.3s ease; cursor:pointer; display:flex; flex-direction:column; max-width:var(--maxW); | |
| backdrop-filter: blur(10px); | |
| } | |
| .card:hover{ | |
| transform:translateY(-2px); | |
| border-color:var(--tech-blue); | |
| box-shadow: 0 8px 25px rgba(0, 212, 255, 0.2); | |
| } | |
| .thumb-box{ | |
| position:relative; width:100%; aspect-ratio:2/1; | |
| background:linear-gradient(135deg, #0e121b 0%, #1a1a2e 100%); | |
| display:grid; place-items:center; overflow:hidden; | |
| border-bottom: 1px solid rgba(0, 212, 255, 0.1); | |
| } | |
| .open{ | |
| font-size:12px; opacity:.7; padding:6px 8px; | |
| border:1px solid rgba(0, 212, 255, 0.3); | |
| border-radius:10px; | |
| background:rgba(0, 212, 255, 0.1); | |
| transition: all 0.3s ease; | |
| } | |
| .open:hover { | |
| background:rgba(0, 212, 255, 0.2); | |
| border-color: var(--tech-blue); | |
| } | |
| } | |
| /* Light mode cards */ | |
| @media (prefers-color-scheme: light) { | |
| .group-item{ | |
| display:flex; align-items:center; gap:12px; padding:12px 14px; | |
| background:rgba(255, 255, 255, 0.8); border:1px solid rgba(0, 212, 255, 0.3); border-radius:14px; cursor:pointer; | |
| transition: all 0.3s ease; | |
| backdrop-filter: blur(10px); | |
| box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); | |
| } | |
| .group-item:hover{ | |
| transform: translateY(-1px); | |
| border-color:#0066ff; | |
| box-shadow: 0 4px 15px rgba(0, 102, 255, 0.2); | |
| } | |
| .card{ | |
| background:rgba(255, 255, 255, 0.8); border:1px solid rgba(0, 212, 255, 0.3); border-radius:var(--card-radius); | |
| overflow:hidden; box-shadow:0 4px 6px rgba(0, 0, 0, 0.1); | |
| transition:all 0.3s ease; cursor:pointer; display:flex; flex-direction:column; max-width:var(--maxW); | |
| backdrop-filter: blur(10px); | |
| } | |
| .card:hover{ | |
| transform:translateY(-2px); | |
| border-color:#0066ff; | |
| box-shadow: 0 8px 25px rgba(0, 102, 255, 0.2); | |
| } | |
| .thumb-box{ | |
| position:relative; width:100%; aspect-ratio:2/1; | |
| background:linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%); | |
| display:grid; place-items:center; overflow:hidden; | |
| border-bottom: 1px solid rgba(0, 212, 255, 0.2); | |
| } | |
| .open{ | |
| font-size:12px; opacity:.7; padding:6px 8px; | |
| border:1px solid rgba(0, 212, 255, 0.4); | |
| border-radius:10px; | |
| background:rgba(0, 212, 255, 0.1); | |
| transition: all 0.3s ease; | |
| } | |
| .open:hover { | |
| background:rgba(0, 212, 255, 0.2); | |
| border-color: #0066ff; | |
| } | |
| } | |
| .gname{ font-weight:600; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; width:100%; } | |
| .grid{ | |
| width:min(1200px,100%); | |
| display:grid; | |
| grid-template-columns:repeat(auto-fill,minmax(260px,1fr)); | |
| gap:var(--gap); | |
| align-items:start; | |
| justify-items:stretch; | |
| margin: 0 auto; | |
| padding: 0 20px; | |
| } | |
| .thumb{ max-width:100%; max-height:100%; object-fit:contain; display:block; } | |
| .meta{ padding:12px 14px; display:flex; justify-content:space-between; align-items:center; gap:8px; } | |
| .title{ font-weight:600; font-size:14px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } | |
| .empty{ opacity:.6; padding:40px 0; text-align:center; } | |
| .crumb{ font-size:13px; opacity:.8; } | |
| .overlay{ position:fixed; inset:0; background:rgba(0,0,0,.6); display:none; place-items:center; padding:20px; z-index:10; } | |
| .overlay.show{ display:grid; } | |
| /* Dark mode viewer */ | |
| @media (prefers-color-scheme: dark) { | |
| .viewer{ | |
| inline-size:min(92vw,var(--maxW)); | |
| block-size:min(82vh,var(--maxH)); | |
| background:#0e121b; border:1px solid rgba(0, 212, 255, 0.3); border-radius:18px; overflow:hidden; position:relative; box-shadow:0 12px 36px rgba(0,0,0,.35); | |
| display:grid; | |
| } | |
| .chip{ background:rgba(0,0,0,.45); border:1px solid rgba(0, 212, 255, 0.3); color:#e8eaed; padding:6px 10px; border-radius:12px; font-size:12px; max-width:60%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } | |
| .btn{ margin-left:auto; background:rgba(0, 0, 0, 0.3); color:#e8eaed; border:1px solid rgba(0, 212, 255, 0.3); border-radius:10px; padding:6px 10px; cursor:pointer; transition: all 0.3s ease; } | |
| .btn:hover { border-color: var(--tech-blue); box-shadow: 0 0 10px rgba(0, 212, 255, 0.3); } | |
| .mv-box{ width:100%; aspect-ratio:1036/518; background:#0b0d12; border:1px solid rgba(0, 212, 255, 0.2); border-radius:12px; overflow:hidden; } | |
| .mv-box model-viewer{ width:100%; height:100%; background:#0b0d12; } | |
| .res-cell{ position:relative; width:100%; aspect-ratio:2/1; background:#0e121b; border:1px solid rgba(0, 212, 255, 0.2); border-radius:12px; overflow:hidden; display:grid; place-items:center; } | |
| .res-empty{ position:absolute; inset:0; display:grid; place-items:center; opacity:.55; font-size:12px; color:#9aa0a6; } | |
| .download-icon{ background:rgba(0, 0, 0, 0.6); border:1px solid rgba(0, 212, 255, 0.3); color:#e8eaed; box-shadow:0 4px 12px rgba(0,0,0,0.3); } | |
| .download-icon:hover{ background:rgba(0, 212, 255, 0.2); border-color:var(--tech-blue); box-shadow:0 0 20px rgba(0, 212, 255, 0.4); transform:scale(1.05); } | |
| } | |
| /* Light mode viewer */ | |
| @media (prefers-color-scheme: light) { | |
| .viewer{ | |
| inline-size:min(92vw,var(--maxW)); | |
| block-size:min(82vh,var(--maxH)); | |
| background:#f8fafc; border:1px solid rgba(0, 212, 255, 0.4); border-radius:18px; overflow:hidden; position:relative; box-shadow:0 12px 36px rgba(0,0,0,.15); | |
| display:grid; | |
| } | |
| .chip{ background:rgba(255,255,255,0.8); border:1px solid rgba(0, 212, 255, 0.4); color:#1e293b; padding:6px 10px; border-radius:12px; font-size:12px; max-width:60%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } | |
| .btn{ margin-left:auto; background:rgba(255, 255, 255, 0.8); color:#1e293b; border:1px solid rgba(0, 212, 255, 0.4); border-radius:10px; padding:6px 10px; cursor:pointer; transition: all 0.3s ease; } | |
| .btn:hover { border-color: #0066ff; box-shadow: 0 0 10px rgba(0, 102, 255, 0.3); } | |
| .mv-box{ width:100%; aspect-ratio:1036/518; background:#f8fafc; border:1px solid rgba(0, 212, 255, 0.3); border-radius:12px; overflow:hidden; } | |
| .mv-box model-viewer{ width:100%; height:100%; background:#f8fafc; } | |
| .res-cell{ position:relative; width:100%; aspect-ratio:2/1; background:#f8fafc; border:1px solid rgba(0, 212, 255, 0.3); border-radius:12px; overflow:hidden; display:grid; place-items:center; } | |
| .res-empty{ position:absolute; inset:0; display:grid; place-items:center; opacity:.55; font-size:12px; color:#64748b; } | |
| .download-icon{ background:rgba(255, 255, 255, 0.9); border:1px solid rgba(0, 212, 255, 0.4); color:#1e293b; box-shadow:0 4px 12px rgba(0,0,0,0.15); } | |
| .download-icon:hover{ background:rgba(0, 212, 255, 0.2); border-color:#0066ff; box-shadow:0 0 20px rgba(0, 102, 255, 0.4); transform:scale(1.05); } | |
| } | |
| .viewer-header{ position:absolute; top:8px; left:8px; right:8px; display:flex; gap:8px; align-items:center; z-index:2; } | |
| .viewer-body{ height:100%; display:grid; grid-template-rows:auto auto; gap:12px; padding:36px 8px 8px 8px; overflow:auto; } | |
| .res-grid{ display:grid; grid-template-columns:1fr 1fr; gap:8px; } | |
| .res-img{ max-width:100%; max-height:100%; object-fit:contain; display:block; } | |
| .download-icon{ position:absolute; bottom:16px; right:16px; width:44px; height:44px; border-radius:50%; display:grid; place-items:center; font-size:20px; cursor:pointer; z-index:3; transition:all 0.3s ease; } | |
| /* Pagination controls */ | |
| .pager { | |
| grid-column: 1 / -1; | |
| justify-content: center; | |
| align-items: center; | |
| display: flex; | |
| gap: 16px; | |
| margin-top: 8px; | |
| font-size: 13px; | |
| text-align: center; | |
| } | |
| /* Dark mode pagination */ | |
| @media (prefers-color-scheme: dark) { | |
| .pager { | |
| color: #ccc; | |
| } | |
| .pager button { | |
| padding: 4px 10px; | |
| border-radius: 8px; | |
| border: 1px solid rgba(0, 212, 255, 0.3); | |
| background: rgba(0, 0, 0, 0.3); | |
| color: #e8eaed; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| } | |
| .pager button:hover:not(:disabled) { | |
| border-color: var(--tech-blue); | |
| box-shadow: 0 0 8px rgba(0, 212, 255, 0.2); | |
| } | |
| .pager button:disabled { | |
| opacity: 0.4; | |
| cursor: not-allowed; | |
| } | |
| } | |
| /* Light mode pagination */ | |
| @media (prefers-color-scheme: light) { | |
| .pager { | |
| color: #64748b; | |
| } | |
| .pager button { | |
| padding: 4px 10px; | |
| border-radius: 8px; | |
| border: 1px solid rgba(0, 212, 255, 0.4); | |
| background: rgba(255, 255, 255, 0.8); | |
| color: #1e293b; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| } | |
| .pager button:hover:not(:disabled) { | |
| border-color: #0066ff; | |
| box-shadow: 0 0 8px rgba(0, 102, 255, 0.2); | |
| } | |
| .pager button:disabled { | |
| opacity: 0.4; | |
| cursor: not-allowed; | |
| } | |
| } | |
| /* Intro card styles */ | |
| @media (prefers-color-scheme: dark) { | |
| .intro-card { | |
| background: linear-gradient(135deg, rgba(0, 212, 255, 0.1) 0%, rgba(0, 102, 255, 0.1) 100%); | |
| border: 1px solid rgba(0, 212, 255, 0.2); | |
| backdrop-filter: blur(10px); | |
| } | |
| .intro-title { | |
| background: linear-gradient(45deg, var(--tech-blue), var(--tech-cyan), var(--tech-purple)); | |
| background-size: 400% 400%; | |
| -webkit-background-clip: text; | |
| background-clip: text; | |
| color: transparent; | |
| animation: techGradient 3s ease infinite; | |
| text-shadow: 0 0 20px rgba(0, 212, 255, 0.3); | |
| } | |
| .intro-description { | |
| color: #e0e0e0; | |
| } | |
| } | |
| @media (prefers-color-scheme: light) { | |
| .intro-card { | |
| background: linear-gradient(135deg, rgba(0, 212, 255, 0.05) 0%, rgba(0, 102, 255, 0.05) 100%); | |
| border: 1px solid rgba(0, 212, 255, 0.3); | |
| backdrop-filter: blur(10px); | |
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
| } | |
| .intro-title { | |
| background: linear-gradient(45deg, #0066ff, #00d4ff, #00ffcc); | |
| background-size: 400% 400%; | |
| -webkit-background-clip: text; | |
| background-clip: text; | |
| color: transparent; | |
| animation: techGradient 3s ease infinite; | |
| text-shadow: 0 0 15px rgba(0, 102, 255, 0.2); | |
| } | |
| .intro-description { | |
| color: #334155; | |
| } | |
| } | |
| footer{ | |
| opacity:.55; | |
| font-size:12px; | |
| padding:12px 24px 24px; | |
| text-align:center; | |
| display:flex; | |
| justify-content:center; | |
| align-items:center; | |
| width:100%; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <div class="row"> | |
| <button id="backBtn">← Back</button> | |
| <h1 id="pageTitle">Depth Anything 3 Gallery</h1> | |
| <span id="crumb" class="crumb"></span> | |
| <input id="search" placeholder="Search…" /> | |
| </div> | |
| <div class="muted" id="hint" style="text-align: center;">Level 1 shows groups only; click a group to browse scenes and previews.</div> | |
| </header> | |
| <main> | |
| <!-- Tech intro card --> | |
| <div class="intro-card" style="margin-bottom: 30px; padding: 25px; border-radius: 15px; text-align: center; max-width: 800px;"> | |
| <h2 class="intro-title" style="margin: 0 0 15px 0; font-size: 1.8em; font-weight: 700;"> | |
| 🎯 Depth Anything 3 Gallery | |
| </h2> | |
| <p class="intro-description" style="margin: 0; font-size: 1.1em; line-height: 1.6;"> | |
| Explore 3D reconstructions and depth visualizations from Depth Anything 3. | |
| Browse through groups of scenes, preview 3D models, and examine depth maps interactively. | |
| </p> | |
| </div> | |
| <div id="level1" class="group-wrap" aria-live="polite"> | |
| <ul id="groupList" class="group-list"></ul> | |
| <div id="groupEmpty" class="empty" style="display:none;">No available groups</div> | |
| </div> | |
| <div id="level2" style="display:none; width:100%;" aria-live="polite"> | |
| <div id="topPager" class="pager" style="margin-bottom: 16px;"></div> | |
| <div id="grid" class="grid"></div> | |
| <div id="sceneEmpty" class="empty" style="display:none;">No available scenes in this group</div> | |
| </div> | |
| </main> | |
| <div id="overlay" class="overlay" role="dialog" aria-modal="true" aria-label="3D Preview"> | |
| <div class="viewer" id="viewer"> | |
| <div class="viewer-header"> | |
| <div id="viewerTitle" class="chip">Loading…</div> | |
| <button id="toggleView" class="btn" title="Toggle between 3D-only and resource view">Resource View</button> | |
| <button id="closeBtn" class="btn">Close</button> | |
| </div> | |
| <div id="downloadBtn" class="download-icon" title="Download GLB model">⬇</div> | |
| <div class="viewer-body"> | |
| <div class="mv-box"><model-viewer id="mv" | |
| src="" | |
| ar | |
| camera-controls | |
| auto-rotate | |
| interaction-prompt="auto" | |
| shadow-intensity="0.7" | |
| exposure="1.0" | |
| alt="GLB Preview"></model-viewer></div> | |
| <div class="res-grid" id="resGrid" hidden></div> | |
| </div> | |
| </div> | |
| </div> | |
| <footer>Depth Anything 3 Gallery. Copyright 2025 Depth Anything 3 authors.</footer> | |
| <script> | |
| const level1=document.getElementById('level1'),level2=document.getElementById('level2'),pageTitle=document.getElementById('pageTitle'),crumb=document.getElementById('crumb'),backBtn=document.getElementById('backBtn'),hint=document.getElementById('hint'),searchInput=document.getElementById('search'),groupList=document.getElementById('groupList'),groupEmpty=document.getElementById('groupEmpty'),topPager=document.getElementById('topPager'),grid=document.getElementById('grid'),sceneEmpty=document.getElementById('sceneEmpty'),overlay=document.getElementById('overlay'),viewer=document.getElementById('viewer'),mv=document.getElementById('mv'),viewerTitle=document.getElementById('viewerTitle'),downloadBtn=document.getElementById('downloadBtn'),toggleViewBtn=document.getElementById('toggleView'),closeBtn=document.getElementById('closeBtn'),resGrid=document.getElementById('resGrid'); | |
| let GROUPS=[],SCENES=[],currentGroup=null,currentScene=null,currentPage=1,currentScenePage=1; | |
| const qs=()=>new URLSearchParams(location.search); | |
| async function loadGroups(){const r=await fetch('/manifest.json',{cache:'no-store'});if(!r.ok)throw new Error(r.status+' '+r.statusText);const j=await r.json();GROUPS=j.groups||[];renderGroups(GROUPS);} | |
| async function loadScenes(g){const r=await fetch('/manifest/'+encodeURIComponent(g)+'.json',{cache:'no-store'});if(!r.ok)throw new Error(r.status+' '+r.statusText);const j=await r.json();SCENES=j.items||[];const p=parseInt(qs().get('page'))||1;renderScenes(SCENES,p);} | |
| function renderGroups(list){groupList.innerHTML='';const q=searchInput.value.trim().toLowerCase();const f=list.filter(g=>(g.title||g.id||'').toLowerCase().includes(q));if(!f.length){groupEmpty.style.display='';return;}groupEmpty.style.display='none';for(const g of f){const li=document.createElement('li');li.className='group-item';li.title=g.title||g.id;li.onclick=()=>enterLevel2(g.id,{push:true});const name=document.createElement('div');name.className='gname';name.textContent=g.title||g.id;li.appendChild(name);groupList.appendChild(li);}} | |
| function renderScenes(list,page=1){topPager.innerHTML='';grid.innerHTML='';const q=searchInput.value.trim().toLowerCase();const f=list.filter(x=>(x.title||'').toLowerCase().includes(q)||(x.id||'').toLowerCase().includes(q));if(!f.length){sceneEmpty.style.display='';topPager.style.display='none';return;}sceneEmpty.style.display='none';topPager.style.display='flex';const perPage=16;const total=f.length;const totalPages=Math.max(1,Math.ceil(total/perPage));currentScenePage=page;const u=new URL(location.href);u.searchParams.set('page',page);history.replaceState(null,'',u);const subset=f.slice((page-1)*perPage,page*perPage);for(const i of subset){const c=document.createElement('div');c.className='card';c.title=i.title;const b=document.createElement('div');b.className='thumb-box';const img=document.createElement('img');img.className='thumb';img.loading='lazy';img.alt=i.title;img.src=i.thumbnail;b.appendChild(img);const m=document.createElement('div');m.className='meta';const t=document.createElement('div');t.className='title';t.textContent=i.title;const o=document.createElement('div');o.className='open';o.textContent='Preview';m.appendChild(t);m.appendChild(o);c.appendChild(b);c.appendChild(m);c.onclick=()=>openViewer(i,{push:true});grid.appendChild(c);}function buildPager(){const pg=document.createElement('div');pg.className='pager';const prev=document.createElement('button');prev.textContent='← Prev';prev.disabled=page<=1;prev.onclick=()=>renderScenes(list,page-1);const info=document.createElement('span');info.textContent=`${page} / ${totalPages}`;const next=document.createElement('button');next.textContent='Next →';next.disabled=page>=totalPages;next.onclick=()=>renderScenes(list,page+1);pg.appendChild(prev);pg.appendChild(info);pg.appendChild(next);return pg;}topPager.innerHTML='';topPager.appendChild(buildPager());grid.appendChild(buildPager());} | |
| function enterLevel1({push=false}={}){currentGroup=null;pageTitle.textContent='Depth Anything 3 Gallery';crumb.textContent='';backBtn.style.display='none';hint.style.display='';level1.style.display='';level2.style.display='none';overlay.classList.remove('show');mv.src='';const u=new URL(location.href);u.searchParams.delete('group');u.searchParams.delete('id');u.searchParams.delete('page');push?history.pushState(null,'',u):history.replaceState(null,'',u);searchInput.value='';loadGroups().catch(e=>{groupList.innerHTML='';groupEmpty.style.display='';groupEmpty.textContent='Failed to load groups: '+e;});} | |
| async function enterLevel2(g,{push=false}={}){currentGroup=g;pageTitle.textContent=g;crumb.textContent='(group)';backBtn.style.display='';hint.style.display='none';level1.style.display='none';level2.style.display='';overlay.classList.remove('show');mv.src='';const u=new URL(location.href);u.searchParams.set('group',g);u.searchParams.delete('id');push?history.pushState(null,'',u):history.replaceState(null,'',u);searchInput.value='';try{await loadScenes(g);const id=qs().get('id');if(id){const hit=SCENES.find(x=>x.id |