Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions DEPENDENCIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@
- three: 0.168.0 ([MIT](https://opensource.org/licenses/MIT))
- tween: 25.0.0 ([MIT](https://opensource.org/licenses/MIT))
- vanilla-jsoneditor: 0.23.8 ([ISC](https://opensource.org/licenses/ISC))
- unocss_runtime: 66.1.4 ([MIT](https://opensource.org/licenses/MIT))
- unocss_reset: 66.1.4 ([MIT](https://opensource.org/licenses/MIT))
3 changes: 2 additions & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,5 @@ def _status():


# NOTE: do not reload on fly.io (see https://github.com/zauberzeug/nicegui/discussions/1720#discussioncomment-7288741)
ui.run(uvicorn_reload_includes='*.py, *.css, *.html', reload=not on_fly, reconnect_timeout=10.0)
ui.run(uvicorn_reload_includes='*.py, *.css, *.html', reload=not on_fly,
reconnect_timeout=10.0, tailwind=False, unocss_preset='wind3')
5 changes: 5 additions & 0 deletions nicegui/app/app_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class AppConfig:
message_history_length: int = field(init=False)
cache_control_directives: str = field(init=False)
tailwind: bool = field(init=False)
unocss_preset: Optional[Literal['mini', 'wind3', 'wind4']] = field(init=False)
prod_js: bool = field(init=False)
show_welcome_message: bool = field(init=False)
_has_run_config: bool = False
Expand All @@ -52,10 +53,13 @@ def add_run_config(self,
message_history_length: int,
cache_control_directives: str = 'public, max-age=31536000, immutable, stale-while-revalidate=31536000',
tailwind: bool,
unocss_preset: Optional[Literal['mini', 'wind3', 'wind4']] = None,
prod_js: bool,
show_welcome_message: bool,
) -> None:
"""Add the run config to the app config."""
if unocss_preset is not None and tailwind:
raise ValueError('unocss_preset and tailwind are mutually exclusive')
self.reload = reload
self.title = title
self.viewport = viewport
Expand All @@ -67,6 +71,7 @@ def add_run_config(self,
self.message_history_length = message_history_length
self.cache_control_directives = cache_control_directives
self.tailwind = tailwind
self.unocss_preset = unocss_preset
self.prod_js = prod_js
self.show_welcome_message = show_welcome_message
self._has_run_config = True
Expand Down
4 changes: 4 additions & 0 deletions nicegui/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from .dependencies import generate_resources
from .element import Element
from .favicon import get_favicon_url
from .helpers import unocss_filenames, unocss_initlines
from .javascript_request import JavaScriptRequest
from .logging import log
from .observables import ObservableDict
Expand Down Expand Up @@ -164,6 +165,9 @@ def build_response(self, request: Request, status_code: int = 200) -> Response:
'translations': translations.get(self.page.resolve_language(), translations['en-US']),
'prefix': prefix,
'tailwind': core.app.config.tailwind,
'unocss': core.app.config.unocss_preset is not None,
'unocss_filename': unocss_filenames.get(core.app.config.unocss_preset, '') if core.app.config.unocss_preset is not None else '',
'unocss_initline': unocss_initlines.get(core.app.config.unocss_preset, '') if core.app.config.unocss_preset is not None else '',
'prod_js': core.app.config.prod_js,
'socket_io_js_query_params': socket_io_js_query_params,
'socket_io_js_extra_headers': core.app.config.socket_io_js_extra_headers,
Expand Down
3 changes: 3 additions & 0 deletions nicegui/elements/dark_mode.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ export default {
if (this.value) document.body.classList.add("dark");
else document.body.classList.remove("dark");
}
if (window.__unocss) {
window.nicegui_unocss_dark = this.value;
}
},
},
props: {
Expand Down
13 changes: 13 additions & 0 deletions nicegui/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,16 @@ def kebab_to_camel_case(string: str) -> str:
def event_type_to_camel_case(string: str) -> str:
"""Convert an event type string to camelCase."""
return '.'.join(kebab_to_camel_case(part) if part != '-' else part for part in string.split('.'))


unocss_filenames = {
'mini': 'preset-mini',
'wind3': 'preset-wind', # until upstream renames this to preset-wind3.css, then update this
'wind4': 'preset-wind4',
}

unocss_initlines = {
'mini': '() => window.__unocss_runtime.presets.presetMini()',
'wind3': '() => window.__unocss_runtime.presets.presetWind()', # until upstream renames this to presetWind3()
'wind4': '() => window.__unocss_runtime.presets.presetWind4()',
}
35 changes: 35 additions & 0 deletions nicegui/static/nicegui.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ let mounted_app = undefined;
const loaded_libraries = new Set();
const loaded_components = new Set();

const allClassesFromElements = new Set();

function parseElements(raw_elements) {
return JSON.parse(
raw_elements
Expand Down Expand Up @@ -319,9 +321,34 @@ window.onbeforeunload = function () {
sessionStorage.__nicegui_tab_closed = "true";
};

async function generateStylesFromClasses(classes) {
if (window.__unocss_runtime === undefined) return
const div = document.createElement("div");
div.className = Array.from(classes).join(" ");
const html = div.outerHTML;
await window.__unocss_runtime.extract(html);
// Wait until the style elements have been moved
await new Promise((resolve) => {
document.querySelectorAll('style[data-unocss-runtime-layer]:not([data-nicegui-moved])')
.forEach(style => {
document.head.appendChild(style);
style.setAttribute('data-nicegui-moved', 'true');
});
resolve();
});
}

function createApp(elements, options) {
Object.entries(elements).forEach(([_, element]) => replaceUndefinedAttributes(element));
setInterval(() => ack(), 3000);
Object.values(elements).forEach((element) => {
if (element.class) {
element.class.forEach((c) => allClassesFromElements.add(c));
}
});
generateStylesFromClasses(allClassesFromElements).then(() => {
document.querySelector(".nicegui-prevent-fouc").classList.remove("nicegui-prevent-fouc");
})
return (app = Vue.createApp({
data() {
return {
Expand Down Expand Up @@ -385,6 +412,14 @@ function createApp(elements, options) {
.map(([_, element]) => loadDependencies(element, options.prefix, options.version));
await Promise.all(loadPromises);

originalClassesCount = allClassesFromElements.size;
Object.entries(msg).forEach(([_, element]) => {
(element?.class || []).forEach((c) => allClassesFromElements.add(c));
});
if (allClassesFromElements.size > originalClassesCount) {
await generateStylesFromClasses(allClassesFromElements);
}

for (const [id, element] of Object.entries(msg)) {
if (element === null) {
delete this.elements[id];
Expand Down
95 changes: 58 additions & 37 deletions nicegui/static/tailwindcss.min.js

Large diffs are not rendered by default.

Loading