hyperview 0.4.2__tar.gz → 0.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. {hyperview-0.4.2 → hyperview-0.6.0}/.agents/skills/hyperview-cli/SKILL.md +25 -15
  2. {hyperview-0.4.2 → hyperview-0.6.0}/.agents/skills/hyperview-cli/references/commands.md +101 -9
  3. hyperview-0.4.2/.agents/skills/hyperview-cli/references/plugins.md → hyperview-0.6.0/.agents/skills/hyperview-cli/references/extensions.md +72 -14
  4. hyperview-0.6.0/.agents/skills/hyperview-cli/references/panel-modules.md +171 -0
  5. hyperview-0.6.0/PKG-INFO +161 -0
  6. hyperview-0.6.0/README.md +110 -0
  7. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/__init__.py +4 -0
  8. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/_version.py +2 -2
  9. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/api.py +172 -15
  10. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/cli.py +172 -19
  11. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/core/dataset.py +21 -1
  12. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/core/selection.py +20 -0
  13. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/embeddings/engine.py +10 -1
  14. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/embeddings/pipelines.py +2 -1
  15. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/embeddings/projection.py +15 -3
  16. hyperview-0.6.0/src/hyperview/figures/__init__.py +13 -0
  17. hyperview-0.6.0/src/hyperview/figures/colors.py +102 -0
  18. hyperview-0.6.0/src/hyperview/figures/render.py +628 -0
  19. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/runtime.py +327 -36
  20. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/app.py +269 -13
  21. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/404/index.html +1 -1
  22. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/404.html +1 -1
  23. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/__next.__PAGE__.txt +2 -2
  24. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/__next._full.txt +3 -3
  25. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/__next._head.txt +1 -1
  26. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/__next._index.txt +2 -2
  27. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/__next._tree.txt +2 -2
  28. {hyperview-0.4.2/src/hyperview/server/static/_next/static/z3NN_l9swds_iWzQNtTap → hyperview-0.6.0/src/hyperview/server/static/_next/static/VXRlYGcnYv3zp0mzwnWWb}/_buildManifest.js +1 -5
  29. hyperview-0.6.0/src/hyperview/server/static/_next/static/chunks/0c17ee078faaeb62.js +8 -0
  30. hyperview-0.4.2/src/hyperview/server/static/_next/static/chunks/4899f901f4ca16ad.css → hyperview-0.6.0/src/hyperview/server/static/_next/static/chunks/7acade957a14420e.css +1 -1
  31. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/_not-found/__next._full.txt +2 -2
  32. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/_not-found/__next._head.txt +1 -1
  33. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/_not-found/__next._index.txt +2 -2
  34. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
  35. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/_not-found/__next._not-found.txt +1 -1
  36. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/_not-found/__next._tree.txt +2 -2
  37. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/_not-found/index.html +1 -1
  38. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/_not-found/index.txt +2 -2
  39. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/index.html +1 -1
  40. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/index.txt +3 -3
  41. hyperview-0.6.0/src/hyperview/storage/geometry.py +118 -0
  42. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/storage/lancedb_backend.py +74 -2
  43. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/storage/memory_backend.py +29 -9
  44. hyperview-0.6.0/src/hyperview/storage/metrics.py +136 -0
  45. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/tools.py +3 -3
  46. hyperview-0.6.0/src/hyperview/ui.py +251 -0
  47. hyperview-0.4.2/.agents/skills/hyperview-cli/references/native-panels.md +0 -138
  48. hyperview-0.4.2/PKG-INFO +0 -237
  49. hyperview-0.4.2/README.md +0 -186
  50. hyperview-0.4.2/src/hyperview/server/static/_next/static/chunks/52ce757ecb4458ef.js +0 -8
  51. {hyperview-0.4.2 → hyperview-0.6.0}/.gitignore +0 -0
  52. {hyperview-0.4.2 → hyperview-0.6.0}/LICENSE +0 -0
  53. {hyperview-0.4.2 → hyperview-0.6.0}/pyproject.toml +0 -0
  54. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/core/__init__.py +0 -0
  55. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/core/sample.py +0 -0
  56. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/embeddings/__init__.py +0 -0
  57. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/embeddings/compute.py +0 -0
  58. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/embeddings/providers/__init__.py +0 -0
  59. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/embeddings/providers/lancedb_providers.py +0 -0
  60. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/extensions.py +0 -0
  61. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/__init__.py +0 -0
  62. {hyperview-0.4.2/src/hyperview/server/static/_next/static/z3NN_l9swds_iWzQNtTap → hyperview-0.6.0/src/hyperview/server/static/_next/static/VXRlYGcnYv3zp0mzwnWWb}/_clientMiddlewareManifest.json +0 -0
  63. {hyperview-0.4.2/src/hyperview/server/static/_next/static/z3NN_l9swds_iWzQNtTap → hyperview-0.6.0/src/hyperview/server/static/_next/static/VXRlYGcnYv3zp0mzwnWWb}/_ssgManifest.js +0 -0
  64. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/_next/static/chunks/567993cf36cd4ab1.js +0 -0
  65. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/_next/static/chunks/661a08547c83f565.js +0 -0
  66. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/_next/static/chunks/86c1fc4cf542f408.js +0 -0
  67. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/_next/static/chunks/a6dad97d9634a72d.js +0 -0
  68. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/_next/static/chunks/a6dad97d9634a72d.js.map +0 -0
  69. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/_next/static/chunks/e954ba82c0a04100.js +0 -0
  70. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/_next/static/chunks/eac713f252f03efd.js +0 -0
  71. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/_next/static/chunks/f29dd35a99c216ea.js +0 -0
  72. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/_next/static/chunks/turbopack-cb59e03a04a579d1.js +0 -0
  73. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/_next/static/media/1bffadaabf893a1e-s.7cd81963.woff2 +0 -0
  74. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/_next/static/media/2bbe8d2671613f1f-s.76dcb0b2.woff2 +0 -0
  75. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/_next/static/media/2c55a0e60120577a-s.2a48534a.woff2 +0 -0
  76. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/_next/static/media/5476f68d60460930-s.c995e352.woff2 +0 -0
  77. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/_next/static/media/83afe278b6a6bb3c-s.p.3a6ba036.woff2 +0 -0
  78. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/_next/static/media/9c72aa0f40e4eef8-s.18a48cbc.woff2 +0 -0
  79. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/server/static/_next/static/media/ad66f9afd8947f86-s.7a40eb73.woff2 +0 -0
  80. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/skill_install.py +0 -0
  81. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/storage/__init__.py +0 -0
  82. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/storage/backend.py +0 -0
  83. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/storage/config.py +0 -0
  84. {hyperview-0.4.2 → hyperview-0.6.0}/src/hyperview/storage/schema.py +0 -0
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: hyperview-cli
3
- description: Use HyperView's control-plane CLI for hyperview serve, dataset create, workspace create, embeddings compute, layouts compute, runtime jobs, ui layout set, ui selection set, ui panel add, extension add, tools run, native module panels, backend tools, and local HyperView plugin workflows.
3
+ description: Use HyperView's control-plane CLI for hyperview serve, dataset create, workspace create, embeddings compute, layouts compute, browserless paper figure export, runtime jobs, ui layout set, ui selection set, ui panel add, extension add, tools run, panel modules, backend tools, and local HyperView extension workflows.
4
4
  license: MIT
5
5
  compatibility: Requires Python 3.10-3.13 and the hyperview CLI (`uv tool install --python 3.12 hyperview`). Runtime-control commands require a running HyperView server.
6
6
  metadata:
@@ -29,9 +29,10 @@ HyperView currently supports Python 3.10 through 3.13; `--python 3.12` keeps the
29
29
  - Start or control a running HyperView runtime.
30
30
  - Register a custom embedding provider.
31
31
  - Compute embeddings or layouts without restarting the UI.
32
+ - Export paper-ready static 3D embedding figures without a browser or Node runtime.
32
33
  - Switch the active workspace, layout, or selection in a running session.
33
- - Add or remove agent-authored native module panels from local files.
34
- - Create, install, reload, or test a local plugin/extension with Python backend tools and a frontend panel.
34
+ - Add, remove, or compose extension-backed panel instances.
35
+ - Create, install, reload, or test a local extension with Python backend tools and a frontend panel.
35
36
 
36
37
  ## Core workflow
37
38
 
@@ -41,7 +42,8 @@ HyperView currently supports Python 3.10 through 3.13; `--python 3.12` keeps the
41
42
  4. Register a provider if needed.
42
43
  5. Submit embedding or layout jobs through the runtime.
43
44
  6. Use `hyperview ui ...` commands to switch what the live UI shows.
44
- 7. For plugins, create an extension folder and install it into the running workspace.
45
+ 7. Export paper figures with `hyperview figure export` when the user needs screenshots or publication diagrams.
46
+ 8. For extensions, create an extension folder and install it into the running workspace.
45
47
 
46
48
  ## Current model
47
49
 
@@ -49,32 +51,40 @@ HyperView currently supports Python 3.10 through 3.13; `--python 3.12` keeps the
49
51
  - Datasets are created separately from workspaces.
50
52
  - The workspace owns the dataset selection.
51
53
  - `ui layout set` changes the active layout and the frontend opens the matching built-in scatter panel.
52
- - Runtime-added panels can be typed scatter instances bound to explicit layout keys, or native module panels loaded into the host React tree.
54
+ - `ui similarity set` selects an anchor sample and pins the nearest-neighbor context to an explicit layout or space.
55
+ - Runtime-added panels can be typed scatter instances bound to explicit layout keys, or extension-backed panel modules loaded into the host React tree.
53
56
  - Runtime-added panels use the stable `HyperViewPanelSDK` surface on `window`.
54
- - Plugins are repo-local extension folders with `extension.toml`, optional Python tools, and optional native panel modules.
55
- - Plugin panels call backend tools through `HyperViewPanelSDK.hooks.useTool()` or `hyperview tools run`.
57
+ - Extensions are repo-local folders with `extension.toml`, optional Python tools, and optional panel modules.
58
+ - Extension panels call backend tools through `HyperViewPanelSDK.hooks.useTool()` or `hyperview tools run`.
59
+ - Extensions define reusable tools/panels; workspace views compose concrete panel instances and layout. In Python launch scripts, register extensions with `session.ui.add_extension(...)` and place panels with `hv.ui.ExtensionPanel(...)`.
56
60
  - In practice, create datasets and workspaces before starting the runtime for that workspace. The current runtime loads workspace registry state on startup.
61
+ - `figure export` is browserless and supports 3D layouts only. It reuses the persisted 3D camera for the layout when available, otherwise it chooses a paper-oriented default view.
62
+ - Paper figure defaults are square, white-background, opaque PNGs with a faint sphere guide and direct labels for small label sets.
57
63
 
58
- Read [references/commands.md](references/commands.md) for command recipes covering datasets, workspaces, providers, embeddings, layouts, runtime UI state, selections, and jobs.
59
- Read [references/native-panels.md](references/native-panels.md) when the task involves authoring or registering a custom panel.
60
- Read [references/plugins.md](references/plugins.md) when the task involves backend-plus-frontend plugins/extensions.
64
+ Read [references/commands.md](references/commands.md) for command recipes covering datasets, workspaces, providers, embeddings, layouts, paper figures, runtime UI state, selections, and jobs.
65
+ Read [references/panel-modules.md](references/panel-modules.md) when the task involves authoring a browser panel module.
66
+ Read [references/extensions.md](references/extensions.md) when the task involves packaging or registering custom panel code or backend tools.
61
67
 
62
68
  ## Agent guidance
63
69
 
64
70
  - Prefer CLI commands over direct file edits when the goal is to operate a running HyperView session.
65
71
  - Treat dataset creation and workspace binding as separate steps when needed: `dataset create ...` creates persisted data, `workspace create --dataset ...` or `workspace set-dataset ...` binds it to a workspace.
66
72
  - Prefer `workspace create --dataset ...` over separate create and dataset-attach calls when setting up a new workspace.
67
- - For custom module panels, have the agent write panel modules outside the app source tree, for example under `agent-context/`, and then add them through `hyperview ui panel add --module-file ...`.
73
+ - For custom panel code, create an extension under `.hyperview/extensions/<extension-name>/`; do not register arbitrary panel module files directly.
68
74
  - For side-by-side embedding comparisons, add typed scatter panels through `hyperview ui panel add --kind scatter --layout-key ... --reference-panel-id ... --direction right`.
69
- - For plugins, prefer `.hyperview/extensions/<plugin-name>/` in the project root. `hyperview serve` auto-discovers those folders and attaches them to the launched workspace, so they can live in version control with the dataset/project code.
75
+ - For nearest-neighbor comparisons, use `hyperview ui similarity set --sample-id ... --layout-key ...` or panel SDK `commands.showSimilar(...)`; do not infer neighbor space from whichever scatter panel is focused.
76
+ - For extensions, prefer `.hyperview/extensions/<extension-name>/` in the project root. `hyperview serve` auto-discovers those folders and attaches them to the launched workspace, so they can live in version control with the dataset/project code.
77
+ - For demos/spaces that launch HyperView from Python, compose panels with `hv.ui.Horizontal`, `hv.ui.Vertical`, `hv.ui.Scatter`, and `hv.ui.ExtensionPanel`; keep extension manifests focused on reusable panel/tool definitions.
70
78
  - Tools can write files under `ctx.extension_storage` and return `ctx.url_for(path)` for panel-renderable artifact URLs.
71
- - Keep plugins self-contained: `extension.toml`, `tools.py`, `panel.js` or `panel.jsx`, and any local assets in the same folder.
79
+ - Keep extensions self-contained: `extension.toml`, `tools.py`, `panel.js` or `panel.jsx`, and any local assets in the same folder.
72
80
  - Prefer `--json` output when chaining commands or inspecting results programmatically.
73
81
  - Wait for embedding/layout jobs to finish before issuing layout-switch commands that depend on their results.
74
82
  - Use `hyperview jobs list` or `hyperview jobs inspect <job-id>` if a compute command is long-running or you started it with `--no-wait`.
75
83
  - For provider args, use repeated `--provider-arg key=value` flags.
76
84
  - Treat the workspace as the durable unit. Changing datasets means setting a new workspace dataset, not switching among many datasets inside one workspace.
77
- - Prefer native module panels over raw HTML. The panel system no longer relies on iframes.
85
+ - Prefer panel modules over raw HTML. The panel system no longer relies on iframes.
86
+ - For paper diagrams, prefer `hyperview figure export` over browser screenshots unless the user explicitly needs exact UI chrome. It does not require Playwright, browser bundling, or Node at runtime.
87
+ - For publication figures, keep the defaults first: `--theme light`, `--guide-style paper`, and `--legend auto`. Use `--show-selection` only when selected samples are meaningful and will be explained in the caption.
78
88
  - The first `uv run hyperview ...` invocation in a session can take 30+ seconds (torch/datasets imports). Allow generous timeouts and avoid sending SIGINT.
79
89
 
80
90
  ## Inspecting runtime state
@@ -85,4 +95,4 @@ The runtime exposes JSON discovery endpoints alongside the CLI. Use them to obta
85
95
  - `GET /api/embeddings?workspace_id=<ws>` &mdash; the active or default layout, including `layout_key`, `geometry`, and sample `ids`. Use the returned `layout_key` for `hyperview ui layout set --layout-key ...` and pick from `ids` for `hyperview ui selection set --ids ...`.
86
96
  - `GET /api/tools` &mdash; registered tool URIs (also returned by `hyperview tools list --json`).
87
97
 
88
- Layout keys encode geometry and dimension as a substring (e.g. `..._euclidean_umap__2d_...`, `..._hyperbolic_umap__3d_...`). Match on those substrings when filtering by geometry/dimension.
98
+ Layout keys encode geometry and dimension as a substring (e.g. `..._euclidean_umap__2d_...`, `..._hyperbolic_umap__3d_...`). Match on those substrings when filtering by geometry/dimension.
@@ -46,7 +46,7 @@ Preview destinations without writing files:
46
46
  hyperview skill install --dry-run --json
47
47
  ```
48
48
 
49
- This is different from `hyperview extension add`, which installs a runtime plugin into a running HyperView workspace.
49
+ This is different from `hyperview extension add`, which installs a runtime extension into a running HyperView workspace.
50
50
 
51
51
  ## Datasets and Workspaces
52
52
 
@@ -131,6 +131,63 @@ hyperview jobs list --json
131
131
  hyperview jobs inspect <job-id> --json
132
132
  ```
133
133
 
134
+ ## Paper Figures
135
+
136
+ Export a browserless, paper-ready PNG from the active 3D layout:
137
+
138
+ ```bash
139
+ hyperview figure export figures/embedding-sphere.png \
140
+ --workspace research \
141
+ --layout active \
142
+ --json
143
+ ```
144
+
145
+ If `--layout` is omitted, HyperView uses the active 3D layout when one is set, otherwise the first available 3D layout. Use `--layout active` when you specifically want the live UI's active layout and want the command to fail if none is active.
146
+
147
+ The export path is pure Python and does not require Playwright, browser bundling, Node, or a running frontend. It supports 3D layouts only; 2D layouts are rejected with a validation message.
148
+
149
+ Paper defaults are tuned for academic figures:
150
+
151
+ - `--width 900 --height 900 --scale 2`
152
+ - `--theme light`
153
+ - `--guide-style paper`
154
+ - `--legend auto` (direct labels for small label sets)
155
+ - opaque PNG output
156
+ - selection rings hidden unless explicitly requested
157
+
158
+ Use the 3D view selected in the UI by rotating the scatter panel first. HyperView persists the layout camera and `figure export` reuses it for that layout.
159
+
160
+ Common variants:
161
+
162
+ ```bash
163
+ # Cleanest sphere context: silhouette only.
164
+ hyperview figure export figures/embedding-outline.png \
165
+ --workspace research \
166
+ --layout active \
167
+ --guide-style outline
168
+
169
+ # No sphere guide, useful when the embedding separation is the whole message.
170
+ hyperview figure export figures/embedding-clean.png \
171
+ --workspace research \
172
+ --layout active \
173
+ --guide-style none \
174
+ --legend direct
175
+
176
+ # Browser-like guide rings and current selection markers.
177
+ hyperview figure export figures/embedding-ui-like.png \
178
+ --workspace research \
179
+ --layout active \
180
+ --guide-style rings \
181
+ --legend on \
182
+ --show-selection
183
+
184
+ # Add a short panel title when the figure will stand alone.
185
+ hyperview figure export figures/embedding-panel-a.png \
186
+ --workspace research \
187
+ --layout active \
188
+ --title "ArcFace spherical embeddings"
189
+ ```
190
+
134
191
  ## Runtime UI
135
192
 
136
193
  Discover an existing layout key and sample IDs before mutating runtime state:
@@ -151,15 +208,18 @@ hyperview ui selection set --workspace research --ids sample-1,sample-8
151
208
 
152
209
  `--layout-key` must be an existing layout (use the `layout_key` returned by `/api/embeddings`). When the chosen layout is Euclidean 3D, HyperView opens or focuses the Euclidean 3D scatter panel.
153
210
 
154
- Add a native panel from a local JavaScript module file:
211
+ Add a custom panel through an extension:
155
212
 
156
213
  ```bash
214
+ hyperview extension add .hyperview/extensions/label-histogram \
215
+ --workspace research
216
+
157
217
  hyperview ui panel add \
158
218
  --workspace research \
159
219
  --panel-id label-histogram \
160
- --title "Label Histogram" \
220
+ --extension label-histogram \
221
+ --extension-panel label-histogram \
161
222
  --position right \
162
- --module-file agent-context/panels/label-histogram/index.js
163
223
  ```
164
224
 
165
225
  Add two runtime scatter panels bound to explicit layouts, side by side:
@@ -184,6 +244,22 @@ hyperview ui panel add \
184
244
  --direction right
185
245
  ```
186
246
 
247
+ Python launch scripts can encode the same composition without calling runtime
248
+ internals:
249
+
250
+ ```python
251
+ view = hv.ui.View(
252
+ hv.ui.Horizontal(
253
+ hv.ui.Scatter("uncha-poincare", title="UNCHA", layout_key=uncha_layout),
254
+ hv.ui.Scatter("hycoclip-poincare", title="HyCoCLIP", layout_key=hycoclip_layout),
255
+ ),
256
+ hv.ui.ExtensionPanel("notes", extension="notes", panel="notes", position="right"),
257
+ )
258
+ session = hv.launch(dataset, block=False)
259
+ session.ui.add_extension(".hyperview/extensions/notes")
260
+ session.ui.apply_view(view)
261
+ ```
262
+
187
263
  Remove a runtime panel by id:
188
264
 
189
265
  ```bash
@@ -192,9 +268,25 @@ hyperview ui panel remove \
192
268
  --panel-id hycoclip-poincare
193
269
  ```
194
270
 
195
- ## Plugins and Tools
271
+ Pin nearest-neighbor results to a specific embedding layout:
272
+
273
+ ```bash
274
+ hyperview ui similarity set \
275
+ --workspace research \
276
+ --sample-id <sample-id> \
277
+ --layout-key <layout-key> \
278
+ --k 18
279
+ ```
280
+
281
+ Clear the explicit nearest-neighbor context:
282
+
283
+ ```bash
284
+ hyperview ui similarity clear --workspace research
285
+ ```
286
+
287
+ ## Extensions and Tools
196
288
 
197
- Create backend-plus-frontend plugins under `.hyperview/extensions/<name>/` so they can be versioned with the project and auto-attached on `hyperview serve`. For a server that is already running, install one explicitly:
289
+ Create backend-plus-frontend extensions under `.hyperview/extensions/<name>/` so they can be versioned with the project and auto-registered on `hyperview serve`. For a server that is already running, install one explicitly:
198
290
 
199
291
  ```bash
200
292
  hyperview extension add .hyperview/extensions/selection-profile \
@@ -202,11 +294,11 @@ hyperview extension add .hyperview/extensions/selection-profile \
202
294
  --json
203
295
  ```
204
296
 
205
- Inspect and run installed plugin tools:
297
+ Inspect and run installed extension tools:
206
298
 
207
299
  ```bash
208
300
  hyperview extension list --json
209
- # => {"extensions":[{"name":"selection-profile","folder":"...","workspace_id":"research","panels":["selection-profile"],"tools":[{"uri":"selection_profile.summarize",...}]}]}
301
+ # => {"extensions":[{"name":"selection-profile","folder":"...","workspace_id":"research","panel_definitions":[{"id":"selection-profile",...}],"tools":[{"uri":"selection_profile.summarize",...}]}]}
210
302
 
211
303
  hyperview tools list --json
212
304
  hyperview tools run selection_profile.summarize \
@@ -220,4 +312,4 @@ hyperview tools run selection_profile.summarize \
220
312
  - `--param 'top_k=5'` for numbers
221
313
  - `--param 'enabled=true'` for booleans
222
314
  - `--param 'name=foo'` for short strings (raw fallback) or `--param 'name="foo bar"'` for explicit JSON strings
223
- - `--param 'ids=["a","b"]'` or `--param 'opts={"k":1}'` for arrays/objects
315
+ - `--param 'ids=["a","b"]'` or `--param 'opts={"k":1}'` for arrays/objects
@@ -1,12 +1,17 @@
1
- # Plugins
1
+ # Extensions
2
2
 
3
- Use this guide when creating a HyperView plugin that includes backend Python tools and a frontend panel.
3
+ Use this guide when creating a HyperView extension that includes backend Python tools and a frontend panel.
4
4
 
5
5
  ## Model
6
6
 
7
- A plugin is a local extension folder with an `extension.toml` manifest. One folder can register Python tools, native panel modules, or both.
7
+ An extension is a local folder with an `extension.toml` manifest. One folder can register Python tools, panel modules, or both.
8
8
 
9
- Preferred shape for agent-authored, project-versioned plugins:
9
+ Extensions define reusable capabilities. They should not encode a whole workspace
10
+ layout or know about sibling panels. Compose concrete demo/workspace layouts
11
+ from Python with `hv.ui.View(...)` and `session.ui.apply_view(...)`, or from
12
+ the CLI with `hyperview ui panel add --extension ...`.
13
+
14
+ Preferred shape for agent-authored, project-versioned extensions:
10
15
 
11
16
  ```text
12
17
  .hyperview/extensions/selection-profile/
@@ -37,6 +42,10 @@ file = "panel.jsx"
37
42
 
38
43
  Valid panel positions are `right`, `bottom`, and `center`.
39
44
 
45
+ Treat `position` as a weak default for where the panel usually belongs. Cross-panel
46
+ relationships such as "this scatter is right of that scatter" belong to the
47
+ workspace view/composition layer, not the extension manifest.
48
+
40
49
  ## Backend Tools
41
50
 
42
51
  Tools are plain Python functions decorated with `@tool("namespace.name")`. The first argument is a `RunContext`.
@@ -69,13 +78,13 @@ def summarize_selection(ctx: RunContext, *, sample_ids: list[str] | None = None)
69
78
  }
70
79
  ```
71
80
 
72
- Use `ctx.dataset` for active dataset reads, `ctx.workspace` for workspace UI state, `ctx.extension_storage` for per-plugin writable files, `ctx.url_for(path)` for renderable artifact URLs, and `ctx.submit_job(...)` for long-running work.
81
+ Use `ctx.dataset` for active dataset reads, `ctx.workspace` for workspace UI state, `ctx.extension_storage` for per-extension writable files, `ctx.url_for(path)` for renderable artifact URLs, and `ctx.submit_job(...)` for long-running work.
73
82
 
74
83
  ### RunContext and Sample shapes
75
84
 
76
85
  - `ctx.dataset` &mdash; the active `Dataset`. Iterate samples with `for s in ctx.dataset.samples:` (returns `list[Sample]`). Look up by id with `ctx.dataset.get_samples_by_ids(ids)`.
77
86
  - `ctx.workspace.ui.selected_ids` &mdash; current selection (`list[str]`).
78
- - `ctx.extension_storage` &mdash; `pathlib.Path` to a writable per-plugin folder.
87
+ - `ctx.extension_storage` &mdash; `pathlib.Path` to a writable per-extension folder.
79
88
  - `ctx.url_for(path)` &mdash; returns a fetchable URL for a file under `extension_storage`.
80
89
  - `ctx.submit_job(...)` &mdash; schedule long-running work; returns a job handle.
81
90
 
@@ -123,9 +132,22 @@ export default function SelectionProfilePanel() {
123
132
  }
124
133
  ```
125
134
 
126
- Available SDK hooks include `usePanelRuntimeState`, `usePanelDatasetInfo`, `usePanelSamplesView`, `usePanelSelection`, `usePanelCommands`, `usePanelUiState`, `usePanelClient`, and `useTool`.
135
+ Available SDK hooks include `usePanelRuntimeState`, `usePanelHostState`, `usePanelDatasetInfo`, `usePanelSamplesView`, `usePanelSelectedSamples`, `usePanelSelection`, `usePanelHover`, `usePanelLayouts`, `usePanelLayoutView`, `usePanelCommands`, `usePanelUiState`, `usePanelClient`, and `useTool`.
136
+
137
+ For dataset-wide panel behavior, prefer `usePanelClient().querySamples(...)`,
138
+ `aggregateSamples(...)`, `selectSamples(...)`, `getSamplesByIds(...)`,
139
+ `searchSimilar(...)`, or a backend tool over scanning a fixed
140
+ `listSamples({ limit: ... })` page or hand-building API URLs in the browser.
141
+
142
+ Use `usePanelHostState()` for low-level synchronized host state instead of
143
+ importing frontend internals. Use narrower hooks such as `usePanelSelection()`,
144
+ `usePanelSelectedSamples()`, `usePanelHover()`, `usePanelLayouts()`, and
145
+ `usePanelLayoutView()` when the panel only needs one part of that state. Use
146
+ `usePanelCommands()` for host writes. Selection and active-layout changes
147
+ persist to runtime UI state by default; pass `{ persist: false }` only for
148
+ local transient UI changes.
127
149
 
128
- `useTool(uri)` returns `{ run, result, loading, error, reset }`. Call `run(params)` to invoke the tool; `result` holds the last successful return value, `loading` is true while a call is in flight, and `error` is the last failure message (or `null`). See [native-panels.md](native-panels.md#hook-return-shapes) for the full hook return shape table.
150
+ `useTool(uri)` returns `{ run, result, loading, error, reset }`. Call `run(params)` to invoke the tool; `result` holds the last successful return value, `loading` is true while a call is in flight, and `error` is the last failure message (or `null`). See [panel-modules.md](panel-modules.md#hook-return-shapes) for the full hook return shape table.
129
151
 
130
152
  ## CLI Workflow
131
153
 
@@ -135,7 +157,7 @@ Start a runtime for an existing workspace and dataset:
135
157
  hyperview serve --workspace research --dataset cifar10_demo --no-browser
136
158
  ```
137
159
 
138
- If the plugin already exists under `.hyperview/extensions/`, starting `hyperview serve` attaches it automatically. For a server that is already running, install or reload the plugin explicitly:
160
+ If the extension already exists under `.hyperview/extensions/`, starting `hyperview serve` registers it automatically. For a server that is already running, install or reload the extension explicitly:
139
161
 
140
162
  ```bash
141
163
  hyperview extension add .hyperview/extensions/selection-profile \
@@ -143,6 +165,19 @@ hyperview extension add .hyperview/extensions/selection-profile \
143
165
  --json
144
166
  ```
145
167
 
168
+ Installing an extension registers its tools and panel definitions. To instantiate
169
+ a panel from the CLI, add an extension-backed panel instance:
170
+
171
+ ```bash
172
+ hyperview ui panel add \
173
+ --workspace research \
174
+ --panel-id selection-profile \
175
+ --extension selection-profile \
176
+ --extension-panel selection-profile \
177
+ --position right \
178
+ --json
179
+ ```
180
+
146
181
  Inspect the result:
147
182
 
148
183
  ```bash
@@ -166,11 +201,34 @@ Reload after editing files:
166
201
  hyperview extension reload selection-profile --json
167
202
  ```
168
203
 
204
+ Compose a demo view from Python instead of importing runtime internals:
205
+
206
+ ```python
207
+ import hyperview as hv
208
+
209
+ view = hv.ui.View(
210
+ hv.ui.Horizontal(
211
+ hv.ui.Scatter("clip-map", title="CLIP", layout_key=clip_layout),
212
+ hv.ui.Scatter("hycoclip-map", title="HyCoCLIP", layout_key=hycoclip_layout),
213
+ ),
214
+ hv.ui.ExtensionPanel(
215
+ "readout",
216
+ extension="catalog-readout",
217
+ panel="readout",
218
+ position="right",
219
+ ),
220
+ )
221
+
222
+ session = hv.launch(dataset, block=False)
223
+ session.ui.add_extension(".hyperview/extensions/catalog-readout")
224
+ session.ui.apply_view(view)
225
+ ```
226
+
169
227
  ## Verification
170
228
 
171
- A good plugin smoke test proves all of these paths:
229
+ A good extension smoke test proves all of these paths:
172
230
 
173
- - `extension add` returns the plugin with expected tools and panels.
231
+ - `extension add` returns the extension with expected tools and panel definitions.
174
232
  - `GET /api/tools` (or `hyperview tools list --json`) includes the tool URI.
175
233
  - `hyperview tools run ...` returns data from the active dataset.
176
234
  - Tool-generated files under `ctx.extension_storage` are fetchable from URLs returned by `ctx.url_for(...)`.
@@ -180,7 +238,7 @@ A good plugin smoke test proves all of these paths:
180
238
 
181
239
  ## Constraints
182
240
 
183
- - Treat plugins as trusted local code. Python tools are imported and executed in the HyperView runtime process.
241
+ - Treat extensions as trusted local code. Python tools are imported and executed in the HyperView runtime process.
184
242
  - Do not use bare npm imports in panel modules unless you bundle first.
185
- - Keep plugin source outside `frontend/src`; the runtime loads it from local files.
186
- - Keep plugin examples small and high-level. Avoid private HyperView APIs in user-facing examples.
243
+ - Keep extension source outside `frontend/src`; the runtime loads panel modules from local extension files.
244
+ - Keep extension examples small and high-level. Avoid private HyperView APIs in user-facing examples.
@@ -0,0 +1,171 @@
1
+ # Panel Modules
2
+
3
+ Use this guide when writing a custom HyperView panel module that should behave like a built-in panel. Panel modules are shipped through [extensions.md](extensions.md).
4
+
5
+ ## Model
6
+
7
+ HyperView no longer treats runtime-added panels as iframe HTML pages.
8
+
9
+ Runtime custom panels are now panel modules:
10
+
11
+ - the user writes a local JavaScript module file
12
+ - the module is declared in an extension manifest
13
+ - the module is instantiated through `hv.ui.ExtensionPanel(...)` or `hyperview ui panel add --extension ...`
14
+ - HyperView loads that module directly into the host React tree
15
+ - the module can use the stable `window.HyperViewPanelSDK` surface
16
+
17
+ Built-in panels and runtime panels now share the same host panel system.
18
+
19
+ Use this surface for frontend-only panel code. Package it as an extension even
20
+ when it does not need Python tools. If the task is to open several panels in a
21
+ particular arrangement, use a workspace view from Python
22
+ (`hv.ui.View(...)` with `hv.launch(..., view=...)`) or the CLI `hyperview ui ...`
23
+ commands. Do not import `HyperViewRuntime`, `CustomPanelSpec`, or `Session` from
24
+ demo/user-facing scripts just to arrange panels.
25
+
26
+ ## Panel Module Contract
27
+
28
+ A runtime panel module must export either:
29
+
30
+ - a default React component
31
+ - or a named export `Panel`
32
+
33
+ The module runs in the browser and should use the SDK from `window.HyperViewPanelSDK`.
34
+ When a Python view provides panel `props`, HyperView passes them to the component
35
+ as the `props` prop, alongside `panel` and `panelId`; panel code can also read
36
+ them with `HyperViewPanelSDK.hooks.usePanelProps()`.
37
+
38
+ Minimal example:
39
+
40
+ ```js
41
+ const sdk = globalThis.HyperViewPanelSDK;
42
+ const { React, components, hooks } = sdk;
43
+ const { Panel, PanelToolbar } = components;
44
+ const { usePanelRuntimeState } = hooks;
45
+
46
+ export default function MyPanel() {
47
+ const { runtimeDatasetName } = usePanelRuntimeState();
48
+
49
+ return React.createElement(
50
+ Panel,
51
+ { className: "h-full" },
52
+ React.createElement(PanelToolbar, {
53
+ items: [{ id: "dataset", label: "Dataset", value: runtimeDatasetName || "unknown" }],
54
+ }),
55
+ React.createElement("div", { style: { padding: 12 } }, "Hello from a panel module")
56
+ );
57
+ }
58
+ ```
59
+
60
+ ## Stable SDK Surface
61
+
62
+ Current global SDK fields:
63
+
64
+ - `React`
65
+ - `components.Panel`
66
+ - `components.PanelHeader`
67
+ - `components.PanelTitle`
68
+ - `components.PanelToolbar`
69
+ - `components.PanelToolbarButton`
70
+ - `components.PanelToolbarMenu`
71
+ - `hooks.usePanelClient()`
72
+ - `hooks.usePanelCommands()`
73
+ - `hooks.usePanelDatasetInfo()`
74
+ - `hooks.usePanelHostState()`
75
+ - `hooks.usePanelHover()`
76
+ - `hooks.usePanelInstance()`
77
+ - `hooks.usePanelLayouts()`
78
+ - `hooks.usePanelLayoutView()`
79
+ - `hooks.usePanelProps()`
80
+ - `hooks.usePanelRuntimeState()`
81
+ - `hooks.usePanelSamples()`
82
+ - `hooks.usePanelSamplesView()`
83
+ - `hooks.usePanelSelectedSamples()`
84
+ - `hooks.usePanelSelection()`
85
+ - `hooks.usePanelUiState()`
86
+ - `hooks.useTool(uri)`
87
+ - `createClient(workspaceId)`
88
+
89
+ Important distinction:
90
+
91
+ - `usePanelSamplesView()` gives access to host-managed collection state and is the best hook for panels that should stay synchronized with the visible HyperView UI.
92
+ - `usePanelHostState()` gives low-level read access to the same host state used by built-in panels, without importing frontend internals.
93
+ - `usePanelClient()` or `createClient()` is the escape hatch for direct backend reads and control-plane calls.
94
+ - `useTool(uri)` calls an installed backend tool registered by an extension and returns `{ loading, result, error, run, reset }`.
95
+
96
+ ### Hook return shapes
97
+
98
+ Verified against the current `panel-sdk` surface:
99
+
100
+ - `usePanelSelection()` → `{ selectedIds: string[], selectionSource: SelectionUpdateSource }`
101
+ - `usePanelCommands()` → `{ setLabelFilter, setHoveredId, clearLassoSelection, clearSelection(): void, setSelection(ids, { source?, persist?, clearLasso? }): Promise<RuntimeSnapshot | null>, showSimilar({ sampleId, layoutKey?, spaceKey?, k?, source?, focus?, persist? }): Promise<RuntimeSnapshot | null>, setActiveLayout(layoutKey, { persist? }): Promise<RuntimeSnapshot | null>, setLayoutViewCamera(layoutKey, camera3d): void, setLayoutViewCameraPersisted(layoutKey, camera3d): Promise<null>, focusPanel(panelId): boolean, focusBuiltin(role): boolean, focusPanelByRole(role): boolean, closePanel(panelId): boolean }`
102
+ - `usePanelHover()` → `{ hoveredId, setHoveredId(id), clearHover() }`
103
+ - `usePanelLayoutView(layoutKey?)` → `{ layoutKey, view, camera3d, setCamera3d(camera3d) }`
104
+ - `usePanelLayouts()` → `{ layouts, spaces, get(layoutKey), getSpace(spaceKey), find(query), filter(query) }`; query supports `layoutKey`, `spaceKey`, `geometry`, `modelId`, and `dimension`.
105
+ - `usePanelSelectedSamples({ includeThumbnails? })` → `{ selectedIds, samples, loading, error }`
106
+ - `usePanelRuntimeState()` → `{ activeWorkspaceId, runtimeDatasetName, activeLayoutKey, activeSimilarityQuery, requestedLayoutKey, workspaces, customPanels, viewRevision, layoutViews }`
107
+ - `usePanelHostState()` → grouped low-level host state: `{ instance, runtime, datasetInfo, samples, samplesView, selection, hover, ui, filters, lasso, neighbors }`
108
+ - `usePanelProps()` → props supplied by the concrete `hv.ui.ExtensionPanel(...)` instance
109
+ - `usePanelUiState()` → `{ sampleGridSize, setSampleGridSize, scatterLabelOverlayMode, setScatterLabelOverlayMode }`
110
+ - `usePanelDatasetInfo()` / `usePanelSamples()` / `usePanelSamplesView()` → host-managed dataset and view state
111
+ - `useTool(uri)` → `{ loading: boolean, result: TResult | null, error: string | null, run(params?): Promise<TResult | null>, reset(): void }`
112
+ - `usePanelClient()` → low-level client; pair with `createClient(workspaceId)` for direct API calls. Useful methods include `querySamples`, `aggregateSamples`, `getSamplesByIds`, `searchSimilar`, `setSimilarityQuery`, `clearSimilarityQuery`, `setSelection`, and `selectSamples`.
113
+
114
+ To clear the current selection from a panel and persist it to the runtime, use `await usePanelCommands().setSelection([])`. Pass `{ persist: false }` only for local transient UI changes.
115
+
116
+ ## Placement
117
+
118
+ Extension-backed panel instances can be added in:
119
+
120
+ - `right`
121
+ - `bottom`
122
+ - `center`
123
+
124
+ Center placement lets a runtime panel behave like a normal center tab.
125
+
126
+ ## CLI Registration
127
+
128
+ Register the extension, then instantiate a panel module into a running workspace:
129
+
130
+ ```bash
131
+ hyperview extension add .hyperview/extensions/label-histogram \
132
+ --workspace imagenette-cli-20260412
133
+
134
+ hyperview ui panel add \
135
+ --host 127.0.0.1 \
136
+ --port 6262 \
137
+ --workspace imagenette-cli-20260412 \
138
+ --panel-id label-histogram \
139
+ --extension label-histogram \
140
+ --extension-panel label-histogram \
141
+ --position right
142
+ ```
143
+
144
+ ## Verification
145
+
146
+ After `hyperview ui panel add --extension ...`:
147
+
148
+ - `curl 'http://127.0.0.1:6262/api/runtime?workspace_id=<ws>'` should list the panel under `workspace.ui.custom_panels[*]` with `data.module_src` set to a `/api/panels/content/<ws>/<panel-id>/<file>` URL.
149
+ - Fetching `data.module_src` should return `application/javascript` with your module body.
150
+ - The panel should appear in the live UI in the requested `position` slot.
151
+
152
+ ## Good Practices
153
+
154
+ - Prefer panel modules over HTML or iframe content.
155
+ - Use `usePanelSamplesView()` for view-synchronized behavior.
156
+ - Use `usePanelCommands()` for host interactions such as label filtering or selection changes.
157
+ - Use `usePanelHostState()` when a custom panel needs low-level synchronized host state such as hover, selection, lasso, neighbors, layout views, or active workspace context.
158
+ - Use `usePanelLayouts()` for layout/space lookup instead of scanning `datasetInfo.layouts` by hand.
159
+ - Use `usePanelSelectedSamples()` for selected sample metadata instead of manually watching selected ids and fetching samples.
160
+ - Use `usePanelClient()` only for data that is not already available through the host state.
161
+ - Use `usePanelClient().querySamples(...)`, `aggregateSamples(...)`, or `selectSamples(...)` for dataset-wide behavior instead of fixed-limit client scans.
162
+ - Use `usePanelClient().searchSimilar(...)` instead of hand-building `/api/search/similar/...` URLs.
163
+ - Keep the panel self-contained under `.hyperview/extensions/<extension-name>/`.
164
+ - If the panel needs sibling assets, keep them next to the module and reference them with relative URLs.
165
+ - Do not render a second title/header inside a normal Dockview runtime panel unless there is a strong reason. Dockview already provides the tab title. Built-in center and runtime panels should usually start with the standardized `PanelToolbar` row.
166
+
167
+ ## Current Limitation
168
+
169
+ Panel modules should currently be authored as browser-loadable JavaScript modules.
170
+
171
+ If an agent wants TypeScript or JSX ergonomics, it should bundle or transpile to JavaScript before registration.