7
7
import types
8
8
from importlib .machinery import ModuleSpec
9
9
from pathlib import Path
10
- from typing import Mapping , Sequence , cast
10
+ from typing import Literal , Mapping , Sequence , cast
11
11
12
12
from htmltools import Tag , TagList
13
+ from starlette .requests import Request
13
14
14
15
from .._app import App
15
16
from .._docstring import no_example
16
17
from .._typing_extensions import NotRequired , TypedDict
17
18
from .._utils import import_module_from_path
19
+ from ..bookmark ._types import BookmarkStore
18
20
from ..session import Inputs , Outputs , Session , get_current_session , session_context
19
21
from ..types import MISSING , MISSING_TYPE
20
22
from ._is_express import find_magic_comment_mode
@@ -115,13 +117,13 @@ def create_express_app(file: Path, package_name: str) -> App:
115
117
116
118
file = file .resolve ()
117
119
120
+ stub_session = ExpressStubSession ()
118
121
try :
119
122
globals_file = file .parent / "globals.py"
120
123
if globals_file .is_file ():
121
124
with session_context (None ):
122
125
import_module_from_path ("globals" , globals_file )
123
126
124
- stub_session = ExpressStubSession ()
125
127
with session_context (stub_session ):
126
128
# We tagify here, instead of waiting for the App object to do it when it wraps
127
129
# the UI in a HTMLDocument and calls render() on it. This is because
@@ -134,6 +136,17 @@ def create_express_app(file: Path, package_name: str) -> App:
134
136
except AttributeError as e :
135
137
raise RuntimeError (e ) from e
136
138
139
+ express_bookmark_store = stub_session .app_opts .get ("bookmark_store" , "disable" )
140
+ if express_bookmark_store != "disable" :
141
+ # If bookmarking is enabled, wrap UI in function to automatically leverage UI
142
+ # functions to restore their values
143
+ def app_ui_wrapper (request : Request ):
144
+ # Stub session used to pass `app_opts()` checks.
145
+ with session_context (ExpressStubSession ()):
146
+ return run_express (file , package_name ).tagify ()
147
+
148
+ app_ui = app_ui_wrapper
149
+
137
150
def express_server (input : Inputs , output : Outputs , session : Session ):
138
151
try :
139
152
run_express (file , package_name )
@@ -290,12 +303,15 @@ def __getattr__(self, name: str):
290
303
291
304
class AppOpts (TypedDict ):
292
305
static_assets : NotRequired [dict [str , Path ]]
306
+ bookmark_store : NotRequired [BookmarkStore ]
293
307
debug : NotRequired [bool ]
294
308
295
309
296
310
@no_example ()
297
311
def app_opts (
312
+ * ,
298
313
static_assets : str | Path | Mapping [str , str | Path ] | MISSING_TYPE = MISSING ,
314
+ bookmark_store : Literal ["url" , "server" , "disable" ] | MISSING_TYPE = MISSING ,
299
315
debug : bool | MISSING_TYPE = MISSING ,
300
316
):
301
317
"""
@@ -313,6 +329,12 @@ def app_opts(
313
329
that mount point. In Shiny Express, if there is a `www` subdirectory of the
314
330
directory containing the app file, it will automatically be mounted at `/`, even
315
331
without needing to set the option here.
332
+ bookmark_store
333
+ Where to store the bookmark state.
334
+
335
+ * `"url"`: Encode the bookmark state in the URL.
336
+ * `"server"`: Store the bookmark state on the server.
337
+ * `"disable"`: Disable bookmarking.
316
338
debug
317
339
Whether to enable debug mode.
318
340
"""
@@ -339,6 +361,9 @@ def app_opts(
339
361
340
362
stub_session .app_opts ["static_assets" ] = static_assets_paths
341
363
364
+ if not isinstance (bookmark_store , MISSING_TYPE ):
365
+ stub_session .app_opts ["bookmark_store" ] = bookmark_store
366
+
342
367
if not isinstance (debug , MISSING_TYPE ):
343
368
stub_session .app_opts ["debug" ] = debug
344
369
@@ -357,6 +382,9 @@ def _merge_app_opts(app_opts: AppOpts, app_opts_new: AppOpts) -> AppOpts:
357
382
elif "static_assets" in app_opts_new :
358
383
app_opts ["static_assets" ] = app_opts_new ["static_assets" ].copy ()
359
384
385
+ if "bookmark_store" in app_opts_new :
386
+ app_opts ["bookmark_store" ] = app_opts_new ["bookmark_store" ]
387
+
360
388
if "debug" in app_opts_new :
361
389
app_opts ["debug" ] = app_opts_new ["debug" ]
362
390
0 commit comments