Deploy Hub Classic research agent UI
Browse filesUse the selected Hub Classic light/dark Prefab design, official Hugging Face logo, Source Sans 3 and IBM Plex Mono, with MCP App CSP resource domains for Google Fonts.
- Dockerfile +1 -1
- __pycache__/fastmcp_research_app.cpython-314.pyc +0 -0
- research/__pycache__/__init__.cpython-314.pyc +0 -0
- research/__pycache__/activity_narrator.cpython-314.pyc +0 -0
- research/__pycache__/app_artifacts.cpython-314.pyc +0 -0
- research/__pycache__/app_auth.cpython-314.pyc +0 -0
- research/__pycache__/app_jobs.cpython-314.pyc +0 -0
- research/__pycache__/app_observability.cpython-314.pyc +0 -0
- research/__pycache__/app_renderer.cpython-314.pyc +0 -0
- research/__pycache__/app_ui.cpython-314.pyc +0 -0
- research/__pycache__/fastmcp_server.cpython-314.pyc +0 -0
- research/__pycache__/research_runner.cpython-314.pyc +0 -0
- research/__pycache__/research_workspace.cpython-314.pyc +0 -0
- research/app_ui.py +39 -3
- research/assets/huggingface-logo.svg +37 -0
- research/fastmcp_server.py +24 -1
- research/hf_design.py +499 -0
Dockerfile
CHANGED
|
@@ -6,7 +6,7 @@ RUN apt-get update && \
|
|
| 6 |
|
| 7 |
WORKDIR /app
|
| 8 |
RUN uv pip install --system --no-cache \
|
| 9 |
-
fast-agent-mcp==0.9.
|
| 10 |
'fastmcp[apps]==3.4.4' \
|
| 11 |
prefab-ui==0.20.2 \
|
| 12 |
huggingface_hub
|
|
|
|
| 6 |
|
| 7 |
WORKDIR /app
|
| 8 |
RUN uv pip install --system --no-cache \
|
| 9 |
+
fast-agent-mcp==0.9.21 \
|
| 10 |
'fastmcp[apps]==3.4.4' \
|
| 11 |
prefab-ui==0.20.2 \
|
| 12 |
huggingface_hub
|
__pycache__/fastmcp_research_app.cpython-314.pyc
DELETED
|
Binary file (35.1 kB)
|
|
|
research/__pycache__/__init__.cpython-314.pyc
DELETED
|
Binary file (245 Bytes)
|
|
|
research/__pycache__/activity_narrator.cpython-314.pyc
DELETED
|
Binary file (18.6 kB)
|
|
|
research/__pycache__/app_artifacts.cpython-314.pyc
DELETED
|
Binary file (7.45 kB)
|
|
|
research/__pycache__/app_auth.cpython-314.pyc
DELETED
|
Binary file (3.39 kB)
|
|
|
research/__pycache__/app_jobs.cpython-314.pyc
DELETED
|
Binary file (23.7 kB)
|
|
|
research/__pycache__/app_observability.cpython-314.pyc
DELETED
|
Binary file (21.4 kB)
|
|
|
research/__pycache__/app_renderer.cpython-314.pyc
DELETED
|
Binary file (5.41 kB)
|
|
|
research/__pycache__/app_ui.cpython-314.pyc
DELETED
|
Binary file (24.5 kB)
|
|
|
research/__pycache__/fastmcp_server.cpython-314.pyc
DELETED
|
Binary file (9.54 kB)
|
|
|
research/__pycache__/research_runner.cpython-314.pyc
DELETED
|
Binary file (8.5 kB)
|
|
|
research/__pycache__/research_workspace.cpython-314.pyc
DELETED
|
Binary file (8.31 kB)
|
|
|
research/app_ui.py
CHANGED
|
@@ -15,6 +15,7 @@ from prefab_ui.components import (
|
|
| 15 |
Dialog,
|
| 16 |
Div,
|
| 17 |
Heading,
|
|
|
|
| 18 |
If,
|
| 19 |
Markdown,
|
| 20 |
Row,
|
|
@@ -24,6 +25,15 @@ from prefab_ui.components import (
|
|
| 24 |
from prefab_ui.components.control_flow import ForEach
|
| 25 |
from prefab_ui.rx import RESULT, STATE
|
| 26 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
BROADSHEET_CSS = """
|
| 28 |
.dispatch-app {
|
| 29 |
--dispatch-accent: color-mix(in oklab, var(--warning) 68%, var(--foreground));
|
|
@@ -562,8 +572,11 @@ def build_research_ui(
|
|
| 562 |
*,
|
| 563 |
build_id: str = "dev",
|
| 564 |
live: bool = True,
|
|
|
|
| 565 |
) -> PrefabApp:
|
| 566 |
snapshot = _ui_snapshot(snapshot)
|
|
|
|
|
|
|
| 567 |
on_mount = None
|
| 568 |
if live:
|
| 569 |
on_mount = [
|
|
@@ -635,10 +648,19 @@ def build_research_ui(
|
|
| 635 |
if not live:
|
| 636 |
read_report = SetState("chat_sent", True)
|
| 637 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 638 |
with PrefabApp(
|
| 639 |
title="Research Dispatch",
|
| 640 |
-
css_class=
|
| 641 |
-
css=
|
|
|
|
| 642 |
state={
|
| 643 |
"job": snapshot,
|
| 644 |
"topic": topic,
|
|
@@ -655,7 +677,21 @@ def build_research_ui(
|
|
| 655 |
with Div(css_class="dispatch-sheet", on_mount=on_mount):
|
| 656 |
with Column(css_class="dispatch-header", gap=0):
|
| 657 |
with Row(css_class="dispatch-topbar", gap=4):
|
| 658 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 659 |
with Row(css_class="dispatch-controls", gap=3):
|
| 660 |
with Row(css_class="dispatch-stats", gap=2):
|
| 661 |
Text(STATE.job.elapsed, css_class="dispatch-stat")
|
|
|
|
| 15 |
Dialog,
|
| 16 |
Div,
|
| 17 |
Heading,
|
| 18 |
+
Image,
|
| 19 |
If,
|
| 20 |
Markdown,
|
| 21 |
Row,
|
|
|
|
| 25 |
from prefab_ui.components.control_flow import ForEach
|
| 26 |
from prefab_ui.rx import RESULT, STATE
|
| 27 |
|
| 28 |
+
from .hf_design import (
|
| 29 |
+
HF_DESIGN_CSS,
|
| 30 |
+
HF_FONT_STYLESHEET,
|
| 31 |
+
HF_LOGO_DATA_URI,
|
| 32 |
+
HFDesign,
|
| 33 |
+
design_css_class,
|
| 34 |
+
validate_design,
|
| 35 |
+
)
|
| 36 |
+
|
| 37 |
BROADSHEET_CSS = """
|
| 38 |
.dispatch-app {
|
| 39 |
--dispatch-accent: color-mix(in oklab, var(--warning) 68%, var(--foreground));
|
|
|
|
| 572 |
*,
|
| 573 |
build_id: str = "dev",
|
| 574 |
live: bool = True,
|
| 575 |
+
design: HFDesign | None = None,
|
| 576 |
) -> PrefabApp:
|
| 577 |
snapshot = _ui_snapshot(snapshot)
|
| 578 |
+
if design is not None:
|
| 579 |
+
design = validate_design(design)
|
| 580 |
on_mount = None
|
| 581 |
if live:
|
| 582 |
on_mount = [
|
|
|
|
| 648 |
if not live:
|
| 649 |
read_report = SetState("chat_sent", True)
|
| 650 |
|
| 651 |
+
css_class = "dispatch-app"
|
| 652 |
+
css = [BROADSHEET_CSS]
|
| 653 |
+
stylesheets = None
|
| 654 |
+
if design is not None:
|
| 655 |
+
css_class = design_css_class(design)
|
| 656 |
+
css.append(HF_DESIGN_CSS)
|
| 657 |
+
stylesheets = [HF_FONT_STYLESHEET]
|
| 658 |
+
|
| 659 |
with PrefabApp(
|
| 660 |
title="Research Dispatch",
|
| 661 |
+
css_class=css_class,
|
| 662 |
+
css=css,
|
| 663 |
+
stylesheets=stylesheets,
|
| 664 |
state={
|
| 665 |
"job": snapshot,
|
| 666 |
"topic": topic,
|
|
|
|
| 677 |
with Div(css_class="dispatch-sheet", on_mount=on_mount):
|
| 678 |
with Column(css_class="dispatch-header", gap=0):
|
| 679 |
with Row(css_class="dispatch-topbar", gap=4):
|
| 680 |
+
if design is None:
|
| 681 |
+
Text("Research Dispatch", css_class="dispatch-kicker")
|
| 682 |
+
else:
|
| 683 |
+
with Row(css_class="hf-brand", gap=2):
|
| 684 |
+
Image(
|
| 685 |
+
src=HF_LOGO_DATA_URI,
|
| 686 |
+
alt="Hugging Face",
|
| 687 |
+
css_class="hf-brand-logo",
|
| 688 |
+
)
|
| 689 |
+
with Column(css_class="hf-brand-copy", gap=0):
|
| 690 |
+
Text("Hugging Face", css_class="hf-brand-name")
|
| 691 |
+
Text(
|
| 692 |
+
"Research Agent",
|
| 693 |
+
css_class="hf-brand-product",
|
| 694 |
+
)
|
| 695 |
with Row(css_class="dispatch-controls", gap=3):
|
| 696 |
with Row(css_class="dispatch-stats", gap=2):
|
| 697 |
Text(STATE.job.elapsed, css_class="dispatch-stat")
|
research/assets/huggingface-logo.svg
ADDED
|
|
research/fastmcp_server.py
CHANGED
|
@@ -10,6 +10,7 @@ from typing import Annotated, Any
|
|
| 10 |
from fast_agent import FastAgent
|
| 11 |
from fastmcp import Context as MCPContext
|
| 12 |
from fastmcp import FastMCP, FastMCPApp
|
|
|
|
| 13 |
from pydantic import Field
|
| 14 |
from prefab_ui.app import PrefabApp
|
| 15 |
|
|
@@ -23,10 +24,25 @@ from .app_jobs import (
|
|
| 23 |
)
|
| 24 |
from .app_renderer import app_build_id, install_versioned_renderer
|
| 25 |
from .app_ui import build_research_ui
|
|
|
|
| 26 |
from .research_runner import ResearchRunner
|
| 27 |
|
| 28 |
RESEARCH_HOME = Path(__file__).parent
|
| 29 |
AGENT_CARDS = RESEARCH_HOME / "agent-cards"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
|
| 31 |
|
| 32 |
def parse_args() -> argparse.Namespace:
|
|
@@ -82,7 +98,14 @@ def register_research_app(
|
|
| 82 |
else "local development user"
|
| 83 |
)
|
| 84 |
job.add_event(f"Workspace access confirmed for {identity}.", kind="Setup")
|
| 85 |
-
return build_research_ui(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
|
| 87 |
@app.tool()
|
| 88 |
async def start_research(
|
|
|
|
| 10 |
from fast_agent import FastAgent
|
| 11 |
from fastmcp import Context as MCPContext
|
| 12 |
from fastmcp import FastMCP, FastMCPApp
|
| 13 |
+
from fastmcp.apps import ResourceCSP
|
| 14 |
from pydantic import Field
|
| 15 |
from prefab_ui.app import PrefabApp
|
| 16 |
|
|
|
|
| 24 |
)
|
| 25 |
from .app_renderer import app_build_id, install_versioned_renderer
|
| 26 |
from .app_ui import build_research_ui
|
| 27 |
+
from .hf_design import HF_RESOURCE_DOMAINS, HFDesign
|
| 28 |
from .research_runner import ResearchRunner
|
| 29 |
|
| 30 |
RESEARCH_HOME = Path(__file__).parent
|
| 31 |
AGENT_CARDS = RESEARCH_HOME / "agent-cards"
|
| 32 |
+
PRODUCTION_UI_DESIGN: HFDesign = "hub-classic"
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def configure_research_ui_csp(app: FastMCPApp, tool_name: str = "research") -> None:
|
| 36 |
+
"""Allow the Google-hosted Hub fonts used by design variants."""
|
| 37 |
+
csp = ResourceCSP(resource_domains=list(HF_RESOURCE_DOMAINS)).model_dump(
|
| 38 |
+
by_alias=True,
|
| 39 |
+
exclude_none=True,
|
| 40 |
+
)
|
| 41 |
+
for tool in app._local._components.values():
|
| 42 |
+
if getattr(tool, "name", None) == tool_name:
|
| 43 |
+
tool.meta.setdefault("ui", {})["csp"] = csp
|
| 44 |
+
return
|
| 45 |
+
raise RuntimeError(f"Prefab UI tool was not registered: {tool_name}")
|
| 46 |
|
| 47 |
|
| 48 |
def parse_args() -> argparse.Namespace:
|
|
|
|
| 98 |
else "local development user"
|
| 99 |
)
|
| 100 |
job.add_event(f"Workspace access confirmed for {identity}.", kind="Setup")
|
| 101 |
+
return build_research_ui(
|
| 102 |
+
topic,
|
| 103 |
+
job.snapshot(),
|
| 104 |
+
build_id=build_id,
|
| 105 |
+
design=PRODUCTION_UI_DESIGN,
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
configure_research_ui_csp(app)
|
| 109 |
|
| 110 |
@app.tool()
|
| 111 |
async def start_research(
|
research/hf_design.py
ADDED
|
@@ -0,0 +1,499 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Hugging Face design options for Prefab review builds."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import base64
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
from typing import Literal, get_args
|
| 8 |
+
|
| 9 |
+
HFDesign = Literal["hub-classic", "spaces-gradient", "dataset-studio"]
|
| 10 |
+
HF_DESIGNS = get_args(HFDesign)
|
| 11 |
+
|
| 12 |
+
HF_FONT_STYLESHEET = (
|
| 13 |
+
"https://fonts.googleapis.com/css2?"
|
| 14 |
+
"family=IBM+Plex+Mono:wght@400;600&"
|
| 15 |
+
"family=Source+Sans+3:wght@400;600;700&display=swap"
|
| 16 |
+
)
|
| 17 |
+
HF_RESOURCE_DOMAINS = (
|
| 18 |
+
"https://fonts.googleapis.com",
|
| 19 |
+
"https://fonts.gstatic.com",
|
| 20 |
+
)
|
| 21 |
+
|
| 22 |
+
_LOGO = Path(__file__).with_name("assets") / "huggingface-logo.svg"
|
| 23 |
+
HF_LOGO_DATA_URI = (
|
| 24 |
+
"data:image/svg+xml;base64,"
|
| 25 |
+
+ base64.b64encode(_LOGO.read_bytes()).decode("ascii")
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
HF_DESIGN_CSS = """
|
| 29 |
+
.hf-design {
|
| 30 |
+
--hf-yellow: #ffd21e;
|
| 31 |
+
--hf-amber: #f59e0b;
|
| 32 |
+
--hf-orange: #ff9d0b;
|
| 33 |
+
--hf-blue: #3b82f6;
|
| 34 |
+
--hf-violet: #7c3aed;
|
| 35 |
+
--hf-gray-50: #f9fafb;
|
| 36 |
+
--hf-gray-100: #f3f4f6;
|
| 37 |
+
--hf-gray-200: #e5e7eb;
|
| 38 |
+
--hf-gray-300: #d1d5db;
|
| 39 |
+
--hf-gray-400: #9ca3af;
|
| 40 |
+
--hf-gray-500: #6b7280;
|
| 41 |
+
--hf-gray-700: #374151;
|
| 42 |
+
--hf-gray-850: #141c2e;
|
| 43 |
+
--hf-gray-925: #101623;
|
| 44 |
+
--hf-gray-950: #0b0f19;
|
| 45 |
+
--hf-canvas: var(--hf-gray-50);
|
| 46 |
+
--hf-surface: #fff;
|
| 47 |
+
--hf-surface-soft: var(--hf-gray-50);
|
| 48 |
+
--hf-line: var(--hf-gray-200);
|
| 49 |
+
--hf-text: #111827;
|
| 50 |
+
--hf-muted: var(--hf-gray-500);
|
| 51 |
+
--dispatch-accent: #b45309;
|
| 52 |
+
--dispatch-live: #10b981;
|
| 53 |
+
--dispatch-panel: var(--hf-surface-soft);
|
| 54 |
+
min-height: 800px;
|
| 55 |
+
padding: 24px;
|
| 56 |
+
background: var(--hf-canvas);
|
| 57 |
+
color: var(--hf-text);
|
| 58 |
+
font-family: "Source Sans 3", "Source Sans Pro", ui-sans-serif, system-ui, sans-serif;
|
| 59 |
+
}
|
| 60 |
+
.dark .hf-design {
|
| 61 |
+
--hf-canvas: var(--hf-gray-950);
|
| 62 |
+
--hf-surface: var(--hf-gray-925);
|
| 63 |
+
--hf-surface-soft: var(--hf-gray-850);
|
| 64 |
+
--hf-line: #263044;
|
| 65 |
+
--hf-text: #e5e7eb;
|
| 66 |
+
--hf-muted: #9ca3af;
|
| 67 |
+
--dispatch-accent: #fbbf24;
|
| 68 |
+
--dispatch-live: #34d399;
|
| 69 |
+
}
|
| 70 |
+
.hf-design .dispatch-sheet {
|
| 71 |
+
width: min(100%, 920px);
|
| 72 |
+
height: 800px;
|
| 73 |
+
border-color: var(--hf-line);
|
| 74 |
+
border-radius: 14px;
|
| 75 |
+
background: var(--hf-surface);
|
| 76 |
+
box-shadow: 0 1px 2px rgb(15 23 42 / .04), 0 10px 32px rgb(15 23 42 / .08);
|
| 77 |
+
}
|
| 78 |
+
.dark .hf-design .dispatch-sheet {
|
| 79 |
+
box-shadow: 0 18px 50px rgb(0 0 0 / .28);
|
| 80 |
+
}
|
| 81 |
+
.hf-design .dispatch-header {
|
| 82 |
+
padding: 22px 30px 18px;
|
| 83 |
+
}
|
| 84 |
+
.hf-design .dispatch-body {
|
| 85 |
+
padding: 0 30px;
|
| 86 |
+
}
|
| 87 |
+
.hf-design .dispatch-footer-log {
|
| 88 |
+
padding-right: 30px;
|
| 89 |
+
padding-left: 30px;
|
| 90 |
+
}
|
| 91 |
+
.hf-design .dispatch-session {
|
| 92 |
+
padding-right: 30px;
|
| 93 |
+
padding-left: 30px;
|
| 94 |
+
}
|
| 95 |
+
.hf-design .dispatch-kicker,
|
| 96 |
+
.hf-design .dispatch-section-label,
|
| 97 |
+
.hf-design .dispatch-time,
|
| 98 |
+
.hf-design .dispatch-source,
|
| 99 |
+
.hf-design .dispatch-meta,
|
| 100 |
+
.hf-design .dispatch-log-line,
|
| 101 |
+
.hf-design .dispatch-query-toggle,
|
| 102 |
+
.hf-design .dispatch-status,
|
| 103 |
+
.hf-design .dispatch-stat,
|
| 104 |
+
.hf-design .dispatch-trace,
|
| 105 |
+
.hf-design .dispatch-log-toggle-button {
|
| 106 |
+
font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
|
| 107 |
+
}
|
| 108 |
+
.hf-brand {
|
| 109 |
+
min-width: 0;
|
| 110 |
+
flex: none;
|
| 111 |
+
align-items: center;
|
| 112 |
+
}
|
| 113 |
+
.hf-brand-logo {
|
| 114 |
+
width: 32px;
|
| 115 |
+
height: 30px;
|
| 116 |
+
flex: none;
|
| 117 |
+
object-fit: contain;
|
| 118 |
+
}
|
| 119 |
+
.hf-brand-copy {
|
| 120 |
+
min-width: 0;
|
| 121 |
+
}
|
| 122 |
+
.hf-brand-name {
|
| 123 |
+
color: var(--hf-text);
|
| 124 |
+
font-size: 16px;
|
| 125 |
+
font-weight: 700;
|
| 126 |
+
line-height: 1;
|
| 127 |
+
}
|
| 128 |
+
.hf-brand-product {
|
| 129 |
+
margin-top: 4px;
|
| 130 |
+
color: var(--hf-muted);
|
| 131 |
+
font-family: "IBM Plex Mono", ui-monospace, monospace;
|
| 132 |
+
font-size: 9px;
|
| 133 |
+
letter-spacing: .09em;
|
| 134 |
+
line-height: 1;
|
| 135 |
+
text-transform: uppercase;
|
| 136 |
+
}
|
| 137 |
+
.hf-design .dispatch-controls {
|
| 138 |
+
gap: 10px;
|
| 139 |
+
}
|
| 140 |
+
.hf-design .dispatch-stat {
|
| 141 |
+
color: var(--hf-muted);
|
| 142 |
+
font-size: 10px;
|
| 143 |
+
letter-spacing: 0;
|
| 144 |
+
text-transform: none;
|
| 145 |
+
}
|
| 146 |
+
.hf-design .dispatch-control-divider {
|
| 147 |
+
background: var(--hf-line);
|
| 148 |
+
}
|
| 149 |
+
.hf-design .dispatch-status {
|
| 150 |
+
min-height: 26px;
|
| 151 |
+
border: 1px solid transparent;
|
| 152 |
+
border-radius: 7px;
|
| 153 |
+
font-weight: 600;
|
| 154 |
+
letter-spacing: 0;
|
| 155 |
+
}
|
| 156 |
+
.hf-design .dispatch-status-running {
|
| 157 |
+
border-color: #fde68a;
|
| 158 |
+
background: #fffbeb;
|
| 159 |
+
color: #92400e;
|
| 160 |
+
}
|
| 161 |
+
.dark .hf-design .dispatch-status-running {
|
| 162 |
+
border-color: rgb(245 158 11 / .28);
|
| 163 |
+
background: rgb(245 158 11 / .12);
|
| 164 |
+
color: #fbbf24;
|
| 165 |
+
}
|
| 166 |
+
.hf-design .dispatch-status-complete {
|
| 167 |
+
border-color: #bbf7d0;
|
| 168 |
+
background: #f0fdf4;
|
| 169 |
+
color: #047857;
|
| 170 |
+
}
|
| 171 |
+
.dark .hf-design .dispatch-status-complete {
|
| 172 |
+
border-color: rgb(16 185 129 / .28);
|
| 173 |
+
background: rgb(16 185 129 / .12);
|
| 174 |
+
color: #34d399;
|
| 175 |
+
}
|
| 176 |
+
.hf-design .dispatch-status-failed {
|
| 177 |
+
border-color: #fecaca;
|
| 178 |
+
background: #fef2f2;
|
| 179 |
+
color: #b91c1c;
|
| 180 |
+
}
|
| 181 |
+
.dark .hf-design .dispatch-status-failed {
|
| 182 |
+
border-color: rgb(239 68 68 / .28);
|
| 183 |
+
background: rgb(239 68 68 / .12);
|
| 184 |
+
color: #fca5a5;
|
| 185 |
+
}
|
| 186 |
+
.hf-design .dispatch-header-separator,
|
| 187 |
+
.hf-design .dispatch-event,
|
| 188 |
+
.hf-design .dispatch-footer {
|
| 189 |
+
border-color: var(--hf-line);
|
| 190 |
+
}
|
| 191 |
+
.hf-design .dispatch-section-label {
|
| 192 |
+
color: var(--hf-muted);
|
| 193 |
+
font-size: 10px;
|
| 194 |
+
font-weight: 600;
|
| 195 |
+
letter-spacing: .08em;
|
| 196 |
+
}
|
| 197 |
+
.hf-design .dispatch-headline,
|
| 198 |
+
.hf-design .dispatch-query,
|
| 199 |
+
.hf-design .dispatch-report-card-title,
|
| 200 |
+
.hf-design .dispatch-report-card-copy,
|
| 201 |
+
.hf-design .dispatch-current-copy,
|
| 202 |
+
.hf-design .dispatch-event-message {
|
| 203 |
+
font-family: "Source Sans 3", "Source Sans Pro", ui-sans-serif, system-ui, sans-serif;
|
| 204 |
+
}
|
| 205 |
+
.hf-design .dispatch-headline {
|
| 206 |
+
color: var(--hf-text);
|
| 207 |
+
font-size: clamp(26px, 4vw, 34px);
|
| 208 |
+
font-weight: 700;
|
| 209 |
+
letter-spacing: -.025em;
|
| 210 |
+
}
|
| 211 |
+
.hf-design .dispatch-query {
|
| 212 |
+
color: var(--hf-muted);
|
| 213 |
+
font-size: 14px;
|
| 214 |
+
}
|
| 215 |
+
.hf-design .dispatch-report-card,
|
| 216 |
+
.hf-design .dispatch-markdown-report,
|
| 217 |
+
.hf-design .dispatch-log {
|
| 218 |
+
border-color: var(--hf-line);
|
| 219 |
+
background: var(--hf-surface-soft);
|
| 220 |
+
}
|
| 221 |
+
.hf-design .dispatch-report-card {
|
| 222 |
+
border-radius: 11px;
|
| 223 |
+
}
|
| 224 |
+
.hf-design .dispatch-report-card-title {
|
| 225 |
+
color: var(--hf-text);
|
| 226 |
+
font-weight: 600;
|
| 227 |
+
}
|
| 228 |
+
.hf-design .dispatch-report-card-copy,
|
| 229 |
+
.hf-design .dispatch-time,
|
| 230 |
+
.hf-design .dispatch-log-line,
|
| 231 |
+
.hf-design .dispatch-trace,
|
| 232 |
+
.hf-design .dispatch-meta {
|
| 233 |
+
color: var(--hf-muted);
|
| 234 |
+
}
|
| 235 |
+
.hf-design .dispatch-current-copy {
|
| 236 |
+
color: var(--hf-text);
|
| 237 |
+
font-size: 20px;
|
| 238 |
+
font-weight: 400;
|
| 239 |
+
}
|
| 240 |
+
.hf-design .dispatch-event-message {
|
| 241 |
+
color: var(--hf-text);
|
| 242 |
+
}
|
| 243 |
+
.hf-design .dispatch-source {
|
| 244 |
+
font-weight: 600;
|
| 245 |
+
}
|
| 246 |
+
.hf-design .dispatch-rule {
|
| 247 |
+
background: var(--hf-line);
|
| 248 |
+
}
|
| 249 |
+
.hf-design .dispatch-footer {
|
| 250 |
+
background: var(--hf-surface);
|
| 251 |
+
}
|
| 252 |
+
.hf-design .dispatch-log-toggle-button {
|
| 253 |
+
color: var(--hf-muted);
|
| 254 |
+
}
|
| 255 |
+
|
| 256 |
+
/* Option A: closest to the neutral Hub list and profile surfaces. */
|
| 257 |
+
.hf-hub-classic {
|
| 258 |
+
background:
|
| 259 |
+
linear-gradient(180deg, var(--hf-surface) 0 68px, var(--hf-canvas) 68px);
|
| 260 |
+
}
|
| 261 |
+
.hf-hub-classic .dispatch-sheet {
|
| 262 |
+
border-radius: 12px;
|
| 263 |
+
}
|
| 264 |
+
.hf-hub-classic .dispatch-header {
|
| 265 |
+
border-top: 3px solid var(--hf-yellow);
|
| 266 |
+
}
|
| 267 |
+
.hf-hub-classic .dispatch-current-block {
|
| 268 |
+
margin-top: 20px;
|
| 269 |
+
padding: 18px;
|
| 270 |
+
border: 1px solid var(--hf-line);
|
| 271 |
+
border-radius: 11px;
|
| 272 |
+
background: var(--hf-surface);
|
| 273 |
+
box-shadow: 0 1px 3px rgb(15 23 42 / .05);
|
| 274 |
+
}
|
| 275 |
+
.hf-hub-classic .dispatch-history {
|
| 276 |
+
margin-top: 18px;
|
| 277 |
+
}
|
| 278 |
+
.hf-hub-classic .dispatch-report-card {
|
| 279 |
+
background: linear-gradient(135deg, var(--hf-surface), var(--hf-surface-soft));
|
| 280 |
+
box-shadow: 0 1px 3px rgb(15 23 42 / .04);
|
| 281 |
+
}
|
| 282 |
+
.hf-hub-classic .dispatch-report-action {
|
| 283 |
+
border-color: var(--hf-line);
|
| 284 |
+
background: var(--hf-surface);
|
| 285 |
+
color: var(--hf-text);
|
| 286 |
+
}
|
| 287 |
+
|
| 288 |
+
/* Option B: borrows the colorful featured-card language from Spaces. */
|
| 289 |
+
.hf-spaces-gradient {
|
| 290 |
+
--dispatch-accent: #7c3aed;
|
| 291 |
+
background:
|
| 292 |
+
radial-gradient(circle at 15% 15%, rgb(255 210 30 / .14), transparent 28%),
|
| 293 |
+
radial-gradient(circle at 85% 5%, rgb(99 102 241 / .16), transparent 30%),
|
| 294 |
+
var(--hf-canvas);
|
| 295 |
+
}
|
| 296 |
+
.dark .hf-spaces-gradient {
|
| 297 |
+
--dispatch-accent: #c4b5fd;
|
| 298 |
+
}
|
| 299 |
+
.hf-spaces-gradient .dispatch-sheet {
|
| 300 |
+
overflow: hidden;
|
| 301 |
+
border-color: rgb(99 102 241 / .22);
|
| 302 |
+
border-radius: 20px;
|
| 303 |
+
}
|
| 304 |
+
.hf-spaces-gradient .dispatch-header {
|
| 305 |
+
position: relative;
|
| 306 |
+
background:
|
| 307 |
+
linear-gradient(135deg, rgb(255 210 30 / .12), rgb(99 102 241 / .10) 55%, rgb(236 72 153 / .08));
|
| 308 |
+
}
|
| 309 |
+
.dark .hf-spaces-gradient .dispatch-header {
|
| 310 |
+
background:
|
| 311 |
+
linear-gradient(135deg, rgb(245 158 11 / .12), rgb(79 70 229 / .18) 55%, rgb(190 24 93 / .12));
|
| 312 |
+
}
|
| 313 |
+
.hf-spaces-gradient .dispatch-header::after {
|
| 314 |
+
content: "";
|
| 315 |
+
position: absolute;
|
| 316 |
+
right: -50px;
|
| 317 |
+
bottom: -80px;
|
| 318 |
+
width: 180px;
|
| 319 |
+
height: 180px;
|
| 320 |
+
border: 28px solid rgb(255 210 30 / .16);
|
| 321 |
+
border-radius: 999px;
|
| 322 |
+
pointer-events: none;
|
| 323 |
+
}
|
| 324 |
+
.hf-spaces-gradient .dispatch-current-block {
|
| 325 |
+
margin-top: 22px;
|
| 326 |
+
padding: 22px;
|
| 327 |
+
overflow: hidden;
|
| 328 |
+
border-radius: 15px;
|
| 329 |
+
background: linear-gradient(135deg, #34469b, #4f46e5 52%, #7c3aed);
|
| 330 |
+
box-shadow: 0 12px 28px rgb(79 70 229 / .22);
|
| 331 |
+
}
|
| 332 |
+
.hf-spaces-gradient .dispatch-current-block .dispatch-time,
|
| 333 |
+
.hf-spaces-gradient .dispatch-current-block .dispatch-source,
|
| 334 |
+
.hf-spaces-gradient .dispatch-current-block .dispatch-current-copy {
|
| 335 |
+
color: #fff;
|
| 336 |
+
}
|
| 337 |
+
.hf-spaces-gradient .dispatch-current-block .dispatch-time {
|
| 338 |
+
opacity: .7;
|
| 339 |
+
}
|
| 340 |
+
.hf-spaces-gradient .dispatch-current-block .dispatch-live-dot {
|
| 341 |
+
background: var(--hf-yellow);
|
| 342 |
+
}
|
| 343 |
+
.hf-spaces-gradient .dispatch-current-block .dispatch-rule {
|
| 344 |
+
background: rgb(255 255 255 / .25);
|
| 345 |
+
}
|
| 346 |
+
.hf-spaces-gradient .dispatch-current-block .dispatch-rule-running::after,
|
| 347 |
+
.hf-spaces-gradient .dispatch-current-block .dispatch-rule-completed {
|
| 348 |
+
background: var(--hf-yellow);
|
| 349 |
+
}
|
| 350 |
+
.hf-spaces-gradient .dispatch-report-card {
|
| 351 |
+
border: 0;
|
| 352 |
+
background: linear-gradient(145deg, #34469b, #4f46e5);
|
| 353 |
+
color: #fff;
|
| 354 |
+
box-shadow: 0 8px 20px rgb(59 70 155 / .18);
|
| 355 |
+
}
|
| 356 |
+
.hf-spaces-gradient .dispatch-report-card:nth-child(2) {
|
| 357 |
+
background: linear-gradient(145deg, #6634b8, #c02675);
|
| 358 |
+
}
|
| 359 |
+
.hf-spaces-gradient .dispatch-report-card .dispatch-section-label,
|
| 360 |
+
.hf-spaces-gradient .dispatch-report-card-title,
|
| 361 |
+
.hf-spaces-gradient .dispatch-report-card-copy {
|
| 362 |
+
color: #fff;
|
| 363 |
+
}
|
| 364 |
+
.hf-spaces-gradient .dispatch-report-card .dispatch-section-label,
|
| 365 |
+
.hf-spaces-gradient .dispatch-report-card-copy {
|
| 366 |
+
opacity: .78;
|
| 367 |
+
}
|
| 368 |
+
.hf-spaces-gradient .dispatch-report-action {
|
| 369 |
+
border-color: rgb(255 255 255 / .32);
|
| 370 |
+
background: rgb(255 255 255 / .10);
|
| 371 |
+
color: #fff;
|
| 372 |
+
}
|
| 373 |
+
|
| 374 |
+
/* Option C: a compact, structured workspace inspired by dataset browsing. */
|
| 375 |
+
.hf-dataset-studio {
|
| 376 |
+
--dispatch-accent: #2563eb;
|
| 377 |
+
padding: 0;
|
| 378 |
+
background: var(--hf-surface);
|
| 379 |
+
}
|
| 380 |
+
.dark .hf-dataset-studio {
|
| 381 |
+
--dispatch-accent: #60a5fa;
|
| 382 |
+
}
|
| 383 |
+
.hf-dataset-studio .dispatch-sheet {
|
| 384 |
+
width: min(100%, 1040px);
|
| 385 |
+
height: 820px;
|
| 386 |
+
border-radius: 0;
|
| 387 |
+
box-shadow: none;
|
| 388 |
+
}
|
| 389 |
+
.hf-dataset-studio .dispatch-header {
|
| 390 |
+
padding: 20px 28px 16px;
|
| 391 |
+
border-bottom: 1px solid var(--hf-line);
|
| 392 |
+
background: var(--hf-surface);
|
| 393 |
+
}
|
| 394 |
+
.hf-dataset-studio .dispatch-header-separator {
|
| 395 |
+
display: none;
|
| 396 |
+
}
|
| 397 |
+
.hf-dataset-studio .dispatch-query-wrap {
|
| 398 |
+
margin-top: 18px;
|
| 399 |
+
padding: 18px 20px;
|
| 400 |
+
border: 1px solid var(--hf-line);
|
| 401 |
+
border-radius: 10px;
|
| 402 |
+
background: var(--hf-surface-soft);
|
| 403 |
+
}
|
| 404 |
+
.hf-dataset-studio .dispatch-headline {
|
| 405 |
+
font-size: clamp(23px, 3vw, 29px);
|
| 406 |
+
}
|
| 407 |
+
.hf-dataset-studio .dispatch-body {
|
| 408 |
+
padding: 18px 28px;
|
| 409 |
+
background:
|
| 410 |
+
linear-gradient(90deg, var(--hf-surface-soft) 0 4px, transparent 4px);
|
| 411 |
+
}
|
| 412 |
+
.hf-dataset-studio .dispatch-report-grid {
|
| 413 |
+
margin-top: 0;
|
| 414 |
+
}
|
| 415 |
+
.hf-dataset-studio .dispatch-report-card {
|
| 416 |
+
border-radius: 8px;
|
| 417 |
+
background: var(--hf-surface);
|
| 418 |
+
box-shadow: 0 1px 3px rgb(15 23 42 / .05);
|
| 419 |
+
}
|
| 420 |
+
.hf-dataset-studio .dispatch-current-block {
|
| 421 |
+
margin-top: 0;
|
| 422 |
+
padding: 18px 18px 20px;
|
| 423 |
+
border: 1px solid var(--hf-line);
|
| 424 |
+
border-radius: 9px;
|
| 425 |
+
background: var(--hf-surface);
|
| 426 |
+
}
|
| 427 |
+
.hf-dataset-studio .dispatch-current-meta {
|
| 428 |
+
padding-bottom: 10px;
|
| 429 |
+
border-bottom: 1px solid var(--hf-line);
|
| 430 |
+
}
|
| 431 |
+
.hf-dataset-studio .dispatch-current {
|
| 432 |
+
margin-top: 14px;
|
| 433 |
+
}
|
| 434 |
+
.hf-dataset-studio .dispatch-history {
|
| 435 |
+
margin-top: 18px;
|
| 436 |
+
padding: 16px 18px;
|
| 437 |
+
border: 1px solid var(--hf-line);
|
| 438 |
+
border-radius: 9px;
|
| 439 |
+
background: var(--hf-surface);
|
| 440 |
+
}
|
| 441 |
+
.hf-dataset-studio .dispatch-event {
|
| 442 |
+
margin-top: 8px;
|
| 443 |
+
padding: 10px 12px;
|
| 444 |
+
border: 1px solid var(--hf-line);
|
| 445 |
+
border-radius: 7px;
|
| 446 |
+
background: var(--hf-surface-soft);
|
| 447 |
+
opacity: 1;
|
| 448 |
+
}
|
| 449 |
+
.hf-dataset-studio .dispatch-event + .dispatch-event {
|
| 450 |
+
margin-top: 8px;
|
| 451 |
+
}
|
| 452 |
+
.hf-dataset-studio .dispatch-footer {
|
| 453 |
+
background: var(--hf-surface-soft);
|
| 454 |
+
}
|
| 455 |
+
|
| 456 |
+
@media (max-width: 720px) {
|
| 457 |
+
.hf-design {
|
| 458 |
+
min-height: 800px;
|
| 459 |
+
padding: 0;
|
| 460 |
+
}
|
| 461 |
+
.hf-design .dispatch-sheet {
|
| 462 |
+
height: 800px;
|
| 463 |
+
border-radius: 0;
|
| 464 |
+
}
|
| 465 |
+
.hf-design .dispatch-header {
|
| 466 |
+
padding: 18px 18px 16px;
|
| 467 |
+
}
|
| 468 |
+
.hf-design .dispatch-body {
|
| 469 |
+
padding-right: 18px;
|
| 470 |
+
padding-left: 18px;
|
| 471 |
+
}
|
| 472 |
+
.hf-design .dispatch-footer-log,
|
| 473 |
+
.hf-design .dispatch-session {
|
| 474 |
+
padding-right: 18px;
|
| 475 |
+
padding-left: 18px;
|
| 476 |
+
}
|
| 477 |
+
.hf-brand-name {
|
| 478 |
+
font-size: 15px;
|
| 479 |
+
}
|
| 480 |
+
.hf-spaces-gradient .dispatch-current-block,
|
| 481 |
+
.hf-hub-classic .dispatch-current-block {
|
| 482 |
+
padding: 17px;
|
| 483 |
+
}
|
| 484 |
+
.hf-dataset-studio .dispatch-query-wrap {
|
| 485 |
+
padding: 14px;
|
| 486 |
+
}
|
| 487 |
+
}
|
| 488 |
+
"""
|
| 489 |
+
|
| 490 |
+
|
| 491 |
+
def design_css_class(design: HFDesign) -> str:
|
| 492 |
+
return f"dispatch-app hf-design hf-{design}"
|
| 493 |
+
|
| 494 |
+
|
| 495 |
+
def validate_design(design: str) -> HFDesign:
|
| 496 |
+
if design not in HF_DESIGNS:
|
| 497 |
+
choices = ", ".join(HF_DESIGNS)
|
| 498 |
+
raise ValueError(f"Unknown design {design!r}; expected one of: {choices}")
|
| 499 |
+
return design # type: ignore[return-value]
|