jac-client 0.2.5__py3-none-any.whl → 0.2.7__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/app.jac +473 -741
- jac_client/examples/all-in-one/src/components/CategoryFilter.jac +35 -0
- jac_client/examples/all-in-one/src/components/Header.jac +13 -0
- jac_client/examples/all-in-one/src/components/ProfitOverview.jac +50 -0
- jac_client/examples/all-in-one/src/components/Summary.jac +53 -0
- jac_client/examples/all-in-one/src/components/TransactionForm.jac +158 -0
- jac_client/examples/all-in-one/src/components/TransactionItem.jac +55 -0
- jac_client/examples/all-in-one/src/components/TransactionList.jac +37 -0
- jac_client/examples/all-in-one/src/components/navigation.jac +132 -0
- jac_client/examples/all-in-one/src/constants/categories.jac +37 -0
- jac_client/examples/all-in-one/src/constants/clients.jac +13 -0
- jac_client/examples/all-in-one/src/context/BudgetContext.jac +28 -0
- jac_client/examples/all-in-one/src/hooks/useBudget.jac +116 -0
- jac_client/examples/all-in-one/src/hooks/useLocalStorage.jac +36 -0
- jac_client/examples/all-in-one/src/pages/BudgetPlanner.cl.jac +70 -0
- jac_client/examples/all-in-one/src/pages/BudgetPlanner.jac +126 -0
- jac_client/examples/all-in-one/src/pages/FeaturesTest.cl.jac +552 -0
- jac_client/examples/all-in-one/src/pages/FeaturesTest.jac +126 -0
- jac_client/examples/all-in-one/src/pages/LandingPage.jac +101 -0
- jac_client/examples/all-in-one/src/pages/loginPage.jac +132 -0
- jac_client/examples/all-in-one/src/pages/nestedDemo.jac +61 -0
- jac_client/examples/all-in-one/src/pages/notFound.jac +24 -0
- jac_client/examples/all-in-one/src/pages/signupPage.jac +133 -0
- jac_client/examples/all-in-one/src/utils/formatters.jac +52 -0
- jac_client/examples/asset-serving/css-with-image/src/app.jac +3 -3
- jac_client/examples/asset-serving/image-asset/src/app.jac +3 -3
- jac_client/examples/asset-serving/import-alias/src/app.jac +3 -3
- jac_client/examples/basic/src/app.jac +3 -3
- jac_client/examples/basic-auth/src/app.jac +31 -37
- jac_client/examples/basic-auth-with-router/src/app.jac +16 -16
- jac_client/examples/basic-full-stack/src/app.jac +24 -30
- jac_client/examples/css-styling/js-styling/src/app.jac +5 -5
- jac_client/examples/css-styling/material-ui/src/app.jac +5 -5
- jac_client/examples/css-styling/pure-css/src/app.jac +5 -5
- jac_client/examples/css-styling/sass-example/src/app.jac +5 -5
- jac_client/examples/css-styling/styled-components/src/app.jac +5 -5
- jac_client/examples/css-styling/tailwind-example/src/app.jac +5 -5
- jac_client/examples/full-stack-with-auth/src/app.jac +16 -16
- jac_client/examples/ts-support/src/app.jac +4 -4
- jac_client/examples/with-router/src/app.jac +4 -4
- jac_client/plugin/cli.jac +155 -203
- jac_client/plugin/client_runtime.cl.jac +5 -1
- jac_client/plugin/impl/client.impl.jac +74 -12
- jac_client/plugin/plugin_config.jac +11 -11
- jac_client/plugin/src/compiler.jac +2 -1
- jac_client/plugin/src/impl/babel_processor.impl.jac +22 -17
- jac_client/plugin/src/impl/compiler.impl.jac +57 -18
- jac_client/plugin/src/impl/vite_bundler.impl.jac +66 -102
- jac_client/plugin/src/package_installer.jac +1 -1
- jac_client/plugin/src/vite_bundler.jac +1 -0
- jac_client/tests/conftest.py +10 -8
- jac_client/tests/fixtures/spawn_test/app.jac +15 -18
- jac_client/tests/fixtures/with-ts/app.jac +4 -4
- jac_client/tests/test_cli.py +99 -45
- jac_client/tests/test_it.py +290 -79
- {jac_client-0.2.5.dist-info → jac_client-0.2.7.dist-info}/METADATA +16 -7
- jac_client-0.2.7.dist-info/RECORD +97 -0
- jac_client-0.2.5.dist-info/RECORD +0 -74
- {jac_client-0.2.5.dist-info → jac_client-0.2.7.dist-info}/WHEEL +0 -0
- {jac_client-0.2.5.dist-info → jac_client-0.2.7.dist-info}/entry_points.txt +0 -0
- {jac_client-0.2.5.dist-info → jac_client-0.2.7.dist-info}/top_level.txt +0 -0
|
@@ -1,32 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
# Combined example: auth + routing + CSS styling + asset serving + nested folder imports
|
|
3
|
-
#
|
|
4
|
-
cl import from react { useState, useEffect, useRef }
|
|
5
|
-
cl import from "@jac-client/utils" {
|
|
6
|
-
Router,
|
|
7
|
-
Routes,
|
|
8
|
-
Route,
|
|
9
|
-
Link,
|
|
10
|
-
Navigate,
|
|
11
|
-
useNavigate,
|
|
12
|
-
useLocation,
|
|
13
|
-
jacSignup,
|
|
14
|
-
jacLogin,
|
|
15
|
-
jacLogout,
|
|
16
|
-
jacIsLoggedIn
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
# Pure CSS + asset-in-CSS example
|
|
20
|
-
cl import ".styles.css";
|
|
21
|
-
|
|
22
|
-
# Nested folder imports (same pattern as nested-basic/)
|
|
23
|
-
cl import from .components.button {
|
|
24
|
-
CustomButton
|
|
25
|
-
}
|
|
26
|
-
cl import from .button { CustomButtonRoot }
|
|
27
|
-
|
|
28
|
-
# TypeScript component import
|
|
29
|
-
cl import from ".components/Card.tsx" { Card }
|
|
1
|
+
import from datetime { datetime, timedelta }
|
|
30
2
|
|
|
31
3
|
#
|
|
32
4
|
# Basic backend walkers
|
|
@@ -57,785 +29,545 @@ walker get_server_message {
|
|
|
57
29
|
}
|
|
58
30
|
}
|
|
59
31
|
|
|
60
|
-
cl {
|
|
61
|
-
# Login Page
|
|
62
|
-
def LoginPage -> any {
|
|
63
|
-
[username, setUsername] = useState("");
|
|
64
|
-
[password, setPassword] = useState("");
|
|
65
|
-
[error, setError] = useState("");
|
|
66
|
-
navigate = useNavigate();
|
|
67
|
-
|
|
68
|
-
async def handleLogin(e: any) -> None {
|
|
69
|
-
e.preventDefault();
|
|
70
|
-
setError("");
|
|
71
|
-
if not username or not password {
|
|
72
|
-
setError("Please fill in all fields");
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
success = await jacLogin(username, password);
|
|
76
|
-
if success {
|
|
77
|
-
navigate("/");
|
|
78
|
-
} else {
|
|
79
|
-
setError("Invalid credentials");
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
32
|
|
|
83
|
-
def handleUsernameChange(e: any) -> None {
|
|
84
|
-
setUsername(e.target.value);
|
|
85
|
-
}
|
|
86
33
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
34
|
+
# Features Test Page - Backend Logic
|
|
35
|
+
# This file is intentionally kept empty to demonstrate the separation of concerns
|
|
36
|
+
# where UI logic resides in .cl.jac files and backend walker logic in app.jac
|
|
90
37
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
>
|
|
96
|
-
{error}
|
|
97
|
-
</div>;
|
|
98
|
-
}
|
|
38
|
+
# All walker implementations for this feature are in src/app.jac
|
|
39
|
+
# This demonstrates that you can have separate .jac files for different purposes
|
|
40
|
+
# Features Test - Backend Walkers
|
|
41
|
+
# This file demonstrates walker functionality for testing various JAC features
|
|
99
42
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
style={{"marginBottom": "24px", "textAlign": "center"}}
|
|
120
|
-
>
|
|
121
|
-
Login
|
|
122
|
-
</h2>
|
|
123
|
-
<form
|
|
124
|
-
onSubmit={handleLogin}
|
|
125
|
-
>
|
|
126
|
-
<input
|
|
127
|
-
type="text"
|
|
128
|
-
value={username}
|
|
129
|
-
onChange={handleUsernameChange}
|
|
130
|
-
placeholder="Username"
|
|
131
|
-
style={{
|
|
132
|
-
"width": "100%",
|
|
133
|
-
"padding": "10px",
|
|
134
|
-
"marginBottom": "12px",
|
|
135
|
-
"border": "1px solid #ddd",
|
|
136
|
-
"borderRadius": "4px",
|
|
137
|
-
"boxSizing": "border-box"
|
|
138
|
-
}}
|
|
139
|
-
/>
|
|
140
|
-
<input
|
|
141
|
-
type="password"
|
|
142
|
-
value={password}
|
|
143
|
-
onChange={handlePasswordChange}
|
|
144
|
-
placeholder="Password"
|
|
145
|
-
style={{
|
|
146
|
-
"width": "100%",
|
|
147
|
-
"padding": "10px",
|
|
148
|
-
"marginBottom": "12px",
|
|
149
|
-
"border": "1px solid #ddd",
|
|
150
|
-
"borderRadius": "4px",
|
|
151
|
-
"boxSizing": "border-box"
|
|
152
|
-
}}
|
|
153
|
-
/>
|
|
154
|
-
{errorDisplay}
|
|
155
|
-
<button
|
|
156
|
-
type="submit"
|
|
157
|
-
style={{
|
|
158
|
-
"width": "100%",
|
|
159
|
-
"padding": "10px",
|
|
160
|
-
"background": "#3b82f6",
|
|
161
|
-
"color": "#ffffff",
|
|
162
|
-
"border": "none",
|
|
163
|
-
"borderRadius": "4px",
|
|
164
|
-
"cursor": "pointer",
|
|
165
|
-
"fontWeight": "600"
|
|
166
|
-
}}
|
|
167
|
-
>
|
|
168
|
-
Login
|
|
169
|
-
</button>
|
|
170
|
-
</form>
|
|
171
|
-
<p
|
|
172
|
-
style={{
|
|
173
|
-
"textAlign": "center",
|
|
174
|
-
"marginTop": "16px",
|
|
175
|
-
"fontSize": "14px"
|
|
176
|
-
}}
|
|
177
|
-
>
|
|
178
|
-
Need an account?
|
|
179
|
-
{" "}
|
|
180
|
-
<Link to="/signup">
|
|
181
|
-
Sign up
|
|
182
|
-
</Link>
|
|
183
|
-
</p>
|
|
184
|
-
</div>
|
|
185
|
-
</div>;
|
|
43
|
+
|
|
44
|
+
# Node definition for storing test data
|
|
45
|
+
node TestData {
|
|
46
|
+
has message: str;
|
|
47
|
+
has count: int = 0;
|
|
48
|
+
has created_at: str = "";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# Walker: Create test data
|
|
52
|
+
walker create_test_data {
|
|
53
|
+
has message: str;
|
|
54
|
+
|
|
55
|
+
can create with `root entry {
|
|
56
|
+
new_data = here ++> TestData(
|
|
57
|
+
message=self.message,
|
|
58
|
+
count=1,
|
|
59
|
+
created_at=datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
60
|
+
);
|
|
61
|
+
report new_data;
|
|
186
62
|
}
|
|
63
|
+
}
|
|
187
64
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
[
|
|
192
|
-
|
|
193
|
-
navigate = useNavigate();
|
|
194
|
-
|
|
195
|
-
async def handleSignup(e: any) -> None {
|
|
196
|
-
e.preventDefault();
|
|
197
|
-
setError("");
|
|
198
|
-
if not username or not password {
|
|
199
|
-
setError("Please fill in all fields");
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
result = await jacSignup(username, password);
|
|
203
|
-
if result["success"] {
|
|
204
|
-
navigate("/");
|
|
205
|
-
} else {
|
|
206
|
-
setError(result["error"] if result["error"] else "Signup failed");
|
|
207
|
-
}
|
|
208
|
-
}
|
|
65
|
+
# Walker: Read all test data
|
|
66
|
+
walker read_test_data {
|
|
67
|
+
can read with `root entry {
|
|
68
|
+
visit [-->(`?TestData)];
|
|
69
|
+
}
|
|
209
70
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
71
|
+
can report_data with exit {
|
|
72
|
+
report here;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
213
75
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
76
|
+
# Walker: Update test data
|
|
77
|
+
walker update_test_data {
|
|
78
|
+
has new_message: str;
|
|
217
79
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
</div>;
|
|
225
|
-
}
|
|
80
|
+
can update with TestData entry {
|
|
81
|
+
here.message = self.new_message;
|
|
82
|
+
here.count = here.count + 1;
|
|
83
|
+
report here;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
226
86
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
"justifyContent": "center",
|
|
233
|
-
"background": "#f5f5f5"
|
|
234
|
-
}}
|
|
235
|
-
>
|
|
236
|
-
<div
|
|
237
|
-
style={{
|
|
238
|
-
"background": "#ffffff",
|
|
239
|
-
"padding": "32px",
|
|
240
|
-
"borderRadius": "8px",
|
|
241
|
-
"width": "300px",
|
|
242
|
-
"boxShadow": "0 2px 8px rgba(0,0,0,0.1)"
|
|
243
|
-
}}
|
|
244
|
-
>
|
|
245
|
-
<h2
|
|
246
|
-
style={{"marginBottom": "24px", "textAlign": "center"}}
|
|
247
|
-
>
|
|
248
|
-
Sign Up
|
|
249
|
-
</h2>
|
|
250
|
-
<form
|
|
251
|
-
onSubmit={handleSignup}
|
|
252
|
-
>
|
|
253
|
-
<input
|
|
254
|
-
type="text"
|
|
255
|
-
value={username}
|
|
256
|
-
onChange={handleUsernameChange}
|
|
257
|
-
placeholder="Username"
|
|
258
|
-
style={{
|
|
259
|
-
"width": "100%",
|
|
260
|
-
"padding": "10px",
|
|
261
|
-
"marginBottom": "12px",
|
|
262
|
-
"border": "1px solid #ddd",
|
|
263
|
-
"borderRadius": "4px",
|
|
264
|
-
"boxSizing": "border-box"
|
|
265
|
-
}}
|
|
266
|
-
/>
|
|
267
|
-
<input
|
|
268
|
-
type="password"
|
|
269
|
-
value={password}
|
|
270
|
-
onChange={handlePasswordChange}
|
|
271
|
-
placeholder="Password"
|
|
272
|
-
style={{
|
|
273
|
-
"width": "100%",
|
|
274
|
-
"padding": "10px",
|
|
275
|
-
"marginBottom": "12px",
|
|
276
|
-
"border": "1px solid #ddd",
|
|
277
|
-
"borderRadius": "4px",
|
|
278
|
-
"boxSizing": "border-box"
|
|
279
|
-
}}
|
|
280
|
-
/>
|
|
281
|
-
{errorDisplay}
|
|
282
|
-
<button
|
|
283
|
-
type="submit"
|
|
284
|
-
style={{
|
|
285
|
-
"width": "100%",
|
|
286
|
-
"padding": "10px",
|
|
287
|
-
"background": "#3b82f6",
|
|
288
|
-
"color": "#ffffff",
|
|
289
|
-
"border": "none",
|
|
290
|
-
"borderRadius": "4px",
|
|
291
|
-
"cursor": "pointer",
|
|
292
|
-
"fontWeight": "600"
|
|
293
|
-
}}
|
|
294
|
-
>
|
|
295
|
-
Sign Up
|
|
296
|
-
</button>
|
|
297
|
-
</form>
|
|
298
|
-
<p
|
|
299
|
-
style={{
|
|
300
|
-
"textAlign": "center",
|
|
301
|
-
"marginTop": "16px",
|
|
302
|
-
"fontSize": "14px"
|
|
303
|
-
}}
|
|
304
|
-
>
|
|
305
|
-
Have an account?
|
|
306
|
-
{" "}
|
|
307
|
-
<Link to="/login">
|
|
308
|
-
Login
|
|
309
|
-
</Link>
|
|
310
|
-
</p>
|
|
311
|
-
</div>
|
|
312
|
-
</div>;
|
|
87
|
+
# Walker: Delete test data
|
|
88
|
+
walker delete_test_data {
|
|
89
|
+
can delete with TestData entry {
|
|
90
|
+
del here;
|
|
91
|
+
report {"status": "deleted"};
|
|
313
92
|
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
# Walker: String manipulation test
|
|
96
|
+
walker test_string_methods {
|
|
97
|
+
has input_text: str;
|
|
98
|
+
|
|
99
|
+
can process with `root entry {
|
|
100
|
+
result = {
|
|
101
|
+
"original": self.input_text,
|
|
102
|
+
"uppercase": self.input_text.upper(),
|
|
103
|
+
"lowercase": self.input_text.lower(),
|
|
104
|
+
"capitalized": self.input_text.capitalize(),
|
|
105
|
+
"reversed": self.input_text[::-1],
|
|
106
|
+
"length": len(self.input_text),
|
|
107
|
+
"words": self.input_text.split(),
|
|
108
|
+
"trimmed": self.input_text.strip(),
|
|
109
|
+
"replaced": self.input_text.replace("test", "demo")
|
|
110
|
+
};
|
|
111
|
+
report result;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# Walker: Array/List operations test
|
|
116
|
+
walker test_list_operations {
|
|
117
|
+
has numbers: list;
|
|
118
|
+
|
|
119
|
+
can process with `root entry {
|
|
120
|
+
result = {
|
|
121
|
+
"original": self.numbers,
|
|
122
|
+
"sorted": sorted(self.numbers),
|
|
123
|
+
"reversed": list(reversed(self.numbers)),
|
|
124
|
+
"sum": sum(self.numbers),
|
|
125
|
+
"max": max(self.numbers) if len(self.numbers) > 0 else 0,
|
|
126
|
+
"min": min(self.numbers) if len(self.numbers) > 0 else 0,
|
|
127
|
+
"length": len(self.numbers),
|
|
128
|
+
"doubled": [x * 2 for x in self.numbers]
|
|
129
|
+
};
|
|
130
|
+
report result;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
314
133
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
134
|
+
# Walker: Complex data processing
|
|
135
|
+
walker process_complex_data {
|
|
136
|
+
has items: list;
|
|
137
|
+
|
|
138
|
+
can process with `root entry {
|
|
139
|
+
processed = [];
|
|
140
|
+
for item in self.items {
|
|
141
|
+
processed.append({
|
|
142
|
+
"id": item.get("id", 0),
|
|
143
|
+
"name": item.get("name", "").upper(),
|
|
144
|
+
"value": item.get("value", 0) * 2,
|
|
145
|
+
"processed_at": datetime.now().strftime("%H:%M:%S")
|
|
146
|
+
});
|
|
320
147
|
}
|
|
321
|
-
[count, setCount] = useState(0);
|
|
322
|
-
[pingResult, setPingResult] = useState("");
|
|
323
|
-
[serverMessage, setServerMessage] = useState("");
|
|
324
|
-
[lastTodoMessage, setLastTodoMessage] = useState("");
|
|
325
148
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
149
|
+
result = {
|
|
150
|
+
"original_count": len(self.items),
|
|
151
|
+
"processed_count": len(processed),
|
|
152
|
+
"items": processed,
|
|
153
|
+
"total_value": sum([p["value"] for p in processed])
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
report result;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
#
|
|
162
|
+
# Combined example: auth + routing + CSS styling + asset serving + nested folder imports
|
|
163
|
+
#
|
|
164
|
+
cl import from react { useEffect, useRef }
|
|
165
|
+
cl import from "@jac-client/utils" {
|
|
166
|
+
Router,
|
|
167
|
+
Routes,
|
|
168
|
+
Route,
|
|
169
|
+
Link,
|
|
170
|
+
useNavigate,
|
|
171
|
+
Navigate,
|
|
172
|
+
jacIsLoggedIn
|
|
173
|
+
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
# Pure CSS + asset-in-CSS example
|
|
177
|
+
cl import ".styles.css";
|
|
178
|
+
|
|
179
|
+
# Login Page
|
|
180
|
+
cl import from .pages.loginPage { LoginPage }
|
|
181
|
+
|
|
182
|
+
# Signup Page
|
|
183
|
+
cl import from .pages.signupPage { SignupPage }
|
|
329
184
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
185
|
+
# Simple 404 page
|
|
186
|
+
cl import from .pages.notFound { NotFound }
|
|
187
|
+
|
|
188
|
+
# Navigation component with active link styling and auth
|
|
189
|
+
cl import from .components.navigation { Navigation }
|
|
190
|
+
|
|
191
|
+
# Page showing nested imports from different folders
|
|
192
|
+
cl import from .pages.nestedDemo { NestedImportsDemo }
|
|
193
|
+
|
|
194
|
+
cl import from .pages.FeaturesTest { FeaturesTest }
|
|
195
|
+
|
|
196
|
+
cl import from .pages.LandingPage { LandingPage }
|
|
197
|
+
|
|
198
|
+
cl import from .pages.BudgetPlanner { BudgetPlanner }
|
|
199
|
+
|
|
200
|
+
# Context provider
|
|
201
|
+
cl import from .context.BudgetContext { BudgetProvider }
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
# TypeScript component import
|
|
206
|
+
cl import from ".components/Card.tsx" { Card }
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
# Main app wrapped in Router (same API as with-router/ example)
|
|
210
|
+
cl {
|
|
211
|
+
def:pub HomePage -> any {
|
|
212
|
+
# Check if user is logged in, redirect if not
|
|
213
|
+
if not jacIsLoggedIn() {
|
|
214
|
+
return <Navigate to="/login" />;
|
|
215
|
+
}
|
|
216
|
+
has count: int = 0;
|
|
217
|
+
has pingResult: str = "";
|
|
218
|
+
has serverMessage: str = "";
|
|
219
|
+
has lastTodoMessage: str = "";
|
|
220
|
+
|
|
221
|
+
useEffect(
|
|
222
|
+
lambda -> None{ console.log("Home count changed: ", count);} , [count]
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
# Call simple backend walkers
|
|
226
|
+
async def handlePing -> None {
|
|
227
|
+
result = root spawn ping_server();
|
|
228
|
+
if result.reports and result.reports.length > 0 {
|
|
229
|
+
pingResult = result.reports[0][0];
|
|
336
230
|
}
|
|
231
|
+
}
|
|
337
232
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
}
|
|
233
|
+
async def loadServerMessage -> None {
|
|
234
|
+
result = root spawn get_server_message();
|
|
235
|
+
if result.reports and result.reports.length > 0 {
|
|
236
|
+
serverMessage = result.reports[0][0];
|
|
343
237
|
}
|
|
238
|
+
}
|
|
344
239
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
}
|
|
240
|
+
# Create a sample Todo node in the graph with a hardcoded payload
|
|
241
|
+
async def handleCreateSampleTodo -> None {
|
|
242
|
+
result = root spawn create_todo(text="Sample todo from all-in-one app");
|
|
243
|
+
if result.reports and result.reports.length > 0 {
|
|
244
|
+
todo = result.reports[0][0];
|
|
245
|
+
lastTodoMessage = "Created Todo: " + todo.text;
|
|
246
|
+
console.log("Created Todo node:", todo);
|
|
353
247
|
}
|
|
248
|
+
}
|
|
354
249
|
|
|
355
|
-
|
|
250
|
+
useEffect(lambda -> None{ loadServerMessage();} , []);
|
|
356
251
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
252
|
+
# Initialize a Web Worker and handle message-based communication
|
|
253
|
+
workerRef = useRef(None);
|
|
254
|
+
has message: str = "";
|
|
360
255
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
};
|
|
367
|
-
return (lambda -> None {
|
|
368
|
-
workerRef.current.terminate();
|
|
369
|
-
});
|
|
370
|
-
}, []);
|
|
371
|
-
handleClick = lambda -> None {
|
|
372
|
-
workerRef.current.postMessage("");
|
|
256
|
+
useEffect(lambda -> None {
|
|
257
|
+
workerRef.current = Reflect.construct(Worker, ["/workers/worker.js"]);
|
|
258
|
+
workerRef.current.onmessage = lambda event: any -> None {
|
|
259
|
+
console.log("Message received from worker:", event.data);
|
|
260
|
+
message = event.data;
|
|
373
261
|
};
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
262
|
+
return (lambda -> None {
|
|
263
|
+
workerRef.current.terminate();
|
|
264
|
+
});
|
|
265
|
+
}, []);
|
|
266
|
+
handleClick = lambda -> None {
|
|
267
|
+
workerRef.current.postMessage("");
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
return <div
|
|
271
|
+
style={{
|
|
272
|
+
"padding": "2rem",
|
|
273
|
+
"fontFamily": "system-ui, -apple-system, sans-serif"
|
|
274
|
+
}}
|
|
275
|
+
>
|
|
276
|
+
<h1>
|
|
277
|
+
🍔 Router + Styling + Assets Demo
|
|
278
|
+
</h1>
|
|
279
|
+
<p>
|
|
280
|
+
This home page combines
|
|
281
|
+
{" "}
|
|
282
|
+
<strong>
|
|
283
|
+
React Router,
|
|
284
|
+
</strong>
|
|
285
|
+
{" "}
|
|
286
|
+
<strong>
|
|
287
|
+
pure CSS styling,
|
|
288
|
+
</strong>
|
|
289
|
+
{" "}
|
|
290
|
+
<strong>
|
|
291
|
+
static assets
|
|
292
|
+
</strong>
|
|
293
|
+
{" "}
|
|
294
|
+
and
|
|
295
|
+
{" "}
|
|
296
|
+
<strong>
|
|
297
|
+
nested folder imports
|
|
298
|
+
</strong>
|
|
299
|
+
</p>
|
|
300
|
+
<div className="container">
|
|
301
|
+
<h2
|
|
302
|
+
style={{
|
|
303
|
+
"color": "white",
|
|
304
|
+
"textShadow": "2px 2px 4px rgba(0,0,0,0.6)"
|
|
305
|
+
}}
|
|
306
|
+
>
|
|
307
|
+
CSS Background Image
|
|
308
|
+
</h2>
|
|
309
|
+
<p
|
|
310
|
+
style={{
|
|
311
|
+
"color": "white",
|
|
312
|
+
"maxWidth": "480px",
|
|
313
|
+
"textShadow": "1px 1px 3px rgba(0,0,0,0.7)"
|
|
314
|
+
}}
|
|
315
|
+
>
|
|
316
|
+
This section uses the burger image as a background via CSS, just like the
|
|
317
|
+
{" "}
|
|
318
|
+
<code>
|
|
319
|
+
asset-serving/css-with-image
|
|
320
|
+
</code>
|
|
321
|
+
{" "}
|
|
322
|
+
example.
|
|
323
|
+
</p>
|
|
324
|
+
</div>
|
|
325
|
+
<Card
|
|
326
|
+
title="TypeScript Card Component"
|
|
327
|
+
description="This card is built with TypeScript and demonstrates type-safe component usage in Jac"
|
|
328
|
+
variant="highlighted"
|
|
380
329
|
>
|
|
381
|
-
<
|
|
382
|
-
|
|
383
|
-
|
|
330
|
+
<p
|
|
331
|
+
style={{"margin": "0.5rem 0", "color": "#374151"}}
|
|
332
|
+
>
|
|
333
|
+
This is a TypeScript component imported and used in Jac code!
|
|
334
|
+
</p>
|
|
335
|
+
</Card>
|
|
336
|
+
<div className="card">
|
|
337
|
+
<h3>
|
|
338
|
+
Direct <img> asset
|
|
339
|
+
</h3>
|
|
340
|
+
<img
|
|
341
|
+
src="/static/assets/burger.png"
|
|
342
|
+
alt="Burger asset served by Jac"
|
|
343
|
+
className="burgerImage"
|
|
344
|
+
/>
|
|
345
|
+
<p
|
|
346
|
+
style={{"marginTop": "0.75rem", "color": "#555"}}
|
|
347
|
+
>
|
|
348
|
+
This image is served from the project
|
|
349
|
+
{" "}
|
|
350
|
+
<code>
|
|
351
|
+
assets/
|
|
352
|
+
</code>
|
|
353
|
+
{" "}
|
|
354
|
+
folder using the
|
|
355
|
+
{" "}
|
|
356
|
+
<code>
|
|
357
|
+
/static/assets/
|
|
358
|
+
</code>
|
|
359
|
+
{" "}
|
|
360
|
+
path.
|
|
361
|
+
</p>
|
|
362
|
+
</div>
|
|
363
|
+
<div
|
|
364
|
+
style={{"marginTop": "2rem"}}
|
|
365
|
+
>
|
|
366
|
+
<h3>
|
|
367
|
+
Counter with pure CSS classes
|
|
368
|
+
</h3>
|
|
384
369
|
<p>
|
|
385
|
-
|
|
370
|
+
You've clicked the burger
|
|
386
371
|
{" "}
|
|
387
372
|
<strong>
|
|
388
|
-
|
|
373
|
+
{count}
|
|
389
374
|
</strong>
|
|
390
375
|
{" "}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
376
|
+
times.
|
|
377
|
+
</p>
|
|
378
|
+
<button
|
|
379
|
+
onClick={lambda e: any -> None{ count = count + 1;} }
|
|
380
|
+
style={{
|
|
381
|
+
"padding": "0.6rem 1.4rem",
|
|
382
|
+
"fontSize": "1rem",
|
|
383
|
+
"backgroundColor": "#ff6b35",
|
|
384
|
+
"color": "white",
|
|
385
|
+
"border": "none",
|
|
386
|
+
"borderRadius": "6px",
|
|
387
|
+
"cursor": "pointer",
|
|
388
|
+
"boxShadow": "0 2px 4px rgba(0,0,0,0.2)"
|
|
389
|
+
}}
|
|
390
|
+
>
|
|
391
|
+
Click the Burger! 🍔
|
|
392
|
+
</button>
|
|
393
|
+
</div>
|
|
394
|
+
<div
|
|
395
|
+
style={{"marginTop": "2rem"}}
|
|
396
|
+
>
|
|
397
|
+
<h3>
|
|
398
|
+
Backend Walkers
|
|
399
|
+
</h3>
|
|
400
|
+
<p>
|
|
401
|
+
Basic example walkers:
|
|
394
402
|
{" "}
|
|
395
|
-
<
|
|
396
|
-
|
|
397
|
-
</
|
|
403
|
+
<code>
|
|
404
|
+
ping_server
|
|
405
|
+
</code>
|
|
398
406
|
{" "}
|
|
399
407
|
and
|
|
400
408
|
{" "}
|
|
401
|
-
<
|
|
402
|
-
|
|
403
|
-
</
|
|
409
|
+
<code>
|
|
410
|
+
get_server_message
|
|
411
|
+
</code>
|
|
404
412
|
</p>
|
|
405
|
-
<
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
"color": "white",
|
|
417
|
-
"maxWidth": "480px",
|
|
418
|
-
"textShadow": "1px 1px 3px rgba(0,0,0,0.7)"
|
|
419
|
-
}}
|
|
420
|
-
>
|
|
421
|
-
This section uses the burger image as a background via CSS, just like the
|
|
422
|
-
{" "}
|
|
423
|
-
<code>
|
|
424
|
-
asset-serving/css-with-image
|
|
425
|
-
</code>
|
|
426
|
-
{" "}
|
|
427
|
-
example.
|
|
428
|
-
</p>
|
|
429
|
-
</div>
|
|
430
|
-
<Card
|
|
431
|
-
title="TypeScript Card Component"
|
|
432
|
-
description="This card is built with TypeScript and demonstrates type-safe component usage in Jac"
|
|
433
|
-
variant="highlighted"
|
|
413
|
+
<button
|
|
414
|
+
onClick={lambda e: any -> None{ handlePing();} }
|
|
415
|
+
style={{
|
|
416
|
+
"padding": "0.5rem 1.2rem",
|
|
417
|
+
"marginRight": "0.75rem",
|
|
418
|
+
"backgroundColor": "#3b82f6",
|
|
419
|
+
"color": "white",
|
|
420
|
+
"border": "none",
|
|
421
|
+
"borderRadius": "6px",
|
|
422
|
+
"cursor": "pointer"
|
|
423
|
+
}}
|
|
434
424
|
>
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
alt="Burger asset served by Jac"
|
|
448
|
-
className="burgerImage"
|
|
449
|
-
/>
|
|
450
|
-
<p
|
|
451
|
-
style={{"marginTop": "0.75rem", "color": "#555"}}
|
|
452
|
-
>
|
|
453
|
-
This image is served from the project
|
|
454
|
-
{" "}
|
|
455
|
-
<code>
|
|
456
|
-
assets/
|
|
457
|
-
</code>
|
|
458
|
-
{" "}
|
|
459
|
-
folder using the
|
|
460
|
-
{" "}
|
|
461
|
-
<code>
|
|
462
|
-
/static/assets/
|
|
463
|
-
</code>
|
|
464
|
-
{" "}
|
|
465
|
-
path.
|
|
466
|
-
</p>
|
|
467
|
-
</div>
|
|
468
|
-
<div
|
|
469
|
-
style={{"marginTop": "2rem"}}
|
|
425
|
+
Ping Backend
|
|
426
|
+
</button>
|
|
427
|
+
<button
|
|
428
|
+
onClick={lambda e: any -> None{ handleCreateSampleTodo();} }
|
|
429
|
+
style={{
|
|
430
|
+
"padding": "0.5rem 1.2rem",
|
|
431
|
+
"backgroundColor": "#10b981",
|
|
432
|
+
"color": "white",
|
|
433
|
+
"border": "none",
|
|
434
|
+
"borderRadius": "6px",
|
|
435
|
+
"cursor": "pointer"
|
|
436
|
+
}}
|
|
470
437
|
>
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
{" "}
|
|
477
|
-
<strong>
|
|
478
|
-
{count}
|
|
479
|
-
</strong>
|
|
480
|
-
{" "}
|
|
481
|
-
times.
|
|
482
|
-
</p>
|
|
483
|
-
<button
|
|
484
|
-
onClick={lambda e: any -> None{ setCount(count + 1);} }
|
|
485
|
-
style={{
|
|
486
|
-
"padding": "0.6rem 1.4rem",
|
|
487
|
-
"fontSize": "1rem",
|
|
488
|
-
"backgroundColor": "#ff6b35",
|
|
489
|
-
"color": "white",
|
|
490
|
-
"border": "none",
|
|
491
|
-
"borderRadius": "6px",
|
|
492
|
-
"cursor": "pointer",
|
|
493
|
-
"boxShadow": "0 2px 4px rgba(0,0,0,0.2)"
|
|
494
|
-
}}
|
|
438
|
+
Create Sample Todo
|
|
439
|
+
</button>
|
|
440
|
+
{pingResult
|
|
441
|
+
and (
|
|
442
|
+
<span
|
|
443
|
+
style={{"marginLeft": "0.5rem", "color": "#374151"}}
|
|
495
444
|
>
|
|
496
|
-
|
|
497
|
-
</button>
|
|
498
|
-
</div>
|
|
499
|
-
<div
|
|
500
|
-
style={{"marginTop": "2rem"}}
|
|
501
|
-
>
|
|
502
|
-
<h3>
|
|
503
|
-
Backend Walkers
|
|
504
|
-
</h3>
|
|
505
|
-
<p>
|
|
506
|
-
Basic example walkers:
|
|
507
|
-
{" "}
|
|
508
|
-
<code>
|
|
509
|
-
ping_server
|
|
510
|
-
</code>
|
|
511
|
-
{" "}
|
|
512
|
-
and
|
|
445
|
+
Result:
|
|
513
446
|
{" "}
|
|
514
447
|
<code>
|
|
515
|
-
|
|
448
|
+
{pingResult}
|
|
516
449
|
</code>
|
|
517
|
-
</
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
"padding": "0.5rem 1.2rem",
|
|
522
|
-
"marginRight": "0.75rem",
|
|
523
|
-
"backgroundColor": "#3b82f6",
|
|
524
|
-
"color": "white",
|
|
525
|
-
"border": "none",
|
|
526
|
-
"borderRadius": "6px",
|
|
527
|
-
"cursor": "pointer"
|
|
528
|
-
}}
|
|
529
|
-
>
|
|
530
|
-
Ping Backend
|
|
531
|
-
</button>
|
|
532
|
-
<button
|
|
533
|
-
onClick={lambda e: any -> None{ handleCreateSampleTodo();} }
|
|
534
|
-
style={{
|
|
535
|
-
"padding": "0.5rem 1.2rem",
|
|
536
|
-
"backgroundColor": "#10b981",
|
|
537
|
-
"color": "white",
|
|
538
|
-
"border": "none",
|
|
539
|
-
"borderRadius": "6px",
|
|
540
|
-
"cursor": "pointer"
|
|
541
|
-
}}
|
|
542
|
-
>
|
|
543
|
-
Create Sample Todo
|
|
544
|
-
</button>
|
|
545
|
-
{pingResult
|
|
546
|
-
and (
|
|
547
|
-
<span
|
|
548
|
-
style={{"marginLeft": "0.5rem", "color": "#374151"}}
|
|
549
|
-
>
|
|
550
|
-
Result:
|
|
551
|
-
{" "}
|
|
552
|
-
<code>
|
|
553
|
-
{pingResult}
|
|
554
|
-
</code>
|
|
555
|
-
</span>
|
|
556
|
-
)}
|
|
557
|
-
{serverMessage
|
|
558
|
-
and (
|
|
559
|
-
<p
|
|
560
|
-
style={{"marginTop": "0.75rem", "color": "#374151"}}
|
|
561
|
-
>
|
|
562
|
-
Message:
|
|
563
|
-
{" "}
|
|
564
|
-
<code>
|
|
565
|
-
{serverMessage}
|
|
566
|
-
</code>
|
|
567
|
-
</p>
|
|
568
|
-
)}
|
|
569
|
-
{lastTodoMessage
|
|
570
|
-
and (
|
|
571
|
-
<p
|
|
572
|
-
style={{"marginTop": "0.5rem", "color": "#111827"}}
|
|
573
|
-
>
|
|
574
|
-
{lastTodoMessage}
|
|
575
|
-
</p>
|
|
576
|
-
)}
|
|
577
|
-
</div>
|
|
578
|
-
<div
|
|
579
|
-
style={{"marginTop": "2rem"}}
|
|
580
|
-
>
|
|
581
|
-
<h3>
|
|
582
|
-
Web Worker
|
|
583
|
-
</h3>
|
|
584
|
-
<p>
|
|
585
|
-
This demonstrates how to communicate with a
|
|
586
|
-
{" "}
|
|
587
|
-
<strong>Python backend worker</strong>
|
|
588
|
-
{" "}
|
|
589
|
-
using Web Workers for asynchronous processing.
|
|
590
|
-
</p>
|
|
450
|
+
</span>
|
|
451
|
+
)}
|
|
452
|
+
{serverMessage
|
|
453
|
+
and (
|
|
591
454
|
<p
|
|
592
|
-
style={{"
|
|
455
|
+
style={{"marginTop": "0.75rem", "color": "#374151"}}
|
|
593
456
|
>
|
|
594
|
-
|
|
457
|
+
Message:
|
|
595
458
|
{" "}
|
|
596
459
|
<code>
|
|
597
|
-
|
|
460
|
+
{serverMessage}
|
|
598
461
|
</code>
|
|
599
|
-
{" "}
|
|
600
|
-
— Runs in a separate thread to avoid blocking the UI.
|
|
601
462
|
</p>
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
"backgroundColor": "#d73bf6ff",
|
|
608
|
-
"color": "white",
|
|
609
|
-
"border": "none",
|
|
610
|
-
"borderRadius": "6px",
|
|
611
|
-
"cursor": "pointer"
|
|
612
|
-
}}
|
|
463
|
+
)}
|
|
464
|
+
{lastTodoMessage
|
|
465
|
+
and (
|
|
466
|
+
<p
|
|
467
|
+
style={{"marginTop": "0.5rem", "color": "#111827"}}
|
|
613
468
|
>
|
|
614
|
-
|
|
615
|
-
</
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
)}
|
|
621
|
-
</div>
|
|
622
|
-
</div>;
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
# Page showing nested imports from different folders
|
|
626
|
-
def NestedImportsDemo -> any {
|
|
627
|
-
# Check if user is logged in, redirect if not
|
|
628
|
-
if not jacIsLoggedIn() {
|
|
629
|
-
return <Navigate to="/login" />;
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
return <div
|
|
633
|
-
style={{
|
|
634
|
-
"padding": "2rem",
|
|
635
|
-
"fontFamily": "system-ui, -apple-system, sans-serif"
|
|
636
|
-
}}
|
|
469
|
+
{lastTodoMessage}
|
|
470
|
+
</p>
|
|
471
|
+
)}
|
|
472
|
+
</div>
|
|
473
|
+
<div
|
|
474
|
+
style={{"marginTop": "2rem"}}
|
|
637
475
|
>
|
|
638
|
-
<
|
|
639
|
-
|
|
640
|
-
</
|
|
476
|
+
<h3>
|
|
477
|
+
Web Worker
|
|
478
|
+
</h3>
|
|
641
479
|
<p>
|
|
642
|
-
This
|
|
480
|
+
This demonstrates how to communicate with a
|
|
643
481
|
{" "}
|
|
644
|
-
<
|
|
645
|
-
nested-folders/nested-basic
|
|
646
|
-
</code>
|
|
482
|
+
<strong>Python backend worker</strong>
|
|
647
483
|
{" "}
|
|
648
|
-
|
|
484
|
+
using Web Workers for asynchronous processing.
|
|
485
|
+
</p>
|
|
486
|
+
<p
|
|
487
|
+
style={{"fontSize": "0.9rem", "color": "#666", "marginTop": "0.5rem"}}
|
|
488
|
+
>
|
|
489
|
+
File:
|
|
649
490
|
{" "}
|
|
650
491
|
<code>
|
|
651
|
-
|
|
492
|
+
worker.py
|
|
652
493
|
</code>
|
|
653
494
|
{" "}
|
|
654
|
-
|
|
655
|
-
{" "}
|
|
656
|
-
<code>
|
|
657
|
-
button
|
|
658
|
-
</code>
|
|
659
|
-
</p>
|
|
660
|
-
<p
|
|
661
|
-
style={{"marginTop": "0.75rem"}}
|
|
662
|
-
>
|
|
663
|
-
Both buttons below are rendered via relative imports:
|
|
664
|
-
</p>
|
|
665
|
-
<div
|
|
666
|
-
style={{"display": "flex", "gap": "1rem", "marginTop": "1.5rem"}}
|
|
667
|
-
>
|
|
668
|
-
<CustomButton />
|
|
669
|
-
<CustomButtonRoot />
|
|
670
|
-
</div>
|
|
671
|
-
</div>;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
# Simple 404 page
|
|
675
|
-
def NotFound -> any {
|
|
676
|
-
return <div
|
|
677
|
-
style={{
|
|
678
|
-
"padding": "2rem",
|
|
679
|
-
"textAlign": "center",
|
|
680
|
-
"fontFamily": "system-ui, -apple-system, sans-serif"
|
|
681
|
-
}}
|
|
682
|
-
>
|
|
683
|
-
<h1>
|
|
684
|
-
🔍 404 - Page Not Found
|
|
685
|
-
</h1>
|
|
686
|
-
<p>
|
|
687
|
-
The page you are looking for does not exist.
|
|
495
|
+
— Runs in a separate thread to avoid blocking the UI.
|
|
688
496
|
</p>
|
|
689
|
-
<
|
|
690
|
-
|
|
691
|
-
</Link>
|
|
692
|
-
</div>;
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
# Navigation component with active link styling and auth
|
|
696
|
-
def Navigation -> any {
|
|
697
|
-
location = useLocation();
|
|
698
|
-
isLoggedIn = jacIsLoggedIn();
|
|
699
|
-
navigate = useNavigate();
|
|
700
|
-
|
|
701
|
-
def linkStyle(path: str) -> dict {
|
|
702
|
-
isActive = location.pathname == path;
|
|
703
|
-
return {
|
|
704
|
-
"padding": "0.5rem 1rem",
|
|
705
|
-
"textDecoration": "none",
|
|
706
|
-
"color": "#0066cc" if isActive else "#333",
|
|
707
|
-
"fontWeight": "bold" if isActive else "normal",
|
|
708
|
-
"backgroundColor": "#e3f2fd" if isActive else "transparent",
|
|
709
|
-
"borderRadius": "4px",
|
|
710
|
-
"display": "inline-block"
|
|
711
|
-
};
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
def handleLogout(e: any) -> None {
|
|
715
|
-
e.preventDefault();
|
|
716
|
-
jacLogout();
|
|
717
|
-
navigate("/login");
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
authButtons = None;
|
|
721
|
-
if isLoggedIn {
|
|
722
|
-
authButtons = <button
|
|
723
|
-
onClick={handleLogout}
|
|
497
|
+
<button
|
|
498
|
+
onClick={lambda -> None { handleClick(); }}
|
|
724
499
|
style={{
|
|
725
|
-
"padding": "0.5rem
|
|
726
|
-
"
|
|
727
|
-
"
|
|
500
|
+
"padding": "0.5rem 1.2rem",
|
|
501
|
+
"marginRight": "0.75rem",
|
|
502
|
+
"backgroundColor": "#d73bf6ff",
|
|
503
|
+
"color": "white",
|
|
728
504
|
"border": "none",
|
|
729
|
-
"borderRadius": "
|
|
730
|
-
"cursor": "pointer"
|
|
731
|
-
"fontWeight": "600"
|
|
505
|
+
"borderRadius": "6px",
|
|
506
|
+
"cursor": "pointer"
|
|
732
507
|
}}
|
|
733
508
|
>
|
|
734
|
-
|
|
735
|
-
</button
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
<Link
|
|
745
|
-
to="/signup"
|
|
746
|
-
style={linkStyle("/signup")}
|
|
747
|
-
>
|
|
748
|
-
Sign Up
|
|
749
|
-
</Link>
|
|
750
|
-
</>;
|
|
751
|
-
}
|
|
509
|
+
Call Python Worker
|
|
510
|
+
</button>
|
|
511
|
+
{message && (
|
|
512
|
+
<p style={{ marginTop: "1rem", fontWeight: "bold" }}>
|
|
513
|
+
{message}
|
|
514
|
+
</p>
|
|
515
|
+
)}
|
|
516
|
+
</div>
|
|
517
|
+
</div>;
|
|
518
|
+
}
|
|
752
519
|
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
"marginBottom": "2rem",
|
|
758
|
-
"boxShadow": "0 2px 4px rgba(0,0,0,0.1)"
|
|
759
|
-
}}
|
|
520
|
+
def:pub app -> any {
|
|
521
|
+
return <Router>
|
|
522
|
+
<div
|
|
523
|
+
style={{"fontFamily": "system-ui, -apple-system, sans-serif"}}
|
|
760
524
|
>
|
|
525
|
+
<Navigation />
|
|
761
526
|
<div
|
|
762
527
|
style={{
|
|
763
|
-
"maxWidth": "
|
|
528
|
+
"maxWidth": "960px",
|
|
764
529
|
"margin": "0 auto",
|
|
765
|
-
"
|
|
766
|
-
"gap": "1rem",
|
|
767
|
-
"alignItems": "center",
|
|
768
|
-
"justifyContent": "space-between"
|
|
530
|
+
"padding": "0 1rem 3rem 1rem"
|
|
769
531
|
}}
|
|
770
532
|
>
|
|
771
|
-
<
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
<Navigation />
|
|
808
|
-
<div
|
|
809
|
-
style={{
|
|
810
|
-
"maxWidth": "960px",
|
|
811
|
-
"margin": "0 auto",
|
|
812
|
-
"padding": "0 1rem 3rem 1rem"
|
|
813
|
-
}}
|
|
814
|
-
>
|
|
815
|
-
<Routes>
|
|
816
|
-
<Route
|
|
817
|
-
path="/"
|
|
818
|
-
element={<Home />}
|
|
819
|
-
/>
|
|
820
|
-
<Route
|
|
821
|
-
path="/login"
|
|
822
|
-
element={<LoginPage />}
|
|
823
|
-
/>
|
|
824
|
-
<Route
|
|
825
|
-
path="/signup"
|
|
826
|
-
element={<SignupPage />}
|
|
827
|
-
/>
|
|
828
|
-
<Route
|
|
829
|
-
path="/nested"
|
|
830
|
-
element={<NestedImportsDemo />}
|
|
831
|
-
/>
|
|
832
|
-
<Route
|
|
833
|
-
path="*"
|
|
834
|
-
element={<NotFound />}
|
|
835
|
-
/>
|
|
836
|
-
</Routes>
|
|
837
|
-
</div>
|
|
533
|
+
<Routes>
|
|
534
|
+
<Route
|
|
535
|
+
path="/"
|
|
536
|
+
element={<HomePage />}
|
|
537
|
+
/>
|
|
538
|
+
<Route
|
|
539
|
+
path="/login"
|
|
540
|
+
element={<LoginPage />}
|
|
541
|
+
/>
|
|
542
|
+
<Route
|
|
543
|
+
path="/signup"
|
|
544
|
+
element={<SignupPage />}
|
|
545
|
+
/>
|
|
546
|
+
<Route
|
|
547
|
+
path="/nested"
|
|
548
|
+
element={<NestedImportsDemo />}
|
|
549
|
+
/>
|
|
550
|
+
<Route
|
|
551
|
+
path="/features-test"
|
|
552
|
+
element={<FeaturesTest />}
|
|
553
|
+
/>
|
|
554
|
+
<Route
|
|
555
|
+
path="/landing"
|
|
556
|
+
element={<LandingPage />}
|
|
557
|
+
/>
|
|
558
|
+
<Route
|
|
559
|
+
path="/budget-planner"
|
|
560
|
+
element={<BudgetProvider>
|
|
561
|
+
<BudgetPlanner />
|
|
562
|
+
</BudgetProvider>}
|
|
563
|
+
/>
|
|
564
|
+
<Route
|
|
565
|
+
path="*"
|
|
566
|
+
element={<NotFound />}
|
|
567
|
+
/>
|
|
568
|
+
</Routes>
|
|
838
569
|
</div>
|
|
839
|
-
</
|
|
840
|
-
|
|
570
|
+
</div>
|
|
571
|
+
</Router>;
|
|
572
|
+
}
|
|
841
573
|
}
|