1
+ import json
1
2
import urllib .parse
2
3
from collections import namedtuple
3
4
from collections .abc import Mapping , Sequence
21
22
DEFAULT_CDN = "https://cdn.jsdelivr.net/gh/wang0618/PyWebIO-assets@v{version}/"
22
23
23
24
_global_config = {'title' : 'PyWebIO Application' }
24
- config_keys = ['title' , 'description' , 'js_file' , 'js_code' , 'css_style' , 'css_file' , 'theme' ]
25
+ config_keys = ['title' , 'description' , 'js_file' , 'js_code' , 'css_style' , 'css_file' , 'theme' , 'manifest' ]
25
26
AppMeta = namedtuple ('App' , config_keys )
26
27
27
28
_here_dir = path .dirname (path .abspath (__file__ ))
@@ -45,13 +46,15 @@ def render_page(app, protocol, cdn):
45
46
else : # user custom cdn
46
47
base_url = cdn .rstrip ('/' ) + '/'
47
48
49
+ manifest = manifest_tag (base_url , meta )
50
+
48
51
theme = environ .get ('PYWEBIO_THEME' , meta .theme ) or 'default'
49
52
check_theme (theme )
50
53
51
54
return _index_page_tpl .generate (title = meta .title , description = meta .description , protocol = protocol ,
52
55
script = True , content = '' , base_url = base_url , version = version ,
53
56
js_file = meta .js_file or [], js_code = meta .js_code , css_style = meta .css_style ,
54
- css_file = meta .css_file or [], theme = theme )
57
+ css_file = meta .css_file or [], theme = theme , manifest = manifest )
55
58
56
59
57
60
@lru_cache (maxsize = 64 )
@@ -65,7 +68,7 @@ def check_theme(theme):
65
68
raise RuntimeError ("Can't find css file for theme `%s`" % theme )
66
69
67
70
68
- def parse_app_metadata (func ):
71
+ def parse_app_metadata (func ) -> AppMeta :
69
72
"""Get metadata form pywebio task function, fallback to global config in empty meta field."""
70
73
prefix = '_pywebio_'
71
74
attrs = get_function_attr (func , [prefix + k for k in config_keys ])
@@ -225,7 +228,42 @@ def hello():
225
228
return config (title = title , description = description )
226
229
227
230
228
- def config (* , title = None , description = None , theme = None , js_code = None , js_file = [], css_style = None , css_file = []):
231
+ def manifest_tag (base_url , meta : AppMeta ):
232
+ """Generate inline web app manifest
233
+ https://stackoverflow.com/questions/46221528/inline-the-web-app-manifest
234
+ """
235
+ if meta .manifest is False :
236
+ return ""
237
+
238
+ manifest_ = meta .manifest or {}
239
+ if manifest_ is True :
240
+ manifest_ = {}
241
+
242
+ manifest = {
243
+ "name" : meta .title ,
244
+ "description" : meta .description ,
245
+ "start_url" : "." ,
246
+ "display" : "standalone" ,
247
+ "theme_color" : "white" ,
248
+ "background_color" : "white" ,
249
+ "icons" : [
250
+ {"src" : f"{ base_url } image/apple-touch-icon.png" , "type" : "image/png" , "sizes" : "180x180" },
251
+ ]
252
+ }
253
+ manifest .update (manifest_ )
254
+
255
+ icon = manifest .pop ("icon" , None )
256
+ if not icon :
257
+ icon = base_url + 'image/apple-touch-icon.png'
258
+
259
+ manifest_encode = urllib .parse .quote (json .dumps (manifest ))
260
+ tag = f"""<link rel="apple-touch-icon" href="{ icon } ">
261
+ <link rel="manifest" href='data:application/manifest+json,{ manifest_encode } ' />"""
262
+ return tag
263
+
264
+
265
+ def config (* , title = None , description = None , theme = None , js_code = None , js_file = [], css_style = None , css_file = [],
266
+ manifest = True ):
229
267
"""PyWebIO application configuration
230
268
231
269
:param str title: Application title
@@ -244,6 +282,15 @@ def config(*, title=None, description=None, theme=None, js_code=None, js_file=[]
244
282
:param str/list js_file: The javascript files that inject to page, can be a URL in str or a list of it.
245
283
:param str css_style: The CSS style that you want to inject to page.
246
284
:param str/list css_file: The CSS files that inject to page, can be a URL in str or a list of it.
285
+ :param bool/dict manifest: `Web application manifest <https://developer.mozilla.org/en-US/docs/Web/Manifest>`_ configuration.
286
+ This feature allows you to add a shortcut to the home screen of your mobile device, and launch the app like a native app.
287
+ If set to ``True``, the default manifest will be used. You can also specify the manifest content in dict.
288
+ If ``False``, the manifest will be disabled.
289
+
290
+ .. collapse:: Note for icon configuration
291
+
292
+ Currently, the `icons <https://developer.mozilla.org/en-US/docs/Web/Manifest/icons>`_ field of the manifest
293
+ is not supported. Instead, you can use the ``icon`` field to specify the icon url.
247
294
248
295
``config()`` can be used in 2 ways: direct call and decorator.
249
296
If you call ``config()`` directly, the configuration will be global.
0 commit comments