diff --git a/CHANGELOG.md b/CHANGELOG.md index ba67ba560..679bb4392 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * `ui.value_box()`, `ui.layout_columns()` and `ui.layout_column_wrap()` now all have `min_height` and `max_height` arguments. These are useful in filling layouts, like `ui.page_fillable()`, `ui.page_sidebar(fillable=True)` or `ui.page_navbar(fillable=True)`. For example, you can use `ui.layout_columns(min_height=300, max_height=500)` to ensure that a set of items (likely arranged in a row of columns) are always between 300 and 500 pixels tall. (#1223) +* Added an error console which displays errors in the browser's UI. This is enabled by default when running applications locally, and can be disabled with `shiny run --no-dev-mode`. It is not enabled for applications that are deployed to a server. (#1060) + ### Bug fixes * On Windows, Shiny Express app files are now read in as UTF-8. (#1203) diff --git a/shiny/_app.py b/shiny/_app.py index f3c6c81d7..fb8dd2f50 100644 --- a/shiny/_app.py +++ b/shiny/_app.py @@ -438,7 +438,7 @@ def _render_page(self, ui: Tag | TagList, lib_prefix: str) -> RenderedHTML: ui_res = copy.copy(ui) # Make sure requirejs, jQuery, and Shiny come before any other dependencies. # (see require_deps() for a comment about why we even include it) - ui_res.insert(0, [require_deps(), jquery_deps(), shiny_deps()]) + ui_res.insert(0, [require_deps(), jquery_deps(), *shiny_deps()]) rendered = HTMLDocument(ui_res).render(lib_prefix=lib_prefix) self._ensure_web_dependencies(rendered["dependencies"]) return rendered @@ -449,7 +449,7 @@ def _render_page_from_file(self, file: Path, lib_prefix: str) -> RenderedHTML: doc = HTMLTextDocument( page_html, - deps=[require_deps(), jquery_deps(), shiny_deps()], + deps=[require_deps(), jquery_deps(), *shiny_deps()], deps_replace_pattern='', ) diff --git a/shiny/_main.py b/shiny/_main.py index 0e384e879..b040f379c 100644 --- a/shiny/_main.py +++ b/shiny/_main.py @@ -143,6 +143,13 @@ def main() -> None: help="Launch app browser after app starts, using the Python webbrowser module.", show_default=True, ) +@click.option( + "--dev-mode/--no-dev-mode", + is_flag=True, + default=True, + help="Dev mode", + show_default=True, +) @no_example() def run( app: str | shiny.App, @@ -159,6 +166,7 @@ def run( app_dir: str, factory: bool, launch_browser: bool, + dev_mode: bool, **kwargs: object, ) -> None: reload_includes_list = reload_includes.split(",") @@ -177,6 +185,7 @@ def run( app_dir=app_dir, factory=factory, launch_browser=launch_browser, + dev_mode=dev_mode, **kwargs, ) @@ -196,6 +205,7 @@ def run_app( app_dir: Optional[str] = ".", factory: bool = False, launch_browser: bool = False, + dev_mode: bool = True, **kwargs: object, ) -> None: """ @@ -276,6 +286,8 @@ def run_app( os.environ["SHINY_HOST"] = host os.environ["SHINY_PORT"] = str(port) + if dev_mode: + os.environ["SHINY_DEV_MODE"] = "1" if isinstance(app, str): # Remove ":app" suffix if present. Normally users would just pass in the diff --git a/shiny/html_dependencies.py b/shiny/html_dependencies.py index a6dc1f628..4c0867932 100644 --- a/shiny/html_dependencies.py +++ b/shiny/html_dependencies.py @@ -1,14 +1,30 @@ +from __future__ import annotations + +import os + from htmltools import HTMLDependency -def shiny_deps() -> HTMLDependency: - return HTMLDependency( - name="shiny", - version="0.0.1", - source={"package": "shiny", "subdir": "www/shared/"}, - script={"src": "shiny.js"}, - stylesheet={"href": "shiny.min.css"}, - ) +def shiny_deps() -> list[HTMLDependency]: + deps = [ + HTMLDependency( + name="shiny", + version="0.0.1", + source={"package": "shiny", "subdir": "www/shared/"}, + script={"src": "shiny.js"}, + stylesheet={"href": "shiny.min.css"}, + ) + ] + if os.getenv("SHINY_DEV_MODE") == "1": + deps.append( + HTMLDependency( + "shiny-devmode", + version="0.0.1", + head="", + ) + ) + + return deps def jquery_deps() -> HTMLDependency: