Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fall back to rendering cell outputs if notebook does not define servable #5802

Merged
merged 27 commits into from
Jan 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
4bfa3af
Fall back to rendering cell outputs if notebook does not define servable
philippjfr Nov 2, 2023
da6b007
Small adjustments
philippjfr Nov 6, 2023
979133f
Switch to Muuri based grid
philippjfr Nov 7, 2023
19860dc
Small improvements
philippjfr Nov 8, 2023
844b9fd
Handle comments and multiple return values
philippjfr Nov 11, 2023
14c0130
Improve reset button
philippjfr Nov 11, 2023
65bf422
Unify template inheritance
philippjfr Nov 14, 2023
f06bcea
Improve notebook state handling
philippjfr Nov 14, 2023
f1aa16e
Cleanup
philippjfr Nov 14, 2023
50e3d02
Allow toggling editable
philippjfr Nov 14, 2023
4baf9e1
Make sizing stable
philippjfr Nov 14, 2023
094fc8b
Smaller fixes
philippjfr Nov 14, 2023
6721d03
Add some tests
philippjfr Nov 14, 2023
2a3271f
Render template editor separately
philippjfr Nov 14, 2023
f75aa9a
Fix golden template
philippjfr Nov 14, 2023
f7c11e1
Refactoring and tests
philippjfr Nov 15, 2023
fa20566
Refactor and test reset
philippjfr Nov 15, 2023
815eb3c
Improvements to resizing behavior
philippjfr Nov 15, 2023
c5b0bd9
Robustify layout calculations
philippjfr Nov 15, 2023
bb420cd
Various improvements
philippjfr Nov 16, 2023
51d6b88
Ensure multi-line expressions are parsed correctly
philippjfr Nov 18, 2023
90064cb
Fixes for layout persistence
philippjfr Nov 18, 2023
e5a88b2
Add separate persistance layer and /panel-preview/layout endpoint
philippjfr Jan 15, 2024
1d78c10
Add docs
philippjfr Jan 15, 2024
c92336e
Update tests
philippjfr Jan 15, 2024
2d35284
Add EditableTemplate docs
philippjfr Jan 15, 2024
dce03a4
Fix bootstrap template
philippjfr Jan 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added doc/_static/images/builder_delete.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/_static/images/builder_initial.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/_static/images/builder_rearrange.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/_static/images/builder_resize.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 9 additions & 1 deletion doc/how_to/notebook/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ How to display output in Jupyter and non-Jupyter based notebook environments.
How to use the Preview functionality in JupyterLab to rapidly develop applications.
:::

:::{grid-item-card} {octicon}`grabber;2.5em;sd-mr-1 sd-animate-grow50` Publish a Notebook as a Dashboard using the Layout Builder
:link: layout_builder
:link-type: doc

How to use the layout builder in JupyterLab to build a dashboard using a drag-and-drop interface.
:::

:::{grid-item-card} {octicon}`rows;2.5em;sd-mr-1 sd-animate-grow50` Develop in other notebook environments
:link: other_nb
:link-type: doc
Expand All @@ -34,6 +41,7 @@ How to develop apps in Google Colab, VSCode notebook, nteract, and other noteboo
:maxdepth: 2

notebook
jupyterlab
jupyterlabpreview
layout_builder
other_nb
```
2 changes: 1 addition & 1 deletion doc/how_to/notebook/jupyterlabpreview.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ This guide addresses how to use the *Preview* functionality in JupyterLab to rap

With this functionality you can preview the app you are building in a JupyterLab tab right next to your code. This is a traditional setup for web developers, who like to quickly visualize the effects of their code changes.

The Preview in JupyterLab has two different modes depending on whether you marked any component as `.servable()` or not. In this guide we will focus on the explicit mode where you use code to declare which components to publish and how they should be laid out. If you are simply publishing a notebook with or without importing Panel, you will see the [layout builder](./layout_builder.md).

To have your app appear in the *Preview*, you need to mark the objects you want to display with `.servable()`. This is identical to how you would mark objects that you want to serve with `panel serve ...`.

You can enable the *Preview* by clicking on Panel's logo in the menu bar of your notebook. Once clicked, you should see a new tab being opened next to your notebook tab, and after some moment your app will be rendered in this tab.

![JupyterLab Preview](../../_static/images/jlabpreview.png)


The *Preview* offers two update modes that are configurable in the preview tab:

- manual: click on the *Reload* button ({fas}`rotate-right`) to re-run and re-render your app.
Expand Down
Binary file added examples/assets/EditableTemplate.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions examples/reference/templates/Bootstrap.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"The `BootstrapTemplate` is a simple extension of the basic template which is built on top of [Bootstrap v4](https://getbootstrap.com/docs/4.0/getting-started/introduction/) and uses the `Bootstrap` design system. It is a list-like variant where the `main` area acts like a list-like container, unlike the grid-like templates such as `React` and `FastGridTemplate`.\n",
"\n",
"## Basic Templates\n",
"\n",
"For a large variety of use cases we do not need complete control over the exact layout of each individual component on the page, as could be achieved with a [custom template](../../user_guide/Templates.ipynb), we just want to achieve a more polished look and feel. For these cases Panel ships with a number of default templates, which are defined by declaring four main content areas on the page, which can be populated as desired:\n",
"\n",
"* **`header`**: The header area of the HTML page\n",
Expand Down
141 changes: 141 additions & 0 deletions examples/reference/templates/EditableTemplate.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "aca86665-8672-421a-b438-16270d75200d",
"metadata": {},
"source": [
"The `EditableTemplate` builds on the [`VanillaTemplate`](./Vanilla.ipynb) extending it with functionality for editing the layout of the components on the page. It is a list-like variant where the `main` area acts like a list-like container, unlike the grid-like templates such as `React` and `FastGridTemplate`. Specifically it allows for the following actions:\n",
"\n",
"- **Hide** outputs\n",
"- **Resize** outputs\n",
"- **Rearrange** outputs\n",
"\n",
"In this way a dashboard can be built using a drag and drop approach. The resulting layout can then be persisted into local storage. To open the dashboard in `editable` mode you can either set the `editable=True` option or append `?editable=true` as a URL parameter.\n",
"\n",
"The `EditableTemplate` is used by the Panel layout builder functionality which makes it trivial to go from a notebook to a deployed dashboard by using the drag-and-drop interface to lay out components on the page.\n",
"\n",
"## Basic Templates\n",
"\n",
"For a large variety of use cases we do not need complete control over the exact layout of each individual component on the page, as could be achieved with a [custom template](../../user_guide/Templates.ipynb), we just want to achieve a more polished look and feel. For these cases Panel ships with a number of default templates, which are defined by declaring four main content areas on the page, which can be populated as desired:\n",
"\n",
"* **`header`**: The header area of the HTML page\n",
"* **`sidebar`**: A collapsible sidebar\n",
"* **`main`**: The main area of the application\n",
"* **`modal`**: A modal area which can be opened and closed from Python\n",
"\n",
"These four areas behave very similarly to other Panel layout components and have list-like semantics. This means we can easily append new components into these areas. Unlike other layout components however, the contents of the areas is fixed once rendered. If you need a dynamic layout you should therefore insert a regular Panel layout component (e.g. a `Column` or `Row`) and modify it in place once added to one of the content areas. \n",
"\n",
"Templates can allow for us to quickly and easily create web apps for displaying our data. Panel comes with a default Template, and includes multiple Templates that extend the default which add some customization for a better display.\n",
"\n",
"#### Parameters:\n",
"\n",
"In addition to the four different areas we can populate the default templates also provide a few additional parameters:\n",
"\n",
"* **`busy_indicator`** (BooleanIndicator): Visual indicator of application busy state.\n",
"* **`collapsed_sidebar`** (str, `default=False`): Whether the sidebar (if present) is initially collapsed.\n",
"* **`editable`** (bool, `default=True`): Whether the layout of the template should be editable.\n",
"* **`header_background`** (str): Optional header background color override.\n",
"* **`header_color`** (str): Optional header text color override.\n",
"* **`logo`** (str): URI of logo to add to the header (if local file, logo is base64 encoded as URI).\n",
"* **`site`** (str): Name of the site. Will be shown in the header. Default is '', i.e. not shown.\n",
"* **`site_url`** (str): Url of the site and logo. Default is \"/\".\n",
"* **`title`** (str): A title to show in the header.\n",
"* **`theme`** (Theme): A Theme class (available in `panel.template.theme`)\n",
"* **`sidebar_width`** (int): The width of the sidebar in pixels. Default is 330.\n",
"\n",
"________\n",
"\n",
"The `EditableTemplate` can be populated like any other list-like template, by extending the `main` layout:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "00c8054f-e160-42bd-a86d-0d1d13073f11",
"metadata": {},
"outputs": [],
"source": [
"import hvplot.pandas\n",
"import numpy as np\n",
"import panel as pn\n",
"import pandas as pd\n",
"\n",
"xs = np.linspace(0, np.pi)\n",
"\n",
"freq = pn.widgets.FloatSlider(name=\"Frequency\", start=0, end=10, value=2)\n",
"phase = pn.widgets.FloatSlider(name=\"Phase\", start=0, end=np.pi)\n",
"\n",
"def sine(freq, phase):\n",
" return pd.DataFrame(dict(y=np.sin(xs*freq+phase)), index=xs)\n",
"\n",
"def cosine(freq, phase):\n",
" return pd.DataFrame(dict(y=np.cos(xs*freq+phase)), index=xs)\n",
"\n",
"dfi_sine = hvplot.bind(sine, freq, phase).interactive()\n",
"dfi_cosine = hvplot.bind(cosine, freq, phase).interactive()\n",
"\n",
"plot_opts = dict(responsive=True, min_height=400)\n",
"\n",
"# Instantiate the template with widgets displayed in the sidebar\n",
"template = pn.template.EditableTemplate(\n",
" editable=True,\n",
" title='EditableTemplate',\n",
" sidebar=[freq, phase],\n",
")\n",
"# Append a layout to the main area, to demonstrate the list-like API\n",
"template.main.extend([\n",
" dfi_sine.hvplot(title='Sine Plot', **plot_opts).output(),\n",
" dfi_cosine.hvplot(title='Cosine Plot', **plot_opts).output(),\n",
"])\n",
"\n",
"template.servable();"
]
},
{
"cell_type": "markdown",
"id": "992ec479-c8a6-4555-a2c4-8d2eee1563f3",
"metadata": {},
"source": [
"The resulting default layout will simply initialize each component on top of one another:"
]
},
{
"cell_type": "markdown",
"id": "d8bfb9b3-a446-4b34-a2aa-9208af6d6d47",
"metadata": {},
"source": [
"<img src=\"../../assets/EditableTemplate.png\" style=\"margin-left: auto; margin-right: auto; display: block;\"></img>"
]
},
{
"cell_type": "markdown",
"id": "ecd35ffd-180f-4685-878f-a04f76bb645e",
"metadata": {},
"source": [
"If the template is in editable mode we can delete, resize, and rearrange the outputs on the page using a drag and drop interface. \n",
"\n",
"- Delete: Hover on the top-right to reveal the delete icon\n",
"\n",
"<img src=\"data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjMDAwMDAwIiBoZWlnaHQ9IjI0cHgiIHdpZHRoPSIyNHB4IiB2ZXJzaW9uPSIxLjEiIGlkPSJDYXBhXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIA0KCSB2aWV3Qm94PSIwIDAgMjcuOTY1IDI3Ljk2NSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8Zz4NCgk8ZyBpZD0iYzE0Ml94Ij4NCgkJPHBhdGggZD0iTTEzLjk4LDBDNi4yNTksMCwwLDYuMjYxLDAsMTMuOTgzYzAsNy43MjEsNi4yNTksMTMuOTgyLDEzLjk4LDEzLjk4MmM3LjcyNSwwLDEzLjk4NS02LjI2MiwxMy45ODUtMTMuOTgyDQoJCQlDMjcuOTY1LDYuMjYxLDIxLjcwNSwwLDEzLjk4LDB6IE0xOS45OTIsMTcuNzY5bC0yLjIyNywyLjIyNGMwLDAtMy41MjMtMy43OC0zLjc4Ni0zLjc4Yy0wLjI1OSwwLTMuNzgzLDMuNzgtMy43ODMsMy43OA0KCQkJbC0yLjIyOC0yLjIyNGMwLDAsMy43ODQtMy40NzIsMy43ODQtMy43ODFjMC0wLjMxNC0zLjc4NC0zLjc4Ny0zLjc4NC0zLjc4N2wyLjIyOC0yLjIyOWMwLDAsMy41NTMsMy43ODIsMy43ODMsMy43ODINCgkJCWMwLjIzMiwwLDMuNzg2LTMuNzgyLDMuNzg2LTMuNzgybDIuMjI3LDIuMjI5YzAsMC0zLjc4NSwzLjUyMy0zLjc4NSwzLjc4N0MxNi4yMDcsMTQuMjM5LDE5Ljk5MiwxNy43NjksMTkuOTkyLDE3Ljc2OXoiLz4NCgk8L2c+DQoJPGcgaWQ9IkNhcGFfMV8xMDRfIj4NCgk8L2c+DQo8L2c+DQo8L3N2Zz4NCg==\"></img>\n",
"\n",
"- Resize: Hover on the bottom-right to reveal the resize handle\n",
"\n",
"<img src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjRweCIgaGVpZ2h0PSIyNHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+DQo8cGF0aCBkPSJNMjEgMTVMMTUgMjFNMjEgOEw4IDIxIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+DQo8L3N2Zz4NCg==\"></img>\n",
"\n",
"\n",
"- Rearrange: Hover on the top-right to reveal the drag icon\n",
"\n",
"<img src=\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0Ij48cGF0aCBmaWxsPSJub25lIiBkPSJNMCAwaDI0djI0SDB6Ii8+PHBhdGggZD0iTTE2IDEzbDYuOTY0IDQuMDYyLTIuOTczLjg1IDIuMTI1IDMuNjgxLTEuNzMyIDEtMi4xMjUtMy42OC0yLjIyMyAyLjE1TDE2IDEzem0tMi03aDJ2Mmg1YTEgMSAwIDAgMSAxIDF2NGgtMnYtM0gxMHYxMGg0djJIOWExIDEgMCAwIDEtMS0xdi01SDZ2LTJoMlY5YTEgMSAwIDAgMSAxLTFoNVY2ek00IDE0djJIMnYtMmgyem0wLTR2Mkgydi0yaDJ6bTAtNHYySDJWNmgyem0wLTR2MkgyVjJoMnptNCAwdjJINlYyaDJ6bTQgMHYyaC0yVjJoMnptNCAwdjJoLTJWMmgyeiIvPjwvc3ZnPg==\"></img>\n"
]
}
],
"metadata": {
"language_info": {
"name": "python",
"pygments_lexer": "ipython3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
4 changes: 4 additions & 0 deletions examples/reference/templates/FastGridTemplate.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"The `FastGridTemplate` is a simple extension of the basic template that uses the [Fast design library](https://www.fast.design/) and applies the `panel.theme.Fast` design system by default. Just like the [`ReactTemplate`](./React.ipynb) it is built on the [react-grid-layout](https://react-grid-layout.github.io/react-grid-layout/examples/0-showcase.html) to provide a drag-and-drop interface for resizing and rearranging components on a grid. It is a grid-like variant where the `main` area acts like a grid-like container, unlike the list-like templates such as `VanillaTemplate` and `MaterialTemplate`.\n",
"\n",
"## Basic Templates\n",
"\n",
"For a large variety of use cases we do not need complete control over the exact layout of each individual component on the page, as could be achieved with a [custom template](../../user_guide/Templates.ipynb), we just want to achieve a more polished look and feel. For these cases Panel ships with a number of default templates, which are defined by declaring four main content areas on the page, which can be populated as desired:\n",
"\n",
"* **`header`**: The header area of the HTML page\n",
Expand Down
4 changes: 4 additions & 0 deletions examples/reference/templates/FastListTemplate.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"The `FastGridTemplate` is a simple extension of the basic template that uses the [Fast design library](https://www.fast.design/) and applies the `panel.theme.Fast` design system by default. It is a list-like variant where the `main` area acts like a list-like container, unlike the grid-like templates such as `ReactTemplate` and `FastGridTemplate`.\n",
"\n",
"## Basic Templates\n",
"\n",
"For a large variety of use cases we do not need complete control over the exact layout of each individual component on the page, as could be achieved with a [custom template](../../user_guide/Templates.ipynb), we just want to achieve a more polished look and feel. For these cases Panel ships with a number of default templates, which are defined by declaring four main content areas on the page, which can be populated as desired:\n",
"\n",
"* **`header`**: The header area of the HTML page\n",
Expand Down
4 changes: 4 additions & 0 deletions examples/reference/templates/Material.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"The `MaterialTemplate` is a simple extension of the basic template which is built on top of [Material Components for the web framework](https://material.io/develop/web/) and uses the `panel.theme.Material` design system. It is a list-like variant where the `main` area acts like a list-like container, unlike the grid-like templates such as `React` and `FastGridTemplate`.\n",
"\n",
"## Basic Templates\n",
"\n",
"For a large variety of use cases we do not need complete control over the exact layout of each individual component on the page, as could be achieved with a [custom template](../../user_guide/Templates.ipynb), we just want to achieve a more polished look and feel. For these cases Panel ships with a number of default templates, which are defined by declaring four main content areas on the page, which can be populated as desired:\n",
"\n",
"* **`header`**: The header area of the HTML page\n",
Expand Down
4 changes: 4 additions & 0 deletions examples/reference/templates/React.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"The `ReactTemplate` is a simple extension of the basic template that uses the [react-grid-layout](https://react-grid-layout.github.io/react-grid-layout/examples/0-showcase.html) to provide a drag-and-drop interface for resizing and rearranging components on a grid. It is a grid-like variant where the `main` area acts like a grid-like container, unlike the list-like templates such as `VanillaTemplate` and `MaterialTemplate`.\n",
"\n",
"## Basic Templates\n",
"\n",
"For a large variety of use cases we do not need complete control over the exact layout of each individual component on the page, as could be achieved with a [custom template](../../user_guide/Templates.ipynb), we just want to achieve a more polished look and feel. For these cases Panel ships with a number of default templates, which are defined by declaring four main content areas on the page, which can be populated as desired:\n",
"\n",
"* **`header`**: The header area of the HTML page\n",
Expand Down
4 changes: 4 additions & 0 deletions examples/reference/templates/Slides.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"The `SlidesTemplate` is a simple extension of the basic template that uses the [reveal.js](https://revealjs.com/) presentation framework to present the published outputs as a set of slides. It is a list-like variant where the `main` area acts like a list-like container but unlike other list-like templates the individual items are published as separate slides (unless marked as a fragment).\n",
"\n",
"## Basic Templates\n",
"\n",
"For a large variety of use cases we do not need complete control over the exact layout of each individual component on the page, as could be achieved with a [custom template](../../user_guide/Templates.ipynb), we just want to achieve a more polished look and feel. For these cases Panel ships with a number of default templates, which are defined by declaring four main content areas on the page, which can be populated as desired:\n",
"\n",
"* **`header`**: The header area of the HTML page\n",
Expand Down
6 changes: 5 additions & 1 deletion examples/reference/templates/Vanilla.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"The `VanillaTemplate` is a simple extension of the basic template that uses vanilla CSS styling and applies the `panel.theme.Native` design system by default. It is a list-like variant where the `main` area acts like a list-like container, unlike the grid-like templates such as `React` and `FastGridTemplate`.\n",
"\n",
"## Basic Templates\n",
"\n",
"For a large variety of use cases we do not need complete control over the exact layout of each individual component on the page, as could be achieved with a [custom template](../../user_guide/Templates.ipynb), we just want to achieve a more polished look and feel. For these cases Panel ships with a number of default templates, which are defined by declaring four main content areas on the page, which can be populated as desired:\n",
"\n",
"* **`header`**: The header area of the HTML page\n",
Expand Down Expand Up @@ -37,7 +41,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"In this case we are using the `VanillaTemplate`, built without using any extra css frameworks. Here is an example of how you can set up a display using this template:"
"In this case we are using the `VanillaTemplate`, built without using any extra CSS frameworks. Here is an example of how you can set up a display using this template:"
]
},
{
Expand Down
2 changes: 1 addition & 1 deletion panel/io/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@

from .. import __version__, config
from ..util import base_version, escape
from .handlers import build_single_handler_application
from .loading import LOADING_INDICATOR_CSS_CLASS
from .markdown import build_single_handler_application
from .mime_render import find_requirements
from .resources import (
BASE_TEMPLATE, CDN_DIST, CDN_ROOT, DIST_DIR, INDEX_TEMPLATE, Resources,
Expand Down
Loading