instaui 0.1.0__py3-none-any.whl
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/__init__.py +9 -0
- instaui/_helper/observable_helper.py +35 -0
- instaui/boot_info.py +43 -0
- instaui/common/jsonable.py +37 -0
- instaui/components/__init__.py +0 -0
- instaui/components/column.py +18 -0
- instaui/components/component.py +47 -0
- instaui/components/content.py +34 -0
- instaui/components/directive.py +55 -0
- instaui/components/element.py +462 -0
- instaui/components/grid.py +80 -0
- instaui/components/html/__init__.py +36 -0
- instaui/components/html/_mixins.py +34 -0
- instaui/components/html/button.py +38 -0
- instaui/components/html/checkbox.py +42 -0
- instaui/components/html/date.py +28 -0
- instaui/components/html/div.py +7 -0
- instaui/components/html/form.py +7 -0
- instaui/components/html/input.py +28 -0
- instaui/components/html/label.py +21 -0
- instaui/components/html/li.py +17 -0
- instaui/components/html/link.py +31 -0
- instaui/components/html/number.py +34 -0
- instaui/components/html/paragraph.py +19 -0
- instaui/components/html/range.py +45 -0
- instaui/components/html/select.py +93 -0
- instaui/components/html/span.py +19 -0
- instaui/components/html/ul.py +20 -0
- instaui/components/match.py +106 -0
- instaui/components/row.py +19 -0
- instaui/components/slot.py +82 -0
- instaui/components/transition_group.py +9 -0
- instaui/components/value_element.py +48 -0
- instaui/components/vfor.py +140 -0
- instaui/components/vif.py +38 -0
- instaui/consts.py +18 -0
- instaui/dependencies/__init__.py +15 -0
- instaui/dependencies/component_registrar.py +82 -0
- instaui/dependencies/installer.py +5 -0
- instaui/event/event_mixin.py +12 -0
- instaui/event/js_event.py +57 -0
- instaui/event/web_event.py +108 -0
- instaui/experimental/__init__.py +4 -0
- instaui/experimental/debug.py +48 -0
- instaui/fastapi_server/_utils.py +42 -0
- instaui/fastapi_server/_uvicorn.py +37 -0
- instaui/fastapi_server/config_router.py +60 -0
- instaui/fastapi_server/debug_mode_router.py +61 -0
- instaui/fastapi_server/event_router.py +58 -0
- instaui/fastapi_server/middlewares.py +19 -0
- instaui/fastapi_server/request_context.py +19 -0
- instaui/fastapi_server/server.py +246 -0
- instaui/fastapi_server/watch_router.py +53 -0
- instaui/handlers/_utils.py +66 -0
- instaui/handlers/computed_handler.py +42 -0
- instaui/handlers/config_handler.py +13 -0
- instaui/handlers/event_handler.py +58 -0
- instaui/handlers/watch_handler.py +57 -0
- instaui/html_tools.py +139 -0
- instaui/inject.py +33 -0
- instaui/js/__init__.py +4 -0
- instaui/js/js_output.py +15 -0
- instaui/js/lambda_func.py +35 -0
- instaui/launch_collector.py +52 -0
- instaui/page_info.py +23 -0
- instaui/runtime/__init__.py +29 -0
- instaui/runtime/_app.py +206 -0
- instaui/runtime/_inner_helper.py +9 -0
- instaui/runtime/context.py +47 -0
- instaui/runtime/dataclass.py +30 -0
- instaui/runtime/resource.py +87 -0
- instaui/runtime/scope.py +107 -0
- instaui/runtime/ui_state_scope.py +15 -0
- instaui/settings/__init__.py +4 -0
- instaui/settings/__settings.py +13 -0
- instaui/skip.py +12 -0
- instaui/spa_router/__init__.py +26 -0
- instaui/spa_router/_components.py +35 -0
- instaui/spa_router/_file_base_utils.py +264 -0
- instaui/spa_router/_functions.py +122 -0
- instaui/spa_router/_install.py +11 -0
- instaui/spa_router/_route_model.py +139 -0
- instaui/spa_router/_router_box.py +40 -0
- instaui/spa_router/_router_output.py +22 -0
- instaui/spa_router/_router_param_var.py +51 -0
- instaui/spa_router/_types.py +4 -0
- instaui/spa_router/templates/page_routes +59 -0
- instaui/static/insta-ui.css +1 -0
- instaui/static/insta-ui.esm-browser.prod.js +3663 -0
- instaui/static/insta-ui.iife.js +29 -0
- instaui/static/insta-ui.iife.js.map +1 -0
- instaui/static/insta-ui.js.map +1 -0
- instaui/static/tailwindcss.min.js +62 -0
- instaui/static/templates/debug/sse.html +117 -0
- instaui/static/templates/web.html +118 -0
- instaui/static/templates/zero.html +55 -0
- instaui/static/vue.esm-browser.prod.js +9 -0
- instaui/static/vue.global.prod.js +9 -0
- instaui/static/vue.runtime.esm-browser.prod.js +5 -0
- instaui/systems/file_system.py +17 -0
- instaui/systems/func_system.py +104 -0
- instaui/systems/js_system.py +22 -0
- instaui/systems/pydantic_system.py +27 -0
- instaui/systems/string_system.py +10 -0
- instaui/template/__init__.py +4 -0
- instaui/template/env.py +7 -0
- instaui/template/web_template.py +55 -0
- instaui/template/zero_template.py +24 -0
- instaui/ui/__init__.py +121 -0
- instaui/ui/events.py +25 -0
- instaui/ui_functions/input_slient_data.py +16 -0
- instaui/ui_functions/server.py +13 -0
- instaui/ui_functions/str_format.py +36 -0
- instaui/ui_functions/ui_page.py +31 -0
- instaui/ui_functions/ui_types.py +13 -0
- instaui/ui_functions/url_location.py +33 -0
- instaui/vars/__init__.py +13 -0
- instaui/vars/_types.py +8 -0
- instaui/vars/_utils.py +12 -0
- instaui/vars/data.py +68 -0
- instaui/vars/element_ref.py +42 -0
- instaui/vars/event_context.py +45 -0
- instaui/vars/event_extend.py +0 -0
- instaui/vars/js_computed.py +95 -0
- instaui/vars/mixin_types/common_type.py +5 -0
- instaui/vars/mixin_types/element_binding.py +10 -0
- instaui/vars/mixin_types/observable.py +7 -0
- instaui/vars/mixin_types/pathable.py +14 -0
- instaui/vars/mixin_types/py_binding.py +13 -0
- instaui/vars/mixin_types/str_format_binding.py +8 -0
- instaui/vars/mixin_types/var_type.py +5 -0
- instaui/vars/path_var.py +89 -0
- instaui/vars/ref.py +103 -0
- instaui/vars/slot_prop.py +46 -0
- instaui/vars/state.py +82 -0
- instaui/vars/types.py +24 -0
- instaui/vars/vfor_item.py +204 -0
- instaui/vars/vue_computed.py +82 -0
- instaui/vars/web_computed.py +157 -0
- instaui/vars/web_view_computed.py +1 -0
- instaui/version.py +3 -0
- instaui/watch/_types.py +4 -0
- instaui/watch/_utils.py +3 -0
- instaui/watch/js_watch.py +74 -0
- instaui/watch/vue_watch.py +61 -0
- instaui/watch/web_watch.py +123 -0
- instaui/zero/__init__.py +3 -0
- instaui/zero/scope.py +9 -0
- instaui-0.1.0.dist-info/LICENSE +21 -0
- instaui-0.1.0.dist-info/METADATA +154 -0
- instaui-0.1.0.dist-info/RECORD +152 -0
- instaui-0.1.0.dist-info/WHEEL +4 -0
instaui/html_tools.py
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from pathlib import Path
|
3
|
+
from typing import Dict, Literal, Optional, Union
|
4
|
+
import instaui.consts as consts
|
5
|
+
from instaui.common.jsonable import dumps, dumps2dict
|
6
|
+
from instaui.runtime import get_app_slot, HtmlResource
|
7
|
+
from instaui.template import render_zero_html
|
8
|
+
|
9
|
+
|
10
|
+
def add_css_link(href: Union[str, Path], *, shared: bool = False):
|
11
|
+
if shared:
|
12
|
+
HtmlResource.default_css_link(href)
|
13
|
+
|
14
|
+
get_app_slot()._html_resource.add_css_link(href)
|
15
|
+
|
16
|
+
|
17
|
+
def add_js_link(
|
18
|
+
link: Union[str, Path],
|
19
|
+
*,
|
20
|
+
shared: bool = False,
|
21
|
+
type: Optional[Literal["module", "importmap"]] = None,
|
22
|
+
):
|
23
|
+
attrs = {
|
24
|
+
"type": type,
|
25
|
+
}
|
26
|
+
|
27
|
+
if shared:
|
28
|
+
HtmlResource.default_js_link(link, attrs=attrs)
|
29
|
+
|
30
|
+
get_app_slot()._html_resource.add_js_link(link, attrs=attrs)
|
31
|
+
|
32
|
+
|
33
|
+
def add_style(content: str, *, shared: bool = False):
|
34
|
+
if shared:
|
35
|
+
HtmlResource.default_style_tag(content)
|
36
|
+
get_app_slot()._html_resource.add_style_tag(content)
|
37
|
+
|
38
|
+
|
39
|
+
def add_js_code(code: str, *, shared: bool = False):
|
40
|
+
if shared:
|
41
|
+
HtmlResource.default_script_tag(code)
|
42
|
+
get_app_slot()._html_resource.add_script_tag(code)
|
43
|
+
|
44
|
+
|
45
|
+
def add_vue_app_use(name: str, *, shared: bool = False):
|
46
|
+
if shared:
|
47
|
+
HtmlResource.default_vue_app_use(name)
|
48
|
+
get_app_slot()._html_resource.add_vue_app_use(name)
|
49
|
+
|
50
|
+
|
51
|
+
def to_config_data() -> Dict:
|
52
|
+
return dumps2dict(get_app_slot())
|
53
|
+
|
54
|
+
|
55
|
+
def to_json(indent=False):
|
56
|
+
return dumps(get_app_slot(), indent=indent)
|
57
|
+
|
58
|
+
|
59
|
+
def to_html(file: Union[str, Path]):
|
60
|
+
if isinstance(file, str):
|
61
|
+
import inspect
|
62
|
+
|
63
|
+
frame = inspect.currentframe().f_back # type: ignore
|
64
|
+
assert frame is not None
|
65
|
+
script_file = inspect.getfile(frame)
|
66
|
+
file = Path(script_file).parent.joinpath(file)
|
67
|
+
|
68
|
+
file = Path(file)
|
69
|
+
system_slot = get_app_slot()
|
70
|
+
html_resource = system_slot._html_resource
|
71
|
+
|
72
|
+
if html_resource.use_tailwind:
|
73
|
+
html_resource.add_js_link(consts.TAILWIND_JS_PATH, insert_before=0)
|
74
|
+
html_resource.add_js_link(consts.APP_IIFE_JS_PATH, insert_before=0)
|
75
|
+
html_resource.add_js_link(consts.VUE_IIFE_JS_PATH, insert_before=0)
|
76
|
+
html_resource.add_css_link(consts.APP_CSS_PATH)
|
77
|
+
|
78
|
+
# register custom components
|
79
|
+
for component in system_slot._js_components:
|
80
|
+
if not component.iife:
|
81
|
+
continue
|
82
|
+
|
83
|
+
html_resource.add_js_link(component.iife)
|
84
|
+
|
85
|
+
if component.css:
|
86
|
+
for css_link in component.css:
|
87
|
+
html_resource.add_css_link(css_link)
|
88
|
+
|
89
|
+
html_resource.add_vue_app_use(component.name)
|
90
|
+
|
91
|
+
for plugin in system_slot._plugins:
|
92
|
+
if not plugin.iife:
|
93
|
+
continue
|
94
|
+
|
95
|
+
html_resource.add_js_link(plugin.iife)
|
96
|
+
|
97
|
+
if plugin.css:
|
98
|
+
for css_link in plugin.css:
|
99
|
+
html_resource.add_css_link(css_link)
|
100
|
+
|
101
|
+
html_resource.add_vue_app_use(plugin.name)
|
102
|
+
|
103
|
+
_css_file_link_to_style_code(html_resource)
|
104
|
+
_js_file_link_to_script_code(html_resource)
|
105
|
+
|
106
|
+
raw = render_zero_html(to_json())
|
107
|
+
file.write_text(raw, "utf8")
|
108
|
+
|
109
|
+
return file.resolve().absolute()
|
110
|
+
|
111
|
+
|
112
|
+
def _css_file_link_to_style_code(html_resource: HtmlResource):
|
113
|
+
files = [link for link in html_resource._css_links.keys() if isinstance(link, Path)]
|
114
|
+
|
115
|
+
for file in files:
|
116
|
+
content = file.read_text(encoding="utf-8")
|
117
|
+
html_resource.add_style_tag(content)
|
118
|
+
|
119
|
+
# remove file links
|
120
|
+
html_resource._css_links = {
|
121
|
+
link: None
|
122
|
+
for link in html_resource._css_links.keys()
|
123
|
+
if not isinstance(link, Path)
|
124
|
+
}
|
125
|
+
|
126
|
+
|
127
|
+
def _js_file_link_to_script_code(html_resource: HtmlResource):
|
128
|
+
files = (
|
129
|
+
info.link for info in html_resource._js_links if isinstance(info.link, Path)
|
130
|
+
)
|
131
|
+
|
132
|
+
for file in files:
|
133
|
+
content = file.read_text(encoding="utf-8")
|
134
|
+
html_resource.add_script_tag(content)
|
135
|
+
|
136
|
+
# remove file links
|
137
|
+
html_resource._js_links = [
|
138
|
+
info for info in html_resource._js_links if not isinstance(info.link, Path)
|
139
|
+
]
|
instaui/inject.py
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
import typing
|
2
|
+
from instaui.runtime import ui_state_scope
|
3
|
+
|
4
|
+
|
5
|
+
_T = typing.TypeVar("_T")
|
6
|
+
|
7
|
+
|
8
|
+
def injection_key(key: str) -> _T:
|
9
|
+
""" creates a key for injection
|
10
|
+
|
11
|
+
Args:
|
12
|
+
key (str): key to use for injection
|
13
|
+
|
14
|
+
Example:
|
15
|
+
.. code-block:: python
|
16
|
+
text_key :str = injection_key("text")
|
17
|
+
|
18
|
+
# state model
|
19
|
+
class Person(ui.StateModel):
|
20
|
+
name:str
|
21
|
+
|
22
|
+
person_key :Person = injection_key(Person(name='foo'))
|
23
|
+
"""
|
24
|
+
return key # type: ignore
|
25
|
+
|
26
|
+
|
27
|
+
def inject_state(key: _T) -> _T:
|
28
|
+
return typing.cast(_T, ui_state_scope.load_state(key)) # type: ignore
|
29
|
+
|
30
|
+
|
31
|
+
def provide_state(key: _T, obj: _T) -> _T:
|
32
|
+
ui_state_scope.save_state(key, obj)
|
33
|
+
return obj
|
instaui/js/__init__.py
ADDED
instaui/js/js_output.py
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
from instaui.common.jsonable import Jsonable
|
2
|
+
from instaui.vars.mixin_types.py_binding import CanOutputMixin
|
3
|
+
|
4
|
+
|
5
|
+
class JsOutput(Jsonable, CanOutputMixin):
|
6
|
+
def __init__(self):
|
7
|
+
self.type = "jsOutput"
|
8
|
+
|
9
|
+
def _to_output_config(self):
|
10
|
+
return self._to_json_dict()
|
11
|
+
|
12
|
+
def _to_json_dict(self):
|
13
|
+
data = super()._to_json_dict()
|
14
|
+
|
15
|
+
return data
|
@@ -0,0 +1,35 @@
|
|
1
|
+
from typing import Dict
|
2
|
+
from instaui.common.jsonable import Jsonable
|
3
|
+
from instaui.vars.mixin_types.element_binding import ElementBindingMixin
|
4
|
+
|
5
|
+
|
6
|
+
class LambdaFunc(Jsonable, ElementBindingMixin):
|
7
|
+
def __init__(
|
8
|
+
self, code: str, *, bindings: Dict[str, ElementBindingMixin], computed=False
|
9
|
+
):
|
10
|
+
self.code = code
|
11
|
+
self.type = "js"
|
12
|
+
self._bindings = bindings
|
13
|
+
self._computed = computed
|
14
|
+
|
15
|
+
def _to_binding_config(self) -> Dict:
|
16
|
+
return self._to_json_dict()
|
17
|
+
|
18
|
+
def _to_js_binding_config(self):
|
19
|
+
return self._to_json_dict()
|
20
|
+
|
21
|
+
def _to_element_binding_config(self):
|
22
|
+
return self._to_json_dict()
|
23
|
+
|
24
|
+
def _to_json_dict(self):
|
25
|
+
data = super()._to_json_dict()
|
26
|
+
|
27
|
+
if self._bindings:
|
28
|
+
data["bind"] = {
|
29
|
+
k: v._to_element_binding_config() for k, v in self._bindings.items()
|
30
|
+
}
|
31
|
+
|
32
|
+
if self._computed is True:
|
33
|
+
data["ext"] = ["cpt"]
|
34
|
+
|
35
|
+
return data
|
@@ -0,0 +1,52 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import Callable, Dict, List, Optional
|
3
|
+
from instaui.consts import _T_App_Mode
|
4
|
+
from instaui.page_info import PageInfo
|
5
|
+
from instaui.systems import func_system
|
6
|
+
|
7
|
+
|
8
|
+
class LaunchCollector:
|
9
|
+
_instance: Optional[LaunchCollector] = None
|
10
|
+
|
11
|
+
@classmethod
|
12
|
+
def get_instance(cls, *args, **kwargs):
|
13
|
+
if cls._instance is None:
|
14
|
+
cls._instance = cls(*args, **kwargs)
|
15
|
+
return cls._instance
|
16
|
+
|
17
|
+
def __init__(self) -> None:
|
18
|
+
self._page_router: Dict[str, PageInfo] = {}
|
19
|
+
self._page_request_lifespans: Dict[int, Callable] = {}
|
20
|
+
self._app_mode: _T_App_Mode = "web"
|
21
|
+
|
22
|
+
@property
|
23
|
+
def page_request_lifespans(self) -> List[Callable]:
|
24
|
+
return list(self._page_request_lifespans.values())
|
25
|
+
|
26
|
+
def register_page(self, info: PageInfo) -> None:
|
27
|
+
self._page_router[info.path] = info
|
28
|
+
|
29
|
+
def add_page_request_lifespan(self, lifespan: Callable) -> int:
|
30
|
+
"""Register a function to be called on each page request.
|
31
|
+
|
32
|
+
Args:
|
33
|
+
lifespan (Callable): A function to be called on each page request.
|
34
|
+
|
35
|
+
Returns:
|
36
|
+
int: A unique key to identify the registered lifespan function.
|
37
|
+
"""
|
38
|
+
key = id(lifespan)
|
39
|
+
self._page_request_lifespans[key] = func_system.make_fn_to_generator(lifespan)
|
40
|
+
|
41
|
+
return key
|
42
|
+
|
43
|
+
def remove_page_request_lifespan(self, key: int) -> None:
|
44
|
+
if key in self._page_request_lifespans:
|
45
|
+
del self._page_request_lifespans[key]
|
46
|
+
|
47
|
+
def set_app_mode(self, mode: _T_App_Mode) -> None:
|
48
|
+
self._app_mode = mode
|
49
|
+
|
50
|
+
|
51
|
+
def get_launch_collector() -> LaunchCollector:
|
52
|
+
return LaunchCollector.get_instance()
|
instaui/page_info.py
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import Callable, Optional, TYPE_CHECKING
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from urllib.parse import quote
|
5
|
+
|
6
|
+
|
7
|
+
if TYPE_CHECKING:
|
8
|
+
from instaui.runtime.resource import HtmlResource
|
9
|
+
|
10
|
+
|
11
|
+
@dataclass
|
12
|
+
class PageInfo:
|
13
|
+
path: str
|
14
|
+
func: Callable
|
15
|
+
page_loading: Optional[Callable] = None
|
16
|
+
use_tailwind: Optional[bool] = None
|
17
|
+
|
18
|
+
def create_key(self) -> str:
|
19
|
+
return quote(self.path)
|
20
|
+
|
21
|
+
def apply_settings(self, html_resource: HtmlResource):
|
22
|
+
if self.use_tailwind is not None:
|
23
|
+
html_resource.use_tailwind = self.use_tailwind
|
@@ -0,0 +1,29 @@
|
|
1
|
+
from ._app import (
|
2
|
+
get_app_slot,
|
3
|
+
reset_app_slot,
|
4
|
+
new_app_slot,
|
5
|
+
use_default_app_slot,
|
6
|
+
get_slot_stacks,
|
7
|
+
pop_slot,
|
8
|
+
get_current_scope,
|
9
|
+
new_scope,
|
10
|
+
check_default_app_slot_or_error,
|
11
|
+
in_default_app_slot,
|
12
|
+
)
|
13
|
+
from ._inner_helper import check_web_only_mode_or_error
|
14
|
+
from .resource import HtmlResource
|
15
|
+
|
16
|
+
__all__ = [
|
17
|
+
"get_slot_stacks",
|
18
|
+
"new_scope",
|
19
|
+
"get_current_scope",
|
20
|
+
"get_app_slot",
|
21
|
+
"reset_app_slot",
|
22
|
+
"new_app_slot",
|
23
|
+
"use_default_app_slot",
|
24
|
+
"pop_slot",
|
25
|
+
"check_web_only_mode_or_error",
|
26
|
+
"check_default_app_slot_or_error",
|
27
|
+
"in_default_app_slot",
|
28
|
+
"HtmlResource",
|
29
|
+
]
|
instaui/runtime/_app.py
ADDED
@@ -0,0 +1,206 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Dict, List, Optional, Set
|
4
|
+
from instaui.common.jsonable import Jsonable
|
5
|
+
from .resource import HtmlResource
|
6
|
+
from instaui.consts import _T_App_Mode
|
7
|
+
from contextvars import ContextVar, Token
|
8
|
+
from copy import copy
|
9
|
+
from contextlib import contextmanager
|
10
|
+
from instaui.runtime.scope import Scope, GlobalScope
|
11
|
+
from types import MappingProxyType
|
12
|
+
|
13
|
+
if TYPE_CHECKING:
|
14
|
+
from instaui.components.component import Component
|
15
|
+
from instaui.components.slot import Slot
|
16
|
+
from instaui.dependencies import ComponentRegistrationInfo, PluginRegistrationInfo
|
17
|
+
from instaui.spa_router._route_model import RouteCollector
|
18
|
+
|
19
|
+
|
20
|
+
class App(Jsonable):
|
21
|
+
_default_app_slot: ClassVar[Optional[App]] = None
|
22
|
+
_default_js_components: ClassVar[Set[ComponentRegistrationInfo]] = set()
|
23
|
+
_default_plugins: ClassVar[Set[PluginRegistrationInfo]] = set()
|
24
|
+
|
25
|
+
def __init__(self, *, mode: _T_App_Mode) -> None:
|
26
|
+
super().__init__()
|
27
|
+
self._scope_id_counter = 0
|
28
|
+
self._vfor_id_counter = 0
|
29
|
+
self._slot_id_counter = 0
|
30
|
+
self.mode: _T_App_Mode = mode
|
31
|
+
self.items: List[Component] = []
|
32
|
+
self._slots_stacks: List[Slot] = []
|
33
|
+
|
34
|
+
defalut_scope = self.create_scope()
|
35
|
+
self._scope_stack: List[Scope] = [defalut_scope]
|
36
|
+
self._scopes: List[Scope] = [defalut_scope]
|
37
|
+
self._html_resource = HtmlResource()
|
38
|
+
self._js_components: Set[ComponentRegistrationInfo] = copy(
|
39
|
+
self._default_js_components
|
40
|
+
)
|
41
|
+
|
42
|
+
self._plugins: Set[PluginRegistrationInfo] = copy(self._default_plugins)
|
43
|
+
self._page_path: Optional[str] = None
|
44
|
+
self._page_params: Dict[str, Any] = {}
|
45
|
+
self._query_params: Dict[str, Any] = {}
|
46
|
+
self._route_collector: Optional[RouteCollector] = None
|
47
|
+
|
48
|
+
@property
|
49
|
+
def page_path(self) -> str:
|
50
|
+
assert self._page_path is not None, "Page path is not set"
|
51
|
+
return self._page_path # type: ignore
|
52
|
+
|
53
|
+
@property
|
54
|
+
def page_params(self):
|
55
|
+
return MappingProxyType(self._page_params)
|
56
|
+
|
57
|
+
@property
|
58
|
+
def query_params(self):
|
59
|
+
return MappingProxyType(self._query_params)
|
60
|
+
|
61
|
+
def create_scope(self) -> Scope:
|
62
|
+
self._scope_id_counter += 1
|
63
|
+
scope = Scope(str(self._scope_id_counter))
|
64
|
+
return scope
|
65
|
+
|
66
|
+
def generate_vfor_id(self) -> str:
|
67
|
+
self._vfor_id_counter += 1
|
68
|
+
return str(self._vfor_id_counter)
|
69
|
+
|
70
|
+
def generate_slot_id(self) -> str:
|
71
|
+
self._slot_id_counter += 1
|
72
|
+
return str(self._slot_id_counter)
|
73
|
+
|
74
|
+
def reset_html_resource(self):
|
75
|
+
self._html_resource = HtmlResource()
|
76
|
+
|
77
|
+
def register_component(self, component: ComponentRegistrationInfo) -> None:
|
78
|
+
self._js_components.add(component)
|
79
|
+
|
80
|
+
def register_plugin(self, plugin: PluginRegistrationInfo) -> None:
|
81
|
+
self._plugins.add(plugin)
|
82
|
+
|
83
|
+
def register_router(self, collector: RouteCollector) -> None:
|
84
|
+
self._route_collector = collector
|
85
|
+
|
86
|
+
def append_component_to_container(self, component: Component):
|
87
|
+
if self._slots_stacks:
|
88
|
+
self._slots_stacks[-1]._children.append(component)
|
89
|
+
else:
|
90
|
+
self.items.append(component)
|
91
|
+
|
92
|
+
def _to_json_dict(self):
|
93
|
+
data = super()._to_json_dict()
|
94
|
+
|
95
|
+
if self._page_path:
|
96
|
+
url_info = {"path": self.page_path}
|
97
|
+
if self._page_params:
|
98
|
+
url_info["params"] = self._page_params # type: ignore
|
99
|
+
|
100
|
+
data["url"] = url_info
|
101
|
+
|
102
|
+
assert len(self._scopes) == 1, "Only one scope is allowed"
|
103
|
+
data["scope"] = self._scopes[0]
|
104
|
+
|
105
|
+
if self._route_collector is not None:
|
106
|
+
data["router"] = self._route_collector.model_dump(
|
107
|
+
exclude_defaults=True, by_alias=True
|
108
|
+
)
|
109
|
+
|
110
|
+
return data
|
111
|
+
|
112
|
+
@classmethod
|
113
|
+
def _create_default(cls):
|
114
|
+
if cls._default_app_slot is None:
|
115
|
+
cls._default_app_slot = DefaultApp(mode="web")
|
116
|
+
return cls._default_app_slot
|
117
|
+
|
118
|
+
@classmethod
|
119
|
+
def default_js_components(cls, component: ComponentRegistrationInfo):
|
120
|
+
cls._default_js_components.add(component)
|
121
|
+
|
122
|
+
@classmethod
|
123
|
+
def default_plugins(cls, plugin: PluginRegistrationInfo):
|
124
|
+
cls._default_plugins.add(plugin)
|
125
|
+
|
126
|
+
|
127
|
+
class DefaultApp(App):
|
128
|
+
_instance = None
|
129
|
+
|
130
|
+
def __new__(cls, *args, **kwargs):
|
131
|
+
if cls._instance is None:
|
132
|
+
cls._instance = super(DefaultApp, cls).__new__(cls)
|
133
|
+
return cls._instance
|
134
|
+
|
135
|
+
def create_scope(self) -> Scope:
|
136
|
+
self._scope_id_counter += 1
|
137
|
+
scope = GlobalScope(str(self._scope_id_counter))
|
138
|
+
return scope
|
139
|
+
|
140
|
+
def append_component_to_container(self, component: Component):
|
141
|
+
raise ValueError("Operations are not allowed outside of ui.page")
|
142
|
+
|
143
|
+
|
144
|
+
_app_var: ContextVar[App] = ContextVar("_app_var", default=App._create_default())
|
145
|
+
|
146
|
+
|
147
|
+
def use_default_app_slot():
|
148
|
+
assert App._default_app_slot is not None, "Default app slot is not set"
|
149
|
+
_app_var.set(App._default_app_slot)
|
150
|
+
|
151
|
+
|
152
|
+
def get_default_app_slot():
|
153
|
+
return App._create_default()
|
154
|
+
|
155
|
+
|
156
|
+
def get_app_slot() -> App:
|
157
|
+
return _app_var.get()
|
158
|
+
|
159
|
+
|
160
|
+
def get_current_scope():
|
161
|
+
current_scope = get_app_slot()._scope_stack[-1]
|
162
|
+
if current_scope is None:
|
163
|
+
raise ValueError("No current scope")
|
164
|
+
return current_scope
|
165
|
+
|
166
|
+
|
167
|
+
@contextmanager
|
168
|
+
def new_scope(*, append_to_app: bool = True):
|
169
|
+
app = get_app_slot()
|
170
|
+
scope = app.create_scope()
|
171
|
+
|
172
|
+
if append_to_app:
|
173
|
+
app._scopes.append(scope)
|
174
|
+
|
175
|
+
scope_stack = app._scope_stack
|
176
|
+
scope_stack.append(scope)
|
177
|
+
|
178
|
+
yield scope
|
179
|
+
scope_stack.pop()
|
180
|
+
|
181
|
+
|
182
|
+
def get_slot_stacks():
|
183
|
+
return get_app_slot()._slots_stacks
|
184
|
+
|
185
|
+
|
186
|
+
def pop_slot():
|
187
|
+
get_slot_stacks().pop()
|
188
|
+
|
189
|
+
|
190
|
+
def new_app_slot(mode: _T_App_Mode):
|
191
|
+
return _app_var.set(App(mode=mode))
|
192
|
+
|
193
|
+
|
194
|
+
def reset_app_slot(token: Token[App]):
|
195
|
+
_app_var.reset(token)
|
196
|
+
|
197
|
+
|
198
|
+
def in_default_app_slot():
|
199
|
+
return isinstance(get_app_slot(), DefaultApp)
|
200
|
+
|
201
|
+
|
202
|
+
def check_default_app_slot_or_error(
|
203
|
+
error_message="Operations are not allowed outside of ui.page",
|
204
|
+
):
|
205
|
+
if isinstance(get_app_slot(), DefaultApp):
|
206
|
+
raise ValueError(error_message)
|
@@ -0,0 +1,9 @@
|
|
1
|
+
from instaui.runtime.context import get_context
|
2
|
+
|
3
|
+
|
4
|
+
def check_web_only_mode_or_error(method_name: str = ""):
|
5
|
+
mode = get_context().app_mode
|
6
|
+
if mode != "web" and mode != "webview":
|
7
|
+
raise Exception(
|
8
|
+
f"{method_name} This is a web-only feature. Please use the 'web' or 'webview' mode."
|
9
|
+
)
|
@@ -0,0 +1,47 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import Any, Dict, Optional
|
3
|
+
from instaui import consts
|
4
|
+
from ._app import get_app_slot
|
5
|
+
|
6
|
+
|
7
|
+
class Context:
|
8
|
+
_instance: Optional[Context] = None
|
9
|
+
|
10
|
+
@classmethod
|
11
|
+
def get_instance(cls, *args, **kwargs):
|
12
|
+
if cls._instance is None:
|
13
|
+
cls._instance = cls(*args, **kwargs)
|
14
|
+
return cls._instance
|
15
|
+
|
16
|
+
def __init__(self):
|
17
|
+
self._app_mode: consts._T_App_Mode = "web"
|
18
|
+
self._debug_mode: bool = False
|
19
|
+
self._page_params: Dict[str, Any] = {}
|
20
|
+
|
21
|
+
@property
|
22
|
+
def app_mode(self):
|
23
|
+
return self._app_mode
|
24
|
+
|
25
|
+
@property
|
26
|
+
def debug_mode(self):
|
27
|
+
return self._debug_mode
|
28
|
+
|
29
|
+
@property
|
30
|
+
def page_path(self):
|
31
|
+
return get_app_slot().page_path
|
32
|
+
|
33
|
+
@property
|
34
|
+
def page_params(self):
|
35
|
+
return get_app_slot().page_params
|
36
|
+
|
37
|
+
@property
|
38
|
+
def query_params(self):
|
39
|
+
return get_app_slot().query_params
|
40
|
+
|
41
|
+
|
42
|
+
def get_context():
|
43
|
+
return Context.get_instance()
|
44
|
+
|
45
|
+
|
46
|
+
def set_debug(debug=True):
|
47
|
+
get_context()._debug_mode = debug
|
@@ -0,0 +1,30 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from pathlib import Path
|
3
|
+
from typing import Any, Dict, Union
|
4
|
+
from dataclasses import dataclass, field
|
5
|
+
|
6
|
+
|
7
|
+
@dataclass
|
8
|
+
class JsLink:
|
9
|
+
link: Union[str, Path]
|
10
|
+
attrs: Dict[str, Any] = field(default_factory=dict)
|
11
|
+
|
12
|
+
def create_attrs_str(self):
|
13
|
+
return " ".join(f'{k}="{v}"' for k, v in self.attrs.items() if v is not None)
|
14
|
+
|
15
|
+
|
16
|
+
@dataclass(frozen=True)
|
17
|
+
class VueAppUse:
|
18
|
+
name: str
|
19
|
+
|
20
|
+
|
21
|
+
@dataclass(frozen=True)
|
22
|
+
class VueAppComponent:
|
23
|
+
name: str
|
24
|
+
url: str
|
25
|
+
|
26
|
+
|
27
|
+
@dataclass
|
28
|
+
class ImportMaps:
|
29
|
+
name: str
|
30
|
+
url: str
|