lightview 2.2.2 → 2.3.4

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.
package/docs/cdom.html CHANGED
@@ -53,7 +53,7 @@
53
53
 
54
54
  <script>
55
55
  globalThis.switchCDOMTab = (tabId) => {
56
- const tabs = ['jprx', 'jprxc', 'operators'];
56
+ const tabs = ['jprx', 'jprxc', 'operators', 'embedded'];
57
57
  tabs.forEach(t => {
58
58
  const btn = document.getElementById(`tab-btn-${t}`);
59
59
  const pane = document.getElementById(`pane-${t}`);
@@ -77,6 +77,22 @@
77
77
  A declarative, expression-based way to build reactive UIs.
78
78
  </p>
79
79
 
80
+ <div class="experimental-notice"
81
+ style="margin: 1.5rem 0; padding: 1.25rem; border-radius: var(--site-radius); background: var(--site-accent-light); border: 1px solid var(--site-warning); color: var(--site-text);">
82
+ <div style="display: flex; gap: 0.75rem; align-items: flex-start;">
83
+ <span style="font-size: 1.5rem;">⚠️</span>
84
+ <div>
85
+ <strong style="display: block; margin-bottom: 0.25rem; font-size: 1.1rem;">Experimental
86
+ Feature</strong>
87
+ <p style="margin: 0; font-size: 0.95rem; opacity: 0.9;">
88
+ cDOM and JPRX are currently in an <strong>experimental</strong> phase. The expression syntax,
89
+ helper functions, and integration patterns are subject to change as we continue to evolve the
90
+ library. Use with caution in production environments.
91
+ </p>
92
+ </div>
93
+ </div>
94
+ </div>
95
+
80
96
  <!-- ===== OVERVIEW ===== -->
81
97
  <h2 id="overview">Overview</h2>
82
98
  <p>
@@ -92,44 +108,57 @@
92
108
  <p>
93
109
  cDOM uses <strong>JPRX (JSON Pointer Reactive eXpressions)</strong> as its expression language. JPRX
94
110
  extends <a href="https://www.rfc-editor.org/rfc/rfc6901" target="_blank">JSON Pointer (RFC 6901)</a>
95
- with reactivity, relative paths, and helper functions.
96
- </p>
97
- <p>
98
- If you wish to explore the implementation further, you can visit the <a
99
- href="https://github.com/anywhichway/lightview" target="_blank">GitHub repository</a>, browse the <a
100
- href="https://github.com/anywhichway/lightview/tree/main/cdom" target="_blank">cdom directory</a>, and
101
- check out the <a href="https://github.com/anywhichway/lightview/tree/main/tests/cdom" target="_blank">cdom
102
- unit tests</a>.
111
+ with reactivity, relative paths, and helper functions. It also provides deep integration with
112
+ <a href="https://json-schema.org/" target="_blank">JSON Schema</a> (Standard Draft 7+) for
113
+ industrial-strength data validation and automatic type coercion.
103
114
  </p>
104
115
 
105
116
  <!-- ===== SIMPLE EXAMPLE ===== -->
106
117
  <h2 id="simple-example">Simple Example</h2>
107
118
  <p>Here's a simple counter in cDOM. Notice how the UI is purely declarative — no JavaScript logic:</p>
108
- <div class="code-block">
109
- <pre><code>{
119
+ <div id="simple-counter-demo">
120
+ <pre><script>
121
+ examplify(document.currentScript.nextElementSibling, {
122
+ at: document.currentScript.parentElement,
123
+ scripts: ['/lightview.js', '/lightview-x.js', '/lightview-cdom.js'],
124
+ type: 'module',
125
+ height: '200px',
126
+ autoRun: true,
127
+ controls: false
128
+ });
129
+ </script><code>await import('/lightview-cdom.js');
130
+ const { parseJPRX, hydrate } = globalThis.LightviewCDOM;
131
+ const { $ } = Lightview;
132
+
133
+ const cdom = `{
110
134
  div: {
111
- "cdom-state": { count: 0 },
135
+ onmount: =state({ count: 0 }, { name: 'local', schema: 'auto', scope: $this }),
112
136
  children: [
113
137
  { h2: "Counter" },
114
- { p: ["Count: ", $/count] },
115
- { button: { onclick: $increment(/count), children: ["+"] } },
116
- { button: { onclick: $decrement(/count), children: ["-"] } }
138
+ { p: ["Count: ", =/local/count] },
139
+ { button: { onclick: =++/local/count, children: ["+"] } },
140
+ { button: { onclick: =--/local/count, children: ["-"] } }
117
141
  ]
118
142
  }
119
- }</code></pre>
143
+ }`;
144
+
145
+ $('#example').content(hydrate(parseJPRX(cdom)));</code></pre>
120
146
  </div>
121
147
  <p>
122
148
  This defines a counter with:
123
149
  </p>
124
150
  <ul>
125
- <li><code>cdom-state</code> — Local reactive state with <code>count: 0</code></li>
126
- <li><code>$/count</code> — A path that reactively displays the count value</li>
127
- <li><code>$increment(/count)</code> — Increment the count when clicked</li>
128
- <li><code>$decrement(/count)</code> — Decrement the count when clicked</li>
151
+ <li><code>onmount</code> — A Lightview lifecycle hook where we initialize state.</li>
152
+ <li><code>=state(...)</code> — An initializer that creates local reactive state.</li>
153
+ <li><code>scope: $this</code> — Explicitly attaches the state to the current element.</li>
154
+ <li><code>=/local/count</code> — A path that reactively displays the count value.</li>
129
155
  </ul>
130
156
  <p>
131
157
  The UI automatically updates whenever <code>count</code> changes — no manual DOM manipulation required.
132
158
  </p>
159
+ <p>
160
+ The UI automatically updates whenever <code>count</code> changes — no manual DOM manipulation required.
161
+ </p>
133
162
 
134
163
  <!-- ===== ADVANTAGES ===== -->
135
164
  <h2 id="advantages">Advantages</h2>
@@ -162,7 +191,7 @@
162
191
 
163
192
  <h3 id="JPRX-delimiters">Delimiters</h3>
164
193
  <p>
165
- JPRX expressions begin with a <code>$</code> delimiter:
194
+ JPRX expressions begin with a <code>=</code> delimiter:
166
195
  </p>
167
196
  <table class="api-table">
168
197
  <thead>
@@ -174,20 +203,40 @@
174
203
  </thead>
175
204
  <tbody>
176
205
  <tr>
177
- <td><code>$/</code></td>
206
+ <td><code>=/</code></td>
178
207
  <td>Access a path in the global registry</td>
179
- <td><code>$/users/0/name</code></td>
208
+ <td><code>=/users/0/name</code></td>
180
209
  </tr>
181
210
  <tr>
182
- <td><code>$function(</code></td>
211
+ <td><code>=function(</code></td>
183
212
  <td>Call a helper function</td>
184
- <td><code>$sum(/items...price)</code></td>
213
+ <td><code>=sum(/items...price)</code></td>
185
214
  </tr>
186
215
  </tbody>
187
216
  </table>
188
217
  <p>
189
- Once inside a JPRX expression, paths follow <strong>JSON Pointer</strong> syntax. The <code>$</code>
190
- is only needed at the start of the expression.
218
+ Once inside a JPRX expression, paths follow <strong>JSON Pointer</strong> syntax. The <code>=</code>
219
+ is only needed at the start of the expression for paths or function names.
220
+ </p>
221
+
222
+ <h3 id="JPRX-escaping">Escaping the <code>=</code> Delimiter</h3>
223
+ <p>
224
+ When using <strong>oDOM</strong> or <strong>vDOM</strong> (Object DOM), any string value starting with
225
+ <code>=</code> is interpreted as a JPRX expression. If you need a literal string that begins with
226
+ an equals sign (e.g., a mathematical equation or status message), you can escape it by prefixing
227
+ with a single quote:
228
+ </p>
229
+ <div class="code-block" style="margin-bottom: 1rem;">
230
+ <pre><code>// Interpreted as JPRX expression:
231
+ { "p": "=/user/name" } // Resolves the path /user/name
232
+
233
+ // Escaped to produce a literal string:
234
+ { "p": "'=E=mc²" } // Renders as "=E=mc²"
235
+ { "p": "'=42" } // Renders as "=42"</code></pre>
236
+ </div>
237
+ <p>
238
+ The single-quote escape (<code>'=</code>) at the start of a string tells the parser to treat
239
+ the rest of the string (including the <code>=</code>) as literal content.
191
240
  </p>
192
241
 
193
242
  <h3 id="JPRX-anatomy">Anatomy of a Path</h3>
@@ -236,7 +285,7 @@
236
285
  Paths can contain function calls to transform data:
237
286
  </p>
238
287
  <div class="code-block">
239
- <pre><code>$currency(sum(map(filter(/orders, eq(_/status, 'paid')), _/total)...))</code></pre>
288
+ <pre><code>=currency(sum(map(filter(/orders, eq(_/status, 'paid')), _/total)...))</code></pre>
240
289
  </div>
241
290
  <ul>
242
291
  <li><strong>/orders</strong>: Access the <code>orders</code> collection (JSON Pointer)</li>
@@ -244,7 +293,7 @@
244
293
  <li><strong>map(..., _/total)</strong>: Extract the <code>total</code> from each order</li>
245
294
  <li><strong>...</strong>: Explode the array into individual arguments</li>
246
295
  <li><strong>sum(...)</strong>: Add up all the totals</li>
247
- <li><strong>$currency(...)</strong>: Format as currency string</li>
296
+ <li><strong>=currency(...)</strong>: Format as currency string</li>
248
297
  </ul>
249
298
 
250
299
  <h3 id="JPRX-placeholders">Placeholders</h3>
@@ -260,12 +309,12 @@
260
309
  <tr>
261
310
  <td><code>_</code></td>
262
311
  <td>Current item during iteration</td>
263
- <td><code>$map(/items, _/name)</code></td>
312
+ <td><code>=map(/items, _/name)</code></td>
264
313
  </tr>
265
314
  <tr>
266
315
  <td><code>$event</code></td>
267
316
  <td>Event object in handlers</td>
268
- <td><code>$set($/selected, $event/target/value)</code></td>
317
+ <td><code>=set(=/selected, $event/target/value)</code></td>
269
318
  </tr>
270
319
  </tbody>
271
320
  </table>
@@ -298,12 +347,12 @@
298
347
  <tr>
299
348
  <td><strong>Formulas</strong></td>
300
349
  <td><code>=SUM(A1:A10)</code></td>
301
- <td><code>$sum(/items...price)</code></td>
350
+ <td><code>=sum(/items...price)</code></td>
302
351
  </tr>
303
352
  <tr>
304
353
  <td><strong>Path Resolution</strong></td>
305
354
  <td>Cell References (A1, $B$2)</td>
306
- <td>JSON Pointer paths (./name, $/global)</td>
355
+ <td>JSON Pointer paths (./name, =/global)</td>
307
356
  </tr>
308
357
  <tr>
309
358
  <td><strong>Recalculation</strong></td>
@@ -317,79 +366,204 @@
317
366
  <h2 id="integration">Lightview Integration</h2>
318
367
  <p>
319
368
  cDOM integrates seamlessly with Lightview's existing DOM formats. You can use JPRX expressions
320
- in any of Lightview's hypermedia formats:
369
+ in any of Lightview's hypermedia formats just by loading <code>lightview-cdom.js</code>:
321
370
  </p>
322
-
323
371
  <h3>vDOM (Virtual DOM)</h3>
324
- <p>JPRX expressions work directly in vDOM arrays:</p>
372
+ <p>JPRX expressions work directly in vDOM arrays or objects:</p>
325
373
  <div class="code-block">
326
- <pre><code>["div", { class: "counter" },
327
- ["p", "Count: ", "$/count"],
328
- ["button", { onclick: "$increment(/count)" }, "+"]
329
- ]</code></pre>
374
+ <pre><code>// vDOM formal object
375
+ {
376
+ tag: "div",
377
+ attributes: { class: "counter" },
378
+ children: [
379
+ { tag: "p", children: ["Count: ", "=/local/count"] },
380
+ { tag: "button", attributes: { onclick: "=++/local/count }, children: ["+"] }
381
+ ]
382
+ }</code></pre>
330
383
  </div>
331
384
 
332
385
  <h3>oDOM (Object DOM)</h3>
333
386
  <p>JPRX expressions work in oDOM objects:</p>
334
387
  <div class="code-block">
335
388
  <pre><code>{
336
- tag: "div",
337
- attributes: { class: "counter" },
338
- children: [
339
- { tag: "p", children: ["Count: ", "$/count"] },
340
- { tag: "button", attributes: { onclick: "$increment(/count)" }, children: ["+"] }
341
- ]
389
+ div: {
390
+ class: "counter",
391
+ children: [
392
+ { p: { children: ["Count: ", "=/local/count] } },
393
+ { button: { onclick: =++/local/count, children: ["+"] } }
394
+ ]
395
+ }
342
396
  }</code></pre>
343
397
  </div>
344
398
 
345
- <h3>cDOM Shorthand</h3>
346
- <p>cDOM's concise shorthand syntax:</p>
399
+ <h3>cDOM Shorthand with JPRX</h3>
400
+ <p>cDOM's concise shorthand syntax. Note that cDOM does not require quoting attribute names or JPRX
401
+ expressions like vDOM and oDOM do and you can include comments:</p>
347
402
  <div class="code-block">
348
403
  <pre><code>{
349
404
  div: {
350
405
  class: "counter",
406
+ // This is a JPRX comment
351
407
  children: [
352
- { p: ["Count: ", $/count] },
353
- { button: { onclick: "$increment(/count)", children: ["+"] } }
408
+ { p: ["Count: ", =/local/count] },
409
+ { button: { onclick: =++/local/count, children: ["+"] } }
354
410
  ]
355
411
  }
356
412
  }</code></pre>
357
413
  </div>
358
414
 
359
- <!-- ===== DIRECTIVES ===== -->
360
- <h2 id="directives">Directives</h2>
415
+ <!-- ===== DOM PATCHES ===== -->
416
+ <h2 id="dom-patches">DOM Patches & Decentralized Layouts</h2>
361
417
  <p>
362
- cDOM provides custom attributes (directives) to bridge static HTML and reactive state.
418
+ cDOM supports <strong>Decentralized Layouts</strong>, allowing components to "move themselves" to their
419
+ rightful
420
+ home in the DOM upon being created. This is especially powerful for LLM-driven streaming UIs.
363
421
  </p>
364
422
 
365
- <h3 id="cdom-state">cdom-state</h3>
423
+ <h3 id="helpers-move">=move(target, location?)</h3>
366
424
  <p>
367
- Defines local reactive state on an element. Children can access this state using relative paths.
425
+ The <code>=move</code> helper (typically used in <code>onmount</code>) teleports the host element to a
426
+ different part of the document.
368
427
  </p>
369
428
  <div class="code-block">
429
+ <pre><code>{
430
+ div: {
431
+ id: "weather-widget",
432
+ onmount: =move('#sidebar', 'afterbegin'),
433
+ content: "Sunny, 75°F"
434
+ }
435
+ }</code></pre>
436
+ </div>
437
+
438
+ <h4>Identity & Patching</h4>
439
+ <p>
440
+ If the moving element has a unique <code>id</code> and an element with that same ID already exists at the
441
+ destination,
442
+ the existing element is <strong>replaced</strong>. This turns <code>=move</code> into an idempotent
443
+ <strong>patch</strong>
444
+ command — simply stream the new version of the component with the same ID, and it will update the UI
445
+ automatically.
446
+ </p>
447
+
448
+ <h4>Placement Locations</h4>
449
+ <table class="api-table">
450
+ <thead>
451
+ <tr>
452
+ <th>Location</th>
453
+ <th>Result</th>
454
+ </tr>
455
+ </thead>
456
+ <tbody>
457
+ <tr>
458
+ <td><code>inner</code> / <code>shadow</code></td>
459
+ <td>Replaces all children of the target.</td>
460
+ </tr>
461
+ <tr>
462
+ <td><code>afterbegin</code> / <code>prepend</code></td>
463
+ <td>Inserts at the start of the target.</td>
464
+ </tr>
465
+ <tr>
466
+ <td><code>beforeend</code> / <code>append</code></td>
467
+ <td>Inserts at the end of the target (default).</td>
468
+ </tr>
469
+ <tr>
470
+ <td><code>outer</code> / <code>replace</code></td>
471
+ <td>Replaces the target node itself.</td>
472
+ </tr>
473
+ <tr>
474
+ <td><code>beforebegin</code> / <code>afterend</code></td>
475
+ <td>Inserts before or after the target node.</td>
476
+ </tr>
477
+ </tbody>
478
+ </table>
479
+
480
+
481
+ <h2 id="state-binding">State & Binding</h2>
482
+ <p>
483
+ cDOM does not use attribute directives for state it uses lifecycle events and helpers instead.
484
+ </p>
485
+
486
+ <h3 id="lifecycle-state">Lifecycle State</h3>
487
+ <p>
488
+ In Lightview, you initialize state within the <code>onmount</code> hook. The <code>=state</code> and
489
+ <code>=signal</code> helpers accept an options object where you can specify a <code>scope</code>,
490
+ as well as <code>schema</code> requirements.
491
+ </p>
492
+
493
+ <h4>Using a Registered Schema</h4>
494
+ <div class="code-block" style="margin-bottom: 1rem;">
495
+ <pre><code>// 1. Register centrally in JS
496
+ Lightview.registerSchema('User', { name: 'string', age: 'number' });
497
+
498
+ // 2. Use in cDOM
499
+ { "onmount": "=state({}, { name: 'profile', schema: 'User', scope: $this })" }</code></pre>
500
+ </div>
501
+
502
+ <h4>Standard Schema Behaviors</h4>
503
+ <p>
504
+ When using the <code>schema</code> option, you can use these standard behaviors:
505
+ </p>
506
+ <ul style="margin-bottom: 2rem;">
507
+ <li><strong>"auto"</strong>: Infers a fixed schema from the initial value. Strict type checking (throws on
508
+ mismatch).</li>
509
+ <li><strong>"dynamic"</strong>: Like auto, but allows adding new properties to the state object.</li>
510
+ <li><strong>"polymorphic"</strong>: The most powerful setting. It includes <strong>"dynamic"</strong>
511
+ behavior and automatically <strong>coerces</strong> values to match the inferred type (e.g., "50" ->
512
+ 50).
513
+ </li>
514
+ </ul>
515
+
516
+ <h4>Example: Polymorphic Coercion</h4>
517
+ <div class="code-block" style="margin-bottom: 2rem;">
370
518
  <pre><code>{
371
519
  div: {
372
- "cdom-state": { count: 0, name: "Guest" },
520
+ "onmount": "=state({ count: 0 }, {
521
+ name: 'local',
522
+ schema: 'polymorphic',
523
+ scope: $this
524
+ })",
373
525
  children: [
374
- { p: ["Hello, ", $/name] },
375
- { p: ["Count: ", $/count] }
526
+ { p: ["Typing '10' into a bind will save it as the number 10."] }
376
527
  ]
377
528
  }
378
529
  }</code></pre>
379
530
  </div>
531
+ <p>
532
+ By scoping the state to the element, you can create multiple independent instances of components.
533
+ Lightview uses a high-performance <strong>up-tree search</strong> to resolve these names.
534
+ </p>
380
535
 
381
- <h3 id="cdom-bind">cdom-bind</h3>
536
+ <h3 id="bind-helper">Two-Way Binding ($bind)</h3>
382
537
  <p>
383
- Creates two-way data binding between an input and a reactive path.
538
+ Two-way data binding is achieved via the <code>=bind(path)</code> helper.
384
539
  </p>
385
540
  <div class="code-block">
386
- <pre><code>{ input: { type: "text", "cdom-bind": "$/user/name", placeholder: "Enter name" } }
387
- { input: { type: "checkbox", "cdom-bind": "$/settings/enabled" } }</code></pre>
541
+ <pre><code>{ input: { type: "text", value: "=bind(/profile/name)", placeholder: "Enter name" } }
542
+ { input: { type: "checkbox", checked: "=bind(/settings/enabled)" } }</code></pre>
388
543
  </div>
544
+
545
+ <h4 id="binding-transformations">Handling Transformations</h4>
389
546
  <p>
390
- <strong>Smart Initialization:</strong> If the state is <code>undefined</code>,
391
- <code>cdom-bind</code> will use the input's initial value to populate the state.
547
+ Because <code>=bind</code> is strict (it only accepts direct paths), you cannot pass a computation
548
+ like <code>=bind(upper(/name))</code>. To transform data during binding, you have two choices:
392
549
  </p>
550
+ <ul>
551
+ <li>
552
+ <strong>Manual Transformation:</strong> Use an <code>oninput</code> handler:
553
+ <code>{ oninput: "=set(/name, upper($event/target/value))" }</code>
554
+ </li>
555
+ <li>
556
+ <strong>Schema Transformation:</strong> Define a <code>transform</code> in your schema:
557
+ <div class="code-block" style="margin-top: 0.5rem;">
558
+ <pre><code>Lightview.registerSchema('Profile', {
559
+ name: { type: 'string', transform: 'upper' }
560
+ });</code></pre>
561
+ </div>
562
+ The state manager will automatically apply the <a
563
+ href="/docs/api/state.html#transformations">transformation</a>
564
+ during the write-back phase of <code>=bind</code>.
565
+ </li>
566
+ </ul>
393
567
 
394
568
  <!-- ===== SHOPPING CART EXAMPLE ===== -->
395
569
  <h2 id="shopping-cart">Shopping Cart Example</h2>
@@ -401,7 +575,7 @@
401
575
  at: document.currentScript.parentElement,
402
576
  scripts: ['/lightview.js', '/lightview-x.js', '/lightview-cdom.js'],
403
577
  type: 'module',
404
- height: '350px',
578
+ height: '250px',
405
579
  autoRun: true
406
580
  });
407
581
  </script><code contenteditable="true">// Shopping Cart: Demonstrating $map and $currency helpers
@@ -411,22 +585,22 @@ const { $ } = Lightview;
411
585
 
412
586
  const cdomString = `{
413
587
  div: {
414
- "cdom-state": {
588
+ "onmount": "=state({
415
589
  cart: {
416
590
  items: [
417
- { name: "Apple", price: 1.00 },
418
- { name: "Orange", price: 2.00 }
591
+ { name: 'Apple', price: 1.00 },
592
+ { name: 'Orange', price: 2.00 }
419
593
  ]
420
594
  }
421
- },
595
+ }, 'store')",
422
596
  children: [
423
597
  { h3: "Shopping Cart" },
424
598
  { ul: {
425
- children: $map(/cart/items, { li: { children: [_/name, " - ", $currency(_/price)] } })
599
+ children: =map(/store/cart/items, { li: { children: [_/name, " - ", =currency(_/price)] } })
426
600
  }},
427
601
  { p: {
428
602
  style: "font-weight: bold; margin-top: 1rem;",
429
- children: ["Total: ", $currency(sum(/cart/items...price))]
603
+ children: ["Total: ", =currency(sum(/store/cart/items...price))]
430
604
  }}
431
605
  ]
432
606
  }
@@ -434,8 +608,7 @@ const cdomString = `{
434
608
 
435
609
  const hydrated = hydrate(parseJPRX(cdomString));
436
610
  $('#example').content(hydrated);
437
- globalThis.LightviewCDOM.activate(hydrated.domEl);
438
- </code></pre>
611
+ </code></pre></code></pre>
439
612
  </div>
440
613
 
441
614
  <!-- ===== INTERACTIVE EXAMPLE ===== -->
@@ -450,6 +623,8 @@ globalThis.LightviewCDOM.activate(hydrated.domEl);
450
623
  (Standard)</button>
451
624
  <button id="tab-btn-jprxc" class="syntax-tab" onclick="switchCDOMTab('jprxc')">JPRXC (Concise)</button>
452
625
  <button id="tab-btn-operators" class="syntax-tab" onclick="switchCDOMTab('operators')">Operators</button>
626
+ <button id="tab-btn-embedded" class="syntax-tab" onclick="switchCDOMTab('embedded')">Embedded
627
+ Signals</button>
453
628
  </div>
454
629
 
455
630
  <!-- JPRX Pane -->
@@ -473,10 +648,10 @@ const cdomString = `{
473
648
  "div": {
474
649
  "children": [
475
650
  { "h3": ["Standard JPRX Counter"] },
476
- { "p": { "children": ["Count: ", "$/count"] }},
651
+ { "p": { "children": ["Count: ", "=/count"] }},
477
652
  { "div": { "children": [
478
- { "button": { "onclick": "$decrement(/count)", "children": ["-"] } },
479
- { "button": { "onclick": "$increment(/count)", "children": ["+"] } }
653
+ { "button": { "onclick": "=decrement(/count)", "children": ["-"] } },
654
+ { "button": { "onclick": "=increment(/count)", "children": ["+"] } }
480
655
  ]}}
481
656
  ]
482
657
  }
@@ -508,10 +683,10 @@ const cdomString = `{
508
683
  div: {
509
684
  children: [
510
685
  { h3: ["Concise Counter"] },
511
- { p: { children: ["Count: ", $/count] }},
686
+ { p: { children: ["Count: ", =/count] }},
512
687
  { div: { children: [
513
- { button: { onclick: $decrement(/count), children: ["-"] } },
514
- { button: { onclick: $increment(/count), children: ["+"] } }
688
+ { button: { onclick: =decrement(/count), children: ["-"] } },
689
+ { button: { onclick: =increment(/count), children: ["+"] } }
515
690
  ]}}
516
691
  ]
517
692
  }
@@ -532,7 +707,7 @@ globalThis.LightviewCDOM.activate(hydrated.domEl);
532
707
  type: 'module',
533
708
  height: '250px'
534
709
  });
535
- </script><code contenteditable="true">// Operators: Using $++/path and $--/path
710
+ </script><code contenteditable="true">// Operators: Using =++/path and =--/path
536
711
  await import('/lightview-cdom.js');
537
712
  const { parseJPRX, hydrate } = globalThis.LightviewCDOM;
538
713
  const { signal, $ } = Lightview;
@@ -543,11 +718,11 @@ const cdomString = `{
543
718
  div: {
544
719
  children: [
545
720
  { h3: ["Operator Counter"] },
546
- { p: { children: ["Count: ", $/count] }},
721
+ { p: { children: ["Count: ", =/count] }},
547
722
  { div: { children: [
548
- // Prefix operators: $-- and $++
549
- { button: { onclick: $--/count, children: ["-"] } },
550
- { button: { onclick: $++/count, children: ["+"] } }
723
+ // Prefix operators: =-- and =++
724
+ { button: { onclick: =--/count, children: ["-"] } },
725
+ { button: { onclick: =++/count, children: ["+"] } }
551
726
  ]}}
552
727
  ]
553
728
  }
@@ -559,10 +734,44 @@ globalThis.LightviewCDOM.activate(hydrated.domEl);
559
734
  </code></pre>
560
735
  </div>
561
736
 
737
+ <!-- Embedded Pane -->
738
+ <div id="pane-embedded" style="display: none;">
739
+ <pre><script>
740
+ examplify(document.currentScript.nextElementSibling, {
741
+ at: document.currentScript.parentElement,
742
+ scripts: ['/lightview.js', '/lightview-x.js', '/lightview-cdom.js'],
743
+ type: 'module',
744
+ height: '250px'
745
+ });
746
+ </script><code contenteditable="true">// Embedded Signals: Using onmount to initialize local state
747
+ await import('/lightview-cdom.js');
748
+ const { parseJPRX, hydrate } = globalThis.LightviewCDOM;
749
+ const { $ } = Lightview;
750
+
751
+ const cdomString = `{
752
+ div: {
753
+ // Initialize a named signal explicitly scoped to '$this' element.
754
+ onmount: =signal(0, { name: 'count', scope: $this }),
755
+ children: [
756
+ { h3: ["Embedded Counter"] },
757
+ { p: ["Count: ", =/count] },
758
+ { div: { children: [
759
+ { button: { onclick: =--/count, children: ["-"] } },
760
+ { button: { onclick: =++/count, children: ["+"] } }
761
+ ]}}
762
+ ]
763
+ }
764
+ }`;
765
+
766
+ const hydrated = hydrate(parseJPRX(cdomString));
767
+ $('#example').content(hydrated);
768
+ </code></pre>
769
+ </div>
770
+
562
771
  <!-- ===== OPERATOR SYNTAX EXAMPLE ===== -->
563
772
  <h2 id="operator-syntax">Operator Syntax</h2>
564
773
  <p>
565
- JPRX now supports <strong>prefix</strong> and <strong>postfix</strong> operator syntax as alternatives to
774
+ JPRX supports <strong>prefix</strong> and <strong>postfix</strong> operator syntax as alternatives to
566
775
  function calls.
567
776
  This makes expressions more concise and familiar to JavaScript developers.
568
777
  </p>
@@ -578,18 +787,18 @@ globalThis.LightviewCDOM.activate(hydrated.domEl);
578
787
  </thead>
579
788
  <tbody>
580
789
  <tr>
581
- <td><code>$increment(/count)</code></td>
582
- <td><code>$++/count</code> or <code>$/count++</code></td>
790
+ <td><code>=increment(/count)</code></td>
791
+ <td><code>=++/count</code> or <code>=/count++</code></td>
583
792
  <td>Increment by 1</td>
584
793
  </tr>
585
794
  <tr>
586
- <td><code>$decrement(/count)</code></td>
587
- <td><code>$--/count</code> or <code>$/count--</code></td>
795
+ <td><code>=decrement(/count)</code></td>
796
+ <td><code>=--/count</code> or <code>=/count--</code></td>
588
797
  <td>Decrement by 1</td>
589
798
  </tr>
590
799
  <tr>
591
- <td><code>$toggle(/enabled)</code></td>
592
- <td><code>$!!/enabled</code></td>
800
+ <td><code>=toggle(/enabled)</code></td>
801
+ <td><code>=!!/enabled</code></td>
593
802
  <td>Toggle boolean (prefix only)</td>
594
803
  </tr>
595
804
  </tbody>
@@ -608,24 +817,31 @@ globalThis.LightviewCDOM.activate(hydrated.domEl);
608
817
  Use standard event attributes with JPRX expressions:
609
818
  </p>
610
819
  <div class="code-block">
611
- <pre><code>{ button: { onclick: $(++, $/count), children: ["Click Me"] } }
612
- { input: { oninput: $set($/name, $event/target/value) } }</code></pre>
820
+ <pre><code>{ button: { onclick: =++/count, children: ["Click Me"] } }
821
+ { input: { oninput: =set(/name, $event/target/value) } }</code></pre>
613
822
  </div>
614
823
 
615
824
  <h3 id="events-llm">LLM-Generated Interaction</h3>
616
825
  <p>
617
- LLMs can generate event handlers to register interest in user actions:
826
+ LLMs can generate event handlers to register interest in user actions or trigger UI updates:
618
827
  </p>
619
828
  <div class="code-block">
620
- <pre><code>{
621
- button: {
622
- onclick: $fetch('/api/notify', { method: 'POST', body: $event }),
623
- children: ["Notify LLM"]
624
- }
625
- }</code></pre>
829
+ <pre><code>// 1. Notify LLM of an event
830
+ { button: { onclick: =fetch('/api/notify', { method: 'POST', body: $event }), children: ["Notify"] } }
831
+
832
+ // 2. Request a UI Patch from LLM
833
+ { button: { onclick: =mount('/api/update', { method: 'POST', body: $event }), children: ["Update UI"] } }</code></pre>
626
834
  </div>
627
835
  <p>
628
- The <code>$event</code> placeholder gives the LLM full context of the interaction.
836
+ The <code>$event</code> placeholder gives the LLM full context of the interaction. When using
837
+ <code>=mount</code>, the server should respond with cDOM, vDOM, or oDOM content (see the
838
+ <a href="#helpers-network">Network section</a> for more details).
839
+ </p>
840
+ <p>
841
+ By default, <code>=mount</code> will <strong>append</strong> the response to the <code>body</code> and
842
+ then let it "rip itself out" and <strong>teleport</strong> to its final destination if the response
843
+ contains a <code>=move</code> helper. This "Safe Landing" strategy ensures decentralized layouts
844
+ work seamlessly without the patcher needing to know the exact destination.
629
845
  </p>
630
846
 
631
847
  <h3 id="events-lifecycle">The Interaction Lifecycle</h3>
@@ -675,7 +891,7 @@ LightviewCDOM.activate(document.getElementById('myApp'));</code></pre>
675
891
  Converts <code>$</code>-prefixed strings into reactive computed signals.
676
892
  </p>
677
893
  <div class="code-block">
678
- <pre><code>const config = { title: "Dashboard", total: "$/cart/items...price" };
894
+ <pre><code>const config = { title: "Dashboard", total: "=/cart/items...price" };
679
895
  const liveConfig = LightviewCDOM.hydrate(config);</code></pre>
680
896
  </div>
681
897
 
@@ -684,7 +900,18 @@ const liveConfig = LightviewCDOM.hydrate(config);</code></pre>
684
900
  Parses cDOM's concise syntax (unquoted keys, JPRX expressions) into a JavaScript object.
685
901
  </p>
686
902
  <div class="code-block">
687
- <pre><code>const obj = LightviewCDOM.parseJPRX(`{ div: { children: [$/name] } }`);</code></pre>
903
+ <pre><code>const obj = LightviewCDOM.parseJPRX(`{ div: { children: [=/name] } }`);</code></pre>
904
+ </div>
905
+
906
+ <h3 id="api-registerSchema">registerSchema(name, definition)</h3>
907
+ <p>
908
+ Registers a reusable schema for state validation and initialization. Supported behaviors include
909
+ <code>"auto"</code> (strict/fixed), <code>"dynamic"</code> (strict/expandable), and
910
+ <code>"polymorphic"</code> (coerce/expandable).
911
+ </p>
912
+ <div class="code-block">
913
+ <pre><code>Lightview.registerSchema('User', { name: 'string', age: 'number' });
914
+ // Usable in JPRX: =state({}, { name: 'profile', schema: 'User' })</code></pre>
688
915
  </div>
689
916
 
690
917
  <h3 id="api-registerHelper">registerHelper(name, fn, options?)</h3>
@@ -693,7 +920,7 @@ const liveConfig = LightviewCDOM.hydrate(config);</code></pre>
693
920
  </p>
694
921
  <div class="code-block">
695
922
  <pre><code>LightviewCDOM.registerHelper('double', (x) => x * 2);
696
- // Now usable: $double($/count)</code></pre>
923
+ // Now usable: =double(/count)</code></pre>
697
924
  </div>
698
925
 
699
926
  <!-- ===== JPRX HELPERS ===== -->
@@ -741,7 +968,7 @@ const liveConfig = LightviewCDOM.hydrate(config);</code></pre>
741
968
  </div>
742
969
 
743
970
  <p class="text-sm italic opacity-80" style="margin-top: 1rem;">
744
- Example: <code>$if(gt($/count, 10), 'Large', 'Small')</code>
971
+ Example: <code>=if(gt(=/count, 10), 'Large', 'Small')</code>
745
972
  </p>
746
973
 
747
974
  <h3 id="helpers-conditional">Conditional Aggregates</h3>
@@ -757,7 +984,7 @@ const liveConfig = LightviewCDOM.hydrate(config);</code></pre>
757
984
  </div>
758
985
  <p class="text-xs italic opacity-70" style="margin-top: 0.5rem;">
759
986
  <strong>Explosion Operator:</strong> In JPRX, <code>...</code> is placed at the end of a path
760
- (e.g., <code>$/items...price</code>) to expand arrays into arguments.
987
+ (e.g., <code>=/items...price</code>) to expand arrays into arguments.
761
988
  </p>
762
989
 
763
990
  <h3 id="helpers-datetime">DateTime</h3>
@@ -772,21 +999,63 @@ const liveConfig = LightviewCDOM.hydrate(config);</code></pre>
772
999
  <pre><code>lookup, vlookup, index, match</code></pre>
773
1000
  </div>
774
1001
 
775
- <h3 id="helpers-mutation">State Mutation</h3>
776
- <p>Modify reactive state from event handlers.</p>
1002
+ <h3 id="helpers-mutation">State & Lifecycle</h3>
1003
+ <p>Initialize and modify reactive state.</p>
777
1004
  <div class="code-block">
778
- <pre><code>set, increment (++), decrement (--), toggle (!!), push, pop, assign, clear</code></pre>
1005
+ <pre><code>state, signal, bind, set, increment (++), decrement (--), toggle (!!), push, pop, assign, clear</code></pre>
779
1006
  </div>
780
1007
 
781
1008
  <h3 id="helpers-network">Network</h3>
782
1009
  <p>HTTP requests.</p>
783
1010
  <div class="code-block">
784
- <pre><code>fetch(url, options?)</code></pre>
1011
+ <pre><code>fetch(url, options?)
1012
+ <span id="helpers-mount"></span>mount(url, options?)</span></code></pre>
785
1013
  </div>
786
- <p>
787
- The <code>fetch</code> helper auto-serializes object bodies to JSON and sets
788
- <code>Content-Type: application/json</code>.
1014
+ <table class="api-table">
1015
+ <thead>
1016
+ <tr>
1017
+ <th>Option</th>
1018
+ <th>Default</th>
1019
+ <th>Description</th>
1020
+ </tr>
1021
+ </thead>
1022
+ <tbody>
1023
+ <tr>
1024
+ <td><code>method</code></td>
1025
+ <td><code>"GET"</code></td>
1026
+ <td>HTTP method (GET, POST, etc.)</td>
1027
+ </tr>
1028
+ <tr>
1029
+ <td><code>body</code></td>
1030
+ <td><code>undefined</code></td>
1031
+ <td>Request body. If an <strong>object</strong>, it is automatically stringified to JSON and the
1032
+ <code>Content-Type</code> is set to <code>application/json</code>.
1033
+ </td>
1034
+ </tr>
1035
+ <tr>
1036
+ <td><code>headers</code></td>
1037
+ <td><code>{}</code></td>
1038
+ <td>Additional HTTP headers.</td>
1039
+ </tr>
1040
+ <tr>
1041
+ <td><code>target</code></td>
1042
+ <td><code>"body"</code></td>
1043
+ <td>(<code>mount</code> only) CSS selector for destination.</td>
1044
+ </tr>
1045
+ <tr>
1046
+ <td><code>location</code></td>
1047
+ <td><code>"beforeend"</code></td>
1048
+ <td>(<code>mount</code> only) Placement relative to target.</td>
1049
+ </tr>
1050
+ </tbody>
1051
+ </table>
1052
+ <p style="margin-top: 1rem;">
1053
+ The <code>mount</code> helper is the primary mechanism for **Hypermedia updates**. It fetches content
1054
+ (cDOM, vDOM, or oDOM) and injects it into the DOM. If the content contains a <code>=move</code>
1055
+ helper, it will automatically relocate itself upon mounting.
789
1056
  </p>
790
1057
 
1058
+
1059
+
791
1060
  </main>
792
- </div>
1061
+ </div>