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.
- instaui/__init__.py +9 -0
- instaui/_helper/observable_helper.py +45 -0
- instaui/arco/__init__.py +191 -0
- instaui/arco/_settings.py +25 -0
- instaui/arco/_use_tools/locale.py +50 -0
- instaui/arco/component_types.py +1019 -0
- instaui/arco/components/_utils.py +22 -0
- instaui/arco/components/affix.py +29 -0
- instaui/arco/components/alert.py +42 -0
- instaui/arco/components/anchor.py +42 -0
- instaui/arco/components/auto_complete.py +96 -0
- instaui/arco/components/avatar.py +55 -0
- instaui/arco/components/back_top.py +14 -0
- instaui/arco/components/badge.py +14 -0
- instaui/arco/components/breadcrumb.py +14 -0
- instaui/arco/components/button.py +43 -0
- instaui/arco/components/calendar.py +47 -0
- instaui/arco/components/card.py +14 -0
- instaui/arco/components/carousel.py +33 -0
- instaui/arco/components/cascader.py +111 -0
- instaui/arco/components/checkbox.py +32 -0
- instaui/arco/components/collapse.py +31 -0
- instaui/arco/components/color_picker.py +45 -0
- instaui/arco/components/comment.py +14 -0
- instaui/arco/components/config_provider.py +13 -0
- instaui/arco/components/date_picker.py +111 -0
- instaui/arco/components/descriptions.py +14 -0
- instaui/arco/components/divider.py +13 -0
- instaui/arco/components/drawer.py +98 -0
- instaui/arco/components/dropdown.py +45 -0
- instaui/arco/components/empty.py +14 -0
- instaui/arco/components/form.py +55 -0
- instaui/arco/components/icon.py +17 -0
- instaui/arco/components/image.py +33 -0
- instaui/arco/components/input.py +102 -0
- instaui/arco/components/input_number.py +97 -0
- instaui/arco/components/input_password.py +38 -0
- instaui/arco/components/input_search.py +37 -0
- instaui/arco/components/input_tag.py +110 -0
- instaui/arco/components/layout.py +13 -0
- instaui/arco/components/layout_content.py +6 -0
- instaui/arco/components/layout_footer.py +6 -0
- instaui/arco/components/layout_header.py +6 -0
- instaui/arco/components/layout_sider.py +53 -0
- instaui/arco/components/link.py +36 -0
- instaui/arco/components/list.py +68 -0
- instaui/arco/components/mention.py +97 -0
- instaui/arco/components/menu.py +88 -0
- instaui/arco/components/modal.py +97 -0
- instaui/arco/components/overflow_list.py +29 -0
- instaui/arco/components/page_header.py +29 -0
- instaui/arco/components/pagination.py +45 -0
- instaui/arco/components/pop_confirm.py +58 -0
- instaui/arco/components/popover.py +32 -0
- instaui/arco/components/progress.py +14 -0
- instaui/arco/components/radio.py +40 -0
- instaui/arco/components/radio_group.py +42 -0
- instaui/arco/components/rate.py +45 -0
- instaui/arco/components/resize_box.py +62 -0
- instaui/arco/components/result.py +14 -0
- instaui/arco/components/select.py +182 -0
- instaui/arco/components/skeleton.py +14 -0
- instaui/arco/components/slider.py +38 -0
- instaui/arco/components/space.py +14 -0
- instaui/arco/components/spin.py +14 -0
- instaui/arco/components/split.py +76 -0
- instaui/arco/components/statistic.py +14 -0
- instaui/arco/components/steps.py +32 -0
- instaui/arco/components/switch.py +57 -0
- instaui/arco/components/tab_pane.py +12 -0
- instaui/arco/components/table.py +276 -0
- instaui/arco/components/tabs.py +101 -0
- instaui/arco/components/tag.py +42 -0
- instaui/arco/components/textarea.py +84 -0
- instaui/arco/components/time_picker.py +76 -0
- instaui/arco/components/timeline.py +14 -0
- instaui/arco/components/tooltip.py +29 -0
- instaui/arco/components/transfer.py +58 -0
- instaui/arco/components/tree.py +120 -0
- instaui/arco/components/tree_select.py +86 -0
- instaui/arco/components/trigger.py +58 -0
- instaui/arco/components/typography.py +142 -0
- instaui/arco/components/upload.py +71 -0
- instaui/arco/components/verification_code.py +58 -0
- instaui/arco/components/watermark.py +14 -0
- instaui/arco/locales/__init__.py +4 -0
- instaui/arco/locales/_index.py +31 -0
- instaui/arco/locales/en_us.py +227 -0
- instaui/arco/locales/zh_cn.py +224 -0
- instaui/arco/setup.py +36 -0
- instaui/arco/static/instaui-arco.css +1 -0
- instaui/arco/static/instaui-arco.js +55771 -0
- instaui/arco/types.py +24 -0
- instaui/boot_info.py +43 -0
- instaui/common/jsonable.py +37 -0
- instaui/components/__init__.py +0 -0
- instaui/components/column.py +26 -0
- instaui/components/component.py +47 -0
- instaui/components/content.py +34 -0
- instaui/components/directive.py +55 -0
- instaui/components/element.py +573 -0
- instaui/components/grid.py +213 -0
- instaui/components/html/__init__.py +49 -0
- instaui/components/html/_mixins.py +34 -0
- instaui/components/html/_preset.py +4 -0
- instaui/components/html/button.py +38 -0
- instaui/components/html/checkbox.py +35 -0
- instaui/components/html/date.py +28 -0
- instaui/components/html/div.py +7 -0
- instaui/components/html/form.py +7 -0
- instaui/components/html/heading.py +51 -0
- instaui/components/html/input.py +28 -0
- instaui/components/html/label.py +21 -0
- instaui/components/html/li.py +17 -0
- instaui/components/html/link.py +31 -0
- instaui/components/html/number.py +34 -0
- instaui/components/html/paragraph.py +29 -0
- instaui/components/html/range.py +48 -0
- instaui/components/html/select.py +69 -0
- instaui/components/html/span.py +19 -0
- instaui/components/html/table.py +36 -0
- instaui/components/html/textarea.py +28 -0
- instaui/components/html/ul.py +20 -0
- instaui/components/label.py +5 -0
- instaui/components/markdown/markdown.js +33 -0
- instaui/components/markdown/markdown.py +41 -0
- instaui/components/markdown/static/github-markdown.css +12 -0
- instaui/components/markdown/static/marked.esm.js +2579 -0
- instaui/components/match.py +108 -0
- instaui/components/row.py +17 -0
- instaui/components/shiki_code/shiki_code.js +126 -0
- instaui/components/shiki_code/shiki_code.py +99 -0
- instaui/components/shiki_code/static/langs/css.mjs +5 -0
- instaui/components/shiki_code/static/langs/markdown.mjs +5 -0
- instaui/components/shiki_code/static/langs/python.mjs +5 -0
- instaui/components/shiki_code/static/langs/shell.mjs +2 -0
- instaui/components/shiki_code/static/langs/shellscript.mjs +5 -0
- instaui/components/shiki_code/static/shiki-core.js +5784 -0
- instaui/components/shiki_code/static/shiki-style.css +179 -0
- instaui/components/shiki_code/static/shiki-transformers.js +461 -0
- instaui/components/shiki_code/static/themes/vitesse-dark.mjs +2 -0
- instaui/components/shiki_code/static/themes/vitesse-light.mjs +2 -0
- instaui/components/slot.py +81 -0
- instaui/components/transition_group.py +9 -0
- instaui/components/value_element.py +52 -0
- instaui/components/vfor.py +142 -0
- instaui/components/vif.py +42 -0
- instaui/consts.py +23 -0
- instaui/dependencies/component_dependency.py +22 -0
- instaui/dependencies/plugin_dependency.py +28 -0
- instaui/event/event_mixin.py +12 -0
- instaui/event/js_event.py +82 -0
- instaui/event/vue_event.py +66 -0
- instaui/event/web_event.py +123 -0
- instaui/experimental/__init__.py +3 -0
- instaui/experimental/debug.py +48 -0
- instaui/extra_libs/_echarts.py +3 -0
- instaui/extra_libs/_import_error.py +9 -0
- instaui/extra_libs/_mermaid.py +3 -0
- instaui/extra_libs/_shiki_code.py +3 -0
- instaui/fastapi_server/_utils.py +42 -0
- instaui/fastapi_server/_uvicorn.py +37 -0
- instaui/fastapi_server/debug_mode_router.py +60 -0
- instaui/fastapi_server/dependency_router.py +28 -0
- instaui/fastapi_server/event_router.py +58 -0
- instaui/fastapi_server/middlewares.py +19 -0
- instaui/fastapi_server/request_context.py +19 -0
- instaui/fastapi_server/resource.py +30 -0
- instaui/fastapi_server/server.py +308 -0
- instaui/fastapi_server/watch_router.py +53 -0
- instaui/handlers/_utils.py +88 -0
- instaui/handlers/event_handler.py +60 -0
- instaui/handlers/watch_handler.py +61 -0
- instaui/html_tools.py +94 -0
- instaui/inject.py +33 -0
- instaui/js/__init__.py +4 -0
- instaui/js/js_output.py +15 -0
- instaui/js/lambda_func.py +35 -0
- instaui/launch_collector.py +52 -0
- instaui/page_info.py +13 -0
- instaui/runtime/__init__.py +29 -0
- instaui/runtime/_app.py +234 -0
- instaui/runtime/_inner_helper.py +9 -0
- instaui/runtime/_link_manager.py +89 -0
- instaui/runtime/context.py +47 -0
- instaui/runtime/dataclass.py +30 -0
- instaui/runtime/resource.py +65 -0
- instaui/runtime/scope.py +133 -0
- instaui/runtime/ui_state_scope.py +15 -0
- instaui/settings/__init__.py +4 -0
- instaui/settings/__settings.py +13 -0
- instaui/shadcn_classless/_index.py +42 -0
- instaui/shadcn_classless/static/shadcn-classless.css +403 -0
- instaui/skip.py +12 -0
- instaui/spa_router/__init__.py +26 -0
- instaui/spa_router/_components.py +35 -0
- instaui/spa_router/_file_base_utils.py +273 -0
- instaui/spa_router/_functions.py +122 -0
- instaui/spa_router/_install.py +11 -0
- instaui/spa_router/_route_model.py +117 -0
- instaui/spa_router/_router_box.py +40 -0
- instaui/spa_router/_router_output.py +22 -0
- instaui/spa_router/_router_param_var.py +51 -0
- instaui/spa_router/_types.py +4 -0
- instaui/spa_router/templates/page_routes +60 -0
- instaui/static/insta-ui.css +1 -0
- instaui/static/insta-ui.esm-browser.prod.js +3717 -0
- instaui/static/insta-ui.ico +0 -0
- instaui/static/insta-ui.js.map +1 -0
- instaui/static/instaui-tools-browser.js +511 -0
- instaui/static/templates/debug/sse.html +117 -0
- instaui/static/templates/web.html +74 -0
- instaui/static/templates/webview.html +78 -0
- instaui/static/templates/zero.html +71 -0
- instaui/static/vue.esm-browser.prod.js +9 -0
- instaui/static/vue.global.prod.js +9 -0
- instaui/static/vue.runtime.esm-browser.prod.js +5 -0
- instaui/systems/file_system.py +6 -0
- instaui/systems/func_system.py +119 -0
- instaui/systems/js_system.py +22 -0
- instaui/systems/module_system.py +46 -0
- instaui/systems/pydantic_system.py +27 -0
- instaui/systems/string_system.py +10 -0
- instaui/tailwind/__init__.py +6 -0
- instaui/tailwind/_index.py +24 -0
- instaui/tailwind/static/tailwindcss-v3.min.js +62 -0
- instaui/tailwind/static/tailwindcss-v4.min.js +8 -0
- instaui/template/__init__.py +4 -0
- instaui/template/_utils.py +23 -0
- instaui/template/env.py +7 -0
- instaui/template/web_template.py +49 -0
- instaui/template/webview_template.py +48 -0
- instaui/template/zero_template.py +105 -0
- instaui/ui/__init__.py +144 -0
- instaui/ui/__init__.pyi +149 -0
- instaui/ui/events.py +25 -0
- instaui/ui_functions/input_slient_data.py +16 -0
- instaui/ui_functions/server.py +15 -0
- instaui/ui_functions/str_format.py +36 -0
- instaui/ui_functions/ui_page.py +16 -0
- instaui/ui_functions/ui_types.py +13 -0
- instaui/ui_functions/url_location.py +33 -0
- instaui/vars/_types.py +8 -0
- instaui/vars/data.py +68 -0
- instaui/vars/element_ref.py +40 -0
- instaui/vars/event_context.py +49 -0
- instaui/vars/event_extend.py +0 -0
- instaui/vars/js_computed.py +117 -0
- instaui/vars/mixin_types/common_type.py +5 -0
- instaui/vars/mixin_types/element_binding.py +16 -0
- instaui/vars/mixin_types/observable.py +7 -0
- instaui/vars/mixin_types/pathable.py +14 -0
- instaui/vars/mixin_types/py_binding.py +13 -0
- instaui/vars/mixin_types/str_format_binding.py +8 -0
- instaui/vars/mixin_types/var_type.py +5 -0
- instaui/vars/path_var.py +90 -0
- instaui/vars/ref.py +103 -0
- instaui/vars/slot_prop.py +46 -0
- instaui/vars/state.py +97 -0
- instaui/vars/types.py +24 -0
- instaui/vars/vfor_item.py +204 -0
- instaui/vars/vue_computed.py +81 -0
- instaui/vars/web_computed.py +209 -0
- instaui/vars/web_view_computed.py +1 -0
- instaui/version.py +3 -0
- instaui/watch/_types.py +4 -0
- instaui/watch/_utils.py +3 -0
- instaui/watch/js_watch.py +110 -0
- instaui/watch/vue_watch.py +77 -0
- instaui/watch/web_watch.py +181 -0
- instaui/webview/__init__.py +2 -0
- instaui/webview/_utils.py +8 -0
- instaui/webview/api.py +72 -0
- instaui/webview/func.py +114 -0
- instaui/webview/index.py +161 -0
- instaui/webview/resource.py +172 -0
- instaui/zero/__init__.py +3 -0
- instaui/zero/func.py +123 -0
- instaui/zero/scope.py +109 -0
- instaui-0.1.15.dist-info/METADATA +152 -0
- instaui-0.1.15.dist-info/RECORD +283 -0
- instaui-0.1.15.dist-info/WHEEL +5 -0
- 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
|
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
|
instaui/webview/func.py
ADDED
@@ -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()
|