Spaces:
Running
Running
Roll back to 65656a0 content (hycoclip demo): Hyper3-CLIP deploy failed on gated model download, no HF_TOKEN runtime secret
Browse files- .hyperview/extensions/hello-world-intro/extension.toml +0 -8
- .hyperview/extensions/hello-world-intro/panel.jsx +0 -80
- Dockerfile +6 -5
- README.md +6 -12
- demo.py +12 -60
.hyperview/extensions/hello-world-intro/extension.toml
DELETED
|
@@ -1,8 +0,0 @@
|
|
| 1 |
-
name = "hello-world-intro"
|
| 2 |
-
description = "Short orientation panel for the HyperView Hello World workspace"
|
| 3 |
-
|
| 4 |
-
[[panels]]
|
| 5 |
-
id = "hello-world-intro"
|
| 6 |
-
title = "Start here"
|
| 7 |
-
position = "right"
|
| 8 |
-
file = "panel.jsx"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.hyperview/extensions/hello-world-intro/panel.jsx
DELETED
|
@@ -1,80 +0,0 @@
|
|
| 1 |
-
const sdk = globalThis.HyperViewPanelSDK;
|
| 2 |
-
if (!sdk || sdk.version !== "2") {
|
| 3 |
-
throw new Error("HyperViewPanelSDK v2 is not available on window.");
|
| 4 |
-
}
|
| 5 |
-
|
| 6 |
-
const { React, components, hooks } = sdk;
|
| 7 |
-
const { Panel } = components;
|
| 8 |
-
const { usePanelState, useSelection } = hooks;
|
| 9 |
-
|
| 10 |
-
const geometryColors = {
|
| 11 |
-
Euclidean: "#67e8f9",
|
| 12 |
-
Spherical: "#a78bfa",
|
| 13 |
-
"Hyperbolic · Poincare": "#fb923c",
|
| 14 |
-
};
|
| 15 |
-
|
| 16 |
-
export default function HelloWorldIntro() {
|
| 17 |
-
const { props } = usePanelState();
|
| 18 |
-
const { selectedIds } = useSelection();
|
| 19 |
-
const geometries = Array.isArray(props.geometries) ? props.geometries : [];
|
| 20 |
-
|
| 21 |
-
return (
|
| 22 |
-
<Panel>
|
| 23 |
-
<div className="hello-world-intro">
|
| 24 |
-
<span className="hello-world-kicker">HyperView Hello World</span>
|
| 25 |
-
<h2>One image collection.<br />Three geometries.</h2>
|
| 26 |
-
<p className="hello-world-lede">
|
| 27 |
-
Every map contains the same {props.sampleCount || 300} {props.dataset || "iNaturalist"} observations.
|
| 28 |
-
What changes is the representation and the geometry used to lay it out.
|
| 29 |
-
</p>
|
| 30 |
-
|
| 31 |
-
<div className="hello-world-geometry-list">
|
| 32 |
-
{geometries.map((geometry) => (
|
| 33 |
-
<article key={geometry.name}>
|
| 34 |
-
<i style={{ background: geometryColors[geometry.name] || "#94a3b8" }} />
|
| 35 |
-
<span>
|
| 36 |
-
<strong>{geometry.name}</strong>
|
| 37 |
-
<small>{geometry.model} · {geometry.dimension}</small>
|
| 38 |
-
</span>
|
| 39 |
-
</article>
|
| 40 |
-
))}
|
| 41 |
-
</div>
|
| 42 |
-
|
| 43 |
-
<div className="hello-world-steps">
|
| 44 |
-
<strong>Try this</strong>
|
| 45 |
-
<ol>
|
| 46 |
-
<li>Rotate and zoom each map.</li>
|
| 47 |
-
<li>Select a cluster in one view.</li>
|
| 48 |
-
<li>Compare where that selection lands in the other geometries.</li>
|
| 49 |
-
<li>Open Samples to inspect the underlying observations.</li>
|
| 50 |
-
</ol>
|
| 51 |
-
</div>
|
| 52 |
-
|
| 53 |
-
<div className={`hello-world-selection${selectedIds.length ? " is-active" : ""}`}>
|
| 54 |
-
<span>{selectedIds.length ? selectedIds.length : "No"}</span>
|
| 55 |
-
{selectedIds.length === 1 ? " observation selected" : " observations selected"}
|
| 56 |
-
</div>
|
| 57 |
-
</div>
|
| 58 |
-
|
| 59 |
-
<style>{`
|
| 60 |
-
.hello-world-intro { height: 100%; overflow: auto; padding: 22px; color: var(--hv-color-foreground); background: radial-gradient(circle at 90% 0%, color-mix(in srgb, var(--hv-color-accent) 12%, transparent), transparent 32%); }
|
| 61 |
-
.hello-world-kicker { color: var(--hv-color-accent); font: 600 10px/1.2 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .16em; text-transform: uppercase; }
|
| 62 |
-
.hello-world-intro h2 { margin: 12px 0 10px; font: 650 27px/1.08 system-ui, sans-serif; letter-spacing: -.035em; }
|
| 63 |
-
.hello-world-lede { margin: 0; color: var(--hv-color-muted-foreground); font: 13px/1.55 system-ui, sans-serif; }
|
| 64 |
-
.hello-world-geometry-list { display: grid; gap: 8px; margin: 20px 0; }
|
| 65 |
-
.hello-world-geometry-list article { display: flex; align-items: center; gap: 11px; padding: 11px 12px; border: 1px solid var(--hv-color-border); border-radius: 10px; background: color-mix(in srgb, var(--hv-color-surface) 86%, transparent); }
|
| 66 |
-
.hello-world-geometry-list i { width: 8px; height: 30px; border-radius: 999px; }
|
| 67 |
-
.hello-world-geometry-list span { display: grid; gap: 2px; }
|
| 68 |
-
.hello-world-geometry-list strong { font: 600 12px/1.2 system-ui, sans-serif; }
|
| 69 |
-
.hello-world-geometry-list small { color: var(--hv-color-muted-foreground); font: 10px/1.2 ui-monospace, SFMono-Regular, Menlo, monospace; }
|
| 70 |
-
.hello-world-steps { padding: 14px; border: 1px solid var(--hv-color-border); border-radius: 12px; background: var(--hv-color-surface-muted); font: 12px/1.5 system-ui, sans-serif; }
|
| 71 |
-
.hello-world-steps > strong { font-size: 11px; letter-spacing: .08em; text-transform: uppercase; }
|
| 72 |
-
.hello-world-steps ol { margin: 9px 0 0; padding-left: 19px; color: var(--hv-color-muted-foreground); }
|
| 73 |
-
.hello-world-steps li + li { margin-top: 5px; }
|
| 74 |
-
.hello-world-selection { display: flex; align-items: baseline; gap: 6px; margin-top: 14px; color: var(--hv-color-muted-foreground); font: 11px/1.2 ui-monospace, SFMono-Regular, Menlo, monospace; }
|
| 75 |
-
.hello-world-selection span { color: var(--hv-color-foreground); font-size: 18px; font-weight: 650; }
|
| 76 |
-
.hello-world-selection.is-active span { color: var(--hv-color-accent); }
|
| 77 |
-
`}</style>
|
| 78 |
-
</Panel>
|
| 79 |
-
);
|
| 80 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Dockerfile
CHANGED
|
@@ -21,7 +21,7 @@ WORKDIR $HOME/app
|
|
| 21 |
|
| 22 |
RUN pip install --upgrade pip
|
| 23 |
|
| 24 |
-
ARG HYPERVIEW_VERSION=1.
|
| 25 |
ARG HYPER_MODELS_VERSION=0.3.1
|
| 26 |
|
| 27 |
# Install CPU-only PyTorch first so the Space does not pull the default CUDA bundle,
|
|
@@ -31,11 +31,12 @@ RUN pip install "hyperview==${HYPERVIEW_VERSION}" && python -c "import hyperview
|
|
| 31 |
RUN pip install "hyper-models[ml]==${HYPER_MODELS_VERSION}" && python -c "import hyper_models; print('hyper_models', hyper_models.__version__)"
|
| 32 |
|
| 33 |
COPY --chown=user demo.py ./demo.py
|
| 34 |
-
COPY --chown=user .hyperview ./.hyperview
|
| 35 |
|
| 36 |
-
#
|
| 37 |
-
#
|
| 38 |
-
#
|
|
|
|
|
|
|
| 39 |
ENV HYPERVIEW_NO_AUTH=1
|
| 40 |
ENV HYPERVIEW_DATASETS_DIR=/home/user/app/demo_data/datasets \
|
| 41 |
HYPERVIEW_MEDIA_DIR=/home/user/app/demo_data/media
|
|
|
|
| 21 |
|
| 22 |
RUN pip install --upgrade pip
|
| 23 |
|
| 24 |
+
ARG HYPERVIEW_VERSION=1.0.0
|
| 25 |
ARG HYPER_MODELS_VERSION=0.3.1
|
| 26 |
|
| 27 |
# Install CPU-only PyTorch first so the Space does not pull the default CUDA bundle,
|
|
|
|
| 31 |
RUN pip install "hyper-models[ml]==${HYPER_MODELS_VERSION}" && python -c "import hyper_models; print('hyper_models', hyper_models.__version__)"
|
| 32 |
|
| 33 |
COPY --chown=user demo.py ./demo.py
|
|
|
|
| 34 |
|
| 35 |
+
# HyperView 1.0 mints a session token and rejects unauthenticated runtime
|
| 36 |
+
# commands. A public Space has no way to hand visitors that token, so panel
|
| 37 |
+
# creation and case switching 401 without this. It marks the server public:
|
| 38 |
+
# visitors get the viewer commands and nothing else, so provider registration,
|
| 39 |
+
# extension install, tool execution and compute stay closed.
|
| 40 |
ENV HYPERVIEW_NO_AUTH=1
|
| 41 |
ENV HYPERVIEW_DATASETS_DIR=/home/user/app/demo_data/datasets \
|
| 42 |
HYPERVIEW_MEDIA_DIR=/home/user/app/demo_data/media
|
README.md
CHANGED
|
@@ -8,15 +8,14 @@ app_port: 7860
|
|
| 8 |
pinned: false
|
| 9 |
---
|
| 10 |
|
| 11 |
-
# HyperView
|
| 12 |
|
| 13 |
-
This is the main HyperView
|
| 14 |
-
|
| 15 |
-
custom introduction panel built with the public HyperView panel SDK:
|
| 16 |
|
| 17 |
- CLIP (`openai/clip-vit-base-patch32`) in Euclidean 3D
|
| 18 |
- CLIP (`openai/clip-vit-base-patch32`) in spherical 3D
|
| 19 |
-
-
|
| 20 |
|
| 21 |
The sample is drawn from `evendrow/inat24_tiny`, a compact iNaturalist 2024
|
| 22 |
subset with 1,000 images, 100 species, and taxonomy metadata. The visible label
|
|
@@ -26,7 +25,7 @@ rights holder.
|
|
| 26 |
|
| 27 |
The Docker image installs released packages from PyPI:
|
| 28 |
|
| 29 |
-
- `hyperview==1.
|
| 30 |
- `hyper-models[ml]==0.3.1`
|
| 31 |
|
| 32 |
## Dataset
|
|
@@ -47,8 +46,7 @@ The default stratified sample contains 300 images:
|
|
| 47 |
| mollusks | 10 |
|
| 48 |
|
| 49 |
This keeps the demo small enough for Hugging Face CPU Spaces while preserving a
|
| 50 |
-
real biological hierarchy for geometry comparison.
|
| 51 |
-
when they exceed 1024 × 1024, avoiding the tiny 32 × 32 appearance of CIFAR.
|
| 52 |
|
| 53 |
## Reuse This Template
|
| 54 |
|
|
@@ -63,7 +61,3 @@ When copying this folder for another dataset:
|
|
| 63 |
|
| 64 |
This folder is synchronized to `hyper3labs/HyperView` by GitHub Actions from
|
| 65 |
the `hyperview-spaces` deployment repository.
|
| 66 |
-
|
| 67 |
-
Because `hyper3-clip-v0.5` is gated on Hugging Face, the running Space needs an
|
| 68 |
-
`HF_TOKEN` Space secret with read access to the model. The GitHub deployment
|
| 69 |
-
credential does not become a runtime secret automatically.
|
|
|
|
| 8 |
pinned: false
|
| 9 |
---
|
| 10 |
|
| 11 |
+
# HyperView - iNat24 Tiny Geometry Showcase
|
| 12 |
|
| 13 |
+
This is the main HyperView demo Space. It shows the same taxonomy-backed image
|
| 14 |
+
sample through multiple geometric views:
|
|
|
|
| 15 |
|
| 16 |
- CLIP (`openai/clip-vit-base-patch32`) in Euclidean 3D
|
| 17 |
- CLIP (`openai/clip-vit-base-patch32`) in spherical 3D
|
| 18 |
+
- HyCoCLIP (`hycoclip-vit-s`) in Poincare 2D
|
| 19 |
|
| 20 |
The sample is drawn from `evendrow/inat24_tiny`, a compact iNaturalist 2024
|
| 21 |
subset with 1,000 images, 100 species, and taxonomy metadata. The visible label
|
|
|
|
| 25 |
|
| 26 |
The Docker image installs released packages from PyPI:
|
| 27 |
|
| 28 |
+
- `hyperview==1.0.0`
|
| 29 |
- `hyper-models[ml]==0.3.1`
|
| 30 |
|
| 31 |
## Dataset
|
|
|
|
| 46 |
| mollusks | 10 |
|
| 47 |
|
| 48 |
This keeps the demo small enough for Hugging Face CPU Spaces while preserving a
|
| 49 |
+
real biological hierarchy for geometry comparison.
|
|
|
|
| 50 |
|
| 51 |
## Reuse This Template
|
| 52 |
|
|
|
|
| 61 |
|
| 62 |
This folder is synchronized to `hyper3labs/HyperView` by GitHub Actions from
|
| 63 |
the `hyperview-spaces` deployment repository.
|
|
|
|
|
|
|
|
|
|
|
|
demo.py
CHANGED
|
@@ -1,11 +1,10 @@
|
|
| 1 |
#!/usr/bin/env python
|
| 2 |
-
"""HyperView
|
| 3 |
|
| 4 |
from __future__ import annotations
|
| 5 |
|
| 6 |
import os
|
| 7 |
import re
|
| 8 |
-
import sys
|
| 9 |
from collections import Counter
|
| 10 |
from pathlib import Path
|
| 11 |
|
|
@@ -17,15 +16,7 @@ import hyperview as hv
|
|
| 17 |
SPACE_HOST = "0.0.0.0"
|
| 18 |
SPACE_PORT = 7860
|
| 19 |
|
| 20 |
-
|
| 21 |
-
# Space is produced: build, exit, export.
|
| 22 |
-
BUILD_ONLY = os.environ.get("HYPERVIEW_BUILD_ONLY", "").lower() in {
|
| 23 |
-
"1",
|
| 24 |
-
"true",
|
| 25 |
-
"yes",
|
| 26 |
-
} or "--build-only" in sys.argv[1:]
|
| 27 |
-
|
| 28 |
-
DATASET_NAME = "hello_world_inat24_multigeometry"
|
| 29 |
HF_DATASET = "evendrow/inat24_tiny"
|
| 30 |
HF_SPLIT = "train"
|
| 31 |
SAMPLE_SEED = 42
|
|
@@ -43,21 +34,20 @@ TARGET_SUPERCATEGORY_COUNTS = {
|
|
| 43 |
"mollusks": 10,
|
| 44 |
}
|
| 45 |
SAMPLE_COUNT = sum(TARGET_SUPERCATEGORY_COUNTS.values())
|
| 46 |
-
IMAGE_MAX_SIZE = (
|
| 47 |
-
INTRO_EXTENSION = Path(__file__).parent / ".hyperview" / "extensions" / "hello-world-intro"
|
| 48 |
|
| 49 |
EMBEDDING_LAYOUTS = [
|
| 50 |
{
|
| 51 |
"name": "CLIP",
|
| 52 |
"provider": "embed-anything",
|
| 53 |
"model": "openai/clip-vit-base-patch32",
|
| 54 |
-
"layouts": ["euclidean:3d", "spherical
|
| 55 |
},
|
| 56 |
{
|
| 57 |
-
"name": "
|
| 58 |
"provider": "hyper-models",
|
| 59 |
-
"model": "
|
| 60 |
-
"layouts": ["poincare
|
| 61 |
},
|
| 62 |
]
|
| 63 |
|
|
@@ -114,7 +104,7 @@ def save_image(row: dict, destination: Path) -> None:
|
|
| 114 |
|
| 115 |
image = ImageOps.exif_transpose(image).convert("RGB")
|
| 116 |
image.thumbnail(IMAGE_MAX_SIZE, Image.Resampling.LANCZOS)
|
| 117 |
-
image.save(destination, format="JPEG", quality=
|
| 118 |
|
| 119 |
|
| 120 |
def existing_label_counts(dataset: hv.Dataset) -> Counter[str]:
|
|
@@ -219,16 +209,6 @@ def build_dataset() -> tuple[hv.Dataset, dict[str, str]]:
|
|
| 219 |
return dataset, layout_keys
|
| 220 |
|
| 221 |
|
| 222 |
-
def geometry_title(name: str, layout: str) -> str:
|
| 223 |
-
geometry, _, dimension = layout.partition(":")
|
| 224 |
-
geometry_name = {
|
| 225 |
-
"euclidean": "Euclidean",
|
| 226 |
-
"spherical": "Spherical",
|
| 227 |
-
"poincare": "Hyperbolic · Poincare",
|
| 228 |
-
}.get(geometry, geometry.title())
|
| 229 |
-
return f"{name} · {geometry_name}{f' {dimension.upper()}' if dimension else ''}"
|
| 230 |
-
|
| 231 |
-
|
| 232 |
def build_demo_view(layout_keys: dict[str, str]) -> hv.ui.View:
|
| 233 |
"""Open the showcase on its three geometries, side by side.
|
| 234 |
|
|
@@ -237,13 +217,13 @@ def build_demo_view(layout_keys: dict[str, str]) -> hv.ui.View:
|
|
| 237 |
though three layouts exist -- the one thing this demo is here to show.
|
| 238 |
"""
|
| 239 |
|
| 240 |
-
panels: list[hv.ui.Scatter | hv.ui.Samples
|
| 241 |
previous_id: str | None = None
|
| 242 |
for label, layout_key in layout_keys.items():
|
| 243 |
name, layout = label.split(":", 1)
|
| 244 |
panel = hv.ui.Scatter(
|
| 245 |
id=f"map-{layout.replace(':', '-')}",
|
| 246 |
-
title=
|
| 247 |
layout_key=layout_key,
|
| 248 |
position="center",
|
| 249 |
reference_panel_id=previous_id,
|
|
@@ -261,47 +241,19 @@ def build_demo_view(layout_keys: dict[str, str]) -> hv.ui.View:
|
|
| 261 |
layout=hv.ui.PanelLayout(height=260, min_height=180),
|
| 262 |
)
|
| 263 |
)
|
| 264 |
-
|
| 265 |
-
id="hello-world-intro",
|
| 266 |
-
extension="hello-world-intro",
|
| 267 |
-
panel="hello-world-intro",
|
| 268 |
-
title="Start here",
|
| 269 |
-
position="right",
|
| 270 |
-
layout=hv.ui.PanelLayout(width=360, min_width=300, max_width=440),
|
| 271 |
-
props={
|
| 272 |
-
"sampleCount": SAMPLE_COUNT,
|
| 273 |
-
"dataset": "iNat24 Tiny",
|
| 274 |
-
"geometries": [
|
| 275 |
-
{"name": "Euclidean", "model": "CLIP", "dimension": "3D"},
|
| 276 |
-
{"name": "Spherical", "model": "CLIP", "dimension": "3D"},
|
| 277 |
-
{
|
| 278 |
-
"name": "Hyperbolic · Poincare",
|
| 279 |
-
"model": "Hyper3-CLIP",
|
| 280 |
-
"dimension": "2D",
|
| 281 |
-
},
|
| 282 |
-
],
|
| 283 |
-
},
|
| 284 |
-
)
|
| 285 |
-
panels.append(intro)
|
| 286 |
-
return hv.ui.View(*panels, active_panel=intro.id)
|
| 287 |
|
| 288 |
|
| 289 |
def main() -> None:
|
| 290 |
dataset, layout_keys = build_dataset()
|
| 291 |
print(f"Starting HyperView on {SPACE_HOST}:{SPACE_PORT}", flush=True)
|
| 292 |
-
|
| 293 |
dataset,
|
| 294 |
host=SPACE_HOST,
|
| 295 |
port=SPACE_PORT,
|
| 296 |
open_browser=False,
|
| 297 |
view=build_demo_view(layout_keys),
|
| 298 |
-
extensions=[str(INTRO_EXTENSION)],
|
| 299 |
-
block=False,
|
| 300 |
)
|
| 301 |
-
if BUILD_ONLY:
|
| 302 |
-
print("Workspace built; stopping before serving (build-only).", flush=True)
|
| 303 |
-
return
|
| 304 |
-
session.wait()
|
| 305 |
|
| 306 |
|
| 307 |
if __name__ == "__main__":
|
|
|
|
| 1 |
#!/usr/bin/env python
|
| 2 |
+
"""HyperView main Hugging Face Space geometry demo."""
|
| 3 |
|
| 4 |
from __future__ import annotations
|
| 5 |
|
| 6 |
import os
|
| 7 |
import re
|
|
|
|
| 8 |
from collections import Counter
|
| 9 |
from pathlib import Path
|
| 10 |
|
|
|
|
| 16 |
SPACE_HOST = "0.0.0.0"
|
| 17 |
SPACE_PORT = 7860
|
| 18 |
|
| 19 |
+
DATASET_NAME = "inat24_tiny_geometry_showcase"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
HF_DATASET = "evendrow/inat24_tiny"
|
| 21 |
HF_SPLIT = "train"
|
| 22 |
SAMPLE_SEED = 42
|
|
|
|
| 34 |
"mollusks": 10,
|
| 35 |
}
|
| 36 |
SAMPLE_COUNT = sum(TARGET_SUPERCATEGORY_COUNTS.values())
|
| 37 |
+
IMAGE_MAX_SIZE = (768, 768)
|
|
|
|
| 38 |
|
| 39 |
EMBEDDING_LAYOUTS = [
|
| 40 |
{
|
| 41 |
"name": "CLIP",
|
| 42 |
"provider": "embed-anything",
|
| 43 |
"model": "openai/clip-vit-base-patch32",
|
| 44 |
+
"layouts": ["euclidean:3d", "spherical"],
|
| 45 |
},
|
| 46 |
{
|
| 47 |
+
"name": "HyCoCLIP",
|
| 48 |
"provider": "hyper-models",
|
| 49 |
+
"model": "hycoclip-vit-s",
|
| 50 |
+
"layouts": ["poincare"],
|
| 51 |
},
|
| 52 |
]
|
| 53 |
|
|
|
|
| 104 |
|
| 105 |
image = ImageOps.exif_transpose(image).convert("RGB")
|
| 106 |
image.thumbnail(IMAGE_MAX_SIZE, Image.Resampling.LANCZOS)
|
| 107 |
+
image.save(destination, format="JPEG", quality=90, optimize=True)
|
| 108 |
|
| 109 |
|
| 110 |
def existing_label_counts(dataset: hv.Dataset) -> Counter[str]:
|
|
|
|
| 209 |
return dataset, layout_keys
|
| 210 |
|
| 211 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 212 |
def build_demo_view(layout_keys: dict[str, str]) -> hv.ui.View:
|
| 213 |
"""Open the showcase on its three geometries, side by side.
|
| 214 |
|
|
|
|
| 217 |
though three layouts exist -- the one thing this demo is here to show.
|
| 218 |
"""
|
| 219 |
|
| 220 |
+
panels: list[hv.ui.Scatter | hv.ui.Samples] = []
|
| 221 |
previous_id: str | None = None
|
| 222 |
for label, layout_key in layout_keys.items():
|
| 223 |
name, layout = label.split(":", 1)
|
| 224 |
panel = hv.ui.Scatter(
|
| 225 |
id=f"map-{layout.replace(':', '-')}",
|
| 226 |
+
title=f"{name} · {layout}",
|
| 227 |
layout_key=layout_key,
|
| 228 |
position="center",
|
| 229 |
reference_panel_id=previous_id,
|
|
|
|
| 241 |
layout=hv.ui.PanelLayout(height=260, min_height=180),
|
| 242 |
)
|
| 243 |
)
|
| 244 |
+
return hv.ui.View(*panels, active_panel=panels[0].id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 245 |
|
| 246 |
|
| 247 |
def main() -> None:
|
| 248 |
dataset, layout_keys = build_dataset()
|
| 249 |
print(f"Starting HyperView on {SPACE_HOST}:{SPACE_PORT}", flush=True)
|
| 250 |
+
hv.launch(
|
| 251 |
dataset,
|
| 252 |
host=SPACE_HOST,
|
| 253 |
port=SPACE_PORT,
|
| 254 |
open_browser=False,
|
| 255 |
view=build_demo_view(layout_keys),
|
|
|
|
|
|
|
| 256 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 257 |
|
| 258 |
|
| 259 |
if __name__ == "__main__":
|