diff --git a/MANIFEST.IN b/MANIFEST.IN new file mode 100644 index 0000000..1fc1a38 --- /dev/null +++ b/MANIFEST.IN @@ -0,0 +1 @@ +include pyscript_bootstrap_templates/data/*.png \ No newline at end of file diff --git a/build.sh b/build.sh index 0399851..f761f8c 100755 --- a/build.sh +++ b/build.sh @@ -9,5 +9,5 @@ wheel_files=( dist/*whl ) cd ./examples/ for example in *; - do pyscript_bootstrap_app update $example $example --pyscript_bootstrap_templates_wheel_url ../${wheel_files[0]} + do pyscript_bootstrap_app update $example $example --pyscript-bootstrap-templates-wheel-url ../${wheel_files[0]} done diff --git a/dist/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl b/dist/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl index f28ee83..af6f652 100644 Binary files a/dist/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl and b/dist/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl differ diff --git a/examples/01_hello_world/config.json b/examples/01_hello_world/config.json index 4382486..a7c4145 100644 --- a/examples/01_hello_world/config.json +++ b/examples/01_hello_world/config.json @@ -8,5 +8,7 @@ "pyscript_py_url": "https://pyscript.net/alpha/pyscript.py", "bootstrap_css_url": "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css", "bootstrap_js_url": "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js", - "pyscript_bootstrap_templates_wheel_url": "../dist/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl" + "pyscript_bootstrap_templates_wheel_url": "../dist/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl", + "pwa_bg_color": "#000000", + "pwa_theme_color": "#ffffff" } \ No newline at end of file diff --git a/examples/01_hello_world/index.html b/examples/01_hello_world/index.html index 4911d12..3b36b11 100644 --- a/examples/01_hello_world/index.html +++ b/examples/01_hello_world/index.html @@ -5,6 +5,8 @@ + + @@ -24,6 +26,7 @@ + \ No newline at end of file diff --git a/examples/01_hello_world/manifest.json b/examples/01_hello_world/manifest.json new file mode 100644 index 0000000..0160d8e --- /dev/null +++ b/examples/01_hello_world/manifest.json @@ -0,0 +1 @@ +{"name": "01_hello_world", "short_name": "01_hello_world", "start_url": "./index.html", "scope": ".", "display": "standalone", "background_color": "#000000", "theme_color": "#ffffff", "icons": [{"src": "resources/icon-512x512.png", "type": "image/png", "sizes": "512x512"}]} \ No newline at end of file diff --git a/examples/01_hello_world/resources/icon-256x256.png b/examples/01_hello_world/resources/icon-256x256.png new file mode 100644 index 0000000..c450626 Binary files /dev/null and b/examples/01_hello_world/resources/icon-256x256.png differ diff --git a/examples/01_hello_world/resources/icon-512x512.png b/examples/01_hello_world/resources/icon-512x512.png new file mode 100644 index 0000000..c450626 Binary files /dev/null and b/examples/01_hello_world/resources/icon-512x512.png differ diff --git a/examples/01_hello_world/resources/pwa.js b/examples/01_hello_world/resources/pwa.js new file mode 100644 index 0000000..e8ec724 --- /dev/null +++ b/examples/01_hello_world/resources/pwa.js @@ -0,0 +1,10 @@ + +if ("serviceWorker" in navigator) { + window.addEventListener("load", function () { + navigator.serviceWorker + .register("./serviceWorker.js") + .then(res => console.log("service worker registered", res)) + .catch(err => console.log("service worker not registered", err)) + }) +} + \ No newline at end of file diff --git a/examples/01_hello_world/resources/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl b/examples/01_hello_world/resources/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl index f28ee83..af6f652 100644 Binary files a/examples/01_hello_world/resources/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl and b/examples/01_hello_world/resources/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl differ diff --git a/examples/01_hello_world/resources/serviceWorker.js b/examples/01_hello_world/resources/serviceWorker.js new file mode 100644 index 0000000..d99a1c3 --- /dev/null +++ b/examples/01_hello_world/resources/serviceWorker.js @@ -0,0 +1,32 @@ + +const pwa_version = "202210160801" +const assets = ["/index.html", + "/main.py", + "/resources/bootstrap.css", + "/resources/bootstrap.js", + "/resources/pyscript.css", + "/resources/pyscript.js", + "/resources/pyscript.py", + "/resources/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl", + "/resources/pwa.js", + "/resourced/site.js", +] +self.addEventListener("install", installEvent => { + installEvent.waitUntil( + caches.open(pwa_version).then(cache => { + cache.addAll(assets).then(r => { + console.log("Cache assets downloaded"); + }).catch(err => console.log("Error caching item", err)) + console.log(`Cache ${pwa_version} opened.`); + }).catch(err => console.log("Error opening cache", err)) + ) +}) + +self.addEventListener("fetch", fetchEvent => { + fetchEvent.respondWith( + caches.match(fetchEvent.request).then(res => { + return res || fetch(fetchEvent.request) + }).catch(err => console.log("Cache fetch error: ", err)) + ) +}) + \ No newline at end of file diff --git a/examples/01_hello_world/resources/site.js b/examples/01_hello_world/resources/site.js new file mode 100644 index 0000000..b0dac65 --- /dev/null +++ b/examples/01_hello_world/resources/site.js @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/examples/01_hello_world/serviceWorker.js b/examples/01_hello_world/serviceWorker.js new file mode 100644 index 0000000..e87fec0 --- /dev/null +++ b/examples/01_hello_world/serviceWorker.js @@ -0,0 +1,32 @@ + +const pwa_version = "202210160804" +const assets = ["./index.html", + "./main.py", + "./resources/bootstrap.css", + "./resources/bootstrap.js", + "./resources/pyscript.css", + "./resources/pyscript.js", + "./resources/pyscript.py", + "./resources/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl", + "./resources/pwa.js", + "./site.js", +] +self.addEventListener("install", installEvent => { + installEvent.waitUntil( + caches.open(pwa_version).then(cache => { + cache.addAll(assets).then(r => { + console.log("Cache assets downloaded"); + }).catch(err => console.log("Error caching item", err)) + console.log(`Cache ${pwa_version} opened.`); + }).catch(err => console.log("Error opening cache", err)) + ) +}) + +self.addEventListener("fetch", fetchEvent => { + fetchEvent.respondWith( + caches.match(fetchEvent.request).then(res => { + return res || fetch(fetchEvent.request) + }).catch(err => console.log("Cache fetch error: ", err)) + ) +}) + \ No newline at end of file diff --git a/examples/01_hello_world/site.js b/examples/01_hello_world/site.js new file mode 100644 index 0000000..b0dac65 --- /dev/null +++ b/examples/01_hello_world/site.js @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/examples/02_image_filter/config.json b/examples/02_image_filter/config.json index a5d9a28..5e08479 100644 --- a/examples/02_image_filter/config.json +++ b/examples/02_image_filter/config.json @@ -11,5 +11,7 @@ "pyscript_py_url": "https://pyscript.net/alpha/pyscript.py", "bootstrap_css_url": "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css", "bootstrap_js_url": "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js", - "pyscript_bootstrap_templates_wheel_url": "../dist/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl" + "pyscript_bootstrap_templates_wheel_url": "../dist/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl", + "pwa_bg_color": "#000000", + "pwa_theme_color": "#ffffff" } \ No newline at end of file diff --git a/examples/02_image_filter/index.html b/examples/02_image_filter/index.html index 9a66b9d..5d365af 100644 --- a/examples/02_image_filter/index.html +++ b/examples/02_image_filter/index.html @@ -5,6 +5,8 @@ + + @@ -25,6 +27,7 @@ + \ No newline at end of file diff --git a/examples/02_image_filter/manifest.json b/examples/02_image_filter/manifest.json new file mode 100644 index 0000000..ae6249a --- /dev/null +++ b/examples/02_image_filter/manifest.json @@ -0,0 +1 @@ +{"name": "02_image_filter", "short_name": "02_image_filter", "start_url": "./index.html", "scope": ".", "display": "standalone", "background_color": "#000000", "theme_color": "#ffffff", "icons": [{"src": "resources/icon-512x512.png", "type": "image/png", "sizes": "512x512"}]} \ No newline at end of file diff --git a/examples/02_image_filter/resources/icon-256x256.png b/examples/02_image_filter/resources/icon-256x256.png new file mode 100644 index 0000000..c450626 Binary files /dev/null and b/examples/02_image_filter/resources/icon-256x256.png differ diff --git a/examples/02_image_filter/resources/icon-512x512.png b/examples/02_image_filter/resources/icon-512x512.png new file mode 100644 index 0000000..c450626 Binary files /dev/null and b/examples/02_image_filter/resources/icon-512x512.png differ diff --git a/examples/02_image_filter/resources/pwa.js b/examples/02_image_filter/resources/pwa.js new file mode 100644 index 0000000..e8ec724 --- /dev/null +++ b/examples/02_image_filter/resources/pwa.js @@ -0,0 +1,10 @@ + +if ("serviceWorker" in navigator) { + window.addEventListener("load", function () { + navigator.serviceWorker + .register("./serviceWorker.js") + .then(res => console.log("service worker registered", res)) + .catch(err => console.log("service worker not registered", err)) + }) +} + \ No newline at end of file diff --git a/examples/02_image_filter/resources/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl b/examples/02_image_filter/resources/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl index f28ee83..af6f652 100644 Binary files a/examples/02_image_filter/resources/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl and b/examples/02_image_filter/resources/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl differ diff --git a/examples/02_image_filter/resources/serviceWorker.js b/examples/02_image_filter/resources/serviceWorker.js new file mode 100644 index 0000000..d99a1c3 --- /dev/null +++ b/examples/02_image_filter/resources/serviceWorker.js @@ -0,0 +1,32 @@ + +const pwa_version = "202210160801" +const assets = ["/index.html", + "/main.py", + "/resources/bootstrap.css", + "/resources/bootstrap.js", + "/resources/pyscript.css", + "/resources/pyscript.js", + "/resources/pyscript.py", + "/resources/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl", + "/resources/pwa.js", + "/resourced/site.js", +] +self.addEventListener("install", installEvent => { + installEvent.waitUntil( + caches.open(pwa_version).then(cache => { + cache.addAll(assets).then(r => { + console.log("Cache assets downloaded"); + }).catch(err => console.log("Error caching item", err)) + console.log(`Cache ${pwa_version} opened.`); + }).catch(err => console.log("Error opening cache", err)) + ) +}) + +self.addEventListener("fetch", fetchEvent => { + fetchEvent.respondWith( + caches.match(fetchEvent.request).then(res => { + return res || fetch(fetchEvent.request) + }).catch(err => console.log("Cache fetch error: ", err)) + ) +}) + \ No newline at end of file diff --git a/examples/02_image_filter/resources/site.js b/examples/02_image_filter/resources/site.js new file mode 100644 index 0000000..b0dac65 --- /dev/null +++ b/examples/02_image_filter/resources/site.js @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/examples/02_image_filter/serviceWorker.js b/examples/02_image_filter/serviceWorker.js new file mode 100644 index 0000000..e87fec0 --- /dev/null +++ b/examples/02_image_filter/serviceWorker.js @@ -0,0 +1,32 @@ + +const pwa_version = "202210160804" +const assets = ["./index.html", + "./main.py", + "./resources/bootstrap.css", + "./resources/bootstrap.js", + "./resources/pyscript.css", + "./resources/pyscript.js", + "./resources/pyscript.py", + "./resources/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl", + "./resources/pwa.js", + "./site.js", +] +self.addEventListener("install", installEvent => { + installEvent.waitUntil( + caches.open(pwa_version).then(cache => { + cache.addAll(assets).then(r => { + console.log("Cache assets downloaded"); + }).catch(err => console.log("Error caching item", err)) + console.log(`Cache ${pwa_version} opened.`); + }).catch(err => console.log("Error opening cache", err)) + ) +}) + +self.addEventListener("fetch", fetchEvent => { + fetchEvent.respondWith( + caches.match(fetchEvent.request).then(res => { + return res || fetch(fetchEvent.request) + }).catch(err => console.log("Cache fetch error: ", err)) + ) +}) + \ No newline at end of file diff --git a/examples/02_image_filter/site.js b/examples/02_image_filter/site.js new file mode 100644 index 0000000..b0dac65 --- /dev/null +++ b/examples/02_image_filter/site.js @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/pyscript_bootstrap_templates/cli_interface.py b/pyscript_bootstrap_templates/cli_interface.py index 9a9b743..4218925 100644 --- a/pyscript_bootstrap_templates/cli_interface.py +++ b/pyscript_bootstrap_templates/cli_interface.py @@ -8,7 +8,8 @@ from numpy import isin import requests import json import shutil - +import datetime as dt +import pkg_resources def download_file(url: str, path: pathlib.Path): if pathlib.Path(url).exists(): @@ -18,6 +19,100 @@ def download_file(url: str, path: pathlib.Path): path.write_bytes(response.content) +def create_pwa_manifest(root_folder: pathlib.Path, **kwargs): + """ + creating the PWA's manifest file + """ + + #TODO: make icon configurable + + manifest_dict = { + "name": kwargs['title'], + "short_name": kwargs['title'], + "start_url": "./index.html", + "scope": ".", + "display": "standalone", + "background_color": kwargs['pwa_bg_color'], + "theme_color": kwargs['pwa_theme_color'], + "icons": [ + { + "src": "resources/icon-512x512.png", + "type": "image/png", + "sizes": "512x512" + } + ] + } + + with open(root_folder / "manifest.json", "w") as f: + json.dump(manifest_dict, f) + + # create dummy icon TODO: making this configurable + + default_icon_path = pkg_resources.resource_filename('pyscript_bootstrap_templates', 'data/icon.png') + download_file(str(default_icon_path), root_folder / "resources" / "icon-512x512.png") + +def create_pwa_service_worker(root_folder: pathlib.Path, **kwargs): + version = dt.datetime.utcnow().strftime("%Y%m%d%H%M") + assets = [ + *[f'"./{p}",' for p in kwargs['paths']], + '"./index.html",', + '"./main.py",', + '"./resources/bootstrap.css",', + '"./resources/bootstrap.js",', + '"./resources/pyscript.css",', + '"./resources/pyscript.js",', + '"./resources/pyscript.py",', + f'"./resources/{kwargs["pyscript_bootstrap_templates_wheel_url"].split("/")[-1]}",', # FIXME: not completely os independent + '"./resources/pwa.js",', + '"./site.js",' + ] + + assets_str = "[" + "\n ".join(assets) + "\n]" + + service_worker_js = f""" +const pwa_version = "{version}" +const assets = {assets_str} +self.addEventListener("install", installEvent => {{ + installEvent.waitUntil( + caches.open(pwa_version).then(cache => {{ + cache.addAll(assets).then(r => {{ + console.log("Cache assets downloaded"); + }}).catch(err => console.log("Error caching item", err)) + console.log(`Cache ${{pwa_version}} opened.`); + }}).catch(err => console.log("Error opening cache", err)) + ) +}}) + +self.addEventListener("fetch", fetchEvent => {{ + fetchEvent.respondWith( + caches.match(fetchEvent.request).then(res => {{ + return res || fetch(fetchEvent.request) + }}).catch(err => console.log("Cache fetch error: ", err)) + ) +}}) + """ + + pwa_js = """ +if ("serviceWorker" in navigator) { + window.addEventListener("load", function () { + navigator.serviceWorker + .register("./serviceWorker.js") + .then(res => console.log("service worker registered", res)) + .catch(err => console.log("service worker not registered", err)) + }) +} + """ + + site_js = """ + """ + + # write js files: + (root_folder / "serviceWorker.js").write_text(service_worker_js, encoding="utf-8") + (root_folder / "resources" / "pwa.js").write_text(pwa_js, encoding="utf-8") + (root_folder / "site.js").write_text(site_js, encoding="utf-8") + + + def generate_project_files(root_folder: pathlib.Path, title: str, packages: List[str], @@ -27,7 +122,8 @@ def generate_project_files(root_folder: pathlib.Path, pyscript_py_url: str = "https://pyscript.net/alpha/pyscript.py", bootstrap_css_url: str = "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css", bootstrap_js_url: str = "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js", - pyscript_bootstrap_templates_wheel_url: str = "https://the-cake-is-a-lie.net/gogs/jonas/pyscript-bootstrap-templates/raw/branch/main/dist/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl"): + pyscript_bootstrap_templates_wheel_url: str = "https://the-cake-is-a-lie.net/gogs/jonas/pyscript-bootstrap-templates/raw/branch/main/dist/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl", + **_): pyenv = f"- ./resources/{pyscript_bootstrap_templates_wheel_url.split('/')[-1]}" for package in packages: @@ -47,6 +143,8 @@ def generate_project_files(root_folder: pathlib.Path, + + @@ -64,6 +162,7 @@ def generate_project_files(root_folder: pathlib.Path, + """ @@ -107,6 +206,8 @@ btn.onclick = lambda _: bHTML.AlertSuccess( # only create if not existing: if not main_py.exists(): main_py.write_text(py, encoding="utf-8") + + def create_project(**kwargs): @@ -118,13 +219,16 @@ def create_project(**kwargs): root_folder.mkdir(parents=True, exist_ok=True) # create initial config.json + + generate_project_files(**kwargs) + create_pwa_manifest(**kwargs) + create_pwa_service_worker(**kwargs) kwargs['root_folder'] = str(root_folder) with open(root_folder / "config.json", 'w') as f: json.dump(kwargs, f, indent=4) - - generate_project_files(**kwargs) + def update_project(**kwargs): @@ -147,6 +251,8 @@ def update_project(**kwargs): config[arg] = val generate_project_files(**config) + create_pwa_manifest(**kwargs) + create_pwa_service_worker(**kwargs) config['root_folder'] = str(root_folder) @@ -168,18 +274,22 @@ def main(): "--packages", type=str, nargs="+", help="the packages to include in the new project") argument_parser.add_argument("--paths", type=str, nargs="+", help="additional local python files to include in the new project") - argument_parser.add_argument("--pyscript_css_url", type=str, + argument_parser.add_argument("--pyscript-css-url", type=str, help="the url of the pyscript css file", default="https://pyscript.net/alpha/pyscript.css") - argument_parser.add_argument("--pyscript_js_url", type=str, + argument_parser.add_argument("--pyscript-js-url", type=str, help="the url of the pyscript js file", default="https://pyscript.net/alpha/pyscript.min.js") - argument_parser.add_argument("--pyscript_py_url", type=str, + argument_parser.add_argument("--pyscript-py-url", type=str, help="the url of the pyscript py file", default="https://pyscript.net/alpha/pyscript.py") - argument_parser.add_argument("--bootstrap_css_url", type=str, + argument_parser.add_argument("--bootstrap-css-url", type=str, help="the url of the bootstrap css file", default="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css") - argument_parser.add_argument("--bootstrap_js_url", type=str, + argument_parser.add_argument("--bootstrap-js-url", type=str, help="the url of the bootstrap js file", default="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js") - argument_parser.add_argument("--pyscript_bootstrap_templates_wheel_url", type=str, + argument_parser.add_argument("--pyscript-bootstrap-templates-wheel-url", type=str, help="the url of the pyscript bootstrap templates wheel file", default="https://the-cake-is-a-lie.net/gogs/jonas/pyscript-bootstrap-templates/raw/branch/main/dist/pyscript_bootstrap_templates-0.1.0-py3-none-any.whl") + argument_parser.add_argument("--pwa-bg-color", type=str, help="background color for pwa configuration", default="#000000") + argument_parser.add_argument("--pwa-theme-color", type=str, help="theme color for pwa configuration", default="#ffffff") + + args = argument_parser.parse_args() @@ -193,10 +303,14 @@ def main(): 'pyscript_py_url': args.pyscript_py_url, 'bootstrap_css_url': args.bootstrap_css_url, 'bootstrap_js_url': args.bootstrap_js_url, - 'pyscript_bootstrap_templates_wheel_url': args.pyscript_bootstrap_templates_wheel_url + 'pyscript_bootstrap_templates_wheel_url': args.pyscript_bootstrap_templates_wheel_url, + 'pwa_bg_color': args.pwa_bg_color, + 'pwa_theme_color': args.pwa_theme_color } + + if args.command == "create": create_project(**config) elif args.command == "update": diff --git a/pyscript_bootstrap_templates/data/icon.png b/pyscript_bootstrap_templates/data/icon.png new file mode 100644 index 0000000..c450626 Binary files /dev/null and b/pyscript_bootstrap_templates/data/icon.png differ