PyWebWinUI3 1.1.0__tar.gz → 1.2.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.
- {pywebwinui3-1.1.0/PyWebWinUI3.egg-info → pywebwinui3-1.2.1}/PKG-INFO +1 -1
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1/PyWebWinUI3.egg-info}/PKG-INFO +1 -1
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/PyWebWinUI3.egg-info/SOURCES.txt +6 -6
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/__init__.py +1 -1
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/core.py +169 -3
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/util.py +122 -3
- pywebwinui3-1.1.0/pywebwinui3/web/_app/immutable/assets/2.DFa7Tulp.css → pywebwinui3-1.2.1/pywebwinui3/web/_app/immutable/assets/2.B-R7-woU.css +1 -1
- pywebwinui3-1.2.1/pywebwinui3/web/_app/immutable/chunks/CDNCM73h.js +1 -0
- pywebwinui3-1.1.0/pywebwinui3/web/_app/immutable/entry/app.COvDX_WQ.js → pywebwinui3-1.2.1/pywebwinui3/web/_app/immutable/entry/app.DZs-LPnH.js +2 -2
- pywebwinui3-1.2.1/pywebwinui3/web/_app/immutable/entry/start.BIkwzMz6.js +1 -0
- pywebwinui3-1.1.0/pywebwinui3/web/_app/immutable/nodes/1.Qo_2yB_V.js → pywebwinui3-1.2.1/pywebwinui3/web/_app/immutable/nodes/1.CrcwixmX.js +1 -1
- pywebwinui3-1.2.1/pywebwinui3/web/_app/immutable/nodes/2.BX-aQgy_.js +99 -0
- pywebwinui3-1.2.1/pywebwinui3/web/_app/version.json +1 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/index.html +8 -8
- pywebwinui3-1.1.0/pywebwinui3/web/_app/immutable/chunks/BrxDGwA4.js +0 -1
- pywebwinui3-1.1.0/pywebwinui3/web/_app/immutable/entry/start.D-PUi8dH.js +0 -1
- pywebwinui3-1.1.0/pywebwinui3/web/_app/immutable/nodes/2.DAAD9EkZ.js +0 -98
- pywebwinui3-1.1.0/pywebwinui3/web/_app/version.json +0 -1
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/LICENSE +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/MANIFEST.in +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/NOTICE +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/PyWebWinUI3.egg-info/dependency_links.txt +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/PyWebWinUI3.egg-info/requires.txt +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/PyWebWinUI3.egg-info/top_level.txt +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/README.md +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pyproject.toml +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/event.py +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/type.py +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.0.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.1.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.10.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.11.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.12.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.13.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.14.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.15.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.16.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.17.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.18.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.19.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.2.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.20.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.21.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.22.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.23.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.24.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.25.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.26.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.27.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.28.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.29.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.3.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.30.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.31.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.32.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.33.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.34.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.35.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.36.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.37.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.38.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.39.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.4.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.40.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.41.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.42.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.43.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.44.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.45.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.46.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.47.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.48.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.49.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.5.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.50.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.51.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.52.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.53.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.54.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.55.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.56.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.57.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.58.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.59.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.6.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.60.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.61.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.62.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.63.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.64.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.65.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.66.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.67.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.68.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.69.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.7.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.70.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.71.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.72.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.73.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.74.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.75.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.76.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.77.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.78.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.79.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.8.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.80.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.81.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.82.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.83.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.84.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.85.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.86.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.87.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.88.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.89.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.9.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.90.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/PretendardVariable.subset.91.woff2 +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/Pretendard/Variable-dynamic-subset.min.css +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/SegoeFluentIcons.ttf +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/_app/env.js +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/_app/immutable/chunks/6pQH9Hkv.js +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/_app/immutable/chunks/B-XLOWlt.js +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/_app/immutable/chunks/CGGCsFu4.js +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/_app/immutable/chunks/CW0wlPPy.js +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/_app/immutable/chunks/CZPLFskb.js +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/_app/immutable/chunks/CZuXpvpo.js +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/_app/immutable/chunks/DF1x2LYX.js +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/pywebwinui3/web/_app/immutable/nodes/0.C49PHrK2.js +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/requirements.txt +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/setup.cfg +0 -0
- {pywebwinui3-1.1.0 → pywebwinui3-1.2.1}/setup.py +0 -0
|
@@ -112,17 +112,17 @@ pywebwinui3/web/Pretendard/PretendardVariable.subset.91.woff2
|
|
|
112
112
|
pywebwinui3/web/Pretendard/Variable-dynamic-subset.min.css
|
|
113
113
|
pywebwinui3/web/_app/env.js
|
|
114
114
|
pywebwinui3/web/_app/version.json
|
|
115
|
-
pywebwinui3/web/_app/immutable/assets/2.
|
|
115
|
+
pywebwinui3/web/_app/immutable/assets/2.B-R7-woU.css
|
|
116
116
|
pywebwinui3/web/_app/immutable/chunks/6pQH9Hkv.js
|
|
117
117
|
pywebwinui3/web/_app/immutable/chunks/B-XLOWlt.js
|
|
118
|
-
pywebwinui3/web/_app/immutable/chunks/
|
|
118
|
+
pywebwinui3/web/_app/immutable/chunks/CDNCM73h.js
|
|
119
119
|
pywebwinui3/web/_app/immutable/chunks/CGGCsFu4.js
|
|
120
120
|
pywebwinui3/web/_app/immutable/chunks/CW0wlPPy.js
|
|
121
121
|
pywebwinui3/web/_app/immutable/chunks/CZPLFskb.js
|
|
122
122
|
pywebwinui3/web/_app/immutable/chunks/CZuXpvpo.js
|
|
123
123
|
pywebwinui3/web/_app/immutable/chunks/DF1x2LYX.js
|
|
124
|
-
pywebwinui3/web/_app/immutable/entry/app.
|
|
125
|
-
pywebwinui3/web/_app/immutable/entry/start.
|
|
124
|
+
pywebwinui3/web/_app/immutable/entry/app.DZs-LPnH.js
|
|
125
|
+
pywebwinui3/web/_app/immutable/entry/start.BIkwzMz6.js
|
|
126
126
|
pywebwinui3/web/_app/immutable/nodes/0.C49PHrK2.js
|
|
127
|
-
pywebwinui3/web/_app/immutable/nodes/1.
|
|
128
|
-
pywebwinui3/web/_app/immutable/nodes/2.
|
|
127
|
+
pywebwinui3/web/_app/immutable/nodes/1.CrcwixmX.js
|
|
128
|
+
pywebwinui3/web/_app/immutable/nodes/2.BX-aQgy_.js
|
|
@@ -3,9 +3,15 @@ from __future__ import annotations
|
|
|
3
3
|
import importlib
|
|
4
4
|
import json
|
|
5
5
|
import logging
|
|
6
|
+
import mimetypes
|
|
7
|
+
import os
|
|
8
|
+
import re
|
|
6
9
|
import sys
|
|
7
10
|
import threading
|
|
11
|
+
from http import HTTPStatus
|
|
12
|
+
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
8
13
|
from pathlib import Path
|
|
14
|
+
from urllib.parse import unquote, urlparse, urlunparse
|
|
9
15
|
|
|
10
16
|
import webview
|
|
11
17
|
from hPyT import title_bar
|
|
@@ -23,6 +29,63 @@ DEFAULT_WINDOW_MIN_WIDTH = 100
|
|
|
23
29
|
DEFAULT_WINDOW_MIN_HEIGHT = 100
|
|
24
30
|
|
|
25
31
|
|
|
32
|
+
def _default_ui_origin_host(title: str) -> str:
|
|
33
|
+
slug = re.sub(r"[^a-z0-9]+", "-", str(title).casefold()).strip("-")
|
|
34
|
+
if not slug:
|
|
35
|
+
slug = "app"
|
|
36
|
+
slug = re.sub(r"-{2,}", "-", slug)[:48].strip("-")
|
|
37
|
+
if not slug:
|
|
38
|
+
slug = "app"
|
|
39
|
+
return f"{slug}.local"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class _UiHttpServer(ThreadingHTTPServer):
|
|
43
|
+
daemon_threads = True
|
|
44
|
+
allow_reuse_address = True
|
|
45
|
+
|
|
46
|
+
def __init__(self, server_address, request_handler_class, main: "MainWindow"):
|
|
47
|
+
super().__init__(server_address, request_handler_class)
|
|
48
|
+
self.main = main
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class _UiRequestHandler(BaseHTTPRequestHandler):
|
|
52
|
+
server: _UiHttpServer
|
|
53
|
+
|
|
54
|
+
def do_GET(self):
|
|
55
|
+
try:
|
|
56
|
+
path = self.server.main._resolve_server_path(urlparse(self.path).path)
|
|
57
|
+
if path is None or not path.is_file():
|
|
58
|
+
self._send_error("Not found.", HTTPStatus.NOT_FOUND)
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
body = path.read_bytes()
|
|
62
|
+
content_type = mimetypes.guess_type(path.name)[0] or "application/octet-stream"
|
|
63
|
+
self.send_response(HTTPStatus.OK)
|
|
64
|
+
self._send_common_headers(content_type)
|
|
65
|
+
self.send_header("Content-Length", str(len(body)))
|
|
66
|
+
self.end_headers()
|
|
67
|
+
self.wfile.write(body)
|
|
68
|
+
except Exception:
|
|
69
|
+
core_logger.debug("Failed to serve UI asset", exc_info=True)
|
|
70
|
+
self._send_error("Failed to serve UI asset.", HTTPStatus.INTERNAL_SERVER_ERROR)
|
|
71
|
+
|
|
72
|
+
def log_message(self, format, *args):
|
|
73
|
+
return
|
|
74
|
+
|
|
75
|
+
def _send_error(self, message: str, status: HTTPStatus):
|
|
76
|
+
body = message.encode("utf-8")
|
|
77
|
+
self.send_response(status)
|
|
78
|
+
self._send_common_headers("text/plain; charset=utf-8")
|
|
79
|
+
self.send_header("Content-Length", str(len(body)))
|
|
80
|
+
self.end_headers()
|
|
81
|
+
self.wfile.write(body)
|
|
82
|
+
|
|
83
|
+
def _send_common_headers(self, content_type: str):
|
|
84
|
+
self.send_header("Content-Type", content_type)
|
|
85
|
+
self.send_header("Cache-Control", "no-store")
|
|
86
|
+
self.send_header("Permissions-Policy", "display-capture=*")
|
|
87
|
+
|
|
88
|
+
|
|
26
89
|
class _JsApi:
|
|
27
90
|
def _init(self, main:MainWindow):
|
|
28
91
|
self.init = main._init_payload
|
|
@@ -58,6 +121,15 @@ class MainWindow:
|
|
|
58
121
|
self._minimum_height = DEFAULT_WINDOW_MIN_HEIGHT
|
|
59
122
|
self._sync_lock = threading.Lock()
|
|
60
123
|
self._pending_sync: dict[str, object] = {}
|
|
124
|
+
self._ui_server: _UiHttpServer | None = None
|
|
125
|
+
self._ui_server_thread: threading.Thread | None = None
|
|
126
|
+
self._ui_origin = ""
|
|
127
|
+
self._ui_bind_host = "127.0.0.1"
|
|
128
|
+
self._ui_origin_host = getattr(self, "ui_origin_host", _default_ui_origin_host(title))
|
|
129
|
+
self._resource_roots: list[tuple[str, Path]] = [
|
|
130
|
+
("root", self.rootPath),
|
|
131
|
+
("package", self.packagePath),
|
|
132
|
+
]
|
|
61
133
|
|
|
62
134
|
self.values = SyncDict(
|
|
63
135
|
{
|
|
@@ -72,10 +144,12 @@ class MainWindow:
|
|
|
72
144
|
}
|
|
73
145
|
)
|
|
74
146
|
self.values.sync = self.queue_sync_value
|
|
147
|
+
self._start_ui_server()
|
|
148
|
+
self._configure_webview2_origin_identity()
|
|
75
149
|
|
|
76
150
|
self._window = webview.create_window(
|
|
77
151
|
self._current_title(),
|
|
78
|
-
self.
|
|
152
|
+
f"{self._ui_origin}/index.html",
|
|
79
153
|
js_api=self.api,
|
|
80
154
|
background_color="#202020",
|
|
81
155
|
text_select=True,
|
|
@@ -178,6 +252,80 @@ class MainWindow:
|
|
|
178
252
|
raise FileNotFoundError(f"Frontend entry not found: {entry}")
|
|
179
253
|
return entry
|
|
180
254
|
|
|
255
|
+
def _start_ui_server(self):
|
|
256
|
+
self._ui_server = _UiHttpServer((self._ui_bind_host, 0), _UiRequestHandler, self)
|
|
257
|
+
self._ui_origin = f"http://{self._ui_origin_host}:{self._ui_server.server_port}"
|
|
258
|
+
self._ui_server_thread = threading.Thread(target=self._ui_server.serve_forever, daemon=True)
|
|
259
|
+
self._ui_server_thread.start()
|
|
260
|
+
|
|
261
|
+
def _configure_webview2_origin_identity(self):
|
|
262
|
+
if self._ui_origin_host == self._ui_bind_host:
|
|
263
|
+
return
|
|
264
|
+
|
|
265
|
+
parsed = urlparse(self._ui_origin)
|
|
266
|
+
if not parsed.scheme or parsed.port is None:
|
|
267
|
+
return
|
|
268
|
+
|
|
269
|
+
required_args = [
|
|
270
|
+
f'--host-resolver-rules="MAP {self._ui_origin_host} {self._ui_bind_host}"',
|
|
271
|
+
f"--unsafely-treat-insecure-origin-as-secure={parsed.scheme}://{self._ui_origin_host}:{parsed.port}",
|
|
272
|
+
]
|
|
273
|
+
existing = os.environ.get("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", "").strip()
|
|
274
|
+
missing = [arg for arg in required_args if arg not in existing]
|
|
275
|
+
if missing:
|
|
276
|
+
os.environ["WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS"] = " ".join(
|
|
277
|
+
[value for value in [existing, *missing] if value]
|
|
278
|
+
).strip()
|
|
279
|
+
|
|
280
|
+
def _stop_ui_server(self):
|
|
281
|
+
if self._ui_server is None:
|
|
282
|
+
return
|
|
283
|
+
try:
|
|
284
|
+
self._ui_server.shutdown()
|
|
285
|
+
self._ui_server.server_close()
|
|
286
|
+
except Exception:
|
|
287
|
+
core_logger.debug("Failed to stop UI server", exc_info=True)
|
|
288
|
+
finally:
|
|
289
|
+
self._ui_server = None
|
|
290
|
+
self._ui_server_thread = None
|
|
291
|
+
|
|
292
|
+
def _resolve_path_under(self, base_path: Path, relative_path: str) -> Path | None:
|
|
293
|
+
candidate = (base_path / relative_path).resolve()
|
|
294
|
+
try:
|
|
295
|
+
candidate.relative_to(base_path)
|
|
296
|
+
except ValueError:
|
|
297
|
+
return None
|
|
298
|
+
return candidate
|
|
299
|
+
|
|
300
|
+
def _resolve_server_path(self, raw_path: str) -> Path | None:
|
|
301
|
+
path = unquote(raw_path or "/")
|
|
302
|
+
if path in {"", "/"}:
|
|
303
|
+
return self._entry_path()
|
|
304
|
+
|
|
305
|
+
for name, base_path in self._resource_roots:
|
|
306
|
+
if name == "package":
|
|
307
|
+
continue
|
|
308
|
+
|
|
309
|
+
prefix = f"/__{name}__/"
|
|
310
|
+
if path.startswith(prefix):
|
|
311
|
+
return self._resolve_path_under(base_path, path[len(prefix) :])
|
|
312
|
+
|
|
313
|
+
return self._resolve_path_under(self.packagePath, path.lstrip("/"))
|
|
314
|
+
|
|
315
|
+
def _resource_url(self, path: Path) -> str:
|
|
316
|
+
path = path.resolve()
|
|
317
|
+
for name, base_path in self._resource_roots:
|
|
318
|
+
try:
|
|
319
|
+
relative = path.relative_to(base_path)
|
|
320
|
+
except ValueError:
|
|
321
|
+
continue
|
|
322
|
+
|
|
323
|
+
if name == "package":
|
|
324
|
+
return f"{self._ui_origin}/{relative.as_posix()}"
|
|
325
|
+
return f"{self._ui_origin}/__{name}__/{relative.as_posix()}"
|
|
326
|
+
|
|
327
|
+
raise ValueError(f"Path is outside of registered resource roots: {path}")
|
|
328
|
+
|
|
181
329
|
def _before_show(self):
|
|
182
330
|
try:
|
|
183
331
|
hwnd = self._window.native.Handle.ToInt64()
|
|
@@ -186,6 +334,8 @@ class MainWindow:
|
|
|
186
334
|
core_logger.debug("Failed to hide title bar", exc_info=True)
|
|
187
335
|
|
|
188
336
|
def _on_closed(self):
|
|
337
|
+
self.accent.stop()
|
|
338
|
+
self._stop_ui_server()
|
|
189
339
|
self.events.closed.set()
|
|
190
340
|
|
|
191
341
|
def _dispatch_or_defer_sync(self, key: str, value):
|
|
@@ -286,11 +436,27 @@ class MainWindow:
|
|
|
286
436
|
if lowered.startswith(("http://", "https://", "file://", "data:", "about:")):
|
|
287
437
|
return raw_value
|
|
288
438
|
|
|
289
|
-
|
|
439
|
+
parsed = urlparse(raw_value)
|
|
440
|
+
path_value = parsed.path or raw_value
|
|
441
|
+
resolved = self.resolve_path(path_value)
|
|
290
442
|
if resolved is None or not resolved.exists() or not resolved.is_file():
|
|
291
443
|
return raw_value
|
|
292
444
|
|
|
293
|
-
|
|
445
|
+
resource_url = self._resource_url(resolved)
|
|
446
|
+
if not parsed.scheme and (parsed.query or parsed.fragment):
|
|
447
|
+
resource_parts = urlparse(resource_url)
|
|
448
|
+
resource_url = urlunparse(
|
|
449
|
+
(
|
|
450
|
+
resource_parts.scheme,
|
|
451
|
+
resource_parts.netloc,
|
|
452
|
+
resource_parts.path,
|
|
453
|
+
resource_parts.params,
|
|
454
|
+
parsed.query,
|
|
455
|
+
parsed.fragment,
|
|
456
|
+
)
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
return resource_url
|
|
294
460
|
|
|
295
461
|
def start(
|
|
296
462
|
self,
|
|
@@ -1,14 +1,57 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
import logging
|
|
3
|
+
import threading
|
|
4
|
+
import winreg
|
|
1
5
|
import xml.etree.ElementTree
|
|
2
6
|
from functools import lru_cache
|
|
3
7
|
from typing import Any, Callable
|
|
4
|
-
import winreg
|
|
5
|
-
import logging
|
|
6
8
|
import re
|
|
9
|
+
from ctypes import wintypes
|
|
7
10
|
|
|
8
11
|
from .event import PathEvent, Event
|
|
9
12
|
|
|
10
13
|
logger = logging.getLogger('pywebwinui3.util')
|
|
11
14
|
|
|
15
|
+
ACCENT_REGISTRY_PATH = r"Software\Microsoft\Windows\CurrentVersion\Explorer\Accent"
|
|
16
|
+
REG_NOTIFY_CHANGE_LAST_SET = 0x00000004
|
|
17
|
+
WAIT_OBJECT_0 = 0x00000000
|
|
18
|
+
WAIT_FAILED = 0xFFFFFFFF
|
|
19
|
+
INFINITE = 0xFFFFFFFF
|
|
20
|
+
|
|
21
|
+
_advapi32 = ctypes.WinDLL("Advapi32", use_last_error=True)
|
|
22
|
+
_kernel32 = ctypes.WinDLL("Kernel32", use_last_error=True)
|
|
23
|
+
|
|
24
|
+
_advapi32.RegNotifyChangeKeyValue.argtypes = [
|
|
25
|
+
wintypes.HKEY,
|
|
26
|
+
wintypes.BOOL,
|
|
27
|
+
wintypes.DWORD,
|
|
28
|
+
wintypes.HANDLE,
|
|
29
|
+
wintypes.BOOL,
|
|
30
|
+
]
|
|
31
|
+
_advapi32.RegNotifyChangeKeyValue.restype = wintypes.LONG
|
|
32
|
+
|
|
33
|
+
_kernel32.CreateEventW.argtypes = [
|
|
34
|
+
wintypes.LPVOID,
|
|
35
|
+
wintypes.BOOL,
|
|
36
|
+
wintypes.BOOL,
|
|
37
|
+
wintypes.LPCWSTR,
|
|
38
|
+
]
|
|
39
|
+
_kernel32.CreateEventW.restype = wintypes.HANDLE
|
|
40
|
+
|
|
41
|
+
_kernel32.SetEvent.argtypes = [wintypes.HANDLE]
|
|
42
|
+
_kernel32.SetEvent.restype = wintypes.BOOL
|
|
43
|
+
|
|
44
|
+
_kernel32.CloseHandle.argtypes = [wintypes.HANDLE]
|
|
45
|
+
_kernel32.CloseHandle.restype = wintypes.BOOL
|
|
46
|
+
|
|
47
|
+
_kernel32.WaitForMultipleObjects.argtypes = [
|
|
48
|
+
wintypes.DWORD,
|
|
49
|
+
ctypes.POINTER(wintypes.HANDLE),
|
|
50
|
+
wintypes.BOOL,
|
|
51
|
+
wintypes.DWORD,
|
|
52
|
+
]
|
|
53
|
+
_kernel32.WaitForMultipleObjects.restype = wintypes.DWORD
|
|
54
|
+
|
|
12
55
|
DEFAULT_ACCENT_PALETTE = [
|
|
13
56
|
"#99ebff",
|
|
14
57
|
"#00ccff",
|
|
@@ -162,16 +205,72 @@ class AccentColorWatcher:
|
|
|
162
205
|
def __init__(self, event:Event=None):
|
|
163
206
|
self.event = event or Event()
|
|
164
207
|
self.palette = self.getSystemAccentColor()
|
|
208
|
+
self._watch_thread: threading.Thread | None = None
|
|
209
|
+
self._stop_event_handle: int | None = None
|
|
165
210
|
|
|
166
211
|
@staticmethod
|
|
167
212
|
def getSystemAccentColor():
|
|
168
213
|
try:
|
|
169
|
-
with winreg.OpenKey(winreg.HKEY_CURRENT_USER,
|
|
214
|
+
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, ACCENT_REGISTRY_PATH) as key:
|
|
170
215
|
p, _ = winreg.QueryValueEx(key, "AccentPalette")
|
|
171
216
|
except OSError:
|
|
172
217
|
return DEFAULT_ACCENT_PALETTE.copy()
|
|
173
218
|
return [f"#{p[i]:02x}{p[i+1]:02x}{p[i+2]:02x}" for i in range(0,len(p),4)]
|
|
174
219
|
|
|
220
|
+
@staticmethod
|
|
221
|
+
def _create_event_handle():
|
|
222
|
+
handle = _kernel32.CreateEventW(None, False, False, None)
|
|
223
|
+
if not handle:
|
|
224
|
+
raise ctypes.WinError(ctypes.get_last_error())
|
|
225
|
+
return int(handle)
|
|
226
|
+
|
|
227
|
+
@staticmethod
|
|
228
|
+
def _close_handle(handle: int | None):
|
|
229
|
+
if handle:
|
|
230
|
+
_kernel32.CloseHandle(handle)
|
|
231
|
+
|
|
232
|
+
def _watch_loop(self, stop_event_handle: int):
|
|
233
|
+
change_event_handle = None
|
|
234
|
+
try:
|
|
235
|
+
with winreg.OpenKey(
|
|
236
|
+
winreg.HKEY_CURRENT_USER,
|
|
237
|
+
ACCENT_REGISTRY_PATH,
|
|
238
|
+
0,
|
|
239
|
+
winreg.KEY_READ | winreg.KEY_NOTIFY,
|
|
240
|
+
) as key:
|
|
241
|
+
change_event_handle = self._create_event_handle()
|
|
242
|
+
wait_handles = (wintypes.HANDLE * 2)(change_event_handle, stop_event_handle)
|
|
243
|
+
key_handle = wintypes.HKEY(int(key.handle))
|
|
244
|
+
while True:
|
|
245
|
+
result = _advapi32.RegNotifyChangeKeyValue(
|
|
246
|
+
key_handle,
|
|
247
|
+
False,
|
|
248
|
+
REG_NOTIFY_CHANGE_LAST_SET,
|
|
249
|
+
wintypes.HANDLE(change_event_handle),
|
|
250
|
+
True,
|
|
251
|
+
)
|
|
252
|
+
if result != 0:
|
|
253
|
+
logger.debug("Accent watcher registry notification failed: %s", result)
|
|
254
|
+
break
|
|
255
|
+
|
|
256
|
+
wait_result = _kernel32.WaitForMultipleObjects(2, wait_handles, False, INFINITE)
|
|
257
|
+
if wait_result == WAIT_OBJECT_0:
|
|
258
|
+
self.refresh()
|
|
259
|
+
continue
|
|
260
|
+
if wait_result == WAIT_OBJECT_0 + 1:
|
|
261
|
+
break
|
|
262
|
+
|
|
263
|
+
logger.debug("Accent watcher wait failed: %s", wait_result)
|
|
264
|
+
break
|
|
265
|
+
except Exception:
|
|
266
|
+
logger.debug("Accent watcher stopped unexpectedly", exc_info=True)
|
|
267
|
+
finally:
|
|
268
|
+
self._close_handle(change_event_handle)
|
|
269
|
+
self._close_handle(stop_event_handle)
|
|
270
|
+
if self._stop_event_handle == stop_event_handle:
|
|
271
|
+
self._stop_event_handle = None
|
|
272
|
+
self._watch_thread = None
|
|
273
|
+
|
|
175
274
|
def refresh(self):
|
|
176
275
|
if self.palette != (color := self.getSystemAccentColor()):
|
|
177
276
|
self.palette = color
|
|
@@ -179,4 +278,24 @@ class AccentColorWatcher:
|
|
|
179
278
|
|
|
180
279
|
def start(self):
|
|
181
280
|
self.refresh()
|
|
281
|
+
if self._watch_thread and self._watch_thread.is_alive():
|
|
282
|
+
return
|
|
283
|
+
|
|
284
|
+
try:
|
|
285
|
+
self._stop_event_handle = self._create_event_handle()
|
|
286
|
+
except Exception:
|
|
287
|
+
logger.debug("Failed to start accent watcher", exc_info=True)
|
|
288
|
+
return
|
|
289
|
+
|
|
290
|
+
self._watch_thread = threading.Thread(
|
|
291
|
+
target=self._watch_loop,
|
|
292
|
+
args=(self._stop_event_handle,),
|
|
293
|
+
daemon=True,
|
|
294
|
+
name="AccentColorWatcher",
|
|
295
|
+
)
|
|
296
|
+
self._watch_thread.start()
|
|
182
297
|
logger.debug("Accent watcher initialized")
|
|
298
|
+
|
|
299
|
+
def stop(self):
|
|
300
|
+
if self._stop_event_handle:
|
|
301
|
+
_kernel32.SetEvent(wintypes.HANDLE(self._stop_event_handle))
|