jac-client 0.2.8__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/button.jac +4 -3
  2. jac_client/examples/all-in-one/components/CategoryFilter.jac +36 -24
  3. jac_client/examples/all-in-one/components/Header.jac +12 -8
  4. jac_client/examples/all-in-one/components/ProfitOverview.jac +49 -35
  5. jac_client/examples/all-in-one/components/Summary.jac +59 -36
  6. jac_client/examples/all-in-one/components/TransactionForm.jac +142 -112
  7. jac_client/examples/all-in-one/components/TransactionItem.jac +37 -30
  8. jac_client/examples/all-in-one/components/TransactionList.jac +33 -26
  9. jac_client/examples/all-in-one/components/button.jac +4 -3
  10. jac_client/examples/all-in-one/components/navigation.jac +111 -117
  11. jac_client/examples/all-in-one/constants/categories.jac +23 -24
  12. jac_client/examples/all-in-one/constants/clients.jac +7 -8
  13. jac_client/examples/all-in-one/context/BudgetContext.jac +9 -6
  14. jac_client/examples/all-in-one/hooks/useBudget.jac +18 -12
  15. jac_client/examples/all-in-one/hooks/useLocalStorage.jac +14 -13
  16. jac_client/examples/all-in-one/main.jac +542 -0
  17. jac_client/examples/all-in-one/pages/BudgetPlanner.jac +26 -12
  18. jac_client/examples/all-in-one/pages/FeaturesTest.jac +43 -12
  19. jac_client/examples/all-in-one/pages/LandingPage.jac +113 -90
  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 +114 -119
  23. jac_client/examples/all-in-one/pages/nestedDemo.jac +44 -51
  24. jac_client/examples/all-in-one/pages/notFound.jac +15 -21
  25. jac_client/examples/all-in-one/pages/signupPage.jac +113 -119
  26. jac_client/examples/all-in-one/utils/formatters.jac +5 -8
  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 +491 -411
  54. jac_client/plugin/client.jac +25 -0
  55. jac_client/plugin/client_runtime.cl.jac +10 -4
  56. jac_client/plugin/impl/client.impl.jac +96 -55
  57. jac_client/plugin/impl/client_runtime.impl.jac +155 -1
  58. jac_client/plugin/plugin_config.jac +211 -29
  59. jac_client/plugin/src/__init__.jac +0 -2
  60. jac_client/plugin/src/compiler.jac +0 -1
  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 +49 -17
  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 +191 -64
  69. jac_client/plugin/src/targets/desktop/sidecar/main.py +144 -0
  70. jac_client/plugin/src/targets/desktop_target.jac +37 -0
  71. jac_client/plugin/src/targets/impl/desktop_target.impl.jac +2347 -0
  72. jac_client/plugin/src/targets/impl/registry.impl.jac +64 -0
  73. jac_client/plugin/src/targets/impl/web_target.impl.jac +157 -0
  74. jac_client/plugin/src/targets/register.jac +21 -0
  75. jac_client/plugin/src/targets/registry.jac +87 -0
  76. jac_client/plugin/src/targets/web_target.jac +35 -0
  77. jac_client/plugin/src/vite_bundler.jac +6 -0
  78. jac_client/plugin/utils/__init__.jac +3 -0
  79. jac_client/plugin/utils/bun_installer.jac +16 -0
  80. jac_client/plugin/utils/impl/bun_installer.impl.jac +99 -0
  81. jac_client/templates/client.jacpack +72 -0
  82. jac_client/templates/fullstack.jacpack +61 -0
  83. jac_client/tests/conftest.py +103 -47
  84. jac_client/tests/fixtures/spawn_test/app.jac +49 -52
  85. jac_client/tests/fixtures/with-ts/app.jac +27 -27
  86. jac_client/tests/test_cli.py +182 -71
  87. jac_client/tests/test_e2e.py +232 -0
  88. jac_client/tests/test_helpers.py +58 -0
  89. jac_client/tests/test_it.py +91 -135
  90. jac_client/tests/test_it_desktop.py +891 -0
  91. {jac_client-0.2.8.dist-info → jac_client-0.2.11.dist-info}/METADATA +6 -6
  92. jac_client-0.2.11.dist-info/RECORD +113 -0
  93. {jac_client-0.2.8.dist-info → jac_client-0.2.11.dist-info}/WHEEL +1 -1
  94. jac_client/examples/all-in-one/app.jac +0 -573
  95. jac_client/examples/all-in-one/pages/BudgetPlanner.cl.jac +0 -70
  96. jac_client/examples/all-in-one/pages/FeaturesTest.cl.jac +0 -552
  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 -371
  102. jac_client/examples/basic-auth-with-router/src/app.jac +0 -464
  103. jac_client/examples/basic-full-stack/src/app.jac +0 -359
  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 -89
  117. jac_client-0.2.8.dist-info/RECORD +0 -97
  118. {jac_client-0.2.8.dist-info → jac_client-0.2.11.dist-info}/entry_points.txt +0 -0
  119. {jac_client-0.2.8.dist-info → jac_client-0.2.11.dist-info}/top_level.txt +0 -0
@@ -1,719 +0,0 @@
1
- import datetime;
2
- cl import from lodash { * as _ }
3
- cl import from antd { Button, Input, Card, Typography, Space }
4
- cl import from pluralize { default as pluralize }
5
- cl import from 'react-animated-components' { Rotate }
6
-
7
- node Profile {
8
- has username: str = "";
9
-
10
- can update with update_profile entry {
11
- self.username = visitor.new_username;
12
- report self ;
13
- }
14
-
15
- can get with get_profile entry {
16
- follwers = [
17
- {"id": jid(i), "username": i.username} for i in [self-->(`?Profile)]
18
- ];
19
- report {"user": self, "followers": follwers} ;
20
- }
21
-
22
- can follow with follow_request entry {
23
- current_profile = [root-->(`?Profile)];
24
- current_profile[0] +>: Follow() :+> self;
25
- report self ;
26
- }
27
-
28
- can un_follow with un_follow_request entry {
29
- current_profile = [root-->(`?Profile)];
30
- follow_edge = [edge current_profile[0]->:Follow:->self];
31
- del follow_edge[0] ;
32
- report self ;
33
- }
34
- }
35
-
36
- obj TweetInfo {
37
- has username: str,
38
- id: str,
39
- content: str,
40
- embedding: list,
41
- likes: list,
42
- comments: list;
43
- }
44
-
45
- node Tweet {
46
- has content: str,
47
- embedding: list,
48
- created_at: str = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S");
49
-
50
- can update with update_tweet exit {
51
- self.content = visitor.updated_content;
52
- report self ;
53
- }
54
-
55
- can delete with remove_tweet exit {
56
- del self ;
57
- disengage;
58
- }
59
-
60
- can like_tweet with like_tweet entry {
61
- current_profile = [root-->(`?Profile)];
62
- self +>: Like() :+> current_profile[0];
63
- report self ;
64
- }
65
-
66
- can remove_like with remove_like entry {
67
- current_profile = [root-->(`?Profile)];
68
- like_edge = [edge self->:Like:->current_profile[0]];
69
- del like_edge[0] ;
70
- report self ;
71
- }
72
-
73
- can comment with comment_tweet entry {
74
- current_profile = [root-->(`?Profile)];
75
- comment_node = current_profile[0] +>: Post() :+> Comment(
76
- content=visitor.content
77
- );
78
- grant(comment_node[0], level=ConnectPerm);
79
- self ++> comment_node[0];
80
- report comment_node[0] ;
81
- }
82
-
83
- def get_info -> TweetInfo {
84
- return TweetInfo(
85
- username=[self<-:Post:<-][0].username,
86
- id=jid(self),
87
- content=self.content,
88
- embedding=self.embedding,
89
- likes=[i.username for i in [self->:Like:->]],
90
- comments=[
91
- {
92
- "username": [i<--(`?Profile)][0].username,
93
- "id": jid(i),
94
- "content": i.content
95
- } for i in [self-->(`?Comment)]
96
- ]
97
- );
98
- }
99
-
100
- can get with load_feed entry {
101
- tweet_info = self.get_info();
102
- visitor.results.append({"Tweet_Info": tweet_info});
103
- }
104
- }
105
-
106
- node Comment {
107
- has content: str;
108
-
109
- can update with update_comment entry {
110
- self.content = visitor.updated_content;
111
- report self ;
112
- }
113
-
114
- can delete with remove_comment entry {
115
- del self ;
116
- disengage;
117
- }
118
- }
119
-
120
- edge Follow {}
121
-
122
- edge Like {}
123
-
124
- edge Post {}
125
-
126
- walker visit_profile {
127
- can visit_profile with `root entry {
128
- visit [-->(`?Profile)] else {
129
- new_profile = here ++> Profile();
130
- grant(new_profile[0], level=ConnectPerm);
131
- visit new_profile;
132
- }
133
- }
134
- }
135
-
136
- walker update_profile(visit_profile) {
137
- has new_username: str;
138
- }
139
-
140
- walker get_profile(visit_profile) {}
141
-
142
- walker load_user_profiles {
143
- can load_profiles with `root entry {
144
- self.profiles: list = [];
145
- for each_root in allroots() {
146
- profile = [each_root-->(`?Profile)][0];
147
- self.profiles.append({"name": profile.username, "id": jid(profile)});
148
- }
149
- }
150
-
151
- can report_profiles with exit {
152
- report self.profiles ;
153
- }
154
- }
155
-
156
- walker follow_request {}
157
-
158
- walker un_follow_request {}
159
-
160
- walker create_tweet(visit_profile) {
161
- has content: str;
162
-
163
- can tweet with Profile entry {
164
- tweet_node = here +>: Post() :+> Tweet(content=self.content, embedding=[]);
165
- grant(tweet_node[0], level=ConnectPerm);
166
- report tweet_node ;
167
- }
168
- }
169
-
170
- walker update_tweet {
171
- has updated_content: str;
172
- }
173
-
174
- walker remove_tweet {}
175
-
176
- walker like_tweet {}
177
-
178
- walker remove_like {}
179
-
180
- walker comment_tweet {
181
- has content: str;
182
- }
183
-
184
- walker update_comment {
185
- has updated_content: str;
186
- }
187
-
188
- walker remove_comment {}
189
-
190
- walker load_feed(visit_profile) {
191
- has search_query: str = "",
192
- results: list = [];
193
-
194
- can load with Profile entry {
195
- visit [-->(`?Tweet)];
196
- for user_node in [->:Follow:->(`?Profile)] {
197
- visit [user_node-->(`?Tweet)];
198
- }
199
- }
200
-
201
- can report_feed with exit {
202
- report self.results ;
203
- }
204
- }
205
-
206
- # Client-side UI Components (marked with 'cl' for browser execution)
207
- # ===================================================================
208
-
209
- # Manual routing functions removed - now using reactive createRouter
210
-
211
- # Shared data model for client/server
212
- cl obj ClientTweet {
213
- has username: str = "",
214
- id: str = "",
215
- content: str = "",
216
- likes: list = [],
217
- comments: list = [];
218
- }
219
-
220
- cl obj ClientProfile {
221
- has username: str = "",
222
- id: str = "";
223
- }
224
-
225
- # UI Components - Render a single tweet card
226
- cl def TweetCard(tweet: ClientTweet) -> any {
227
- return <div
228
- class="tweet-card"
229
- style={{
230
- "border": "1px solid #e1e8ed",
231
- "padding": "15px",
232
- "margin": "10px 0",
233
- "borderRadius": "8px"
234
- }}
235
- >
236
- <div
237
- class="tweet-header"
238
- style={{"fontWeight": "bold", "marginBottom": "80px"}}
239
- >
240
- @{tweet.username}
241
- </div>
242
- <div
243
- class="tweet-content"
244
- style={{"marginBottom": "12px"}}
245
- >
246
- {tweet.content}
247
- </div>
248
- <div
249
- class="tweet-actions"
250
- style={{"display": "flex", "gap": "15px"}}
251
- >
252
- <button
253
- onclick={like_tweet_action(tweet.id)}
254
- style={{"padding": "5px 10px", "cursor": "pointer"}}
255
- >
256
- Like ({tweet.likes.length})
257
- </button>
258
- <button
259
- style={{"padding": "5px 10px"}}
260
- >
261
- Comment ({tweet.comments.length})
262
- </button>
263
- </div>
264
- </div>;
265
- }
266
-
267
- # Handle liking a tweet - calls server walker directly
268
- # The compiler automatically transforms this to __jacCallFunction at compile time!
269
- cl async def like_tweet_action(
270
- tweet_id: str
271
- ) -> any {
272
- try {
273
- result = await like_tweet(tweet_id);
274
- print("Tweet liked:", result);
275
- # Re-render feed after like
276
- window.location.reload();
277
- } except Exception as e {
278
- print("Error liking tweet:", e);
279
- }
280
- }
281
-
282
- # Render the main feed view
283
- cl def FeedView(tweets: list) -> any {
284
- return <div
285
- class="feed-container"
286
- style={{"maxWidth": "600px", "margin": "0 auto", "fontFamily": "sans-serif"}}
287
- >
288
- <div
289
- class="feed-header"
290
- style={{"padding": "20px", "borderBottom": "1px solid #e1e8ed"}}
291
- >
292
- <h1
293
- style={{"margin": "0"}}
294
- >
295
- LittleX Feed
296
- </h1>
297
- </div>
298
- <div class="feed-content">
299
- {[TweetCard(tweet) for tweet in tweets]}
300
- </div>
301
- </div>;
302
- }
303
-
304
- # Render login form
305
- cl def LoginForm -> any {
306
- suggestions = ['good luck', 'have fun', 'enjoy the ride'];
307
- randomSuggestion = _.sample(suggestions);
308
- result = "Good luck with your journey!";
309
- return <Card
310
- title="Login to LittleX"
311
- style={{"maxWidth": "400px", "margin": "50px auto"}}
312
- >
313
- <Card.Meta
314
- title={randomSuggestion}
315
- description={result}
316
- />
317
- <form
318
- onSubmit={handle_login}
319
- >
320
- <div
321
- style={{"marginBottom": "15px"}}
322
- >
323
- <label
324
- style={{"display": "block", "marginBottom": "5px"}}
325
- >
326
- Username:
327
- </label>
328
- <input
329
- type="text"
330
- id="username"
331
- style={{
332
- "width": "100%",
333
- "padding": "8px",
334
- "boxSizing": "border-box"
335
- }}
336
- />
337
- </div>
338
- <div
339
- style={{"marginBottom": "15px"}}
340
- >
341
- <label
342
- style={{"display": "block", "marginBottom": "5px"}}
343
- >
344
- Password:
345
- </label>
346
- <input
347
- type="password"
348
- id="password"
349
- style={{
350
- "width": "100%",
351
- "padding": "8px",
352
- "boxSizing": "border-box"
353
- }}
354
- />
355
- </div>
356
- <Button
357
- htmlType="submit"
358
- style={{
359
- "width": "100%",
360
- "padding": "10px",
361
- "backgroundColor": "#1da1f2",
362
- "color": "white",
363
- "border": "none",
364
- "borderRadius": "4px",
365
- "cursor": "pointer"
366
- }}
367
- >
368
- Login
369
- </Button>
370
- <Button
371
- color="default"
372
- variant="dashed"
373
- >
374
- Dashed
375
- </Button>
376
- <Button
377
- color="default"
378
- variant="filled"
379
- >
380
- Filled
381
- </Button>
382
- <Button
383
- color="default"
384
- variant="text"
385
- >
386
- Text
387
- </Button>
388
- <Button
389
- color="default"
390
- variant="link"
391
- >
392
- Link
393
- </Button>
394
- </form>
395
- <div
396
- style={{"marginTop": "15px", "textAlign": "center"}}
397
- >
398
- <Link href="/signup">
399
- Don't have an account? Sign up
400
- </Link>
401
- </div>
402
- </Card>;
403
- }
404
-
405
- # Handle login form submission
406
- cl async def handle_login(event: any) -> None {
407
- event.preventDefault();
408
- username = document.getElementById("username").value;
409
- password = document.getElementById("password").value;
410
- success = await jacLogin(username, password);
411
- if success {
412
- navigate("/home");
413
- } else {
414
- alert("Login failed. Please try again.");
415
- }
416
- }
417
-
418
- # Render signup form
419
- cl def SignupForm -> any {
420
- return <div
421
- class="signup-container"
422
- style={{
423
- "maxWidth": "400px",
424
- "margin": "50px auto",
425
- "padding": "20px",
426
- "border": "1px solid #e1e8ed",
427
- "borderRadius": "8px",
428
- "fontFamily": "sans-serif"
429
- }}
430
- >
431
- <Typography.Title
432
- level={2}
433
- style={{"marginTop": "0"}}
434
- >
435
- Sign Up for LittleX
436
- </Typography.Title>
437
- <form
438
- onSubmit={handle_signup}
439
- >
440
- <div
441
- style={{"marginBottom": "15px"}}
442
- >
443
- <label
444
- style={{"display": "block", "marginBottom": "5px"}}
445
- >
446
- Username:
447
- </label>
448
- <input
449
- type="text"
450
- id="signup-username"
451
- required
452
- style={{
453
- "width": "100%",
454
- "padding": "8px",
455
- "boxSizing": "border-box"
456
- }}
457
- />
458
- </div>
459
- <div
460
- style={{"marginBottom": "15px"}}
461
- >
462
- <label
463
- style={{"display": "block", "marginBottom": "5px"}}
464
- >
465
- Password:
466
- </label>
467
- <input
468
- type="password"
469
- id="signup-password"
470
- required
471
- style={{
472
- "width": "100%",
473
- "padding": "8px",
474
- "boxSizing": "border-box"
475
- }}
476
- />
477
- </div>
478
- <div
479
- style={{"marginBottom": "15px"}}
480
- >
481
- <label
482
- style={{"display": "block", "marginBottom": "5px"}}
483
- >
484
- Confirm Password:
485
- </label>
486
- <input
487
- type="password"
488
- id="signup-password-confirm"
489
- required
490
- style={{
491
- "width": "100%",
492
- "padding": "8px",
493
- "boxSizing": "border-box"
494
- }}
495
- />
496
- </div>
497
- <button
498
- type="submit"
499
- style={{
500
- "width": "100%",
501
- "padding": "10px",
502
- "backgroundColor": "#1da1f2",
503
- "color": "white",
504
- "border": "none",
505
- "borderRadius": "4px",
506
- "cursor": "pointer"
507
- }}
508
- >
509
- Sign Up
510
- </button>
511
- </form>
512
- <div
513
- style={{"marginTop": "15px", "textAlign": "center"}}
514
- >
515
- <Link href="/login">
516
- Already have an account? Login
517
- </Link>
518
- </div>
519
- </div>;
520
- }
521
-
522
- # Navigation helper functions removed - using Link component and navigate() directly
523
-
524
- # Handle signup form submission
525
- cl async def handle_signup(event: any) -> None {
526
- event.preventDefault();
527
- username = document.getElementById("signup-username").value;
528
- password = document.getElementById("signup-password").value;
529
- password_confirm = document.getElementById("signup-password-confirm").value;
530
- # Client-side validation
531
- if password != password_confirm {
532
- alert("Passwords do not match!");
533
- return;
534
- }
535
- if username.length < 3 {
536
- alert("Username must be at least 3 characters long.");
537
- return;
538
- }
539
- if password.length < 6 {
540
- alert("Password must be at least 6 characters long.");
541
- return;
542
- }
543
- # Use runtime auth function - no need to know about /user/register endpoint!
544
- result = await jacSignup(username, password);
545
- if result["success"] if "success" in result else False {
546
- alert("Account created successfully! Welcome to LittleX!");
547
- navigate("/home");
548
- } else {
549
- alert(result["error"] if "error" in result else "Signup failed");
550
- }
551
- }
552
-
553
- # Handle logout
554
- cl def logout_action -> None {
555
- jacLogout();
556
- navigate("/login");
557
- }
558
-
559
- # Main App component with declarative router
560
- cl def:pub App -> any {
561
- # Create routes array manually (workaround for JS compiler bug with named args)
562
- login_route = {
563
- "path": "/login",
564
- "component": lambda -> any{ return LoginForm(); } ,
565
- "guard": None
566
- };
567
- signup_route = {
568
- "path": "/signup",
569
- "component": lambda -> any{ return SignupForm(); } ,
570
- "guard": None
571
- };
572
- home_route = {
573
- "path": "/home",
574
- "component": lambda -> any{ return HomeView(); } ,
575
- "guard": jacIsLoggedIn
576
- };
577
- profile_route = {
578
- "path": "/profile",
579
- "component": lambda -> any{ return ProfileView(); } ,
580
- "guard": jacIsLoggedIn
581
- };
582
-
583
- routes = [login_route, signup_route, home_route, profile_route];
584
- router = initRouter(routes, "/login");
585
-
586
- # Get current path for navbar
587
- currentPath = router.path();
588
-
589
- return <div class="app-container">
590
- {build_nav_bar(currentPath)}
591
- <Rotate>
592
- <span>
593
- 😂
594
- </span>
595
- </Rotate>
596
- {router.render()}
597
- </div>;
598
- }
599
-
600
- # Helper to build navigation bar
601
- cl def build_nav_bar(route: str) -> any {
602
- if not jacIsLoggedIn() or route == "/login" or route == "/signup" {
603
- return None;
604
- }
605
- return <nav
606
- style={{
607
- "backgroundColor": "#1da1f2",
608
- "padding": "15px",
609
- "marginBottom": "20px"
610
- }}
611
- >
612
- <div
613
- style={{
614
- "maxWidth": "600px",
615
- "margin": "0 auto",
616
- "display": "flex",
617
- "gap": "20px",
618
- "alignItems": "center"
619
- }}
620
- >
621
- <Link href="/home">
622
- <span
623
- style={{
624
- "color": "white",
625
- "textDecoration": "none",
626
- "fontWeight": "bold"
627
- }}
628
- >
629
- Home
630
- </span>
631
- </Link>
632
- <Link href="/profile">
633
- <span
634
- style={{
635
- "color": "white",
636
- "textDecoration": "none",
637
- "fontWeight": "bold"
638
- }}
639
- >
640
- Profile
641
- </span>
642
- </Link>
643
- <button
644
- onClick={logout_action}
645
- style={{
646
- "marginLeft": "auto",
647
- "padding": "5px 15px",
648
- "backgroundColor": "white",
649
- "color": "#1da1f2",
650
- "border": "none",
651
- "borderRadius": "4px",
652
- "cursor": "pointer",
653
- "fontWeight": "bold"
654
- }}
655
- >
656
- Logout
657
- </button>
658
- </div>
659
- </nav>;
660
- }
661
-
662
- # Home view - simplified for testing reactive routing
663
- cl def HomeView -> any {
664
- if not jacIsLoggedIn() {
665
- navigate("/login");
666
- return <div></div>;
667
- }
668
-
669
- return <div
670
- style={{"textAlign": "center", "padding": "50px", "fontFamily": "sans-serif"}}
671
- >
672
- <h1>
673
- Home Feed
674
- </h1>
675
- <p>
676
- Welcome to LittleX! This is the home page.
677
- </p>
678
- <p>
679
- The reactive router is working!
680
- </p>
681
- </div>;
682
- }
683
-
684
- # Profile view
685
- cl def ProfileView -> any {
686
- if not jacIsLoggedIn() {
687
- navigate("/login");
688
- return <div></div>;
689
- }
690
- return <div
691
- class="profile-container"
692
- style={{
693
- "maxWidth": "600px",
694
- "margin": "20px auto",
695
- "padding": "20px",
696
- "fontFamily": "sans-serif"
697
- }}
698
- >
699
- <h1>
700
- Profile
701
- </h1>
702
- <div
703
- style={{
704
- "padding": "15px",
705
- "border": "1px solid #e1e8ed",
706
- "borderRadius": "8px"
707
- }}
708
- >
709
- <p>
710
- Profile information will be displayed here.
711
- </p>
712
- </div>
713
- </div>;
714
- }
715
-
716
- # Main SPA entry point - simplified with reactive routing
717
- cl def:pub jac_app -> any {
718
- return App();
719
- }