instaui 0.1.15__py2.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 (283) hide show
  1. instaui/__init__.py +9 -0
  2. instaui/_helper/observable_helper.py +45 -0
  3. instaui/arco/__init__.py +191 -0
  4. instaui/arco/_settings.py +25 -0
  5. instaui/arco/_use_tools/locale.py +50 -0
  6. instaui/arco/component_types.py +1019 -0
  7. instaui/arco/components/_utils.py +22 -0
  8. instaui/arco/components/affix.py +29 -0
  9. instaui/arco/components/alert.py +42 -0
  10. instaui/arco/components/anchor.py +42 -0
  11. instaui/arco/components/auto_complete.py +96 -0
  12. instaui/arco/components/avatar.py +55 -0
  13. instaui/arco/components/back_top.py +14 -0
  14. instaui/arco/components/badge.py +14 -0
  15. instaui/arco/components/breadcrumb.py +14 -0
  16. instaui/arco/components/button.py +43 -0
  17. instaui/arco/components/calendar.py +47 -0
  18. instaui/arco/components/card.py +14 -0
  19. instaui/arco/components/carousel.py +33 -0
  20. instaui/arco/components/cascader.py +111 -0
  21. instaui/arco/components/checkbox.py +32 -0
  22. instaui/arco/components/collapse.py +31 -0
  23. instaui/arco/components/color_picker.py +45 -0
  24. instaui/arco/components/comment.py +14 -0
  25. instaui/arco/components/config_provider.py +13 -0
  26. instaui/arco/components/date_picker.py +111 -0
  27. instaui/arco/components/descriptions.py +14 -0
  28. instaui/arco/components/divider.py +13 -0
  29. instaui/arco/components/drawer.py +98 -0
  30. instaui/arco/components/dropdown.py +45 -0
  31. instaui/arco/components/empty.py +14 -0
  32. instaui/arco/components/form.py +55 -0
  33. instaui/arco/components/icon.py +17 -0
  34. instaui/arco/components/image.py +33 -0
  35. instaui/arco/components/input.py +102 -0
  36. instaui/arco/components/input_number.py +97 -0
  37. instaui/arco/components/input_password.py +38 -0
  38. instaui/arco/components/input_search.py +37 -0
  39. instaui/arco/components/input_tag.py +110 -0
  40. instaui/arco/components/layout.py +13 -0
  41. instaui/arco/components/layout_content.py +6 -0
  42. instaui/arco/components/layout_footer.py +6 -0
  43. instaui/arco/components/layout_header.py +6 -0
  44. instaui/arco/components/layout_sider.py +53 -0
  45. instaui/arco/components/link.py +36 -0
  46. instaui/arco/components/list.py +68 -0
  47. instaui/arco/components/mention.py +97 -0
  48. instaui/arco/components/menu.py +88 -0
  49. instaui/arco/components/modal.py +97 -0
  50. instaui/arco/components/overflow_list.py +29 -0
  51. instaui/arco/components/page_header.py +29 -0
  52. instaui/arco/components/pagination.py +45 -0
  53. instaui/arco/components/pop_confirm.py +58 -0
  54. instaui/arco/components/popover.py +32 -0
  55. instaui/arco/components/progress.py +14 -0
  56. instaui/arco/components/radio.py +40 -0
  57. instaui/arco/components/radio_group.py +42 -0
  58. instaui/arco/components/rate.py +45 -0
  59. instaui/arco/components/resize_box.py +62 -0
  60. instaui/arco/components/result.py +14 -0
  61. instaui/arco/components/select.py +182 -0
  62. instaui/arco/components/skeleton.py +14 -0
  63. instaui/arco/components/slider.py +38 -0
  64. instaui/arco/components/space.py +14 -0
  65. instaui/arco/components/spin.py +14 -0
  66. instaui/arco/components/split.py +76 -0
  67. instaui/arco/components/statistic.py +14 -0
  68. instaui/arco/components/steps.py +32 -0
  69. instaui/arco/components/switch.py +57 -0
  70. instaui/arco/components/tab_pane.py +12 -0
  71. instaui/arco/components/table.py +276 -0
  72. instaui/arco/components/tabs.py +101 -0
  73. instaui/arco/components/tag.py +42 -0
  74. instaui/arco/components/textarea.py +84 -0
  75. instaui/arco/components/time_picker.py +76 -0
  76. instaui/arco/components/timeline.py +14 -0
  77. instaui/arco/components/tooltip.py +29 -0
  78. instaui/arco/components/transfer.py +58 -0
  79. instaui/arco/components/tree.py +120 -0
  80. instaui/arco/components/tree_select.py +86 -0
  81. instaui/arco/components/trigger.py +58 -0
  82. instaui/arco/components/typography.py +142 -0
  83. instaui/arco/components/upload.py +71 -0
  84. instaui/arco/components/verification_code.py +58 -0
  85. instaui/arco/components/watermark.py +14 -0
  86. instaui/arco/locales/__init__.py +4 -0
  87. instaui/arco/locales/_index.py +31 -0
  88. instaui/arco/locales/en_us.py +227 -0
  89. instaui/arco/locales/zh_cn.py +224 -0
  90. instaui/arco/setup.py +36 -0
  91. instaui/arco/static/instaui-arco.css +1 -0
  92. instaui/arco/static/instaui-arco.js +55771 -0
  93. instaui/arco/types.py +24 -0
  94. instaui/boot_info.py +43 -0
  95. instaui/common/jsonable.py +37 -0
  96. instaui/components/__init__.py +0 -0
  97. instaui/components/column.py +26 -0
  98. instaui/components/component.py +47 -0
  99. instaui/components/content.py +34 -0
  100. instaui/components/directive.py +55 -0
  101. instaui/components/element.py +573 -0
  102. instaui/components/grid.py +213 -0
  103. instaui/components/html/__init__.py +49 -0
  104. instaui/components/html/_mixins.py +34 -0
  105. instaui/components/html/_preset.py +4 -0
  106. instaui/components/html/button.py +38 -0
  107. instaui/components/html/checkbox.py +35 -0
  108. instaui/components/html/date.py +28 -0
  109. instaui/components/html/div.py +7 -0
  110. instaui/components/html/form.py +7 -0
  111. instaui/components/html/heading.py +51 -0
  112. instaui/components/html/input.py +28 -0
  113. instaui/components/html/label.py +21 -0
  114. instaui/components/html/li.py +17 -0
  115. instaui/components/html/link.py +31 -0
  116. instaui/components/html/number.py +34 -0
  117. instaui/components/html/paragraph.py +29 -0
  118. instaui/components/html/range.py +48 -0
  119. instaui/components/html/select.py +69 -0
  120. instaui/components/html/span.py +19 -0
  121. instaui/components/html/table.py +36 -0
  122. instaui/components/html/textarea.py +28 -0
  123. instaui/components/html/ul.py +20 -0
  124. instaui/components/label.py +5 -0
  125. instaui/components/markdown/markdown.js +33 -0
  126. instaui/components/markdown/markdown.py +41 -0
  127. instaui/components/markdown/static/github-markdown.css +12 -0
  128. instaui/components/markdown/static/marked.esm.js +2579 -0
  129. instaui/components/match.py +108 -0
  130. instaui/components/row.py +17 -0
  131. instaui/components/shiki_code/shiki_code.js +126 -0
  132. instaui/components/shiki_code/shiki_code.py +99 -0
  133. instaui/components/shiki_code/static/langs/css.mjs +5 -0
  134. instaui/components/shiki_code/static/langs/markdown.mjs +5 -0
  135. instaui/components/shiki_code/static/langs/python.mjs +5 -0
  136. instaui/components/shiki_code/static/langs/shell.mjs +2 -0
  137. instaui/components/shiki_code/static/langs/shellscript.mjs +5 -0
  138. instaui/components/shiki_code/static/shiki-core.js +5784 -0
  139. instaui/components/shiki_code/static/shiki-style.css +179 -0
  140. instaui/components/shiki_code/static/shiki-transformers.js +461 -0
  141. instaui/components/shiki_code/static/themes/vitesse-dark.mjs +2 -0
  142. instaui/components/shiki_code/static/themes/vitesse-light.mjs +2 -0
  143. instaui/components/slot.py +81 -0
  144. instaui/components/transition_group.py +9 -0
  145. instaui/components/value_element.py +52 -0
  146. instaui/components/vfor.py +142 -0
  147. instaui/components/vif.py +42 -0
  148. instaui/consts.py +23 -0
  149. instaui/dependencies/component_dependency.py +22 -0
  150. instaui/dependencies/plugin_dependency.py +28 -0
  151. instaui/event/event_mixin.py +12 -0
  152. instaui/event/js_event.py +82 -0
  153. instaui/event/vue_event.py +66 -0
  154. instaui/event/web_event.py +123 -0
  155. instaui/experimental/__init__.py +3 -0
  156. instaui/experimental/debug.py +48 -0
  157. instaui/extra_libs/_echarts.py +3 -0
  158. instaui/extra_libs/_import_error.py +9 -0
  159. instaui/extra_libs/_mermaid.py +3 -0
  160. instaui/extra_libs/_shiki_code.py +3 -0
  161. instaui/fastapi_server/_utils.py +42 -0
  162. instaui/fastapi_server/_uvicorn.py +37 -0
  163. instaui/fastapi_server/debug_mode_router.py +60 -0
  164. instaui/fastapi_server/dependency_router.py +28 -0
  165. instaui/fastapi_server/event_router.py +58 -0
  166. instaui/fastapi_server/middlewares.py +19 -0
  167. instaui/fastapi_server/request_context.py +19 -0
  168. instaui/fastapi_server/resource.py +30 -0
  169. instaui/fastapi_server/server.py +308 -0
  170. instaui/fastapi_server/watch_router.py +53 -0
  171. instaui/handlers/_utils.py +88 -0
  172. instaui/handlers/event_handler.py +60 -0
  173. instaui/handlers/watch_handler.py +61 -0
  174. instaui/html_tools.py +94 -0
  175. instaui/inject.py +33 -0
  176. instaui/js/__init__.py +4 -0
  177. instaui/js/js_output.py +15 -0
  178. instaui/js/lambda_func.py +35 -0
  179. instaui/launch_collector.py +52 -0
  180. instaui/page_info.py +13 -0
  181. instaui/runtime/__init__.py +29 -0
  182. instaui/runtime/_app.py +234 -0
  183. instaui/runtime/_inner_helper.py +9 -0
  184. instaui/runtime/_link_manager.py +89 -0
  185. instaui/runtime/context.py +47 -0
  186. instaui/runtime/dataclass.py +30 -0
  187. instaui/runtime/resource.py +65 -0
  188. instaui/runtime/scope.py +133 -0
  189. instaui/runtime/ui_state_scope.py +15 -0
  190. instaui/settings/__init__.py +4 -0
  191. instaui/settings/__settings.py +13 -0
  192. instaui/shadcn_classless/_index.py +42 -0
  193. instaui/shadcn_classless/static/shadcn-classless.css +403 -0
  194. instaui/skip.py +12 -0
  195. instaui/spa_router/__init__.py +26 -0
  196. instaui/spa_router/_components.py +35 -0
  197. instaui/spa_router/_file_base_utils.py +273 -0
  198. instaui/spa_router/_functions.py +122 -0
  199. instaui/spa_router/_install.py +11 -0
  200. instaui/spa_router/_route_model.py +117 -0
  201. instaui/spa_router/_router_box.py +40 -0
  202. instaui/spa_router/_router_output.py +22 -0
  203. instaui/spa_router/_router_param_var.py +51 -0
  204. instaui/spa_router/_types.py +4 -0
  205. instaui/spa_router/templates/page_routes +60 -0
  206. instaui/static/insta-ui.css +1 -0
  207. instaui/static/insta-ui.esm-browser.prod.js +3717 -0
  208. instaui/static/insta-ui.ico +0 -0
  209. instaui/static/insta-ui.js.map +1 -0
  210. instaui/static/instaui-tools-browser.js +511 -0
  211. instaui/static/templates/debug/sse.html +117 -0
  212. instaui/static/templates/web.html +74 -0
  213. instaui/static/templates/webview.html +78 -0
  214. instaui/static/templates/zero.html +71 -0
  215. instaui/static/vue.esm-browser.prod.js +9 -0
  216. instaui/static/vue.global.prod.js +9 -0
  217. instaui/static/vue.runtime.esm-browser.prod.js +5 -0
  218. instaui/systems/file_system.py +6 -0
  219. instaui/systems/func_system.py +119 -0
  220. instaui/systems/js_system.py +22 -0
  221. instaui/systems/module_system.py +46 -0
  222. instaui/systems/pydantic_system.py +27 -0
  223. instaui/systems/string_system.py +10 -0
  224. instaui/tailwind/__init__.py +6 -0
  225. instaui/tailwind/_index.py +24 -0
  226. instaui/tailwind/static/tailwindcss-v3.min.js +62 -0
  227. instaui/tailwind/static/tailwindcss-v4.min.js +8 -0
  228. instaui/template/__init__.py +4 -0
  229. instaui/template/_utils.py +23 -0
  230. instaui/template/env.py +7 -0
  231. instaui/template/web_template.py +49 -0
  232. instaui/template/webview_template.py +48 -0
  233. instaui/template/zero_template.py +105 -0
  234. instaui/ui/__init__.py +144 -0
  235. instaui/ui/__init__.pyi +149 -0
  236. instaui/ui/events.py +25 -0
  237. instaui/ui_functions/input_slient_data.py +16 -0
  238. instaui/ui_functions/server.py +15 -0
  239. instaui/ui_functions/str_format.py +36 -0
  240. instaui/ui_functions/ui_page.py +16 -0
  241. instaui/ui_functions/ui_types.py +13 -0
  242. instaui/ui_functions/url_location.py +33 -0
  243. instaui/vars/_types.py +8 -0
  244. instaui/vars/data.py +68 -0
  245. instaui/vars/element_ref.py +40 -0
  246. instaui/vars/event_context.py +49 -0
  247. instaui/vars/event_extend.py +0 -0
  248. instaui/vars/js_computed.py +117 -0
  249. instaui/vars/mixin_types/common_type.py +5 -0
  250. instaui/vars/mixin_types/element_binding.py +16 -0
  251. instaui/vars/mixin_types/observable.py +7 -0
  252. instaui/vars/mixin_types/pathable.py +14 -0
  253. instaui/vars/mixin_types/py_binding.py +13 -0
  254. instaui/vars/mixin_types/str_format_binding.py +8 -0
  255. instaui/vars/mixin_types/var_type.py +5 -0
  256. instaui/vars/path_var.py +90 -0
  257. instaui/vars/ref.py +103 -0
  258. instaui/vars/slot_prop.py +46 -0
  259. instaui/vars/state.py +97 -0
  260. instaui/vars/types.py +24 -0
  261. instaui/vars/vfor_item.py +204 -0
  262. instaui/vars/vue_computed.py +81 -0
  263. instaui/vars/web_computed.py +209 -0
  264. instaui/vars/web_view_computed.py +1 -0
  265. instaui/version.py +3 -0
  266. instaui/watch/_types.py +4 -0
  267. instaui/watch/_utils.py +3 -0
  268. instaui/watch/js_watch.py +110 -0
  269. instaui/watch/vue_watch.py +77 -0
  270. instaui/watch/web_watch.py +181 -0
  271. instaui/webview/__init__.py +2 -0
  272. instaui/webview/_utils.py +8 -0
  273. instaui/webview/api.py +72 -0
  274. instaui/webview/func.py +114 -0
  275. instaui/webview/index.py +161 -0
  276. instaui/webview/resource.py +172 -0
  277. instaui/zero/__init__.py +3 -0
  278. instaui/zero/func.py +123 -0
  279. instaui/zero/scope.py +109 -0
  280. instaui-0.1.15.dist-info/METADATA +152 -0
  281. instaui-0.1.15.dist-info/RECORD +283 -0
  282. instaui-0.1.15.dist-info/WHEEL +5 -0
  283. instaui-0.1.15.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,110 @@
1
+ import typing
2
+ from . import _types
3
+ from . import _utils
4
+
5
+ from instaui.common.jsonable import Jsonable
6
+ from instaui.runtime._app import get_current_scope
7
+
8
+ from instaui.vars.mixin_types.py_binding import CanOutputMixin
9
+ from instaui.vars.mixin_types.common_type import TObservableInput
10
+ from instaui._helper import observable_helper
11
+
12
+
13
+ class JsWatch(Jsonable):
14
+ def __init__(
15
+ self,
16
+ code: str,
17
+ inputs: typing.Optional[typing.Sequence[TObservableInput]] = None,
18
+ outputs: typing.Optional[typing.Sequence[CanOutputMixin]] = None,
19
+ immediate: bool = True,
20
+ deep: typing.Union[bool, int] = False,
21
+ once: bool = False,
22
+ flush: typing.Optional[_types.TFlush] = None,
23
+ ) -> None:
24
+ inputs = observable_helper.auto_made_inputs_to_slient(inputs, outputs)
25
+
26
+ get_current_scope().register_js_watch(self)
27
+
28
+ self.code = code
29
+
30
+ self._inputs, self._is_slient_inputs, self._is_data = (
31
+ observable_helper.analyze_observable_inputs(list(inputs or []))
32
+ )
33
+ self._outputs = [output._to_output_config() for output in outputs or []]
34
+
35
+ if immediate is not True:
36
+ self.immediate = immediate
37
+
38
+ if deep is not False:
39
+ _utils.assert_deep(deep)
40
+ self.deep = deep
41
+
42
+ if once is not False:
43
+ self.once = once
44
+
45
+ if flush is not None:
46
+ self.flush = flush
47
+
48
+ def _to_json_dict(self):
49
+ data = super()._to_json_dict()
50
+
51
+ if self._inputs:
52
+ data["inputs"] = self._inputs
53
+
54
+ if sum(self._is_slient_inputs) > 0:
55
+ data["slient"] = self._is_slient_inputs
56
+
57
+ if sum(self._is_data) > 0:
58
+ data["data"] = self._is_data
59
+
60
+ if self._outputs:
61
+ data["outputs"] = self._outputs
62
+
63
+ return data
64
+
65
+
66
+ def js_watch(
67
+ *,
68
+ inputs: typing.Optional[typing.Sequence] = None,
69
+ outputs: typing.Optional[typing.Sequence] = None,
70
+ code: str,
71
+ immediate: bool = True,
72
+ deep: typing.Union[bool, int] = False,
73
+ once: bool = False,
74
+ flush: typing.Optional[_types.TFlush] = None,
75
+ ):
76
+ """
77
+ Creates a client-side observer that executes JavaScript code in response to reactive source changes.
78
+
79
+ Args:
80
+ inputs (typing.Optional[typing.Sequence], optional): Reactive sources to observe. Changes to these sources
81
+ trigger the watcher's JavaScript execution.
82
+ outputs (typing.Optional[typing.Sequence], optional): Output targets associated with this watcher. Used for
83
+ coordination with other observers.
84
+ code (str, optional): JavaScript code to execute when changes are detected. The code has access
85
+ to the current values of observed inputs through the `args` parameter.
86
+ immediate (bool, optional):If True, executes the watcher immediately after creation with current values. Defaults to True.
87
+ deep (typing.Union[bool, int], optional): Controls depth of change detection:
88
+ - True: Recursively tracks nested properties
89
+ - False: Shallow comparison only
90
+ - int: Maximum depth level to track (for complex objects).
91
+ Defaults to False.
92
+ once (bool, optional): If True, automatically stops observation after first trigger. Defaults to False.
93
+ flush (typing.Optional[_types.TFlush], optional): Controls when to flush updates:
94
+ - 'sync': Execute immediately on change
95
+ - 'post': Batch updates and execute after current tick
96
+ - 'pre': Execute before render phase (if applicable)
97
+
98
+ # Example:
99
+ .. code-block:: python
100
+ from instaui import ui, html
101
+
102
+ num = ui.state(0)
103
+ msg = ui.state('')
104
+ ui.js_watch(inputs=[num], outputs=[msg], code="num => `The number is ${num}`")
105
+
106
+ html.number(num)
107
+ ui.label(msg)
108
+ """
109
+
110
+ return JsWatch(code, inputs, outputs, immediate, deep, once, flush)
@@ -0,0 +1,77 @@
1
+ from __future__ import annotations
2
+ from typing import (
3
+ Any,
4
+ Dict,
5
+ Optional,
6
+ Sequence,
7
+ Union,
8
+ cast,
9
+ )
10
+
11
+ from instaui.vars.mixin_types.observable import ObservableMixin
12
+
13
+ from . import _types
14
+ from . import _utils
15
+
16
+ from instaui.common.jsonable import Jsonable
17
+ from instaui.runtime._app import get_current_scope
18
+
19
+
20
+ class VueWatch(Jsonable):
21
+ def __init__(
22
+ self,
23
+ sources: Union[Any, Sequence],
24
+ callback: str,
25
+ *,
26
+ bindings: Optional[Dict[str, Any]] = None,
27
+ immediate: bool = False,
28
+ deep: Union[bool, int] = False,
29
+ once: bool = False,
30
+ flush: Optional[_types.TFlush] = None,
31
+ ) -> None:
32
+ get_current_scope().register_vue_watch(self)
33
+
34
+ self.code = callback
35
+
36
+ if not isinstance(sources, Sequence):
37
+ sources = [sources]
38
+
39
+ onData = [int(not isinstance(varObj, ObservableMixin)) for varObj in sources]
40
+
41
+ if sum(onData) > 0:
42
+ self.onData = onData
43
+
44
+ self.on = [
45
+ cast(ObservableMixin, varObj)._to_observable_config()
46
+ if isinstance(varObj, ObservableMixin)
47
+ else varObj
48
+ for varObj in sources
49
+ ]
50
+
51
+ if bindings:
52
+ bindData = [
53
+ int(not isinstance(v, ObservableMixin)) for v in bindings.values()
54
+ ]
55
+
56
+ if sum(bindData) > 0:
57
+ self.bindData = bindData
58
+
59
+ self.bind = {
60
+ k: cast(ObservableMixin, v)._to_observable_config()
61
+ if isinstance(v, ObservableMixin)
62
+ else v
63
+ for k, v in bindings.items()
64
+ }
65
+
66
+ if immediate is not False:
67
+ self.immediate = immediate
68
+
69
+ if deep is not False:
70
+ _utils.assert_deep(deep)
71
+ self.deep = deep
72
+
73
+ if once is not False:
74
+ self.once = once
75
+
76
+ if flush is not None:
77
+ self.flush = flush
@@ -0,0 +1,181 @@
1
+ from __future__ import annotations
2
+ import inspect
3
+ import typing
4
+ from typing_extensions import ParamSpec
5
+ from . import _types
6
+ from . import _utils
7
+
8
+ from instaui.common.jsonable import Jsonable
9
+ from instaui.runtime._app import get_app_slot, get_current_scope
10
+ from instaui.handlers import watch_handler
11
+
12
+ from instaui.vars.mixin_types.py_binding import CanOutputMixin
13
+ from instaui.vars.mixin_types.element_binding import ElementBindingMixin
14
+ from instaui.vars.mixin_types.common_type import TObservableInput
15
+ from instaui._helper import observable_helper
16
+
17
+ _SYNC_TYPE = "sync"
18
+ _ASYNC_TYPE = "async"
19
+
20
+ P = ParamSpec("P")
21
+ R = typing.TypeVar("R")
22
+
23
+
24
+ class WebWatch(Jsonable, typing.Generic[P, R]):
25
+ def __init__(
26
+ self,
27
+ func: typing.Callable[P, R],
28
+ inputs: typing.Optional[typing.Sequence[TObservableInput]] = None,
29
+ outputs: typing.Optional[typing.Sequence[CanOutputMixin]] = None,
30
+ immediate: bool = True,
31
+ deep: typing.Union[bool, int] = True,
32
+ once: bool = False,
33
+ flush: typing.Optional[_types.TFlush] = None,
34
+ _debug: typing.Optional[typing.Any] = None,
35
+ ) -> None:
36
+ self.__org_inputs = inputs or []
37
+ inputs = observable_helper.auto_made_inputs_to_slient(inputs, outputs)
38
+
39
+ get_current_scope().register_web_watch(self)
40
+
41
+ self._inputs, self._is_slient_inputs, self._is_data = (
42
+ observable_helper.analyze_observable_inputs(list(inputs or []))
43
+ )
44
+
45
+ self._outputs = [output._to_output_config() for output in outputs or []]
46
+ self._fn = func
47
+ self._immediate = immediate
48
+ self._deep = deep
49
+ self._once = once
50
+ self._flush = flush
51
+ self._debug = _debug
52
+
53
+ def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
54
+ return self._fn(*args, **kwargs)
55
+
56
+ def _to_json_dict(self):
57
+ data = super()._to_json_dict()
58
+
59
+ app = get_app_slot()
60
+
61
+ for _input in self.__org_inputs:
62
+ if isinstance(_input, ElementBindingMixin):
63
+ _input._mark_used()
64
+
65
+ if app.mode == "web":
66
+ hkey = watch_handler.create_handler_key(
67
+ page_path=app.page_path,
68
+ handler=self._fn,
69
+ )
70
+
71
+ watch_handler.register_handler(hkey, self._fn, len(self._outputs))
72
+
73
+ data["fType"] = (
74
+ _ASYNC_TYPE if inspect.iscoroutinefunction(self._fn) else _SYNC_TYPE
75
+ )
76
+ data["key"] = hkey
77
+ if self._inputs:
78
+ data["inputs"] = self._inputs
79
+
80
+ if sum(self._is_slient_inputs) > 0:
81
+ data["slient"] = self._is_slient_inputs
82
+
83
+ if sum(self._is_data) > 0:
84
+ data["data"] = self._is_data
85
+
86
+ if self._debug:
87
+ data["debug"] = self._debug
88
+
89
+ if self._outputs:
90
+ data["outputs"] = self._outputs
91
+
92
+ if self._immediate is not True:
93
+ data["immediate"] = self._immediate
94
+
95
+ if self._deep is not True:
96
+ _utils.assert_deep(self._deep)
97
+ data["deep"] = self._deep
98
+ if self._once is not False:
99
+ data["once"] = self._once
100
+ if self._flush is not None:
101
+ data["flush"] = self._flush
102
+
103
+ return data
104
+
105
+ return {}
106
+
107
+
108
+ def watch(
109
+ *,
110
+ inputs: typing.Optional[typing.Sequence] = None,
111
+ outputs: typing.Optional[typing.Sequence] = None,
112
+ immediate: bool = True,
113
+ deep: typing.Union[bool, int] = True,
114
+ once: bool = False,
115
+ flush: typing.Optional[_types.TFlush] = None,
116
+ _debug: typing.Optional[typing.Any] = None,
117
+ ):
118
+ """
119
+ Creates an observer that tracks changes in reactive sources and triggers callbacks.
120
+
121
+ Args:
122
+ inputs (typing.Optional[typing.Sequence], optional): Reactive sources to observe (state objects or computed properties).
123
+ Changes to these sources trigger the watcher callback.
124
+ outputs (typing.Optional[typing.Sequence], optional): Output targets associated with this watcher.
125
+ Used for coordination with computed properties or other observers.
126
+ immediate (bool, optional): If True, executes callback immediately after creation with current values. Defaults to True.
127
+ deep (typing.Union[bool, int], optional): Controls depth of change detection:
128
+ - True: Recursively tracks nested properties
129
+ - False: Shallow comparison only
130
+ - int: Maximum depth level to track (for complex objects).
131
+ Defaults to True.
132
+ once (bool, optional): If True, automatically stops observation after first trigger. Defaults to False.
133
+ flush (typing.Optional[_types.TFlush], optional): Controls when to flush updates:
134
+ - 'sync': Execute immediately on change
135
+ - 'post': Batch updates and execute after current tick
136
+ - 'pre': Execute before render phase (if applicable)
137
+
138
+ # Example:
139
+ .. code-block:: python
140
+ from instaui import ui, html
141
+
142
+ num = ui.state(0)
143
+ msg = ui.state('')
144
+
145
+ @ui.watch(inputs=[num], outputs=[msg])
146
+ def when_num_change(num):
147
+ return f"The number is {num}"
148
+
149
+ html.number(num)
150
+ ui.label(msg)
151
+
152
+ list append:
153
+ .. code-block:: python
154
+ from instaui import ui, html
155
+
156
+ num = ui.state(0)
157
+ msg = ui.state([])
158
+
159
+ @ui.watch(inputs=[num, msg], outputs=[msg])
160
+ def when_num_change(num, msg:list):
161
+ msg.append(f"The number changed to {num}")
162
+ return msg
163
+
164
+ html.number(num)
165
+ ui.label(msg)
166
+
167
+ """
168
+
169
+ def wrapper(func: typing.Callable[P, R]):
170
+ return WebWatch(
171
+ func,
172
+ inputs,
173
+ outputs=outputs,
174
+ immediate=immediate,
175
+ deep=deep,
176
+ once=once,
177
+ flush=flush,
178
+ _debug=_debug,
179
+ )
180
+
181
+ return wrapper
@@ -0,0 +1,2 @@
1
+ __all__ = ["WebviewWrapper"]
2
+ from .index import WebviewWrapper
@@ -0,0 +1,8 @@
1
+ from pathlib import Path
2
+ import shutil
3
+
4
+
5
+ def reset_dir(dir_path: Path):
6
+ if dir_path.exists():
7
+ shutil.rmtree(dir_path)
8
+ dir_path.mkdir(exist_ok=True)
instaui/webview/api.py ADDED
@@ -0,0 +1,72 @@
1
+ from typing import Any, Dict
2
+
3
+
4
+ from instaui.runtime._app import get_app_slot
5
+ from instaui.handlers import watch_handler
6
+ from instaui.handlers import event_handler
7
+ from instaui.skip import is_skip_output
8
+
9
+
10
+ class Api:
11
+ def watch_call(self, data: Dict):
12
+ hkey = data.pop("key")
13
+ handler_info = watch_handler.get_handler_info(hkey)
14
+ if handler_info is None:
15
+ return {"error": "watch handler not found"}
16
+
17
+ update_app_page_info(data)
18
+
19
+ result = handler_info.fn(
20
+ *handler_info.get_handler_args(_get_binds_from_data(data))
21
+ )
22
+ return response_data(handler_info.outputs_binding_count, result)
23
+
24
+ def event_call(self, data: Dict):
25
+ handler = event_handler.get_handler(data["hKey"])
26
+ if handler is None:
27
+ raise ValueError("event handler not found")
28
+
29
+ update_app_page_info(data)
30
+
31
+
32
+ args = [bind for bind in data.get("bind", [])]
33
+
34
+ result = handler.fn(*handler.get_handler_args(args))
35
+ return response_data(handler.outputs_binding_count, result)
36
+
37
+
38
+ def update_app_page_info(data: Dict):
39
+ app = get_app_slot()
40
+
41
+ page_info = data.get("page", {})
42
+ app._page_path = page_info["path"]
43
+
44
+ if "params" in page_info:
45
+ app._page_params = page_info["params"]
46
+
47
+ if "queryParams" in page_info:
48
+ app._query_params = page_info["queryParams"]
49
+
50
+
51
+ def _get_binds_from_data(data: Dict):
52
+ return data.get("input", [])
53
+
54
+
55
+ def response_data(outputs_binding_count: int, result: Any):
56
+ data = {}
57
+ if outputs_binding_count > 0:
58
+ if not isinstance(result, tuple):
59
+ result = [result]
60
+
61
+ result_infos = [(r, int(is_skip_output(r))) for r in result]
62
+
63
+ if len(result_infos) == 1 and result_infos[0][1] == 1:
64
+ return data
65
+
66
+ data["values"] = [0 if info[1] == 1 else info[0] for info in result_infos]
67
+ skips = [info[1] for info in result_infos]
68
+
69
+ if sum(skips) > 0:
70
+ data["skips"] = skips
71
+
72
+ return data
@@ -0,0 +1,114 @@
1
+ from __future__ import annotations
2
+ import itertools
3
+ from pathlib import Path
4
+ import instaui.consts as consts
5
+ from instaui.runtime._app import get_app_slot, get_default_app_slot
6
+ from instaui.template import render_zero_html
7
+ from instaui.template import zero_template
8
+ from instaui.html_tools import to_config_data
9
+ from instaui.runtime.dataclass import JsLink
10
+ from instaui.runtime.resource import HtmlResource
11
+
12
+
13
+ def get_template_model():
14
+ system_slot = get_app_slot()
15
+
16
+ merge_global_resources = (system_slot.meta or {}).get(
17
+ "merge_global_resources", True
18
+ )
19
+
20
+ default_app_slot = get_default_app_slot()
21
+ html_resource = system_slot._html_resource
22
+ default_html_resource = (
23
+ default_app_slot._html_resource
24
+ if merge_global_resources
25
+ else _empty_html_resource()
26
+ )
27
+
28
+ config_data = to_config_data()
29
+
30
+ model = zero_template.ZeroTemplateModel(
31
+ vue_js_code=consts.VUE_ES_JS_PATH,
32
+ instaui_js_code=consts.APP_ES_JS_PATH,
33
+ css_links=[
34
+ consts.APP_CSS_PATH,
35
+ ],
36
+ config_dict=config_data,
37
+ favicon=html_resource.favicon
38
+ or default_html_resource.favicon
39
+ or consts.FAVICON_PATH,
40
+ title=html_resource.title or default_html_resource.title or consts.PAGE_TITLE,
41
+ )
42
+
43
+ # register custom components
44
+ for component in system_slot._component_dependencies:
45
+ if not component.esm:
46
+ continue
47
+
48
+ model.vue_app_component.append(
49
+ zero_template.ZeroVueAppComponent(
50
+ name=component.tag_name,
51
+ url=component.esm,
52
+ )
53
+ )
54
+
55
+ if component.css:
56
+ for css_link in component.css:
57
+ model.css_links.append(css_link)
58
+
59
+ if component.externals:
60
+ for name, url in component.externals.items():
61
+ if url.is_file():
62
+ model.add_extra_import_map(name, url)
63
+
64
+ # register custom plugins
65
+ for plugin in set(
66
+ itertools.chain(
67
+ system_slot._plugin_dependencies, default_app_slot._plugin_dependencies
68
+ )
69
+ ):
70
+ if not plugin.esm:
71
+ continue
72
+
73
+ model.vue_app_use.append(plugin.name)
74
+
75
+ model.add_extra_import_map(plugin.name, plugin.esm)
76
+
77
+ if plugin.css:
78
+ for css_link in plugin.css:
79
+ model.css_links.append(css_link)
80
+
81
+ # css file link to web static link
82
+ for link in html_resource.get_valid_css_links(
83
+ default_html_resource._css_links_manager
84
+ ):
85
+ if isinstance(link, Path):
86
+ model.css_links.append(link)
87
+
88
+ # js file link to web static link
89
+ for info in html_resource.get_valid_js_links(
90
+ default_html_resource._js_links_manager
91
+ ):
92
+ if isinstance(info.link, Path):
93
+ model.js_links.append(JsLink(info.link))
94
+
95
+ for js_code in itertools.chain(
96
+ html_resource._script_tags, default_html_resource._script_tags
97
+ ):
98
+ model.script_tags.append(js_code)
99
+
100
+ for sylte_code in itertools.chain(
101
+ html_resource._style_tags, default_html_resource._style_tags
102
+ ):
103
+ model.style_tags.append(sylte_code)
104
+
105
+ return model
106
+
107
+
108
+ def to_html_str():
109
+ model = get_template_model()
110
+ return render_zero_html(model)
111
+
112
+
113
+ def _empty_html_resource():
114
+ return HtmlResource()