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,273 @@
1
+ from __future__ import annotations
2
+ from collections import deque
3
+ from datetime import datetime
4
+ import importlib.util
5
+ from pathlib import Path
6
+ import typing
7
+ import jinja2
8
+ import inspect
9
+ from dataclasses import dataclass, field
10
+
11
+
12
+ def build_routes_from_files(
13
+ folder_path: typing.Union[str, Path] = "pages",
14
+ module_name: str = "_routes",
15
+ route_config_var_name: str = "_route_config",
16
+ ):
17
+ global_args = _GlobalArgs(
18
+ base_folder_path=_utils.get_caller_path().parent / Path(folder_path),
19
+ module_name=module_name,
20
+ route_config_var_name=route_config_var_name,
21
+ )
22
+
23
+ root = _model_utils.create_root(global_args)
24
+ _code_gen.generate_router_file(root)
25
+
26
+ print(f"Build _routes from files in {global_args.base_folder_path}...")
27
+
28
+
29
+ @dataclass
30
+ class _GlobalArgs:
31
+ base_folder_path: Path
32
+ module_name: str
33
+ route_config_var_name: str
34
+
35
+
36
+ class _model_utils:
37
+ @dataclass
38
+ class FileRouteInfo:
39
+ file: Path
40
+ base_folder: Path
41
+ global_args: _GlobalArgs
42
+ children: typing.List[_model_utils.FileRouteInfo] = field(default_factory=list)
43
+ path: str = field(init=False, default="")
44
+ name: str = field(init=False, default="")
45
+ fn_path: typing.Optional[str] = field(init=False, default=None)
46
+
47
+ params: str = field(init=False, default="")
48
+ meta: typing.Dict = field(init=False, default_factory=dict)
49
+
50
+ def __post_init__(self):
51
+ self.params = self._extract_params()
52
+ self.meta = self._extract_meta()
53
+ self.path, self.name = self._extra_path_name()
54
+
55
+ if self.file.is_file():
56
+ self.fn_path = ".".join(
57
+ self.file.relative_to(self.base_folder).with_suffix("").parts
58
+ )
59
+
60
+ def is_index_file(self):
61
+ return self.file.is_file() and self.file.stem == "index"
62
+
63
+ def change_sync_index_info(self, index_info: _model_utils.FileRouteInfo):
64
+ self.fn_path = index_info.fn_path
65
+ self.path = self.path + index_info.params
66
+ self.meta = index_info.meta
67
+
68
+ def _extract_params(self):
69
+ if self.file.is_file():
70
+ route_config = _module_utils.get_module_getter(self.file)(
71
+ self.global_args.route_config_var_name
72
+ )
73
+ if route_config:
74
+ if "params" in route_config:
75
+ return route_config["params"]
76
+
77
+ return ""
78
+
79
+ def _extract_meta(self):
80
+ if self.file.is_file():
81
+ route_config = _module_utils.get_module_getter(self.file)(
82
+ self.global_args.route_config_var_name
83
+ )
84
+
85
+ if route_config:
86
+ if "meta" in route_config:
87
+ return route_config["meta"]
88
+
89
+ return {}
90
+
91
+ def _extra_path_name(self):
92
+ name_parts = list(
93
+ self.file.relative_to(self.base_folder).with_suffix("").parts
94
+ )
95
+
96
+ is_root = len(name_parts) == 1
97
+
98
+ path = self.file.stem
99
+ if path == "index":
100
+ path = ""
101
+
102
+ name = ".".join(name_parts)
103
+
104
+ if is_root:
105
+ path = "/" + path
106
+
107
+ if self.params:
108
+ path += self.params
109
+
110
+ return path, name
111
+
112
+ def import_code(self):
113
+ if not self.fn_path:
114
+ return ""
115
+
116
+ return f"from .{self.fn_path.replace(' ','_')} import main as {self.main_fn_name()}"
117
+
118
+ def main_fn_name(self):
119
+ if not self.fn_path:
120
+ return ""
121
+ return self.name.replace(".", "_").replace(" ", "_")
122
+
123
+ @dataclass
124
+ class FileRouteRoot:
125
+ folder: str
126
+ module_name: str
127
+ infos: list[_model_utils.FileRouteInfo] = field(default_factory=list)
128
+
129
+ @staticmethod
130
+ def create_root(global_args: _GlobalArgs) -> FileRouteRoot:
131
+ base_folder = Path(global_args.base_folder_path)
132
+ infos = _model_utils._create_route_info(base_folder, global_args)
133
+ return _model_utils.FileRouteRoot(
134
+ folder=str(base_folder), module_name=global_args.module_name, infos=infos
135
+ )
136
+
137
+ @staticmethod
138
+ def _create_route_info(
139
+ base_folder: Path, global_args: _GlobalArgs
140
+ ) -> typing.List[FileRouteInfo]:
141
+ result: typing.List[_model_utils.FileRouteInfo] = []
142
+
143
+ stack: deque[
144
+ typing.Tuple[typing.Optional[_model_utils.FileRouteInfo], Path]
145
+ ] = deque()
146
+ stack.extendleft((None, path) for path in base_folder.iterdir())
147
+
148
+ while stack:
149
+ parent_info, item = stack.pop()
150
+ is_dir = item.is_dir()
151
+
152
+ if item.stem.startswith("_"):
153
+ continue
154
+
155
+ if is_dir:
156
+ folder_info = _model_utils.FileRouteInfo(
157
+ file=item, base_folder=base_folder, global_args=global_args
158
+ )
159
+ infos = ((folder_info, path) for path in item.iterdir())
160
+ stack.extendleft(infos)
161
+
162
+ if parent_info is None:
163
+ result.append(folder_info)
164
+ else:
165
+ parent_info.children.append(folder_info)
166
+ continue
167
+
168
+ if item.suffix != ".py":
169
+ continue
170
+
171
+ file_info = _model_utils.FileRouteInfo(
172
+ file=item, base_folder=base_folder, global_args=global_args
173
+ )
174
+
175
+ if parent_info is None:
176
+ result.append(file_info)
177
+ else:
178
+ if file_info.is_index_file():
179
+ parent_info.change_sync_index_info(file_info)
180
+
181
+ else:
182
+ parent_info.children.append(file_info)
183
+
184
+ return result
185
+
186
+ @staticmethod
187
+ def iter_route_info(infos: typing.List[FileRouteInfo]):
188
+ stack: typing.List[_model_utils.FileRouteInfo] = []
189
+ stack.extend(infos)
190
+
191
+ while stack:
192
+ info = stack.pop()
193
+ stack.extend(info.children)
194
+ yield info
195
+
196
+
197
+ class _code_gen:
198
+ _env = jinja2.Environment(
199
+ loader=jinja2.PackageLoader("instaui.spa_router", "templates"),
200
+ )
201
+
202
+ @dataclass
203
+ class TemplateModel:
204
+ update_time: datetime = field(default_factory=datetime.now)
205
+ route_names: typing.List[str] = field(default_factory=list)
206
+ routes: typing.List[_model_utils.FileRouteInfo] = field(default_factory=list)
207
+
208
+ def get_all_main_import(self):
209
+ return [
210
+ info.import_code() for info in _model_utils.iter_route_info(self.routes)
211
+ ]
212
+
213
+ @staticmethod
214
+ def generate_router_file(root: _model_utils.FileRouteRoot):
215
+ _template = _code_gen._env.get_template("page_routes")
216
+
217
+ template_model = _code_gen.TemplateModel(
218
+ route_names=_code_gen._extract_all_route_names(root), routes=root.infos
219
+ )
220
+
221
+ code = _template.render(model=template_model)
222
+ Path(root.folder).joinpath(f"{root.module_name}.py").write_text(
223
+ code, encoding="utf-8"
224
+ )
225
+
226
+ @staticmethod
227
+ def _extract_all_route_names(root: _model_utils.FileRouteRoot):
228
+ return [
229
+ info.name
230
+ for info in _model_utils.iter_route_info(root.infos)
231
+ if info.fn_path
232
+ ]
233
+
234
+
235
+ class _module_utils:
236
+ @staticmethod
237
+ def get_module_getter(path: Path):
238
+ if not isinstance(path, Path):
239
+ raise ValueError("Expected a Path object")
240
+
241
+ if not path.exists():
242
+ raise FileNotFoundError(f"The file {path} does not exist.")
243
+
244
+ module_name = path.stem
245
+ module_path = str(path.absolute())
246
+
247
+ spec = importlib.util.spec_from_file_location(module_name, module_path)
248
+ if spec is None:
249
+ raise ImportError(f"Cannot create a module spec for {module_path}")
250
+
251
+ module = importlib.util.module_from_spec(spec)
252
+
253
+ try:
254
+ spec.loader.exec_module(module) # type: ignore
255
+ except Exception as e:
256
+ raise ImportError(f"Failed to import {module_path}: {e}")
257
+
258
+ def getter_fn(var_name: str):
259
+ return getattr(module, var_name, None)
260
+
261
+ return getter_fn
262
+
263
+
264
+ class _utils:
265
+ @staticmethod
266
+ def get_caller_path():
267
+ current_frame = inspect.currentframe()
268
+ try:
269
+ caller_frame = current_frame.f_back.f_back # type: ignore
270
+ filename = caller_frame.f_code.co_filename # type: ignore
271
+ return Path(filename)
272
+ finally:
273
+ del current_frame
@@ -0,0 +1,122 @@
1
+ import typing
2
+ from . import _types
3
+ from instaui.runtime._app import get_app_slot
4
+ from . import _install
5
+ from ._router_param_var import RouterParamsVar
6
+ from ._router_output import RouterOutput, RouterMethod
7
+ from ._route_model import RouteItem
8
+
9
+ _ASSERT_MSG = "Router is not initialized."
10
+
11
+
12
+ def add_route(
13
+ page_fn: typing.Callable,
14
+ *,
15
+ name: typing.Optional[str] = None,
16
+ path: typing.Optional[str] = None,
17
+ children: typing.Optional[typing.List[RouteItem]] = None,
18
+ lazy_loading: bool = False,
19
+ ):
20
+ _install.try_register_router_collector()
21
+ route_collector = get_app_slot()._route_collector
22
+ assert route_collector is not None, _ASSERT_MSG
23
+ route_collector.add_route(
24
+ RouteItem.create(
25
+ path=path,
26
+ component_fn=page_fn,
27
+ name=name,
28
+ children=children,
29
+ )
30
+ )
31
+
32
+
33
+ def config_router(
34
+ routes: typing.Optional[typing.List[RouteItem]] = None,
35
+ *,
36
+ history: _types.TRouterHistoryMode = "hash",
37
+ keep_alive: bool = False,
38
+ ):
39
+ """Configure the router.
40
+
41
+ Examples:
42
+ .. code-block:: python
43
+ routes = [
44
+ spa_router.RouteItem.create(path='/',component_fn=home),
45
+ spa_router.RouteItem.create(path='/user',component_fn=user_home),
46
+ ]
47
+
48
+ spa_router.config_router(routes=routes)
49
+
50
+ Args:
51
+ routes (typing.Optional[typing.List[RouteItem]], optional): list of routes to be added to the router. Defaults to None.
52
+ history (_types.TRouterHistoryMode, optional): router history mode. Can be "web", "memory" or "hash". Defaults to "hash".
53
+ keep_alive (bool, optional): whether to keep the components alive when navigating to a new route.Defaults to False.
54
+ """
55
+
56
+ _install.try_register_router_collector()
57
+
58
+ route_collector = get_app_slot()._route_collector
59
+ assert route_collector is not None, _ASSERT_MSG
60
+
61
+ route_collector.mode = history
62
+ route_collector.keep_alive = keep_alive
63
+ route_collector.routes = routes or []
64
+
65
+
66
+ def get_params(param_name: str) -> typing.Any:
67
+ return RouterParamsVar("params")[param_name]
68
+
69
+
70
+ def get_path():
71
+ return RouterParamsVar("path")
72
+
73
+
74
+ def get_full_path():
75
+ return RouterParamsVar("fullPath")
76
+
77
+
78
+ def push(
79
+ *,
80
+ path: typing.Optional[str] = None,
81
+ name: typing.Optional[str] = None,
82
+ params: typing.Optional[typing.Dict[str, typing.Any]] = None,
83
+ query: typing.Optional[typing.Dict[str, typing.Any]] = None,
84
+ hash: typing.Optional[str] = None,
85
+ ):
86
+ method_params: typing.Dict = {}
87
+
88
+ if path is not None:
89
+ method_params["path"] = path
90
+ if name is not None:
91
+ method_params["name"] = name
92
+
93
+ if params is not None:
94
+ method_params["params"] = params
95
+ if query is not None:
96
+ method_params["query"] = query
97
+ if hash is not None:
98
+ method_params["hash"] = hash
99
+
100
+ return RouterMethod(
101
+ fn="push",
102
+ args=[method_params],
103
+ )
104
+
105
+
106
+ def go(n: int):
107
+ return RouterMethod(
108
+ fn="go",
109
+ args=[n],
110
+ )
111
+
112
+
113
+ def forward():
114
+ return go(1)
115
+
116
+
117
+ def back():
118
+ return go(-1)
119
+
120
+
121
+ def output():
122
+ return RouterOutput()
@@ -0,0 +1,11 @@
1
+ from instaui.runtime._app import get_app_slot
2
+ from instaui.spa_router._route_model import RouteCollector
3
+
4
+
5
+ def try_register_router_collector():
6
+ app = get_app_slot()
7
+ if app._route_collector is not None:
8
+ return
9
+
10
+ rb = RouteCollector()
11
+ app.register_router(rb)
@@ -0,0 +1,117 @@
1
+ from __future__ import annotations
2
+ import typing
3
+ from pydantic import BaseModel, Field, ConfigDict, field_serializer, model_serializer
4
+
5
+ from instaui.components.html.div import Div
6
+ from instaui.runtime._app import get_app_slot, new_scope
7
+ from instaui.components.component import Component
8
+ from instaui.runtime.scope import Scope
9
+ from instaui.common.jsonable import dumps2dict
10
+
11
+ from . import _types
12
+
13
+
14
+ class RouteItem(BaseModel):
15
+ model_config = ConfigDict(arbitrary_types_allowed=True)
16
+
17
+ component_fn: typing.Optional[typing.Callable] = Field(exclude=True)
18
+ scope: typing.Optional[Scope] = None
19
+ vue_route_item: VueRouteItem = Field(serialization_alias="vueItem")
20
+ meta: typing.Optional[typing.Dict] = None
21
+
22
+ @model_serializer
23
+ def model_ser(self):
24
+ if self.component_fn is None and (not self.vue_route_item.path):
25
+ raise ValueError("Either component_fn or vue_route_item.path must be set")
26
+
27
+ if self.component_fn is None:
28
+ return {
29
+ "vueItem": self.vue_route_item,
30
+ }
31
+
32
+ if self.vue_route_item.path is None:
33
+ self.vue_route_item.path = f"/{'' if self.component_fn.__name__=='index' else self.component_fn.__name__}"
34
+
35
+ app = get_app_slot()
36
+ with new_scope(append_to_app=False) as scope:
37
+ with Div() as div:
38
+ self.component_fn()
39
+
40
+ app.items.pop()
41
+
42
+ self.scope = scope
43
+ self.vue_route_item.component = div._slot_manager.default._children
44
+
45
+ return {
46
+ "scope": dumps2dict(scope),
47
+ "vueItem": self.vue_route_item,
48
+ }
49
+
50
+ @classmethod
51
+ def create(
52
+ cls,
53
+ *,
54
+ component_fn: typing.Optional[typing.Callable] = None,
55
+ path: typing.Optional[str] = None,
56
+ name: typing.Optional[str] = None,
57
+ params: typing.Optional[typing.Dict[str, str]] = None,
58
+ children: typing.Optional[typing.List[RouteItem]] = None,
59
+ meta: typing.Optional[typing.Dict] = None,
60
+ ):
61
+ """Create a new RouteItem
62
+
63
+ Examples:
64
+ .. code-block:: python
65
+ routes = [
66
+ spa_router.RouteItem.create(path='/',component_fn=home),
67
+ spa_router.RouteItem.create(path='/user',component_fn=user_home),
68
+ ]
69
+
70
+ spa_router.config_router(routes=routes)
71
+
72
+ Args:
73
+ component_fn (typing.Callable): function that returns a component to be rendered.
74
+ path (typing.Optional[str], optional): route path. Defaults to None.
75
+ name (typing.Optional[str], optional): route name. Defaults to None.
76
+ params (typing.Optional[typing.Dict[str, str]], optional): route params. Defaults to None.
77
+ children (typing.Optional[typing.List[RouteItem]], optional): child routes. Defaults to None.
78
+
79
+ """
80
+
81
+ return cls(
82
+ component_fn=component_fn,
83
+ meta=meta,
84
+ vue_route_item=VueRouteItem(
85
+ path=path,
86
+ name=name,
87
+ params=params,
88
+ component=[],
89
+ children=children,
90
+ ),
91
+ )
92
+
93
+
94
+ class VueRouteItem(BaseModel):
95
+ model_config = ConfigDict(arbitrary_types_allowed=True)
96
+
97
+ path: typing.Optional[str] = None
98
+ params: typing.Optional[typing.Dict[str, str]] = None
99
+ name: typing.Optional[str] = None
100
+ component: typing.Optional[typing.List[Component]] = []
101
+ children: typing.Optional[typing.List[RouteItem]] = None
102
+
103
+ @field_serializer("component")
104
+ def serialize_component(self, value: typing.List[Component]):
105
+ if not value:
106
+ return []
107
+ return dumps2dict(value)
108
+
109
+
110
+ class RouteCollector(BaseModel):
111
+ mode: _types.TRouterHistoryMode = "hash"
112
+ keep_alive: bool = Field(default=False, serialization_alias="kAlive")
113
+ routes: typing.List[RouteItem] = []
114
+
115
+ def add_route(self, item: RouteItem):
116
+ self.routes.append(item)
117
+ return self
@@ -0,0 +1,40 @@
1
+ from __future__ import annotations
2
+ from typing import Dict, List
3
+ from instaui.common.jsonable import Jsonable
4
+ from instaui.components.component import Component
5
+ from instaui.runtime.scope import Scope
6
+
7
+ from . import _types
8
+ from ._route_model import RouteCollector
9
+
10
+
11
+ class RouterBox_(Jsonable):
12
+ def __init__(self):
13
+ self._mode: _types.TRouterHistoryMode = "web"
14
+ self._keep_alive: bool = False
15
+ self._component_map: Dict[str, Dict] = {}
16
+ self._route_collector: RouteCollector = RouteCollector()
17
+
18
+ def add_component(
19
+ self,
20
+ path: str,
21
+ components: List[Component],
22
+ scope: Scope,
23
+ lazy_loading: bool = False,
24
+ ):
25
+ config = {"items": components, "scope": scope}
26
+ if lazy_loading:
27
+ config["lazy"] = True
28
+
29
+ self._component_map[path] = config
30
+
31
+ def _to_json_dict(self):
32
+ data = {}
33
+
34
+ data["routes"] = self._route_collector.model_dump(exclude_none=True)["routes"]
35
+ data["mode"] = self._mode
36
+
37
+ if self._keep_alive is not False:
38
+ data["kAlive"] = self._keep_alive
39
+
40
+ return data
@@ -0,0 +1,22 @@
1
+ from typing import List, Optional
2
+ from instaui.common.jsonable import Jsonable
3
+ from instaui.vars.mixin_types.py_binding import CanOutputMixin
4
+ from pydantic import BaseModel
5
+
6
+
7
+ class RouterOutput(Jsonable, CanOutputMixin):
8
+ def __init__(self):
9
+ self.type = "routeAct"
10
+
11
+ def _to_output_config(self):
12
+ return self._to_json_dict()
13
+
14
+ def _to_json_dict(self):
15
+ data = super()._to_json_dict()
16
+
17
+ return data
18
+
19
+
20
+ class RouterMethod(BaseModel):
21
+ fn: str
22
+ args: Optional[List]
@@ -0,0 +1,51 @@
1
+ from __future__ import annotations
2
+ import typing
3
+
4
+ from instaui.common.jsonable import Jsonable
5
+ from instaui.vars.path_var import PathVar
6
+
7
+ from instaui.vars.mixin_types.element_binding import ElementBindingMixin
8
+ from instaui.vars.mixin_types.observable import ObservableMixin
9
+ from instaui.vars.mixin_types.str_format_binding import StrFormatBindingMixin
10
+ from instaui.vars.mixin_types.pathable import CanPathPropMixin
11
+
12
+
13
+ class RouterParamsVar(
14
+ Jsonable,
15
+ PathVar,
16
+ ObservableMixin,
17
+ CanPathPropMixin,
18
+ StrFormatBindingMixin,
19
+ ElementBindingMixin,
20
+ ):
21
+ VAR_TYPE = "routePar"
22
+
23
+ def __init__(
24
+ self,
25
+ prop: typing.Literal["params", "path", "fullPath"] = "params",
26
+ ) -> None:
27
+ super().__init__()
28
+ self._prop = prop
29
+
30
+ def __to_binding_config(self):
31
+ return self._to_json_dict()
32
+
33
+ def _to_pathable_binding_config(self) -> typing.Dict:
34
+ return self.__to_binding_config()
35
+
36
+ def _to_path_prop_binding_config(self) -> typing.Dict:
37
+ return self.__to_binding_config()
38
+
39
+ def _to_observable_config(self):
40
+ return self.__to_binding_config()
41
+
42
+ def _to_element_binding_config(self):
43
+ return self.__to_binding_config()
44
+
45
+ def _to_json_dict(self):
46
+ data = super()._to_json_dict()
47
+ data["type"] = self.VAR_TYPE
48
+
49
+ if self._prop != "params":
50
+ data["prop"] = self._prop
51
+ return data
@@ -0,0 +1,4 @@
1
+ import typing
2
+
3
+
4
+ TRouterHistoryMode = typing.Literal["web", "memory", "hash"]
@@ -0,0 +1,60 @@
1
+ {% macro render_fn_arg(main_fn_name) -%}
2
+ {% if main_fn_name -%}
3
+ component_fn={{main_fn_name}},
4
+ {% endif -%}
5
+ {% endmacro -%}
6
+
7
+ {% macro render_children_arg(children) -%}
8
+ {% if children -%}
9
+ children=[
10
+ {{render_routes(children)}}],
11
+ {% endif -%}
12
+ {% endmacro -%}
13
+
14
+
15
+ {% macro render_routes(routes) -%}
16
+
17
+ {% if not routes -%}
18
+
19
+ {% else -%}
20
+ {% for route in routes -%}
21
+ router.RouteItem.create(
22
+ path="{{route.path}}",
23
+ name="{{route.name}}",
24
+ meta = {{route.meta}},
25
+ {{ render_fn_arg(route.main_fn_name()) |trim}}
26
+ {{ render_children_arg(route.children) |trim}}
27
+ ),
28
+ {% endfor -%}
29
+
30
+ {% endif -%}
31
+
32
+ {% endmacro -%}
33
+
34
+ """
35
+ This file is automatically generated by InstaUI. Do not modify it manually.
36
+
37
+ update: {{model.update_time}}
38
+ """
39
+
40
+ import typing
41
+ from instaui import spa_router as router
42
+
43
+ TNames = typing.Literal{{model.route_names}}
44
+
45
+ def link(text: str, name: TNames, params: typing.Optional[typing.Dict] = None):
46
+ return router.link.by_name(text, name=name, params=params)
47
+
48
+ def get_routes():
49
+ try:
50
+ {% for code in model.get_all_main_import() -%}
51
+ {{code}}
52
+ {% endfor -%}
53
+
54
+ return [
55
+ {{render_routes(model.routes)}}
56
+ ]
57
+ except Exception as e:
58
+ print(f"router error: {e}")
59
+
60
+