composeui 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- composeui-0.1.0/.gitignore +20 -0
- composeui-0.1.0/.python-version +1 -0
- composeui-0.1.0/AGENTS.md +206 -0
- composeui-0.1.0/CHANGELOG.md +30 -0
- composeui-0.1.0/LICENSE +9 -0
- composeui-0.1.0/MANIFEST.in +7 -0
- composeui-0.1.0/Makefile +206 -0
- composeui-0.1.0/PKG-INFO +222 -0
- composeui-0.1.0/README.md +165 -0
- composeui-0.1.0/ROADMAP.md +252 -0
- composeui-0.1.0/css/app.css +438 -0
- composeui-0.1.0/css/uPlot.min.css +1 -0
- composeui-0.1.0/docs/_ext/widgetcatalog.py +78 -0
- composeui-0.1.0/docs/_site/llms.txt +46 -0
- composeui-0.1.0/docs/_site/skills/composeui/SKILL.md +140 -0
- composeui-0.1.0/docs/_static/composeui-icon.svg +17 -0
- composeui-0.1.0/docs/_static/composeui-logo-dark.svg +19 -0
- composeui-0.1.0/docs/_static/composeui-logo-light.svg +19 -0
- composeui-0.1.0/docs/api/application.md +5 -0
- composeui-0.1.0/docs/api/backends.md +23 -0
- composeui-0.1.0/docs/api/chartimage.md +5 -0
- composeui-0.1.0/docs/api/dataframesource.md +5 -0
- composeui-0.1.0/docs/api/index.md +31 -0
- composeui-0.1.0/docs/api/jobs.md +5 -0
- composeui-0.1.0/docs/api/model.md +5 -0
- composeui-0.1.0/docs/api/registry.md +5 -0
- composeui-0.1.0/docs/api/reload.md +5 -0
- composeui-0.1.0/docs/api/renderer.md +5 -0
- composeui-0.1.0/docs/api/signal.md +5 -0
- composeui-0.1.0/docs/api/sqlitemodel.md +5 -0
- composeui-0.1.0/docs/api/study.md +5 -0
- composeui-0.1.0/docs/api/translation.md +5 -0
- composeui-0.1.0/docs/api/translationcheck.md +5 -0
- composeui-0.1.0/docs/api/view.md +5 -0
- composeui-0.1.0/docs/conf.py +83 -0
- composeui-0.1.0/docs/examples-guide.md +509 -0
- composeui-0.1.0/docs/index.md +92 -0
- composeui-0.1.0/docs/screenshots.py +478 -0
- composeui-0.1.0/docs/technical-guide.md +608 -0
- composeui-0.1.0/docs/user-guide/building-views.md +371 -0
- composeui-0.1.0/docs/user-guide/charts.md +153 -0
- composeui-0.1.0/docs/user-guide/coding-agents.md +33 -0
- composeui-0.1.0/docs/user-guide/core-concepts.md +52 -0
- composeui-0.1.0/docs/user-guide/csv-import.md +210 -0
- composeui-0.1.0/docs/user-guide/maps.md +97 -0
- composeui-0.1.0/docs/user-guide/organizing-the-window.md +459 -0
- composeui-0.1.0/docs/user-guide/running-the-app.md +248 -0
- composeui-0.1.0/docs/user-guide/sqlite-model.md +153 -0
- composeui-0.1.0/docs/user-guide/tables.md +417 -0
- composeui-0.1.0/docs/user-guide/testing.md +274 -0
- composeui-0.1.0/docs/user-guide/threads.md +207 -0
- composeui-0.1.0/docs/user-guide/translation.md +107 -0
- composeui-0.1.0/docs/user-guide/widgets.md +247 -0
- composeui-0.1.0/docs/user-guide.md +132 -0
- composeui-0.1.0/docs/widgets.md +24 -0
- composeui-0.1.0/make.ps1 +349 -0
- composeui-0.1.0/pyproject.toml +223 -0
- composeui-0.1.0/setup.cfg +4 -0
- composeui-0.1.0/src/composeui/__init__.py +11 -0
- composeui-0.1.0/src/composeui/core/__init__.py +0 -0
- composeui-0.1.0/src/composeui/core/application.py +368 -0
- composeui-0.1.0/src/composeui/core/chartcolors.py +23 -0
- composeui-0.1.0/src/composeui/core/chartimage.py +336 -0
- composeui-0.1.0/src/composeui/core/dataframesource.py +307 -0
- composeui-0.1.0/src/composeui/core/jobs.py +426 -0
- composeui-0.1.0/src/composeui/core/model.py +66 -0
- composeui-0.1.0/src/composeui/core/registry.py +66 -0
- composeui-0.1.0/src/composeui/core/reload.py +476 -0
- composeui-0.1.0/src/composeui/core/renderer.py +193 -0
- composeui-0.1.0/src/composeui/core/signal.py +358 -0
- composeui-0.1.0/src/composeui/core/sqlitemodel.py +1183 -0
- composeui-0.1.0/src/composeui/core/study.py +121 -0
- composeui-0.1.0/src/composeui/core/translation.py +221 -0
- composeui-0.1.0/src/composeui/core/translationcheck.py +254 -0
- composeui-0.1.0/src/composeui/core/view.py +5508 -0
- composeui-0.1.0/src/composeui/images/composeui.svg +15 -0
- composeui-0.1.0/src/composeui/locales/fr.json +73 -0
- composeui-0.1.0/src/composeui/mypy.py +46 -0
- composeui-0.1.0/src/composeui/py.typed +0 -0
- composeui-0.1.0/src/composeui/qtcore/__init__.py +0 -0
- composeui-0.1.0/src/composeui/qtcore/maprenderer.py +115 -0
- composeui-0.1.0/src/composeui/qtcore/qtapplication.py +222 -0
- composeui-0.1.0/src/composeui/qtcore/qtrenderer.py +2862 -0
- composeui-0.1.0/src/composeui/qtcore/widgets/__init__.py +0 -0
- composeui-0.1.0/src/composeui/qtcore/widgets/map.qml +123 -0
- composeui-0.1.0/src/composeui/qtcore/widgets/qchart.py +393 -0
- composeui-0.1.0/src/composeui/qtcore/widgets/qdatefield.py +101 -0
- composeui-0.1.0/src/composeui/qtcore/widgets/qexpander.py +60 -0
- composeui-0.1.0/src/composeui/qtcore/widgets/qfilepathfield.py +34 -0
- composeui-0.1.0/src/composeui/qtcore/widgets/qfiltertable.py +394 -0
- composeui-0.1.0/src/composeui/qtcore/widgets/qformdialog.py +64 -0
- composeui-0.1.0/src/composeui/qtcore/widgets/qformlabel.py +53 -0
- composeui-0.1.0/src/composeui/qtcore/widgets/qmap.py +68 -0
- composeui-0.1.0/src/composeui/qtcore/widgets/qmarkicon.py +37 -0
- composeui-0.1.0/src/composeui/qtcore/widgets/qmetric.py +44 -0
- composeui-0.1.0/src/composeui/qtcore/widgets/qplaintext.py +29 -0
- composeui-0.1.0/src/composeui/qtcore/widgets/qradiogroup.py +57 -0
- composeui-0.1.0/src/composeui/qtcore/widgets/qsidebar.py +116 -0
- composeui-0.1.0/src/composeui/qtcore/widgets/qsourcetable.py +216 -0
- composeui-0.1.0/src/composeui/qtcore/widgets/qsplitter.py +69 -0
- composeui-0.1.0/src/composeui/qtcore/widgets/qtexttable.py +703 -0
- composeui-0.1.0/src/composeui/qtcore/widgets/qvaluecombobox.py +41 -0
- composeui-0.1.0/src/composeui/qtcore/widgets/qvalueslider.py +64 -0
- composeui-0.1.0/src/composeui/qtcore/widgets/qwindowrelease.py +32 -0
- composeui-0.1.0/src/composeui/textualcore/__init__.py +0 -0
- composeui-0.1.0/src/composeui/textualcore/textualapplication.py +121 -0
- composeui-0.1.0/src/composeui/textualcore/textualrenderer.py +2893 -0
- composeui-0.1.0/src/composeui/textualcore/widgets/__init__.py +0 -0
- composeui-0.1.0/src/composeui/textualcore/widgets/composebutton.py +17 -0
- composeui-0.1.0/src/composeui/textualcore/widgets/composechart.py +212 -0
- composeui-0.1.0/src/composeui/textualcore/widgets/composecheckbox.py +35 -0
- composeui-0.1.0/src/composeui/textualcore/widgets/composecollapsible.py +41 -0
- composeui-0.1.0/src/composeui/textualcore/widgets/composecombobox.py +54 -0
- composeui-0.1.0/src/composeui/textualcore/widgets/composedateinput.py +325 -0
- composeui-0.1.0/src/composeui/textualcore/widgets/composedialog.py +90 -0
- composeui-0.1.0/src/composeui/textualcore/widgets/composefilepathinput.py +85 -0
- composeui-0.1.0/src/composeui/textualcore/widgets/composefiltertable.py +367 -0
- composeui-0.1.0/src/composeui/textualcore/widgets/composeformdialog.py +86 -0
- composeui-0.1.0/src/composeui/textualcore/widgets/composeinput.py +50 -0
- composeui-0.1.0/src/composeui/textualcore/widgets/composelist.py +20 -0
- composeui-0.1.0/src/composeui/textualcore/widgets/composemarkdown.py +17 -0
- composeui-0.1.0/src/composeui/textualcore/widgets/composemetric.py +60 -0
- composeui-0.1.0/src/composeui/textualcore/widgets/composeradioset.py +54 -0
- composeui-0.1.0/src/composeui/textualcore/widgets/composesidebar.py +111 -0
- composeui-0.1.0/src/composeui/textualcore/widgets/composeslider.py +144 -0
- composeui-0.1.0/src/composeui/textualcore/widgets/composesourcetable.py +288 -0
- composeui-0.1.0/src/composeui/textualcore/widgets/composesplitter.py +211 -0
- composeui-0.1.0/src/composeui/textualcore/widgets/composetabbedcontent.py +35 -0
- composeui-0.1.0/src/composeui/textualcore/widgets/composetable.py +476 -0
- composeui-0.1.0/src/composeui/textualcore/widgets/composetoolbar.py +62 -0
- composeui-0.1.0/src/composeui/textualcore/widgets/composetree.py +139 -0
- composeui-0.1.0/src/composeui/webcore/__init__.py +0 -0
- composeui-0.1.0/src/composeui/webcore/headers.py +57 -0
- composeui-0.1.0/src/composeui/webcore/icons.py +58 -0
- composeui-0.1.0/src/composeui/webcore/limits.py +55 -0
- composeui-0.1.0/src/composeui/webcore/origin.py +63 -0
- composeui-0.1.0/src/composeui/webcore/page.py +146 -0
- composeui-0.1.0/src/composeui/webcore/routes.py +303 -0
- composeui-0.1.0/src/composeui/webcore/session.py +436 -0
- composeui-0.1.0/src/composeui/webcore/static/app.css +2 -0
- composeui-0.1.0/src/composeui/webcore/static/cally.js +1312 -0
- composeui-0.1.0/src/composeui/webcore/static/daisyui.LICENSE +21 -0
- composeui-0.1.0/src/composeui/webcore/static/deckgl.LICENSE +19 -0
- composeui-0.1.0/src/composeui/webcore/static/deckgl.min.js +6423 -0
- composeui-0.1.0/src/composeui/webcore/static/htmx.min.js +1 -0
- composeui-0.1.0/src/composeui/webcore/static/page.js +1175 -0
- composeui-0.1.0/src/composeui/webcore/static/uPlot.LICENSE +21 -0
- composeui-0.1.0/src/composeui/webcore/static/uPlot.iife.min.js +2 -0
- composeui-0.1.0/src/composeui/webcore/webapplication.py +495 -0
- composeui-0.1.0/src/composeui/webcore/webrenderer.py +5113 -0
- composeui-0.1.0/src/composeui.egg-info/PKG-INFO +222 -0
- composeui-0.1.0/src/composeui.egg-info/SOURCES.txt +249 -0
- composeui-0.1.0/src/composeui.egg-info/dependency_links.txt +1 -0
- composeui-0.1.0/src/composeui.egg-info/requires.txt +30 -0
- composeui-0.1.0/src/composeui.egg-info/scm_file_list.json +291 -0
- composeui-0.1.0/src/composeui.egg-info/scm_version.json +8 -0
- composeui-0.1.0/src/composeui.egg-info/top_level.txt +1 -0
- composeui-0.1.0/src/examples/__init__.py +0 -0
- composeui-0.1.0/src/examples/batman.py +140 -0
- composeui-0.1.0/src/examples/largetable.py +219 -0
- composeui-0.1.0/src/examples/locales/pipeform/fr.json +37 -0
- composeui-0.1.0/src/examples/locales/pipesweep/fr.json +22 -0
- composeui-0.1.0/src/examples/locales/taskview/fr.json +31 -0
- composeui-0.1.0/src/examples/markdownviewer.py +91 -0
- composeui-0.1.0/src/examples/pipecatalogue.py +584 -0
- composeui-0.1.0/src/examples/pipechart.py +455 -0
- composeui-0.1.0/src/examples/pipeform.py +356 -0
- composeui-0.1.0/src/examples/pipesweep.py +292 -0
- composeui-0.1.0/src/examples/py.typed +0 -0
- composeui-0.1.0/src/examples/taskview.py +321 -0
- composeui-0.1.0/src/examples/textual_demo.py +79 -0
- composeui-0.1.0/src/examples/uberpickups.py +208 -0
- composeui-0.1.0/src/examples/uberpickupsframe.py +136 -0
- composeui-0.1.0/src/examples/webhello.py +141 -0
- composeui-0.1.0/tests/__init__.py +0 -0
- composeui-0.1.0/tests/bookings.py +28 -0
- composeui-0.1.0/tests/buildings.py +83 -0
- composeui-0.1.0/tests/charts.py +93 -0
- composeui-0.1.0/tests/datafiles.py +25 -0
- composeui-0.1.0/tests/dialogs.py +55 -0
- composeui-0.1.0/tests/expander.py +45 -0
- composeui-0.1.0/tests/formdialog.py +71 -0
- composeui-0.1.0/tests/grid.py +44 -0
- composeui-0.1.0/tests/horizontal.py +72 -0
- composeui-0.1.0/tests/hours.py +22 -0
- composeui-0.1.0/tests/listview.py +80 -0
- composeui-0.1.0/tests/locales/fr.json +11 -0
- composeui-0.1.0/tests/maps.py +68 -0
- composeui-0.1.0/tests/markdown.py +51 -0
- composeui-0.1.0/tests/markup.py +47 -0
- composeui-0.1.0/tests/metrics.py +23 -0
- composeui-0.1.0/tests/pages.py +75 -0
- composeui-0.1.0/tests/partial.py +118 -0
- composeui-0.1.0/tests/sidebar.py +36 -0
- composeui-0.1.0/tests/splitter.py +72 -0
- composeui-0.1.0/tests/tables.py +280 -0
- composeui-0.1.0/tests/tabs.py +46 -0
- composeui-0.1.0/tests/test_application.py +311 -0
- composeui-0.1.0/tests/test_batman.py +27 -0
- composeui-0.1.0/tests/test_browser.py +2377 -0
- composeui-0.1.0/tests/test_chartimage.py +224 -0
- composeui-0.1.0/tests/test_csv_typing.py +141 -0
- composeui-0.1.0/tests/test_csv_url.py +130 -0
- composeui-0.1.0/tests/test_csvsqlitetable.py +561 -0
- composeui-0.1.0/tests/test_dataframe_source.py +226 -0
- composeui-0.1.0/tests/test_dialog.py +57 -0
- composeui-0.1.0/tests/test_docs.py +189 -0
- composeui-0.1.0/tests/test_examples.py +65 -0
- composeui-0.1.0/tests/test_form_dialog.py +84 -0
- composeui-0.1.0/tests/test_form_dialog_typing.py +80 -0
- composeui-0.1.0/tests/test_grid.py +39 -0
- composeui-0.1.0/tests/test_jobs.py +639 -0
- composeui-0.1.0/tests/test_largetable.py +200 -0
- composeui-0.1.0/tests/test_list_view.py +96 -0
- composeui-0.1.0/tests/test_llms_txt.py +37 -0
- composeui-0.1.0/tests/test_maps.py +108 -0
- composeui-0.1.0/tests/test_markdownviewer.py +118 -0
- composeui-0.1.0/tests/test_pipecatalogue.py +328 -0
- composeui-0.1.0/tests/test_pipechart.py +158 -0
- composeui-0.1.0/tests/test_pipesweep.py +193 -0
- composeui-0.1.0/tests/test_pipeview.py +192 -0
- composeui-0.1.0/tests/test_qtapplication.py +38 -0
- composeui-0.1.0/tests/test_qtrenderer.py +3788 -0
- composeui-0.1.0/tests/test_record_table.py +245 -0
- composeui-0.1.0/tests/test_reload.py +1010 -0
- composeui-0.1.0/tests/test_renderer.py +69 -0
- composeui-0.1.0/tests/test_renderer_registry.py +77 -0
- composeui-0.1.0/tests/test_router.py +104 -0
- composeui-0.1.0/tests/test_signal.py +158 -0
- composeui-0.1.0/tests/test_source_table.py +543 -0
- composeui-0.1.0/tests/test_sqlite_source.py +353 -0
- composeui-0.1.0/tests/test_sqlitemodel.py +980 -0
- composeui-0.1.0/tests/test_statusbar_thread_safety.py +63 -0
- composeui-0.1.0/tests/test_table_looks.py +78 -0
- composeui-0.1.0/tests/test_table_sort_filter.py +300 -0
- composeui-0.1.0/tests/test_table_source.py +158 -0
- composeui-0.1.0/tests/test_taskview.py +214 -0
- composeui-0.1.0/tests/test_textual_renderer.py +3955 -0
- composeui-0.1.0/tests/test_translation.py +390 -0
- composeui-0.1.0/tests/test_translationcheck.py +206 -0
- composeui-0.1.0/tests/test_uberpickups.py +323 -0
- composeui-0.1.0/tests/test_uberpickupsframe.py +154 -0
- composeui-0.1.0/tests/test_validation.py +131 -0
- composeui-0.1.0/tests/test_view_state.py +872 -0
- composeui-0.1.0/tests/test_views.py +198 -0
- composeui-0.1.0/tests/test_webrenderer.py +5680 -0
- composeui-0.1.0/tests/toolbar.py +49 -0
- composeui-0.1.0/tests/translated.py +88 -0
- composeui-0.1.0/tests/unlabelled.py +20 -0
- composeui-0.1.0/tests/vertical.py +46 -0
- composeui-0.1.0/tests/workers.py +32 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Python-generated files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[oc]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv
|
|
11
|
+
|
|
12
|
+
# coverage
|
|
13
|
+
*.coverage
|
|
14
|
+
|
|
15
|
+
*.pipe
|
|
16
|
+
# downloaded by `make css`, not committed
|
|
17
|
+
css/vendor/
|
|
18
|
+
|
|
19
|
+
# built by `make docs`
|
|
20
|
+
docs/_build/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.9
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
ComposeUI is a small, framework-agnostic MVVM layer for building UIs in Python. Views are
|
|
4
|
+
typed `attrs` classes; a `Renderer` binds each view to a toolkit widget; `Signal` objects
|
|
5
|
+
carry events between view, model and application code. See `README.md` for the overview and
|
|
6
|
+
quickstart, `docs/user-guide/` for each feature, `docs/examples-guide.md` for the example apps, and
|
|
7
|
+
`docs/technical-guide.md` for the architecture and development.
|
|
8
|
+
|
|
9
|
+
## Setup
|
|
10
|
+
|
|
11
|
+
- Package manager: [uv](https://docs.astral.sh/uv/). Python `>=3.9` (`.python-version` pins 3.9).
|
|
12
|
+
- Install everything needed for development:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
uv sync --group dev --all-extras # extras: pyside6, tui (Textual), web (Starlette), report, reload, dataframe
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
- Always run tools through `uv run` (or the `make` targets below), never a global Python.
|
|
19
|
+
|
|
20
|
+
## Commands
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
make lint # ruff format + ruff check --fix
|
|
24
|
+
make mypy # mypy on composeui, examples and tests, Qt binding resolved to PySide6
|
|
25
|
+
make typecheck # mypy on source + tests; Pyright, Pyrefly and ty on tests
|
|
26
|
+
make pyright pyrefly ty # individual test checkers
|
|
27
|
+
make zuban # optional test checker; currently reports known incompatibilities
|
|
28
|
+
make test # pytest tests in parallel, Qt offscreen, with coverage (browser tests excluded)
|
|
29
|
+
make browser-test # the web backend in headless Chromium (tests/test_browser.py, Playwright)
|
|
30
|
+
make translations # each app's catalogs against the text it translates (TRANSLATED_MAIN_VIEWS)
|
|
31
|
+
make check # lint + mypy + translations
|
|
32
|
+
make all # clean + check + test
|
|
33
|
+
make css # rebuild webcore/static/app.css after changing classes in webcore (commit it)
|
|
34
|
+
make web-deps # htmx, uPlot, Cally and deck.gl from npm at pinned versions and SHA-256 (commit them)
|
|
35
|
+
make docs # Sphinx + MyST site in docs/_build/html, warnings as errors (needs `--group docs`)
|
|
36
|
+
make screenshots # docs/images/<example>-<backend>.png for the docs and README (Chromium)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
On Windows without `make`, run the same targets in PowerShell with `make.ps1`
|
|
40
|
+
(`.\make.ps1 check test`; `-Examples pipeform, batman` for `EXAMPLES`, `-TailwindPlatform`
|
|
41
|
+
for `TAILWIND_PLATFORM`). Keep it in step with the `Makefile`, including its
|
|
42
|
+
`$TranslatedMainViews` list (`TRANSLATED_MAIN_VIEWS`).
|
|
43
|
+
|
|
44
|
+
`make mypy` checks source, examples and tests together. It installs the `browser` group and
|
|
45
|
+
all extras so every test import resolves; Chromium is not needed. The other checker targets
|
|
46
|
+
also install the `typecheck` group. The same targets are available in `make.ps1`.
|
|
47
|
+
Zuban is excluded from the aggregate target and retains its nonzero exit status when
|
|
48
|
+
diagnostics remain.
|
|
49
|
+
|
|
50
|
+
Run a single test file or test:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
QT_QPA_PLATFORM=offscreen uv run pytest tests/test_qtrenderer.py -k combobox
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`QT_QPA_PLATFORM=offscreen` is required for Qt tests in a headless environment (CI, WSL,
|
|
57
|
+
agents). Run the examples with `uv run python -m examples.pipeform --backend qt` (or
|
|
58
|
+
`textual`, or `web`) or `uv run python -m examples.textual_demo` (Textual).
|
|
59
|
+
|
|
60
|
+
## Project layout
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
src/composeui/
|
|
64
|
+
core/ # toolkit-agnostic: views, signals, renderer base, application, registry
|
|
65
|
+
qtcore/ # Qt backend via qtpy (QtApplication, qtrenderer.py, custom widgets/)
|
|
66
|
+
textualcore/ # Textual (TUI) backend
|
|
67
|
+
webcore/ # Web backend (Starlette + htmx + daisyUI); static/app.css is built
|
|
68
|
+
css/ # source of webcore/static/app.css (`make css`; tools land in css/vendor/)
|
|
69
|
+
src/examples/ # example apps (pipeform.py, pipecatalogue.py, ...), also used by tests; not in the wheel
|
|
70
|
+
tests/ # pytest suite (pytest-qt, pytest-asyncio, Textual run_test pilot)
|
|
71
|
+
docs/ # user guide, examples guide, technical guide; Sphinx site (conf.py, api/, _ext/)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Architecture rules
|
|
75
|
+
|
|
76
|
+
- **`composeui.core` must never import a backend** (Qt, Textual, Starlette) at import time.
|
|
77
|
+
Backends are resolved lazily through `composeui.core.registry`.
|
|
78
|
+
- **Backend registration:** each `RendererType` member's value is a `Backend`: the dotted
|
|
79
|
+
paths of the backend's renderers module, exposing a
|
|
80
|
+
`RENDERERS: dict[str, Callable[[Any], Renderer]]` mapping, and of its application module,
|
|
81
|
+
exposing `APPLICATION` and `MODEL_APPLICATION` (what `core.application.run` starts),
|
|
82
|
+
plus the extra that installs its dependencies and their top-level modules (`run` names
|
|
83
|
+
the extra when one is missing). A view picks its renderer by its `_renderer_key` class variable. A backend that doesn't
|
|
84
|
+
register a key simply doesn't render that view (returns `None`), so partial backends are
|
|
85
|
+
fine.
|
|
86
|
+
- **Adding a widget:** define the view in `core/view.py` (set `_renderer_key`), then add a
|
|
87
|
+
renderer + `RENDERERS` entry in each backend that should support it, plus tests per backend.
|
|
88
|
+
- **Adding a backend:** add one `RendererType` member in `core/registry.py` (with its extra
|
|
89
|
+
in `pyproject.toml`), one
|
|
90
|
+
`RENDERERS` mapping in the new backend's renderers module, and its application classes
|
|
91
|
+
(built from `build_main_view=`, plus `build_model=`/`setup_model=` for the model one) as
|
|
92
|
+
`APPLICATION`/`MODEL_APPLICATION`. `core/view.py` should not need changes.
|
|
93
|
+
- **Renderer contract:** implement `render`, `refresh_attribute` and the
|
|
94
|
+
`_refresh_dispatcher` (one handler per view attribute the renderer cares about; a missing
|
|
95
|
+
handler raises `MissingRefreshAttributeFunctionError`). The user's changes reach the view
|
|
96
|
+
from the widget's signals, set inside `stop_refresh()`.
|
|
97
|
+
- Views are reactive: setting a view attribute pushes the change to its renderer. Don't add
|
|
98
|
+
manual `update()`/`refresh()` calls.
|
|
99
|
+
- `Signal.connect` injects `main_view` and `model`, and `Application.connect` also `view`, as
|
|
100
|
+
keyword arguments based on the callback's parameter names; keep those names exact.
|
|
101
|
+
- View state is (de)serialized with `cattrs` (`MainView.save_state` / `load_state`); new view
|
|
102
|
+
fields must remain serializable, or be excluded like `Signal`/`Renderer`/`Validator` fields.
|
|
103
|
+
|
|
104
|
+
## Code style
|
|
105
|
+
|
|
106
|
+
- `ruff` with `select = ["ALL"]` and line length 100; see `pyproject.toml` for the ignore list.
|
|
107
|
+
Fix lint properly rather than adding `noqa`, unless it is clearly justified.
|
|
108
|
+
- `mypy` must pass. Fully type-annotate new code; the package ships `py.typed`.
|
|
109
|
+
- Every Python module starts with `from __future__ import annotations` (after its docstring).
|
|
110
|
+
Ruff enforces it (`required-imports`, rule I002) and `make lint` adds it to a new file.
|
|
111
|
+
Annotations are then strings, so: don't quote them; define views that `attrs`/`cattrs`
|
|
112
|
+
resolve (`attrs.resolve_types`) at module level, not inside a function, so their field
|
|
113
|
+
types resolve from the module globals; and give `cattrs` hooks their type explicitly
|
|
114
|
+
(`CONVERTER.register_structure_hook(cls, func)`) rather than as a bare decorator, which
|
|
115
|
+
reads the annotation.
|
|
116
|
+
- Stay compatible with Python 3.9: use `Optional[...]`/`Union[...]`, not `X | Y`, in
|
|
117
|
+
annotations that `attrs`/`cattrs` evaluate at runtime (view fields), since `X | Y` fails
|
|
118
|
+
there on 3.9 (ruff's `keep-runtime-typing` stops pyupgrade from rewriting them).
|
|
119
|
+
- Use `qtpy` imports, never `PySide6`/`PyQt*` directly, in `qtcore`.
|
|
120
|
+
- In `webcore`, build HTML with `Markup(template).format(...)`, never an f-string: it
|
|
121
|
+
escapes every value (see the `web-security` skill).
|
|
122
|
+
- Put imports only needed for typing under `if TYPE_CHECKING:`.
|
|
123
|
+
- The public API is the `__all__` of each `composeui.core` module: the API reference shows
|
|
124
|
+
only it, and `tests/test_docs.py` checks that the docs and examples import nothing else.
|
|
125
|
+
Add a name that apps use to it. The other names, also those the backends import, can
|
|
126
|
+
change in any release.
|
|
127
|
+
- Match the existing comment density: brief docstrings, comments explain *why*.
|
|
128
|
+
|
|
129
|
+
## Permission for documentation and tests
|
|
130
|
+
|
|
131
|
+
- Ask the user for explicit permission before creating, editing, moving, or deleting
|
|
132
|
+
documentation or tests.
|
|
133
|
+
- Permission to implement a feature or fix a bug does not authorize documentation or test
|
|
134
|
+
changes. Ask first when those changes are needed by the rules below.
|
|
135
|
+
|
|
136
|
+
## Testing
|
|
137
|
+
|
|
138
|
+
- Every behavior change needs a test in `tests/`. Backend tests should exercise real rendered
|
|
139
|
+
widgets (`qtbot` for Qt, `App.run_test()` for Textual, `httpx`/Starlette test client for web).
|
|
140
|
+
- Tests for optional backends guard with `pytest.importorskip(...)`.
|
|
141
|
+
- `make test` runs the tests in parallel, a worker per CPU (pytest-xdist): a test must not
|
|
142
|
+
depend on another's state or order. Without `-n`, `pytest` runs them one after another.
|
|
143
|
+
- What only a browser shows (the page's scripts, htmx swaps) goes in `tests/test_browser.py`,
|
|
144
|
+
marked `browser`. It needs the `browser` group and a Chromium:
|
|
145
|
+
`uv run --group browser playwright install chromium` once, then `make browser-test`.
|
|
146
|
+
- `make test` fails below 100% coverage of lines and branches (`fail_under` in
|
|
147
|
+
`pyproject.toml`, child processes included). A new line comes with its test, or with a
|
|
148
|
+
`# pragma: no cover` that says why no test reaches it. A line only POSIX runs gets a
|
|
149
|
+
`# posix only` comment, which coverage leaves out on Windows (`make.ps1`).
|
|
150
|
+
- `make check test` must be clean before committing; CI (`.github/workflows/ci.yml`) runs the
|
|
151
|
+
same checks on Python 3.9 to 3.14 for every pull request and push to `main`, and the tests
|
|
152
|
+
on Windows (`.\make.ps1 translations test`) and macOS (`make translations test`), on
|
|
153
|
+
Python 3.9 and 3.14. A change of
|
|
154
|
+
the documentation only (`docs/`, Markdown files) runs only the docs build and its lint.
|
|
155
|
+
The existing mypy matrix also checks tests. Separate CI jobs run Pyright, Pyrefly and ty
|
|
156
|
+
on tests on Python 3.9. Zuban runs in an additional non-blocking job while its known
|
|
157
|
+
incompatibilities remain.
|
|
158
|
+
|
|
159
|
+
## Documentation
|
|
160
|
+
|
|
161
|
+
Load the `documentation-style` skill before you write or review documentation: `README.md`,
|
|
162
|
+
`docs/`, docstrings, `ROADMAP.md`, this file, or a skill. It holds the writing rules, which
|
|
163
|
+
document owns what, and the checks.
|
|
164
|
+
|
|
165
|
+
- **Change the docs in the commit that changes the behavior.** A change that users see gets
|
|
166
|
+
a line under "Unreleased" in `CHANGELOG.md`. A new view gets a section
|
|
167
|
+
in its page of `docs/user-guide/` and a class docstring, which the widget catalog shows. In
|
|
168
|
+
`ROADMAP.md`, add it to "Already there" and remove its entry. A new example gets its
|
|
169
|
+
section, its overview row, its contents entry, and its screenshots in
|
|
170
|
+
`docs/examples-guide.md`. When a name is renamed or changes, search `README.md`, `docs/`
|
|
171
|
+
and the docstrings for it, and fix every place.
|
|
172
|
+
- **Keep the path of a new user working:** the README's installation and quickstart, then
|
|
173
|
+
the user guide's "Core concepts" and "Building views", then "Where to start" in the
|
|
174
|
+
examples guide. Introduce a name before an example uses it, or link to its section. When
|
|
175
|
+
the quickstart changes, run `make screenshots EXAMPLES=quickstart`.
|
|
176
|
+
- **Run every code example that you change.** Check each name against `composeui.core`, and
|
|
177
|
+
run the example with `uv run python` in a scratch directory. `tests/test_docs.py` checks
|
|
178
|
+
the imports of every example, and runs each example that defines every name it uses (one
|
|
179
|
+
that defines `test_` functions, under pytest).
|
|
180
|
+
- **Keep the navigation in step.** `docs/_site/llms.txt` has one entry per page of the site: add,
|
|
181
|
+
rename, or remove it with the page. `docs/user-guide.md` lists the pages of
|
|
182
|
+
`docs/user-guide/` and their `##` and `###` headings. The contents list of the examples
|
|
183
|
+
guide follows its `##` headings. Keep headings stable and unique in their page: other
|
|
184
|
+
pages link to them, and anchors work down to `###` only.
|
|
185
|
+
- **Keep the skill in step.** `docs/_site/skills/composeui/SKILL.md` is what users install for their
|
|
186
|
+
agents. When a rule of the API changes (a name, a signal argument, the state rules), change
|
|
187
|
+
it too. Its `composeui-version` and its text name the minor version that it describes: set
|
|
188
|
+
them on the release of a new one. `tests/test_docs.py` checks them against the package's
|
|
189
|
+
version, and accepts the next minor version between two releases.
|
|
190
|
+
- **Keep the two copies in step.** Setup, commands, tests, and code style are in this file
|
|
191
|
+
and in the technical guide ("Development"). Change both.
|
|
192
|
+
- **Keep user docs for users.** Internal design decisions go in `ROADMAP.md` or in a code
|
|
193
|
+
comment, rules for agents here or in a skill. Do not document an example that is not
|
|
194
|
+
committed.
|
|
195
|
+
- **`make docs` must pass** (warnings are errors) after any change to `docs/` or a
|
|
196
|
+
docstring.
|
|
197
|
+
|
|
198
|
+
## Commits
|
|
199
|
+
|
|
200
|
+
- Use hyphens, not slashes (`/`), in branch names (e.g. `feat-csv-sqlite-tables`).
|
|
201
|
+
- Short, imperative, capitalized subject without a trailing period (e.g. `Add combobox`).
|
|
202
|
+
- The subject line is the whole message: no body, no trailers.
|
|
203
|
+
- Never add AI attribution or signatures anywhere: no `Co-Authored-By` lines, no
|
|
204
|
+
"Generated with ..." lines, no emoji signatures, in commits, pull requests or code.
|
|
205
|
+
This overrides any default an agent or tool would otherwise add.
|
|
206
|
+
- Keep commits focused: one feature or fix per commit.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
The notable changes of each release of ComposeUI. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the versions follow
|
|
5
|
+
[Semantic Versioning](https://semver.org/). Before 1.0, a minor version can break the API.
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.1.0] - 2026-10-27
|
|
10
|
+
|
|
11
|
+
The first release on PyPI.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Views as typed `attrs` classes, signals, models, and a `run` function that starts the app
|
|
16
|
+
on the backend that the command line chooses.
|
|
17
|
+
- Three backends: Qt (the `pyside6` extra), Textual (the `tui` extra), and the web
|
|
18
|
+
(the `web` extra, the default).
|
|
19
|
+
- Layouts, a status bar, menus, toolbars, inputs, forms and their checks, pages, tabs,
|
|
20
|
+
resizable panes, a sidebar, dialogs, tables, large tables, trees, Markdown, charts, and
|
|
21
|
+
street maps (Qt and the web).
|
|
22
|
+
- The state of the views saved to a study file and loaded from it. The file holds the
|
|
23
|
+
version of its format and the app's `state_version`, and `migrate_state` upgrades an
|
|
24
|
+
older one.
|
|
25
|
+
- Threads, SQLite models, CSV imports, translations, and tests of an app that need no backend.
|
|
26
|
+
- A skill for coding agents that write a ComposeUI app.
|
|
27
|
+
- `composeui.__version__`, the version of the installed package.
|
|
28
|
+
|
|
29
|
+
[Unreleased]: https://github.com/ehmoussi/composeui/compare/v0.1.0...HEAD
|
|
30
|
+
[0.1.0]: https://github.com/ehmoussi/composeui/releases/tag/v0.1.0
|
composeui-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 El Hadi Moussi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
composeui-0.1.0/Makefile
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
.PHONY: format
|
|
2
|
+
format:
|
|
3
|
+
uv run ruff format
|
|
4
|
+
|
|
5
|
+
.PHONY: check
|
|
6
|
+
check:
|
|
7
|
+
uv run ruff check
|
|
8
|
+
|
|
9
|
+
.PHONY: check_fix
|
|
10
|
+
check_fix:
|
|
11
|
+
uv run ruff check --fix
|
|
12
|
+
|
|
13
|
+
.PHONY: lint
|
|
14
|
+
lint: format check_fix
|
|
15
|
+
|
|
16
|
+
.PHONY: mypy
|
|
17
|
+
mypy:
|
|
18
|
+
uv run --locked --group browser --all-extras mypy \
|
|
19
|
+
--config-file pyproject.toml \
|
|
20
|
+
--package composeui \
|
|
21
|
+
--package examples \
|
|
22
|
+
--package tests \
|
|
23
|
+
--always-false=PYQT5 \
|
|
24
|
+
--always-false=PYSIDE2 \
|
|
25
|
+
--always-false=PYQT6 \
|
|
26
|
+
--always-true=PYSIDE6 \
|
|
27
|
+
--show-traceback
|
|
28
|
+
|
|
29
|
+
# Tests import every backend and Playwright; static checks need their packages, not Chromium.
|
|
30
|
+
TYPECHECK_RUN = uv run --locked --group typecheck --group browser --all-extras
|
|
31
|
+
|
|
32
|
+
.PHONY: typecheck pyright pyrefly ty zuban
|
|
33
|
+
typecheck: mypy pyright pyrefly ty
|
|
34
|
+
|
|
35
|
+
pyright:
|
|
36
|
+
$(TYPECHECK_RUN) pyright
|
|
37
|
+
|
|
38
|
+
pyrefly:
|
|
39
|
+
$(TYPECHECK_RUN) pyrefly check
|
|
40
|
+
|
|
41
|
+
ty:
|
|
42
|
+
$(TYPECHECK_RUN) ty check
|
|
43
|
+
|
|
44
|
+
# Keep its nonzero exit visible locally; CI runs Zuban as a non-blocking job.
|
|
45
|
+
zuban:
|
|
46
|
+
$(TYPECHECK_RUN) zuban mypy tests
|
|
47
|
+
|
|
48
|
+
# the main views of the apps with catalogs, as module:Class
|
|
49
|
+
TRANSLATED_MAIN_VIEWS := examples.pipeform:PipeMainView \
|
|
50
|
+
examples.pipesweep:PipeSweepMainView \
|
|
51
|
+
examples.taskview:TaskMainView
|
|
52
|
+
|
|
53
|
+
# every catalog against the text its app translates; each app reported before failing
|
|
54
|
+
.PHONY: translations
|
|
55
|
+
translations:
|
|
56
|
+
@status=0; for main_view in $(TRANSLATED_MAIN_VIEWS); do \
|
|
57
|
+
uv run python -m composeui.core.translationcheck $$main_view || status=1; \
|
|
58
|
+
done; exit $$status
|
|
59
|
+
|
|
60
|
+
.PHONY: check
|
|
61
|
+
check: lint mypy translations
|
|
62
|
+
|
|
63
|
+
.PHONY: test
|
|
64
|
+
# in parallel, a worker per CPU (pytest-xdist)
|
|
65
|
+
test:
|
|
66
|
+
QT_QPA_PLATFORM=offscreen uv run pytest tests -m "not browser" -n auto --cov=src --cov-report=term-missing:skip-covered
|
|
67
|
+
|
|
68
|
+
# the web backend in headless Chromium; skipped unless Chromium is installed:
|
|
69
|
+
# uv run --group browser playwright install chromium
|
|
70
|
+
# No pytest-qt: it refuses to start without a Qt binding, which these tests don't need.
|
|
71
|
+
.PHONY: browser-test
|
|
72
|
+
browser-test:
|
|
73
|
+
uv run --group browser pytest tests/test_browser.py -m browser -p no:pytest-qt
|
|
74
|
+
|
|
75
|
+
# the documentation site (Sphinx + MyST): the guides, the widget catalog and the API
|
|
76
|
+
# reference, in docs/_build/html; any warning fails it. The API reference imports every
|
|
77
|
+
# backend's application module: run `uv sync --group docs --all-extras` first.
|
|
78
|
+
.PHONY: docs
|
|
79
|
+
docs:
|
|
80
|
+
uv run --group docs sphinx-build -W --keep-going -b html docs docs/_build/html
|
|
81
|
+
|
|
82
|
+
# the screenshots of the examples guide, on every backend, into docs/images/; needs the
|
|
83
|
+
# browser group's Chromium (see browser-test). Pass names to take only some of them:
|
|
84
|
+
# make screenshots EXAMPLES="pipeform batman"
|
|
85
|
+
.PHONY: screenshots
|
|
86
|
+
screenshots:
|
|
87
|
+
QT_QPA_PLATFORM=offscreen uv run --group browser python docs/screenshots.py $(EXAMPLES)
|
|
88
|
+
|
|
89
|
+
# Tailwind's standalone CLI and daisyUI's plugin files: no Node/npm needed. Downloaded
|
|
90
|
+
# once into css/vendor/ (not committed); the built static/app.css is committed.
|
|
91
|
+
TAILWIND_VERSION := 4.3.3
|
|
92
|
+
DAISYUI_VERSION := 5.7.47
|
|
93
|
+
CSS_VENDOR := css/vendor
|
|
94
|
+
# Tailwind's release names for this machine: linux, macos or windows (Git Bash, MSYS2 or
|
|
95
|
+
# Cygwin report MINGW64_NT-..., MSYS_NT-..., CYGWIN_NT-...), then x64 or arm64. Override
|
|
96
|
+
# it for another build, e.g. `make css TAILWIND_PLATFORM=linux-x64-musl` on Alpine.
|
|
97
|
+
TAILWIND_PLATFORMS := linux-x64 linux-arm64 macos-x64 macos-arm64 linux-x64-musl \
|
|
98
|
+
linux-arm64-musl windows-x64
|
|
99
|
+
TAILWIND_PLATFORM ?= $(shell uname -s | sed -e 's/^Linux$$/linux/' -e 's/^Darwin$$/macos/' \
|
|
100
|
+
-e 's/^MINGW.*/windows/' -e 's/^MSYS.*/windows/' -e 's/^CYGWIN.*/windows/')-$(shell uname -m | sed -e 's/^x86_64$$/x64/' \
|
|
101
|
+
-e 's/^aarch64$$/arm64/')
|
|
102
|
+
# Windows only runs it as an .exe
|
|
103
|
+
TAILWIND_SUFFIX := $(if $(filter windows-%,$(TAILWIND_PLATFORM)),.exe)
|
|
104
|
+
# the platform in the name: a checkout shared between machines keeps one binary for each
|
|
105
|
+
TAILWIND := $(CSS_VENDOR)/tailwindcss-$(TAILWIND_VERSION)-$(TAILWIND_PLATFORM)$(TAILWIND_SUFFIX)
|
|
106
|
+
DAISYUI := $(CSS_VENDOR)/daisyui.mjs
|
|
107
|
+
DAISYUI_THEME := $(CSS_VENDOR)/daisyui-theme.mjs
|
|
108
|
+
# the SHA-256 of each download, from Tailwind's sha256sums.txt and the digests of daisyUI's
|
|
109
|
+
# release assets: change them with the versions
|
|
110
|
+
TAILWIND_SHA256_linux-x64 := dc61b3ac6b8c9ca874c0cc4c57b2409791a64c5540404ca5f5367360babc313a
|
|
111
|
+
TAILWIND_SHA256_linux-arm64 := 55fd0b241214eff3de1e8ee4f22796662f2d2e7a49bcfca7477cfd0bac398195
|
|
112
|
+
TAILWIND_SHA256_macos-x64 := 7922e0953f2110c05976e3bf58f14e643d90427575e766b7d433f5f80cbee7e1
|
|
113
|
+
TAILWIND_SHA256_macos-arm64 := cdf646702987a743464dff4d9c60fd4480d1c1e73dd819a9a67f1078815dce9d
|
|
114
|
+
TAILWIND_SHA256_linux-x64-musl := a04d34ceacc8f52cbe8920ad846cdeb61d3d0021dba32db0d1f77c9d9fad7a6c
|
|
115
|
+
TAILWIND_SHA256_linux-arm64-musl := 71ea4be79c9de9827545682df3e040053fb535d37c71ed2cfdedf9385a0868e0
|
|
116
|
+
TAILWIND_SHA256_windows-x64 := e0e260ce048014e9268f6237ff18f8ccf02cef521cbd0ae04e82c2cdf7aa3955
|
|
117
|
+
TAILWIND_SHA256 := $(TAILWIND_SHA256_$(TAILWIND_PLATFORM))
|
|
118
|
+
DAISYUI_SHA256 := 85819d3fe86a852237b439b13f481481aac58e562ac47694cd11c3038e994f2a
|
|
119
|
+
DAISYUI_THEME_SHA256 := d92d2f488933dab757f046e81ebd4986e51e9423307798dc3545d09a15542c89
|
|
120
|
+
# macOS before 15 has shasum, not sha256sum
|
|
121
|
+
SHA256SUM := $(shell command -v sha256sum >/dev/null 2>&1 && echo sha256sum || echo shasum -a 256)
|
|
122
|
+
# download URL $(1) into file $(2), kept only when its SHA-256 is $(3)
|
|
123
|
+
fetch = mkdir -p $(dir $(2)) && curl -sSfL -o $(2).tmp $(1) \
|
|
124
|
+
&& { echo "$(3) $(2).tmp" | $(SHA256SUM) -c || { rm -f $(2).tmp; exit 1; }; } \
|
|
125
|
+
&& mv $(2).tmp $(2)
|
|
126
|
+
|
|
127
|
+
$(TAILWIND):
|
|
128
|
+
$(if $(filter $(TAILWIND_PLATFORM),$(TAILWIND_PLATFORMS)),,\
|
|
129
|
+
$(error No Tailwind binary for '$(TAILWIND_PLATFORM)': pass one of \
|
|
130
|
+
$(TAILWIND_PLATFORMS) as TAILWIND_PLATFORM))
|
|
131
|
+
$(call fetch,https://github.com/tailwindlabs/tailwindcss/releases/download/v$(TAILWIND_VERSION)/tailwindcss-$(TAILWIND_PLATFORM)$(TAILWIND_SUFFIX),$@,$(TAILWIND_SHA256))
|
|
132
|
+
chmod +x $@
|
|
133
|
+
|
|
134
|
+
$(DAISYUI):
|
|
135
|
+
$(call fetch,https://github.com/saadeghi/daisyui/releases/download/v$(DAISYUI_VERSION)/daisyui.mjs,$@,$(DAISYUI_SHA256))
|
|
136
|
+
|
|
137
|
+
$(DAISYUI_THEME):
|
|
138
|
+
$(call fetch,https://github.com/saadeghi/daisyui/releases/download/v$(DAISYUI_VERSION)/daisyui-theme.mjs,$@,$(DAISYUI_THEME_SHA256))
|
|
139
|
+
|
|
140
|
+
# the files already there are checked too: they can be from an older version, or have
|
|
141
|
+
# changed since
|
|
142
|
+
.PHONY: css
|
|
143
|
+
css: $(TAILWIND) $(DAISYUI) $(DAISYUI_THEME)
|
|
144
|
+
@printf '%s %s\n' $(TAILWIND_SHA256) $(TAILWIND) $(DAISYUI_SHA256) $(DAISYUI) \
|
|
145
|
+
$(DAISYUI_THEME_SHA256) $(DAISYUI_THEME) | $(SHA256SUM) -c \
|
|
146
|
+
|| { echo "Delete $(CSS_VENDOR)/ to download them again"; exit 1; }
|
|
147
|
+
$(TAILWIND) -i css/app.css -o src/composeui/webcore/static/app.css --minify
|
|
148
|
+
|
|
149
|
+
# the web page's scripts from npm, through unpkg, and uPlot's stylesheet, which app.css
|
|
150
|
+
# imports: committed, so the package needs no download. Change a version and its SHA-256,
|
|
151
|
+
# run `make web-deps css`, and commit the files.
|
|
152
|
+
STATIC := src/composeui/webcore/static
|
|
153
|
+
HTMX_VERSION := 2.0.10
|
|
154
|
+
UPLOT_VERSION := 1.6.32
|
|
155
|
+
CALLY_VERSION := 0.9.2
|
|
156
|
+
DECKGL_VERSION := 9.2.6
|
|
157
|
+
DECKGL_SHA256 := 234e0af97a691b8e5aea6c6ca9e703a7ba5206fd1f53daa9f670e41db089de2d
|
|
158
|
+
DECKGL_LICENSE_SHA256 := e499a2c2ba3b9faabe91fdfddf43e329439263264b7148e5548d0ec605cce9f2
|
|
159
|
+
HTMX_SHA256 := 71ea67185bfa8c98c39d31717c6fce5d852370fcdfd129db4543774d3145c0de
|
|
160
|
+
UPLOT_SHA256 := 19c8d4c6ad88929a79f4ae49d6f7161566dfd0ba3d15cc495e974f787eb78f1f
|
|
161
|
+
UPLOT_CSS_SHA256 := df630c6a8d6f8eeaff264b50f73ce5b114f646ffd9a0bb74f049b0a00135fa04
|
|
162
|
+
CALLY_SHA256 := 4dcba6a3b8ec63b66ff2fbfc6cf47b7dee1eb4896eac89fe7487d82e28e15870
|
|
163
|
+
CALLY_LICENSE_SHA256 := c48fd407090d6d752b0370d39db55bd750e6dfc678ee252d4c906bddf54132eb
|
|
164
|
+
UPLOT_LICENSE_SHA256 := 8f989229699b4fe2f1a0432d0e9edc338a8a911e250e2d1b01ecd770a5f5b1bd
|
|
165
|
+
# daisyUI's classes are built into app.css (`make css`); its license ships beside it
|
|
166
|
+
DAISYUI_LICENSE_SHA256 := 8709e3ac65c84637c422dc8082b893d9997fce093751c77b2f1983bf29dbf9ea
|
|
167
|
+
|
|
168
|
+
# Cally's file has no license comment: it gets Cally's LICENSE as one; uPlot's has only its
|
|
169
|
+
# address, and its LICENSE goes beside it
|
|
170
|
+
.PHONY: web-deps
|
|
171
|
+
web-deps:
|
|
172
|
+
$(call fetch,https://unpkg.com/deck.gl@$(DECKGL_VERSION)/dist.min.js,$(STATIC)/deckgl.min.js,$(DECKGL_SHA256))
|
|
173
|
+
$(call fetch,https://unpkg.com/deck.gl@$(DECKGL_VERSION)/LICENSE,$(STATIC)/deckgl.LICENSE,$(DECKGL_LICENSE_SHA256))
|
|
174
|
+
$(call fetch,https://unpkg.com/htmx.org@$(HTMX_VERSION)/dist/htmx.min.js,$(STATIC)/htmx.min.js,$(HTMX_SHA256))
|
|
175
|
+
$(call fetch,https://unpkg.com/uplot@$(UPLOT_VERSION)/dist/uPlot.iife.min.js,$(STATIC)/uPlot.iife.min.js,$(UPLOT_SHA256))
|
|
176
|
+
$(call fetch,https://unpkg.com/uplot@$(UPLOT_VERSION)/LICENSE,$(STATIC)/uPlot.LICENSE,$(UPLOT_LICENSE_SHA256))
|
|
177
|
+
$(call fetch,https://unpkg.com/uplot@$(UPLOT_VERSION)/dist/uPlot.min.css,css/uPlot.min.css,$(UPLOT_CSS_SHA256))
|
|
178
|
+
$(call fetch,https://unpkg.com/daisyui@$(DAISYUI_VERSION)/LICENSE,$(STATIC)/daisyui.LICENSE,$(DAISYUI_LICENSE_SHA256))
|
|
179
|
+
$(call fetch,https://unpkg.com/cally@$(CALLY_VERSION)/dist/cally.js,$(STATIC)/cally.dist.js,$(CALLY_SHA256))
|
|
180
|
+
$(call fetch,https://unpkg.com/cally@$(CALLY_VERSION)/LICENSE,$(STATIC)/cally.LICENSE,$(CALLY_LICENSE_SHA256))
|
|
181
|
+
{ echo '/*! https://github.com/WickyNilliams/cally (v$(CALLY_VERSION)), its dependency atomico bundled in'; \
|
|
182
|
+
sed -e 's/^/ * /' -e 's/ *$$//' $(STATIC)/cally.LICENSE; echo ' */'; \
|
|
183
|
+
cat $(STATIC)/cally.dist.js; } > $(STATIC)/cally.js
|
|
184
|
+
rm -f $(STATIC)/cally.dist.js $(STATIC)/cally.LICENSE
|
|
185
|
+
|
|
186
|
+
.PHONY: clean
|
|
187
|
+
clean:
|
|
188
|
+
rm -rf `find . -name __pycache__`
|
|
189
|
+
rm -f `find . -type f -name '*.py[co]'`
|
|
190
|
+
rm -f `find . -type f -name '*~'`
|
|
191
|
+
rm -f `find . -type f -name '.*~'`
|
|
192
|
+
rm -rf .cache
|
|
193
|
+
rm -rf .pytest_cache
|
|
194
|
+
rm -rf .ruff_cache
|
|
195
|
+
rm -rf htmlcov
|
|
196
|
+
rm -rf *.egg-info
|
|
197
|
+
rm -f .coverage
|
|
198
|
+
rm -f .coverage.*
|
|
199
|
+
rm -rf build
|
|
200
|
+
rm -rf dist
|
|
201
|
+
rm -rf site
|
|
202
|
+
rm -rf docs/_build
|
|
203
|
+
rm -rf coverage.xml
|
|
204
|
+
|
|
205
|
+
.PHONY: all
|
|
206
|
+
all: clean check test
|