Skip to content

Commit 5ae0558

Browse files
authored
Merge pull request #278 from vkottler/dev/5.7.4
5.7.4 - Working on initial landing page impl
2 parents 734e55b + aea900e commit 5ae0558

File tree

31 files changed

+289
-168
lines changed

31 files changed

+289
-168
lines changed

.github/workflows/python-package.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ jobs:
7777
7878
- run: |
7979
mk python-release owner=vkottler \
80-
repo=runtimepy version=5.7.3
80+
repo=runtimepy version=5.7.4
8181
if: |
8282
matrix.python-version == '3.12'
8383
&& matrix.system == 'ubuntu-latest'

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
=====================================
33
generator=datazen
44
version=3.1.4
5-
hash=ef917873929a5dc6a70367fde7f1447f
5+
hash=fbb71900eae64b008d2a2df1b7dde92b
66
=====================================
77
-->
88

9-
# runtimepy ([5.7.3](https://pypi.org/project/runtimepy/))
9+
# runtimepy ([5.7.4](https://pypi.org/project/runtimepy/))
1010

1111
[![python](https://img.shields.io/pypi/pyversions/runtimepy.svg)](https://pypi.org/project/runtimepy/)
1212
![Build Status](https://github.com/vkottler/runtimepy/workflows/Python%20Package/badge.svg)

local/variables/package.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
22
major: 5
33
minor: 7
4-
patch: 3
4+
patch: 4
55
entry: runtimepy

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta:__legacy__"
44

55
[project]
66
name = "runtimepy"
7-
version = "5.7.3"
7+
version = "5.7.4"
88
description = "A framework for implementing Python services."
99
readme = "README.md"
1010
requires-python = ">=3.11"

runtimepy/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# =====================================
22
# generator=datazen
33
# version=3.1.4
4-
# hash=37a32f51e0bf04276100ce27658b7f8f
4+
# hash=cb3ed2d01203d78ad794d39500d706f9
55
# =====================================
66

77
"""
@@ -10,7 +10,7 @@
1010

1111
DESCRIPTION = "A framework for implementing Python services."
1212
PKG_NAME = "runtimepy"
13-
VERSION = "5.7.3"
13+
VERSION = "5.7.4"
1414

1515
# runtimepy-specific content.
1616
METRICS_NAME = "metrics"

runtimepy/data/js/markdown_page.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/* Dark is hard-coded initial state (in HTML). */
2+
let lightMode = false;
3+
4+
function lightDarkClick(event) {
5+
lightMode = !lightMode;
6+
7+
document.getElementById("runtimepy")
8+
.setAttribute("data-bs-theme", lightMode ? "light" : "dark");
9+
10+
window.location.hash = lightMode ? "#light-mode" : "";
11+
}
12+
13+
let lightDarkButton = document.getElementById("theme-button");
14+
if (lightDarkButton) {
15+
lightDarkButton.addEventListener("click", lightDarkClick);
16+
}
17+
18+
if (window.location.hash) {
19+
let parts = window.location.hash.slice(1).split(",");
20+
21+
if (parts.includes("light-mode")) {
22+
lightDarkButton.click();
23+
}
24+
}

runtimepy/net/html/__init__.py

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
"""
2+
A module implementing HTML-related interfaces.
3+
"""
4+
5+
# built-in
6+
from io import StringIO
7+
from typing import Optional
8+
9+
# third-party
10+
from svgen.element import Element
11+
from svgen.element.html import Html, div
12+
from vcorelib import DEFAULT_ENCODING
13+
from vcorelib.io import IndentedFileWriter
14+
from vcorelib.paths import find_file
15+
16+
# internal
17+
from runtimepy import PKG_NAME
18+
from runtimepy.net.html.bootstrap import (
19+
add_bootstrap_css,
20+
add_bootstrap_js,
21+
icon_str,
22+
)
23+
from runtimepy.net.html.bootstrap.elements import (
24+
bootstrap_button,
25+
centered_markdown,
26+
)
27+
28+
29+
def create_app_shell(parent: Element, **kwargs) -> tuple[Element, Element]:
30+
"""Create a bootstrap-based application shell."""
31+
32+
container = div(parent=parent, **kwargs)
33+
container.add_class("d-flex", "align-items-start", "bg-body")
34+
35+
# Dark theme.
36+
container["data-bs-theme"] = "dark"
37+
38+
# Buttons.
39+
button_column = div(parent=container)
40+
button_column.add_class("d-flex", "flex-column", "h-100", "bg-dark-subtle")
41+
42+
# Dark/light theme switch button.
43+
bootstrap_button(
44+
icon_str("lightbulb"),
45+
tooltip=" Toggle light/dark.",
46+
id="theme-button",
47+
parent=button_column,
48+
)
49+
50+
return container, button_column
51+
52+
53+
def markdown_page(parent: Element, markdown: str, **kwargs) -> None:
54+
"""Compose a landing page."""
55+
56+
container = centered_markdown(
57+
create_app_shell(parent, **kwargs)[0], markdown, "h-100", "text-body"
58+
)
59+
container.add_class("overflow-y-auto")
60+
61+
62+
def common_css(document: Html) -> None:
63+
"""Add common CSS to an HTML document."""
64+
65+
append_kind(document.head, "font", kind="css", tag="style")
66+
add_bootstrap_css(document.head)
67+
append_kind(
68+
document.head, "main", "bootstrap_extra", kind="css", tag="style"
69+
)
70+
71+
72+
def full_markdown_page(document: Html, markdown: str) -> None:
73+
"""Render a full markdown HTML app."""
74+
75+
common_css(document)
76+
markdown_page(document.body, markdown, id=PKG_NAME)
77+
78+
# JavaScript.
79+
append_kind(document.body, "markdown_page")
80+
add_bootstrap_js(document.body)
81+
82+
83+
def handle_worker(writer: IndentedFileWriter) -> int:
84+
"""Boilerplate contents for worker thread block."""
85+
86+
# Not currently used.
87+
# return write_found_file(
88+
# writer, kind_url("js", "webgl-debug", subdir="third-party")
89+
# )
90+
del writer
91+
92+
return 0
93+
94+
95+
def write_found_file(writer: IndentedFileWriter, *args, **kwargs) -> bool:
96+
"""Write a file's contents to the file-writer's stream."""
97+
98+
result = False
99+
100+
entry = find_file(*args, **kwargs)
101+
if entry is not None:
102+
with entry.open(encoding=DEFAULT_ENCODING) as path_fd:
103+
for line in path_fd:
104+
writer.write(line)
105+
106+
result = True
107+
108+
return result
109+
110+
111+
def kind_url(
112+
kind: str, name: str, subdir: str = None, package: str = PKG_NAME
113+
) -> str:
114+
"""Return a URL to find a package resource."""
115+
116+
path = kind
117+
118+
if subdir is not None:
119+
path += "/" + subdir
120+
121+
path += f"/{name}"
122+
123+
return f"package://{package}/{path}.{kind}"
124+
125+
126+
WORKER_TYPE = "text/js-worker"
127+
128+
129+
def append_kind(
130+
element: Element,
131+
*names: str,
132+
package: str = PKG_NAME,
133+
kind: str = "js",
134+
tag: str = "script",
135+
subdir: str = None,
136+
worker: bool = False,
137+
) -> Optional[Element]:
138+
"""Append a new script element."""
139+
140+
elem = Element(tag=tag, allow_no_end_tag=False)
141+
142+
with StringIO() as stream:
143+
writer = IndentedFileWriter(stream, per_indent=2)
144+
found_count = 0
145+
for name in names:
146+
if write_found_file(
147+
writer, kind_url(kind, name, subdir=subdir, package=package)
148+
):
149+
found_count += 1
150+
151+
if worker:
152+
found_count += handle_worker(writer)
153+
154+
if found_count:
155+
elem.text = stream.getvalue()
156+
157+
if found_count:
158+
element.children.append(elem)
159+
160+
if worker:
161+
elem["type"] = WORKER_TYPE
162+
163+
return elem if found_count else None

runtimepy/net/server/app/bootstrap/elements.py renamed to runtimepy/net/html/bootstrap/elements.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from vcorelib.io.file_writer import IndentedFileWriter
1313

1414
# internal
15-
from runtimepy.net.server.app.bootstrap import icon_str
15+
from runtimepy.net.html.bootstrap import icon_str
1616

1717
TEXT = "font-monospace"
1818
BOOTSTRAP_BUTTON = f"rounded-0 {TEXT} button-bodge text-nowrap"
@@ -167,7 +167,7 @@ def slider(
167167

168168
def centered_markdown(
169169
parent: Element, markdown: str, *container_classes: str
170-
) -> None:
170+
) -> Element:
171171
"""Add centered markdown."""
172172

173173
container = div(parent=parent)
@@ -198,3 +198,5 @@ def centered_markdown(
198198
div(parent=horiz_container)
199199

200200
div(parent=container)
201+
202+
return container

runtimepy/net/server/app/bootstrap/tabs.py renamed to runtimepy/net/html/bootstrap/tabs.py

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@
88

99
# internal
1010
from runtimepy import PKG_NAME
11-
from runtimepy.net.server.app.bootstrap import icon_str
12-
from runtimepy.net.server.app.bootstrap.elements import (
11+
from runtimepy.net.html import create_app_shell
12+
from runtimepy.net.html.bootstrap.elements import (
1313
BOOTSTRAP_BUTTON,
14-
bootstrap_button,
1514
collapse_button,
1615
flex,
1716
toggle_button,
@@ -84,27 +83,7 @@ def __init__(self, name: str, parent: Element) -> None:
8483
"""Initialize this instance."""
8584

8685
self.name = name
87-
88-
# Create application container.
89-
self.container = div(parent=parent, id=name)
90-
self.container.add_class("d-flex", "align-items-start", "bg-body")
91-
92-
# Dark theme.
93-
self.container["data-bs-theme"] = "dark"
94-
95-
# Buttons.
96-
self.button_column = div(parent=self.container)
97-
self.button_column.add_class(
98-
"d-flex", "flex-column", "h-100", "bg-dark-subtle"
99-
)
100-
101-
# Dark/light theme switch button.
102-
bootstrap_button(
103-
icon_str("lightbulb"),
104-
tooltip=" Toggle light/dark.",
105-
id="theme-button",
106-
parent=self.button_column,
107-
)
86+
self.container, self.button_column = create_app_shell(parent, id=name)
10887

10988
# Toggle tabs button.
11089
self.add_button("Toggle tabs", f"#{PKG_NAME}-tabs", id="tabs-button")

0 commit comments

Comments
 (0)