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: