domonic-libs 0.0.1__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.
- domonic_libs-0.0.1/LICENSE +21 -0
- domonic_libs-0.0.1/PKG-INFO +308 -0
- domonic_libs-0.0.1/README.md +269 -0
- domonic_libs-0.0.1/THIRD_PARTY_LICENSES.md +278 -0
- domonic_libs-0.0.1/pyproject.toml +65 -0
- domonic_libs-0.0.1/setup.cfg +4 -0
- domonic_libs-0.0.1/src/domonic_libs/__init__.py +43 -0
- domonic_libs-0.0.1/src/domonic_libs/__main__.py +8 -0
- domonic_libs-0.0.1/src/domonic_libs/app/__init__.py +29 -0
- domonic_libs-0.0.1/src/domonic_libs/app/bridge.py +55 -0
- domonic_libs-0.0.1/src/domonic_libs/app/client.py +494 -0
- domonic_libs-0.0.1/src/domonic_libs/app/core.py +320 -0
- domonic_libs-0.0.1/src/domonic_libs/app/desktop.py +165 -0
- domonic_libs-0.0.1/src/domonic_libs/app/events.py +130 -0
- domonic_libs-0.0.1/src/domonic_libs/app/file_drop.py +50 -0
- domonic_libs-0.0.1/src/domonic_libs/app/menus.py +77 -0
- domonic_libs-0.0.1/src/domonic_libs/app/storage.py +48 -0
- domonic_libs-0.0.1/src/domonic_libs/app/web.py +144 -0
- domonic_libs-0.0.1/src/domonic_libs/cli.py +464 -0
- domonic_libs-0.0.1/src/domonic_libs/dagre/__init__.py +29 -0
- domonic_libs-0.0.1/src/domonic_libs/dagre/acyclic.py +55 -0
- domonic_libs-0.0.1/src/domonic_libs/dagre/add_border_segments.py +36 -0
- domonic_libs-0.0.1/src/domonic_libs/dagre/coordinate_system.py +61 -0
- domonic_libs-0.0.1/src/domonic_libs/dagre/graphlib.py +496 -0
- domonic_libs-0.0.1/src/domonic_libs/dagre/greedy_fas.py +113 -0
- domonic_libs-0.0.1/src/domonic_libs/dagre/layout.py +348 -0
- domonic_libs-0.0.1/src/domonic_libs/dagre/list.py +44 -0
- domonic_libs-0.0.1/src/domonic_libs/dagre/nesting_graph.py +84 -0
- domonic_libs-0.0.1/src/domonic_libs/dagre/normalize.py +72 -0
- domonic_libs-0.0.1/src/domonic_libs/dagre/order.py +439 -0
- domonic_libs-0.0.1/src/domonic_libs/dagre/parent_dummy_chains.py +86 -0
- domonic_libs-0.0.1/src/domonic_libs/dagre/position.py +360 -0
- domonic_libs-0.0.1/src/domonic_libs/dagre/rank.py +251 -0
- domonic_libs-0.0.1/src/domonic_libs/dagre/util.py +190 -0
- domonic_libs-0.0.1/src/domonic_libs/dompurify.py +1542 -0
- domonic_libs-0.0.1/src/domonic_libs/marked/__init__.py +106 -0
- domonic_libs-0.0.1/src/domonic_libs/marked/_unicode.py +137 -0
- domonic_libs-0.0.1/src/domonic_libs/marked/defaults.py +27 -0
- domonic_libs-0.0.1/src/domonic_libs/marked/helpers.py +138 -0
- domonic_libs-0.0.1/src/domonic_libs/marked/lexer.py +354 -0
- domonic_libs-0.0.1/src/domonic_libs/marked/parser.py +99 -0
- domonic_libs-0.0.1/src/domonic_libs/marked/renderer.py +171 -0
- domonic_libs-0.0.1/src/domonic_libs/marked/rules.py +644 -0
- domonic_libs-0.0.1/src/domonic_libs/marked/text_renderer.py +38 -0
- domonic_libs-0.0.1/src/domonic_libs/marked/tokenizer.py +843 -0
- domonic_libs-0.0.1/src/domonic_libs/mermaid/__init__.py +77 -0
- domonic_libs-0.0.1/src/domonic_libs/mermaid/common.py +57 -0
- domonic_libs-0.0.1/src/domonic_libs/mermaid/config.py +77 -0
- domonic_libs-0.0.1/src/domonic_libs/mermaid/flowchart/__init__.py +10 -0
- domonic_libs-0.0.1/src/domonic_libs/mermaid/flowchart/db.py +70 -0
- domonic_libs-0.0.1/src/domonic_libs/mermaid/flowchart/parser.py +227 -0
- domonic_libs-0.0.1/src/domonic_libs/mermaid/flowchart/renderer.py +200 -0
- domonic_libs-0.0.1/src/domonic_libs/mermaid/pie/__init__.py +10 -0
- domonic_libs-0.0.1/src/domonic_libs/mermaid/pie/db.py +52 -0
- domonic_libs-0.0.1/src/domonic_libs/mermaid/pie/parser.py +83 -0
- domonic_libs-0.0.1/src/domonic_libs/mermaid/pie/renderer.py +131 -0
- domonic_libs-0.0.1/src/domonic_libs/mermaid/sequence/__init__.py +13 -0
- domonic_libs-0.0.1/src/domonic_libs/mermaid/sequence/db.py +456 -0
- domonic_libs-0.0.1/src/domonic_libs/mermaid/sequence/parser.py +282 -0
- domonic_libs-0.0.1/src/domonic_libs/mermaid/sequence/renderer.py +569 -0
- domonic_libs-0.0.1/src/domonic_libs/mermaid/text_metrics.py +101 -0
- domonic_libs-0.0.1/src/domonic_libs/mermaid/timeline/__init__.py +10 -0
- domonic_libs-0.0.1/src/domonic_libs/mermaid/timeline/db.py +71 -0
- domonic_libs-0.0.1/src/domonic_libs/mermaid/timeline/parser.py +74 -0
- domonic_libs-0.0.1/src/domonic_libs/mermaid/timeline/renderer.py +186 -0
- domonic_libs-0.0.1/src/domonic_libs/preact/__init__.py +66 -0
- domonic_libs-0.0.1/src/domonic_libs/preact/clone_element.py +45 -0
- domonic_libs-0.0.1/src/domonic_libs/preact/component.py +217 -0
- domonic_libs-0.0.1/src/domonic_libs/preact/constants.py +57 -0
- domonic_libs-0.0.1/src/domonic_libs/preact/create_context.py +75 -0
- domonic_libs-0.0.1/src/domonic_libs/preact/create_element.py +132 -0
- domonic_libs-0.0.1/src/domonic_libs/preact/diff/catch_error.py +42 -0
- domonic_libs-0.0.1/src/domonic_libs/preact/diff/children.py +273 -0
- domonic_libs-0.0.1/src/domonic_libs/preact/diff/index.py +558 -0
- domonic_libs-0.0.1/src/domonic_libs/preact/diff/props.py +178 -0
- domonic_libs-0.0.1/src/domonic_libs/preact/hooks.py +439 -0
- domonic_libs-0.0.1/src/domonic_libs/preact/options.py +43 -0
- domonic_libs-0.0.1/src/domonic_libs/preact/render.py +90 -0
- domonic_libs-0.0.1/src/domonic_libs/preact/util.py +35 -0
- domonic_libs-0.0.1/src/domonic_libs/py.typed +1 -0
- domonic_libs-0.0.1/src/domonic_libs/qs/__init__.py +6 -0
- domonic_libs-0.0.1/src/domonic_libs/qs/formats.py +17 -0
- domonic_libs-0.0.1/src/domonic_libs/qs/index.py +8 -0
- domonic_libs-0.0.1/src/domonic_libs/qs/parse.py +387 -0
- domonic_libs-0.0.1/src/domonic_libs/qs/stringify.py +447 -0
- domonic_libs-0.0.1/src/domonic_libs/qs/utils.py +372 -0
- domonic_libs-0.0.1/src/domonic_libs/readability.py +1586 -0
- domonic_libs-0.0.1/src/domonic_libs/turndown/__init__.py +7 -0
- domonic_libs-0.0.1/src/domonic_libs/turndown/_turndown.py +166 -0
- domonic_libs-0.0.1/src/domonic_libs/turndown/collapse_whitespace.py +87 -0
- domonic_libs-0.0.1/src/domonic_libs/turndown/commonmark_rules.py +270 -0
- domonic_libs-0.0.1/src/domonic_libs/turndown/gfm.py +161 -0
- domonic_libs-0.0.1/src/domonic_libs/turndown/html_parser.py +53 -0
- domonic_libs-0.0.1/src/domonic_libs/turndown/node.py +95 -0
- domonic_libs-0.0.1/src/domonic_libs/turndown/root_node.py +44 -0
- domonic_libs-0.0.1/src/domonic_libs/turndown/rules.py +72 -0
- domonic_libs-0.0.1/src/domonic_libs/turndown/utilities.py +119 -0
- domonic_libs-0.0.1/src/domonic_libs/validator/__init__.py +1699 -0
- domonic_libs-0.0.1/src/domonic_libs/validator/_data.py +230 -0
- domonic_libs-0.0.1/src/domonic_libs.egg-info/PKG-INFO +308 -0
- domonic_libs-0.0.1/src/domonic_libs.egg-info/SOURCES.txt +116 -0
- domonic_libs-0.0.1/src/domonic_libs.egg-info/dependency_links.txt +1 -0
- domonic_libs-0.0.1/src/domonic_libs.egg-info/entry_points.txt +3 -0
- domonic_libs-0.0.1/src/domonic_libs.egg-info/requires.txt +11 -0
- domonic_libs-0.0.1/src/domonic_libs.egg-info/top_level.txt +1 -0
- domonic_libs-0.0.1/tests/test_app.py +331 -0
- domonic_libs-0.0.1/tests/test_cli.py +119 -0
- domonic_libs-0.0.1/tests/test_dagre.py +132 -0
- domonic_libs-0.0.1/tests/test_dompurify.py +171 -0
- domonic_libs-0.0.1/tests/test_events.py +74 -0
- domonic_libs-0.0.1/tests/test_marked.py +213 -0
- domonic_libs-0.0.1/tests/test_menus.py +79 -0
- domonic_libs-0.0.1/tests/test_mermaid.py +607 -0
- domonic_libs-0.0.1/tests/test_preact.py +631 -0
- domonic_libs-0.0.1/tests/test_qs.py +195 -0
- domonic_libs-0.0.1/tests/test_readability.py +902 -0
- domonic_libs-0.0.1/tests/test_turndown.py +191 -0
- domonic_libs-0.0.1/tests/test_validator.py +119 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 byteface
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: domonic-libs
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: DOM-focused Python ports and an optional pywebview app wrapper built on domonic
|
|
5
|
+
Author-email: byteface <byteface@googlemail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/byteface/domonic-libs
|
|
8
|
+
Project-URL: Source, https://github.com/byteface/domonic-libs
|
|
9
|
+
Project-URL: Tracker, https://github.com/byteface/domonic-libs/issues
|
|
10
|
+
Project-URL: Examples, https://github.com/byteface/domonic-libs/tree/v0.0.1/examples
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: MacOS X
|
|
13
|
+
Classifier: Environment :: Win32 (MS Windows)
|
|
14
|
+
Classifier: Environment :: X11 Applications
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Natural Language :: English
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
24
|
+
Classifier: Topic :: Software Development :: User Interfaces
|
|
25
|
+
Classifier: Topic :: Text Processing :: Markup :: HTML
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
License-File: THIRD_PARTY_LICENSES.md
|
|
30
|
+
Requires-Dist: domonic>=1.5.0
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
33
|
+
Requires-Dist: pywebview>=5.0; extra == "dev"
|
|
34
|
+
Provides-Extra: app
|
|
35
|
+
Requires-Dist: pywebview>=5.0; extra == "app"
|
|
36
|
+
Provides-Extra: examples
|
|
37
|
+
Requires-Dist: pywebview>=5.0; extra == "examples"
|
|
38
|
+
Dynamic: license-file
|
|
39
|
+
|
|
40
|
+
# domonic-libs
|
|
41
|
+
|
|
42
|
+
[](https://github.com/byteface/domonic-libs/actions/workflows/tests.yml)
|
|
43
|
+
<!-- [](https://pypi.org/project/domonic-libs/) -->
|
|
44
|
+
[](LICENSE)
|
|
45
|
+
|
|
46
|
+
DOM-shaped Python ports and an optional pywebview app wrapper built on [domonic](https://github.com/byteface/domonic).
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
## Experimental libraries
|
|
50
|
+
|
|
51
|
+
Additional ports and experimental libraries not included with the standard domonic installation.
|
|
52
|
+
|
|
53
|
+
This package is deliberately split:
|
|
54
|
+
|
|
55
|
+
- libs for compatibility testing domonic.
|
|
56
|
+
- an `app` wrapper for building desktop UIs from domonic trees.
|
|
57
|
+
|
|
58
|
+
but they will be here so you can use them in an app if you want by importing off the tags here.
|
|
59
|
+
|
|
60
|
+
DOM behaviours that trip up the ports are logged in [docs/domonic-wrinkles.md](docs/domonic-wrinkles.md) to feed back upstream.
|
|
61
|
+
|
|
62
|
+
## Install
|
|
63
|
+
|
|
64
|
+
To get all the libraries like Mermaid, preact, turndown, marked, validator, qs, dompurify, readability etc...
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install domonic-libs
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The pywebview app wrapper is an optional extra:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pip install "domonic-libs[app]"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Hack on the repo and run examples:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
python3 -m venv .venv
|
|
80
|
+
./.venv/bin/pip install -r requirements.txt # editable install + dev deps
|
|
81
|
+
./.venv/bin/python examples/dompurify_demo.py
|
|
82
|
+
make test
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The app wrapper API is in [docs/app.md](docs/app.md); the `dlx` CLI in [docs/cli.md](docs/cli.md). Ported modules keep their upstream licenses -- see [THIRD_PARTY_LICENSES.md](THIRD_PARTY_LICENSES.md).
|
|
86
|
+
|
|
87
|
+
## Command line -- `dlx`
|
|
88
|
+
|
|
89
|
+
Installing the package puts a `dlx` command on your `PATH` (also aliased `domonic-libs`, or run it as `python -m domonic_libs`). The cleanest way to get just the CLI, isolated, is [pipx](https://pipx.pypa.io):
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pipx install domonic-libs
|
|
93
|
+
|
|
94
|
+
# ...or run it once without installing
|
|
95
|
+
pipx run --spec domonic-libs dlx --help
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Every text command reads a file argument, or **stdin** when the argument is `-` or omitted, and writes to `--output` / `-o` or **stdout** -- so they pipe.
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# Mermaid -> SVG (flowchart / sequence / pie / timeline, auto-detected)
|
|
102
|
+
dlx mermaid architecture.mmd -o architecture.svg
|
|
103
|
+
dlx mermaid architecture.mmd --open # render + open in the browser
|
|
104
|
+
dlx mermaid architecture.mmd --stats # counts on stderr
|
|
105
|
+
cat flow.mmd | dlx mermaid --html > flow.html
|
|
106
|
+
|
|
107
|
+
# Markdown <-> HTML
|
|
108
|
+
echo "# Hi *there*" | dlx md
|
|
109
|
+
dlx html2md article.html --gfm > article.md
|
|
110
|
+
|
|
111
|
+
# Pull the readable article out of a page
|
|
112
|
+
dlx read https://example.com/some/post --md > post.md
|
|
113
|
+
dlx read page.html --json # just the metadata
|
|
114
|
+
|
|
115
|
+
# Sanitise hostile HTML (DOMPurify)
|
|
116
|
+
dlx sanitize comment.html --profile html --report
|
|
117
|
+
|
|
118
|
+
# One-off validator.js checks (exit 0 = true, 1 = false)
|
|
119
|
+
dlx validate isEmail ada@example.com
|
|
120
|
+
dlx validate isIBAN DE89370400440532013000
|
|
121
|
+
dlx validate --list
|
|
122
|
+
|
|
123
|
+
# Query strings
|
|
124
|
+
dlx qs parse "user[name]=ada&tags[]=a&tags[]=b"
|
|
125
|
+
dlx qs stringify '{"a": 1, "b": {"c": 2}}'
|
|
126
|
+
|
|
127
|
+
# Lay out and draw an arbitrary graph with the dagre port
|
|
128
|
+
printf 'build -> test\ntest -> deploy\nbuild -> lint\nlint -> deploy\n' \
|
|
129
|
+
| dlx dagre --rankdir LR -o pipeline.svg
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`dlx <command> -h` lists each command's options; full reference in [docs/cli.md](docs/cli.md).
|
|
133
|
+
|
|
134
|
+
## Library Ports
|
|
135
|
+
|
|
136
|
+
The domonic repo has tests but also small complex ports like javascript and libraries like dquery and d3 were done to make sure the DOM was behaving as it should.
|
|
137
|
+
|
|
138
|
+
The more things I can port faithfully from js, the more expectations on the DOM I can correct. And out of that will also pop useful tools.
|
|
139
|
+
|
|
140
|
+
### DOMPurify
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from domonic_libs.dompurify import sanitize
|
|
144
|
+
|
|
145
|
+
clean = sanitize('<p onclick="x"><a href="javascript:bad()">bad</a>Hello</p>')
|
|
146
|
+
print(clean)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
A port of [DOMPurify](https://github.com/cure53/DOMPurify) -- config options, hooks, namespaces, `RETURN_DOM` / `WHOLE_DOCUMENT`, and DOM-clobbering protection. It runs DOMPurify's own `test/fixtures/expect.mjs` corpus (`tests/fixtures/dompurify/expect.json`, 223 real-world XSS payloads) at **212/223 exact-match** with default config; the remaining eleven are fidelity gaps (safe output, not byte-identical) from domonic's HTML5 parser or DOMPurify's deepest namespace-confusion checks, listed in `tests/test_dompurify.py::KNOWN_GAPS`.
|
|
150
|
+
|
|
151
|
+
### QS
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from domonic_libs.qs import parse, stringify
|
|
155
|
+
|
|
156
|
+
state = parse("filters[status][]=open&filters[status][]=draft")
|
|
157
|
+
print(state)
|
|
158
|
+
print(stringify(state, {"arrayFormat": "brackets"}))
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`qs` is useful when browser-style URL state needs to round-trip through nested Python dictionaries and arrays.
|
|
162
|
+
|
|
163
|
+
### validator
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
from domonic_libs import validator
|
|
167
|
+
|
|
168
|
+
validator.isEmail("ada@example.com") # True
|
|
169
|
+
validator.isIBAN("DE89370400440532013000") # True (mod-97 checksum)
|
|
170
|
+
validator.isCreditCard("4111111111111111") # True (Luhn)
|
|
171
|
+
validator.normalizeEmail("Foo.Bar+x@googlemail.com") # 'foobar@gmail.com'
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
A file-by-file port of [validator.js](https://github.com/validatorjs/validator.js) v13 -- ~90 string validators and sanitizers with the upstream camelCase names. Options are passed as a dict or as keyword arguments. It runs the `valid` / `invalid` case tables from validator.js's own test suite (`tests/fixtures/validator/cases.json`, 858 cases). Locale-table-heavy validators (`isMobilePhone`, `isPostalCode`, `isTaxID`, ...) and the full `normalizeEmail` provider lists are not ported yet.
|
|
175
|
+
|
|
176
|
+
### Readability And Turndown
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
from domonic_libs.readability import Readability
|
|
180
|
+
from domonic_libs.turndown import turndown, TurndownService
|
|
181
|
+
|
|
182
|
+
article = Readability(html).parse()
|
|
183
|
+
markdown = turndown(article["content"])
|
|
184
|
+
|
|
185
|
+
# TurndownService mirrors turndown.js: options, .use(plugin), .addRule,
|
|
186
|
+
# .keep, .remove. Python option names are snake_case (heading_style="atx").
|
|
187
|
+
service = TurndownService(heading_style="atx", code_block_style="fenced")
|
|
188
|
+
|
|
189
|
+
from domonic_libs.turndown.gfm import gfm
|
|
190
|
+
service.use(gfm) # tables, strikethrough, task lists (turndown-plugin-gfm port)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
`turndown` is a file-for-file port of [turndown.js](https://github.com/mixmark-io/turndown) and runs its upstream fixture suite (see `tests/test_turndown.py`). Together with Readability it is useful for content extraction, reader views, local archives, and LLM-friendly page summaries.
|
|
194
|
+
|
|
195
|
+
### marked
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
from domonic_libs.marked import marked
|
|
199
|
+
|
|
200
|
+
marked("# Title\n\nSome **bold** text and a [link](https://x.io).")
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
A file-for-file port of [marked](https://github.com/markedjs/marked) v18 (Markdown to HTML) -- the inverse of turndown. It passes marked's own CommonMark 0.31.2 and GFM 0.29 conformance suites in full and 98% of marked's `new/` regression specs (`tests/test_marked.py`), compared with the same html-differ semantics marked uses. Options mirror marked's (`gfm`, `breaks`, `pedantic`, `silent`, custom `renderer`); hooks, async, and third-party extensions are not ported. marked's ``\p{P}``/``\p{S}`` regex classes are baked from `unicodedata`, keeping the port dependency-free.
|
|
204
|
+
|
|
205
|
+
### preact
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
from domonic.dom import document
|
|
209
|
+
from domonic_libs.preact import h, render, Component
|
|
210
|
+
from domonic_libs.preact.hooks import useState, useEffect
|
|
211
|
+
|
|
212
|
+
def Greeting(props):
|
|
213
|
+
return h("h1", None, "Hello ", props["name"])
|
|
214
|
+
|
|
215
|
+
root = document.createElement("div")
|
|
216
|
+
render(h(Greeting, {"name": "world"}), root)
|
|
217
|
+
str(root) # '<div><h1>Hello world</h1></div>'
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
A file-for-file port of [Preact](https://github.com/preactjs/preact) 10.29.8 -- `create_element`/`h`, the vnode-diffing reconciler (keyed children with the skew algorithm, Fragments, refs, `dangerouslySetInnerHTML`), the `Component` class with the full lifecycle, `createContext`, and `preact/hooks` (as `domonic_libs.preact.hooks`) -- running against domonic's server-side DOM instead of a browser. There is no JSX, so `h` is the authoring API and lifecycle names keep their upstream camelCase. Rendering is synchronous (Preact's microtask batching is replaced by a flush at the end of each `render` or event handler); Suspense and `preact/compat` are not ported. `tests/test_preact.py` ports a cross-section of Preact's browser suite (render, components, keys, fragments, refs, context, hooks) to `unittest`.
|
|
221
|
+
|
|
222
|
+
### mermaid
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
from domonic_libs.mermaid import render
|
|
226
|
+
|
|
227
|
+
svg = render("""flowchart TD
|
|
228
|
+
A[Parse text] --> B{Diagram type?}
|
|
229
|
+
B -->|sequence / pie| C[Direct renderer]
|
|
230
|
+
B -->|flowchart| D[dagre layout]
|
|
231
|
+
C --> E[Emit SVG]
|
|
232
|
+
D --> E
|
|
233
|
+
E --> F((domonic tree))""")
|
|
234
|
+
# -> '<svg class="mermaid flowchart" viewBox="0 0 ..." ...>...</svg>'
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
A port of [Mermaid](https://github.com/mermaid-js/mermaid) (mermaid@11.9.0) that turns diagram text into a domonic SVG tree -- no `mermaid.js` bundle, no headless browser, no Graphviz binary. `render(text)` auto-detects the type; the CLI is `dlx mermaid`.
|
|
238
|
+
|
|
239
|
+
| diagram | status |
|
|
240
|
+
| --- | --- |
|
|
241
|
+
| **sequence** | grammar + `SequenceDB` + renderer: participants (box and actor glyph), lifelines, the full arrow set, self-message curves, notes, activation bars, and the `loop` / `alt` / `opt` / `par` / `critical` / `break` / `rect` box machinery (nested boxes grow to enclose their contents). Gaps: `box` groups, autonumber badges, KaTeX, wrapping |
|
|
242
|
+
| **pie** | slices, percentages, legend, `showData` -- over domonic's `d3.shape` (`pie`/`arc`) and `d3.scale` |
|
|
243
|
+
| **timeline** | sections, periods, events, the activity line, wrapped node text |
|
|
244
|
+
| **flowchart** / `graph` | node shapes, the arrow zoo (labels, lengths, `--x` / `--o`), `&` groups, chains, `subgraph`; laid out by the bundled **dagre** port |
|
|
245
|
+
|
|
246
|
+
The sequence / pie / timeline parsers are checked against mermaid's own spec assertions (`tests/test_mermaid.py`); the flowchart parser is a pragmatic scanner for the common `flow.jison` subset (mermaid's is ~630 lines of stateful lexer), tested against hand-written cases. Text is measured through domonic's real `getBBox()` -- a gap this port surfaced, [since implemented in domonic 1.5.0](docs/domonic-wrinkles.md#resolved-in-domonic-150). `examples/mermaid_demo.py` is a live workbench.
|
|
247
|
+
|
|
248
|
+
### dagre
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
from domonic_libs.dagre import Graph, layout
|
|
252
|
+
|
|
253
|
+
g = Graph({"compound": True})
|
|
254
|
+
g.setGraph({"rankdir": "LR", "nodesep": 40, "ranksep": 60})
|
|
255
|
+
g.setDefaultEdgeLabel(lambda *a: {})
|
|
256
|
+
for v in "abc":
|
|
257
|
+
g.setNode(v, {"width": 80, "height": 30})
|
|
258
|
+
g.setEdge("a", "b", {}); g.setEdge("a", "c", {})
|
|
259
|
+
layout(g)
|
|
260
|
+
g.node("b") # {'x': ..., 'y': ..., 'rank': ..., ...}
|
|
261
|
+
g.edge("a", "b")["points"]
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
A faithful file-for-file port of [dagre](https://github.com/dagrejs/dagre) and the slice of [graphlib](https://github.com/dagrejs/graphlib) it needs -- directed-graph hierarchical layout, no C or JS dependency. The whole pipeline is ported: greedy-FAS acyclic → Sander nesting graph → network-simplex rank → barycenter + Barth bilayer crossing minimisation → Brandes-Köpf x-coordinates → edge routing. Used by the Mermaid flowchart renderer, and usable on its own for any layered-graph drawing (`dlx dagre` takes an edge list). Per-cluster `rankdir` recursion is the one omission; `tests/test_dagre.py` does structural/invariant checks.
|
|
265
|
+
|
|
266
|
+
## App Wrapper
|
|
267
|
+
|
|
268
|
+
Install `.[app]` or `.[examples]` before using this part.
|
|
269
|
+
|
|
270
|
+
```python
|
|
271
|
+
from domonic.events import Event
|
|
272
|
+
from domonic.html import button, h1, main
|
|
273
|
+
|
|
274
|
+
from domonic_libs import App, on
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
app = App("Hello")
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def clicked(event):
|
|
281
|
+
print(event.type)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
@app.route("/")
|
|
285
|
+
def index():
|
|
286
|
+
return main(h1("Hello"), on(button("Click me"), Event.CLICK, clicked))
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
app.run()
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
`App` is a thin bridge for domonic trees: routes return domonic HTML, callbacks receive domonic events, and examples can use OS file dialogs, menus, drag/drop, timers, storage, and transparent windows.
|
|
293
|
+
|
|
294
|
+
The render loop lives in a host-agnostic `BaseApp`. `App` is an alias for `DesktopApp`, which hosts it in a pywebview window. `BrowserApp` serves the same application to an ordinary browser over a small stdlib HTTP server -- the same route/handler code, no changes:
|
|
295
|
+
|
|
296
|
+
```python
|
|
297
|
+
from domonic_libs.app import BrowserApp
|
|
298
|
+
|
|
299
|
+
app = BrowserApp("Hello")
|
|
300
|
+
|
|
301
|
+
@app.route("/")
|
|
302
|
+
def index():
|
|
303
|
+
return main(h1("Hello"), on(button("Click me"), Event.CLICK, clicked))
|
|
304
|
+
|
|
305
|
+
app.run(port=8000) # serves http://127.0.0.1:8000/
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Only the transport differs (pywebview's `js_api` vs `fetch`) and the native host (menus, dialogs, native drag/drop paths are desktop-only). Because the browser has no server-to-client push, `evaluate_js` / `refresh` on `BrowserApp` queue a command that rides back on the next event or timer response.
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# domonic-libs
|
|
2
|
+
|
|
3
|
+
[](https://github.com/byteface/domonic-libs/actions/workflows/tests.yml)
|
|
4
|
+
<!-- [](https://pypi.org/project/domonic-libs/) -->
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
DOM-shaped Python ports and an optional pywebview app wrapper built on [domonic](https://github.com/byteface/domonic).
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Experimental libraries
|
|
11
|
+
|
|
12
|
+
Additional ports and experimental libraries not included with the standard domonic installation.
|
|
13
|
+
|
|
14
|
+
This package is deliberately split:
|
|
15
|
+
|
|
16
|
+
- libs for compatibility testing domonic.
|
|
17
|
+
- an `app` wrapper for building desktop UIs from domonic trees.
|
|
18
|
+
|
|
19
|
+
but they will be here so you can use them in an app if you want by importing off the tags here.
|
|
20
|
+
|
|
21
|
+
DOM behaviours that trip up the ports are logged in [docs/domonic-wrinkles.md](docs/domonic-wrinkles.md) to feed back upstream.
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
To get all the libraries like Mermaid, preact, turndown, marked, validator, qs, dompurify, readability etc...
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install domonic-libs
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The pywebview app wrapper is an optional extra:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install "domonic-libs[app]"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Hack on the repo and run examples:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
python3 -m venv .venv
|
|
41
|
+
./.venv/bin/pip install -r requirements.txt # editable install + dev deps
|
|
42
|
+
./.venv/bin/python examples/dompurify_demo.py
|
|
43
|
+
make test
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The app wrapper API is in [docs/app.md](docs/app.md); the `dlx` CLI in [docs/cli.md](docs/cli.md). Ported modules keep their upstream licenses -- see [THIRD_PARTY_LICENSES.md](THIRD_PARTY_LICENSES.md).
|
|
47
|
+
|
|
48
|
+
## Command line -- `dlx`
|
|
49
|
+
|
|
50
|
+
Installing the package puts a `dlx` command on your `PATH` (also aliased `domonic-libs`, or run it as `python -m domonic_libs`). The cleanest way to get just the CLI, isolated, is [pipx](https://pipx.pypa.io):
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pipx install domonic-libs
|
|
54
|
+
|
|
55
|
+
# ...or run it once without installing
|
|
56
|
+
pipx run --spec domonic-libs dlx --help
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Every text command reads a file argument, or **stdin** when the argument is `-` or omitted, and writes to `--output` / `-o` or **stdout** -- so they pipe.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Mermaid -> SVG (flowchart / sequence / pie / timeline, auto-detected)
|
|
63
|
+
dlx mermaid architecture.mmd -o architecture.svg
|
|
64
|
+
dlx mermaid architecture.mmd --open # render + open in the browser
|
|
65
|
+
dlx mermaid architecture.mmd --stats # counts on stderr
|
|
66
|
+
cat flow.mmd | dlx mermaid --html > flow.html
|
|
67
|
+
|
|
68
|
+
# Markdown <-> HTML
|
|
69
|
+
echo "# Hi *there*" | dlx md
|
|
70
|
+
dlx html2md article.html --gfm > article.md
|
|
71
|
+
|
|
72
|
+
# Pull the readable article out of a page
|
|
73
|
+
dlx read https://example.com/some/post --md > post.md
|
|
74
|
+
dlx read page.html --json # just the metadata
|
|
75
|
+
|
|
76
|
+
# Sanitise hostile HTML (DOMPurify)
|
|
77
|
+
dlx sanitize comment.html --profile html --report
|
|
78
|
+
|
|
79
|
+
# One-off validator.js checks (exit 0 = true, 1 = false)
|
|
80
|
+
dlx validate isEmail ada@example.com
|
|
81
|
+
dlx validate isIBAN DE89370400440532013000
|
|
82
|
+
dlx validate --list
|
|
83
|
+
|
|
84
|
+
# Query strings
|
|
85
|
+
dlx qs parse "user[name]=ada&tags[]=a&tags[]=b"
|
|
86
|
+
dlx qs stringify '{"a": 1, "b": {"c": 2}}'
|
|
87
|
+
|
|
88
|
+
# Lay out and draw an arbitrary graph with the dagre port
|
|
89
|
+
printf 'build -> test\ntest -> deploy\nbuild -> lint\nlint -> deploy\n' \
|
|
90
|
+
| dlx dagre --rankdir LR -o pipeline.svg
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`dlx <command> -h` lists each command's options; full reference in [docs/cli.md](docs/cli.md).
|
|
94
|
+
|
|
95
|
+
## Library Ports
|
|
96
|
+
|
|
97
|
+
The domonic repo has tests but also small complex ports like javascript and libraries like dquery and d3 were done to make sure the DOM was behaving as it should.
|
|
98
|
+
|
|
99
|
+
The more things I can port faithfully from js, the more expectations on the DOM I can correct. And out of that will also pop useful tools.
|
|
100
|
+
|
|
101
|
+
### DOMPurify
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from domonic_libs.dompurify import sanitize
|
|
105
|
+
|
|
106
|
+
clean = sanitize('<p onclick="x"><a href="javascript:bad()">bad</a>Hello</p>')
|
|
107
|
+
print(clean)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
A port of [DOMPurify](https://github.com/cure53/DOMPurify) -- config options, hooks, namespaces, `RETURN_DOM` / `WHOLE_DOCUMENT`, and DOM-clobbering protection. It runs DOMPurify's own `test/fixtures/expect.mjs` corpus (`tests/fixtures/dompurify/expect.json`, 223 real-world XSS payloads) at **212/223 exact-match** with default config; the remaining eleven are fidelity gaps (safe output, not byte-identical) from domonic's HTML5 parser or DOMPurify's deepest namespace-confusion checks, listed in `tests/test_dompurify.py::KNOWN_GAPS`.
|
|
111
|
+
|
|
112
|
+
### QS
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from domonic_libs.qs import parse, stringify
|
|
116
|
+
|
|
117
|
+
state = parse("filters[status][]=open&filters[status][]=draft")
|
|
118
|
+
print(state)
|
|
119
|
+
print(stringify(state, {"arrayFormat": "brackets"}))
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`qs` is useful when browser-style URL state needs to round-trip through nested Python dictionaries and arrays.
|
|
123
|
+
|
|
124
|
+
### validator
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
from domonic_libs import validator
|
|
128
|
+
|
|
129
|
+
validator.isEmail("ada@example.com") # True
|
|
130
|
+
validator.isIBAN("DE89370400440532013000") # True (mod-97 checksum)
|
|
131
|
+
validator.isCreditCard("4111111111111111") # True (Luhn)
|
|
132
|
+
validator.normalizeEmail("Foo.Bar+x@googlemail.com") # 'foobar@gmail.com'
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
A file-by-file port of [validator.js](https://github.com/validatorjs/validator.js) v13 -- ~90 string validators and sanitizers with the upstream camelCase names. Options are passed as a dict or as keyword arguments. It runs the `valid` / `invalid` case tables from validator.js's own test suite (`tests/fixtures/validator/cases.json`, 858 cases). Locale-table-heavy validators (`isMobilePhone`, `isPostalCode`, `isTaxID`, ...) and the full `normalizeEmail` provider lists are not ported yet.
|
|
136
|
+
|
|
137
|
+
### Readability And Turndown
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
from domonic_libs.readability import Readability
|
|
141
|
+
from domonic_libs.turndown import turndown, TurndownService
|
|
142
|
+
|
|
143
|
+
article = Readability(html).parse()
|
|
144
|
+
markdown = turndown(article["content"])
|
|
145
|
+
|
|
146
|
+
# TurndownService mirrors turndown.js: options, .use(plugin), .addRule,
|
|
147
|
+
# .keep, .remove. Python option names are snake_case (heading_style="atx").
|
|
148
|
+
service = TurndownService(heading_style="atx", code_block_style="fenced")
|
|
149
|
+
|
|
150
|
+
from domonic_libs.turndown.gfm import gfm
|
|
151
|
+
service.use(gfm) # tables, strikethrough, task lists (turndown-plugin-gfm port)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
`turndown` is a file-for-file port of [turndown.js](https://github.com/mixmark-io/turndown) and runs its upstream fixture suite (see `tests/test_turndown.py`). Together with Readability it is useful for content extraction, reader views, local archives, and LLM-friendly page summaries.
|
|
155
|
+
|
|
156
|
+
### marked
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from domonic_libs.marked import marked
|
|
160
|
+
|
|
161
|
+
marked("# Title\n\nSome **bold** text and a [link](https://x.io).")
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
A file-for-file port of [marked](https://github.com/markedjs/marked) v18 (Markdown to HTML) -- the inverse of turndown. It passes marked's own CommonMark 0.31.2 and GFM 0.29 conformance suites in full and 98% of marked's `new/` regression specs (`tests/test_marked.py`), compared with the same html-differ semantics marked uses. Options mirror marked's (`gfm`, `breaks`, `pedantic`, `silent`, custom `renderer`); hooks, async, and third-party extensions are not ported. marked's ``\p{P}``/``\p{S}`` regex classes are baked from `unicodedata`, keeping the port dependency-free.
|
|
165
|
+
|
|
166
|
+
### preact
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
from domonic.dom import document
|
|
170
|
+
from domonic_libs.preact import h, render, Component
|
|
171
|
+
from domonic_libs.preact.hooks import useState, useEffect
|
|
172
|
+
|
|
173
|
+
def Greeting(props):
|
|
174
|
+
return h("h1", None, "Hello ", props["name"])
|
|
175
|
+
|
|
176
|
+
root = document.createElement("div")
|
|
177
|
+
render(h(Greeting, {"name": "world"}), root)
|
|
178
|
+
str(root) # '<div><h1>Hello world</h1></div>'
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
A file-for-file port of [Preact](https://github.com/preactjs/preact) 10.29.8 -- `create_element`/`h`, the vnode-diffing reconciler (keyed children with the skew algorithm, Fragments, refs, `dangerouslySetInnerHTML`), the `Component` class with the full lifecycle, `createContext`, and `preact/hooks` (as `domonic_libs.preact.hooks`) -- running against domonic's server-side DOM instead of a browser. There is no JSX, so `h` is the authoring API and lifecycle names keep their upstream camelCase. Rendering is synchronous (Preact's microtask batching is replaced by a flush at the end of each `render` or event handler); Suspense and `preact/compat` are not ported. `tests/test_preact.py` ports a cross-section of Preact's browser suite (render, components, keys, fragments, refs, context, hooks) to `unittest`.
|
|
182
|
+
|
|
183
|
+
### mermaid
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from domonic_libs.mermaid import render
|
|
187
|
+
|
|
188
|
+
svg = render("""flowchart TD
|
|
189
|
+
A[Parse text] --> B{Diagram type?}
|
|
190
|
+
B -->|sequence / pie| C[Direct renderer]
|
|
191
|
+
B -->|flowchart| D[dagre layout]
|
|
192
|
+
C --> E[Emit SVG]
|
|
193
|
+
D --> E
|
|
194
|
+
E --> F((domonic tree))""")
|
|
195
|
+
# -> '<svg class="mermaid flowchart" viewBox="0 0 ..." ...>...</svg>'
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
A port of [Mermaid](https://github.com/mermaid-js/mermaid) (mermaid@11.9.0) that turns diagram text into a domonic SVG tree -- no `mermaid.js` bundle, no headless browser, no Graphviz binary. `render(text)` auto-detects the type; the CLI is `dlx mermaid`.
|
|
199
|
+
|
|
200
|
+
| diagram | status |
|
|
201
|
+
| --- | --- |
|
|
202
|
+
| **sequence** | grammar + `SequenceDB` + renderer: participants (box and actor glyph), lifelines, the full arrow set, self-message curves, notes, activation bars, and the `loop` / `alt` / `opt` / `par` / `critical` / `break` / `rect` box machinery (nested boxes grow to enclose their contents). Gaps: `box` groups, autonumber badges, KaTeX, wrapping |
|
|
203
|
+
| **pie** | slices, percentages, legend, `showData` -- over domonic's `d3.shape` (`pie`/`arc`) and `d3.scale` |
|
|
204
|
+
| **timeline** | sections, periods, events, the activity line, wrapped node text |
|
|
205
|
+
| **flowchart** / `graph` | node shapes, the arrow zoo (labels, lengths, `--x` / `--o`), `&` groups, chains, `subgraph`; laid out by the bundled **dagre** port |
|
|
206
|
+
|
|
207
|
+
The sequence / pie / timeline parsers are checked against mermaid's own spec assertions (`tests/test_mermaid.py`); the flowchart parser is a pragmatic scanner for the common `flow.jison` subset (mermaid's is ~630 lines of stateful lexer), tested against hand-written cases. Text is measured through domonic's real `getBBox()` -- a gap this port surfaced, [since implemented in domonic 1.5.0](docs/domonic-wrinkles.md#resolved-in-domonic-150). `examples/mermaid_demo.py` is a live workbench.
|
|
208
|
+
|
|
209
|
+
### dagre
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
from domonic_libs.dagre import Graph, layout
|
|
213
|
+
|
|
214
|
+
g = Graph({"compound": True})
|
|
215
|
+
g.setGraph({"rankdir": "LR", "nodesep": 40, "ranksep": 60})
|
|
216
|
+
g.setDefaultEdgeLabel(lambda *a: {})
|
|
217
|
+
for v in "abc":
|
|
218
|
+
g.setNode(v, {"width": 80, "height": 30})
|
|
219
|
+
g.setEdge("a", "b", {}); g.setEdge("a", "c", {})
|
|
220
|
+
layout(g)
|
|
221
|
+
g.node("b") # {'x': ..., 'y': ..., 'rank': ..., ...}
|
|
222
|
+
g.edge("a", "b")["points"]
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
A faithful file-for-file port of [dagre](https://github.com/dagrejs/dagre) and the slice of [graphlib](https://github.com/dagrejs/graphlib) it needs -- directed-graph hierarchical layout, no C or JS dependency. The whole pipeline is ported: greedy-FAS acyclic → Sander nesting graph → network-simplex rank → barycenter + Barth bilayer crossing minimisation → Brandes-Köpf x-coordinates → edge routing. Used by the Mermaid flowchart renderer, and usable on its own for any layered-graph drawing (`dlx dagre` takes an edge list). Per-cluster `rankdir` recursion is the one omission; `tests/test_dagre.py` does structural/invariant checks.
|
|
226
|
+
|
|
227
|
+
## App Wrapper
|
|
228
|
+
|
|
229
|
+
Install `.[app]` or `.[examples]` before using this part.
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
from domonic.events import Event
|
|
233
|
+
from domonic.html import button, h1, main
|
|
234
|
+
|
|
235
|
+
from domonic_libs import App, on
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
app = App("Hello")
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def clicked(event):
|
|
242
|
+
print(event.type)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@app.route("/")
|
|
246
|
+
def index():
|
|
247
|
+
return main(h1("Hello"), on(button("Click me"), Event.CLICK, clicked))
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
app.run()
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
`App` is a thin bridge for domonic trees: routes return domonic HTML, callbacks receive domonic events, and examples can use OS file dialogs, menus, drag/drop, timers, storage, and transparent windows.
|
|
254
|
+
|
|
255
|
+
The render loop lives in a host-agnostic `BaseApp`. `App` is an alias for `DesktopApp`, which hosts it in a pywebview window. `BrowserApp` serves the same application to an ordinary browser over a small stdlib HTTP server -- the same route/handler code, no changes:
|
|
256
|
+
|
|
257
|
+
```python
|
|
258
|
+
from domonic_libs.app import BrowserApp
|
|
259
|
+
|
|
260
|
+
app = BrowserApp("Hello")
|
|
261
|
+
|
|
262
|
+
@app.route("/")
|
|
263
|
+
def index():
|
|
264
|
+
return main(h1("Hello"), on(button("Click me"), Event.CLICK, clicked))
|
|
265
|
+
|
|
266
|
+
app.run(port=8000) # serves http://127.0.0.1:8000/
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Only the transport differs (pywebview's `js_api` vs `fetch`) and the native host (menus, dialogs, native drag/drop paths are desktop-only). Because the browser has no server-to-client push, `evaluate_js` / `refresh` on `BrowserApp` queue a command that rides back on the next event or timer response.
|