instaui 0.1.0__tar.gz → 0.1.2__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.
Files changed (168) hide show
  1. {instaui-0.1.0 → instaui-0.1.2}/PKG-INFO +1 -1
  2. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/component.py +1 -1
  3. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/element.py +37 -17
  4. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/checkbox.py +0 -7
  5. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/slot.py +2 -3
  6. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/vfor.py +1 -1
  7. instaui-0.1.2/instaui/dependencies/component_dependency.py +16 -0
  8. instaui-0.1.2/instaui/dependencies/plugin_dependency.py +28 -0
  9. {instaui-0.1.0 → instaui-0.1.2}/instaui/fastapi_server/debug_mode_router.py +0 -1
  10. instaui-0.1.2/instaui/fastapi_server/dependency_router.py +21 -0
  11. instaui-0.1.2/instaui/fastapi_server/resource.py +35 -0
  12. instaui-0.1.2/instaui/fastapi_server/server.py +270 -0
  13. instaui-0.1.2/instaui/html_tools.py +45 -0
  14. instaui-0.1.2/instaui/page_info.py +13 -0
  15. {instaui-0.1.0 → instaui-0.1.2}/instaui/runtime/_app.py +9 -20
  16. instaui-0.1.2/instaui/runtime/resource.py +52 -0
  17. {instaui-0.1.0 → instaui-0.1.2}/instaui/spa_router/_file_base_utils.py +47 -42
  18. {instaui-0.1.0 → instaui-0.1.2}/instaui/spa_router/_route_model.py +3 -25
  19. {instaui-0.1.0 → instaui-0.1.2}/instaui/spa_router/templates/page_routes +1 -0
  20. {instaui-0.1.0 → instaui-0.1.2}/instaui/static/insta-ui.esm-browser.prod.js +3663 -3663
  21. {instaui-0.1.0 → instaui-0.1.2}/instaui/static/insta-ui.iife.js +29 -29
  22. instaui-0.1.2/instaui/static/templates/web.html +74 -0
  23. instaui-0.1.2/instaui/static/templates/zero.html +71 -0
  24. instaui-0.1.2/instaui/systems/file_system.py +8 -0
  25. instaui-0.1.2/instaui/template/web_template.py +49 -0
  26. instaui-0.1.2/instaui/template/zero_template.py +109 -0
  27. {instaui-0.1.0 → instaui-0.1.2}/instaui/ui/__init__.py +2 -5
  28. instaui-0.1.2/instaui/ui_functions/ui_page.py +16 -0
  29. {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/path_var.py +2 -1
  30. instaui-0.1.2/instaui/zero/func.py +111 -0
  31. instaui-0.1.2/instaui/zero/scope.py +20 -0
  32. {instaui-0.1.0 → instaui-0.1.2}/pyproject.toml +1 -1
  33. instaui-0.1.0/instaui/dependencies/__init__.py +0 -15
  34. instaui-0.1.0/instaui/dependencies/component_registrar.py +0 -82
  35. instaui-0.1.0/instaui/dependencies/installer.py +0 -5
  36. instaui-0.1.0/instaui/fastapi_server/config_router.py +0 -60
  37. instaui-0.1.0/instaui/fastapi_server/server.py +0 -246
  38. instaui-0.1.0/instaui/html_tools.py +0 -139
  39. instaui-0.1.0/instaui/page_info.py +0 -23
  40. instaui-0.1.0/instaui/runtime/resource.py +0 -87
  41. instaui-0.1.0/instaui/static/templates/web.html +0 -118
  42. instaui-0.1.0/instaui/static/templates/zero.html +0 -55
  43. instaui-0.1.0/instaui/systems/file_system.py +0 -17
  44. instaui-0.1.0/instaui/template/web_template.py +0 -55
  45. instaui-0.1.0/instaui/template/zero_template.py +0 -24
  46. instaui-0.1.0/instaui/ui_functions/ui_page.py +0 -31
  47. instaui-0.1.0/instaui/zero/scope.py +0 -9
  48. {instaui-0.1.0 → instaui-0.1.2}/LICENSE +0 -0
  49. {instaui-0.1.0 → instaui-0.1.2}/README.md +0 -0
  50. {instaui-0.1.0 → instaui-0.1.2}/instaui/__init__.py +0 -0
  51. {instaui-0.1.0 → instaui-0.1.2}/instaui/_helper/observable_helper.py +0 -0
  52. {instaui-0.1.0 → instaui-0.1.2}/instaui/boot_info.py +0 -0
  53. {instaui-0.1.0 → instaui-0.1.2}/instaui/common/jsonable.py +0 -0
  54. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/__init__.py +0 -0
  55. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/column.py +0 -0
  56. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/content.py +0 -0
  57. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/directive.py +0 -0
  58. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/grid.py +0 -0
  59. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/__init__.py +0 -0
  60. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/_mixins.py +0 -0
  61. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/button.py +0 -0
  62. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/date.py +0 -0
  63. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/div.py +0 -0
  64. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/form.py +0 -0
  65. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/input.py +0 -0
  66. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/label.py +0 -0
  67. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/li.py +0 -0
  68. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/link.py +0 -0
  69. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/number.py +0 -0
  70. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/paragraph.py +0 -0
  71. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/range.py +0 -0
  72. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/select.py +0 -0
  73. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/span.py +0 -0
  74. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/html/ul.py +0 -0
  75. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/match.py +0 -0
  76. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/row.py +0 -0
  77. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/transition_group.py +0 -0
  78. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/value_element.py +0 -0
  79. {instaui-0.1.0 → instaui-0.1.2}/instaui/components/vif.py +0 -0
  80. {instaui-0.1.0 → instaui-0.1.2}/instaui/consts.py +0 -0
  81. {instaui-0.1.0 → instaui-0.1.2}/instaui/event/event_mixin.py +0 -0
  82. {instaui-0.1.0 → instaui-0.1.2}/instaui/event/js_event.py +0 -0
  83. {instaui-0.1.0 → instaui-0.1.2}/instaui/event/web_event.py +0 -0
  84. {instaui-0.1.0 → instaui-0.1.2}/instaui/experimental/__init__.py +0 -0
  85. {instaui-0.1.0 → instaui-0.1.2}/instaui/experimental/debug.py +0 -0
  86. {instaui-0.1.0 → instaui-0.1.2}/instaui/fastapi_server/_utils.py +0 -0
  87. {instaui-0.1.0 → instaui-0.1.2}/instaui/fastapi_server/_uvicorn.py +0 -0
  88. {instaui-0.1.0 → instaui-0.1.2}/instaui/fastapi_server/event_router.py +0 -0
  89. {instaui-0.1.0 → instaui-0.1.2}/instaui/fastapi_server/middlewares.py +0 -0
  90. {instaui-0.1.0 → instaui-0.1.2}/instaui/fastapi_server/request_context.py +0 -0
  91. {instaui-0.1.0 → instaui-0.1.2}/instaui/fastapi_server/watch_router.py +0 -0
  92. {instaui-0.1.0 → instaui-0.1.2}/instaui/handlers/_utils.py +0 -0
  93. {instaui-0.1.0 → instaui-0.1.2}/instaui/handlers/computed_handler.py +0 -0
  94. {instaui-0.1.0 → instaui-0.1.2}/instaui/handlers/config_handler.py +0 -0
  95. {instaui-0.1.0 → instaui-0.1.2}/instaui/handlers/event_handler.py +0 -0
  96. {instaui-0.1.0 → instaui-0.1.2}/instaui/handlers/watch_handler.py +0 -0
  97. {instaui-0.1.0 → instaui-0.1.2}/instaui/inject.py +0 -0
  98. {instaui-0.1.0 → instaui-0.1.2}/instaui/js/__init__.py +0 -0
  99. {instaui-0.1.0 → instaui-0.1.2}/instaui/js/js_output.py +0 -0
  100. {instaui-0.1.0 → instaui-0.1.2}/instaui/js/lambda_func.py +0 -0
  101. {instaui-0.1.0 → instaui-0.1.2}/instaui/launch_collector.py +0 -0
  102. {instaui-0.1.0 → instaui-0.1.2}/instaui/runtime/__init__.py +0 -0
  103. {instaui-0.1.0 → instaui-0.1.2}/instaui/runtime/_inner_helper.py +0 -0
  104. {instaui-0.1.0 → instaui-0.1.2}/instaui/runtime/context.py +0 -0
  105. {instaui-0.1.0 → instaui-0.1.2}/instaui/runtime/dataclass.py +0 -0
  106. {instaui-0.1.0 → instaui-0.1.2}/instaui/runtime/scope.py +0 -0
  107. {instaui-0.1.0 → instaui-0.1.2}/instaui/runtime/ui_state_scope.py +0 -0
  108. {instaui-0.1.0 → instaui-0.1.2}/instaui/settings/__init__.py +0 -0
  109. {instaui-0.1.0 → instaui-0.1.2}/instaui/settings/__settings.py +0 -0
  110. {instaui-0.1.0 → instaui-0.1.2}/instaui/skip.py +0 -0
  111. {instaui-0.1.0 → instaui-0.1.2}/instaui/spa_router/__init__.py +0 -0
  112. {instaui-0.1.0 → instaui-0.1.2}/instaui/spa_router/_components.py +0 -0
  113. {instaui-0.1.0 → instaui-0.1.2}/instaui/spa_router/_functions.py +0 -0
  114. {instaui-0.1.0 → instaui-0.1.2}/instaui/spa_router/_install.py +0 -0
  115. {instaui-0.1.0 → instaui-0.1.2}/instaui/spa_router/_router_box.py +0 -0
  116. {instaui-0.1.0 → instaui-0.1.2}/instaui/spa_router/_router_output.py +0 -0
  117. {instaui-0.1.0 → instaui-0.1.2}/instaui/spa_router/_router_param_var.py +0 -0
  118. {instaui-0.1.0 → instaui-0.1.2}/instaui/spa_router/_types.py +0 -0
  119. {instaui-0.1.0 → instaui-0.1.2}/instaui/static/insta-ui.css +0 -0
  120. {instaui-0.1.0 → instaui-0.1.2}/instaui/static/insta-ui.iife.js.map +0 -0
  121. {instaui-0.1.0 → instaui-0.1.2}/instaui/static/insta-ui.js.map +0 -0
  122. {instaui-0.1.0 → instaui-0.1.2}/instaui/static/tailwindcss.min.js +0 -0
  123. {instaui-0.1.0 → instaui-0.1.2}/instaui/static/templates/debug/sse.html +0 -0
  124. {instaui-0.1.0 → instaui-0.1.2}/instaui/static/vue.esm-browser.prod.js +0 -0
  125. {instaui-0.1.0 → instaui-0.1.2}/instaui/static/vue.global.prod.js +0 -0
  126. {instaui-0.1.0 → instaui-0.1.2}/instaui/static/vue.runtime.esm-browser.prod.js +0 -0
  127. {instaui-0.1.0 → instaui-0.1.2}/instaui/systems/func_system.py +0 -0
  128. {instaui-0.1.0 → instaui-0.1.2}/instaui/systems/js_system.py +0 -0
  129. {instaui-0.1.0 → instaui-0.1.2}/instaui/systems/pydantic_system.py +0 -0
  130. {instaui-0.1.0 → instaui-0.1.2}/instaui/systems/string_system.py +0 -0
  131. {instaui-0.1.0 → instaui-0.1.2}/instaui/template/__init__.py +0 -0
  132. {instaui-0.1.0 → instaui-0.1.2}/instaui/template/env.py +0 -0
  133. {instaui-0.1.0 → instaui-0.1.2}/instaui/ui/events.py +0 -0
  134. {instaui-0.1.0 → instaui-0.1.2}/instaui/ui_functions/input_slient_data.py +0 -0
  135. {instaui-0.1.0 → instaui-0.1.2}/instaui/ui_functions/server.py +0 -0
  136. {instaui-0.1.0 → instaui-0.1.2}/instaui/ui_functions/str_format.py +0 -0
  137. {instaui-0.1.0 → instaui-0.1.2}/instaui/ui_functions/ui_types.py +0 -0
  138. {instaui-0.1.0 → instaui-0.1.2}/instaui/ui_functions/url_location.py +0 -0
  139. {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/__init__.py +0 -0
  140. {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/_types.py +0 -0
  141. {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/_utils.py +0 -0
  142. {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/data.py +0 -0
  143. {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/element_ref.py +0 -0
  144. {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/event_context.py +0 -0
  145. {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/event_extend.py +0 -0
  146. {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/js_computed.py +0 -0
  147. {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/mixin_types/common_type.py +0 -0
  148. {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/mixin_types/element_binding.py +0 -0
  149. {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/mixin_types/observable.py +0 -0
  150. {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/mixin_types/pathable.py +0 -0
  151. {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/mixin_types/py_binding.py +0 -0
  152. {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/mixin_types/str_format_binding.py +0 -0
  153. {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/mixin_types/var_type.py +0 -0
  154. {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/ref.py +0 -0
  155. {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/slot_prop.py +0 -0
  156. {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/state.py +0 -0
  157. {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/types.py +0 -0
  158. {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/vfor_item.py +0 -0
  159. {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/vue_computed.py +0 -0
  160. {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/web_computed.py +0 -0
  161. {instaui-0.1.0 → instaui-0.1.2}/instaui/vars/web_view_computed.py +0 -0
  162. {instaui-0.1.0 → instaui-0.1.2}/instaui/version.py +0 -0
  163. {instaui-0.1.0 → instaui-0.1.2}/instaui/watch/_types.py +0 -0
  164. {instaui-0.1.0 → instaui-0.1.2}/instaui/watch/_utils.py +0 -0
  165. {instaui-0.1.0 → instaui-0.1.2}/instaui/watch/js_watch.py +0 -0
  166. {instaui-0.1.0 → instaui-0.1.2}/instaui/watch/vue_watch.py +0 -0
  167. {instaui-0.1.0 → instaui-0.1.2}/instaui/watch/web_watch.py +0 -0
  168. {instaui-0.1.0 → instaui-0.1.2}/instaui/zero/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: instaui
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: insta-ui is a Python-based UI library for rapidly building user interfaces.
5
5
  License: MIT
6
6
  Author: CrystalWindSnake
@@ -22,7 +22,7 @@ class Component(Jsonable):
22
22
 
23
23
  self.tag = (
24
24
  "div"
25
- if tag is None
25
+ if tag is None or tag == ""
26
26
  else (
27
27
  tag._to_element_binding_config()
28
28
  if isinstance(tag, ElementBindingMixin)
@@ -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 ComponentRegistrationInfo, register_component
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
- component: ClassVar[Optional[ComponentRegistrationInfo]] = None
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.component:
79
- tag = self.component.name
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
- component: Union[str, Path, None] = None,
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 component:
109
- if isinstance(component, str):
110
- component = Path(component)
111
- if not component.is_absolute():
112
- component = Path(inspect.getfile(cls)).parent / component
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
- # TODO: Lazy load component registration
115
- cls.component = register_component(
116
- component.stem, esm=component, shared=True
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.component:
369
- get_app_slot().register_component(self.component)
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
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Dict, List, Optional, Set, Union
3
+ from typing import TYPE_CHECKING, Dict, List, Optional, Set
4
4
  from instaui.common.jsonable import Jsonable
5
5
  from instaui.runtime import get_slot_stacks, pop_slot
6
6
  from instaui.runtime._app import get_app_slot
@@ -8,7 +8,6 @@ from instaui.vars.slot_prop import BindingSlotPropItem
8
8
 
9
9
  if TYPE_CHECKING:
10
10
  from instaui.components.component import Component
11
- from instaui.components.vfor import VFor
12
11
 
13
12
  _DEFAULT_SLOT_NAME = ":"
14
13
 
@@ -50,7 +49,7 @@ class Slot(Jsonable):
50
49
 
51
50
  self._id: Optional[str] = None
52
51
  self._name = name
53
- self._children: List[Union[Component, VFor]] = []
52
+ self._children: List[Component] = []
54
53
  self._props_use_name: Set[str] = set()
55
54
 
56
55
  def _has_props_use(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__(*_)
@@ -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
@@ -24,7 +24,6 @@ async def event_generator(
24
24
  task_event = asyncio.Event()
25
25
  _task_events[connection_id] = task_event
26
26
 
27
- # yield "event: task\ndata: 0 \n\n\n"
28
27
  try:
29
28
  while not task_event.is_set():
30
29
  if await request.is_disconnected():
@@ -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,35 @@
1
+ from pathlib import Path
2
+ from typing import Dict
3
+ from urllib.parse import quote as urllib_quote
4
+ from instaui.systems import file_system
5
+ from instaui.version import __version__ as _INSTA_VERSION
6
+
7
+ URL = f"/_instaui_{_INSTA_VERSION}/resource"
8
+ _THashPart = str
9
+ _HASH_PART_MAP: Dict[_THashPart, Path] = {}
10
+ _FILE_URL_MAP: Dict[Path, _THashPart] = {}
11
+
12
+
13
+ def get_by_hash(hash_part: str) -> Path:
14
+ return _HASH_PART_MAP[hash_part]
15
+
16
+
17
+ def record_resource(path: Path):
18
+ if path in _FILE_URL_MAP:
19
+ return _FILE_URL_MAP[path]
20
+
21
+ hash_part = _generate_hash_part(path)
22
+ _HASH_PART_MAP[hash_part] = path
23
+ url = f"{URL}/{hash_part}"
24
+ _FILE_URL_MAP[path] = url
25
+ return url
26
+
27
+
28
+ def generate_static_url(file_path: Path):
29
+ return urllib_quote(f"{URL}/{_generate_hash_part(file_path)}")
30
+
31
+
32
+ def _generate_hash_part(file_path: Path):
33
+ file_path = Path(file_path).resolve()
34
+ hash_name = file_system.generate_hash_name_from_path(file_path)
35
+ return f"{hash_name}/{file_path.name}"
@@ -0,0 +1,270 @@
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.responses import HTMLResponse
13
+ import uvicorn
14
+ from uvicorn.supervisors import ChangeReload
15
+ import itertools
16
+
17
+ from instaui.html_tools import to_config_data
18
+ from instaui.launch_collector import get_launch_collector
19
+ from instaui.page_info import PageInfo
20
+
21
+ from instaui import consts
22
+ from instaui.runtime._app import get_app_slot, get_default_app_slot
23
+ from instaui.runtime.dataclass import JsLink, VueAppComponent
24
+ from instaui.template import web_template
25
+
26
+
27
+ from . import dependency_router
28
+ from . import event_router
29
+ from . import watch_router
30
+ from . import debug_mode_router
31
+ from .middlewares import RequestContextMiddleware
32
+ from ._uvicorn import UvicornServer
33
+ from . import resource
34
+
35
+ APP_IMPORT_STRING = "instaui.fastapi_server.server:Server._instance.app"
36
+
37
+ VUE_JS_HASH_LINK = resource.record_resource(consts.VUE_ES_JS_PATH)
38
+ INSTAUI_JS_HASH_LINK = resource.record_resource(consts.APP_ES_JS_PATH)
39
+ APP_CSS_LINK = resource.record_resource(consts.APP_CSS_PATH)
40
+ TAILWIND_JS_HASH_LINK = resource.record_resource(consts.TAILWIND_JS_PATH)
41
+
42
+
43
+ class Server:
44
+ _instance: Optional[Server] = None
45
+
46
+ @classmethod
47
+ def get_instance(cls):
48
+ if cls._instance is None:
49
+ cls._instance = cls()
50
+ return cls._instance
51
+
52
+ def __init__(self):
53
+ self.app = FastAPI()
54
+ self.app.add_middleware(RequestContextMiddleware)
55
+ dependency_router.create_router(self.app)
56
+ event_router.create_router(self.app)
57
+ watch_router.create_router(self.app)
58
+ debug_mode_router.create_router(self.app)
59
+
60
+ for page_info in get_launch_collector()._page_router.values():
61
+ self.register_page(page_info)
62
+
63
+ self._registered_static_routes: Set[str] = set()
64
+
65
+ def register_page(self, info: PageInfo):
66
+ is_async = inspect.iscoroutinefunction(info.func)
67
+
68
+ self._remove_route(info.path)
69
+
70
+ if is_async:
71
+
72
+ @self.app.get(info.path)
73
+ async def _(request: Request):
74
+ self._update_page_info(request, info)
75
+ with _execute_request_lifespans():
76
+ await info.func()
77
+ html = self._to_web_html(
78
+ page_info=info,
79
+ request=request,
80
+ )
81
+
82
+ return HTMLResponse(html)
83
+
84
+ else:
85
+
86
+ @self.app.get(info.path)
87
+ def _(request: Request):
88
+ self._update_page_info(request, info)
89
+ with _execute_request_lifespans():
90
+ info.func()
91
+ html = self._to_web_html(
92
+ page_info=info,
93
+ request=request,
94
+ )
95
+
96
+ return HTMLResponse(html)
97
+
98
+ def _to_web_html(
99
+ self,
100
+ *,
101
+ page_info: PageInfo,
102
+ request: Request,
103
+ ):
104
+ config_data = to_config_data()
105
+
106
+ model = web_template.WebTemplateModel(
107
+ vue_js_link=VUE_JS_HASH_LINK,
108
+ instaui_js_link=INSTAUI_JS_HASH_LINK,
109
+ css_links=[
110
+ APP_CSS_LINK,
111
+ ],
112
+ config_dict=config_data,
113
+ )
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
+ if html_resource.use_tailwind is None:
121
+ if default_html_resource.use_tailwind:
122
+ model.js_links.append(JsLink(TAILWIND_JS_HASH_LINK))
123
+ else:
124
+ if html_resource.use_tailwind:
125
+ model.js_links.append(JsLink(TAILWIND_JS_HASH_LINK))
126
+
127
+ # register custom components
128
+ for component in system_slot._component_dependencies:
129
+ if not component.esm:
130
+ continue
131
+
132
+ model.vue_app_component.append(
133
+ VueAppComponent(
134
+ name=component.tag_name,
135
+ url=resource.record_resource(component.esm),
136
+ )
137
+ )
138
+
139
+ if component.css:
140
+ for css_link in component.css:
141
+ model.css_links.append(resource.record_resource(css_link))
142
+
143
+ # register custom plugins
144
+ for plugin in set(
145
+ itertools.chain(
146
+ system_slot._plugin_dependencies, default_app_slot._plugin_dependencies
147
+ )
148
+ ):
149
+ if not plugin.esm:
150
+ continue
151
+
152
+ model.vue_app_use.append(plugin.name)
153
+
154
+ model.add_extra_import_map(
155
+ plugin.name, resource.record_resource(plugin.esm)
156
+ )
157
+
158
+ if plugin.css:
159
+ for css_link in plugin.css:
160
+ model.css_links.append(resource.record_resource(css_link))
161
+
162
+ # css file link to web static link
163
+ for link, attrs in itertools.chain(
164
+ html_resource._css_links.items(), default_html_resource._css_links.items()
165
+ ):
166
+ if isinstance(link, Path):
167
+ model.css_links.append(resource.record_resource(link))
168
+
169
+ # js file link to web static link
170
+ for info in itertools.chain(
171
+ html_resource._js_links, default_html_resource._js_links
172
+ ):
173
+ if isinstance(info.link, Path):
174
+ model.js_links.append(JsLink((resource.record_resource(info.link))))
175
+
176
+ for js_code in itertools.chain(
177
+ html_resource._script_tags, default_html_resource._script_tags
178
+ ):
179
+ model.script_tags.append(js_code)
180
+
181
+ for sylte_code in itertools.chain(
182
+ html_resource._style_tags, default_html_resource._style_tags
183
+ ):
184
+ model.style_tags.append(sylte_code)
185
+
186
+ model.prefix = request.headers.get(
187
+ "X-Forwarded-Prefix", request.scope.get("root_path", "")
188
+ )
189
+
190
+ return web_template.render_web_html(model)
191
+
192
+ def _update_page_info(self, request: Request, page_info: PageInfo):
193
+ app = get_app_slot()
194
+
195
+ app._page_path = page_info.path
196
+ app._page_params = request.path_params
197
+ app._query_params = dict(request.query_params)
198
+
199
+ def _remove_route(self, path: str) -> None:
200
+ self.app.routes[:] = [
201
+ r for r in self.app.routes if getattr(r, "path", None) != path
202
+ ]
203
+
204
+ def try_close_server(self):
205
+ UvicornServer.instance.should_exit = True
206
+
207
+ def run(
208
+ self,
209
+ host="0.0.0.0",
210
+ port=8080,
211
+ reload: bool = True,
212
+ reload_dirs: str = ".",
213
+ reload_includes: str = "*.py",
214
+ reload_excludes: str = ".*, .py[cod], .sw.*, ~*",
215
+ log_level="info",
216
+ workers: int | None = None,
217
+ uds: str | None = None,
218
+ **kwargs: Any,
219
+ ):
220
+ if multiprocessing.current_process().name != "MainProcess":
221
+ return
222
+
223
+ if reload and not hasattr(__main__, "__file__"):
224
+ reload = False
225
+
226
+ config = uvicorn.Config(
227
+ APP_IMPORT_STRING if reload else self.app,
228
+ host=host,
229
+ port=port,
230
+ reload=reload,
231
+ log_level=log_level,
232
+ workers=workers,
233
+ uds=uds,
234
+ reload_includes=_split_args(reload_includes) if reload else None,
235
+ reload_excludes=_split_args(reload_excludes) if reload else None,
236
+ reload_dirs=_split_args(reload_dirs) if reload else None,
237
+ **kwargs,
238
+ )
239
+
240
+ UvicornServer.create_singleton(config, [debug_mode_router.when_server_reload])
241
+
242
+ if config.should_reload:
243
+ ChangeReload(config, target=UvicornServer.instance.run, sockets=[]).run()
244
+ else:
245
+ UvicornServer.instance.run()
246
+
247
+ if config.uds:
248
+ os.remove(config.uds) # pragma: py-win32
249
+
250
+ def run_with(self, app):
251
+ assert isinstance(app, FastAPI), "app must be a FastAPI instance"
252
+
253
+
254
+ def _split_args(args: str):
255
+ return [a.strip() for a in args.split(",")]
256
+
257
+
258
+ @contextmanager
259
+ def _execute_request_lifespans():
260
+ events = [iter(event()) for event in get_launch_collector().page_request_lifespans]
261
+ for event in events:
262
+ next(event)
263
+
264
+ yield
265
+
266
+ for event in events:
267
+ try:
268
+ next(event)
269
+ except StopIteration:
270
+ pass
@@ -0,0 +1,45 @@
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 add_js_code(code: str, *, script_attrs: Optional[Dict[str, Any]] = None):
33
+ get_app_slot()._html_resource.add_script_tag(code, script_attrs=script_attrs)
34
+
35
+
36
+ def add_vue_app_use(name: str):
37
+ get_app_slot()._html_resource.add_vue_app_use(name)
38
+
39
+
40
+ def to_config_data() -> Dict:
41
+ return dumps2dict(get_app_slot())
42
+
43
+
44
+ def to_json(indent=False):
45
+ 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)
@@ -5,7 +5,6 @@ from instaui.common.jsonable import Jsonable
5
5
  from .resource import HtmlResource
6
6
  from instaui.consts import _T_App_Mode
7
7
  from contextvars import ContextVar, Token
8
- from copy import copy
9
8
  from contextlib import contextmanager
10
9
  from instaui.runtime.scope import Scope, GlobalScope
11
10
  from types import MappingProxyType
@@ -13,14 +12,14 @@ from types import MappingProxyType
13
12
  if TYPE_CHECKING:
14
13
  from instaui.components.component import Component
15
14
  from instaui.components.slot import Slot
16
- from instaui.dependencies import ComponentRegistrationInfo, PluginRegistrationInfo
15
+
16
+ from instaui.dependencies.component_dependency import ComponentDependencyInfo
17
+ from instaui.dependencies.plugin_dependency import PluginDependencyInfo
17
18
  from instaui.spa_router._route_model import RouteCollector
18
19
 
19
20
 
20
21
  class App(Jsonable):
21
22
  _default_app_slot: ClassVar[Optional[App]] = None
22
- _default_js_components: ClassVar[Set[ComponentRegistrationInfo]] = set()
23
- _default_plugins: ClassVar[Set[PluginRegistrationInfo]] = set()
24
23
 
25
24
  def __init__(self, *, mode: _T_App_Mode) -> None:
26
25
  super().__init__()
@@ -35,11 +34,9 @@ class App(Jsonable):
35
34
  self._scope_stack: List[Scope] = [defalut_scope]
36
35
  self._scopes: List[Scope] = [defalut_scope]
37
36
  self._html_resource = HtmlResource()
38
- self._js_components: Set[ComponentRegistrationInfo] = copy(
39
- self._default_js_components
40
- )
37
+ self._component_dependencies: Set[ComponentDependencyInfo] = set()
38
+ self._plugin_dependencies: Set[PluginDependencyInfo] = set()
41
39
 
42
- self._plugins: Set[PluginRegistrationInfo] = copy(self._default_plugins)
43
40
  self._page_path: Optional[str] = None
44
41
  self._page_params: Dict[str, Any] = {}
45
42
  self._query_params: Dict[str, Any] = {}
@@ -74,11 +71,11 @@ class App(Jsonable):
74
71
  def reset_html_resource(self):
75
72
  self._html_resource = HtmlResource()
76
73
 
77
- def register_component(self, component: ComponentRegistrationInfo) -> None:
78
- self._js_components.add(component)
74
+ def use_component_dependency(self, dependency: ComponentDependencyInfo) -> None:
75
+ self._component_dependencies.add(dependency)
79
76
 
80
- def register_plugin(self, plugin: PluginRegistrationInfo) -> None:
81
- self._plugins.add(plugin)
77
+ def use_plugin_dependency(self, dependency: PluginDependencyInfo) -> None:
78
+ self._plugin_dependencies.add(dependency)
82
79
 
83
80
  def register_router(self, collector: RouteCollector) -> None:
84
81
  self._route_collector = collector
@@ -115,14 +112,6 @@ class App(Jsonable):
115
112
  cls._default_app_slot = DefaultApp(mode="web")
116
113
  return cls._default_app_slot
117
114
 
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
115
 
127
116
  class DefaultApp(App):
128
117
  _instance = None