jac-client 0.2.0__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 (72) hide show
  1. jac_client/docs/README.md +659 -0
  2. jac_client/docs/advanced-state.md +1266 -0
  3. jac_client/docs/assets/pipe_line.png +0 -0
  4. jac_client/docs/guide-example/intro.md +117 -0
  5. jac_client/docs/guide-example/step-01-setup.md +260 -0
  6. jac_client/docs/guide-example/step-02-components.md +416 -0
  7. jac_client/docs/guide-example/step-03-styling.md +478 -0
  8. jac_client/docs/guide-example/step-04-todo-ui.md +477 -0
  9. jac_client/docs/guide-example/step-05-local-state.md +530 -0
  10. jac_client/docs/guide-example/step-06-events.md +750 -0
  11. jac_client/docs/guide-example/step-07-effects.md +469 -0
  12. jac_client/docs/guide-example/step-08-walkers.md +534 -0
  13. jac_client/docs/guide-example/step-09-authentication.md +586 -0
  14. jac_client/docs/guide-example/step-10-routing.md +540 -0
  15. jac_client/docs/guide-example/step-11-final.md +964 -0
  16. jac_client/docs/imports.md +1142 -0
  17. jac_client/docs/lifecycle-hooks.md +774 -0
  18. jac_client/docs/routing.md +660 -0
  19. jac_client/examples/basic/.babelrc +9 -0
  20. jac_client/examples/basic/README.md +16 -0
  21. jac_client/examples/basic/app.jac +16 -0
  22. jac_client/examples/basic/package.json +27 -0
  23. jac_client/examples/basic/vite.config.js +28 -0
  24. jac_client/examples/basic-auth/.babelrc +9 -0
  25. jac_client/examples/basic-auth/README.md +16 -0
  26. jac_client/examples/basic-auth/app.jac +308 -0
  27. jac_client/examples/basic-auth/package.json +27 -0
  28. jac_client/examples/basic-auth/vite.config.js +28 -0
  29. jac_client/examples/basic-auth-with-router/.babelrc +9 -0
  30. jac_client/examples/basic-auth-with-router/README.md +60 -0
  31. jac_client/examples/basic-auth-with-router/app.jac +464 -0
  32. jac_client/examples/basic-auth-with-router/package.json +28 -0
  33. jac_client/examples/basic-auth-with-router/vite.config.js +28 -0
  34. jac_client/examples/basic-full-stack/.babelrc +9 -0
  35. jac_client/examples/basic-full-stack/README.md +18 -0
  36. jac_client/examples/basic-full-stack/app.jac +320 -0
  37. jac_client/examples/basic-full-stack/package.json +28 -0
  38. jac_client/examples/basic-full-stack/vite.config.js +28 -0
  39. jac_client/examples/full-stack-with-auth/.babelrc +9 -0
  40. jac_client/examples/full-stack-with-auth/README.md +16 -0
  41. jac_client/examples/full-stack-with-auth/app.jac +735 -0
  42. jac_client/examples/full-stack-with-auth/package.json +28 -0
  43. jac_client/examples/full-stack-with-auth/vite.config.js +30 -0
  44. jac_client/examples/little-x/app.jac +615 -0
  45. jac_client/examples/little-x/package.json +23 -0
  46. jac_client/examples/little-x/submit-button.jac +8 -0
  47. jac_client/examples/with-router/.babelrc +9 -0
  48. jac_client/examples/with-router/README.md +17 -0
  49. jac_client/examples/with-router/app.jac +323 -0
  50. jac_client/examples/with-router/package.json +28 -0
  51. jac_client/examples/with-router/vite.config.js +28 -0
  52. jac_client/plugin/cli.py +239 -0
  53. jac_client/plugin/client.py +89 -0
  54. jac_client/plugin/client_runtime.jac +234 -0
  55. jac_client/plugin/vite_client_bundle.py +355 -0
  56. jac_client/tests/__init__.py +2 -0
  57. jac_client/tests/fixtures/basic-app/app.jac +18 -0
  58. jac_client/tests/fixtures/client_app_with_antd/app.jac +28 -0
  59. jac_client/tests/fixtures/js_import/app.jac +30 -0
  60. jac_client/tests/fixtures/js_import/utils.js +22 -0
  61. jac_client/tests/fixtures/package-lock.json +329 -0
  62. jac_client/tests/fixtures/package.json +11 -0
  63. jac_client/tests/fixtures/relative_import/app.jac +13 -0
  64. jac_client/tests/fixtures/relative_import/button.jac +6 -0
  65. jac_client/tests/fixtures/spawn_test/app.jac +133 -0
  66. jac_client/tests/fixtures/test_fragments_spread/app.jac +53 -0
  67. jac_client/tests/test_cl.py +476 -0
  68. jac_client/tests/test_create_jac_app.py +139 -0
  69. jac_client-0.2.0.dist-info/METADATA +182 -0
  70. jac_client-0.2.0.dist-info/RECORD +72 -0
  71. jac_client-0.2.0.dist-info/WHEEL +4 -0
  72. jac_client-0.2.0.dist-info/entry_points.txt +4 -0
@@ -0,0 +1,320 @@
1
+ # Todo App
2
+ cl import from react {useState, useEffect}
3
+
4
+ node Todo {
5
+ has text: str;
6
+ has done: bool = False;
7
+ }
8
+
9
+ walker create_todo {
10
+ has text:str;
11
+ can create with `root entry {
12
+ new_todo = here ++> Todo(text= self.text);
13
+ report new_todo;
14
+ }
15
+ }
16
+ walker toggle_todo {
17
+ can toggle with Todo entry {
18
+ here.done = not here.done;
19
+ report here;
20
+ }
21
+ }
22
+ walker read_todos {
23
+ can read with `root entry {
24
+ visit [-->(`?Todo)];
25
+ }
26
+ can report_todos with exit {
27
+ report here;
28
+ }
29
+ }
30
+
31
+ cl {
32
+ def app() -> any {
33
+ let [todos, setTodos] = useState([]);
34
+ let [inputValue, setInputValue] = useState("");
35
+ let [filter, setFilter] = useState("all");
36
+
37
+ useEffect(lambda -> None {
38
+ async def loadTodos() -> None {
39
+ todos = root spawn read_todos();
40
+ setTodos(todos.reports);
41
+ }
42
+ loadTodos();
43
+ }, []);
44
+
45
+ # Add a new todo
46
+ async def addTodo() -> None {
47
+ if not inputValue.trim() { return; }
48
+ newTodo = {
49
+ "id": Date.now(),
50
+ "text": inputValue.trim(),
51
+ "done": False
52
+ };
53
+ response = root spawn create_todo(text=inputValue.trim());
54
+ newTodos = todos.concat([response.reports[0][0]]);
55
+ setTodos(newTodos);
56
+ setInputValue("");
57
+ }
58
+
59
+ # Toggle todo completion status
60
+ async def toggleTodo(id: any) -> None {
61
+ console.log("toggleTodo", id);
62
+ id spawn toggle_todo();
63
+ setTodos(todos.map(lambda todo: any -> any {
64
+ if todo._jac_id == id {
65
+ updatedTodo = {
66
+ "_jac_id": todo._jac_id,
67
+ "text": todo.text,
68
+ "done": not todo.done,
69
+ "id": todo.id
70
+ };
71
+ return updatedTodo;
72
+ }
73
+ return todo;
74
+ }));
75
+ }
76
+
77
+ # Delete a todo
78
+ def deleteTodo(id: any) -> None {
79
+ setTodos(todos.filter(lambda todo: any -> bool { return todo.id != id; }));
80
+ }
81
+
82
+ # Clear all completed todos
83
+ def clearCompleted() -> None {
84
+ setTodos(todos.filter(lambda todo: any -> bool { return not todo.done; }));
85
+ }
86
+
87
+ # Filter todos based on current filter
88
+ def getFilteredTodos() -> list {
89
+ if filter == "active" {
90
+ return todos.filter(lambda todo: any -> bool { return not todo.done; });
91
+ } elif filter == "completed" {
92
+ return todos.filter(lambda todo: any -> bool { return todo.done; });
93
+ }
94
+ return todos;
95
+ }
96
+
97
+ # Count active todos
98
+ activeCount = todos.filter(lambda todo: any -> bool { return not todo.done; }).length;
99
+
100
+ filteredTodos = getFilteredTodos();
101
+
102
+
103
+ return <div style={{
104
+ "maxWidth": "600px",
105
+ "margin": "40px auto",
106
+ "padding": "24px",
107
+ "fontFamily": "system-ui, -apple-system, sans-serif",
108
+ "background": "#f9fafb",
109
+ "minHeight": "100vh"
110
+ }}>
111
+ <h1 style={{
112
+ "textAlign": "center",
113
+ "color": "#1f2937",
114
+ "marginBottom": "32px",
115
+ "fontSize": "2.5rem",
116
+ "fontWeight": "700"
117
+ }}>📝 My Todo App</h1>
118
+
119
+ # Add todo form
120
+ <div style={{
121
+ "display": "flex",
122
+ "gap": "8px",
123
+ "marginBottom": "24px",
124
+ "background": "#ffffff",
125
+ "padding": "16px",
126
+ "borderRadius": "12px",
127
+ "boxShadow": "0 1px 3px rgba(0,0,0,0.1)"
128
+ }}>
129
+ <input
130
+ type="text"
131
+ value={inputValue}
132
+ onChange={lambda e: any -> None { setInputValue(e.target.value); }}
133
+ onKeyPress={lambda e: any -> None {
134
+ if e.key == "Enter" { addTodo(); }
135
+ }}
136
+ placeholder="What needs to be done?"
137
+ style={{
138
+ "flex": "1",
139
+ "padding": "12px 16px",
140
+ "border": "1px solid #e5e7eb",
141
+ "borderRadius": "8px",
142
+ "fontSize": "16px",
143
+ "outline": "none"
144
+ }}
145
+ />
146
+ <button
147
+ onClick={addTodo}
148
+ style={{
149
+ "padding": "12px 24px",
150
+ "background": "#3b82f6",
151
+ "color": "#ffffff",
152
+ "border": "none",
153
+ "borderRadius": "8px",
154
+ "fontSize": "16px",
155
+ "fontWeight": "600",
156
+ "cursor": "pointer",
157
+ "transition": "background 0.2s"
158
+ }}
159
+ >
160
+ Add
161
+ </button>
162
+ </div>
163
+
164
+ # Filter buttons
165
+ <div style={{
166
+ "display": "flex",
167
+ "gap": "8px",
168
+ "marginBottom": "24px",
169
+ "justifyContent": "center"
170
+ }}>
171
+ <button
172
+ onClick={lambda -> None { setFilter("all"); }}
173
+ style={{
174
+ "padding": "8px 16px",
175
+ "background": ("#3b82f6" if filter == "all" else "#ffffff"),
176
+ "color": ("#ffffff" if filter == "all" else "#3b82f6"),
177
+ "border": "1px solid #3b82f6",
178
+ "borderRadius": "6px",
179
+ "fontSize": "14px",
180
+ "fontWeight": "600",
181
+ "cursor": "pointer"
182
+ }}
183
+ >
184
+ All
185
+ </button>
186
+ <button
187
+ onClick={lambda -> None { setFilter("active"); }}
188
+ style={{
189
+ "padding": "8px 16px",
190
+ "background": ("#3b82f6" if filter == "active" else "#ffffff"),
191
+ "color": ("#ffffff" if filter == "active" else "#3b82f6"),
192
+ "border": "1px solid #3b82f6",
193
+ "borderRadius": "6px",
194
+ "fontSize": "14px",
195
+ "fontWeight": "600",
196
+ "cursor": "pointer"
197
+ }}
198
+ >
199
+ Active
200
+ </button>
201
+ <button
202
+ onClick={lambda -> None { setFilter("completed"); }}
203
+ style={{
204
+ "padding": "8px 16px",
205
+ "background": ("#3b82f6" if filter == "completed" else "#ffffff"),
206
+ "color": ("#ffffff" if filter == "completed" else "#3b82f6"),
207
+ "border": "1px solid #3b82f6",
208
+ "borderRadius": "6px",
209
+ "fontSize": "14px",
210
+ "fontWeight": "600",
211
+ "cursor": "pointer"
212
+ }}
213
+ >
214
+ Completed
215
+ </button>
216
+ </div>
217
+
218
+ # Todo list
219
+ <div style={{
220
+ "background": "#ffffff",
221
+ "borderRadius": "12px",
222
+ "boxShadow": "0 1px 3px rgba(0,0,0,0.1)",
223
+ "overflow": "hidden"
224
+ }}>
225
+ {(<div style={{
226
+ "padding": "40px",
227
+ "textAlign": "center",
228
+ "color": "#9ca3af"
229
+ }}>
230
+ {("No todos yet. Add one above!" if filter == "all" else
231
+ ("No active todos!" if filter == "active" else
232
+ "No completed todos!"))}
233
+ </div>) if filteredTodos.length == 0 else (
234
+ filteredTodos.map(lambda todo: any -> any {
235
+ return <div
236
+ key={todo._jac_id}
237
+ style={{
238
+ "display": "flex",
239
+ "alignItems": "center",
240
+ "gap": "12px",
241
+ "padding": "16px",
242
+ "borderBottom": "1px solid #e5e7eb",
243
+ "transition": "background 0.2s"
244
+ }}
245
+ >
246
+ <input
247
+ type="checkbox"
248
+ checked={todo.done}
249
+ onChange={lambda -> None { toggleTodo(todo._jac_id); }}
250
+ style={{
251
+ "width": "20px",
252
+ "height": "20px",
253
+ "cursor": "pointer"
254
+ }}
255
+ />
256
+ <span style={{
257
+ "flex": "1",
258
+ "textDecoration": ("line-through" if todo.done else "none"),
259
+ "color": ("#9ca3af" if todo.done else "#1f2937"),
260
+ "fontSize": "16px"
261
+ }}>
262
+ {todo.text}
263
+ </span>
264
+ <button
265
+ onClick={lambda -> None { deleteTodo(todo.__jac_id); }}
266
+ style={{
267
+ "padding": "6px 12px",
268
+ "background": "#ef4444",
269
+ "color": "#ffffff",
270
+ "border": "none",
271
+ "borderRadius": "6px",
272
+ "fontSize": "14px",
273
+ "fontWeight": "500",
274
+ "cursor": "pointer",
275
+ "transition": "background 0.2s"
276
+ }}
277
+ >
278
+ Delete
279
+ </button>
280
+ </div>;
281
+ })
282
+ )}
283
+ </div>
284
+
285
+ # Stats and clear completed button
286
+ {(<div style={{
287
+ "display": "flex",
288
+ "justifyContent": "space-between",
289
+ "alignItems": "center",
290
+ "marginTop": "24px",
291
+ "padding": "16px",
292
+ "background": "#ffffff",
293
+ "borderRadius": "12px",
294
+ "boxShadow": "0 1px 3px rgba(0,0,0,0.1)"
295
+ }}>
296
+ <span style={{
297
+ "color": "#6b7280",
298
+ "fontSize": "14px"
299
+ }}>
300
+ {activeCount} {"item" if activeCount == 1 else "items"} left
301
+ </span>
302
+ {(<button
303
+ onClick={clearCompleted}
304
+ style={{
305
+ "padding": "8px 16px",
306
+ "background": "#ef4444",
307
+ "color": "#ffffff",
308
+ "border": "none",
309
+ "borderRadius": "6px",
310
+ "fontSize": "14px",
311
+ "fontWeight": "600",
312
+ "cursor": "pointer"
313
+ }}
314
+ >
315
+ Clear Completed
316
+ </button>) if todos.some(lambda todo: any -> bool { return todo.done; }) else None}
317
+ </div>) if todos.length > 0 else None}
318
+ </div>;
319
+ }
320
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "basic-full-stack",
3
+ "version": "1.0.0",
4
+ "main": "index.js",
5
+ "scripts": {
6
+ "build": "npm run compile && vite build",
7
+ "dev": "vite dev",
8
+ "preview": "vite preview",
9
+ "compile": "babel src --out-dir build --extensions \".jsx,.js\" --out-file-extension .js"
10
+ },
11
+ "keywords": [],
12
+ "author": "",
13
+ "license": "ISC",
14
+ "description": "Jac application: basic-full-stack",
15
+ "type": "module",
16
+ "devDependencies": {
17
+ "vite": "^6.4.1",
18
+ "@babel/cli": "^7.28.3",
19
+ "@babel/core": "^7.28.5",
20
+ "@babel/preset-env": "^7.28.5",
21
+ "@babel/preset-react": "^7.28.5"
22
+ },
23
+ "dependencies": {
24
+ "react": "^19.2.0",
25
+ "react-dom": "^19.2.0",
26
+ "react-router-dom": "^7.3.0"
27
+ }
28
+ }
@@ -0,0 +1,28 @@
1
+
2
+ import { defineConfig } from "vite";
3
+ import path from "path";
4
+ import { fileURLToPath } from "url";
5
+
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+
8
+ export default defineConfig({
9
+ root: ".", // base folder
10
+ build: {
11
+ rollupOptions: {
12
+ input: "build/main.js", // your compiled entry file
13
+ output: {
14
+ entryFileNames: "client.[hash].js", // name of the final js file
15
+ assetFileNames: "[name].[ext]",
16
+ },
17
+ },
18
+ outDir: "dist", // final bundled output
19
+ emptyOutDir: true,
20
+ },
21
+ publicDir: false,
22
+ resolve: {
23
+ alias: {
24
+ "@jac-client/utils": path.resolve(__dirname, "src/client_runtime.js"),
25
+ },
26
+ },
27
+ });
28
+
@@ -0,0 +1,9 @@
1
+
2
+ {
3
+ "presets": [[
4
+ "@babel/preset-env",
5
+ {
6
+ "modules": false
7
+ }
8
+ ], "@babel/preset-react"]
9
+ }
@@ -0,0 +1,16 @@
1
+ # full-stack-with-auth
2
+
3
+ ## Running Jac Code
4
+
5
+ make sure node modules are installed:
6
+ ```bash
7
+ npm install
8
+ ```
9
+
10
+ To run your Jac code, use the Jac CLI:
11
+
12
+ ```bash
13
+ jac serve app.jac
14
+ ```
15
+
16
+ Happy coding with Jac!