|
| 1 | +"""An app to retrieve hvPlot docstring documentation for plot types. |
| 2 | +
|
| 3 | +An interactive version of the holoviz_mcp.hvplot_mcp.server.get_docstring tool. |
| 4 | +""" |
| 5 | + |
| 6 | +import panel as pn |
| 7 | +import panel_material_ui as pmui |
| 8 | +import param |
| 9 | + |
| 10 | +from holoviz_mcp.client import call_tool |
| 11 | + |
| 12 | +pn.extension() |
| 13 | + |
| 14 | +pn.pane.Markdown.disable_anchors = True |
| 15 | + |
| 16 | +ABOUT = """ |
| 17 | +## hvPlot Get Docstring Tool |
| 18 | +
|
| 19 | +Get comprehensive documentation for any hvPlot plot type, equivalent to calling `hvplot.help(plot_type)` in Python. |
| 20 | +
|
| 21 | +### How to Use This Tool |
| 22 | +
|
| 23 | +1. **Plot Type**: Select the type of plot you want documentation for (e.g., "line", "scatter", "bar") |
| 24 | +2. **Configuration Options**: Adjust what content to include: |
| 25 | + - **Include Docstring**: Main documentation and examples |
| 26 | + - **Include Generic Options**: Options shared by all plot types |
| 27 | + - **Style Backend**: Get options specific to matplotlib, bokeh, plotly, or auto-detect |
| 28 | +
|
| 29 | +### Use Cases |
| 30 | +
|
| 31 | +**Learn hvPlot plotting**: |
| 32 | +
|
| 33 | +- Understand available parameters for customization |
| 34 | +- Explore examples and usage patterns |
| 35 | +- Compare options across different backends |
| 36 | +- Get quick reference while coding |
| 37 | +
|
| 38 | +**Plot Development**: |
| 39 | +
|
| 40 | +- Check parameter names and types before coding |
| 41 | +- Discover advanced customization options |
| 42 | +- Learn best practices from examples |
| 43 | +- Understand backend differences |
| 44 | +
|
| 45 | +### Configuration Options |
| 46 | +
|
| 47 | +- **Plot Type**: Choose from all available hvPlot plot types |
| 48 | +- **Include Docstring**: Toggle main documentation (default: True) |
| 49 | +- **Include Generic Options**: Toggle shared plotting options (default: True) |
| 50 | +- **Style Backend**: Select specific backend or auto-detect (default: auto) |
| 51 | +
|
| 52 | +### Learn More |
| 53 | +
|
| 54 | +For more information visit: [hvPlot Documentation](https://hvplot.holoviz.org/) and [HoloViz MCP](https://marcskovmadsen.github.io/holoviz-mcp/). |
| 55 | +""" |
| 56 | + |
| 57 | + |
| 58 | +class GetDocstringConfiguration(param.Parameterized): |
| 59 | + """Configuration for hvPlot get_docstring tool.""" |
| 60 | + |
| 61 | + plot_type = param.Selector(default="line", objects=["line"], label="Plot Type") |
| 62 | + docstring = param.Boolean(default=True, label="Include Docstring") |
| 63 | + generic = param.Boolean(default=True, label="Include Generic Options") |
| 64 | + style = param.Selector(default=True, objects=[True, "matplotlib", "bokeh", "plotly"], label="Style Backend") |
| 65 | + |
| 66 | + result = param.String(default="", doc="Docstring result") |
| 67 | + loading = param.Boolean(default=False, doc="Loading state") |
| 68 | + error_message = param.String(default="", doc="Error message if request fails") |
| 69 | + |
| 70 | + def __init__(self, **params): |
| 71 | + super().__init__(**params) |
| 72 | + if pn.state.location: |
| 73 | + pn.state.location.sync(self, ["plot_type", "docstring", "generic", "style"]) |
| 74 | + # Initialize with available plot types |
| 75 | + pn.state.execute(self._update_plot_types) |
| 76 | + # Load default docstring on startup |
| 77 | + pn.state.execute(self._update_result) |
| 78 | + |
| 79 | + async def _update_plot_types(self): |
| 80 | + """Update the available plot types.""" |
| 81 | + try: |
| 82 | + result = await call_tool("hvplot_list_plot_types", {}) |
| 83 | + plot_types = sorted(result.data) |
| 84 | + self.param.plot_type.objects = plot_types |
| 85 | + except Exception as e: |
| 86 | + self.error_message = f"Failed to load plot types: {str(e)}" |
| 87 | + |
| 88 | + @param.depends("plot_type", "docstring", "generic", "style", watch=True) |
| 89 | + async def _update_result(self): |
| 90 | + """Execute tool and update result.""" |
| 91 | + self.loading = True |
| 92 | + self.error_message = "" |
| 93 | + self.result = "" |
| 94 | + |
| 95 | + params = {"plot_type": self.plot_type, "docstring": self.docstring, "generic": self.generic, "style": self.style} |
| 96 | + |
| 97 | + try: |
| 98 | + result = await call_tool("hvplot_get_docstring", params) |
| 99 | + if result and hasattr(result, "structured_content"): |
| 100 | + if result.data: |
| 101 | + self.result = result.data.replace("``", "`") |
| 102 | + else: |
| 103 | + self.error_message = "No docstring returned for the specified plot type" |
| 104 | + else: |
| 105 | + self.error_message = "Request returned no data" |
| 106 | + |
| 107 | + except Exception as e: |
| 108 | + error_str = str(e) |
| 109 | + self.error_message = f"Request failed: {error_str}" |
| 110 | + finally: |
| 111 | + self.loading = False |
| 112 | + |
| 113 | + |
| 114 | +class DocstringViewer(pn.viewable.Viewer): |
| 115 | + """Viewer for displaying hvPlot docstring.""" |
| 116 | + |
| 117 | + result = param.String(default="", allow_refs=True, doc="Docstring content") |
| 118 | + |
| 119 | + def __init__(self, **params): |
| 120 | + super().__init__(**params) |
| 121 | + |
| 122 | + raw_view = pn.pane.Str( |
| 123 | + self.param.result, |
| 124 | + name="Raw Response", |
| 125 | + sizing_mode="stretch_width", |
| 126 | + styles={"font-family": "monospace", "white-space": "pre-wrap"}, |
| 127 | + ) |
| 128 | + |
| 129 | + self._layout = raw_view |
| 130 | + |
| 131 | + def __panel__(self): |
| 132 | + """Return the Panel layout.""" |
| 133 | + return self._layout |
| 134 | + |
| 135 | + |
| 136 | +class HvplotGetDocstringApp(pn.viewable.Viewer): |
| 137 | + """Main application for retrieving hvPlot docstrings.""" |
| 138 | + |
| 139 | + title = param.String(default="HoloViz MCP - hvplot_get_docstring Tool Demo") |
| 140 | + |
| 141 | + def __init__(self, **params): |
| 142 | + super().__init__(**params) |
| 143 | + |
| 144 | + # Create configuration and viewer |
| 145 | + self._config = GetDocstringConfiguration() |
| 146 | + self._docstring_viewer = DocstringViewer(result=self._config.param.result) |
| 147 | + |
| 148 | + with pn.config.set(sizing_mode="stretch_width"): |
| 149 | + self._plot_type_select = pmui.Select.from_param( |
| 150 | + self._config.param.plot_type, |
| 151 | + label="Plot Type", |
| 152 | + variant="outlined", |
| 153 | + sx={"width": "100%"}, |
| 154 | + ) |
| 155 | + |
| 156 | + self._docstring_switch = pmui.Switch.from_param( |
| 157 | + self._config.param.docstring, |
| 158 | + label="Include Docstring", |
| 159 | + ) |
| 160 | + |
| 161 | + self._generic_switch = pmui.Switch.from_param( |
| 162 | + self._config.param.generic, |
| 163 | + label="Include Generic Options", |
| 164 | + ) |
| 165 | + |
| 166 | + self._style_select = pmui.Select.from_param( |
| 167 | + self._config.param.style, |
| 168 | + label="Style Backend", |
| 169 | + variant="outlined", |
| 170 | + sx={"width": "100%"}, |
| 171 | + ) |
| 172 | + |
| 173 | + # Status indicators |
| 174 | + self._status_pane = pn.pane.Markdown(self._status_text, sizing_mode="stretch_width") |
| 175 | + self._error_pane = pmui.Alert( |
| 176 | + self._error_text, |
| 177 | + alert_type="error", |
| 178 | + visible=pn.rx(bool)(self._config.param.error_message), |
| 179 | + sizing_mode="stretch_width", |
| 180 | + ) |
| 181 | + |
| 182 | + # Quick access buttons for common plot types |
| 183 | + # Create static layout structure |
| 184 | + self._sidebar = pn.Column( |
| 185 | + pmui.Typography("## Documentation Lookup", variant="h6"), |
| 186 | + self._plot_type_select, |
| 187 | + pmui.Typography("### Options", variant="subtitle2", margin=(15, 0, 5, 0)), |
| 188 | + self._docstring_switch, |
| 189 | + self._generic_switch, |
| 190 | + self._style_select, |
| 191 | + self._error_pane, |
| 192 | + self._status_pane, |
| 193 | + sizing_mode="stretch_width", |
| 194 | + ) |
| 195 | + |
| 196 | + self._main = pmui.Container(self._docstring_viewer, width_option="xl", margin=10) |
| 197 | + |
| 198 | + @param.depends("_config.loading", "_config.result") |
| 199 | + def _status_text(self): |
| 200 | + """Generate status message.""" |
| 201 | + if self._config.loading: |
| 202 | + return "_Loading docstring..._" |
| 203 | + elif self._config.result.strip(): |
| 204 | + char_count = len(self._config.result) |
| 205 | + plot_type = self._config.plot_type |
| 206 | + return f"_Successfully loaded **{char_count} characters** of documentation for **{plot_type}** plot_" |
| 207 | + return "" |
| 208 | + |
| 209 | + @param.depends("_config.error_message") |
| 210 | + def _error_text(self): |
| 211 | + """Get error message.""" |
| 212 | + return self._config.error_message |
| 213 | + |
| 214 | + def __panel__(self): |
| 215 | + """Return the main page layout.""" |
| 216 | + with pn.config.set(sizing_mode="stretch_width"): |
| 217 | + # About button and dialog |
| 218 | + about_button = pmui.IconButton( |
| 219 | + label="About", |
| 220 | + icon="info", |
| 221 | + description="Click to learn about the hvPlot Get Docstring Tool.", |
| 222 | + sizing_mode="fixed", |
| 223 | + width=40, |
| 224 | + height=40, |
| 225 | + color="light", |
| 226 | + margin=(10, 0), |
| 227 | + ) |
| 228 | + about = pmui.Dialog(ABOUT, close_on_click=True, width=0) |
| 229 | + about_button.js_on_click(args={"about": about}, code="about.data.open = true") |
| 230 | + |
| 231 | + # GitHub button |
| 232 | + github_button = pmui.IconButton( |
| 233 | + label="Github", |
| 234 | + icon="star", |
| 235 | + description="Give HoloViz-MCP a star on GitHub", |
| 236 | + sizing_mode="fixed", |
| 237 | + width=40, |
| 238 | + height=40, |
| 239 | + color="light", |
| 240 | + margin=(10, 0), |
| 241 | + href="https://github.com/MarcSkovMadsen/holoviz-mcp", |
| 242 | + target="_blank", |
| 243 | + ) |
| 244 | + |
| 245 | + return pmui.Page( |
| 246 | + title=self.title, |
| 247 | + site_url="./", |
| 248 | + sidebar=[self._sidebar], |
| 249 | + header=[pn.Row(pn.Spacer(), about_button, github_button, align="end")], |
| 250 | + main=[about, self._main], |
| 251 | + ) |
| 252 | + |
| 253 | + |
| 254 | +if pn.state.served: |
| 255 | + HvplotGetDocstringApp().servable() |
0 commit comments