From 77cc9e450d271da93ab9e8cf3f60a3adfd5b7d5e Mon Sep 17 00:00:00 2001 From: Vaughn Kottler Date: Thu, 17 Oct 2024 06:13:51 +0000 Subject: [PATCH] 5.7.0 - Add light/dark support --- .github/workflows/python-package.yml | 2 +- README.md | 4 +-- local/variables/package.yaml | 4 +-- pyproject.toml | 2 +- runtimepy/__init__.py | 4 +-- runtimepy/data/css/bootstrap_extra.css | 15 ++++++-- runtimepy/data/css/main.css | 1 - .../data/js/classes/WindowHashManager.js | 36 +++++++++++++++---- .../net/server/app/bootstrap/elements.py | 2 +- runtimepy/net/server/app/bootstrap/tabs.py | 15 ++++++-- runtimepy/net/server/app/env/__init__.py | 6 +++- runtimepy/net/server/app/env/modal.py | 2 +- runtimepy/net/server/app/env/settings.py | 4 +-- runtimepy/net/server/app/env/tab/html.py | 4 +-- runtimepy/net/server/app/placeholder.py | 2 +- runtimepy/net/server/app/sound.py | 2 +- runtimepy/requirements.txt | 2 +- 17 files changed, 75 insertions(+), 32 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 9dd7b964..26b4ca90 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -77,7 +77,7 @@ jobs: - run: | mk python-release owner=vkottler \ - repo=runtimepy version=5.6.4 + repo=runtimepy version=5.7.0 if: | matrix.python-version == '3.12' && matrix.system == 'ubuntu-latest' diff --git a/README.md b/README.md index 3aef67d1..5d961535 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ ===================================== generator=datazen version=3.1.4 - hash=41c5a2511c80b2cbbaee668bb6a75bd2 + hash=aa59dcef7f11c767458b14ad97f9de59 ===================================== --> -# runtimepy ([5.6.4](https://pypi.org/project/runtimepy/)) +# runtimepy ([5.7.0](https://pypi.org/project/runtimepy/)) [![python](https://img.shields.io/pypi/pyversions/runtimepy.svg)](https://pypi.org/project/runtimepy/) ![Build Status](https://github.com/vkottler/runtimepy/workflows/Python%20Package/badge.svg) diff --git a/local/variables/package.yaml b/local/variables/package.yaml index 93bcb3fa..eac174f6 100644 --- a/local/variables/package.yaml +++ b/local/variables/package.yaml @@ -1,5 +1,5 @@ --- major: 5 -minor: 6 -patch: 4 +minor: 7 +patch: 0 entry: runtimepy diff --git a/pyproject.toml b/pyproject.toml index 6c585ec8..085ef1e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta:__legacy__" [project] name = "runtimepy" -version = "5.6.4" +version = "5.7.0" description = "A framework for implementing Python services." readme = "README.md" requires-python = ">=3.11" diff --git a/runtimepy/__init__.py b/runtimepy/__init__.py index cbe432d2..9ac1236c 100644 --- a/runtimepy/__init__.py +++ b/runtimepy/__init__.py @@ -1,7 +1,7 @@ # ===================================== # generator=datazen # version=3.1.4 -# hash=beefe82269955725f177c01474f7cea1 +# hash=c51f4ac6cf134b3c3fa872a096e83489 # ===================================== """ @@ -10,7 +10,7 @@ DESCRIPTION = "A framework for implementing Python services." PKG_NAME = "runtimepy" -VERSION = "5.6.4" +VERSION = "5.7.0" # runtimepy-specific content. METRICS_NAME = "metrics" diff --git a/runtimepy/data/css/bootstrap_extra.css b/runtimepy/data/css/bootstrap_extra.css index f66eb527..07fe17c3 100644 --- a/runtimepy/data/css/bootstrap_extra.css +++ b/runtimepy/data/css/bootstrap_extra.css @@ -77,19 +77,17 @@ textarea.text-logs { padding-bottom: 0 !important; border-top: 0; - background-color: var(--bs-secondary-bg-subtle); } .vertical-divider { flex-basis: 0.75em; flex-grow: 0; flex-shrink: 0; - background-color: var(--bs-secondary-bg-subtle); } .vertical-divider:hover { cursor: col-resize; - background-color: var(--bs-secondary-bg); + background-color: var(--bs-highlight-bg) !important; } button:hover { @@ -99,3 +97,14 @@ button:hover { .channel-value-input { width: 6em; } + +/* + * Should probably build the bootstrap stuff from source and set custom Sass: + * https://getbootstrap.com/docs/5.3/customize/sass/ + */ + +:root { + /* Prefer 'Arial' and 'Consolas' at the highest priority. */ + --bs-font-sans-serif: Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --bs-font-monospace: Consolas, "Liberation Mono", "Courier New", monospace; +} diff --git a/runtimepy/data/css/main.css b/runtimepy/data/css/main.css index f6458923..1f532db0 100644 --- a/runtimepy/data/css/main.css +++ b/runtimepy/data/css/main.css @@ -28,7 +28,6 @@ body > :first-child { width: 100vw; height: 100vh; opacity: 1.0; - background-color: var(--bs-secondary-bg-subtle); } .click-plot { diff --git a/runtimepy/data/js/classes/WindowHashManager.js b/runtimepy/data/js/classes/WindowHashManager.js index e236f91c..58480f46 100644 --- a/runtimepy/data/js/classes/WindowHashManager.js +++ b/runtimepy/data/js/classes/WindowHashManager.js @@ -7,6 +7,7 @@ class WindowHashManager { this.tabFilter = ""; this.tabsShown = true; this.channelsShown = true; + this.lightMode = false; this.plotChannels = {}; this.filters = {}; this.minTxPeriod = 0.0; @@ -17,6 +18,15 @@ class WindowHashManager { this.update(); } + lightDarkClick(event) { + this.lightMode = !this.lightMode; + + document.getElementById("runtimepy") + .setAttribute("data-bs-theme", this.lightMode ? "light" : "dark"); + + this.update(); + } + channelClick(event) { this.channelsShown = !this.channelsShown; @@ -106,6 +116,12 @@ class WindowHashManager { channelsButton.addEventListener("click", this.channelClick.bind(this)); } + /* Click handler for light/dark toggle. */ + let lightDarkButton = document.getElementById("theme-button"); + if (lightDarkButton) { + lightDarkButton.addEventListener("click", this.lightDarkClick.bind(this)); + } + /* Click handlers for new window buttons. */ for (const button of document.querySelectorAll(".window-button")) { button.onclick = () => { @@ -150,13 +166,6 @@ class WindowHashManager { } } - if (split.includes("hide-tabs")) { - tabButton.click(); - } - if (split.includes("hide-channels")) { - channelsButton.click(); - } - /* Check for tab filter. */ for (let item of split) { if (item.includes("=")) { @@ -171,6 +180,16 @@ class WindowHashManager { } } } + + if (split.includes("hide-tabs")) { + tabButton.click(); + } + if (split.includes("hide-channels")) { + channelsButton.click(); + } + if (split.includes("light-mode")) { + lightDarkButton.click(); + } } } @@ -199,6 +218,9 @@ class WindowHashManager { if (!this.channelsShown) { hash += ",hide-channels" } + if (this.lightMode) { + hash += ",light-mode"; + } if (this.minTxPeriod != 0.0) { hash += `,min-tx-period-ms=${this.minTxPeriod}`; diff --git a/runtimepy/net/server/app/bootstrap/elements.py b/runtimepy/net/server/app/bootstrap/elements.py index 5d99f86e..dc5a209c 100644 --- a/runtimepy/net/server/app/bootstrap/elements.py +++ b/runtimepy/net/server/app/bootstrap/elements.py @@ -192,7 +192,7 @@ def centered_markdown( div( text=stream.getvalue(), parent=horiz_container, - class_str="text-light p-3 pb-0", + class_str="text-body p-3 pb-0", ) div(parent=horiz_container) diff --git a/runtimepy/net/server/app/bootstrap/tabs.py b/runtimepy/net/server/app/bootstrap/tabs.py index 780b5be8..5f934d38 100644 --- a/runtimepy/net/server/app/bootstrap/tabs.py +++ b/runtimepy/net/server/app/bootstrap/tabs.py @@ -8,8 +8,10 @@ # internal from runtimepy import PKG_NAME +from runtimepy.net.server.app.bootstrap import icon_str from runtimepy.net.server.app.bootstrap.elements import ( BOOTSTRAP_BUTTON, + bootstrap_button, collapse_button, flex, toggle_button, @@ -85,16 +87,23 @@ def __init__(self, name: str, parent: Element) -> None: # Create application container. self.container = div(parent=parent, id=name) - self.container.add_class("d-flex", "align-items-start") + self.container.add_class("d-flex", "align-items-start", "bg-body") # Dark theme. self.container["data-bs-theme"] = "dark" - parent.add_class("bg-dark") # Buttons. self.button_column = div(parent=self.container) self.button_column.add_class( - "d-flex", "flex-column", "h-100", "bg-secondary-subtle" + "d-flex", "flex-column", "h-100", "bg-dark-subtle" + ) + + # Dark/light theme switch button. + bootstrap_button( + icon_str("lightbulb"), + tooltip=" Toggle light/dark.", + id="theme-button", + parent=self.button_column, ) # Toggle tabs button. diff --git a/runtimepy/net/server/app/env/__init__.py b/runtimepy/net/server/app/env/__init__.py index dd581849..b1fe1fec 100644 --- a/runtimepy/net/server/app/env/__init__.py +++ b/runtimepy/net/server/app/env/__init__.py @@ -155,4 +155,8 @@ def channel_environments(app: AppInfo, tabs: TabbedContent) -> None: ) # Add splash screen element. - div(id=f"{PKG_NAME}-splash", parent=tabs.container) + div( + id=f"{PKG_NAME}-splash", + parent=tabs.container, + class_str="bg-success-subtle bg-gradient", + ) diff --git a/runtimepy/net/server/app/env/modal.py b/runtimepy/net/server/app/env/modal.py index 1f7a8766..d986e9f8 100644 --- a/runtimepy/net/server/app/env/modal.py +++ b/runtimepy/net/server/app/env/modal.py @@ -32,7 +32,7 @@ def __init__( content = div( parent=div( - parent=modal, class_str="modal-dialog text-light " + TEXT + parent=modal, class_str="modal-dialog text-body " + TEXT ), class_str="modal-content rounded-0", ) diff --git a/runtimepy/net/server/app/env/settings.py b/runtimepy/net/server/app/env/settings.py index 8e6bca74..0e0e4e99 100644 --- a/runtimepy/net/server/app/env/settings.py +++ b/runtimepy/net/server/app/env/settings.py @@ -43,7 +43,7 @@ def plot_settings(tabs: TabbedContent) -> None: div( text="0 ms ('high', run at native refresh rate)", parent=container, - class_str="text-nowrap text-primary", + class_str="text-nowrap text-body-emphasis", ) slider( @@ -53,7 +53,7 @@ def plot_settings(tabs: TabbedContent) -> None: div( text="100 ms ('low', 10 Hz)", parent=container, - class_str="text-nowrap text-primary", + class_str="text-nowrap text-body-emphasis", ) div(tag="hr", parent=modal.body) diff --git a/runtimepy/net/server/app/env/tab/html.py b/runtimepy/net/server/app/env/tab/html.py index 2316c522..db2c973a 100644 --- a/runtimepy/net/server/app/env/tab/html.py +++ b/runtimepy/net/server/app/env/tab/html.py @@ -238,7 +238,7 @@ def compose(self, parent: Element) -> None: logs = div( tag="textarea", parent=div(parent=vert_container, class_str="form-floating"), - class_str=f"form-control rounded-0 {TEXT} text-logs", + class_str=(f"form-control rounded-0 {TEXT} text-logs"), id=self.get_id("logs"), title=f"Text logs for {self.name}.", ) @@ -258,7 +258,7 @@ def compose(self, parent: Element) -> None: div( id=self.get_id("divider"), parent=container, - class_str="vertical-divider border-start", + class_str="vertical-divider border-start bg-dark-subtle", ) self._compose_plot(container) diff --git a/runtimepy/net/server/app/placeholder.py b/runtimepy/net/server/app/placeholder.py index 491c0416..2c08b28a 100644 --- a/runtimepy/net/server/app/placeholder.py +++ b/runtimepy/net/server/app/placeholder.py @@ -19,7 +19,7 @@ class DummyTab(Tab): def compose(self, parent: Element) -> None: """Compose the tab's HTML elements.""" - parent.add_class("text-light") + parent.add_class("text-body") for idx in range(10): div(parent=parent, text="Hello, world! " + str(idx)) diff --git a/runtimepy/net/server/app/sound.py b/runtimepy/net/server/app/sound.py index c9d9d0db..ab2e8c30 100644 --- a/runtimepy/net/server/app/sound.py +++ b/runtimepy/net/server/app/sound.py @@ -17,7 +17,7 @@ class SoundTab(Tab): def compose(self, parent: Element) -> None: """Compose the tab's HTML elements.""" - container = div(parent=parent, class_str="text-light") + container = div(parent=parent, class_str="text-body") div(text="Hello, world! 1", parent=container) div(text="Hello, world! 2", parent=container) diff --git a/runtimepy/requirements.txt b/runtimepy/requirements.txt index 567efbb5..6eed8ad8 100644 --- a/runtimepy/requirements.txt +++ b/runtimepy/requirements.txt @@ -1,5 +1,5 @@ aiofiles -vcorelib>=3.4.1 +vcorelib>=3.4.2 svgen>=0.6.8 websockets psutil