Skip to content

Commit 45cf98d

Browse files
Merge pull request #60 from MarcSkovMadsen/enhancement/docs-apps
more apps
2 parents 337d179 + 27a739d commit 45cf98d

9 files changed

Lines changed: 251 additions & 14 deletions

File tree

docs/how-to/serve-apps.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
How‑To: Serve HoloViz MCP Panel apps locally for exploration, learning, and validation.
22

3-
### What You Get
3+
## What You Get
44

55
- Search Tool: semantic search over indexed documentation; open results on the source site.
66
- Configuration Viewer: inspect default, user, and merged configuration settings.
77

88
## Online Demo
99

10-
Try the hosted demo: [Hugging Face · holoviz-mcp-ui](https://huggingface.co/spaces/awesome-panel/holoviz-mcp-ui)
10+
Try the hosted demo: [🤗 holoviz-mcp-ui](https://huggingface.co/spaces/awesome-panel/holoviz-mcp-ui)
1111

1212
[![Search Tool](../assets/images/holoviz-mcp-search-tool.png)](https://awesome-panel-holoviz-mcp-ui.hf.space/search)
1313

pixi.lock

Lines changed: 44 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pixi.toml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,18 @@ certifi = "*"
1212
chromadb = "*"
1313
fastmcp = "*"
1414
GitPython = "*"
15-
nbconvert = "*"
15+
hatch-vcs = "*"
16+
hatchling = "*"
1617
hvplot = "*"
18+
nbconvert = "*"
1719
panel = "*"
20+
panel-material-ui = "*"
21+
pip = "*"
1822
pydantic = ">=2.0"
1923
PyYAML = "*"
20-
pip = "*"
21-
hatchling = "*"
22-
hatch-vcs = "*"
2324
setuptools = ">=61"
2425
setuptools-scm = "*"
26+
watchfiles = "*"
2527

2628
[feature.test.dependencies]
2729
mypy = "*"
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
"""Panel app for exploring the HoloViz MCP docs_get_best_practices tool.
2+
3+
Uses panel-material-ui widgets and Page layout.
4+
"""
5+
6+
import panel as pn
7+
import panel_material_ui as pmui
8+
import param
9+
10+
from holoviz_mcp.docs_mcp.data import get_best_practices
11+
from holoviz_mcp.docs_mcp.data import list_best_practices
12+
13+
pn.extension()
14+
15+
ABOUT = """
16+
## Best Practices Tool
17+
18+
This tool provides best practices and guidelines for using HoloViz projects with Large Language Models (LLMs).
19+
20+
### What Are Best Practices?
21+
22+
Best practices are curated guidelines that help LLMs (and developers) write better code using HoloViz libraries. Each best practices document includes:
23+
24+
- **Hello World Examples**: Annotated starter code showing proper usage patterns
25+
- **DO/DON'T Guidelines**: Clear rules for what to do and what to avoid
26+
- **Code Patterns**: Common idioms and recommended approaches
27+
- **LLM-Specific Guidance**: How to structure prompts and responses effectively
28+
29+
### Available Projects
30+
31+
This tool currently provides best practices for:
32+
33+
- **panel**: Core Panel library for building web apps and dashboards
34+
- **panel-material-ui**: Material Design UI components for Panel applications
35+
- **holoviz**: General HoloViz ecosystem guidelines
36+
37+
### How to Use
38+
39+
Select a project in the sidebar to view its best practices.
40+
The content is displayed in Markdown format and includes code examples you can reference when building applications.
41+
42+
### Learn More
43+
44+
For more information about this project, including setup instructions and advanced configuration options,
45+
visit: [HoloViz MCP](https://github.com/MarcSkovMadsen/holoviz-mcp).
46+
"""
47+
48+
49+
class BestPracticesConfiguration(param.Parameterized):
50+
"""
51+
Configuration for the best practices viewer.
52+
53+
Parameters correspond to the project selection for viewing best practices.
54+
"""
55+
56+
project = param.Selector(
57+
default=None,
58+
objects=[],
59+
doc="Select a project to view its best practices",
60+
)
61+
62+
content = param.String(default="", doc="Markdown content of the selected best practices", precedence=-1)
63+
64+
def __init__(self, **params):
65+
"""Initialize the BestPracticesConfiguration with available projects."""
66+
super().__init__(**params)
67+
self._load_projects()
68+
69+
if pn.state.location:
70+
pn.state.location.sync(self, parameters=["project"])
71+
72+
def _load_projects(self):
73+
"""Load available best practices projects."""
74+
try:
75+
projects = list_best_practices()
76+
self.param.project.objects = projects
77+
if projects and self.project is None:
78+
self.project = projects[0] # Default to first project
79+
except Exception as e:
80+
self.param.project.objects = []
81+
self.content = f"**Error loading projects:** {e}"
82+
83+
@param.depends("project", watch=True, on_init=True)
84+
def _update_content(self):
85+
"""Update content when project selection changes."""
86+
if self.project is None or not isinstance(self.project, str):
87+
self.content = "Please select a project to view its best practices."
88+
return
89+
90+
try:
91+
self.content = get_best_practices(str(self.project))
92+
except FileNotFoundError as e:
93+
self.content = f"**Error:** {e}"
94+
except Exception as e:
95+
self.content = f"**Error loading best practices:** {e}"
96+
97+
98+
class BestPracticesViewer(pn.viewable.Viewer):
99+
"""
100+
A Panel Material UI app for viewing HoloViz best practices.
101+
102+
Features:
103+
- Parameter-driven reactivity
104+
- Modern, responsive UI using Panel Material UI
105+
- Integration with HoloViz MCP docs_get_best_practices tool
106+
"""
107+
108+
title = param.String(default="HoloViz MCP Best Practices", doc="Title of the best practices viewer")
109+
config: BestPracticesConfiguration = param.Parameter(doc="Configuration for the best practices viewer") # type: ignore
110+
111+
def __init__(self, **params):
112+
"""Initialize the BestPracticesViewer with default configuration."""
113+
params["config"] = params.get("config", BestPracticesConfiguration())
114+
super().__init__(**params)
115+
116+
def _config_panel(self):
117+
"""Create the configuration panel for the sidebar."""
118+
return pmui.widgets.RadioButtonGroup.from_param(self.config.param.project, sizing_mode="stretch_width", orientation="vertical", button_style="outlined")
119+
120+
def __panel__(self):
121+
"""Create the Panel layout for the best practices viewer."""
122+
with pn.config.set(sizing_mode="stretch_width"):
123+
# About button and dialog
124+
about_button = pmui.IconButton(
125+
label="About",
126+
icon="info",
127+
description="Click to learn about the Best Practices Tool.",
128+
sizing_mode="fixed",
129+
color="light",
130+
margin=(10, 0),
131+
)
132+
about = pmui.Dialog(ABOUT, close_on_click=True, width=0)
133+
about_button.js_on_click(args={"about": about}, code="about.data.open = true")
134+
135+
# GitHub button
136+
github_button = pmui.IconButton(
137+
label="Github",
138+
icon="star",
139+
description="Give HoloViz-MCP a star on GitHub",
140+
sizing_mode="fixed",
141+
color="light",
142+
margin=(10, 0),
143+
href="https://github.com/MarcSkovMadsen/holoviz-mcp",
144+
target="_blank",
145+
)
146+
147+
return pmui.Page(
148+
title=self.title,
149+
site_url="./",
150+
sidebar=[self._config_panel()],
151+
sidebar_width=350,
152+
header=[pn.Row(pn.Spacer(), about_button, github_button, align="end")],
153+
main=[
154+
pmui.Container(
155+
about,
156+
pn.Column(
157+
pn.pane.Markdown(
158+
self.config.param.content,
159+
sizing_mode="stretch_both",
160+
styles={"padding": "20px"},
161+
),
162+
scroll=True,
163+
sizing_mode="stretch_both",
164+
),
165+
width_option="xl",
166+
sizing_mode="stretch_both",
167+
)
168+
],
169+
)
170+
171+
172+
if pn.state.served:
173+
BestPracticesViewer().servable()

src/holoviz_mcp/apps/search.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -291,21 +291,23 @@ def __panel__(self):
291291
about_button.js_on_click(args={"about": about}, code="about.data.open = true")
292292

293293
github_button = pmui.IconButton(
294-
label="Github", icon="star", description="Give HoloViz-MCP a star on GitHub", sizing_mode="fixed", color="light", margin=(10, 0)
294+
label="Github",
295+
icon="star",
296+
description="Give HoloViz-MCP a star on GitHub",
297+
sizing_mode="fixed",
298+
color="light",
299+
margin=(10, 0),
300+
href="https://github.com/MarcSkovMadsen/holoviz-mcp",
301+
target="_blank",
295302
)
296-
href = "https://github.com/MarcSkovMadsen/holoviz-mcp"
297-
js_code_to_open_holoviz_mcp = f"window.open('{href}', '_blank')"
298-
github_button.js_on_click(code=js_code_to_open_holoviz_mcp)
299303

300304
return pmui.Page(
301305
title=self.title,
302306
site_url="./",
303307
sidebar=[self._config, menu],
304308
sidebar_width=400,
305-
header=[pn.Row(about, about_button, github_button, align="end")],
306-
main=[pmui.Container(DocumentView(document=menu.param.value), width_option="xl", sizing_mode="stretch_both")],
307-
# logo="https://holoviz.org/_static/holoviz-logo-unstacked.svg",
308-
# stylesheets=[".logo {background: white;border-radius: 5px;margin: 15px 15px 5px 10px;padding:7px}"],
309+
header=[pn.Row(pn.Spacer(), about_button, github_button, align="end")],
310+
main=[pmui.Container(about, DocumentView(document=menu.param.value), width_option="xl", sizing_mode="stretch_both")],
309311
)
310312

311313

src/holoviz_mcp/config/config.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,13 @@ docs:
138138
- "doc/reference/**/*.md"
139139
- "doc/reference/**/*.ipynb"
140140
- "doc/reference/**/*.rst"
141+
holoviz-mcp:
142+
url: "https://github.com/MarcSkovMadsen/holoviz-mcp.git"
143+
url_transform: "plotly"
144+
folders:
145+
docs:
146+
url_path: ""
147+
base_url: "https://marcskovmadsen.github.io/holoviz-mcp"
141148

142149
index_patterns:
143150
- "**/*.md"

src/holoviz_mcp/config/resources/best-practices/panel.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ elif pn.state.served:
9999
HelloWorld.create_app().servable()
100100
```
101101

102+
For testing with pytest:
103+
102104
```python
103105
# DO put tests in a separate test file.
104106
# DO always test that the reactivity works as expected

src/holoviz_mcp/docs_mcp/data.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,8 @@ def convert_path_to_url(path: Path, remove_first_part: bool = True, url_transfor
195195
path_obj = Path(url_path)
196196
if url_transform == "plotly":
197197
url_path = str(path_obj.with_suffix(suffix="")) + "/"
198+
if url_path.endswith("index/"):
199+
url_path = url_path[: -len("index/")] + "/"
198200
else:
199201
url_path = str(path_obj.with_suffix(suffix=".html"))
200202

tests/docs_mcp/test_data.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ def test_convert_path_to_url_plotly():
3434
assert url == "doc/python/3d-axes/"
3535

3636

37+
def test_convert_index_path_to_url_plotly():
38+
url = convert_path_to_url(Path("docs/index.md"), url_transform="plotly")
39+
assert url == "/"
40+
41+
3742
def test_convert_path_to_url_datashader():
3843
url = convert_path_to_url(Path("/examples/user_guide/10_Performance.ipynb"), url_transform="datashader")
3944
assert url == "examples/user_guide/Performance.html"

0 commit comments

Comments
 (0)