jac-client 0.2.6__py3-none-any.whl → 0.2.11__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.
- jac_client/examples/all-in-one/{src/button.jac → button.jac} +4 -3
- jac_client/examples/all-in-one/components/CategoryFilter.jac +47 -0
- jac_client/examples/all-in-one/components/Header.jac +17 -0
- jac_client/examples/all-in-one/components/ProfitOverview.jac +64 -0
- jac_client/examples/all-in-one/components/Summary.jac +76 -0
- jac_client/examples/all-in-one/components/TransactionForm.jac +188 -0
- jac_client/examples/all-in-one/components/TransactionItem.jac +62 -0
- jac_client/examples/all-in-one/components/TransactionList.jac +44 -0
- jac_client/examples/all-in-one/components/button.jac +8 -0
- jac_client/examples/all-in-one/components/navigation.jac +126 -0
- jac_client/examples/all-in-one/constants/categories.jac +36 -0
- jac_client/examples/all-in-one/constants/clients.jac +12 -0
- jac_client/examples/all-in-one/context/BudgetContext.jac +31 -0
- jac_client/examples/all-in-one/hooks/useBudget.jac +122 -0
- jac_client/examples/all-in-one/hooks/useLocalStorage.jac +37 -0
- jac_client/examples/all-in-one/main.jac +542 -0
- jac_client/examples/all-in-one/pages/BudgetPlanner.jac +140 -0
- jac_client/examples/all-in-one/pages/FeaturesTest.jac +157 -0
- jac_client/examples/all-in-one/pages/LandingPage.jac +124 -0
- jac_client/examples/all-in-one/pages/budget_planner_ui.cl.jac +65 -0
- jac_client/examples/all-in-one/pages/features_test_ui.cl.jac +675 -0
- jac_client/examples/all-in-one/pages/loginPage.jac +127 -0
- jac_client/examples/all-in-one/pages/nestedDemo.jac +54 -0
- jac_client/examples/all-in-one/pages/notFound.jac +18 -0
- jac_client/examples/all-in-one/pages/signupPage.jac +127 -0
- jac_client/examples/all-in-one/utils/formatters.jac +49 -0
- jac_client/examples/asset-serving/css-with-image/main.jac +92 -0
- jac_client/examples/asset-serving/image-asset/main.jac +56 -0
- jac_client/examples/asset-serving/import-alias/main.jac +109 -0
- jac_client/examples/basic/main.jac +23 -0
- jac_client/examples/basic-auth/main.jac +363 -0
- jac_client/examples/basic-auth-with-router/main.jac +451 -0
- jac_client/examples/basic-full-stack/main.jac +362 -0
- jac_client/examples/css-styling/js-styling/main.jac +63 -0
- jac_client/examples/css-styling/material-ui/main.jac +122 -0
- jac_client/examples/css-styling/pure-css/main.jac +55 -0
- jac_client/examples/css-styling/sass-example/main.jac +55 -0
- jac_client/examples/css-styling/styled-components/main.jac +62 -0
- jac_client/examples/css-styling/tailwind-example/main.jac +74 -0
- jac_client/examples/full-stack-with-auth/main.jac +696 -0
- jac_client/examples/little-x/main.jac +681 -0
- jac_client/examples/little-x/src/submit-button.jac +15 -14
- jac_client/examples/nested-folders/nested-advance/main.jac +26 -0
- jac_client/examples/nested-folders/nested-advance/src/ButtonRoot.jac +4 -6
- jac_client/examples/nested-folders/nested-advance/src/level1/ButtonSecondL.jac +9 -13
- jac_client/examples/nested-folders/nested-advance/src/level1/Card.jac +29 -32
- jac_client/examples/nested-folders/nested-advance/src/level1/level2/ButtonThirdL.jac +12 -18
- jac_client/examples/nested-folders/nested-basic/{src/app.jac → main.jac} +7 -5
- jac_client/examples/nested-folders/nested-basic/src/button.jac +4 -3
- jac_client/examples/nested-folders/nested-basic/src/components/button.jac +4 -3
- jac_client/examples/ts-support/main.jac +35 -0
- jac_client/examples/with-router/main.jac +286 -0
- jac_client/plugin/cli.jac +507 -470
- jac_client/plugin/client.jac +30 -12
- jac_client/plugin/client_runtime.cl.jac +25 -15
- jac_client/plugin/impl/client.impl.jac +126 -26
- jac_client/plugin/impl/client_runtime.impl.jac +182 -10
- jac_client/plugin/plugin_config.jac +216 -34
- jac_client/plugin/src/__init__.jac +0 -2
- jac_client/plugin/src/compiler.jac +2 -2
- jac_client/plugin/src/config_loader.jac +1 -0
- jac_client/plugin/src/desktop_config.jac +31 -0
- jac_client/plugin/src/impl/compiler.impl.jac +99 -30
- jac_client/plugin/src/impl/config_loader.impl.jac +8 -0
- jac_client/plugin/src/impl/desktop_config.impl.jac +191 -0
- jac_client/plugin/src/impl/jac_to_js.impl.jac +5 -1
- jac_client/plugin/src/impl/package_installer.impl.jac +20 -20
- jac_client/plugin/src/impl/vite_bundler.impl.jac +384 -144
- jac_client/plugin/src/package_installer.jac +1 -1
- jac_client/plugin/src/targets/desktop/sidecar/main.py +144 -0
- jac_client/plugin/src/targets/desktop_target.jac +37 -0
- jac_client/plugin/src/targets/impl/desktop_target.impl.jac +2347 -0
- jac_client/plugin/src/targets/impl/registry.impl.jac +64 -0
- jac_client/plugin/src/targets/impl/web_target.impl.jac +157 -0
- jac_client/plugin/src/targets/register.jac +21 -0
- jac_client/plugin/src/targets/registry.jac +87 -0
- jac_client/plugin/src/targets/web_target.jac +35 -0
- jac_client/plugin/src/vite_bundler.jac +15 -1
- jac_client/plugin/utils/__init__.jac +3 -0
- jac_client/plugin/utils/bun_installer.jac +16 -0
- jac_client/plugin/utils/impl/bun_installer.impl.jac +99 -0
- jac_client/templates/client.jacpack +72 -0
- jac_client/templates/fullstack.jacpack +61 -0
- jac_client/tests/conftest.py +110 -52
- jac_client/tests/fixtures/spawn_test/app.jac +64 -70
- jac_client/tests/fixtures/with-ts/app.jac +28 -28
- jac_client/tests/test_cli.py +280 -113
- jac_client/tests/test_e2e.py +232 -0
- jac_client/tests/test_helpers.py +58 -0
- jac_client/tests/test_it.py +325 -154
- jac_client/tests/test_it_desktop.py +891 -0
- {jac_client-0.2.6.dist-info → jac_client-0.2.11.dist-info}/METADATA +20 -11
- jac_client-0.2.11.dist-info/RECORD +113 -0
- {jac_client-0.2.6.dist-info → jac_client-0.2.11.dist-info}/WHEEL +1 -1
- jac_client/examples/all-in-one/src/app.jac +0 -841
- jac_client/examples/all-in-one/src/components/button.jac +0 -7
- jac_client/examples/asset-serving/css-with-image/src/app.jac +0 -88
- jac_client/examples/asset-serving/image-asset/src/app.jac +0 -55
- jac_client/examples/asset-serving/import-alias/src/app.jac +0 -111
- jac_client/examples/basic/src/app.jac +0 -21
- jac_client/examples/basic-auth/src/app.jac +0 -377
- jac_client/examples/basic-auth-with-router/src/app.jac +0 -464
- jac_client/examples/basic-full-stack/src/app.jac +0 -365
- jac_client/examples/css-styling/js-styling/src/app.jac +0 -84
- jac_client/examples/css-styling/material-ui/src/app.jac +0 -122
- jac_client/examples/css-styling/pure-css/src/app.jac +0 -64
- jac_client/examples/css-styling/sass-example/src/app.jac +0 -64
- jac_client/examples/css-styling/styled-components/src/app.jac +0 -71
- jac_client/examples/css-styling/tailwind-example/src/app.jac +0 -63
- jac_client/examples/full-stack-with-auth/src/app.jac +0 -722
- jac_client/examples/little-x/src/app.jac +0 -719
- jac_client/examples/nested-folders/nested-advance/src/app.jac +0 -35
- jac_client/examples/ts-support/src/app.jac +0 -35
- jac_client/examples/with-router/src/app.jac +0 -323
- jac_client/plugin/src/babel_processor.jac +0 -18
- jac_client/plugin/src/impl/babel_processor.impl.jac +0 -84
- jac_client-0.2.6.dist-info/RECORD +0 -74
- {jac_client-0.2.6.dist-info → jac_client-0.2.11.dist-info}/entry_points.txt +0 -0
- {jac_client-0.2.6.dist-info → jac_client-0.2.11.dist-info}/top_level.txt +0 -0
jac_client/plugin/client.jac
CHANGED
|
@@ -18,14 +18,29 @@ glob JsonValue: TypeAlias = None | str | int | float | bool | list['JsonValue']
|
|
|
18
18
|
],
|
|
19
19
|
StatusCode: TypeAlias = Literal[(200, 201, 400, 401, 404, 503)];
|
|
20
20
|
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
self: JacClientModuleIntrospector,
|
|
21
|
+
"""Builds HTML head content from meta data."""
|
|
22
|
+
obj HeaderBuilder {
|
|
23
|
+
has meta_data: dict,
|
|
25
24
|
function_name: str,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
DEFAULTS: dict = {
|
|
26
|
+
"charset": "UTF-8",
|
|
27
|
+
"viewport": "width=device-width,initial-scale=1"
|
|
28
|
+
},
|
|
29
|
+
standard_tags: list = [
|
|
30
|
+
"description",
|
|
31
|
+
"keywords",
|
|
32
|
+
"author",
|
|
33
|
+
"robots",
|
|
34
|
+
"theme_color"
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
def build_head -> str;
|
|
38
|
+
def _get_value(key: str) -> str;
|
|
39
|
+
def _build_special_tags -> list[str];
|
|
40
|
+
def _build_meta_tags -> list[str];
|
|
41
|
+
def _build_og_tags -> list[str];
|
|
42
|
+
def _build_link_tags -> list[str];
|
|
43
|
+
def _build_generic_meta_tags -> list[str];
|
|
29
44
|
}
|
|
30
45
|
|
|
31
46
|
"""Jac Client."""
|
|
@@ -38,15 +53,18 @@ class JacClient {
|
|
|
38
53
|
module: types.ModuleType, force: bool = False
|
|
39
54
|
) -> ClientBundle;
|
|
40
55
|
|
|
41
|
-
@hookimpl
|
|
42
|
-
static def get_module_introspector(
|
|
43
|
-
module_name: str, base_path: (str | None)
|
|
44
|
-
) -> ModuleIntrospector;
|
|
45
|
-
|
|
46
56
|
@hookimpl
|
|
47
57
|
static def send_static_file(
|
|
48
58
|
handler: BaseHTTPRequestHandler,
|
|
49
59
|
file_path: Path,
|
|
50
60
|
content_type: (str | None) = None
|
|
51
61
|
) -> None;
|
|
62
|
+
|
|
63
|
+
@hookimpl
|
|
64
|
+
static def render_page(
|
|
65
|
+
introspector: ModuleIntrospector,
|
|
66
|
+
function_name: str,
|
|
67
|
+
args: dict[(str, Any)],
|
|
68
|
+
username: str
|
|
69
|
+
) -> dict[str, Any];
|
|
52
70
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Client-side runtime for Jac JSX and walker interactions."""
|
|
2
2
|
|
|
3
3
|
import from 'react' { * as React }
|
|
4
|
+
import from 'react' { useState as reactUseState, useEffect as reactUseEffect }
|
|
4
5
|
import from 'react-dom/client' { * as ReactDOM }
|
|
5
6
|
import from 'react-router-dom' {
|
|
6
7
|
HashRouter as ReactRouterHashRouter,
|
|
@@ -12,27 +13,36 @@ import from 'react-router-dom' {
|
|
|
12
13
|
useLocation as reactRouterUseLocation,
|
|
13
14
|
useParams as reactRouterUseParams
|
|
14
15
|
}
|
|
16
|
+
import from 'react-error-boundary' { ErrorBoundary }
|
|
15
17
|
|
|
16
|
-
def
|
|
18
|
+
def:pub __jacJsx(tag: any, props: dict = {}, children: any = []) -> any;
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
# React hooks re-exported for auto-injection by `has` variables and `can with entry/exit` effects
|
|
21
|
+
glob:
|
|
22
|
+
pub useState = reactUseState,
|
|
23
|
+
useEffect = reactUseEffect,
|
|
24
|
+
Router = ReactRouterHashRouter,
|
|
19
25
|
Routes = ReactRouterRoutes,
|
|
20
26
|
Route = ReactRouterRoute,
|
|
21
27
|
Link = ReactRouterLink,
|
|
22
28
|
Navigate = ReactRouterNavigate,
|
|
23
29
|
useNavigate = reactRouterUseNavigate,
|
|
24
30
|
useLocation = reactRouterUseLocation,
|
|
25
|
-
useParams = reactRouterUseParams
|
|
31
|
+
useParams = reactRouterUseParams,
|
|
32
|
+
JacClientErrorBoundary = ErrorBoundary;
|
|
26
33
|
|
|
27
|
-
def
|
|
28
|
-
def
|
|
29
|
-
async def
|
|
30
|
-
def
|
|
31
|
-
async def
|
|
32
|
-
async def
|
|
33
|
-
async def
|
|
34
|
-
def
|
|
35
|
-
def
|
|
36
|
-
def
|
|
37
|
-
def
|
|
38
|
-
def
|
|
34
|
+
def:pub useRouter -> dict;
|
|
35
|
+
def:pub navigate(path: str) -> None;
|
|
36
|
+
async def:pub __jacSpawn(left: str, right: str = "", fields: dict = {}) -> any;
|
|
37
|
+
def:pub jacSpawn(left: str, right: str = "", fields: dict = {}) -> any;
|
|
38
|
+
async def:pub __jacCallFunction(function_name: str, args: dict = {}) -> any;
|
|
39
|
+
async def:pub jacSignup(username: str, password: str) -> dict;
|
|
40
|
+
async def:pub jacLogin(username: str, password: str) -> bool;
|
|
41
|
+
def:pub jacLogout -> None;
|
|
42
|
+
def:pub jacIsLoggedIn -> bool;
|
|
43
|
+
def:pub __getLocalStorage(key: str) -> str;
|
|
44
|
+
def:pub __setLocalStorage(key: str, value: str) -> None;
|
|
45
|
+
def:pub __removeLocalStorage(key: str) -> None;
|
|
46
|
+
def:pub ErrorFallback(error: str, resetErrorBoundary: any) -> any;
|
|
47
|
+
def:pub errorOverlay(filePath: str, errors: str) -> any;
|
|
48
|
+
# React Router components
|
|
@@ -30,13 +30,6 @@ impl JacClient.send_static_file(
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
"""Get a module introspector for the supplied module."""
|
|
34
|
-
impl JacClient.get_module_introspector(
|
|
35
|
-
module_name: str, base_path: (str | None)
|
|
36
|
-
) -> ModuleIntrospector {
|
|
37
|
-
return JacClientModuleIntrospector(module_name, base_path);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
33
|
"""Build a client bundle for the supplied module."""
|
|
41
34
|
impl JacClient.build_client_bundle(
|
|
42
35
|
module: types.ModuleType, force: bool = False
|
|
@@ -57,12 +50,13 @@ impl JacClient.get_client_bundle_builder -> ViteClientBundleBuilder {
|
|
|
57
50
|
# Fallback to base_path_dir if no project root found
|
|
58
51
|
base_path = base_path_dir;
|
|
59
52
|
}
|
|
60
|
-
#
|
|
61
|
-
|
|
53
|
+
# Use ViteBundler to get the client directory
|
|
54
|
+
bundler = ViteBundler(base_path);
|
|
55
|
+
client_dir = bundler._get_client_dir();
|
|
56
|
+
# package.json should only exist in .jac/client/configs/
|
|
57
|
+
generated_package_json = client_dir / 'configs' / 'package.json';
|
|
62
58
|
# Generate package.json if it doesn't exist
|
|
63
59
|
if not generated_package_json.exists() {
|
|
64
|
-
# Use ViteBundler to generate package.json from config.json
|
|
65
|
-
bundler = ViteBundler(base_path);
|
|
66
60
|
generated_path = bundler.create_package_json();
|
|
67
61
|
# Verify the file was created and resolve to absolute path
|
|
68
62
|
if not generated_path.exists() {
|
|
@@ -80,7 +74,7 @@ impl JacClient.get_client_bundle_builder -> ViteClientBundleBuilder {
|
|
|
80
74
|
f'package.json not found at {package_json_path}. Expected at {generated_package_json}'
|
|
81
75
|
) ;
|
|
82
76
|
}
|
|
83
|
-
output_dir =
|
|
77
|
+
output_dir = client_dir / 'dist';
|
|
84
78
|
runtime_path = Path(__file__).with_name('client_runtime.cl.jac');
|
|
85
79
|
return ViteClientBundleBuilder(
|
|
86
80
|
runtime_path=runtime_path,
|
|
@@ -91,22 +85,22 @@ impl JacClient.get_client_bundle_builder -> ViteClientBundleBuilder {
|
|
|
91
85
|
}
|
|
92
86
|
|
|
93
87
|
"""Render HTML page for client function using the Vite bundle."""
|
|
94
|
-
impl
|
|
95
|
-
|
|
88
|
+
impl JacClient.render_page(
|
|
89
|
+
introspector: ModuleIntrospector,
|
|
96
90
|
function_name: str,
|
|
97
91
|
args: dict[(str, Any)],
|
|
98
92
|
username: str
|
|
99
93
|
) -> dict[str, Any] {
|
|
100
|
-
|
|
94
|
+
introspector.load();
|
|
101
95
|
available_exports = (
|
|
102
|
-
set(
|
|
103
|
-
or set(
|
|
96
|
+
set(introspector._client_manifest.get('exports', []))
|
|
97
|
+
or set(introspector.get_client_functions().keys())
|
|
104
98
|
);
|
|
105
99
|
if (function_name not in available_exports) {
|
|
106
100
|
raise ValueError(f"Client function '{function_name}' not found") ;
|
|
107
101
|
}
|
|
108
|
-
bundle_hash =
|
|
109
|
-
import from jaclang.project.config { find_project_root }
|
|
102
|
+
bundle_hash = introspector.ensure_bundle();
|
|
103
|
+
import from jaclang.project.config { find_project_root, get_config }
|
|
110
104
|
# Find project root by looking for jac.toml (base_path_dir might be src/ for entry files)
|
|
111
105
|
base_path_dir = Path(Jac.base_path_dir);
|
|
112
106
|
project_root_result = find_project_root(base_path_dir);
|
|
@@ -116,19 +110,125 @@ impl JacClientModuleIntrospector.render_page(
|
|
|
116
110
|
# Fallback to base_path_dir if no project root found
|
|
117
111
|
base_path = base_path_dir;
|
|
118
112
|
}
|
|
119
|
-
|
|
113
|
+
# Get client directory from config or use default
|
|
114
|
+
config = get_config();
|
|
115
|
+
if config is not None {
|
|
116
|
+
dist_dir = config.get_client_dir() / 'dist';
|
|
117
|
+
} else {
|
|
118
|
+
dist_dir = base_path / '.jac' / 'client' / 'dist';
|
|
119
|
+
}
|
|
120
120
|
css_link = '';
|
|
121
|
-
css_file = dist_dir / '
|
|
121
|
+
css_file = dist_dir / 'styles.css';
|
|
122
122
|
if css_file.exists() {
|
|
123
123
|
css_hash = hashlib.sha256(css_file.read_bytes()).hexdigest()[:8];
|
|
124
|
-
css_link = f'<link rel="stylesheet" href="/static/
|
|
124
|
+
css_link = f'<link rel="stylesheet" href="/static/styles.css?hash={css_hash}"/>';
|
|
125
125
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
)}
|
|
126
|
+
# Get meta data from config
|
|
127
|
+
client_cfg = config.get_plugin_config("client") if config else None;
|
|
128
|
+
meta_data = client_cfg.get("app_meta_data", {}) if client_cfg else {};
|
|
129
|
+
head_builder = HeaderBuilder(meta_data, function_name);
|
|
130
|
+
head_content = head_builder.build_head();
|
|
129
131
|
if css_link {
|
|
130
132
|
head_content += f"\n {css_link}";
|
|
131
133
|
}
|
|
132
134
|
page = f'<!DOCTYPE html><html lang="en"><head>{head_content}</head><body><div id="root"></div><script src="/static/client.js?hash={bundle_hash}" defer></script></body></html>';
|
|
133
|
-
return {
|
|
135
|
+
return {
|
|
136
|
+
'html': page,
|
|
137
|
+
'bundle_hash': bundle_hash,
|
|
138
|
+
'bundle_code': introspector._bundle.code
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
"""Build complete HTML head."""
|
|
143
|
+
impl HeaderBuilder.build_head -> str {
|
|
144
|
+
lines: list = [];
|
|
145
|
+
lines.extend(self._build_special_tags()); # charset, viewport, title
|
|
146
|
+
lines.extend(self._build_meta_tags()); # description, robots, etc
|
|
147
|
+
lines.extend(self._build_og_tags()); # Open Graph
|
|
148
|
+
lines.extend(self._build_link_tags()); # canonical, icon, etc
|
|
149
|
+
lines.extend(self._build_generic_meta_tags()); # custom meta_* tags
|
|
150
|
+
return "\n ".join(lines);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
"""Build generic meta tags (meta_* prefix for any custom metadata)."""
|
|
154
|
+
impl HeaderBuilder._build_generic_meta_tags -> list[str] {
|
|
155
|
+
lines: list = [];
|
|
156
|
+
for (key, val) in self.meta_data.items() {
|
|
157
|
+
if key.startswith("meta_") and val {
|
|
158
|
+
# meta_custom_field → name="custom-field"
|
|
159
|
+
name = key[5:].replace("_", "-");
|
|
160
|
+
lines.append(f'<meta name={name} content={html.escape(str(val))} />');
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return lines;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
"""Build link tags (link_* prefix or special cases)."""
|
|
167
|
+
impl HeaderBuilder._build_link_tags -> list[str] {
|
|
168
|
+
lines: list = [];
|
|
169
|
+
special_links = {
|
|
170
|
+
"canonical": "canonical",
|
|
171
|
+
"icon": "icon",
|
|
172
|
+
"apple_touch_icon": "apple-touch-icon",
|
|
173
|
+
"manifest": "manifest"
|
|
174
|
+
};
|
|
175
|
+
for (key, rel) in special_links.items() {
|
|
176
|
+
if val := self._get_value(key) {
|
|
177
|
+
lines.append(f'<link rel={rel} href={html.escape(str(val))} />');
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
for (key, val) in self.meta_data.items() {
|
|
181
|
+
if key.startswith("link_") and val {
|
|
182
|
+
# link_preconnect_google → rel="preconnect"
|
|
183
|
+
rel = key[5:].replace("_", "-");
|
|
184
|
+
lines.append(f'<link rel={rel} href={html.escape(str(val))} />');
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return lines;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
"""Build Open Graph tags (og_* prefix)."""
|
|
191
|
+
impl HeaderBuilder._build_og_tags -> list[str] {
|
|
192
|
+
lines: list = [];
|
|
193
|
+
for (key, val) in self.meta_data.items() {
|
|
194
|
+
if key.startswith("og_") and val {
|
|
195
|
+
property_name = key[3:].replace("_", ":");
|
|
196
|
+
lines.append(
|
|
197
|
+
f'<meta property=og:{property_name} content={html.escape(str(val))} />'
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return lines;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
"""Build standard meta tags (description, robots, keywords, author, etc)."""
|
|
205
|
+
impl HeaderBuilder._build_meta_tags -> list[str] {
|
|
206
|
+
lines: list = [];
|
|
207
|
+
for key in self.standard_tags {
|
|
208
|
+
if val := self._get_value(key) {
|
|
209
|
+
# Convert underscore to dash (theme_color → theme-color)
|
|
210
|
+
name = key.replace("_", "-");
|
|
211
|
+
lines.append(f'<meta name={name} content={html.escape(val)} />');
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return lines;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
"""Build special tags (charset, viewport, title)."""
|
|
218
|
+
impl HeaderBuilder._build_special_tags -> list[str] {
|
|
219
|
+
lines: list = [];
|
|
220
|
+
if charset := self._get_value("charset") {
|
|
221
|
+
lines.append(f'<meta charset={html.escape(charset)} />');
|
|
222
|
+
}
|
|
223
|
+
if viewport := self._get_value("viewport") {
|
|
224
|
+
lines.append(f'<meta name="viewport" content={html.escape(viewport)} />');
|
|
225
|
+
}
|
|
226
|
+
title = self._get_value("title") or self.function_name;
|
|
227
|
+
lines.append(f'<title>{html.escape(title)}</title>');
|
|
228
|
+
return lines;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
"""Get value with fallback to defaults."""
|
|
232
|
+
impl HeaderBuilder._get_value(key: str) -> str {
|
|
233
|
+
return self.meta_data.get(key) or self.DEFAULTS.get(key);
|
|
134
234
|
}
|
|
@@ -65,9 +65,11 @@ impl __jacSpawn(left: str, right: str = "", fields: dict = {}) -> any {
|
|
|
65
65
|
);
|
|
66
66
|
if not response.ok {
|
|
67
67
|
error_text = await response.json();
|
|
68
|
-
|
|
68
|
+
walker_name = f"{left}/{right}" if right else left;
|
|
69
|
+
raise Exception(f"Walker {walker_name} failed: {error_text}") ;
|
|
69
70
|
}
|
|
70
|
-
|
|
71
|
+
payload = await response.json();
|
|
72
|
+
return payload["data"] if payload["data"] else {};
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
impl jacSpawn(left: str, right: str = "", fields: dict = {}) -> any {
|
|
@@ -84,15 +86,21 @@ impl __jacCallFunction(function_name: str, args: dict = {}) -> any {
|
|
|
84
86
|
"Content-Type": "application/json",
|
|
85
87
|
"Authorization": f"Bearer {token}" if token else ""
|
|
86
88
|
},
|
|
87
|
-
"body": JSON.stringify(
|
|
89
|
+
"body": JSON.stringify(args)
|
|
88
90
|
}
|
|
89
91
|
);
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
payload = await response.json();
|
|
93
|
+
if not payload["ok"] {
|
|
94
|
+
error_msg = payload["error"] if payload["error"] else "Unknown error";
|
|
95
|
+
raise Exception(f"Function {function_name} failed: {error_msg}") ;
|
|
93
96
|
}
|
|
94
|
-
|
|
95
|
-
|
|
97
|
+
result = None;
|
|
98
|
+
try {
|
|
99
|
+
if payload["data"] and payload["data"]["result"] {
|
|
100
|
+
result = payload["data"]["result"];
|
|
101
|
+
}
|
|
102
|
+
} except Exception { }
|
|
103
|
+
return result;
|
|
96
104
|
}
|
|
97
105
|
|
|
98
106
|
impl jacSignup(username: str, password: str) -> dict {
|
|
@@ -106,7 +114,10 @@ impl jacSignup(username: str, password: str) -> dict {
|
|
|
106
114
|
);
|
|
107
115
|
if response.ok {
|
|
108
116
|
data = JSON.parse(await response.text());
|
|
109
|
-
token =
|
|
117
|
+
token = None;
|
|
118
|
+
if data["data"] and data["data"]["token"] {
|
|
119
|
+
token = data["data"]["token"];
|
|
120
|
+
}
|
|
110
121
|
if token {
|
|
111
122
|
__setLocalStorage("jac_token", token);
|
|
112
123
|
return {"success": True, "token": token, "username": username};
|
|
@@ -139,7 +150,14 @@ impl jacLogin(username: str, password: str) -> bool {
|
|
|
139
150
|
);
|
|
140
151
|
if response.ok {
|
|
141
152
|
data = JSON.parse(await response.text());
|
|
142
|
-
|
|
153
|
+
console.log("data", data);
|
|
154
|
+
token = None;
|
|
155
|
+
try {
|
|
156
|
+
if data["data"] and data["data"]["token"] {
|
|
157
|
+
token = data["data"]["token"];
|
|
158
|
+
}
|
|
159
|
+
} except Exception { }
|
|
160
|
+
console.log("token", token);
|
|
143
161
|
if token {
|
|
144
162
|
__setLocalStorage("jac_token", token);
|
|
145
163
|
return True;
|
|
@@ -175,3 +193,157 @@ impl __removeLocalStorage(key: str) -> None {
|
|
|
175
193
|
storage.removeItem(key);
|
|
176
194
|
}
|
|
177
195
|
}
|
|
196
|
+
|
|
197
|
+
impl ErrorFallback(error: str, resetErrorBoundary: any) -> any {
|
|
198
|
+
return
|
|
199
|
+
<div
|
|
200
|
+
role="alert"
|
|
201
|
+
style={{
|
|
202
|
+
minHeight: "100vh",
|
|
203
|
+
display: "flex",
|
|
204
|
+
justifyContent: "center",
|
|
205
|
+
alignItems: "center",
|
|
206
|
+
backgroundColor: "#f9fafb",
|
|
207
|
+
fontFamily: "system-ui, sans-serif",
|
|
208
|
+
|
|
209
|
+
}}
|
|
210
|
+
>
|
|
211
|
+
<div
|
|
212
|
+
role="alert"
|
|
213
|
+
style={{
|
|
214
|
+
minHeight: "100vh",
|
|
215
|
+
display: "flex",
|
|
216
|
+
flexDirection: "column",
|
|
217
|
+
justifyContent: "center",
|
|
218
|
+
alignItems: "center",
|
|
219
|
+
backgroundColor: "#f9fafb",
|
|
220
|
+
fontFamily: "system-ui, sans-serif",
|
|
221
|
+
|
|
222
|
+
}}
|
|
223
|
+
>
|
|
224
|
+
<h2 style={{color: "#dc2626", marginBottom: "12px"}}>
|
|
225
|
+
🚨 Something went wrong
|
|
226
|
+
</h2>
|
|
227
|
+
<p style={{color: "#374151", marginBottom: "16px"}}>
|
|
228
|
+
An unexpected error occurred. Please try again.
|
|
229
|
+
</p>
|
|
230
|
+
<pre
|
|
231
|
+
style={{
|
|
232
|
+
color: "#991b1b",
|
|
233
|
+
background: "#fee2e2",
|
|
234
|
+
padding: "12px",
|
|
235
|
+
borderRadius: "8px",
|
|
236
|
+
fontSize: "14px",
|
|
237
|
+
overflowX: "auto",
|
|
238
|
+
marginBottom: "16px",
|
|
239
|
+
|
|
240
|
+
}}
|
|
241
|
+
>
|
|
242
|
+
{error.error.message}
|
|
243
|
+
</pre>
|
|
244
|
+
<button
|
|
245
|
+
onClick={lambda -> None { error.resetErrorBoundary();}}
|
|
246
|
+
style={{
|
|
247
|
+
backgroundColor: "#2563eb",
|
|
248
|
+
color: "#fff",
|
|
249
|
+
padding: "10px 16px",
|
|
250
|
+
borderRadius: "8px",
|
|
251
|
+
border: "none",
|
|
252
|
+
cursor: "pointer",
|
|
253
|
+
fontSize: "14px",
|
|
254
|
+
|
|
255
|
+
}}
|
|
256
|
+
>
|
|
257
|
+
🔄 Try again
|
|
258
|
+
</button>
|
|
259
|
+
</div>
|
|
260
|
+
</div>;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
impl errorOverlay(filePath: str, errors: str) -> any {
|
|
264
|
+
return (
|
|
265
|
+
<div
|
|
266
|
+
style={{
|
|
267
|
+
position: "fixed",
|
|
268
|
+
top: 0,
|
|
269
|
+
left: 0,
|
|
270
|
+
width: "100%",
|
|
271
|
+
height: "100%",
|
|
272
|
+
background: "rgba(0, 0, 0, 0.85)",
|
|
273
|
+
color: "#fff",
|
|
274
|
+
fontFamily: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace",
|
|
275
|
+
fontSize: 14,
|
|
276
|
+
zIndex: 999999,
|
|
277
|
+
overflow: "auto",
|
|
278
|
+
padding: 20,
|
|
279
|
+
boxSizing: "border-box",
|
|
280
|
+
|
|
281
|
+
}}
|
|
282
|
+
>
|
|
283
|
+
<div style={{maxWidth: 1200, margin: "0 auto"}}>
|
|
284
|
+
<div
|
|
285
|
+
style={{
|
|
286
|
+
background: "#d32f2f",
|
|
287
|
+
color: "white",
|
|
288
|
+
padding: "16px 24px",
|
|
289
|
+
borderRadius: "8px 8px 0 0",
|
|
290
|
+
fontSize: 18,
|
|
291
|
+
fontWeight: "bold",
|
|
292
|
+
|
|
293
|
+
}}
|
|
294
|
+
>
|
|
295
|
+
⚠️ Compilation Error
|
|
296
|
+
</div>
|
|
297
|
+
<div
|
|
298
|
+
style={{
|
|
299
|
+
background: "#1e1e1e",
|
|
300
|
+
padding: 24,
|
|
301
|
+
borderRadius: "0 0 8px 8px",
|
|
302
|
+
|
|
303
|
+
}}
|
|
304
|
+
>
|
|
305
|
+
<div style={{marginBottom: 16}}>
|
|
306
|
+
<div style={{color: "#888", marginBottom: 8}}>
|
|
307
|
+
File:
|
|
308
|
+
</div>
|
|
309
|
+
<div style={{color: "#64b5f6", fontWeight: "bold"}}>
|
|
310
|
+
{filePath}
|
|
311
|
+
</div>
|
|
312
|
+
</div>
|
|
313
|
+
<div>
|
|
314
|
+
<div style={{color: "#888", marginBottom: 8}}>
|
|
315
|
+
Error:
|
|
316
|
+
</div>
|
|
317
|
+
<pre
|
|
318
|
+
style={{
|
|
319
|
+
background: "#2d2d2d",
|
|
320
|
+
padding: 16,
|
|
321
|
+
borderRadius: 4,
|
|
322
|
+
overflowX: "auto",
|
|
323
|
+
margin: 0,
|
|
324
|
+
borderLeft: "4px solid #d32f2f",
|
|
325
|
+
lineHeight: 1.6,
|
|
326
|
+
color: "#ff6b6b",
|
|
327
|
+
|
|
328
|
+
}}
|
|
329
|
+
>
|
|
330
|
+
{errors}
|
|
331
|
+
</pre>
|
|
332
|
+
</div>
|
|
333
|
+
<div
|
|
334
|
+
style={{
|
|
335
|
+
marginTop: 24,
|
|
336
|
+
paddingTop: 16,
|
|
337
|
+
borderTop: "1px solid #444",
|
|
338
|
+
color: "#888",
|
|
339
|
+
fontSize: 13,
|
|
340
|
+
|
|
341
|
+
}}
|
|
342
|
+
>
|
|
343
|
+
💡 Fix the error and save the file to continue development.
|
|
344
|
+
</div>
|
|
345
|
+
</div>
|
|
346
|
+
</div>
|
|
347
|
+
</div>
|
|
348
|
+
);
|
|
349
|
+
}
|