@gramlot/gramlot-examples 0.2.10 → 0.2.12

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 (67) hide show
  1. package/README.md +4 -3
  2. package/package.json +3 -3
  3. package/src/gramlot_examples/catalog.json +42 -3
  4. package/src/gramlot_examples/gallery/dist/frame.js +1473 -5
  5. package/src/gramlot_examples/gallery/dist/gallery.js +3822 -17162
  6. package/src/gramlot_examples/gallery/dist/notices.json +2 -22
  7. package/src/gramlot_examples/gallery/dist/theme-styles.js +2 -0
  8. package/src/gramlot_examples/gallery/gallery-page.js +38 -67
  9. package/src/gramlot_examples/gallery/gallery.css +22 -94
  10. package/src/gramlot_examples/index.js +2 -1
  11. package/src/gramlot_examples/pages/README.md +87 -4
  12. package/src/gramlot_examples/pages/binding/01_pointers.js +9 -0
  13. package/src/gramlot_examples/pages/binding/01_pointers.md +56 -9
  14. package/src/gramlot_examples/pages/binding/02_variable_datapath.md +50 -4
  15. package/src/gramlot_examples/pages/binding/03_setters_and_defaults.md +45 -6
  16. package/src/gramlot_examples/pages/binding/04_native_editing.md +48 -6
  17. package/src/gramlot_examples/pages/binding/05_checkbox_radio.md +44 -4
  18. package/src/gramlot_examples/pages/binding/06_style_and_visibility.md +40 -6
  19. package/src/gramlot_examples/pages/binding/07_bound_svg.md +48 -3
  20. package/src/gramlot_examples/pages/binding/08_freeze.js +1 -1
  21. package/src/gramlot_examples/pages/binding/08_freeze.md +44 -6
  22. package/src/gramlot_examples/pages/binding/09_forms.js +2 -1
  23. package/src/gramlot_examples/pages/binding/09_forms.md +53 -7
  24. package/src/gramlot_examples/pages/binding/10_svg_shapes.md +50 -5
  25. package/src/gramlot_examples/pages/binding/11_complete_page.md +53 -7
  26. package/src/gramlot_examples/pages/binding/README.md +34 -0
  27. package/src/gramlot_examples/pages/controllers/01_formula.md +48 -7
  28. package/src/gramlot_examples/pages/controllers/02_controller.js +24 -2
  29. package/src/gramlot_examples/pages/controllers/02_controller.md +61 -6
  30. package/src/gramlot_examples/pages/controllers/03_named_logic.md +63 -7
  31. package/src/gramlot_examples/pages/controllers/04_inline_expressions.md +50 -7
  32. package/src/gramlot_examples/pages/controllers/05_node_methods.md +41 -7
  33. package/src/gramlot_examples/pages/controllers/06_button_controller.md +49 -6
  34. package/src/gramlot_examples/pages/controllers/07_events.md +45 -6
  35. package/src/gramlot_examples/pages/controllers/08_end_to_end.js +26 -1
  36. package/src/gramlot_examples/pages/controllers/08_end_to_end.md +138 -9
  37. package/src/gramlot_examples/pages/controllers/09_endpoint.md +47 -7
  38. package/src/gramlot_examples/pages/controllers/10_call_on_change.md +48 -8
  39. package/src/gramlot_examples/pages/controllers/11_call_on_click.js +6 -0
  40. package/src/gramlot_examples/pages/controllers/11_call_on_click.md +58 -6
  41. package/src/gramlot_examples/pages/controllers/12_remote.js +33 -0
  42. package/src/gramlot_examples/pages/controllers/12_remote.md +60 -0
  43. package/src/gramlot_examples/pages/controllers/13_cached_remote.js +44 -0
  44. package/src/gramlot_examples/pages/controllers/13_cached_remote.md +67 -0
  45. package/src/gramlot_examples/pages/controllers/14_data_remote.js +39 -0
  46. package/src/gramlot_examples/pages/controllers/14_data_remote.md +69 -0
  47. package/src/gramlot_examples/pages/controllers/15_lifecycle.js +51 -0
  48. package/src/gramlot_examples/pages/controllers/15_lifecycle.md +59 -0
  49. package/src/gramlot_examples/pages/controllers/16_attribute_projection.js +25 -0
  50. package/src/gramlot_examples/pages/controllers/16_attribute_projection.md +58 -0
  51. package/src/gramlot_examples/pages/controllers/17_data_exchange.js +51 -0
  52. package/src/gramlot_examples/pages/controllers/17_data_exchange.md +61 -0
  53. package/src/gramlot_examples/pages/controllers/README.md +57 -3
  54. package/src/gramlot_examples/pages/html_svg/01_hello_world.md +48 -4
  55. package/src/gramlot_examples/pages/html_svg/02_text_and_links.md +42 -4
  56. package/src/gramlot_examples/pages/html_svg/03_lists.md +45 -4
  57. package/src/gramlot_examples/pages/html_svg/04_semantic_page.md +44 -4
  58. package/src/gramlot_examples/pages/html_svg/05_tables.md +46 -4
  59. package/src/gramlot_examples/pages/html_svg/06_forms.md +48 -4
  60. package/src/gramlot_examples/pages/html_svg/07_native_disclosure.md +41 -4
  61. package/src/gramlot_examples/pages/html_svg/08_svg_shapes.md +45 -4
  62. package/src/gramlot_examples/pages/html_svg/09_svg_composition.md +44 -4
  63. package/src/gramlot_examples/pages/html_svg/10_cards_with_icons.md +42 -15
  64. package/src/gramlot_examples/pages/html_svg/11_static_report.md +37 -4
  65. package/src/gramlot_examples/pages/html_svg/12_complete_page.md +41 -4
  66. package/src/gramlot_examples/pages/html_svg/13_live_source.md +47 -16
  67. package/src/gramlot_examples/pages/html_svg/README.md +36 -0
@@ -1,9 +1,55 @@
1
1
  # 02 · Variable datapath
2
2
 
3
- A `datapath` can itself be a pointer. `datapath='^current'` reads the Data path `current` and uses its value as the context of the card.
3
+ One card can show different records. Change its Data context and watch all its fields switch together: you learn how a variable `datapath` avoids rebuilding the card.
4
4
 
5
- The select writes `people.ada`, `people.alan` or `people.grace` into `current`. Every `.name`, `.field` and `.born` pointer in the card then reads the chosen record, and the field below edits it.
5
+ **Uses:** Source, Data, pointers and native binding — core 0.2.0. No endpoint calls.
6
6
 
7
- The `people` records come from one `dataSetter` with a dictionary (Python) or a plain object (JavaScript): it becomes a Bag in the Data.
7
+ <details>
8
+ <summary>More detail</summary>
8
9
 
9
- **Try it:** Rename Alan, choose Grace, then choose Alan again: his record keeps the new name.
10
+
11
+ **Release stage:** Data binding and browser behavior (core 0.2.0).
12
+
13
+ **Goal:** Retarget a whole card by changing its Data context.
14
+
15
+ **Before this lesson:** [b01 · Pointers](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/binding/01_pointers.md), [b04 · Native editing](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/binding/04_native_editing.md). These prerequisites support the release sequence; later comparisons are explicitly labelled below.
16
+
17
+ **Open and edit:** select **b02 · Variable datapath** in your environment’s gallery. For source changes, copy/edit the matching page in your own project using the [setup guide](https://github.com/gramlot-org/gramlot/blob/main/docs/public/025-try.md); the gallery is a source viewer. Save, then reload/reopen as that guide explains. The snippets below are excerpts/edits within that Page, not standalone launch commands.
18
+
19
+ ## Read the page
20
+
21
+ The select stores a **path string**, such as `people.alan`, in `current`. The card’s `datapath="^current"` follows that choice; all `.name`, `.field` and `.born` references resolve within the selected record.
22
+
23
+ ```python
24
+ card = pane.article(datapath="^current", class_="card stack")
25
+ card.h2("^.name", id="name")
26
+ card.input(value="^.name", id="name-field")
27
+ ```
28
+
29
+ The dictionary becomes a nested Data Bag through `dataSetter`.
30
+
31
+ Python builds Source on the server; its JavaScript counterpart builds it on Node/Bun or in a standalone Worker. Bindings, actions and any Logic execute in the browser; Python strings containing JavaScript are not executed by Python.
32
+
33
+ ## Try it and predict the result
34
+
35
+ 1. Start with Ada Lovelace, Mathematics, 1815.
36
+ 2. Choose Alan, edit his name to “A. M. Turing”, and leave the field.
37
+ 3. Choose Grace, then Alan again: his edited name remains; Grace’s record is unchanged.
38
+
39
+ ## Change it and check yourself
40
+
41
+ Add another person to `PEOPLE` with `name`, `field` and `born`; reload and choose them.
42
+
43
+ **Answer:** the existing select loop adds an option and the same card shows the new record. No separate handlers are needed for the three displays or edit field.
44
+
45
+ ## The same example across stages
46
+
47
+ Compared with b01’s fixed context, one variable `datapath` retargets the same declarations together. In the later 0.2.16 c13 lesson this very technique selects a product while remote rules depend only on its category.
48
+
49
+ ## Limits and next step
50
+
51
+ The selected value must be a Data path, not a record object. Reload reconstructs the initial records; local edits are not persistence. Empty/null contexts are an advanced reference case, not a new record editor.
52
+
53
+ Continue with [b03 · Setters and defaults](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/binding/03_setters_and_defaults.md). The [authoring reference](https://github.com/gramlot-org/gramlot/blob/main/docs/public/095-writing-pages.md) explains the full contract.
54
+
55
+ </details>
@@ -1,10 +1,49 @@
1
1
  # 03 · Setters and defaults
2
2
 
3
- `dataSetter` writes a value in the Data. All the setters of a branch run before its first render, wherever they sit: here they come after the form and in a later section, yet the first DOM already shows their values.
3
+ Fields start with values supplied by setters and defaults. You learn when those values are applied and why an existing zero differs from a missing value.
4
4
 
5
- - Setters run parents before descendants, in document order. Two setters write `.customer`; the one in the later section runs last, so the customer is Grace Hopper.
6
- - A dictionary (Python) or a plain object (JavaScript) becomes a Bag: `.shipping.city` reads inside it.
7
- - A control fills an empty value path with its `default`. `default_value` wins over `default`. Only a null or missing value is empty: `0`, `false` and `''` are values.
8
- - `attr_currency='EUR'` sets the attribute `currency` on the Data node of the control's value, after the defaults. `^.price?currency` reads it.
5
+ **Uses:** Source, Data, pointers and native binding — core 0.2.0. No endpoint calls.
9
6
 
10
- **Try it:** Remove the setter in the later section: the customer becomes Ada Lovelace. Add a setter of `.quantity` with `0`: the default no longer applies.
7
+ <details>
8
+ <summary>More detail</summary>
9
+
10
+
11
+ **Release stage:** Data binding and browser behavior (core 0.2.0).
12
+
13
+ **Goal:** Predict initialization order and distinguish an absent value from zero.
14
+
15
+ **Before this lesson:** [b01 · Pointers](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/binding/01_pointers.md). These prerequisites support the release sequence; later comparisons are explicitly labelled below.
16
+
17
+ **Open and edit:** select **b03 · Setters and defaults** in your environment’s gallery. For source changes, copy/edit the matching page in your own project using the [setup guide](https://github.com/gramlot-org/gramlot/blob/main/docs/public/025-try.md); the gallery is a source viewer. Save, then reload/reopen as that guide explains. The snippets below are excerpts/edits within that Page, not standalone launch commands.
18
+
19
+ ## Read the page
20
+
21
+ Setters run before first render, parents before descendants in Source order. The later `.customer` setter wins, so the first display is Grace Hopper. The nested shipping dictionary supplies London.
22
+
23
+ ```python
24
+ form.input(type="number", value="^.quantity", default=1, id="quantity")
25
+ form.input(value="^.note", default_value="No note", default="Never used", id="note")
26
+ ```
27
+
28
+ `default_value` wins over `default`; defaults fill null/missing paths, not `0`, `False` or `""`. `attr_currency="EUR"` sets metadata on the price Data node; `^.price?currency` reads it.
29
+
30
+ Python builds Source on the server; its JavaScript counterpart builds it on Node/Bun or in a standalone Worker. Bindings, actions and any Logic execute in the browser; Python strings containing JavaScript are not executed by Python.
31
+
32
+ ## Try it and predict the result
33
+
34
+ 1. Before editing, predict Customer = Grace Hopper, Quantity = 1, Note = No note, Price = 12.5, Currency = EUR and City = London.
35
+ 2. Find the later setter below the form: visual position does not delay initialization.
36
+
37
+ ## Change it and check yourself
38
+
39
+ Remove only the later `.customer` setter and add `pane.dataSetter(".quantity", 0)`.
40
+
41
+ **Answer:** after reload Customer is Ada Lovelace and Quantity is 0. The earlier customer setter now wins; the quantity default cannot overwrite a real zero.
42
+
43
+ ## Limits and next step
44
+
45
+ These are build-time defaults, not record/new-record defaults or automatic refill whenever a user clears a control. Destination paths are bare Data paths, not `^` pointers or `?attr` destinations.
46
+
47
+ Continue with [b04 · Native editing](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/binding/04_native_editing.md). The [authoring reference](https://github.com/gramlot-org/gramlot/blob/main/docs/public/095-writing-pages.md) explains the full contract.
48
+
49
+ </details>
@@ -1,11 +1,53 @@
1
1
  # 04 · Native editing
2
2
 
3
- A control whose `value` is a `^` pointer shows the Data and writes it back when you edit.
3
+ A form displays the Data written by each control. Switch **live** on and off: you learn when typing updates Data and whether each control writes text, a number or a list.
4
4
 
5
- - Text and textarea write a string; number and range write a number, or null when the field is empty; date writes its text such as `2026-09-30`; color writes the color string.
6
- - A single select writes the chosen value; a multiple select writes a list.
7
- - With `live` false a field writes at `change`, when you leave it or press Enter. With `live` true text, textarea, number and range write at every `input`. Here `live='^.live'` follows the checkbox, without rebuilding the fields.
5
+ **Uses:** Source, Data, pointers and native binding — core 0.2.0. No endpoint calls.
8
6
 
9
- The list on the right reads the same Data paths.
7
+ <details>
8
+ <summary>More detail</summary>
10
9
 
11
- **Try it:** Type in the text field with the checkbox off, then on, and compare when the list changes.
10
+
11
+ **Release stage:** Data binding and browser behavior (core 0.2.0).
12
+
13
+ **Goal:** Observe when a control writes Data and which type it writes.
14
+
15
+ **Before this lesson:** [e06 · Forms](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/html_svg/06_forms.md), [b01 · Pointers](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/binding/01_pointers.md). These prerequisites support the release sequence; later comparisons are explicitly labelled below.
16
+
17
+ **Open and edit:** select **b04 · Native editing** in your environment’s gallery. For source changes, copy/edit the matching page in your own project using the [setup guide](https://github.com/gramlot-org/gramlot/blob/main/docs/public/025-try.md); the gallery is a source viewer. Save, then reload/reopen as that guide explains. The snippets below are excerpts/edits within that Page, not standalone launch commands.
18
+
19
+ ## Read the page
20
+
21
+ Text and textarea write strings; number/range write numbers or null for an empty value; dates keep their string representation. Single select writes one value, multiple select a list. The checkbox controls `live` for the other fields.
22
+
23
+ ```python
24
+ form.input(type=kind, value=f"^.{key}", live="^.live", id=key)
25
+ ```
26
+
27
+ The right-hand values read those same paths. There is no application input-scraping handler.
28
+
29
+ Python builds Source on the server; its JavaScript counterpart builds it on Node/Bun or in a standalone Worker. Bindings, actions and any Logic execute in the browser; Python strings containing JavaScript are not executed by Python.
30
+
31
+ ## Try it and predict the result
32
+
33
+ 1. With live unchecked, type in Text without leaving it: the Data display stays “Edit me”. Leave the field: it updates.
34
+ 2. Check live and type again: the display follows each input event.
35
+ 3. Choose size L and change Topics: the Data stores a value and a list respectively.
36
+
37
+ ## Change it and check yourself
38
+
39
+ Clear Number while live is on, then enter 7.
40
+
41
+ **Answer:** the empty control writes null (its display is empty), then numeric 7. It does not write the strings `"null"` or `"7"`. A date is still a date string; editing does not make it a Python datetime.
42
+
43
+ ## The same example across stages
44
+
45
+ In e06 the browser edits native control values only. Here `value="^.text"` plus a Data setter replaces the literal default and connects editing to application state. The mirrored display needs only another pointer; no per-control copying routine is introduced.
46
+
47
+ ## Limits and next step
48
+
49
+ Use `change`/leaving the field for the non-live trial; do not rely on Enter having identical behavior in every native control. Browser validation and picker presentation vary.
50
+
51
+ Continue with [b05 · Checkbox and radio](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/binding/05_checkbox_radio.md). The [authoring reference](https://github.com/gramlot-org/gramlot/blob/main/docs/public/095-writing-pages.md) explains the full contract.
52
+
53
+ </details>
@@ -1,9 +1,49 @@
1
1
  # 05 · Checkbox and radio
2
2
 
3
- A checkbox bound with `value='^path'` reads a boolean and writes `true` or `false`, never the text `on`.
3
+ Checkboxes and radio buttons control boolean values. You learn how to represent yes/no choices and how radio buttons keep a single choice selected.
4
4
 
5
- Radio buttons with the same `group` form one choice. Each button owns its own boolean path. Choosing one writes `true` in its path and `false` in the path of the button that was on. Gramlot generates the DOM `name` of the group, so two pages or two groups never mix.
5
+ **Uses:** Source, Data, pointers and native binding — core 0.2.0. No endpoint calls.
6
6
 
7
- Only `plan.plus` has a setter. `plan.basic` and `plan.team` start empty; a button that is already off is not written, so an empty path stays empty until its button is chosen once.
7
+ <details>
8
+ <summary>More detail</summary>
8
9
 
9
- **Try it:** Choose Team, then Basic, and watch which Data values change; then add a fourth plan.
10
+
11
+ **Release stage:** Data binding and browser behavior (core 0.2.0).
12
+
13
+ **Goal:** Model checkboxes and a radio group as boolean Data.
14
+
15
+ **Before this lesson:** [b04 · Native editing](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/binding/04_native_editing.md). These prerequisites support the release sequence; later comparisons are explicitly labelled below.
16
+
17
+ **Open and edit:** select **b05 · Checkbox and radio** in your environment’s gallery. For source changes, copy/edit the matching page in your own project using the [setup guide](https://github.com/gramlot-org/gramlot/blob/main/docs/public/025-try.md); the gallery is a source viewer. Save, then reload/reopen as that guide explains. The snippets below are excerpts/edits within that Page, not standalone launch commands.
18
+
19
+ ## Read the page
20
+
21
+ A checkbox’s bound `value` is boolean. Each radio has a separate boolean path; `group="plan"` makes them mutually exclusive. The runtime supplies the DOM group name.
22
+
23
+ ```python
24
+ row.input(type="radio", group="plan", value=f"^.plan.{key}", id=key)
25
+ ```
26
+
27
+ Only `.plan.plus` starts true. Basic and Team begin null; selecting one writes it true and writes the previously selected path false.
28
+
29
+ Python builds Source on the server; its JavaScript counterpart builds it on Node/Bun or in a standalone Worker. Bindings, actions and any Logic execute in the browser; Python strings containing JavaScript are not executed by Python.
30
+
31
+ ## Try it and predict the result
32
+
33
+ 1. Newsletter is checked, Paper invoice is off and Plus is selected.
34
+ 2. Choose Team: Plus becomes false, Team true, Basic remains empty.
35
+ 3. Choose Basic: Basic becomes true, Team false, Plus stays false.
36
+
37
+ ## Change it and check yourself
38
+
39
+ Add `("pro", "Pro")` to `PLANS` and include `plan.pro` in the displayed key list.
40
+
41
+ **Answer:** the new radio joins the same group. On selection it becomes true and the old selection becomes false. No manual loop clearing all radio paths is necessary.
42
+
43
+ ## Limits and next step
44
+
45
+ A radio’s bound value is not a string enum. Unselected paths need not all have been written false. Use the existing `group` contract rather than assigning a shared native name manually.
46
+
47
+ Continue with [b06 · Style and visibility](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/binding/06_style_and_visibility.md). The [authoring reference](https://github.com/gramlot-org/gramlot/blob/main/docs/public/095-writing-pages.md) explains the full contract.
48
+
49
+ </details>
@@ -1,11 +1,45 @@
1
1
  # 06 · Style and visibility
2
2
 
3
- Every attribute of the box is a `^` pointer, so the controls restyle it through the Data.
3
+ Controls change an element’s appearance and visibility. You learn how Data drives styles and classes, and how hiding content differs from removing its space in the layout.
4
4
 
5
- - `class` and `style` take a string, or null to remove the attribute.
6
- - The style shortcuts `color`, `background_color`, `font_size`, `padding` and the macro `rounded` compose one `style` attribute with `style` itself. A shortcut wins over the same property written in `style`; a null shortcut drops only its own property.
7
- - `visible` false sets `visibility: hidden`: the box disappears and keeps its space. `hidden` is the native attribute: the box leaves the layout.
5
+ **Uses:** Source, Data, pointers and native binding — core 0.2.0. No endpoint calls.
8
6
 
9
- Values need their CSS unit: `font_size='1.5rem'`, not `1.5`.
7
+ <details>
8
+ <summary>More detail</summary>
10
9
 
11
- **Try it:** Write `color: red` in the style field: the shortcut `color` still wins. Then clear the visible checkbox, and afterwards check hidden.
10
+
11
+ **Release stage:** Data binding and browser behavior (core 0.2.0).
12
+
13
+ **Goal:** Predict attribute precedence and distinguish visibility from layout removal.
14
+
15
+ **Before this lesson:** [b04 · Native editing](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/binding/04_native_editing.md), [b05 · Checkbox and radio](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/binding/05_checkbox_radio.md). These prerequisites support the release sequence; later comparisons are explicitly labelled below.
16
+
17
+ **Open and edit:** select **b06 · Style and visibility** in your environment’s gallery. For source changes, copy/edit the matching page in your own project using the [setup guide](https://github.com/gramlot-org/gramlot/blob/main/docs/public/025-try.md); the gallery is a source viewer. Save, then reload/reopen as that guide explains. The snippets below are excerpts/edits within that Page, not standalone launch commands.
18
+
19
+ ## Read the page
20
+
21
+ The box binds class, style, color, background, size, radius and visibility to Data. Supported class/style values are strings or null. CSS shortcuts compose the style and take precedence over a duplicate property in `style`.
22
+
23
+ `visible=False` sets hidden visibility but retains space; native `hidden=True` removes the box from layout. Use CSS units such as `1.5rem` for font size.
24
+
25
+ Python builds Source on the server; its JavaScript counterpart builds it on Node/Bun or in a standalone Worker. Bindings, actions and any Logic execute in the browser; Python strings containing JavaScript are not executed by Python.
26
+
27
+ ## Try it and predict the result
28
+
29
+ 1. Expect a blue box, white text, dashed border, 1.5rem font and rounded corners.
30
+ 2. Replace the style field with `color: red` and leave it: the color shortcut still makes the text white; replacing the style string also removes its old border declaration.
31
+ 3. Uncheck visible: the space remains. Then check hidden: the space collapses.
32
+
33
+ ## Change it and check yourself
34
+
35
+ Restore visible, clear hidden and choose 2rem with 24px corners.
36
+
37
+ **Answer:** the same box changes appearance from Data; its text and Source structure are unchanged. A null shortcut removes its own property, not every other style declaration.
38
+
39
+ ## Limits and next step
40
+
41
+ Stay with the agreed string/null contract. The development review has a pending renderer-parity correction for rejection of unsupported class/style types; this lesson does not teach object coercions or rely on them.
42
+
43
+ Continue with [b07 · Bound SVG](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/binding/07_bound_svg.md). The [authoring reference](https://github.com/gramlot-org/gramlot/blob/main/docs/public/095-writing-pages.md) explains the full contract.
44
+
45
+ </details>
@@ -1,7 +1,52 @@
1
1
  # 07 · Bound SVG
2
2
 
3
- SVG attributes take `^` pointers as HTML attributes do. The circle's `r` and `fill`, the bar's `width` and `fill`, and the label text read Data paths edited by the controls.
3
+ Controls and an SVG drawing share the same values. Change a value and watch the shape follow: binding works for SVG geometry as well as HTML text.
4
4
 
5
- The ranges have `live` true, so the drawing follows the slider while you drag it. SVG has no style shortcuts: `fill` and `stroke` are SVG presentation attributes.
5
+ **Uses:** Source, Data, pointers and native binding — core 0.2.0. No endpoint calls.
6
6
 
7
- **Try it:** Bind the circle's `cy` to a third range.
7
+ <details>
8
+ <summary>More detail</summary>
9
+
10
+
11
+ **Release stage:** Data binding and browser behavior (core 0.2.0).
12
+
13
+ **Goal:** Use the same Data value for a native control and SVG geometry.
14
+
15
+ **Before this lesson:** [e08 · SVG shapes](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/html_svg/08_svg_shapes.md), [b04 · Native editing](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/binding/04_native_editing.md). These prerequisites support the release sequence; later comparisons are explicitly labelled below.
16
+
17
+ **Open and edit:** select **b07 · Bound SVG** in your environment’s gallery. For source changes, copy/edit the matching page in your own project using the [setup guide](https://github.com/gramlot-org/gramlot/blob/main/docs/public/025-try.md); the gallery is a source viewer. Save, then reload/reopen as that guide explains. The snippets below are excerpts/edits within that Page, not standalone launch commands.
18
+
19
+ ## Read the page
20
+
21
+ The circle’s radius/fill and bar’s width/fill are pointers; their controls write the same paths. Radius and width ranges are live. SVG `fill` and `stroke` are presentation attributes, not HTML style shortcuts.
22
+
23
+ ```python
24
+ controls.input(type="range", min=5, max=80, value="^.radius", live=True, id="radius")
25
+ art.circle(cx=100, cy=100, r="^.radius", fill="^.fill", id="circle")
26
+ art.text("^.radius", x=100, y=190, text_anchor="middle", fill="currentColor", id="radius-label")
27
+ ```
28
+
29
+ Python builds Source on the server; its JavaScript counterpart builds it on Node/Bun or in a standalone Worker. Bindings, actions and any Logic execute in the browser; Python strings containing JavaScript are not executed by Python.
30
+
31
+ ## Try it and predict the result
32
+
33
+ 1. The circle begins at radius 40; its numeric label also shows 40. The bar width is 160.
34
+ 2. Drag Radius to 60: both circle and label follow, while the bar remains unchanged.
35
+
36
+ ## Change it and check yourself
37
+
38
+ Add a range bound to `.cy` (min 40, max 160), initialize it with `pane.dataSetter(".cy", 100)`, and replace the circle’s `cy=100` with `cy="^.cy"`.
39
+
40
+ **Answer:** moving the new range moves the same circle vertically; radius remains controlled independently. There is no new Source lookup or attribute-setting action.
41
+
42
+ ## The same example across stages
43
+
44
+ Earlier e13 explicitly called `setAttr` on a Source node for each animated coordinate. For user-controlled geometry here, one pointer replaces repeated update calls. The two lessons solve different triggers: this is a simplification of value synchronization, not an identical animation.
45
+
46
+ ## Limits and next step
47
+
48
+ The range has to remain compatible with the viewBox; a large radius near an edge can be clipped. This lesson changes geometry, not SVG structure.
49
+
50
+ Continue with [b08 · Freeze](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/binding/08_freeze.md). The [authoring reference](https://github.com/gramlot-org/gramlot/blob/main/docs/public/095-writing-pages.md) explains the full contract.
51
+
52
+ </details>
@@ -8,7 +8,7 @@ export class Page extends BasePage {
8
8
  const pane = root.div({class: 'example-page stack', datapath: 'board'});
9
9
  pane.h1('Freeze');
10
10
  const actions = pane.div({class: 'actions'});
11
- for (const [label, method] of [['Freeze', 'freeze'], ['Add one', 'add'],
11
+ for (const [label, method] of [['Freeze', 'freeze'], ['Increment Data count', 'add'],
12
12
  ['Remove last item', 'removeLast'], ['Thaw', 'thaw']]) {
13
13
  actions.button(label, {id: method}).dataController({func: method});
14
14
  }
@@ -1,11 +1,49 @@
1
1
  # 08 · Freeze
2
2
 
3
- `renderer.freeze(node)` suspends the DOM work of a branch; `renderer.unfreeze(node)` resumes it and builds the current Source of the branch once.
3
+ Freeze a list, add or remove items, then thaw it. You learn that structural changes can wait while existing Data bindings keep working. Return here after **Named logic** and **Button controller**.
4
4
 
5
- The four buttons call methods of the `Logic` class in `08_freeze.js`, through a `dataController` nested in each button.
5
+ **Uses:** Source freeze/thaw, Data binding and local controllers (0.2.0); shared Page/Logic module layout from 0.2.5.
6
6
 
7
- - While the board is frozen, **Add one** still changes the counter: the elements already built keep following the Data.
8
- - **Remove last item** removes a Source node at once, but its `<li>` stays on screen until the thaw.
9
- - **Thaw** builds the branch once, with every removal applied.
7
+ <details>
8
+ <summary>More detail</summary>
10
9
 
11
- **Try it:** Freeze, remove two items, add one, then thaw.
10
+
11
+ **Release stage:** Data binding and browser behavior (core 0.2.0). The maintained Page/Logic module arrangement is from 0.2.5; it does not move the original behavior into a later release.
12
+
13
+ **Goal:** Observe immediate Data updates and deferred structural rendering separately.
14
+
15
+ **Before this lesson:** [e10 · Cards with icons](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/html_svg/10_cards_with_icons.md), [b01 · Pointers](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/binding/01_pointers.md), [c03 · Named logic](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/controllers/03_named_logic.md), [c06 · Button controller](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/controllers/06_button_controller.md). These prerequisites support the release sequence; later comparisons are explicitly labelled below.
16
+
17
+ **Open and edit:** select **b08 · Freeze** in your environment’s gallery. For source changes, copy/edit the matching page in your own project using the [setup guide](https://github.com/gramlot-org/gramlot/blob/main/docs/public/025-try.md); the gallery is a source viewer. Save, then reload/reopen as that guide explains. The snippets below are excerpts/edits within that Page, not standalone launch commands.
18
+
19
+ ## Read the page
20
+
21
+ The buttons call named `Logic` methods through nested `dataController` declarations. Freeze targets the `board` Source node. `add` increments `.count`; despite the method’s short name, it does **not** create a list item. `removeLast` deletes a Source child.
22
+
23
+ ```python
24
+ actions.button(label, id=method).dataController(func=method)
25
+ ```
26
+
27
+ Existing Data bindings continue while the branch is frozen. Source removal takes effect semantically at once; DOM rebuilding waits until unfreeze.
28
+
29
+ Python builds Source on the server; its JavaScript counterpart builds it on Node/Bun or in a standalone Worker. Bindings, actions and any Logic execute in the browser; Python strings containing JavaScript are not executed by Python. Read the gallery’s **logic.js** tab (the Logic class from `08_freeze.js`) beside the Python source.
30
+
31
+ ## Try it and predict the result
32
+
33
+ 1. Start with count 0, frozen false and three items. Click Freeze.
34
+ 2. Click Increment Data count: count becomes 1 immediately. Remove last item twice: three items are still visible.
35
+ 3. Click Thaw: only Item 1 remains; count is still 1 and frozen is false.
36
+
37
+ ## Change it and check yourself
38
+
39
+ Reload, freeze, increment twice, remove once, then thaw.
40
+
41
+ **Answer:** count 2 is visible before thaw; two list items are visible after thaw. The Data counter is deliberately independent of list length, so it must not be used as an item count.
42
+
43
+ ## Limits and next step
44
+
45
+ Freeze is not a transaction, a Data pause or a persistence mechanism. Removed nodes stop participating even while old DOM waits for thaw. Avoid adding arbitrary DOM operations to “force” the visual change.
46
+
47
+ Continue with [b09 · Forms with binding](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/binding/09_forms.md). The [authoring reference](https://github.com/gramlot-org/gramlot/blob/main/docs/public/095-writing-pages.md) explains the full contract.
48
+
49
+ </details>
@@ -1,7 +1,7 @@
1
1
  import {Page as BasePage} from '@gramlot/gramlot/page';
2
2
 
3
3
  const RECORD = {name: 'Ada Lovelace', quantity: 3, accent: '#456bc4', intensity: 60,
4
- region: 'south', topics: ['html'], note: 'A native multiline field.', updates: true};
4
+ region: 'south', note: 'A native multiline field.', updates: true};
5
5
  const SHOWN = ['name', 'email', 'search', 'website', 'telephone', 'quantity', 'date', 'time', 'month',
6
6
  'week', 'accent', 'intensity', 'region', 'topics', 'note', 'updates'];
7
7
 
@@ -69,6 +69,7 @@ export class Page extends BasePage {
69
69
  facts.dd('^.frequency.weekly', {id: 'out-weekly'});
70
70
 
71
71
  pane.dataSetter({destination_path: 'form', value: RECORD});
72
+ pane.dataSetter({destination_path: '.topics', value: ['html']});
72
73
  pane.dataSetter({destination_path: '.frequency.weekly', value: true});
73
74
  }
74
75
 
@@ -1,12 +1,58 @@
1
1
  # 09 · Forms with binding
2
2
 
3
- The fieldsets of HTML / SVG example 06, with each `value` bound to a field of the Data record `form`.
3
+ The form from **Native forms** returns with its fields connected to one Data record. Edit a value and watch other views follow. You learn how binding replaces code that would otherwise copy values between controls and displays.
4
4
 
5
- - One `dataSetter` writes the whole record from a dictionary (Python) or a plain object (JavaScript); a second setter adds `frequency.weekly`.
6
- - Each field writes its own type: numbers for quantity and intensity, a list for the topics, booleans for the checkbox and the two radio buttons.
7
- - The record on the right reads the same paths, so it changes while you edit. Intensity has `live` true and follows the slider.
8
- - The password is bound but not shown, and the file field stays native: a file has no Data value.
5
+ **Uses:** Source, Data, pointers and native binding — core 0.2.0. No endpoint calls.
9
6
 
10
- **Limit:** Reset is not offered: a native reset changes the fields without an input event, so the Data would not follow.
7
+ <details>
8
+ <summary>More detail</summary>
11
9
 
12
- **Try it:** Compare this page with example 06: the fieldsets are the same, and the initial values moved from the attributes to the Data record.
10
+
11
+ **Release stage:** Data binding and browser behavior (core 0.2.0).
12
+
13
+ **Goal:** Revisit the native form with one authoritative Data record.
14
+
15
+ **Before this lesson:** [e06 · Forms](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/html_svg/06_forms.md), [b03 · Setters and defaults](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/binding/03_setters_and_defaults.md), [b04 · Native editing](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/binding/04_native_editing.md), [b05 · Checkbox and radio](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/binding/05_checkbox_radio.md). These prerequisites support the release sequence; later comparisons are explicitly labelled below.
16
+
17
+ **Open and edit:** select **b09 · Forms with binding** in your environment’s gallery. For source changes, copy/edit the matching page in your own project using the [setup guide](https://github.com/gramlot-org/gramlot/blob/main/docs/public/025-try.md); the gallery is a source viewer. Save, then reload/reopen as that guide explains. The snippets below are excerpts/edits within that Page, not standalone launch commands.
18
+
19
+ ## Read the page
20
+
21
+ The fieldsets are inherited conceptually from e06. Most initial values now live in `RECORD`; `dataSetter("form", RECORD)` populates it. The multiple select needs a list: `dataSetter(".topics", ["html"])` sets it directly. Putting that list inside `RECORD` would convert it recursively into a Bag. Each control and each summary entry bind to the same relative path. Intensity is live; ordinary text fields update on change.
22
+
23
+ ```python
24
+ parent.input(type=kind, id=key, name=key, value=f"^.{key}", **attrs)
25
+ record.h2("The Data record")
26
+ ```
27
+
28
+ Password is bound but deliberately absent from the summary; the file input stays native. Boolean radio paths use the `frequency` group.
29
+
30
+ Python builds Source on the server; its JavaScript counterpart builds it on Node/Bun or in a standalone Worker. Bindings, actions and any Logic execute in the browser; Python strings containing JavaScript are not executed by Python.
31
+
32
+ ## Try it and predict the result
33
+
34
+ 1. Compare initial Name, Quantity, Region and Topics with e06. They still describe Ada, 3, South and HTML.
35
+ 2. Change Name and leave the field: the Data record shows the edit. Drag Intensity: its summary follows live.
36
+ 3. Choose daily: weekly becomes false in Data.
37
+
38
+ ## Change it and check yourself
39
+
40
+ Change `RECORD["quantity"]` from 3 to 5 and reload.
41
+
42
+ **Answer:** both Quantity and its summary begin at 5. You edit the initial record once; neither the input declaration nor its summary contains another copied numeric default.
43
+
44
+ ## The same example across stages
45
+
46
+ **Earlier, e06:** the field call supplies `value="Ada Lovelace"`; checkbox/option declarations carry `checked`/`selected` flags.
47
+
48
+ **Later, 0.2.0:** the shared field helper supplies `value=f"^.{key}"`; defaults move to `RECORD`. A select supplies `value="^.region"` and its options no longer choose the selection individually.
49
+
50
+ The same initial information is declared centrally and reflected in multiple places. This removes duplicated state/update responsibility; it does not claim fewer total lines after adding the summary and interaction.
51
+
52
+ ## Limits and next step
53
+
54
+ No native Reset is offered: native reset does not emit the input/change events needed to synchronize Data. Submit remains disabled. File selection has no bound Data value or upload; this is not a record/form persistence framework.
55
+
56
+ Continue with [b10 · SVG shapes with binding](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/binding/10_svg_shapes.md). The [authoring reference](https://github.com/gramlot-org/gramlot/blob/main/docs/public/095-writing-pages.md) explains the full contract.
57
+
58
+ </details>
@@ -1,9 +1,54 @@
1
1
  # 10 · SVG shapes with binding
2
2
 
3
- The SVG of HTML / SVG example 08, with six attributes turned into `^` pointers.
3
+ The six shapes from **SVG shapes** now have editable dimensions and stroke settings. You learn how pointers connect controls to the drawing without a separate update routine for each shape.
4
4
 
5
- - The ranges change the rectangle width, the circle radius, the ellipse height and one `stroke_width` shared by the line and the polyline.
6
- - The color field paints the triangle; the text field rewrites the label.
7
- - Every control has `live` true, so the drawing follows while you drag or type.
5
+ **Uses:** Source, Data, pointers and native binding — core 0.2.0. No endpoint calls.
8
6
 
9
- **Try it:** Bind the circle's `cx` to a new range and move the circle along the row.
7
+ <details>
8
+ <summary>More detail</summary>
9
+
10
+
11
+ **Release stage:** Data binding and browser behavior (core 0.2.0).
12
+
13
+ **Goal:** Revisit the six-shape SVG with bound dimensions and shared stroke state.
14
+
15
+ **Before this lesson:** [e08 · SVG shapes](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/html_svg/08_svg_shapes.md), [b04 · Native editing](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/binding/04_native_editing.md), [b07 · Bound SVG](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/binding/07_bound_svg.md). These prerequisites support the release sequence; later comparisons are explicitly labelled below.
16
+
17
+ **Open and edit:** select **b10 · SVG shapes with binding** in your environment’s gallery. For source changes, copy/edit the matching page in your own project using the [setup guide](https://github.com/gramlot-org/gramlot/blob/main/docs/public/025-try.md); the gallery is a source viewer. Save, then reload/reopen as that guide explains. The snippets below are excerpts/edits within that Page, not standalone launch commands.
18
+
19
+ ## Read the page
20
+
21
+ The six shapes retain their positions. Width, radius, ellipse height, shared stroke, triangle color and caption now read Data. The ranges and caption text use `live=True`; the color field uses its native change behavior.
22
+
23
+ ```python
24
+ art.circle(cx=172, cy=58, r="^.radius", fill="var(--gramlot-warning)", id="circle")
25
+ pane.dataSetter(".radius", 38)
26
+ ```
27
+
28
+ Line and polyline both read `.stroke`. Their initial shared width is 7; e08 originally used 8 for the line and 7 for the polyline, so this is an explicit small visual change, not exact pixel identity.
29
+
30
+ Python builds Source on the server; its JavaScript counterpart builds it on Node/Bun or in a standalone Worker. Bindings, actions and any Logic execute in the browser; Python strings containing JavaScript are not executed by Python.
31
+
32
+ ## Try it and predict the result
33
+
34
+ 1. Begin with rectangle width 95, radius 38 and ellipse height 38.
35
+ 2. Move Stroke width to 10: both line and polyline change together.
36
+ 3. Type a new Label: the SVG caption follows each keystroke; changing Triangle color changes only the polygon.
37
+
38
+ ## Change it and check yourself
39
+
40
+ Add a range for `.cx`, initialize it to 172, and replace only the circle’s `cx=172` with `cx="^.cx"`.
41
+
42
+ **Answer:** the control moves that circle without moving the ellipse or rectangle. No code for finding/updating the circle in DOM is needed.
43
+
44
+ ## The same example across stages
45
+
46
+ Compare e08’s `r=38` with this page’s `r="^.radius"` and one setter. Static structure stays; the range and circle now share Data. For stroke, one `.stroke` replaces two independently authored widths. The additional controls add code, while the binding removes the need to author separate synchronization behavior.
47
+
48
+ ## Limits and next step
49
+
50
+ Not every control is live: the color field uses change. SVG presentation values retain their own types/units. This is a maintained current-API comparison, not an old-package installation exercise.
51
+
52
+ Continue with [b11 · Complete page with binding](https://github.com/gramlot-org/gramlot-examples/blob/main/src/gramlot_examples/pages/binding/11_complete_page.md). The [authoring reference](https://github.com/gramlot-org/gramlot/blob/main/docs/public/095-writing-pages.md) explains the full contract.
53
+
54
+ </details>