lightview 2.4.4 → 2.5.0

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', 'embedded'];
56
+ const tabs = ['jprx', 'cdomc', 'operators'];
57
57
  tabs.forEach(t => {
58
58
  const btn = document.getElementById(`tab-btn-${t}`);
59
59
  const pane = document.getElementById(`pane-${t}`);
@@ -89,6 +89,20 @@
89
89
  helper functions, and integration patterns are subject to change as we continue to evolve the
90
90
  library. Use with caution in production environments.
91
91
  </p>
92
+ <p
93
+ style="margin: 0.75rem 0 0 0; padding-top: 0.75rem; border-top: 1px solid var(--site-border); font-size: 0.95rem; opacity: 0.9;">
94
+ <strong>v2.5.0 Syntax Update:</strong> Starting with Lightview v2.5.0 and JPRX v1.4.0,
95
+ expressions use a new wrapper syntax:
96
+ <code
97
+ style="background: var(--site-bg-secondary); padding: 0.125rem 0.375rem; border-radius: 3px;">=(expr)</code>
98
+ for JPRX and
99
+ <code
100
+ style="background: var(--site-bg-secondary); padding: 0.125rem 0.375rem; border-radius: 3px;">#(xpath)</code>
101
+ for XPath.
102
+ The legacy prefix-only syntax (e.g., <code
103
+ style="background: var(--site-bg-secondary); padding: 0.125rem 0.375rem; border-radius: 3px;">=path</code>)
104
+ will be <strong>fully deprecated after March 31, 2026</strong>.
105
+ </p>
92
106
  </div>
93
107
  </div>
94
108
  </div>
@@ -132,13 +146,14 @@ const { parseJPRX, hydrate } = globalThis.LightviewCDOM;
132
146
  const { $ } = Lightview;
133
147
 
134
148
  const cdom = `{
135
- div: {
136
- onmount: =state({ count: 0 }, { name: 'local', schema: 'auto', scope: $this }),
137
- children: [
138
- { h2: "Counter" },
139
- { p: ["Count: ", =/local/count] },
140
- { button: { onclick: =++/local/count, children: ["+"] } },
141
- { button: { onclick: =--/local/count, children: ["-"] } }
149
+ "div": {
150
+ "id": "Counter",
151
+ "onmount": "=state({ count: 0 }, { name: 'local', schema: 'auto', scope: $this })",
152
+ "children": [
153
+ { "h2": "#(../../@id)" }, // XPath: text node -> h2 -> div
154
+ { "p": ["Count: ", "=(/local/count)"] },
155
+ { "button": { "onclick": "=(++/local/count)", "children": ["+"] } },
156
+ { "button": { "onclick": "=(--/local/count)", "children": ["-"] } }
142
157
  ]
143
158
  }
144
159
  }`;
@@ -150,14 +165,45 @@ $('#example').content(hydrate(parseJPRX(cdom)));</code></pre>
150
165
  </p>
151
166
  <ul>
152
167
  <li><code>onmount</code> — A Lightview lifecycle hook where we initialize state.</li>
168
+ <li><code>#../../@id</code> — An XPath expression that extracts the <code>id</code> from the grandparent
169
+ element.</li>
153
170
  <li><code>=state(...)</code> — An initializer that creates local reactive state.</li>
154
171
  <li><code>scope: $this</code> — Explicitly attaches the state to the current element.</li>
155
172
  <li><code>=/local/count</code> — A path that reactively displays the count value.</li>
156
173
  </ul>
174
+ <p style="font-size: 0.95rem; font-style: italic; color: var(--site-text-secondary);">
175
+ <strong>Note:</strong> In cDOM, expressions use a wrapper syntax for clarity: <code>#(xpath)</code> for
176
+ <strong>XPath</strong> expressions that
177
+ navigate and extract data from the DOM, and <code>=(expr)</code> for <strong>JPRX</strong>
178
+ expressions that navigate or apply functions to reactive state. Legacy syntax without parentheses is still
179
+ supported.
180
+ </p>
157
181
  <p>
158
- The UI automatically updates whenever <code>count</code> changes — no manual DOM manipulation required.
182
+ The UI automatically updates whenever <code>count</code> changes — no manual DOM manipulation required. It
183
+ also uses XPath to navigate the DOM structure during construction.
159
184
  </p>
160
185
 
186
+ <!-- ===== ADVANTAGES ===== -->
187
+ <h2 id="advantages">Advantages</h2>
188
+ <div class="feature-grid" style="margin: 2rem 0;">
189
+ <div class="feature-card">
190
+ <h3 class="feature-title">🛡️ Enhanced Security</h3>
191
+ <p>
192
+ cDOM strictly avoids <code>eval()</code> and direct HTML injection. By using a custom
193
+ high-performance parser and a registry of pre-defined helper functions, it provides a safe sandbox
194
+ for dynamic content, making it highly resistant to XSS attacks.
195
+ </p>
196
+ </div>
197
+ <div class="feature-card">
198
+ <h3 class="feature-title">🤖 LLM Friendly</h3>
199
+ <p>
200
+ Large Language Models excel at generating structured data and formulaic expressions. cDOM's
201
+ declarative nature and concise syntax make it far easier for AI to generate correct,
202
+ bug-free UI components compared to traditional JavaScript-heavy frameworks.
203
+ </p>
204
+ </div>
205
+ </div>
206
+
161
207
  <!-- ===== XPATH NAVIGATION ===== -->
162
208
  <h2 id="xpath-navigation">Using XPath</h2>
163
209
  <p>
@@ -184,17 +230,20 @@ const { parseJPRX, hydrate } = globalThis.LightviewCDOM;
184
230
  const { $ } = Lightview;
185
231
 
186
232
  const cdom = `{
233
+ // The cdom below uses a compressed syntax. It does not require quotes
234
+ // around properties or JPRX/XPath values and supports comments.
235
+ // It is JSON-like, but not JSON.
187
236
  div: {
188
237
  id: "profile-container",
189
238
  class: "card",
190
- "data-theme": "dark",
239
+ data-theme: "dark",
191
240
  children: [
192
241
  { h3: "User Profile" },
193
242
  { button: {
194
243
  id: "7",
195
- // XPath #../@id gets the "7" from this button's id
196
- // XPath #../../@id gets "profile-container" from the g-parent div
197
- children: ["Button ", #../@id, " in section ", #../../@id]
244
+ // XPath #(../@id) gets the "7" from this button's id
245
+ // XPath #(../../@id) gets "profile-container" from the g-parent div
246
+ children: ["Button ", #(../@id), " in section ", #(../../@id)]
198
247
  }}
199
248
  ]
200
249
  }
@@ -204,33 +253,12 @@ $('#example').content(hydrate(parseJPRX(cdom)));</code></pre>
204
253
  </div>
205
254
  <p>
206
255
  In the example above, the button's text is derived entirely from its own <code>id</code> and its
207
- parent's <code>id</code> using <code>#../@id</code> and <code>#../../@id</code>.
256
+ parent's <code>id</code> using <code>#(../@id)</code> and <code>#(../../@id)</code>.
208
257
  </p>
209
258
  <p>
210
259
  For more details, see the <a href="/docs/cdom-xpath.html">Full XPath Documentation</a>.
211
260
  </p>
212
261
 
213
- <!-- ===== ADVANTAGES ===== -->
214
- <h2 id="advantages">Advantages</h2>
215
- <div class="feature-grid" style="margin: 2rem 0;">
216
- <div class="feature-card">
217
- <h3 class="feature-title">🛡️ Enhanced Security</h3>
218
- <p>
219
- cDOM strictly avoids <code>eval()</code> and direct HTML injection. By using a custom
220
- high-performance parser and a registry of pre-defined helper functions, it provides a safe sandbox
221
- for dynamic content, making it highly resistant to XSS attacks.
222
- </p>
223
- </div>
224
- <div class="feature-card">
225
- <h3 class="feature-title">🤖 LLM Friendly</h3>
226
- <p>
227
- Large Language Models excel at generating structured data and formulaic expressions. cDOM's
228
- declarative nature and concise syntax make it far easier for AI to generate correct,
229
- bug-free UI components compared to traditional JavaScript-heavy frameworks.
230
- </p>
231
- </div>
232
- </div>
233
-
234
262
  <!-- ===== JPRX INTRODUCTION ===== -->
235
263
  <h2 id="JPRX">JPRX (JSON Pointer Reactive eXpressions)</h2>
236
264
  <p>
@@ -239,54 +267,61 @@ $('#example').content(hydrate(parseJPRX(cdom)));</code></pre>
239
267
  with reactivity, relative paths, and helper functions.
240
268
  </p>
241
269
 
242
- <h3 id="JPRX-delimiters">Delimiters</h3>
270
+ <h3 id="JPRX-delimiters">Expression Syntax</h3>
243
271
  <p>
244
- JPRX expressions begin with a <code>=</code> delimiter:
272
+ JPRX expressions use a <strong>wrapper syntax</strong> for unambiguous parsing. The recommended syntax is
273
+ <code>=(expression)</code>:
245
274
  </p>
246
275
  <table class="api-table">
247
276
  <thead>
248
277
  <tr>
249
- <th>Delimiter</th>
278
+ <th>Syntax</th>
250
279
  <th>Purpose</th>
251
280
  <th>Example</th>
252
281
  </tr>
253
282
  </thead>
254
283
  <tbody>
255
284
  <tr>
256
- <td><code>=/</code></td>
285
+ <td><code>=(/path)</code></td>
257
286
  <td>Access a path in the global registry</td>
258
- <td><code>=/users/0/name</code></td>
287
+ <td><code>=(/users/0/name)</code></td>
259
288
  </tr>
260
289
  <tr>
261
- <td><code>=function(</code></td>
290
+ <td><code>=(function(...))</code></td>
262
291
  <td>Call a helper function</td>
263
- <td><code>=sum(/items...price)</code></td>
292
+ <td><code>=(sum(/items...price))</code></td>
293
+ </tr>
294
+ <tr>
295
+ <td><code>#(xpath)</code></td>
296
+ <td>XPath expression for DOM navigation</td>
297
+ <td><code>#(../../@id)</code></td>
264
298
  </tr>
265
299
  </tbody>
266
300
  </table>
267
301
  <p>
268
- Once inside a JPRX expression, paths follow <strong>JSON Pointer</strong> syntax. The <code>=</code>
269
- is only needed at the start of the expression for paths or function names.
302
+ The wrapper syntax <code>=()</code> and <code>#()</code> makes expressions unambiguous and eliminates
303
+ conflicts with literal strings.
304
+ Legacy syntax without parentheses (e.g., <code>=/path</code>) is still supported for backward compatibility.
270
305
  </p>
271
306
 
272
- <h3 id="JPRX-escaping">Escaping the <code>=</code> Delimiter</h3>
307
+ <h3 id="JPRX-literal-strings">Literal Strings</h3>
273
308
  <p>
274
- When using <strong>oDOM</strong> or <strong>vDOM</strong> (Object DOM), any string value starting with
275
- <code>=</code> is interpreted as a JPRX expression. If you need a literal string that begins with
276
- an equals sign (e.g., a mathematical equation or status message), you can escape it by prefixing
277
- with a single quote:
309
+ With the new wrapper syntax, you no longer need escape sequences. Any string that doesn't match the wrapper
310
+ pattern
311
+ is treated as a literal:
278
312
  </p>
279
313
  <div class="code-block" style="margin-bottom: 1rem;">
280
- <pre><code>// Interpreted as JPRX expression:
281
- { "p": "=/user/name" } // Resolves the path /user/name
314
+ <pre><code>// JPRX expression (wrapped):
315
+ { "p": "=(/user/name)" } // Resolves the path /user/name
282
316
 
283
- // Escaped to produce a literal string:
284
- { "p": "'=E=mc²" } // Renders as "=E=mc²"
285
- { "p": "'=42" } // Renders as "=42"</code></pre>
317
+ // Literal strings (no wrapper):
318
+ { "p": "=E=mc²" } // Renders as "=E=mc²"
319
+ { "p": "=42" } // Renders as "=42"
320
+ { "p": "#ff0000" } // Renders as "#ff0000" (hex color)</code></pre>
286
321
  </div>
287
322
  <p>
288
- The single-quote escape (<code>'=</code>) at the start of a string tells the parser to treat
289
- the rest of the string (including the <code>=</code>) as literal content.
323
+ The wrapper syntax eliminates ambiguity: <code>=(expr)</code> is always an expression, while strings without
324
+ the wrapper pattern are always literals.
290
325
  </p>
291
326
 
292
327
  <h3 id="JPRX-anatomy">Anatomy of a Path</h3>
@@ -369,6 +404,44 @@ $('#example').content(hydrate(parseJPRX(cdom)));</code></pre>
369
404
  </tbody>
370
405
  </table>
371
406
 
407
+ <h3 id="JPRX-explosion">Explosion & Mapping (<code>...</code>)</h3>
408
+ <p>
409
+ The <code>...</code> operator is a powerful JPRX extension used for mapping properties from arrays and
410
+ spreading array elements as function arguments.
411
+ </p>
412
+
413
+ <h4>Mapping & Auto-Explosion: <code>path...property</code></h4>
414
+ <p>
415
+ When used between a path to an array and a property name, it acts as a shorthand for mapping. It extracts
416
+ the specified property from every object in the array. <strong>Inside a function call, it automatically
417
+ spreads (explodes) the resulting values as individual arguments.</strong>
418
+ </p>
419
+ <div class="code-block" style="margin-bottom: 1.5rem;">
420
+ <pre><code>// Correct: Maps 'price' and automatically spreads results as arguments to sum()
421
+ =sum(/cart/items...price)
422
+
423
+ // Mapping also works for direct display (returns an array)
424
+ { p: ["Prices: ", =/cart/items...price] }</code></pre>
425
+ </div>
426
+
427
+ <h4>Argument Spreading: <code>path...</code></h4>
428
+ <p>
429
+ When an array path is followed by a trailing <code>...</code> at the end of the path expression (and no
430
+ property name follows), the array elements are spread as individual arguments. Use this when you have a
431
+ direct reference to an array.
432
+ </p>
433
+ <div class="code-block">
434
+ <pre><code>// Passes each number in the array as a separate argument
435
+ =sum(/listOfNumbers...)</code></pre>
436
+ </div>
437
+
438
+ <div class="warning-notice"
439
+ style="margin-top: 1.5rem; padding: 1rem; background: rgba(239, 68, 68, 0.1); border-left: 4px solid #ef4444; border-radius: 4px;">
440
+ <strong>Warning:</strong> Do not combine infix mapping with trailing dots. <code>/items...price...</code> is
441
+ invalid because the trailing dots are interpreted as part of the property name (looking for a property
442
+ literally named "price...").
443
+ </div>
444
+
372
445
  <!-- ===== COMPARISON TO EXCEL ===== -->
373
446
  <h2 id="comparison">Comparison to Excel</h2>
374
447
  <p>
@@ -536,8 +609,37 @@ $('#example').content(hydrate(parseJPRX(cdom)));</code></pre>
536
609
  <h3 id="lifecycle-state">Lifecycle State</h3>
537
610
  <p>
538
611
  In Lightview, you initialize state within the <code>onmount</code> hook. The <code>=state</code> and
539
- <code>=signal</code> helpers accept an options object where you can specify a <code>scope</code>,
540
- as well as <code>schema</code> requirements.
612
+ <code>=signal</code> helpers allow you to create reactive data that is tied to the DOM structure.
613
+ </p>
614
+
615
+ <h4 id="scope-resolution">Scope & Name Resolution</h4>
616
+ <p>
617
+ When JPRX encounters a name (e.g., <code>=/profile/name</code>), it doesn't just look in a global registry.
618
+ Instead, it performs an <strong>up-tree search</strong>:
619
+ </p>
620
+ <ol>
621
+ <li>It starts at the element where the expression is defined.</li>
622
+ <li>It looks for a registered signal or state with that name.</li>
623
+ <li>If not found, it moves to the parent element and repeats the search.</li>
624
+ <li>This continues all the way to the <code>document</code> root, finally checking the global registry if
625
+ needed.</li>
626
+ </ol>
627
+
628
+ <h4>The <code>$this</code> Placeholder</h4>
629
+ <p>
630
+ The <code>$this</code> keyword is a special placeholder that represents the <strong>current
631
+ element</strong>. When used in the <code>scope</code> option of <code>=state</code> or
632
+ <code>=signal</code>, it instructs Lightview to register the name specifically at that element's level in
633
+ the DOM.
634
+ </p>
635
+ <div class="code-block" style="margin-bottom: 1.5rem;">
636
+ <pre><code>// Scoping state to the current element creates a local namespace
637
+ { "onmount": "=state({ count: 0 }, { name: 'local', scope: $this })" }</code></pre>
638
+ </div>
639
+ <p>
640
+ This mechanism is what allows you to create <strong>reusable components</strong>. Each instance of a
641
+ component can have its own "local" state because the search for <code>local</code> will stop at the first
642
+ element it finds that has it registered—typically the root of the component instance.
541
643
  </p>
542
644
 
543
645
  <h4>Using a Registered Schema</h4>
@@ -578,10 +680,7 @@ Lightview.registerSchema('User', { name: 'string', age: 'number' });
578
680
  }
579
681
  }</code></pre>
580
682
  </div>
581
- <p>
582
- By scoping the state to the element, you can create multiple independent instances of components.
583
- Lightview uses a high-performance <strong>up-tree search</strong> to resolve these names.
584
- </p>
683
+
585
684
 
586
685
  <h3 id="bind-helper">Two-Way Binding ($bind)</h3>
587
686
  <p>
@@ -646,11 +745,11 @@ const cdomString = `{
646
745
  children: [
647
746
  { h3: "Shopping Cart" },
648
747
  { ul: {
649
- children: =map(/store/cart/items, { li: { children: [_/name, " - ", currency(_/price)] } })
748
+ children: =(map(/store/cart/items, { li: { children: [_/name, " - ", currency(_/price)] } }))
650
749
  }},
651
750
  { p: {
652
751
  style: "font-weight: bold; margin-top: 1rem;",
653
- children: ["Total: ", =currency(sum(/store/cart/items...price))]
752
+ children: ["Total: ", =(currency(sum(/store/cart/items...price)))]
654
753
  }}
655
754
  ]
656
755
  }
@@ -664,17 +763,15 @@ $('#example').content(hydrated);
664
763
  <!-- ===== INTERACTIVE EXAMPLE ===== -->
665
764
  <h2 id="interactive-example">Interactive Example</h2>
666
765
  <p>
667
- Choose a syntax to see how the same reactive counter can be defined using standard JSON, concise JPRXC, or
766
+ Choose a syntax to see how the same reactive counter can be defined using standard JSON, concise cDOMC, or
668
767
  operator syntax.
669
768
  </p>
670
769
 
671
770
  <div role="tablist" class="syntax-tabs">
672
771
  <button id="tab-btn-jprx" class="syntax-tab syntax-tab-active" onclick="switchCDOMTab('jprx')">JPRX
673
772
  (Standard)</button>
674
- <button id="tab-btn-jprxc" class="syntax-tab" onclick="switchCDOMTab('jprxc')">JPRXC (Concise)</button>
773
+ <button id="tab-btn-cdomc" class="syntax-tab" onclick="switchCDOMTab('cdomc')">cDOMC</button>
675
774
  <button id="tab-btn-operators" class="syntax-tab" onclick="switchCDOMTab('operators')">Operators</button>
676
- <button id="tab-btn-embedded" class="syntax-tab" onclick="switchCDOMTab('embedded')">Embedded
677
- Signals</button>
678
775
  </div>
679
776
 
680
777
  <!-- JPRX Pane -->
@@ -690,18 +787,17 @@ $('#example').content(hydrated);
690
787
  </script><code contenteditable="true">// JPRX: Standard JSON format (strict)
691
788
  await import('/lightview-cdom.js');
692
789
  const { parseJPRX, hydrate } = globalThis.LightviewCDOM;
693
- const { signal, $ } = Lightview;
694
-
695
- const count = signal(0, 'count');
790
+ const { $ } = Lightview;
696
791
 
697
792
  const cdomString = `{
698
793
  "div": {
794
+ onmount: "=signal(0, 'count')",
699
795
  "children": [
700
796
  { "h3": ["Standard JPRX Counter"] },
701
- { "p": { "children": ["Count: ", "=/count"] }},
797
+ { "p": { "children": ["Count: ", "=(/count)"] }},
702
798
  { "div": { "children": [
703
- { "button": { "onclick": "=decrement(/count)", "children": ["-"] } },
704
- { "button": { "onclick": "=increment(/count)", "children": ["+"] } }
799
+ { "button": { "onclick": "=(decrement(/count))", "children": ["-"] } },
800
+ { "button": { "onclick": "=(increment(/count))", "children": ["+"] } }
705
801
  ]}}
706
802
  ]
707
803
  }
@@ -709,12 +805,11 @@ const cdomString = `{
709
805
 
710
806
  const hydrated = hydrate(parseJPRX(cdomString));
711
807
  $('#example').content(hydrated);
712
- globalThis.LightviewCDOM.activate(hydrated.domEl);
713
808
  </code></pre>
714
809
  </div>
715
810
 
716
- <!-- JPRXC Pane -->
717
- <div id="pane-jprxc" style="display: none;">
811
+ <!-- cDOMC Pane -->
812
+ <div id="pane-cdomc" style="display: none;">
718
813
  <pre><script>
719
814
  examplify(document.currentScript.nextElementSibling, {
720
815
  at: document.currentScript.parentElement,
@@ -722,21 +817,21 @@ globalThis.LightviewCDOM.activate(hydrated.domEl);
722
817
  type: 'module',
723
818
  height: '250px'
724
819
  });
725
- </script><code contenteditable="true">// JPRXC: Concise format (unquoted keys, comments)
820
+ </script><code contenteditable="true">// cDOMC: Compressed format (unquoted keys, comments)
726
821
  await import('/lightview-cdom.js');
727
822
  const { parseJPRX, hydrate } = globalThis.LightviewCDOM;
728
- const { signal, $ } = Lightview;
823
+ const { $ } = ightview;
729
824
 
730
- const count = signal(0, 'count');
731
825
 
732
826
  const cdomString = `{
733
827
  div: {
828
+ onmount: =signal(0, 'count'),
734
829
  children: [
735
- { h3: ["Concise Counter"] },
736
- { p: { children: ["Count: ", =/count] }},
830
+ { h3: ["Compressed Counter"] },
831
+ { p: { children: ["Count: ", =(/count)] }},
737
832
  { div: { children: [
738
- { button: { onclick: =decrement(/count), children: ["-"] } },
739
- { button: { onclick: =increment(/count), children: ["+"] } }
833
+ { button: { onclick: =(decrement(/count)), children: ["-"] } },
834
+ { button: { onclick: =(increment(/count)), children: ["+"] } }
740
835
  ]}}
741
836
  ]
742
837
  }
@@ -760,19 +855,18 @@ globalThis.LightviewCDOM.activate(hydrated.domEl);
760
855
  </script><code contenteditable="true">// Operators: Using =++/path and =--/path
761
856
  await import('/lightview-cdom.js');
762
857
  const { parseJPRX, hydrate } = globalThis.LightviewCDOM;
763
- const { signal, $ } = Lightview;
764
-
765
- const count = signal(0, 'count');
858
+ const { $ } = Lightview;
766
859
 
767
860
  const cdomString = `{
768
861
  div: {
862
+ onmount: =signal(0, 'count'),
769
863
  children: [
770
864
  { h3: ["Operator Counter"] },
771
- { p: { children: ["Count: ", =/count] }},
865
+ { p: { children: ["Count: ", =(/count)] }},
772
866
  { div: { children: [
773
- // Prefix operators: =-- and =++
774
- { button: { onclick: =--/count, children: ["-"] } },
775
- { button: { onclick: =++/count, children: ["+"] } }
867
+ // Prefix operators: =(--/count) and =(++/count)
868
+ { button: { onclick: =(--/count), children: ["-"] } },
869
+ { button: { onclick: =(++/count), children: ["+"] } }
776
870
  ]}}
777
871
  ]
778
872
  }
@@ -784,39 +878,6 @@ globalThis.LightviewCDOM.activate(hydrated.domEl);
784
878
  </code></pre>
785
879
  </div>
786
880
 
787
- <!-- Embedded Pane -->
788
- <div id="pane-embedded" style="display: none;">
789
- <pre><script>
790
- examplify(document.currentScript.nextElementSibling, {
791
- at: document.currentScript.parentElement,
792
- scripts: ['/lightview.js', '/lightview-x.js', '/lightview-cdom.js'],
793
- type: 'module',
794
- height: '250px'
795
- });
796
- </script><code contenteditable="true">// Embedded Signals: Using onmount to initialize local state
797
- await import('/lightview-cdom.js');
798
- const { parseJPRX, hydrate } = globalThis.LightviewCDOM;
799
- const { $ } = Lightview;
800
-
801
- const cdomString = `{
802
- div: {
803
- // Initialize a named signal explicitly scoped to '$this' element.
804
- onmount: =signal(0, { name: 'count', scope: $this }),
805
- children: [
806
- { h3: ["Embedded Counter"] },
807
- { p: ["Count: ", =/count] },
808
- { div: { children: [
809
- { button: { onclick: =--/count, children: ["-"] } },
810
- { button: { onclick: =++/count, children: ["+"] } }
811
- ]}}
812
- ]
813
- }
814
- }`;
815
-
816
- const hydrated = hydrate(parseJPRX(cdomString));
817
- $('#example').content(hydrated);
818
- </code></pre>
819
- </div>
820
881
 
821
882
  <!-- ===== OPERATOR SYNTAX EXAMPLE ===== -->
822
883
  <h2 id="operator-syntax">Operator Syntax</h2>
@@ -938,7 +999,7 @@ LightviewCDOM.activate(document.getElementById('myApp'));</code></pre>
938
999
 
939
1000
  <h3 id="api-hydrate">hydrate(object)</h3>
940
1001
  <p>
941
- Converts <code>$</code>-prefixed strings into reactive computed signals.
1002
+ Converts <code>=/</code> prefixed strings into reactive computed signals.
942
1003
  </p>
943
1004
  <div class="code-block">
944
1005
  <pre><code>const config = { title: "Dashboard", total: "=/cart/items...price" };