jac-client 0.1.0__py3-none-any.whl → 0.2.1__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 (141) hide show
  1. jac_client/docs/README.md +232 -172
  2. jac_client/docs/advanced-state.md +1012 -452
  3. jac_client/docs/asset-serving/intro.md +209 -0
  4. jac_client/docs/assets/pipe_line-v2.svg +32 -0
  5. jac_client/docs/assets/pipe_line.png +0 -0
  6. jac_client/docs/file-system/intro.md +90 -0
  7. jac_client/docs/guide-example/intro.md +117 -0
  8. jac_client/docs/guide-example/step-01-setup.md +260 -0
  9. jac_client/docs/guide-example/step-02-components.md +416 -0
  10. jac_client/docs/guide-example/step-03-styling.md +478 -0
  11. jac_client/docs/guide-example/step-04-todo-ui.md +477 -0
  12. jac_client/docs/guide-example/step-05-local-state.md +530 -0
  13. jac_client/docs/guide-example/step-06-events.md +750 -0
  14. jac_client/docs/guide-example/step-07-effects.md +469 -0
  15. jac_client/docs/guide-example/step-08-walkers.md +534 -0
  16. jac_client/docs/guide-example/step-09-authentication.md +586 -0
  17. jac_client/docs/guide-example/step-10-routing.md +540 -0
  18. jac_client/docs/guide-example/step-11-final.md +964 -0
  19. jac_client/docs/imports.md +538 -46
  20. jac_client/docs/lifecycle-hooks.md +517 -297
  21. jac_client/docs/routing.md +487 -357
  22. jac_client/docs/styling/intro.md +250 -0
  23. jac_client/docs/styling/js-styling.md +373 -0
  24. jac_client/docs/styling/material-ui.md +346 -0
  25. jac_client/docs/styling/pure-css.md +305 -0
  26. jac_client/docs/styling/sass.md +409 -0
  27. jac_client/docs/styling/styled-components.md +401 -0
  28. jac_client/docs/styling/tailwind.md +303 -0
  29. jac_client/examples/asset-serving/css-with-image/.babelrc +9 -0
  30. jac_client/examples/asset-serving/css-with-image/README.md +91 -0
  31. jac_client/examples/asset-serving/css-with-image/app.jac +67 -0
  32. jac_client/examples/asset-serving/css-with-image/assets/burger.png +0 -0
  33. jac_client/examples/asset-serving/css-with-image/package.json +28 -0
  34. jac_client/examples/asset-serving/css-with-image/styles.css +27 -0
  35. jac_client/examples/asset-serving/css-with-image/vite.config.js +29 -0
  36. jac_client/examples/asset-serving/image-asset/.babelrc +9 -0
  37. jac_client/examples/asset-serving/image-asset/README.md +119 -0
  38. jac_client/examples/asset-serving/image-asset/app.jac +43 -0
  39. jac_client/examples/asset-serving/image-asset/assets/burger.png +0 -0
  40. jac_client/examples/asset-serving/image-asset/package.json +28 -0
  41. jac_client/examples/asset-serving/image-asset/styles.css +27 -0
  42. jac_client/examples/asset-serving/image-asset/vite.config.js +29 -0
  43. jac_client/examples/asset-serving/import-alias/.babelrc +9 -0
  44. jac_client/examples/asset-serving/import-alias/README.md +83 -0
  45. jac_client/examples/asset-serving/import-alias/app.jac +57 -0
  46. jac_client/examples/asset-serving/import-alias/assets/burger.png +0 -0
  47. jac_client/examples/asset-serving/import-alias/package.json +28 -0
  48. jac_client/examples/asset-serving/import-alias/vite.config.js +29 -0
  49. jac_client/examples/basic/.babelrc +9 -0
  50. jac_client/examples/basic/README.md +16 -0
  51. jac_client/examples/basic/app.jac +16 -0
  52. jac_client/examples/basic/package.json +27 -0
  53. jac_client/examples/basic/vite.config.js +28 -0
  54. jac_client/examples/basic-auth/.babelrc +9 -0
  55. jac_client/examples/basic-auth/README.md +16 -0
  56. jac_client/examples/basic-auth/app.jac +308 -0
  57. jac_client/examples/basic-auth/package.json +27 -0
  58. jac_client/examples/basic-auth/vite.config.js +28 -0
  59. jac_client/examples/basic-auth-with-router/.babelrc +9 -0
  60. jac_client/examples/basic-auth-with-router/README.md +60 -0
  61. jac_client/examples/basic-auth-with-router/app.jac +464 -0
  62. jac_client/examples/basic-auth-with-router/package.json +28 -0
  63. jac_client/examples/basic-auth-with-router/vite.config.js +28 -0
  64. jac_client/examples/basic-full-stack/.babelrc +9 -0
  65. jac_client/examples/basic-full-stack/README.md +18 -0
  66. jac_client/examples/basic-full-stack/app.jac +320 -0
  67. jac_client/examples/basic-full-stack/package.json +28 -0
  68. jac_client/examples/basic-full-stack/vite.config.js +28 -0
  69. jac_client/examples/css-styling/js-styling/.babelrc +9 -0
  70. jac_client/examples/css-styling/js-styling/README.md +183 -0
  71. jac_client/examples/css-styling/js-styling/app.jac +63 -0
  72. jac_client/examples/css-styling/js-styling/package.json +28 -0
  73. jac_client/examples/css-styling/js-styling/styles.js +100 -0
  74. jac_client/examples/css-styling/js-styling/vite.config.js +28 -0
  75. jac_client/examples/css-styling/material-ui/.babelrc +9 -0
  76. jac_client/examples/css-styling/material-ui/README.md +16 -0
  77. jac_client/examples/css-styling/material-ui/app.jac +82 -0
  78. jac_client/examples/css-styling/material-ui/package.json +32 -0
  79. jac_client/examples/css-styling/material-ui/vite.config.js +28 -0
  80. jac_client/examples/css-styling/pure-css/.babelrc +9 -0
  81. jac_client/examples/css-styling/pure-css/README.md +16 -0
  82. jac_client/examples/css-styling/pure-css/app.jac +63 -0
  83. jac_client/examples/css-styling/pure-css/package.json +28 -0
  84. jac_client/examples/css-styling/pure-css/styles.css +112 -0
  85. jac_client/examples/css-styling/pure-css/vite.config.js +28 -0
  86. jac_client/examples/css-styling/sass-example/.babelrc +9 -0
  87. jac_client/examples/css-styling/sass-example/README.md +16 -0
  88. jac_client/examples/css-styling/sass-example/app.jac +63 -0
  89. jac_client/examples/css-styling/sass-example/package.json +29 -0
  90. jac_client/examples/css-styling/sass-example/styles.scss +158 -0
  91. jac_client/examples/css-styling/sass-example/vite.config.js +28 -0
  92. jac_client/examples/css-styling/styled-components/.babelrc +9 -0
  93. jac_client/examples/css-styling/styled-components/README.md +16 -0
  94. jac_client/examples/css-styling/styled-components/app.jac +66 -0
  95. jac_client/examples/css-styling/styled-components/package.json +29 -0
  96. jac_client/examples/css-styling/styled-components/styled.js +91 -0
  97. jac_client/examples/css-styling/styled-components/vite.config.js +28 -0
  98. jac_client/examples/css-styling/tailwind-example/.babelrc +9 -0
  99. jac_client/examples/css-styling/tailwind-example/README.md +16 -0
  100. jac_client/examples/css-styling/tailwind-example/app.jac +64 -0
  101. jac_client/examples/css-styling/tailwind-example/global.css +1 -0
  102. jac_client/examples/css-styling/tailwind-example/package.json +30 -0
  103. jac_client/examples/css-styling/tailwind-example/vite.config.js +30 -0
  104. jac_client/examples/full-stack-with-auth/.babelrc +9 -0
  105. jac_client/examples/full-stack-with-auth/README.md +16 -0
  106. jac_client/examples/full-stack-with-auth/app.jac +735 -0
  107. jac_client/examples/full-stack-with-auth/package.json +28 -0
  108. jac_client/examples/full-stack-with-auth/vite.config.js +30 -0
  109. jac_client/examples/with-router/.babelrc +9 -0
  110. jac_client/examples/with-router/README.md +17 -0
  111. jac_client/examples/with-router/app.jac +323 -0
  112. jac_client/examples/with-router/package.json +28 -0
  113. jac_client/examples/with-router/vite.config.js +28 -0
  114. jac_client/plugin/cli.py +95 -179
  115. jac_client/plugin/client.py +111 -2
  116. jac_client/plugin/client_runtime.jac +183 -890
  117. jac_client/plugin/vite_client_bundle.py +185 -205
  118. jac_client/tests/__init__.py +0 -1
  119. jac_client/tests/fixtures/{client_app.jac → basic-app/app.jac} +1 -1
  120. jac_client/tests/fixtures/cl_file/app.cl.jac +38 -0
  121. jac_client/tests/fixtures/cl_file/app.jac +15 -0
  122. jac_client/tests/fixtures/{client_app_with_antd.jac → client_app_with_antd/app.jac} +7 -0
  123. jac_client/tests/fixtures/{js_import.jac → js_import/app.jac} +2 -2
  124. jac_client/tests/fixtures/{relative_import.jac → relative_import/app.jac} +1 -1
  125. jac_client/tests/fixtures/{button.jac → relative_import/button.jac} +2 -2
  126. jac_client/tests/fixtures/spawn_test/app.jac +133 -0
  127. jac_client/tests/fixtures/{test_fragments_spread.jac → test_fragments_spread/app.jac} +11 -2
  128. jac_client/tests/test_asset_examples.py +339 -0
  129. jac_client/tests/test_cl.py +345 -151
  130. jac_client/tests/test_create_jac_app.py +41 -45
  131. {jac_client-0.1.0.dist-info → jac_client-0.2.1.dist-info}/METADATA +72 -16
  132. jac_client-0.2.1.dist-info/RECORD +140 -0
  133. jac_client/examples/little-x/package-lock.json +0 -2840
  134. jac_client/examples/todo-app/README.md +0 -82
  135. jac_client/examples/todo-app/app.jac +0 -683
  136. jac_client/examples/todo-app/package-lock.json +0 -999
  137. jac_client/examples/todo-app/package.json +0 -22
  138. jac_client-0.1.0.dist-info/RECORD +0 -33
  139. /jac_client/tests/fixtures/{utils.js → js_import/utils.js} +0 -0
  140. {jac_client-0.1.0.dist-info → jac_client-0.2.1.dist-info}/WHEEL +0 -0
  141. {jac_client-0.1.0.dist-info → jac_client-0.2.1.dist-info}/entry_points.txt +0 -0
jac_client/docs/README.md CHANGED
@@ -6,9 +6,39 @@ Welcome to the Todo App example! This guide will walk you through building a ful
6
6
 
7
7
  ## 📦 1. Creating the Application
8
8
 
9
+ ### Prerequisites
10
+
11
+ Before installing Jac client, you need to have **Node.js** installed on your system.
12
+
13
+ #### Installing Node.js
14
+
15
+ **For Linux users:**
16
+
17
+ Visit [https://nodejs.org/en/download](https://nodejs.org/en/download) and follow the instructions to install Node.js using **nvm** (Node Version Manager) with **npm**.
18
+
19
+ Select:
20
+ - **Platform**: Linux
21
+ - **Package Manager**: nvm
22
+ - **Package**: npm
23
+
24
+ Then follow the installation commands provided on that page.
25
+
26
+ **For macOS users:**
27
+
28
+ Download and install Node.js from [https://nodejs.org/en/download](https://nodejs.org/en/download) by selecting your operating system.
29
+
30
+ **Verify Installation:**
31
+
32
+ After installation, verify Node.js and npm are installed correctly:
33
+
34
+ ```bash
35
+ node -v
36
+ npm -v
37
+ ```
38
+
9
39
  ### Installation
10
40
 
11
- First, install the Jac client package:
41
+ Once Node.js is installed, install the Jac client package:
12
42
 
13
43
  ```bash
14
44
  pip install jac-client
@@ -57,48 +87,63 @@ You can access your app at `http://localhost:8000`
57
87
 
58
88
  Every Jac client application needs an entry point function. This is where your app starts rendering.
59
89
 
60
- ### The `jac_app()` Function
90
+ ### The `app()` Function
61
91
 
62
- At the bottom of your `app.jac` file, you'll find the entry point:
92
+ Inside your `cl` block, define a function called `app()`:
63
93
 
64
94
  ```jac
65
- cl {
66
- def App() -> any {
67
- # Your main app component with routing
68
- return <div>Hello World</div>;
69
- }
95
+ cl import from react {useState}
70
96
 
71
- def jac_app() -> any {
72
- return App();
97
+ cl {
98
+ def app() -> any {
99
+ let [count, setCount] = useState(0);
100
+ return <div>
101
+ <h1>Hello, World!</h1>
102
+ <p>Count: {count}</p>
103
+ <button onClick={lambda e: any -> None { setCount(count + 1); }}>
104
+ Increment
105
+ </button>
106
+ </div>;
73
107
  }
74
108
  }
75
109
  ```
76
110
 
77
111
  **Key Points:**
78
- - `jac_app()` is the **required entry point** that Jac looks for
79
- - It should return your root component (usually called `App`)
112
+ - `app()` is the **required entry point** that Jac looks for
113
+ - It must be defined inside a `cl { }` block (client-side code)
80
114
  - The `cl` (client) block indicates this code runs in the browser
81
115
  - This function gets called automatically when the page loads
116
+ - You can define other components and helper functions in the same `cl` block
82
117
 
83
- **Example from Todo App:**
118
+ **Example with Multiple Components:**
84
119
  ```jac
85
- def App() -> any {
86
- login_route = {"path": "/login", "component": lambda -> any { return LoginForm(); }, "guard": None};
87
- signup_route = {"path": "/signup", "component": lambda -> any { return SignupForm(); }, "guard": None};
88
- todos_route = {"path": "/todos", "component": lambda -> any { return TodoApp(); }, "guard": jacIsLoggedIn};
89
-
90
- routes = [login_route, signup_route, todos_route];
91
- router = initRouter(routes, "/login");
92
-
93
- currentPath = router.path();
94
- return <div>
95
- {Nav(currentPath)}
96
- {router.render()}
97
- </div>;
98
- }
120
+ cl import from react {useState, useEffect}
121
+
122
+ cl {
123
+ def TodoList(todos: list) -> any {
124
+ return <ul>
125
+ {todos.map(lambda todo: any -> any {
126
+ return <li key={todo._jac_id}>{todo.text}</li>;
127
+ })}
128
+ </ul>;
129
+ }
99
130
 
100
- def jac_app() -> any {
101
- return App();
131
+ def app() -> any {
132
+ let [todos, setTodos] = useState([]);
133
+
134
+ useEffect(lambda -> None {
135
+ async def loadTodos() -> None {
136
+ result = root spawn read_todos();
137
+ setTodos(result.reports if result.reports else []);
138
+ }
139
+ loadTodos();
140
+ }, []);
141
+
142
+ return <div>
143
+ <h1>My Todos</h1>
144
+ <TodoList todos={todos} />
145
+ </div>;
146
+ }
102
147
  }
103
148
  ```
104
149
 
@@ -179,112 +224,104 @@ def TodoItem(item: dict) -> any {
179
224
 
180
225
  ---
181
226
 
182
- ## 🗄️ 4. Adding State
183
-
184
- State management in Jac uses `createState()`, which provides reactive state similar to React hooks but simpler.
227
+ ## 🗄️ 4. Adding State with React Hooks
185
228
 
186
- ### Understanding `createState()`
229
+ Jac uses React hooks for state management. You can use all standard React hooks by importing them:
187
230
 
188
231
  ```jac
189
- let [todoState, setTodoState] = createState({
190
- "items": [],
191
- "filter": "all",
192
- "input": ""
193
- });
194
- ```
195
-
196
- **How it works:**
197
- - `createState()` returns a tuple: `[getter, setter]`
198
- - **Getter** (`todoState`): A function that returns the current state value
199
- - **Setter** (`setTodoState`): A function that updates the state and triggers re-renders
232
+ cl import from react { useState, useEffect }
200
233
 
201
- ### Reading State
202
-
203
- Always call the getter as a function:
234
+ cl {
235
+ def Counter() -> any {
236
+ let [count, setCount] = useState(0);
204
237
 
205
- ```jac
206
- def TodoApp() -> any {
207
- s = todoState(); # Call getter function
238
+ useEffect(lambda -> None {
239
+ console.log("Count changed:", count);
240
+ }, [count]);
208
241
 
209
- return <div>
210
- <p>Total todos: {s.items.length}</p>
211
- <p>Current filter: {s.filter}</p>
212
- </div>;
242
+ return <div>
243
+ <h1>Count: {count}</h1>
244
+ <button onClick={lambda e: any -> None {
245
+ setCount(count + 1);
246
+ }}>
247
+ Increment
248
+ </button>
249
+ </div>;
250
+ }
213
251
  }
214
252
  ```
215
253
 
216
- ### Updating State
217
-
218
- The setter merges updates with existing state:
219
-
220
- ```jac
221
- # Update single property
222
- setTodoState({"filter": "active"});
223
-
224
- # Update multiple properties
225
- setTodoState({
226
- "filter": "completed",
227
- "input": "New todo text"
228
- });
254
+ **Available React Hooks:**
255
+ - `useState` - For state management
256
+ - `useEffect` - For side effects
257
+ - `useRef` - For refs
258
+ - `useContext` - For context
259
+ - `useCallback`, `useMemo` - For performance optimization
260
+ - And more...
229
261
 
230
- # Update arrays (create new array)
231
- s = todoState();
232
- setTodoState({"items": s.items.concat([newItem])});
233
- ```
262
+ ### State Management Example
234
263
 
235
- **Important:** The setter performs a **shallow merge**, meaning:
236
- - Existing properties are preserved
237
- - Only specified properties are updated
238
- - Arrays and objects are replaced, not merged deeply
239
-
240
- ### Complete State Example
264
+ Here's a complete example showing state management in a todo app:
241
265
 
242
266
  ```jac
267
+ cl import from react {useState, useEffect}
268
+
243
269
  cl {
244
- # Initialize state
245
- let [todoState, setTodoState] = createState({
246
- "items": [],
247
- "filter": "all",
248
- "input": ""
249
- });
250
-
251
- # Read state in component
252
- def TodoApp() -> any {
253
- s = todoState();
254
- itemsArr = s.items;
270
+ def app() -> any {
271
+ let [todos, setTodos] = useState([]);
272
+ let [input, setInput] = useState("");
273
+ let [filter, setFilter] = useState("all");
274
+
275
+ # Load todos on mount
276
+ useEffect(lambda -> None {
277
+ async def loadTodos() -> None {
278
+ result = root spawn read_todos();
279
+ setTodos(result.reports if result.reports else []);
280
+ }
281
+ loadTodos();
282
+ }, []);
283
+
284
+ # Add new todo
285
+ async def addTodo() -> None {
286
+ if not input.trim() { return; }
287
+ response = root spawn create_todo(text=input.trim());
288
+ new_todo = response.reports[0][0];
289
+ setTodos(todos.concat([new_todo]));
290
+ setInput("");
291
+ }
292
+
293
+ # Filter todos
294
+ def getFilteredTodos() -> list {
295
+ if filter == "active" {
296
+ return todos.filter(lambda todo: any -> bool { return not todo.done; });
297
+ } elif filter == "completed" {
298
+ return todos.filter(lambda todo: any -> bool { return todo.done; });
299
+ }
300
+ return todos;
301
+ }
302
+
303
+ filteredTodos = getFilteredTodos();
255
304
 
256
305
  return <div>
306
+ <h1>My Todos</h1>
257
307
  <input
258
- value={s.input}
259
- onChange={lambda e: any -> None {
260
- setTodoState({"input": e.target.value});
308
+ value={input}
309
+ onChange={lambda e: any -> None { setInput(e.target.value); }}
310
+ onKeyPress={lambda e: any -> None {
311
+ if e.key == "Enter" { addTodo(); }
261
312
  }}
262
313
  />
314
+ <button onClick={addTodo}>Add</button>
315
+
263
316
  <div>
264
- {[TodoItem(item) for item in itemsArr]}
317
+ {filteredTodos.map(lambda todo: any -> any {
318
+ return <div key={todo._jac_id}>
319
+ <span>{todo.text}</span>
320
+ </div>;
321
+ })}
265
322
  </div>
266
323
  </div>;
267
324
  }
268
-
269
- # Update state in event handler
270
- async def onAddTodo(e: any) -> None {
271
- e.preventDefault();
272
- text = document.getElementById("todo-input").value.trim();
273
-
274
- if not text { return; }
275
-
276
- # Create todo on backend
277
- new_todo = await __jacSpawn("create_todo", {"text": text});
278
-
279
- # Update frontend state
280
- s = todoState();
281
- newItem = {
282
- "id": new_todo._jac_id,
283
- "text": new_todo.text,
284
- "done": new_todo.done
285
- };
286
- setTodoState({"items": s.items.concat([newItem])});
287
- }
288
325
  }
289
326
  ```
290
327
 
@@ -310,11 +347,14 @@ def Button() -> any {
310
347
 
311
348
  ```jac
312
349
  def InputField() -> any {
350
+ let [value, setValue] = useState("");
351
+
313
352
  return <input
314
353
  type="text"
354
+ value={value}
315
355
  onChange={lambda e: any -> None {
316
356
  console.log("Input value:", e.target.value);
317
- setTodoState({"input": e.target.value});
357
+ setValue(e.target.value);
318
358
  }}
319
359
  />;
320
360
  }
@@ -338,7 +378,8 @@ async def onAddTodo(e: any) -> None {
338
378
  if not text { return; }
339
379
 
340
380
  # Handle form submission
341
- new_todo = await __jacSpawn("create_todo", {"text": text});
381
+ response = root spawn create_todo(text=text);
382
+ new_todo = response.reports[0][0];
342
383
  # ... update state
343
384
  }
344
385
  ```
@@ -355,13 +396,12 @@ async def onAddTodo(e: any) -> None {
355
396
  ### Advanced: Conditional Event Handlers
356
397
 
357
398
  ```jac
358
- def FilterButton(filterType: str) -> any {
359
- s = todoState();
360
- isActive = s.filter == filterType;
399
+ def FilterButton(filterType: str, currentFilter: str, onFilterChange: any) -> any {
400
+ isActive = currentFilter == filterType;
361
401
 
362
402
  return <button
363
403
  onClick={lambda -> None {
364
- setFilter(filterType);
404
+ onFilterChange(filterType);
365
405
  }}
366
406
  style={{
367
407
  "background": ("#7C3AED" if isActive else "#FFFFFF"),
@@ -379,7 +419,7 @@ def FilterButton(filterType: str) -> any {
379
419
 
380
420
  One of Jac's most powerful features is **seamless backend communication** without writing HTTP requests, fetch calls, or axios code.
381
421
 
382
- ### The `__jacSpawn()` Function
422
+ ### The `spawn` Syntax
383
423
 
384
424
  Instead of writing:
385
425
  ```javascript
@@ -394,30 +434,32 @@ const data = await response.json();
394
434
 
395
435
  You simply write:
396
436
  ```jac
397
- new_todo = await __jacSpawn("create_todo", {"text": "New todo"});
437
+ response = root spawn create_todo(text="New todo");
398
438
  ```
399
439
 
400
- ### How `__jacSpawn()` Works
440
+ ### How `spawn` Works
401
441
 
402
442
  ```jac
403
- # Call a walker on the backend
404
- result = await __jacSpawn(
405
- walker_name, # Name of the walker to execute
406
- fields, # Dictionary of parameters to pass
407
- node_id # Optional: specific node ID (defaults to "root")
408
- );
443
+ # Call a walker on a node
444
+ result = node_reference spawn walker_name(parameters);
409
445
  ```
410
446
 
447
+ **Syntax:**
448
+ - `node_reference` - The node to spawn the walker on (commonly `root` for the root node, or a node ID)
449
+ - `spawn` - The spawn keyword
450
+ - `walker_name` - Name of the walker to execute
451
+ - `parameters` - Parameters to pass to the walker (as function arguments)
452
+
411
453
  **Example from Todo App:**
412
454
  ```jac
413
- # Create a new todo
414
- new_todo = await __jacSpawn("create_todo", {"text": text});
455
+ # Create a new todo (spawn on root node)
456
+ response = root spawn create_todo(text=text);
415
457
 
416
- # Toggle todo status
417
- toggled_todo = await __jacSpawn("toggle_todo", {}, todo_id);
458
+ # Toggle todo status (spawn on specific todo node)
459
+ id spawn toggle_todo();
418
460
 
419
461
  # Read all todos
420
- todos = await __jacSpawn("read_todos");
462
+ todos = root spawn read_todos();
421
463
  ```
422
464
 
423
465
  ### Backend Walkers
@@ -433,8 +475,9 @@ node Todo {
433
475
 
434
476
  # Walker: Create a new todo
435
477
  walker create_todo {
478
+ has text: str;
436
479
  can create with `root entry {
437
- new_todo = here ++> Todo(text="Example Todo");
480
+ new_todo = here ++> Todo(text=self.text);
438
481
  report new_todo;
439
482
  }
440
483
  }
@@ -469,7 +512,8 @@ async def onAddTodo(e: any) -> None {
469
512
  if not text { return; }
470
513
 
471
514
  # Call backend walker - no fetch/axios needed!
472
- new_todo = await __jacSpawn("create_todo", {"text": text});
515
+ response = root spawn create_todo(text=text);
516
+ new_todo = response.reports[0][0];
473
517
 
474
518
  # Update frontend state
475
519
  s = todoState();
@@ -485,16 +529,16 @@ async def onAddTodo(e: any) -> None {
485
529
  **Backend (outside `cl` block):**
486
530
  ```jac
487
531
  walker create_todo {
532
+ has text: str;
488
533
  can create with `root entry {
489
- # 'text' comes from the fields dictionary passed to __jacSpawn
490
- text = context.text;
491
- new_todo = here ++> Todo(text=text);
534
+ # 'text' comes from the walker parameter
535
+ new_todo = here ++> Todo(text=self.text);
492
536
  report new_todo;
493
537
  }
494
538
  }
495
539
  ```
496
540
 
497
- ### Benefits of `__jacSpawn()`
541
+ ### Benefits of `spawn`
498
542
 
499
543
  ✅ **No HTTP Configuration**: No need to set up API endpoints, CORS, or request/response formats
500
544
  ✅ **Type Safety**: Jac handles serialization automatically
@@ -502,6 +546,7 @@ walker create_todo {
502
546
  ✅ **Error Handling**: Exceptions are properly propagated
503
547
  ✅ **Graph Operations**: Direct access to graph-based data operations
504
548
  ✅ **Less Code**: Eliminates boilerplate HTTP client code
549
+ ✅ **Natural Syntax**: Call walkers on nodes using intuitive syntax
505
550
 
506
551
  ### Authentication Helpers
507
552
 
@@ -546,8 +591,9 @@ node Todo {
546
591
  }
547
592
 
548
593
  walker create_todo {
594
+ has text: str;
549
595
  can create with `root entry {
550
- new_todo = here ++> Todo(text=context.text);
596
+ new_todo = here ++> Todo(text=self.text);
551
597
  report new_todo;
552
598
  }
553
599
  }
@@ -555,37 +601,41 @@ walker create_todo {
555
601
  # ============================================================================
556
602
  # FRONTEND: Components, State, and Events
557
603
  # ============================================================================
604
+ cl import from react {useState}
605
+
558
606
  cl {
559
- # State
560
- let [todoState, setTodoState] = createState({
561
- "items": [],
562
- "filter": "all"
563
- });
564
-
565
- # Component
566
- def TodoApp() -> any {
567
- s = todoState();
607
+ def app() -> any {
608
+ let [todos, setTodos] = useState([]);
609
+ let [input, setInput] = useState("");
610
+
611
+ # Event Handler
612
+ async def addTodo() -> None {
613
+ if not input.trim() { return; }
614
+ response = root spawn create_todo(text=input.trim());
615
+ new_todo = response.reports[0][0];
616
+ setTodos(todos.concat([new_todo]));
617
+ setInput("");
618
+ }
619
+
568
620
  return <div>
569
621
  <h2>My Todos</h2>
570
- <form onSubmit={onAddTodo}>
571
- <input id="todo-input" type="text" />
572
- <button type="submit">Add Todo</button>
573
- </form>
574
- {[TodoItem(item) for item in s.items]}
575
- </div>;
576
- }
577
-
578
- # Event Handler
579
- async def onAddTodo(e: any) -> None {
580
- e.preventDefault();
581
- text = document.getElementById("todo-input").value;
582
- new_todo = await __jacSpawn("create_todo", {"text": text});
583
- # Update state...
584
- }
622
+ <input
623
+ value={input}
624
+ onChange={lambda e: any -> None { setInput(e.target.value); }}
625
+ onKeyPress={lambda e: any -> None {
626
+ if e.key == "Enter" { addTodo(); }
627
+ }}
628
+ />
629
+ <button onClick={addTodo}>Add Todo</button>
585
630
 
586
- # Entry Point
587
- def jac_app() -> any {
588
- return TodoApp();
631
+ <div>
632
+ {todos.map(lambda todo: any -> any {
633
+ return <div key={todo._jac_id}>
634
+ <span>{todo.text}</span>
635
+ </div>;
636
+ })}
637
+ </div>
638
+ </div>;
589
639
  }
590
640
  }
591
641
  ```
@@ -609,19 +659,29 @@ Then visit `http://localhost:8000` in your browser.
609
659
 
610
660
  Ready to dive deeper? Explore these advanced topics:
611
661
 
612
- - **[Routing](routing.md)**: Learn about `initRouter()` for multi-page applications
613
- - **[Lifecycle Hooks](lifecycle-hooks.md)**: Use `onMount()` for initialization logic
614
- - **[Advanced State](advanced-state.md)**: Combine multiple `createState()` calls for complex apps
615
- - **[Imports](imports.md)**: Import third-party libraries, other Jac files, and JavaScript modules
662
+ - **[Routing](routing.md)**: Build multi-page apps with declarative routing (`<Router>`, `<Routes>`, `<Route>`)
663
+ - **[Lifecycle Hooks](lifecycle-hooks.md)**: Use `onMount()` and React hooks for initialization logic
664
+ - **[Advanced State](advanced-state.md)**: Manage complex state with React hooks and context
665
+ - **[Imports](imports.md)**: Import third-party libraries (React, Ant Design, Lodash), other Jac files, and JavaScript modules
616
666
  - **[Learn JAC](https://www.jac-lang.org)**: Explore Jac's graph-based data modeling
617
667
 
668
+ ## 🎓 Examples
669
+
670
+ Check out the `examples/` directory for working applications:
671
+
672
+ - **[basic](../../examples/basic/)**: Simple counter app using React hooks
673
+ - **[with-router](../../examples/with-router/)**: Multi-page app with declarative routing
674
+ - **[little-x](../../examples/little-x/)**: Social media app with third-party libraries
675
+ - **[todo-app](../../examples/todo-app/)**: Full-featured todo app with authentication
676
+ - **[basic-full-stack](../../examples/basic-full-stack/)**: Full-stack app with backend integration
677
+
618
678
  ---
619
679
 
620
680
  ## 💡 Key Takeaways
621
681
 
622
682
  1. **Single Language**: Write frontend and backend in Jac
623
- 2. **No HTTP Client**: Use `__jacSpawn()` instead of fetch/axios
624
- 3. **Reactive State**: `createState()` manages UI updates automatically
683
+ 2. **No HTTP Client**: Use `spawn` syntax instead of fetch/axios
684
+ 3. **Reactive State**: React hooks manage UI updates automatically
625
685
  4. **Component-Based**: Build reusable UI components with JSX
626
686
  5. **Type Safety**: Jac provides type checking across frontend and backend
627
687
  6. **Graph Database**: Built-in graph data model eliminates need for SQL/NoSQL