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.
Files changed (152) hide show
  1. instaui/__init__.py +9 -0
  2. instaui/_helper/observable_helper.py +35 -0
  3. instaui/boot_info.py +43 -0
  4. instaui/common/jsonable.py +37 -0
  5. instaui/components/__init__.py +0 -0
  6. instaui/components/column.py +18 -0
  7. instaui/components/component.py +47 -0
  8. instaui/components/content.py +34 -0
  9. instaui/components/directive.py +55 -0
  10. instaui/components/element.py +462 -0
  11. instaui/components/grid.py +80 -0
  12. instaui/components/html/__init__.py +36 -0
  13. instaui/components/html/_mixins.py +34 -0
  14. instaui/components/html/button.py +38 -0
  15. instaui/components/html/checkbox.py +42 -0
  16. instaui/components/html/date.py +28 -0
  17. instaui/components/html/div.py +7 -0
  18. instaui/components/html/form.py +7 -0
  19. instaui/components/html/input.py +28 -0
  20. instaui/components/html/label.py +21 -0
  21. instaui/components/html/li.py +17 -0
  22. instaui/components/html/link.py +31 -0
  23. instaui/components/html/number.py +34 -0
  24. instaui/components/html/paragraph.py +19 -0
  25. instaui/components/html/range.py +45 -0
  26. instaui/components/html/select.py +93 -0
  27. instaui/components/html/span.py +19 -0
  28. instaui/components/html/ul.py +20 -0
  29. instaui/components/match.py +106 -0
  30. instaui/components/row.py +19 -0
  31. instaui/components/slot.py +82 -0
  32. instaui/components/transition_group.py +9 -0
  33. instaui/components/value_element.py +48 -0
  34. instaui/components/vfor.py +140 -0
  35. instaui/components/vif.py +38 -0
  36. instaui/consts.py +18 -0
  37. instaui/dependencies/__init__.py +15 -0
  38. instaui/dependencies/component_registrar.py +82 -0
  39. instaui/dependencies/installer.py +5 -0
  40. instaui/event/event_mixin.py +12 -0
  41. instaui/event/js_event.py +57 -0
  42. instaui/event/web_event.py +108 -0
  43. instaui/experimental/__init__.py +4 -0
  44. instaui/experimental/debug.py +48 -0
  45. instaui/fastapi_server/_utils.py +42 -0
  46. instaui/fastapi_server/_uvicorn.py +37 -0
  47. instaui/fastapi_server/config_router.py +60 -0
  48. instaui/fastapi_server/debug_mode_router.py +61 -0
  49. instaui/fastapi_server/event_router.py +58 -0
  50. instaui/fastapi_server/middlewares.py +19 -0
  51. instaui/fastapi_server/request_context.py +19 -0
  52. instaui/fastapi_server/server.py +246 -0
  53. instaui/fastapi_server/watch_router.py +53 -0
  54. instaui/handlers/_utils.py +66 -0
  55. instaui/handlers/computed_handler.py +42 -0
  56. instaui/handlers/config_handler.py +13 -0
  57. instaui/handlers/event_handler.py +58 -0
  58. instaui/handlers/watch_handler.py +57 -0
  59. instaui/html_tools.py +139 -0
  60. instaui/inject.py +33 -0
  61. instaui/js/__init__.py +4 -0
  62. instaui/js/js_output.py +15 -0
  63. instaui/js/lambda_func.py +35 -0
  64. instaui/launch_collector.py +52 -0
  65. instaui/page_info.py +23 -0
  66. instaui/runtime/__init__.py +29 -0
  67. instaui/runtime/_app.py +206 -0
  68. instaui/runtime/_inner_helper.py +9 -0
  69. instaui/runtime/context.py +47 -0
  70. instaui/runtime/dataclass.py +30 -0
  71. instaui/runtime/resource.py +87 -0
  72. instaui/runtime/scope.py +107 -0
  73. instaui/runtime/ui_state_scope.py +15 -0
  74. instaui/settings/__init__.py +4 -0
  75. instaui/settings/__settings.py +13 -0
  76. instaui/skip.py +12 -0
  77. instaui/spa_router/__init__.py +26 -0
  78. instaui/spa_router/_components.py +35 -0
  79. instaui/spa_router/_file_base_utils.py +264 -0
  80. instaui/spa_router/_functions.py +122 -0
  81. instaui/spa_router/_install.py +11 -0
  82. instaui/spa_router/_route_model.py +139 -0
  83. instaui/spa_router/_router_box.py +40 -0
  84. instaui/spa_router/_router_output.py +22 -0
  85. instaui/spa_router/_router_param_var.py +51 -0
  86. instaui/spa_router/_types.py +4 -0
  87. instaui/spa_router/templates/page_routes +59 -0
  88. instaui/static/insta-ui.css +1 -0
  89. instaui/static/insta-ui.esm-browser.prod.js +3663 -0
  90. instaui/static/insta-ui.iife.js +29 -0
  91. instaui/static/insta-ui.iife.js.map +1 -0
  92. instaui/static/insta-ui.js.map +1 -0
  93. instaui/static/tailwindcss.min.js +62 -0
  94. instaui/static/templates/debug/sse.html +117 -0
  95. instaui/static/templates/web.html +118 -0
  96. instaui/static/templates/zero.html +55 -0
  97. instaui/static/vue.esm-browser.prod.js +9 -0
  98. instaui/static/vue.global.prod.js +9 -0
  99. instaui/static/vue.runtime.esm-browser.prod.js +5 -0
  100. instaui/systems/file_system.py +17 -0
  101. instaui/systems/func_system.py +104 -0
  102. instaui/systems/js_system.py +22 -0
  103. instaui/systems/pydantic_system.py +27 -0
  104. instaui/systems/string_system.py +10 -0
  105. instaui/template/__init__.py +4 -0
  106. instaui/template/env.py +7 -0
  107. instaui/template/web_template.py +55 -0
  108. instaui/template/zero_template.py +24 -0
  109. instaui/ui/__init__.py +121 -0
  110. instaui/ui/events.py +25 -0
  111. instaui/ui_functions/input_slient_data.py +16 -0
  112. instaui/ui_functions/server.py +13 -0
  113. instaui/ui_functions/str_format.py +36 -0
  114. instaui/ui_functions/ui_page.py +31 -0
  115. instaui/ui_functions/ui_types.py +13 -0
  116. instaui/ui_functions/url_location.py +33 -0
  117. instaui/vars/__init__.py +13 -0
  118. instaui/vars/_types.py +8 -0
  119. instaui/vars/_utils.py +12 -0
  120. instaui/vars/data.py +68 -0
  121. instaui/vars/element_ref.py +42 -0
  122. instaui/vars/event_context.py +45 -0
  123. instaui/vars/event_extend.py +0 -0
  124. instaui/vars/js_computed.py +95 -0
  125. instaui/vars/mixin_types/common_type.py +5 -0
  126. instaui/vars/mixin_types/element_binding.py +10 -0
  127. instaui/vars/mixin_types/observable.py +7 -0
  128. instaui/vars/mixin_types/pathable.py +14 -0
  129. instaui/vars/mixin_types/py_binding.py +13 -0
  130. instaui/vars/mixin_types/str_format_binding.py +8 -0
  131. instaui/vars/mixin_types/var_type.py +5 -0
  132. instaui/vars/path_var.py +89 -0
  133. instaui/vars/ref.py +103 -0
  134. instaui/vars/slot_prop.py +46 -0
  135. instaui/vars/state.py +82 -0
  136. instaui/vars/types.py +24 -0
  137. instaui/vars/vfor_item.py +204 -0
  138. instaui/vars/vue_computed.py +82 -0
  139. instaui/vars/web_computed.py +157 -0
  140. instaui/vars/web_view_computed.py +1 -0
  141. instaui/version.py +3 -0
  142. instaui/watch/_types.py +4 -0
  143. instaui/watch/_utils.py +3 -0
  144. instaui/watch/js_watch.py +74 -0
  145. instaui/watch/vue_watch.py +61 -0
  146. instaui/watch/web_watch.py +123 -0
  147. instaui/zero/__init__.py +3 -0
  148. instaui/zero/scope.py +9 -0
  149. instaui-0.1.0.dist-info/LICENSE +21 -0
  150. instaui-0.1.0.dist-info/METADATA +154 -0
  151. instaui-0.1.0.dist-info/RECORD +152 -0
  152. 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
@@ -0,0 +1,4 @@
1
+ from .lambda_func import LambdaFunc as func
2
+
3
+
4
+ __all__ = ["func"]
@@ -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
+ ]
@@ -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