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.
Files changed (119) hide show
  1. jac_client/examples/all-in-one/{src/button.jac → button.jac} +4 -3
  2. jac_client/examples/all-in-one/components/CategoryFilter.jac +47 -0
  3. jac_client/examples/all-in-one/components/Header.jac +17 -0
  4. jac_client/examples/all-in-one/components/ProfitOverview.jac +64 -0
  5. jac_client/examples/all-in-one/components/Summary.jac +76 -0
  6. jac_client/examples/all-in-one/components/TransactionForm.jac +188 -0
  7. jac_client/examples/all-in-one/components/TransactionItem.jac +62 -0
  8. jac_client/examples/all-in-one/components/TransactionList.jac +44 -0
  9. jac_client/examples/all-in-one/components/button.jac +8 -0
  10. jac_client/examples/all-in-one/components/navigation.jac +126 -0
  11. jac_client/examples/all-in-one/constants/categories.jac +36 -0
  12. jac_client/examples/all-in-one/constants/clients.jac +12 -0
  13. jac_client/examples/all-in-one/context/BudgetContext.jac +31 -0
  14. jac_client/examples/all-in-one/hooks/useBudget.jac +122 -0
  15. jac_client/examples/all-in-one/hooks/useLocalStorage.jac +37 -0
  16. jac_client/examples/all-in-one/main.jac +542 -0
  17. jac_client/examples/all-in-one/pages/BudgetPlanner.jac +140 -0
  18. jac_client/examples/all-in-one/pages/FeaturesTest.jac +157 -0
  19. jac_client/examples/all-in-one/pages/LandingPage.jac +124 -0
  20. jac_client/examples/all-in-one/pages/budget_planner_ui.cl.jac +65 -0
  21. jac_client/examples/all-in-one/pages/features_test_ui.cl.jac +675 -0
  22. jac_client/examples/all-in-one/pages/loginPage.jac +127 -0
  23. jac_client/examples/all-in-one/pages/nestedDemo.jac +54 -0
  24. jac_client/examples/all-in-one/pages/notFound.jac +18 -0
  25. jac_client/examples/all-in-one/pages/signupPage.jac +127 -0
  26. jac_client/examples/all-in-one/utils/formatters.jac +49 -0
  27. jac_client/examples/asset-serving/css-with-image/main.jac +92 -0
  28. jac_client/examples/asset-serving/image-asset/main.jac +56 -0
  29. jac_client/examples/asset-serving/import-alias/main.jac +109 -0
  30. jac_client/examples/basic/main.jac +23 -0
  31. jac_client/examples/basic-auth/main.jac +363 -0
  32. jac_client/examples/basic-auth-with-router/main.jac +451 -0
  33. jac_client/examples/basic-full-stack/main.jac +362 -0
  34. jac_client/examples/css-styling/js-styling/main.jac +63 -0
  35. jac_client/examples/css-styling/material-ui/main.jac +122 -0
  36. jac_client/examples/css-styling/pure-css/main.jac +55 -0
  37. jac_client/examples/css-styling/sass-example/main.jac +55 -0
  38. jac_client/examples/css-styling/styled-components/main.jac +62 -0
  39. jac_client/examples/css-styling/tailwind-example/main.jac +74 -0
  40. jac_client/examples/full-stack-with-auth/main.jac +696 -0
  41. jac_client/examples/little-x/main.jac +681 -0
  42. jac_client/examples/little-x/src/submit-button.jac +15 -14
  43. jac_client/examples/nested-folders/nested-advance/main.jac +26 -0
  44. jac_client/examples/nested-folders/nested-advance/src/ButtonRoot.jac +4 -6
  45. jac_client/examples/nested-folders/nested-advance/src/level1/ButtonSecondL.jac +9 -13
  46. jac_client/examples/nested-folders/nested-advance/src/level1/Card.jac +29 -32
  47. jac_client/examples/nested-folders/nested-advance/src/level1/level2/ButtonThirdL.jac +12 -18
  48. jac_client/examples/nested-folders/nested-basic/{src/app.jac → main.jac} +7 -5
  49. jac_client/examples/nested-folders/nested-basic/src/button.jac +4 -3
  50. jac_client/examples/nested-folders/nested-basic/src/components/button.jac +4 -3
  51. jac_client/examples/ts-support/main.jac +35 -0
  52. jac_client/examples/with-router/main.jac +286 -0
  53. jac_client/plugin/cli.jac +507 -470
  54. jac_client/plugin/client.jac +30 -12
  55. jac_client/plugin/client_runtime.cl.jac +25 -15
  56. jac_client/plugin/impl/client.impl.jac +126 -26
  57. jac_client/plugin/impl/client_runtime.impl.jac +182 -10
  58. jac_client/plugin/plugin_config.jac +216 -34
  59. jac_client/plugin/src/__init__.jac +0 -2
  60. jac_client/plugin/src/compiler.jac +2 -2
  61. jac_client/plugin/src/config_loader.jac +1 -0
  62. jac_client/plugin/src/desktop_config.jac +31 -0
  63. jac_client/plugin/src/impl/compiler.impl.jac +99 -30
  64. jac_client/plugin/src/impl/config_loader.impl.jac +8 -0
  65. jac_client/plugin/src/impl/desktop_config.impl.jac +191 -0
  66. jac_client/plugin/src/impl/jac_to_js.impl.jac +5 -1
  67. jac_client/plugin/src/impl/package_installer.impl.jac +20 -20
  68. jac_client/plugin/src/impl/vite_bundler.impl.jac +384 -144
  69. jac_client/plugin/src/package_installer.jac +1 -1
  70. jac_client/plugin/src/targets/desktop/sidecar/main.py +144 -0
  71. jac_client/plugin/src/targets/desktop_target.jac +37 -0
  72. jac_client/plugin/src/targets/impl/desktop_target.impl.jac +2347 -0
  73. jac_client/plugin/src/targets/impl/registry.impl.jac +64 -0
  74. jac_client/plugin/src/targets/impl/web_target.impl.jac +157 -0
  75. jac_client/plugin/src/targets/register.jac +21 -0
  76. jac_client/plugin/src/targets/registry.jac +87 -0
  77. jac_client/plugin/src/targets/web_target.jac +35 -0
  78. jac_client/plugin/src/vite_bundler.jac +15 -1
  79. jac_client/plugin/utils/__init__.jac +3 -0
  80. jac_client/plugin/utils/bun_installer.jac +16 -0
  81. jac_client/plugin/utils/impl/bun_installer.impl.jac +99 -0
  82. jac_client/templates/client.jacpack +72 -0
  83. jac_client/templates/fullstack.jacpack +61 -0
  84. jac_client/tests/conftest.py +110 -52
  85. jac_client/tests/fixtures/spawn_test/app.jac +64 -70
  86. jac_client/tests/fixtures/with-ts/app.jac +28 -28
  87. jac_client/tests/test_cli.py +280 -113
  88. jac_client/tests/test_e2e.py +232 -0
  89. jac_client/tests/test_helpers.py +58 -0
  90. jac_client/tests/test_it.py +325 -154
  91. jac_client/tests/test_it_desktop.py +891 -0
  92. {jac_client-0.2.6.dist-info → jac_client-0.2.11.dist-info}/METADATA +20 -11
  93. jac_client-0.2.11.dist-info/RECORD +113 -0
  94. {jac_client-0.2.6.dist-info → jac_client-0.2.11.dist-info}/WHEEL +1 -1
  95. jac_client/examples/all-in-one/src/app.jac +0 -841
  96. jac_client/examples/all-in-one/src/components/button.jac +0 -7
  97. jac_client/examples/asset-serving/css-with-image/src/app.jac +0 -88
  98. jac_client/examples/asset-serving/image-asset/src/app.jac +0 -55
  99. jac_client/examples/asset-serving/import-alias/src/app.jac +0 -111
  100. jac_client/examples/basic/src/app.jac +0 -21
  101. jac_client/examples/basic-auth/src/app.jac +0 -377
  102. jac_client/examples/basic-auth-with-router/src/app.jac +0 -464
  103. jac_client/examples/basic-full-stack/src/app.jac +0 -365
  104. jac_client/examples/css-styling/js-styling/src/app.jac +0 -84
  105. jac_client/examples/css-styling/material-ui/src/app.jac +0 -122
  106. jac_client/examples/css-styling/pure-css/src/app.jac +0 -64
  107. jac_client/examples/css-styling/sass-example/src/app.jac +0 -64
  108. jac_client/examples/css-styling/styled-components/src/app.jac +0 -71
  109. jac_client/examples/css-styling/tailwind-example/src/app.jac +0 -63
  110. jac_client/examples/full-stack-with-auth/src/app.jac +0 -722
  111. jac_client/examples/little-x/src/app.jac +0 -719
  112. jac_client/examples/nested-folders/nested-advance/src/app.jac +0 -35
  113. jac_client/examples/ts-support/src/app.jac +0 -35
  114. jac_client/examples/with-router/src/app.jac +0 -323
  115. jac_client/plugin/src/babel_processor.jac +0 -18
  116. jac_client/plugin/src/impl/babel_processor.impl.jac +0 -84
  117. jac_client-0.2.6.dist-info/RECORD +0 -74
  118. {jac_client-0.2.6.dist-info → jac_client-0.2.11.dist-info}/entry_points.txt +0 -0
  119. {jac_client-0.2.6.dist-info → jac_client-0.2.11.dist-info}/top_level.txt +0 -0
@@ -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
- """Jac Client Module Introspector."""
22
- class JacClientModuleIntrospector(ModuleIntrospector) {
23
- def render_page(
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
- args: dict[(str, Any)],
27
- username: str
28
- ) -> dict[str, Any];
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 : pub __jacJsx(tag: any, props: dict = {}, children: any = []) -> any;
18
+ def:pub __jacJsx(tag: any, props: dict = {}, children: any = []) -> any;
17
19
 
18
- glob : pub Router = ReactRouterHashRouter,
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 : pub useRouter -> dict;
28
- def : pub navigate(path: str) -> None;
29
- async def : pub __jacSpawn(left: str, right: str = "", fields: dict = {}) -> any;
30
- def : pub jacSpawn(left: str, right: str = "", fields: dict = {}) -> any;
31
- async def : pub __jacCallFunction(function_name: str, args: dict = {}) -> any;
32
- async def : pub jacSignup(username: str, password: str) -> dict;
33
- async def : pub jacLogin(username: str, password: str) -> bool;
34
- def : pub jacLogout -> None;
35
- def : pub jacIsLoggedIn -> bool;
36
- def : pub __getLocalStorage(key: str) -> str;
37
- def : pub __setLocalStorage(key: str, value: str) -> None;
38
- def : pub __removeLocalStorage(key: str) -> None;
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
- # package.json should only exist in .client-build/.jac-client.configs/
61
- generated_package_json = base_path / '.client-build' / '.jac-client.configs' / 'package.json';
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 = base_path / '.client-build' / 'dist';
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 JacClientModuleIntrospector.render_page(
95
- self: JacClientModuleIntrospector,
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
- self.load();
94
+ introspector.load();
101
95
  available_exports = (
102
- set(self._client_manifest.get('exports', []))
103
- or set(self.get_client_functions().keys())
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 = self.ensure_bundle();
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
- dist_dir = base_path / '.client-build' / 'dist';
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 / 'main.css';
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/main.css?hash={css_hash}"/>';
124
+ css_link = f'<link rel="stylesheet" href="/static/styles.css?hash={css_hash}"/>';
125
125
  }
126
- head_content = f'<meta charset="utf-8"/>\n <title>{html.escape(
127
- function_name
128
- )}</title>';
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 {'html': page, 'bundle_hash': bundle_hash, 'bundle_code': self._bundle.code};
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
- raise Exception(f"Walker {walker} failed: {error_text}") ;
68
+ walker_name = f"{left}/{right}" if right else left;
69
+ raise Exception(f"Walker {walker_name} failed: {error_text}") ;
69
70
  }
70
- return await response.json();
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({"args": args})
89
+ "body": JSON.stringify(args)
88
90
  }
89
91
  );
90
- if not response.ok {
91
- error_text = await response.text();
92
- raise Exception(f"Function {function_name} failed: {error_text}") ;
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
- data = JSON.parse(await response.text());
95
- return data["result"];
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 = data["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
- token = data["token"];
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
+ }