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