instaui 0.1.0__py3-none-any.whl → 0.1.2__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 (38) hide show
  1. instaui/components/component.py +1 -1
  2. instaui/components/element.py +37 -17
  3. instaui/components/html/checkbox.py +0 -7
  4. instaui/components/slot.py +2 -3
  5. instaui/components/vfor.py +1 -1
  6. instaui/dependencies/component_dependency.py +16 -0
  7. instaui/dependencies/plugin_dependency.py +28 -0
  8. instaui/fastapi_server/debug_mode_router.py +0 -1
  9. instaui/fastapi_server/dependency_router.py +21 -0
  10. instaui/fastapi_server/resource.py +35 -0
  11. instaui/fastapi_server/server.py +124 -100
  12. instaui/html_tools.py +12 -106
  13. instaui/page_info.py +1 -11
  14. instaui/runtime/_app.py +9 -20
  15. instaui/runtime/resource.py +11 -46
  16. instaui/spa_router/_file_base_utils.py +47 -42
  17. instaui/spa_router/_route_model.py +3 -25
  18. instaui/spa_router/templates/page_routes +1 -0
  19. instaui/static/insta-ui.esm-browser.prod.js +3663 -3663
  20. instaui/static/insta-ui.iife.js +29 -29
  21. instaui/static/templates/web.html +32 -76
  22. instaui/static/templates/zero.html +48 -32
  23. instaui/systems/file_system.py +0 -9
  24. instaui/template/web_template.py +40 -46
  25. instaui/template/zero_template.py +100 -15
  26. instaui/ui/__init__.py +2 -5
  27. instaui/ui_functions/ui_page.py +3 -18
  28. instaui/vars/path_var.py +2 -1
  29. instaui/zero/func.py +111 -0
  30. instaui/zero/scope.py +12 -1
  31. {instaui-0.1.0.dist-info → instaui-0.1.2.dist-info}/METADATA +1 -1
  32. {instaui-0.1.0.dist-info → instaui-0.1.2.dist-info}/RECORD +34 -33
  33. instaui/dependencies/__init__.py +0 -15
  34. instaui/dependencies/component_registrar.py +0 -82
  35. instaui/dependencies/installer.py +0 -5
  36. instaui/fastapi_server/config_router.py +0 -60
  37. {instaui-0.1.0.dist-info → instaui-0.1.2.dist-info}/LICENSE +0 -0
  38. {instaui-0.1.0.dist-info → instaui-0.1.2.dist-info}/WHEEL +0 -0
@@ -1,55 +1,71 @@
1
+ {% from 'debug/sse.html' import sse_style, sse_script,sse_html -%}
2
+
3
+
1
4
  <!doctype html>
2
5
  <html lang="en">
3
6
 
4
7
  <head>
5
8
  <meta charset="UTF-8" />
6
9
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <link href="{{ favicon_url }}" rel="shortcut icon" />
8
- <title>{{ title }}</title>
10
+ <link href="{{ model.favicon_url }}" rel="shortcut icon" />
11
+ <title>{{ model.title }}</title>
9
12
 
10
- {% for link in css_links %}
11
- <link rel="stylesheet" href="{{ prefix | safe }}{{link}}" />
13
+ {%- for link in model.css_links %}
14
+ <link rel="stylesheet" href="{{ model.prefix | safe }}{{link}}" />
12
15
  {% endfor -%}
13
16
 
14
- {% for content in style_tags %}
17
+ {%- for content in model.style_tags %}
15
18
  <style>{{content}}</style>
16
19
  {% endfor -%}
20
+
17
21
  </head>
18
22
 
19
23
  <body>
24
+
20
25
  <div id="app">
21
- <insta-ui :config="appConfig"></insta-ui>
26
+ <insta-ui :config="config"></insta-ui>
22
27
  </div>
23
28
 
24
- {% for info in js_links %}
25
- <script src="{{ prefix | safe }}{{info.link}}" {{info.create_attrs_str()}}></script>
26
- {% endfor -%}
29
+ <script type="importmap">
30
+ {
31
+ "imports":{{model.import_maps_string | safe}}
32
+ }
33
+ </script>
27
34
 
28
- {% for content in script_tags %}
35
+ {%- for info in model.js_links -%}
36
+ <script src="{{ model.prefix | safe }}{{info.link}}"></script>
37
+ {%- endfor -%}
38
+
39
+ {%- for content in model.script_tags -%}
29
40
  <script>{{content}}</script>
30
- {% endfor -%}
41
+ {%- endfor -%}
42
+
43
+
44
+ <script type="module">
45
+ import { createApp } from 'vue'
46
+ import installInsta from 'instaui'
47
+ const True = true;
48
+ const False = false;
49
+ const None = undefined;
50
+
51
+ const appConfig = {{model.config_dict | safe}};
52
+
53
+ const app = createApp({
54
+ setup() {
55
+ return {
56
+ config:appConfig
57
+ }
58
+ }
59
+ })
31
60
 
32
- <script>
33
- const True = true;
34
- const False = false;
35
- const None = undefined;
36
- const { createApp } = Vue
37
- const insta = InstaUI
38
- const appConfig = {{appConfig}}
39
-
40
- const app = createApp({
41
- setup() {
42
- return {
43
- appConfig
44
- }
45
- }
46
- })
47
-
48
- insta(app,appConfig)
49
- {% for info in vue_app_use %}
50
- app.use({{info.name}})
51
- {% endfor %}
52
- app.mount('#app')
61
+ installInsta(app, appConfig)
62
+ {% for info in model.vue_app_component -%}
63
+ app.component('{{info.name | safe}}',(await import('{{info.url | safe }}')).default)
64
+ {% endfor -%}
65
+ {% for plugin_url in model.vue_app_use -%}
66
+ app.use(await import('{{plugin_url | safe }}'))
67
+ {% endfor -%}
68
+ app.mount('#app')
53
69
  </script>
54
70
  </body>
55
71
  </html>
@@ -1,17 +1,8 @@
1
1
  import uuid
2
2
  from pathlib import Path
3
3
 
4
- from urllib.parse import quote as urllib_quote
5
-
6
4
 
7
5
  def generate_hash_name_from_path(file_path: Path):
8
6
  path_str = str(file_path)
9
7
  unique_id = uuid.uuid5(uuid.NAMESPACE_URL, path_str)
10
8
  return str(unique_id)
11
-
12
-
13
- def generate_static_url_from_file_path(file_path: Path):
14
- file_path = Path(file_path).resolve()
15
- return urllib_quote(
16
- f"/_instaui/static/{generate_hash_name_from_path(file_path)}/{file_path.name}"
17
- )
@@ -1,55 +1,49 @@
1
1
  from __future__ import annotations
2
- from typing import Any, Dict, Optional
2
+ import typing
3
+ from dataclasses import dataclass, field
3
4
  from .env import env
4
5
  from instaui.common.jsonable import dumps
5
- from instaui.runtime import get_app_slot
6
6
  from instaui.runtime.context import get_context
7
+ from instaui.runtime.dataclass import JsLink, VueAppComponent
8
+
7
9
 
8
10
  _html_template = env.get_template("web.html")
9
11
 
10
12
 
11
- def render_web_html(
12
- *,
13
- config_json: Optional[str] = None,
14
- config_fetch_url: Optional[str] = None,
15
- config_fetch_key: Optional[str] = None,
16
- query_path_params: Optional[str] = None,
17
- query_params: Optional[Dict] = None,
18
- query_path: Optional[str] = None,
19
- page_loadding_json: Optional[str] = None,
20
- prefix: Any = "",
21
- ) -> str:
22
- if config_json is None and config_fetch_url is None:
23
- raise ValueError("Either config_json or config_fetch_url must be provided")
24
-
25
- is_remote_config = config_fetch_url is not None
26
- has_preload = page_loadding_json is not None
27
-
28
- system_slot = get_app_slot()
29
- resources = system_slot._html_resource
30
-
31
- context = get_context()
32
-
33
- return _html_template.render(
34
- {
35
- "is_debug": context.debug_mode,
36
- "css_links": list(resources._css_links.keys()),
37
- "style_tags": resources._style_tags,
38
- "js_links": resources._js_links,
39
- "script_tags": resources._script_tags,
40
- "vue_app_use": list(resources._vue_app_use),
41
- "vue_app_component": list(resources._vue_app_components),
42
- "import_maps": dumps(resources._import_maps),
43
- "config_fetch_url": config_fetch_url,
44
- "config_fetch_key": config_fetch_key,
45
- "query_path": query_path,
46
- "query_path_params": query_path_params,
47
- "query_params": query_params,
48
- "config_json": config_json,
49
- "is_remote_config": is_remote_config,
50
- "has_preload": has_preload,
51
- "preload_json": page_loadding_json,
52
- "title": resources.title,
53
- "prefix": prefix,
13
+ @dataclass
14
+ class WebTemplateModel:
15
+ vue_js_link: str
16
+ instaui_js_link: str
17
+ config_dict: typing.Dict[str, typing.Any] = field(default_factory=dict)
18
+ extra_import_maps: typing.Dict[str, str] = field(default_factory=dict)
19
+ css_links: typing.List[str] = field(default_factory=list)
20
+ style_tags: typing.List[str] = field(default_factory=list)
21
+ js_links: typing.List[JsLink] = field(default_factory=list)
22
+ script_tags: typing.List[str] = field(default_factory=list)
23
+ vue_app_use: typing.List[str] = field(default_factory=list)
24
+ vue_app_component: typing.List[VueAppComponent] = field(default_factory=list)
25
+ prefix: str = ""
26
+ title: str = ""
27
+ favicon_url: str = ""
28
+
29
+ def add_extra_import_map(self, name: str, url: str):
30
+ self.extra_import_maps[name] = url
31
+
32
+ @property
33
+ def import_maps_string(self):
34
+ data = {
35
+ **self.extra_import_maps,
36
+ "vue": self.vue_js_link,
37
+ "instaui": self.instaui_js_link,
54
38
  }
55
- )
39
+
40
+ return dumps(data)
41
+
42
+ @property
43
+ def is_debug(self):
44
+ context = get_context()
45
+ return context.debug_mode
46
+
47
+
48
+ def render_web_html(model: WebTemplateModel):
49
+ return _html_template.render(model=model)
@@ -1,24 +1,109 @@
1
1
  from __future__ import annotations
2
- from typing import Any
2
+ import typing
3
+ from pathlib import Path
4
+ from urllib.parse import quote
5
+ import base64
6
+ from dataclasses import dataclass, field
3
7
  from .env import env
4
- from instaui.runtime import get_app_slot
8
+ from instaui.common.jsonable import dumps
9
+ from instaui.runtime.dataclass import JsLink
5
10
 
11
+ _TCodeOrPath = typing.Union[str, Path]
12
+ _JS_PREFIX = "data:text/javascript;charset=utf-8"
13
+ _CSS_PREFIX = "data:text/css;charset=utf-8"
14
+ _ICON_PREFIX = "data:image/x-icon;base64"
6
15
 
7
16
  _html_template = env.get_template("zero.html")
8
17
 
9
18
 
10
- def render_zero_html(config_json: str = "{}", prefix: Any = "") -> str:
11
- resources = get_app_slot()._html_resource
19
+ @dataclass(frozen=True)
20
+ class ZeroVueAppComponent:
21
+ name: str
22
+ url: _TCodeOrPath
12
23
 
13
- return _html_template.render(
14
- {
15
- "css_links": list(resources._css_links.keys()),
16
- "style_tags": resources._style_tags,
17
- "js_links": resources._js_links,
18
- "script_tags": resources._script_tags,
19
- "vue_app_use": list(resources._vue_app_use),
20
- "appConfig": config_json,
21
- "title": resources.title,
22
- "prefix": prefix,
24
+
25
+ @dataclass
26
+ class ZeroTemplateModel:
27
+ vue_js_code: _TCodeOrPath
28
+ instaui_js_code: _TCodeOrPath
29
+ config_dict: typing.Dict[str, typing.Any] = field(default_factory=dict)
30
+ extra_import_maps: typing.Dict[str, _TCodeOrPath] = field(default_factory=dict)
31
+ css_links: typing.List[_TCodeOrPath] = field(default_factory=list)
32
+ style_tags: typing.List[str] = field(default_factory=list)
33
+ js_links: typing.List[JsLink] = field(default_factory=list)
34
+ script_tags: typing.List[str] = field(default_factory=list)
35
+ vue_app_use: typing.List[str] = field(default_factory=list)
36
+ vue_app_component: typing.List[ZeroVueAppComponent] = field(default_factory=list)
37
+ prefix: str = ""
38
+ title: str = ""
39
+ favicon_url: typing.Optional[_TCodeOrPath] = None
40
+
41
+ def add_extra_import_map(self, name: str, url: _TCodeOrPath):
42
+ self.extra_import_maps[name] = url
43
+
44
+ @property
45
+ def import_maps_string(self):
46
+ data = {
47
+ **self.extra_import_maps,
48
+ "vue": self.vue_js_code,
49
+ "instaui": self.instaui_js_code,
50
+ }
51
+
52
+ return dumps(data)
53
+
54
+ def normalize_path_with_self(self):
55
+ self.vue_js_code = self._normalize_path_to_dataurl(self.vue_js_code, _JS_PREFIX)
56
+ self.instaui_js_code = self._normalize_path_to_dataurl(
57
+ self.instaui_js_code, _JS_PREFIX
58
+ )
59
+
60
+ self.css_links = [
61
+ self._normalize_path_to_dataurl(link, _CSS_PREFIX)
62
+ for link in self.css_links
63
+ ]
64
+
65
+ self.js_links = [
66
+ JsLink(
67
+ link=self._normalize_path_to_dataurl(link.link, _JS_PREFIX),
68
+ attrs=link.attrs,
69
+ )
70
+ for link in self.js_links
71
+ ]
72
+
73
+ self.extra_import_maps = {
74
+ k: self._normalize_path_to_dataurl(v, _JS_PREFIX)
75
+ for k, v in self.extra_import_maps.items()
23
76
  }
24
- )
77
+
78
+ self.vue_app_component = [
79
+ ZeroVueAppComponent(
80
+ name=component.name,
81
+ url=self._normalize_path_to_dataurl(component.url, _JS_PREFIX),
82
+ )
83
+ for component in self.vue_app_component
84
+ ]
85
+
86
+ self.favicon_url = (
87
+ self._normalize_path_to_base64_url(self.favicon_url, _ICON_PREFIX)
88
+ if self.favicon_url is not None
89
+ else None
90
+ )
91
+
92
+ @staticmethod
93
+ def _normalize_path_to_dataurl(path: typing.Union[str, Path], prefix: str):
94
+ if isinstance(path, Path):
95
+ path = path.read_text(encoding="utf-8")
96
+
97
+ return f"{prefix},{quote(path)}"
98
+
99
+ @staticmethod
100
+ def _normalize_path_to_base64_url(path: typing.Union[str, Path], prefix: str):
101
+ if isinstance(path, Path):
102
+ path = path.read_text(encoding="utf-8")
103
+
104
+ return f"{prefix},{base64.b64encode(path.encode('utf-8')).decode('utf-8')}"
105
+
106
+
107
+ def render_zero_html(model: ZeroTemplateModel) -> str:
108
+ model.normalize_path_with_self()
109
+ return _html_template.render(model=model)
instaui/ui/__init__.py CHANGED
@@ -12,16 +12,15 @@ from instaui.vars.vfor_item import TVForItem, TVForIndex
12
12
  from instaui.vars.element_ref import ElementRef as element_ref, run_element_method
13
13
  from instaui.html_tools import (
14
14
  to_json,
15
- to_html,
16
15
  add_css_link,
17
16
  add_js_link,
18
17
  add_style,
19
18
  add_js_code,
20
19
  add_vue_app_use,
21
20
  to_config_data,
21
+ use_tailwind,
22
22
  )
23
23
 
24
-
25
24
  from instaui.components.element import Element as element
26
25
  from instaui.components.row import Row as row
27
26
  from instaui.components.column import Column as column
@@ -31,7 +30,6 @@ from instaui.components.vfor import VFor as vfor
31
30
  from instaui.components.vif import VIf as vif
32
31
  from instaui.components.match import Match as match
33
32
  from instaui.components.content import Content as content
34
- from instaui.dependencies import install_component
35
33
 
36
34
  from instaui.event.web_event import ui_event as event, WebEvent as TEventFn
37
35
  from instaui.event.js_event import js_event
@@ -83,7 +81,6 @@ __all__ = [
83
81
  "element_ref",
84
82
  "run_element_method",
85
83
  "to_json",
86
- "to_html",
87
84
  "to_config_data",
88
85
  "element",
89
86
  "vfor",
@@ -93,7 +90,7 @@ __all__ = [
93
90
  "add_js_code",
94
91
  "add_js_link",
95
92
  "add_vue_app_use",
96
- "install_component",
93
+ "use_tailwind",
97
94
  "directive",
98
95
  "vif",
99
96
  "page",
@@ -1,31 +1,16 @@
1
- import inspect
2
- from typing import Callable, Optional
1
+ from typing import Callable
3
2
  from instaui.launch_collector import get_launch_collector, PageInfo
4
3
 
5
4
 
6
- def page(
7
- url: str,
8
- *,
9
- page_loading: Optional[Callable] = None,
10
- use_tailwind: Optional[bool] = None,
11
- ):
5
+ def page(url: str):
12
6
  """Register a page route.
13
7
 
14
8
  Args:
15
9
  url (str): The route URL.
16
- page_loading (Optional[Callable], optional): Function to display a preload interface for the page. Defaults to None.
17
- use_tailwind (Optional[bool], optional): Whether to use tailwind or not. Defaults to None(not use tailwind).
18
-
19
- Raises:
20
- ValueError: if page_loading_fn is a coroutine function
21
10
  """
22
- if page_loading is not None and inspect.iscoroutinefunction(page_loading):
23
- raise ValueError("page_loading_fn must be a synchronous function")
24
11
 
25
12
  def wrapper(func: Callable):
26
- get_launch_collector().register_page(
27
- PageInfo(url, func, page_loading, use_tailwind=use_tailwind)
28
- )
13
+ get_launch_collector().register_page(PageInfo(url, func))
29
14
  return func
30
15
 
31
16
  return wrapper
instaui/vars/path_var.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
  from abc import abstractmethod
3
- from typing import List, Optional, Union, Self
3
+ from typing import List, Optional, Union
4
+ from typing_extensions import Self
4
5
  from instaui.vars.mixin_types.pathable import PathableMixin, CanPathPropMixin
5
6
  from instaui.vars.mixin_types.element_binding import ElementBindingMixin
6
7
  from instaui.vars.mixin_types.py_binding import CanInputMixin, CanOutputMixin
instaui/zero/func.py ADDED
@@ -0,0 +1,111 @@
1
+ from __future__ import annotations
2
+ import itertools
3
+ from pathlib import Path
4
+ from typing import Union
5
+ import instaui.consts as consts
6
+ from instaui.runtime._app import get_app_slot, get_default_app_slot
7
+ from instaui.template import render_zero_html
8
+ from instaui.template import zero_template
9
+ from instaui.html_tools import to_config_data
10
+ from instaui.runtime.dataclass import JsLink
11
+
12
+
13
+ def to_html(file: Union[str, Path]):
14
+ if isinstance(file, str):
15
+ import inspect
16
+
17
+ frame = inspect.currentframe().f_back # type: ignore
18
+ assert frame is not None
19
+ script_file = inspect.getfile(frame)
20
+ file = Path(script_file).parent.joinpath(file)
21
+
22
+ file = Path(file)
23
+
24
+ raw = to_html_str()
25
+ file.write_text(raw, "utf8")
26
+
27
+ return file.resolve().absolute()
28
+
29
+
30
+ def to_html_str():
31
+ system_slot = get_app_slot()
32
+ default_app_slot = get_default_app_slot()
33
+ html_resource = system_slot._html_resource
34
+ default_html_resource = default_app_slot._html_resource
35
+
36
+ config_data = to_config_data()
37
+
38
+ model = zero_template.ZeroTemplateModel(
39
+ vue_js_code=consts.VUE_ES_JS_PATH,
40
+ instaui_js_code=consts.APP_ES_JS_PATH,
41
+ css_links=[
42
+ consts.APP_CSS_PATH,
43
+ ],
44
+ config_dict=config_data,
45
+ )
46
+
47
+ if html_resource.use_tailwind is None:
48
+ if default_html_resource.use_tailwind:
49
+ model.js_links.append(JsLink(consts.TAILWIND_JS_PATH))
50
+ else:
51
+ if html_resource.use_tailwind:
52
+ model.js_links.append(JsLink(consts.TAILWIND_JS_PATH))
53
+
54
+ # register custom components
55
+ for component in system_slot._component_dependencies:
56
+ if not component.esm:
57
+ continue
58
+
59
+ model.vue_app_component.append(
60
+ zero_template.ZeroVueAppComponent(
61
+ name=component.tag_name,
62
+ url=component.esm,
63
+ )
64
+ )
65
+
66
+ if component.css:
67
+ for css_link in component.css:
68
+ model.css_links.append(css_link)
69
+
70
+ # register custom plugins
71
+ for plugin in set(
72
+ itertools.chain(
73
+ system_slot._plugin_dependencies, default_app_slot._plugin_dependencies
74
+ )
75
+ ):
76
+ if not plugin.esm:
77
+ continue
78
+
79
+ model.vue_app_use.append(plugin.name)
80
+
81
+ model.add_extra_import_map(plugin.name, plugin.esm)
82
+
83
+ if plugin.css:
84
+ for css_link in plugin.css:
85
+ model.css_links.append(css_link)
86
+
87
+ # css file link to web static link
88
+ for link, attrs in itertools.chain(
89
+ html_resource._css_links.items(), default_html_resource._css_links.items()
90
+ ):
91
+ if isinstance(link, Path):
92
+ model.css_links.append(link)
93
+
94
+ # js file link to web static link
95
+ for info in itertools.chain(
96
+ html_resource._js_links, default_html_resource._js_links
97
+ ):
98
+ if isinstance(info.link, Path):
99
+ model.js_links.append(JsLink(info.link))
100
+
101
+ for js_code in itertools.chain(
102
+ html_resource._script_tags, default_html_resource._script_tags
103
+ ):
104
+ model.script_tags.append(js_code)
105
+
106
+ for sylte_code in itertools.chain(
107
+ html_resource._style_tags, default_html_resource._style_tags
108
+ ):
109
+ model.style_tags.append(sylte_code)
110
+
111
+ return render_zero_html(model)
instaui/zero/scope.py CHANGED
@@ -1,9 +1,20 @@
1
+ from pathlib import Path
2
+ from typing import Union
1
3
  from instaui.runtime import new_app_slot, reset_app_slot
2
4
  from contextlib import contextmanager
5
+ from .func import to_html, to_html_str
3
6
 
4
7
 
5
8
  @contextmanager
6
9
  def scope():
7
10
  token = new_app_slot("zero")
8
- yield
11
+ yield Wrapper()
9
12
  reset_app_slot(token)
13
+
14
+
15
+ class Wrapper:
16
+ def to_html(self, file: Union[str, Path]):
17
+ return to_html(file)
18
+
19
+ def to_html_str(self):
20
+ return to_html_str()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: instaui
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: insta-ui is a Python-based UI library for rapidly building user interfaces.
5
5
  License: MIT
6
6
  Author: CrystalWindSnake