instaui 0.1.1__tar.gz → 0.1.3__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.
- {instaui-0.1.1 → instaui-0.1.3}/PKG-INFO +1 -1
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/component.py +1 -1
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/element.py +37 -17
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/html/checkbox.py +0 -7
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/html/select.py +1 -1
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/vfor.py +1 -1
- {instaui-0.1.1 → instaui-0.1.3}/instaui/consts.py +4 -0
- instaui-0.1.3/instaui/dependencies/component_dependency.py +16 -0
- instaui-0.1.3/instaui/dependencies/plugin_dependency.py +28 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/fastapi_server/debug_mode_router.py +0 -1
- instaui-0.1.3/instaui/fastapi_server/dependency_router.py +21 -0
- instaui-0.1.3/instaui/fastapi_server/resource.py +34 -0
- instaui-0.1.3/instaui/fastapi_server/server.py +298 -0
- instaui-0.1.3/instaui/html_tools.py +53 -0
- instaui-0.1.3/instaui/page_info.py +13 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/runtime/_app.py +9 -20
- instaui-0.1.3/instaui/runtime/resource.py +52 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/static/insta-ui.esm-browser.prod.js +3663 -3663
- instaui-0.1.3/instaui/static/insta-ui.ico +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/static/insta-ui.iife.js +29 -29
- {instaui-0.1.1 → instaui-0.1.3}/instaui/static/templates/debug/sse.html +2 -2
- instaui-0.1.3/instaui/static/templates/web.html +74 -0
- instaui-0.1.3/instaui/static/templates/zero.html +71 -0
- instaui-0.1.3/instaui/systems/file_system.py +6 -0
- instaui-0.1.3/instaui/template/web_template.py +49 -0
- instaui-0.1.3/instaui/template/zero_template.py +104 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/ui/__init__.py +4 -5
- instaui-0.1.3/instaui/ui_functions/ui_page.py +16 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/vars/path_var.py +2 -1
- instaui-0.1.3/instaui/zero/func.py +115 -0
- instaui-0.1.3/instaui/zero/scope.py +20 -0
- instaui-0.1.3/instaui/zero/test.html +44 -0
- {instaui-0.1.1 → instaui-0.1.3}/pyproject.toml +1 -1
- instaui-0.1.1/instaui/dependencies/__init__.py +0 -15
- instaui-0.1.1/instaui/dependencies/component_registrar.py +0 -82
- instaui-0.1.1/instaui/dependencies/installer.py +0 -5
- instaui-0.1.1/instaui/fastapi_server/config_router.py +0 -60
- instaui-0.1.1/instaui/fastapi_server/server.py +0 -246
- instaui-0.1.1/instaui/html_tools.py +0 -139
- instaui-0.1.1/instaui/page_info.py +0 -23
- instaui-0.1.1/instaui/runtime/resource.py +0 -87
- instaui-0.1.1/instaui/static/templates/web.html +0 -118
- instaui-0.1.1/instaui/static/templates/zero.html +0 -55
- instaui-0.1.1/instaui/systems/file_system.py +0 -17
- instaui-0.1.1/instaui/template/web_template.py +0 -55
- instaui-0.1.1/instaui/template/zero_template.py +0 -24
- instaui-0.1.1/instaui/ui_functions/ui_page.py +0 -31
- instaui-0.1.1/instaui/zero/scope.py +0 -9
- {instaui-0.1.1 → instaui-0.1.3}/LICENSE +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/README.md +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/__init__.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/_helper/observable_helper.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/boot_info.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/common/jsonable.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/__init__.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/column.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/content.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/directive.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/grid.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/html/__init__.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/html/_mixins.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/html/button.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/html/date.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/html/div.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/html/form.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/html/input.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/html/label.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/html/li.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/html/link.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/html/number.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/html/paragraph.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/html/range.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/html/span.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/html/ul.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/match.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/row.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/slot.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/transition_group.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/value_element.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/components/vif.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/event/event_mixin.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/event/js_event.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/event/web_event.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/experimental/__init__.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/experimental/debug.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/fastapi_server/_utils.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/fastapi_server/_uvicorn.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/fastapi_server/event_router.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/fastapi_server/middlewares.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/fastapi_server/request_context.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/fastapi_server/watch_router.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/handlers/_utils.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/handlers/computed_handler.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/handlers/config_handler.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/handlers/event_handler.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/handlers/watch_handler.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/inject.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/js/__init__.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/js/js_output.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/js/lambda_func.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/launch_collector.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/runtime/__init__.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/runtime/_inner_helper.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/runtime/context.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/runtime/dataclass.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/runtime/scope.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/runtime/ui_state_scope.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/settings/__init__.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/settings/__settings.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/skip.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/spa_router/__init__.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/spa_router/_components.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/spa_router/_file_base_utils.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/spa_router/_functions.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/spa_router/_install.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/spa_router/_route_model.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/spa_router/_router_box.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/spa_router/_router_output.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/spa_router/_router_param_var.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/spa_router/_types.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/spa_router/templates/page_routes +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/static/insta-ui.css +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/static/insta-ui.iife.js.map +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/static/insta-ui.js.map +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/static/tailwindcss.min.js +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/static/vue.esm-browser.prod.js +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/static/vue.global.prod.js +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/static/vue.runtime.esm-browser.prod.js +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/systems/func_system.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/systems/js_system.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/systems/pydantic_system.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/systems/string_system.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/template/__init__.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/template/env.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/ui/events.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/ui_functions/input_slient_data.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/ui_functions/server.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/ui_functions/str_format.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/ui_functions/ui_types.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/ui_functions/url_location.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/vars/__init__.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/vars/_types.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/vars/_utils.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/vars/data.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/vars/element_ref.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/vars/event_context.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/vars/event_extend.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/vars/js_computed.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/vars/mixin_types/common_type.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/vars/mixin_types/element_binding.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/vars/mixin_types/observable.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/vars/mixin_types/pathable.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/vars/mixin_types/py_binding.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/vars/mixin_types/str_format_binding.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/vars/mixin_types/var_type.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/vars/ref.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/vars/slot_prop.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/vars/state.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/vars/types.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/vars/vfor_item.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/vars/vue_computed.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/vars/web_computed.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/vars/web_view_computed.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/version.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/watch/_types.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/watch/_utils.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/watch/js_watch.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/watch/vue_watch.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/watch/web_watch.py +0 -0
- {instaui-0.1.1 → instaui-0.1.3}/instaui/zero/__init__.py +0 -0
@@ -13,6 +13,7 @@ from typing import (
|
|
13
13
|
Optional,
|
14
14
|
Tuple,
|
15
15
|
Union,
|
16
|
+
cast,
|
16
17
|
overload,
|
17
18
|
TYPE_CHECKING,
|
18
19
|
)
|
@@ -21,11 +22,11 @@ from collections import defaultdict
|
|
21
22
|
from instaui.runtime._app import get_app_slot
|
22
23
|
from instaui.runtime._app import get_current_scope
|
23
24
|
from instaui.vars.element_ref import ElementRef
|
24
|
-
|
25
|
-
|
26
25
|
from instaui.vars.vfor_item import VForItem
|
27
26
|
from instaui.components.directive import Directive
|
28
|
-
from instaui.dependencies import
|
27
|
+
from instaui.dependencies.component_dependency import (
|
28
|
+
ComponentDependencyInfo,
|
29
|
+
)
|
29
30
|
from .slot import SlotManager, Slot
|
30
31
|
from instaui import consts
|
31
32
|
from instaui.components.component import Component
|
@@ -69,14 +70,14 @@ PROPS_PATTERN = re.compile(
|
|
69
70
|
|
70
71
|
|
71
72
|
class Element(Component):
|
72
|
-
|
73
|
+
dependency: ClassVar[Optional[ComponentDependencyInfo]] = None
|
73
74
|
_default_props: ClassVar[Dict[str, Any]] = {}
|
74
75
|
_default_classes: ClassVar[List[str]] = []
|
75
76
|
_default_style: ClassVar[Dict[str, str]] = {}
|
76
77
|
|
77
78
|
def __init__(self, tag: Optional[Union[str, ElementBindingMixin]] = None):
|
78
|
-
if self.
|
79
|
-
tag = self.
|
79
|
+
if self.dependency:
|
80
|
+
tag = self.dependency.tag_name or ""
|
80
81
|
|
81
82
|
super().__init__(tag)
|
82
83
|
|
@@ -101,19 +102,28 @@ class Element(Component):
|
|
101
102
|
def __init_subclass__(
|
102
103
|
cls,
|
103
104
|
*,
|
104
|
-
|
105
|
+
esm: Union[str, Path, None] = None,
|
106
|
+
externals: Optional[List[Union[str, Path]]] = None,
|
107
|
+
css: Union[List[Union[str, Path]], None] = None,
|
105
108
|
) -> None:
|
106
109
|
super().__init_subclass__()
|
107
110
|
|
108
|
-
if
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
111
|
+
if esm:
|
112
|
+
esm = _make_dependency_path(esm, cls)
|
113
|
+
|
114
|
+
if externals:
|
115
|
+
externals = [_make_dependency_path(e, cls) for e in externals]
|
116
|
+
|
117
|
+
if css:
|
118
|
+
css = [_make_dependency_path(c, cls) for c in css]
|
113
119
|
|
114
|
-
|
115
|
-
|
116
|
-
|
120
|
+
tag_name = f"instaui-{esm.stem}"
|
121
|
+
|
122
|
+
cls.dependency = ComponentDependencyInfo(
|
123
|
+
tag_name=tag_name,
|
124
|
+
esm=esm,
|
125
|
+
externals=cast(List[Path], externals or []),
|
126
|
+
css=cast(List[Path], css or []),
|
117
127
|
)
|
118
128
|
|
119
129
|
cls._default_props = copy(cls._default_props)
|
@@ -365,8 +375,8 @@ class Element(Component):
|
|
365
375
|
if self._directives:
|
366
376
|
data["dir"] = list(self._directives.keys())
|
367
377
|
|
368
|
-
if self.
|
369
|
-
get_app_slot().
|
378
|
+
if self.dependency:
|
379
|
+
get_app_slot().use_component_dependency(self.dependency)
|
370
380
|
|
371
381
|
if self._element_ref:
|
372
382
|
scope = get_current_scope()
|
@@ -460,3 +470,13 @@ def _classifyBindableDict(
|
|
460
470
|
value_data[key] = value
|
461
471
|
|
462
472
|
return value_data, bind_data
|
473
|
+
|
474
|
+
|
475
|
+
def _make_dependency_path(path: Union[str, Path], cls: type):
|
476
|
+
if isinstance(path, str):
|
477
|
+
path = Path(path)
|
478
|
+
|
479
|
+
if not path.is_absolute():
|
480
|
+
path = Path(inspect.getfile(cls)).parent / path
|
481
|
+
|
482
|
+
return path
|
@@ -31,12 +31,5 @@ class Checkbox(InputEventMixin, ValueElement[Union[bool, str]]):
|
|
31
31
|
if model_value is not None:
|
32
32
|
self.props({"value": model_value})
|
33
33
|
|
34
|
-
# def vmodel(
|
35
|
-
# self,
|
36
|
-
# value: Any,
|
37
|
-
# *modifiers: Literal["trim"] | Literal["number"] | Literal["lazy"],
|
38
|
-
# ):
|
39
|
-
# return super().vmodel(value, *modifiers) # type: ignore
|
40
|
-
|
41
34
|
def _input_event_mixin_element(self) -> Element:
|
42
35
|
return self
|
@@ -60,7 +60,7 @@ class VFor(Component, Generic[_T]):
|
|
60
60
|
def __enter__(self) -> _T:
|
61
61
|
self.__scope = self.__scope_manager.__enter__()
|
62
62
|
super().__enter__()
|
63
|
-
return VForItem(self).proxy
|
63
|
+
return VForItem(self).proxy # type: ignore
|
64
64
|
|
65
65
|
def __exit__(self, *_) -> None:
|
66
66
|
self.__scope_manager.__exit__(*_)
|
@@ -8,11 +8,15 @@ _STATIC_DIR = _THIS_DIR.joinpath("static")
|
|
8
8
|
INDEX_TEMPLATE_PATH = _TEMPLATES_DIR.joinpath("index.html")
|
9
9
|
APP_IIFE_JS_PATH = _STATIC_DIR.joinpath("insta-ui.iife.js")
|
10
10
|
APP_ES_JS_PATH = _STATIC_DIR.joinpath("insta-ui.esm-browser.prod.js")
|
11
|
+
APP_ES_JS_MAP_PATH = _STATIC_DIR.joinpath("insta-ui.js.map")
|
11
12
|
APP_CSS_PATH = _STATIC_DIR.joinpath("insta-ui.css")
|
12
13
|
VUE_IIFE_JS_PATH = _STATIC_DIR.joinpath("vue.global.prod.js")
|
13
14
|
VUE_ES_JS_PATH = _STATIC_DIR.joinpath("vue.esm-browser.prod.js")
|
14
15
|
VUE_ES_RUNTIME_JS_PATH = _STATIC_DIR.joinpath("vue.runtime.esm-browser.prod.js")
|
15
16
|
TAILWIND_JS_PATH = _STATIC_DIR.joinpath("tailwindcss.min.js")
|
17
|
+
FAVICON_PATH = _STATIC_DIR.joinpath("insta-ui.ico")
|
18
|
+
|
19
|
+
PAGE_TITLE = "insta-ui"
|
16
20
|
|
17
21
|
_T_App_Mode = Literal["zero", "web", "webview"]
|
18
22
|
TModifier = Literal["trim", "number", "lazy"]
|
@@ -0,0 +1,16 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from dataclasses import dataclass, field
|
3
|
+
from pathlib import Path
|
4
|
+
from typing import List, Optional
|
5
|
+
|
6
|
+
_TTagName = str
|
7
|
+
|
8
|
+
|
9
|
+
@dataclass(frozen=True)
|
10
|
+
class ComponentDependencyInfo:
|
11
|
+
tag_name: _TTagName = field(hash=True)
|
12
|
+
esm: Path = field(hash=False)
|
13
|
+
externals: Optional[List[Path]] = field(
|
14
|
+
default_factory=list, compare=False, hash=False
|
15
|
+
)
|
16
|
+
css: List[Path] = field(default_factory=list, compare=False, hash=False)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from dataclasses import dataclass, field
|
3
|
+
from pathlib import Path
|
4
|
+
from typing import List, Optional
|
5
|
+
from instaui.runtime._app import get_app_slot
|
6
|
+
|
7
|
+
|
8
|
+
@dataclass(frozen=True)
|
9
|
+
class PluginDependencyInfo:
|
10
|
+
name: str = field(hash=True)
|
11
|
+
esm: Path = field(hash=False)
|
12
|
+
externals: Optional[List[Path]] = field(
|
13
|
+
default_factory=list, compare=False, hash=False
|
14
|
+
)
|
15
|
+
css: List[Path] = field(default_factory=list, compare=False, hash=False)
|
16
|
+
|
17
|
+
|
18
|
+
def register_plugin(
|
19
|
+
name: str,
|
20
|
+
esm: Path,
|
21
|
+
*,
|
22
|
+
externals: Optional[List[Path]] = None,
|
23
|
+
css: Optional[List[Path]] = None,
|
24
|
+
):
|
25
|
+
info = PluginDependencyInfo(f"plugin/{name}", esm, externals or [], css or [])
|
26
|
+
|
27
|
+
get_app_slot().use_plugin_dependency(info)
|
28
|
+
return info
|
@@ -0,0 +1,21 @@
|
|
1
|
+
from fastapi import FastAPI
|
2
|
+
from fastapi.responses import FileResponse
|
3
|
+
|
4
|
+
from instaui.fastapi_server import resource
|
5
|
+
|
6
|
+
|
7
|
+
URL = f"{resource.URL}/{{hash_part:path}}"
|
8
|
+
|
9
|
+
|
10
|
+
def create_router(app: FastAPI):
|
11
|
+
_dependency_handler(app)
|
12
|
+
|
13
|
+
|
14
|
+
def _dependency_handler(app: FastAPI):
|
15
|
+
@app.get(URL)
|
16
|
+
def _(hash_part: str) -> FileResponse:
|
17
|
+
local_file = resource.get_by_hash(hash_part)
|
18
|
+
|
19
|
+
return FileResponse(
|
20
|
+
local_file, headers={"Cache-Control": "public, max-age=3600"}
|
21
|
+
)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
from typing import Dict
|
3
|
+
from instaui.systems import file_system
|
4
|
+
from instaui.version import __version__ as _INSTA_VERSION
|
5
|
+
|
6
|
+
URL = f"/_instaui_{_INSTA_VERSION}/resource"
|
7
|
+
_THashPart = str
|
8
|
+
_HASH_PART_MAP: Dict[_THashPart, Path] = {}
|
9
|
+
_FILE_URL_MAP: Dict[Path, _THashPart] = {}
|
10
|
+
|
11
|
+
|
12
|
+
def get_by_hash(hash_part: str) -> Path:
|
13
|
+
return _HASH_PART_MAP[hash_part]
|
14
|
+
|
15
|
+
|
16
|
+
def record_resource(path: Path):
|
17
|
+
if path in _FILE_URL_MAP:
|
18
|
+
return _FILE_URL_MAP[path]
|
19
|
+
|
20
|
+
hash_part = _generate_hash_part(path)
|
21
|
+
_HASH_PART_MAP[hash_part] = path
|
22
|
+
url = f"{URL}/{hash_part}"
|
23
|
+
_FILE_URL_MAP[path] = url
|
24
|
+
return url
|
25
|
+
|
26
|
+
|
27
|
+
def _generate_hash_part(path: Path):
|
28
|
+
path = Path(path).resolve()
|
29
|
+
is_file = path.is_file()
|
30
|
+
|
31
|
+
if is_file:
|
32
|
+
return f"{file_system.generate_hash_name_from_path(path.parent)}/{path.name}"
|
33
|
+
|
34
|
+
return file_system.generate_hash_name_from_path(path)
|
@@ -0,0 +1,298 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from contextlib import contextmanager
|
3
|
+
import inspect
|
4
|
+
import os
|
5
|
+
import multiprocessing
|
6
|
+
from pathlib import Path
|
7
|
+
from typing import Any, Optional, Set
|
8
|
+
import __main__
|
9
|
+
|
10
|
+
from fastapi import FastAPI
|
11
|
+
from fastapi import Request
|
12
|
+
from fastapi.staticfiles import StaticFiles
|
13
|
+
|
14
|
+
from fastapi.responses import HTMLResponse
|
15
|
+
import uvicorn
|
16
|
+
from uvicorn.supervisors import ChangeReload
|
17
|
+
import itertools
|
18
|
+
|
19
|
+
from instaui.html_tools import to_config_data
|
20
|
+
from instaui.launch_collector import get_launch_collector
|
21
|
+
from instaui.page_info import PageInfo
|
22
|
+
|
23
|
+
from instaui import consts
|
24
|
+
from instaui.runtime._app import get_app_slot, get_default_app_slot
|
25
|
+
from instaui.runtime.dataclass import JsLink, VueAppComponent
|
26
|
+
from instaui.template import web_template
|
27
|
+
|
28
|
+
|
29
|
+
from . import dependency_router
|
30
|
+
from . import event_router
|
31
|
+
from . import watch_router
|
32
|
+
from . import debug_mode_router
|
33
|
+
from .middlewares import RequestContextMiddleware
|
34
|
+
from ._uvicorn import UvicornServer
|
35
|
+
from . import resource
|
36
|
+
from instaui.version import __version__ as _INSTA_VERSION
|
37
|
+
|
38
|
+
APP_IMPORT_STRING = "instaui.fastapi_server.server:Server._instance.app"
|
39
|
+
|
40
|
+
|
41
|
+
INSTAUI_STATIC_URL = f"/_instaui_{_INSTA_VERSION}/static"
|
42
|
+
|
43
|
+
VUE_JS_HASH_LINK = f"{INSTAUI_STATIC_URL}/{consts.VUE_ES_JS_PATH.name}"
|
44
|
+
INSTAUI_JS_HASH_LINK = f"{INSTAUI_STATIC_URL}/{consts.APP_ES_JS_PATH.name}"
|
45
|
+
APP_CSS_LINK = f"{INSTAUI_STATIC_URL}/{consts.APP_CSS_PATH.name}"
|
46
|
+
TAILWIND_JS_HASH_LINK = f"{INSTAUI_STATIC_URL}/{consts.TAILWIND_JS_PATH.name}"
|
47
|
+
FAVICON_LINK = f"{INSTAUI_STATIC_URL}/{consts.FAVICON_PATH.name}"
|
48
|
+
|
49
|
+
|
50
|
+
class Server:
|
51
|
+
_instance: Optional[Server] = None
|
52
|
+
|
53
|
+
@classmethod
|
54
|
+
def get_instance(cls):
|
55
|
+
if cls._instance is None:
|
56
|
+
cls._instance = cls()
|
57
|
+
return cls._instance
|
58
|
+
|
59
|
+
def __init__(self):
|
60
|
+
self.app = FastAPI()
|
61
|
+
self.app.add_middleware(RequestContextMiddleware)
|
62
|
+
dependency_router.create_router(self.app)
|
63
|
+
event_router.create_router(self.app)
|
64
|
+
watch_router.create_router(self.app)
|
65
|
+
debug_mode_router.create_router(self.app)
|
66
|
+
|
67
|
+
self.add_instaui_static(self.app)
|
68
|
+
|
69
|
+
for page_info in get_launch_collector()._page_router.values():
|
70
|
+
self.register_page(page_info)
|
71
|
+
|
72
|
+
self._registered_static_routes: Set[str] = set()
|
73
|
+
|
74
|
+
def register_page(self, info: PageInfo):
|
75
|
+
is_async = inspect.iscoroutinefunction(info.func)
|
76
|
+
|
77
|
+
self._remove_route(info.path)
|
78
|
+
|
79
|
+
if is_async:
|
80
|
+
|
81
|
+
@self.app.get(info.path)
|
82
|
+
async def _(request: Request):
|
83
|
+
self._update_page_info(request, info)
|
84
|
+
with _execute_request_lifespans():
|
85
|
+
await info.func()
|
86
|
+
html = self._to_web_html(
|
87
|
+
page_info=info,
|
88
|
+
request=request,
|
89
|
+
)
|
90
|
+
|
91
|
+
return HTMLResponse(html)
|
92
|
+
|
93
|
+
else:
|
94
|
+
|
95
|
+
@self.app.get(info.path)
|
96
|
+
def _(request: Request):
|
97
|
+
self._update_page_info(request, info)
|
98
|
+
with _execute_request_lifespans():
|
99
|
+
info.func()
|
100
|
+
html = self._to_web_html(
|
101
|
+
page_info=info,
|
102
|
+
request=request,
|
103
|
+
)
|
104
|
+
|
105
|
+
return HTMLResponse(html)
|
106
|
+
|
107
|
+
def _to_web_html(
|
108
|
+
self,
|
109
|
+
*,
|
110
|
+
page_info: PageInfo,
|
111
|
+
request: Request,
|
112
|
+
):
|
113
|
+
config_data = to_config_data()
|
114
|
+
|
115
|
+
system_slot = get_app_slot()
|
116
|
+
default_app_slot = get_default_app_slot()
|
117
|
+
html_resource = system_slot._html_resource
|
118
|
+
default_html_resource = default_app_slot._html_resource
|
119
|
+
|
120
|
+
favicon_url = FAVICON_LINK
|
121
|
+
if html_resource.favicon:
|
122
|
+
favicon_url = resource.record_resource(html_resource.favicon)
|
123
|
+
else:
|
124
|
+
if default_html_resource.favicon:
|
125
|
+
favicon_url = resource.record_resource(default_html_resource.favicon)
|
126
|
+
|
127
|
+
model = web_template.WebTemplateModel(
|
128
|
+
vue_js_link=VUE_JS_HASH_LINK,
|
129
|
+
instaui_js_link=INSTAUI_JS_HASH_LINK,
|
130
|
+
css_links=[
|
131
|
+
APP_CSS_LINK,
|
132
|
+
],
|
133
|
+
config_dict=config_data,
|
134
|
+
favicon_url=favicon_url,
|
135
|
+
title=html_resource.title
|
136
|
+
or default_html_resource.title
|
137
|
+
or consts.PAGE_TITLE,
|
138
|
+
)
|
139
|
+
|
140
|
+
if html_resource.use_tailwind is None:
|
141
|
+
if default_html_resource.use_tailwind:
|
142
|
+
model.js_links.append(JsLink(TAILWIND_JS_HASH_LINK))
|
143
|
+
else:
|
144
|
+
if html_resource.use_tailwind:
|
145
|
+
model.js_links.append(JsLink(TAILWIND_JS_HASH_LINK))
|
146
|
+
|
147
|
+
# register custom components
|
148
|
+
for component in system_slot._component_dependencies:
|
149
|
+
if not component.esm:
|
150
|
+
continue
|
151
|
+
|
152
|
+
model.vue_app_component.append(
|
153
|
+
VueAppComponent(
|
154
|
+
name=component.tag_name,
|
155
|
+
url=resource.record_resource(component.esm),
|
156
|
+
)
|
157
|
+
)
|
158
|
+
|
159
|
+
if component.css:
|
160
|
+
for css_link in component.css:
|
161
|
+
model.css_links.append(resource.record_resource(css_link))
|
162
|
+
|
163
|
+
# register custom plugins
|
164
|
+
for plugin in set(
|
165
|
+
itertools.chain(
|
166
|
+
system_slot._plugin_dependencies, default_app_slot._plugin_dependencies
|
167
|
+
)
|
168
|
+
):
|
169
|
+
if not plugin.esm:
|
170
|
+
continue
|
171
|
+
|
172
|
+
model.vue_app_use.append(plugin.name)
|
173
|
+
|
174
|
+
model.add_extra_import_map(
|
175
|
+
plugin.name, resource.record_resource(plugin.esm)
|
176
|
+
)
|
177
|
+
|
178
|
+
if plugin.css:
|
179
|
+
for css_link in plugin.css:
|
180
|
+
model.css_links.append(resource.record_resource(css_link))
|
181
|
+
|
182
|
+
# css file link to web static link
|
183
|
+
for link, attrs in itertools.chain(
|
184
|
+
html_resource._css_links.items(), default_html_resource._css_links.items()
|
185
|
+
):
|
186
|
+
if isinstance(link, Path):
|
187
|
+
model.css_links.append(resource.record_resource(link))
|
188
|
+
|
189
|
+
# js file link to web static link
|
190
|
+
for info in itertools.chain(
|
191
|
+
html_resource._js_links, default_html_resource._js_links
|
192
|
+
):
|
193
|
+
if isinstance(info.link, Path):
|
194
|
+
model.js_links.append(JsLink((resource.record_resource(info.link))))
|
195
|
+
|
196
|
+
for js_code in itertools.chain(
|
197
|
+
html_resource._script_tags, default_html_resource._script_tags
|
198
|
+
):
|
199
|
+
model.script_tags.append(js_code)
|
200
|
+
|
201
|
+
for sylte_code in itertools.chain(
|
202
|
+
html_resource._style_tags, default_html_resource._style_tags
|
203
|
+
):
|
204
|
+
model.style_tags.append(sylte_code)
|
205
|
+
|
206
|
+
model.prefix = request.headers.get(
|
207
|
+
"X-Forwarded-Prefix", request.scope.get("root_path", "")
|
208
|
+
)
|
209
|
+
|
210
|
+
return web_template.render_web_html(model)
|
211
|
+
|
212
|
+
def _update_page_info(self, request: Request, page_info: PageInfo):
|
213
|
+
app = get_app_slot()
|
214
|
+
|
215
|
+
app._page_path = page_info.path
|
216
|
+
app._page_params = request.path_params
|
217
|
+
app._query_params = dict(request.query_params)
|
218
|
+
|
219
|
+
def _remove_route(self, path: str) -> None:
|
220
|
+
self.app.routes[:] = [
|
221
|
+
r for r in self.app.routes if getattr(r, "path", None) != path
|
222
|
+
]
|
223
|
+
|
224
|
+
def try_close_server(self):
|
225
|
+
UvicornServer.instance.should_exit = True
|
226
|
+
|
227
|
+
def run(
|
228
|
+
self,
|
229
|
+
host="0.0.0.0",
|
230
|
+
port=8080,
|
231
|
+
reload: bool = True,
|
232
|
+
reload_dirs: str = ".",
|
233
|
+
reload_includes: str = "*.py",
|
234
|
+
reload_excludes: str = ".*, .py[cod], .sw.*, ~*",
|
235
|
+
log_level="info",
|
236
|
+
workers: int | None = None,
|
237
|
+
uds: str | None = None,
|
238
|
+
**kwargs: Any,
|
239
|
+
):
|
240
|
+
if multiprocessing.current_process().name != "MainProcess":
|
241
|
+
return
|
242
|
+
|
243
|
+
if reload and not hasattr(__main__, "__file__"):
|
244
|
+
reload = False
|
245
|
+
|
246
|
+
config = uvicorn.Config(
|
247
|
+
APP_IMPORT_STRING if reload else self.app,
|
248
|
+
host=host,
|
249
|
+
port=port,
|
250
|
+
reload=reload,
|
251
|
+
log_level=log_level,
|
252
|
+
workers=workers,
|
253
|
+
uds=uds,
|
254
|
+
reload_includes=_split_args(reload_includes) if reload else None,
|
255
|
+
reload_excludes=_split_args(reload_excludes) if reload else None,
|
256
|
+
reload_dirs=_split_args(reload_dirs) if reload else None,
|
257
|
+
**kwargs,
|
258
|
+
)
|
259
|
+
|
260
|
+
UvicornServer.create_singleton(config, [debug_mode_router.when_server_reload])
|
261
|
+
|
262
|
+
if config.should_reload:
|
263
|
+
ChangeReload(config, target=UvicornServer.instance.run, sockets=[]).run()
|
264
|
+
else:
|
265
|
+
UvicornServer.instance.run()
|
266
|
+
|
267
|
+
if config.uds:
|
268
|
+
os.remove(config.uds) # pragma: py-win32
|
269
|
+
|
270
|
+
def run_with(self, app):
|
271
|
+
assert isinstance(app, FastAPI), "app must be a FastAPI instance"
|
272
|
+
|
273
|
+
@staticmethod
|
274
|
+
def add_instaui_static(app: FastAPI):
|
275
|
+
app.mount(
|
276
|
+
INSTAUI_STATIC_URL,
|
277
|
+
StaticFiles(directory=consts._STATIC_DIR),
|
278
|
+
name=INSTAUI_STATIC_URL,
|
279
|
+
)
|
280
|
+
|
281
|
+
|
282
|
+
def _split_args(args: str):
|
283
|
+
return [a.strip() for a in args.split(",")]
|
284
|
+
|
285
|
+
|
286
|
+
@contextmanager
|
287
|
+
def _execute_request_lifespans():
|
288
|
+
events = [iter(event()) for event in get_launch_collector().page_request_lifespans]
|
289
|
+
for event in events:
|
290
|
+
next(event)
|
291
|
+
|
292
|
+
yield
|
293
|
+
|
294
|
+
for event in events:
|
295
|
+
try:
|
296
|
+
next(event)
|
297
|
+
except StopIteration:
|
298
|
+
pass
|
@@ -0,0 +1,53 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from pathlib import Path
|
3
|
+
from typing import Any, Dict, Literal, Optional, Union
|
4
|
+
from instaui.common.jsonable import dumps, dumps2dict
|
5
|
+
from instaui.runtime._app import get_app_slot
|
6
|
+
|
7
|
+
|
8
|
+
def add_css_link(href: Union[str, Path]):
|
9
|
+
get_app_slot()._html_resource.add_css_link(href)
|
10
|
+
|
11
|
+
|
12
|
+
def add_js_link(
|
13
|
+
link: Union[str, Path],
|
14
|
+
*,
|
15
|
+
type: Optional[Literal["module"]] = None,
|
16
|
+
):
|
17
|
+
attrs = {
|
18
|
+
"type": type,
|
19
|
+
}
|
20
|
+
|
21
|
+
get_app_slot()._html_resource.add_js_link(link, attrs=attrs)
|
22
|
+
|
23
|
+
|
24
|
+
def add_style(content: str):
|
25
|
+
get_app_slot()._html_resource.add_style_tag(content)
|
26
|
+
|
27
|
+
|
28
|
+
def use_tailwind(value=True):
|
29
|
+
get_app_slot()._html_resource.use_tailwind = value
|
30
|
+
|
31
|
+
|
32
|
+
def use_page_title(title: str):
|
33
|
+
get_app_slot()._html_resource.title = title
|
34
|
+
|
35
|
+
|
36
|
+
def use_favicon(favicon: Path):
|
37
|
+
get_app_slot()._html_resource.favicon = favicon
|
38
|
+
|
39
|
+
|
40
|
+
def add_js_code(code: str, *, script_attrs: Optional[Dict[str, Any]] = None):
|
41
|
+
get_app_slot()._html_resource.add_script_tag(code, script_attrs=script_attrs)
|
42
|
+
|
43
|
+
|
44
|
+
def add_vue_app_use(name: str):
|
45
|
+
get_app_slot()._html_resource.add_vue_app_use(name)
|
46
|
+
|
47
|
+
|
48
|
+
def to_config_data() -> Dict:
|
49
|
+
return dumps2dict(get_app_slot())
|
50
|
+
|
51
|
+
|
52
|
+
def to_json(indent=False):
|
53
|
+
return dumps(get_app_slot(), indent=indent)
|
@@ -0,0 +1,13 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import Callable
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from urllib.parse import quote
|
5
|
+
|
6
|
+
|
7
|
+
@dataclass
|
8
|
+
class PageInfo:
|
9
|
+
path: str
|
10
|
+
func: Callable
|
11
|
+
|
12
|
+
def create_key(self) -> str:
|
13
|
+
return quote(self.path)
|