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,6 +1,5 @@
1
1
  # Utility functions for formatting
2
2
  # Demonstrates: def:pub exports, string methods
3
-
4
3
  cl {
5
4
  # Format number as currency - demonstrates string methods
6
5
  def:pub formatCurrency(amount: float) -> str {
@@ -12,11 +11,9 @@ cl {
12
11
  # Format ISO date string to readable format
13
12
  def:pub formatDate(dateStr: str) -> str {
14
13
  date = Reflect.construct(Date, [dateStr]);
15
- return date.toLocaleDateString("en-US", {
16
- "month": "short",
17
- "day": "numeric",
18
- "year": "numeric"
19
- });
14
+ return date.toLocaleDateString(
15
+ "en-US", {"month": "short", "day": "numeric", "year": "numeric"}
16
+ );
20
17
  }
21
18
 
22
19
  # Capitalize first letter of string
@@ -39,12 +36,12 @@ cl {
39
36
  def:pub formatCompact(num: float) -> str {
40
37
  if num >= 1000000 {
41
38
  divided = num / 1000000;
42
- rounded = (Math.round(divided * 10) / 10);
39
+ rounded = Math.round(divided * 10) / 10;
43
40
  return (rounded.toString().concat("M"));
44
41
  }
45
42
  if num >= 1000 {
46
43
  divided = num / 1000;
47
- rounded = (Math.round(divided * 10) / 10);
44
+ rounded = Math.round(divided * 10) / 10;
48
45
  return rounded.toString().concat("K");
49
46
  }
50
47
  return Math.round(num).toString();
@@ -0,0 +1,92 @@
1
+ # Pages
2
+ cl import ".styles.css";
3
+
4
+ cl {
5
+ def:pub app -> any {
6
+ has count: int = 0;
7
+
8
+ can with count entry {
9
+ console.log("Count: ", count);
10
+ }
11
+
12
+ return
13
+ <div
14
+ style={{
15
+ padding: "20px",
16
+ textAlign: "center",
17
+ fontFamily: "Arial, sans-serif"
18
+ }}
19
+ >
20
+ <h1>
21
+ 🍔 Burger Counter App
22
+ </h1>
23
+ <img
24
+ src="/static/assets/burger.png"
25
+ alt="Delicious Burger"
26
+ style={{
27
+ width: "200px",
28
+ height: "auto",
29
+ margin: "20px 0",
30
+ borderRadius: "10px",
31
+ boxShadow: "0 4px 8px rgba(0,0,0,0.2)"
32
+ }}
33
+ />
34
+ <p style={{fontSize: "18px", margin: "20px 0"}}>
35
+ You've clicked the burger
36
+ <strong>
37
+ {count}
38
+ </strong>
39
+ times!
40
+ </p>
41
+ <button
42
+ onClick={lambda e: any -> None { count = count + 1;}}
43
+ style={{
44
+ padding: "10px 20px",
45
+ fontSize: "16px",
46
+ backgroundColor: "#ff6b35",
47
+ color: "white",
48
+ border: "none",
49
+ borderRadius: "5px",
50
+ cursor: "pointer",
51
+ boxShadow: "0 2px 4px rgba(0,0,0,0.2)"
52
+ }}
53
+ >
54
+ Click the Burger! 🍔
55
+ </button>
56
+ <h2 style={{marginTop: "40px", marginBottom: "20px"}}>
57
+ CSS Asset Examples
58
+ </h2>
59
+ <div className="container">
60
+ <h3
61
+ style={{
62
+ color: "white",
63
+ textShadow: "2px 2px 4px rgba(0,0,0,0.5)"
64
+ }}
65
+ >
66
+ Background Image Example
67
+ </h3>
68
+ <p
69
+ style={{
70
+ color: "white",
71
+ textShadow: "2px 2px 4px rgba(0,0,0,0.5)"
72
+ }}
73
+ >
74
+ This container uses the burger image as a background via CSS
75
+ </p>
76
+ </div>
77
+ <div className="card">
78
+ <h3>
79
+ Image in Card
80
+ </h3>
81
+ <img
82
+ src="/static/assets/burger.png"
83
+ alt="Burger in Card"
84
+ className="burgerImage"
85
+ />
86
+ <p style={{marginTop: "15px", color: "#666"}}>
87
+ This image is displayed within a styled card using CSS classes
88
+ </p>
89
+ </div>
90
+ </div>;
91
+ }
92
+ }
@@ -0,0 +1,56 @@
1
+ # Pages
2
+ cl {
3
+ def:pub app -> any {
4
+ has count: int = 0;
5
+
6
+ can with count entry {
7
+ console.log("Count: ", count);
8
+ }
9
+
10
+ return
11
+ <div
12
+ style={{
13
+ padding: "20px",
14
+ textAlign: "center",
15
+ fontFamily: "Arial, sans-serif"
16
+ }}
17
+ >
18
+ <h1>
19
+ 🍔 Burger Counter App
20
+ </h1>
21
+ <img
22
+ src="/static/assets/burger.png"
23
+ alt="Delicious Burger"
24
+ style={{
25
+ width: "200px",
26
+ height: "auto",
27
+ margin: "20px 0",
28
+ borderRadius: "10px",
29
+ boxShadow: "0 4px 8px rgba(0,0,0,0.2)"
30
+ }}
31
+ />
32
+ <p style={{fontSize: "18px", margin: "20px 0"}}>
33
+ You've clicked the burger
34
+ <strong>
35
+ {count}
36
+ </strong>
37
+ times!
38
+ </p>
39
+ <button
40
+ onClick={lambda e: any -> None { count = count + 1;}}
41
+ style={{
42
+ padding: "10px 20px",
43
+ fontSize: "16px",
44
+ backgroundColor: "#ff6b35",
45
+ color: "white",
46
+ border: "none",
47
+ borderRadius: "5px",
48
+ cursor: "pointer",
49
+ boxShadow: "0 2px 4px rgba(0,0,0,0.2)"
50
+ }}
51
+ >
52
+ Click the Burger! 🍔
53
+ </button>
54
+ </div>;
55
+ }
56
+ }
@@ -0,0 +1,109 @@
1
+ # Pages
2
+ # Import image using the @jac-client/assets alias
3
+ cl import from "@jac-client/assets/burger.png" {
4
+ default as burgerImage
5
+ }
6
+
7
+ cl {
8
+ def:pub app -> any {
9
+ has count: int = 0;
10
+
11
+ can with count entry {
12
+ console.log("Count: ", count);
13
+ }
14
+
15
+ return
16
+ <div
17
+ style={{
18
+ padding: "20px",
19
+ textAlign: "center",
20
+ fontFamily: "Arial, sans-serif"
21
+ }}
22
+ >
23
+ <h1>
24
+ 🍔 Import Alias Example
25
+ </h1>
26
+ <p style={{color: "#666", marginBottom: "20px"}}>
27
+ Using
28
+ <code>
29
+ @jac-client/assets
30
+ </code>
31
+ alias to import assets
32
+ </p>
33
+ <img
34
+ src={burgerImage}
35
+ alt="Delicious Burger"
36
+ style={{
37
+ width: "200px",
38
+ height: "auto",
39
+ margin: "20px 0",
40
+ borderRadius: "10px",
41
+ boxShadow: "0 4px 8px rgba(0,0,0,0.2)"
42
+ }}
43
+ />
44
+ <p style={{fontSize: "18px", margin: "20px 0"}}>
45
+ You've clicked the burger
46
+ <strong>
47
+ {count}
48
+ </strong>
49
+ times!
50
+ </p>
51
+ <button
52
+ onClick={lambda e: any -> None { count = count + 1;}}
53
+ style={{
54
+ padding: "10px 20px",
55
+ fontSize: "16px",
56
+ backgroundColor: "#ff6b35",
57
+ color: "white",
58
+ border: "none",
59
+ borderRadius: "5px",
60
+ cursor: "pointer",
61
+ boxShadow: "0 2px 4px rgba(0,0,0,0.2)"
62
+ }}
63
+ >
64
+ Click the Burger! 🍔
65
+ </button>
66
+ <div
67
+ style={{
68
+ marginTop: "30px",
69
+ padding: "15px",
70
+ backgroundColor: "#f5f5f5",
71
+ borderRadius: "5px",
72
+ fontSize: "12px",
73
+ textAlign: "left",
74
+ maxWidth: "600px",
75
+ margin: "30px auto"
76
+ }}
77
+ >
78
+ <strong>
79
+ How it works:
80
+ </strong>
81
+ <ul style={{marginTop: "10px", paddingLeft: "20px"}}>
82
+ <li>
83
+ Import using:
84
+ <code>
85
+ climportfrom'@jac-client/assets/burger.png'
86
+ </code>
87
+ </li>
88
+ <li>
89
+ Vite processes the import and generates optimized URLs
90
+ </li>
91
+ <li>
92
+ Assets are automatically copied from
93
+ <code>
94
+ assets/
95
+ </code>
96
+ to
97
+ <code>
98
+ compiled/assets/
99
+ </code>
100
+ during build
101
+ </li>
102
+ <li>
103
+ Automatic hash generation for cache busting
104
+ </li>
105
+ </ul>
106
+ </div>
107
+ </div>;
108
+ }
109
+ }
@@ -0,0 +1,23 @@
1
+ # Pages
2
+ cl {
3
+ def:pub app -> any {
4
+ has count: int = 0;
5
+
6
+ can with count entry {
7
+ console.log("Count: ", count);
8
+ }
9
+
10
+ return
11
+ <div>
12
+ <h1>
13
+ Hello, World!
14
+ </h1>
15
+ <p>
16
+ Count: {count}
17
+ </p>
18
+ <button onClick={lambda e: any -> None { count = count + 1;}}>
19
+ Increment
20
+ </button>
21
+ </div>;
22
+ }
23
+ }
@@ -0,0 +1,363 @@
1
+ # Simple Authentication App with Jac Client Utils
2
+ cl import from '@jac/runtime' {
3
+ jacSignup,
4
+ jacLogin,
5
+ jacLogout,
6
+ jacIsLoggedIn
7
+ }
8
+
9
+ cl {
10
+ def:pub app -> any {
11
+ has isLoggedIn: bool = False,
12
+ isSignup: bool = False,
13
+ username: str = "",
14
+ password: str = "",
15
+ error: str = "",
16
+ loading: bool = False;
17
+
18
+ # Check login status on mount
19
+ can with entry {
20
+ isLoggedIn = jacIsLoggedIn();
21
+ }
22
+
23
+ # Handle login
24
+ async def handleLogin -> None {
25
+ error = "";
26
+ if not username.trim() or not password {
27
+ error = "Please fill in all fields";
28
+ return;
29
+ }
30
+ loading = True;
31
+ success = await jacLogin(username, password);
32
+ loading = False;
33
+ if success {
34
+ isLoggedIn = True;
35
+ username = "";
36
+ password = "";
37
+ } else {
38
+ error = "Invalid username or password";
39
+ }
40
+ }
41
+
42
+ # Handle signup
43
+ async def handleSignup -> None {
44
+ error = "";
45
+ if not username.trim() or not password {
46
+ error = "Please fill in all fields";
47
+ return;
48
+ }
49
+ if password.length < 6 {
50
+ error = "Password must be at least 6 characters";
51
+ return;
52
+ }
53
+ loading = True;
54
+ result = await jacSignup(username, password);
55
+ loading = False;
56
+ if result["success"] {
57
+ isLoggedIn = True;
58
+ username = "";
59
+ password = "";
60
+ } else {
61
+ error = result["error"] if result["error"] else "Signup failed";
62
+ }
63
+ }
64
+
65
+ # Handle logout
66
+ def handleLogout -> None {
67
+ jacLogout();
68
+ isLoggedIn = False;
69
+ username = "";
70
+ password = "";
71
+ error = "";
72
+ }
73
+
74
+ # Handle form submit
75
+ async def handleSubmit(e: any) -> None {
76
+ e.preventDefault();
77
+ if isSignup {
78
+ await handleSignup();
79
+ } else {
80
+ await handleLogin();
81
+ }
82
+ }
83
+
84
+ # Logged in view - Dashboard
85
+ if isLoggedIn {
86
+ return
87
+ <div
88
+ style={{
89
+ "minHeight": "100vh",
90
+ "background": "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
91
+ "display": "flex",
92
+ "alignItems": "center",
93
+ "justifyContent": "center",
94
+ "fontFamily": "system-ui, -apple-system, sans-serif"
95
+ }}
96
+ >
97
+ <div
98
+ style={{
99
+ "background": "#ffffff",
100
+ "borderRadius": "16px",
101
+ "padding": "48px",
102
+ "boxShadow": "0 20px 60px rgba(0,0,0,0.3)",
103
+ "maxWidth": "500px",
104
+ "width": "90%",
105
+ "textAlign": "center"
106
+ }}
107
+ >
108
+ <h1
109
+ style={{
110
+ "fontSize": "2.5rem",
111
+ "marginBottom": "16px",
112
+ "color": "#1f2937",
113
+ "fontWeight": "700"
114
+ }}
115
+ >
116
+ ✅ Welcome!
117
+ </h1>
118
+ <p
119
+ style={{
120
+ "fontSize": "1.1rem",
121
+ "color": "#6b7280",
122
+ "marginBottom": "32px"
123
+ }}
124
+ >
125
+ You are successfully logged in
126
+ </p>
127
+ <div
128
+ style={{
129
+ "background": "#f3f4f6",
130
+ "padding": "24px",
131
+ "borderRadius": "12px",
132
+ "marginBottom": "32px"
133
+ }}
134
+ >
135
+ <p
136
+ style={{
137
+ "fontSize": "0.9rem",
138
+ "color": "#6b7280",
139
+ "marginBottom": "8px"
140
+ }}
141
+ >
142
+ Logged in as:
143
+ </p>
144
+ <p
145
+ style={{
146
+ "fontSize": "1.2rem",
147
+ "fontWeight": "600",
148
+ "color": "#667eea"
149
+ }}
150
+ >
151
+ User
152
+ </p>
153
+ </div>
154
+ <button
155
+ onClick={handleLogout}
156
+ style={{
157
+ "width": "100%",
158
+ "padding": "14px 24px",
159
+ "background": "#ef4444",
160
+ "color": "#ffffff",
161
+ "border": "none",
162
+ "borderRadius": "10px",
163
+ "fontSize": "16px",
164
+ "fontWeight": "600",
165
+ "cursor": "pointer",
166
+ "transition": "all 0.3s",
167
+ "boxShadow": "0 4px 12px rgba(239,68,68,0.3)"
168
+ }}
169
+ >
170
+ Logout
171
+ </button>
172
+ </div>
173
+ </div>;
174
+ }
175
+
176
+ # Login/Signup view
177
+ return
178
+ <div
179
+ style={{
180
+ "minHeight": "100vh",
181
+ "background": "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
182
+ "display": "flex",
183
+ "alignItems": "center",
184
+ "justifyContent": "center",
185
+ "fontFamily": "system-ui, -apple-system, sans-serif",
186
+ "padding": "20px"
187
+ }}
188
+ >
189
+ <div
190
+ style={{
191
+ "background": "#ffffff",
192
+ "borderRadius": "16px",
193
+ "padding": "48px",
194
+ "boxShadow": "0 20px 60px rgba(0,0,0,0.3)",
195
+ "maxWidth": "400px",
196
+ "width": "100%"
197
+ }}
198
+ >
199
+ # Header
200
+ <div style={{"textAlign": "center", "marginBottom": "32px"}}>
201
+ <h1
202
+ style={{
203
+ "fontSize": "2rem",
204
+ "fontWeight": "700",
205
+ "color": "#1f2937",
206
+ "marginBottom": "8px"
207
+ }}
208
+ >
209
+ {("Create Account" if isSignup else "Welcome Back")}
210
+ </h1>
211
+ <p style={{"color": "#6b7280", "fontSize": "0.95rem"}}>
212
+ {(
213
+ "Sign up to get started"
214
+ if isSignup
215
+ else "Sign in to continue"
216
+ )}
217
+ </p>
218
+ </div>
219
+ # Form
220
+ <form onSubmit={handleSubmit} style={{"marginBottom": "24px"}}>
221
+ <div style={{"marginBottom": "20px"}}>
222
+ <label
223
+ style={{
224
+ "display": "block",
225
+ "marginBottom": "8px",
226
+ "color": "#374151",
227
+ "fontSize": "14px",
228
+ "fontWeight": "600"
229
+ }}
230
+ >
231
+ Username
232
+ </label>
233
+ <input
234
+ type="text"
235
+ value={username}
236
+ onChange={lambda e: any -> None { username = e.target.value;}}
237
+ placeholder="Enter your username"
238
+ style={{
239
+ "width": "100%",
240
+ "padding": "12px 16px",
241
+ "border": "2px solid #e5e7eb",
242
+ "borderRadius": "10px",
243
+ "fontSize": "16px",
244
+ "outline": "none",
245
+ "transition": "border 0.2s",
246
+ "boxSizing": "border-box"
247
+ }}
248
+ />
249
+ </div>
250
+ <div style={{"marginBottom": "24px"}}>
251
+ <label
252
+ style={{
253
+ "display": "block",
254
+ "marginBottom": "8px",
255
+ "color": "#374151",
256
+ "fontSize": "14px",
257
+ "fontWeight": "600"
258
+ }}
259
+ >
260
+ Password
261
+ </label>
262
+ <input
263
+ type="password"
264
+ value={password}
265
+ onChange={lambda e: any -> None { password = e.target.value;}}
266
+ placeholder="Enter your password"
267
+ style={{
268
+ "width": "100%",
269
+ "padding": "12px 16px",
270
+ "border": "2px solid #e5e7eb",
271
+ "borderRadius": "10px",
272
+ "fontSize": "16px",
273
+ "outline": "none",
274
+ "transition": "border 0.2s",
275
+ "boxSizing": "border-box"
276
+ }}
277
+ />
278
+ </div>
279
+ {(
280
+ <div
281
+ style={{
282
+ "padding": "12px",
283
+ "background": "#fee2e2",
284
+ "border": "1px solid #fecaca",
285
+ "borderRadius": "8px",
286
+ "marginBottom": "20px"
287
+ }}
288
+ >
289
+ <p
290
+ style={{
291
+ "color": "#dc2626",
292
+ "fontSize": "14px",
293
+ "margin": "0"
294
+ }}
295
+ >
296
+ {error}
297
+ </p>
298
+ </div>
299
+ )
300
+ if error
301
+ else None}
302
+ <button
303
+ type="submit"
304
+ disabled={loading}
305
+ style={{
306
+ "width": "100%",
307
+ "padding": "14px 24px",
308
+ "background": (("#9ca3af" if loading else "#667eea")),
309
+ "color": "#ffffff",
310
+ "border": "none",
311
+ "borderRadius": "10px",
312
+ "fontSize": "16px",
313
+ "fontWeight": "600",
314
+ "cursor": (("not-allowed" if loading else "pointer")),
315
+ "transition": "all 0.3s",
316
+ "boxShadow": "0 4px 12px rgba(102,126,234,0.4)"
317
+ }}
318
+ >
319
+ {(
320
+ (
321
+ "Processing..."
322
+ if loading
323
+ else ("Sign Up" if isSignup else "Sign In")
324
+ )
325
+ )}
326
+ </button>
327
+ </form>
328
+ # Toggle between login/signup
329
+ <div style={{"textAlign": "center"}}>
330
+ <p
331
+ style={{
332
+ "color": "#6b7280",
333
+ "fontSize": "14px",
334
+ "marginBottom": "8px"
335
+ }}
336
+ >
337
+ {(
338
+ (
339
+ "Already have an account?"
340
+ if isSignup
341
+ else "Don't have an account?"
342
+ )
343
+ )}
344
+ </p>
345
+ <button
346
+ onClick={lambda -> None { isSignup = not isSignup;error = "";username = "";password = "";}}
347
+ style={{
348
+ "background": "none",
349
+ "border": "none",
350
+ "color": "#667eea",
351
+ "fontSize": "14px",
352
+ "fontWeight": "600",
353
+ "cursor": "pointer",
354
+ "textDecoration": "underline"
355
+ }}
356
+ >
357
+ {(("Sign In" if isSignup else "Create Account"))}
358
+ </button>
359
+ </div>
360
+ </div>
361
+ </div>;
362
+ }
363
+ }