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/AI-GUIDANCE.md CHANGED
@@ -151,8 +151,25 @@ Lightview X allows embedded `${}` expressions in attributes for simple reactivit
151
151
 
152
152
  **cDOM (Computed DOM)** and **JPRX (JSON Reactive Expressions)** allow you to build fully reactive UIs using **only JSON**.
153
153
 
154
- ### JPRX Helper Reference
155
- JPRX supports a wide range of helpers. AI agents should use these instead of trying to write JS when builing streaming UIs at runtime.
154
+ ### Expression Syntax
155
+ * **`=(expr)` (JPRX)**: Wraps reactive expressions that navigate **reactive state** (Signals/States). Use `=(/path)` for paths, `=(function(...))` for helpers.
156
+ * **`#(xpath)` (cDOM XPath)**: Wraps XPath expressions that navigate the **DOM tree** during construction. Use `#(../../@id)` to navigate ancestors and access attributes.
157
+
158
+ **Note**: For initialization functions like `state()` and `signal()`, use `=function(...)` without the outer wrapper, as they execute once on mount rather than being reactive expressions.
159
+
160
+ ### Name Resolution & Scoping
161
+ JPRX uses an **up-tree search** to find signals or states:
162
+ 1. **Search starts** at the element where the expression is defined.
163
+ 2. **Bubbles up** through parent elements looking for a registered name.
164
+ 3. **Falls back** to the global registry.
165
+
166
+ **The `$this` Placeholder**:
167
+ Use `{ scope: $this }` in `=state` or `=signal` to register data specifically at the current element's level. This is essential for creating multiple independent instances of a component.
168
+
169
+ ### The `...` (Explosion) Operator
170
+ * **Infix Mapping (`/path...property`)**: Extracts `property` from every object in the `/path` array. **Inside a function call, it automatically explodes** the results into separate arguments.
171
+ * **Trailing Spread (`/path...`)**: Spreads a direct array reference into individual arguments.
172
+ * **Warning**: Never write `/path...property...`. The trailing dots will be incorrectly included in the property name lookup.
156
173
 
157
174
  | Category | Helpers |
158
175
  | :--- | :--- |
@@ -163,7 +180,7 @@ JPRX supports a wide range of helpers. AI agents should use these instead of try
163
180
  | **Stats** | `sum`, `avg`, `min`, `max`, `median`, `stdev`, `variance` |
164
181
  | **Data** | `lookup(val, searchArr, resultArr)` (VLOOKUP-style) |
165
182
  | **State** | `state(val, opts)`, `set(path, val)`, `bind(path)`, `increment`, `decrement`, `toggle` |
166
- | **DOM** | `xpath(expr)` (Backward-looking only), `move(target, loc)` |
183
+ | **DOM** | `#(path)` (XPath Navigation), `move(target, loc)` |
167
184
  | **Net** | `fetchHelper(url, opts)`, `mount(url, opts)` |
168
185
 
169
186
  ### The "Decentralized Layout" Pattern (AI Strategy)
@@ -177,7 +194,7 @@ JPRX supports a wide range of helpers. AI agents should use these instead of try
177
194
  div: {
178
195
  id: "widget-1",
179
196
  onmount: ["=state({val:0}, {name:'w1', scope:$this})", "=move('#sidebar')"],
180
- children: [ { p: "Val: =/w1/val" } ]
197
+ children: [ { p: ["Val: ", =(/w1/val)] } ]
181
198
  }
182
199
  }
183
200
  ```
@@ -247,13 +264,13 @@ router.use(
247
264
  <a name="deep-links"></a>
248
265
  ## 8. Deep Links & Resources
249
266
 
250
- * **Documentation Home**: [/docs/index.html](file:///c:/Users/Owner/AntigravityProjects/lightview/docs/index.html)
251
- * **Core Logic**: [/lightview.js](file:///c:/Users/Owner/AntigravityProjects/lightview/lightview.js)
252
- * **Extension (Hypermedia)**: [/lightview-x.js](file:///c:/Users/Owner/AntigravityProjects/lightview/lightview-x.js)
253
- * **Router**: [/lightview-router.js](file:///c:/Users/Owner/AntigravityProjects/lightview/lightview-router.js)
254
- * **CDOM/JPRX Parser**: [/lightview-cdom.js](file:///c:/Users/Owner/AntigravityProjects/lightview/lightview-cdom.js)
255
- * **Component Index**: [/components/index.js](file:///c:/Users/Owner/AntigravityProjects/lightview/components/index.js)
256
- * **JPRX Helpers**: [/jprx/helpers/](file:///c:/Users/Owner/AntigravityProjects/lightview/jprx/helpers/)
267
+ * **Documentation Home**: [docs/index.html](docs/index.html)
268
+ * **Core Logic**: [lightview.js](lightview.js) and [docs/api/index.html](docs/api/index.html)
269
+ * **Extension (Hypermedia)**: [lightview-x.js](lightview-x.js) and [docs/api/hypermedia.html](docs/api/hypermedia.html)
270
+ * **Router**: [lightview-router.js](lightview-router.js) and [docs/router.html](docs/router.html)
271
+ * **CDOM/JPRX Parser**: [lightview-cdom.js](lightview-cdom.js) and [docs/cdom.html](docs/cdom.html)
272
+ * **Component Index**: [components/index.js](components/index.js) and [docs/components/index.html](docs/components/index.html)
273
+ * **JPRX Helpers**: [jprx/helpers/](jprx/helpers/) and [docs/cdom#helpers](docs/cdom.html#helpers)
257
274
 
258
275
  ---
259
276
  © 2026 AnyWhichWay LLC.
package/README.md CHANGED
@@ -43,17 +43,17 @@ Traditional UI development requires AI agents to generate imperative JavaScript
43
43
 
44
44
  ```json
45
45
  {
46
- "state": { "count": 0 },
47
46
  "div": {
47
+ "onmount": "=state({ count: 0 }, 'counter')",
48
48
  "children": [
49
- { "p": "{$count}" },
50
- { "button": { "onclick": "{set('count', add(count, 1))}", "children": ["Increment"] } }
49
+ { "p": ["Count: ", "=(/counter/count)"] },
50
+ { "button": { "onclick": "=(++/counter/count)", "children": ["Increment"] } }
51
51
  ]
52
52
  }
53
53
  }
54
54
  ```
55
55
 
56
- This JSON is **the entire application**. The `{...}` expressions are JPRX—a sandboxed, spreadsheet-like formula language (inspired by **XPath** and **JSON Pointers**) that resolves paths, calls registered helpers, and triggers reactivity automatically.
56
+ This JSON is **the entire application**. The `=()` expressions are JPRX—a sandboxed, spreadsheet-like formula language (inspired by **XPath** and **JSON Pointers**) that resolves paths, calls registered helpers, and triggers reactivity automatically.
57
57
 
58
58
  ### Learn More
59
59
 
@@ -498,7 +498,11 @@ img {
498
498
 
499
499
  /* ============= Hero Section ============= */
500
500
  .hero {
501
- padding: 5rem 1.5rem;
501
+ max-height: calc(75vh - var(--site-nav-height));
502
+ display: flex;
503
+ align-items: center;
504
+ justify-content: center;
505
+ padding: 1rem 1.5rem;
502
506
  text-align: center;
503
507
  background: linear-gradient(135deg, var(--site-bg) 0%, var(--site-bg-alt) 100%);
504
508
  position: relative;
@@ -526,7 +530,7 @@ img {
526
530
  .hero-logo {
527
531
  width: 100px;
528
532
  height: 100px;
529
- margin-bottom: 1.5rem;
533
+ margin-bottom: 0.25rem;
530
534
  }
531
535
 
532
536
 
@@ -534,7 +538,7 @@ img {
534
538
  .hero h1 {
535
539
  font-size: 3.5rem;
536
540
  font-weight: 800;
537
- margin: 0 0 0.5rem;
541
+ margin: 0 0 0.25rem;
538
542
  background: linear-gradient(135deg, var(--site-primary) 0%, var(--site-accent) 100%);
539
543
  -webkit-background-clip: text;
540
544
  -webkit-text-fill-color: transparent;
@@ -544,14 +548,14 @@ img {
544
548
  .hero-tagline {
545
549
  font-size: 1.5rem;
546
550
  color: var(--site-text-secondary);
547
- margin: 0 0 1rem;
551
+ margin: 0 0 0.5rem;
548
552
  font-weight: 500;
549
553
  }
550
554
 
551
555
  .hero-description {
552
556
  font-size: 1.125rem;
553
557
  color: var(--site-text-secondary);
554
- margin: 0 0 2rem;
558
+ margin: 0 0 1.25rem;
555
559
  max-width: 600px;
556
560
  margin-left: auto;
557
561
  margin-right: auto;
@@ -568,8 +572,8 @@ img {
568
572
  display: flex;
569
573
  gap: 3rem;
570
574
  justify-content: center;
571
- margin-top: 3rem;
572
- padding-top: 2rem;
575
+ margin-top: 1.5rem;
576
+ padding-top: 1rem;
573
577
  border-top: 1px solid var(--site-border);
574
578
  }
575
579
 
@@ -644,6 +648,9 @@ img {
644
648
  }
645
649
 
646
650
  .feature-card {
651
+ display: block;
652
+ color: inherit;
653
+ text-decoration: none;
647
654
  background: var(--site-surface);
648
655
  border: 1px solid var(--site-border);
649
656
  border-radius: var(--site-radius-lg);
@@ -673,6 +680,7 @@ img {
673
680
  font-size: 1.125rem;
674
681
  font-weight: 600;
675
682
  margin: 0 0 0.5rem;
683
+ color: var(--site-primary);
676
684
  }
677
685
 
678
686
  .feature-description {
@@ -681,6 +689,7 @@ img {
681
689
  font-size: 0.9375rem;
682
690
  }
683
691
 
692
+
684
693
  /* ============= Code Blocks ============= */
685
694
  pre,
686
695
  code {
@@ -0,0 +1,41 @@
1
+ (async function () {
2
+ // In ES modules, document.currentScript is null, so we use import.meta.url
3
+ const urlParams = new URLSearchParams(import.meta.url.split('?')[1]);
4
+ const count = parseInt(urlParams.get('count')) || 1000;
5
+
6
+ // Settle inside the script context
7
+ if (window.gc) window.gc();
8
+ await new Promise(r => setTimeout(r, 100));
9
+
10
+ const start = performance.now();
11
+
12
+ // Import Bau from CDN
13
+ const { default: Bau } = await import('https://cdn.jsdelivr.net/npm/@grucloud/bau@0.106.0/+esm');
14
+ const bau = Bau();
15
+ const { section, header, h2, h3, div, article, p, span, footer } = bau.tags;
16
+
17
+ const items = [];
18
+ for (let i = 0; i < count; i++) {
19
+ items.push(article({ class: 'item-card' },
20
+ header(h3({ class: 'item-title' }, `Item ${i}`)),
21
+ div({ class: 'item-content' }, p({ class: 'item-description' }, `Detailed description for item ${i} in the benchmark list.`)),
22
+ footer({ class: 'item-footer' }, span(`Metadata for item ${i}`))
23
+ ));
24
+ }
25
+ const fragment = section({ class: 'benchmark-container' },
26
+ header(h2('Benchmark Results')),
27
+ div({ class: 'items-grid' }, ...items)
28
+ );
29
+ const target = document.getElementById('benchmark-target');
30
+ target.replaceChildren(fragment);
31
+
32
+ const end = performance.now();
33
+
34
+ // Store timing in a global for the driver to pick up
35
+ window.__bauTaggedBenchmarkTime = end - start;
36
+
37
+ // Signal completion
38
+ if (window.__resolveBauTaggedBenchmark) {
39
+ window.__resolveBauTaggedBenchmark();
40
+ }
41
+ })();
@@ -69,9 +69,9 @@ const cdom = `{
69
69
  { h3: "User Profile" },
70
70
  { button: {
71
71
  id: "7",
72
- // XPath #@id gets "7" from this button's id
73
- // XPath #../@id gets "profile-container" from the parent div
74
- children: ["Button ", #@id, " in section ", #../@id]
72
+ // XPath #(@id) gets "7" from this button's id
73
+ // XPath #(../@id) gets "profile-container" from the parent div
74
+ children: ["Button ", #(@id), " in section ", #(../@id)]
75
75
  }}
76
76
  ]
77
77
  }
@@ -116,17 +116,35 @@ $('#example').content(hydrate(parseJPRX(cdom)));</code></pre>
116
116
  <strong>Forbidden:</strong> <code>child::</code>, <code>descendant::</code>, and <code>following::</code>
117
117
  axes are disabled as they could create infinite loops or reference nodes that do not exist yet.
118
118
  </p>
119
+ <div
120
+ style="margin-top: 1rem; padding: 1rem; background: var(--site-bg-secondary); border-left: 4px solid var(--site-primary); border-radius: var(--site-radius);">
121
+ <p style="margin: 0; font-size: 0.95rem;">
122
+ <strong>💡 Need Forward-Looking XPath?</strong> For advanced use cases requiring <code>child::</code>,
123
+ <code>descendant::</code>, or <code>following::</code> axes, use the reactive <code>=xpath()</code>
124
+ helper
125
+ in JPRX instead of static <code>#()</code> in cDOM. The reactive helper evaluates XPath expressions
126
+ after the DOM is fully constructed and can access any part of the tree. See the
127
+ <a href="#reactive-xpath">Reactive XPath section</a> below for details.
128
+ </p>
129
+ </div>
119
130
 
120
131
  <h2 id="reactive-xpath">Reactive XPath (<code>=xpath()</code>)</h2>
121
132
  <p>
122
- If you need an XPath expression to update reactively if attributes elsewhere in the DOM change,
123
- use the <code>=xpath()</code> helper within a JPRX expression.
133
+ If you need an XPath expression to update reactively when attributes elsewhere in the DOM change,
134
+ or if you need to use <strong>forward-looking axes</strong> (like <code>child::</code>,
135
+ <code>descendant::</code>,
136
+ or <code>following::</code>), use the <code>=xpath()</code> helper within a JPRX expression.
137
+ </p>
138
+ <p>
139
+ Unlike static <code>#()</code> expressions which are evaluated once during construction,
140
+ <code>=xpath()</code> creates a reactive computed signal that re-evaluates whenever its dependencies change,
141
+ and it has <strong>no axis restrictions</strong> since the DOM is already fully constructed.
124
142
  </p>
125
143
  <div class="code-block">
126
144
  <pre><code>{
127
145
  div: {
128
- title: "=xpath('../@data-section')",
129
- class: "=concat('item ', xpath('../@theme'))"
146
+ title: "=(xpath('../@data-section'))",
147
+ class: "=(concat('item ', xpath('../@theme')))"
130
148
  }
131
149
  }</code></pre>
132
150
  </div>
@@ -142,12 +160,12 @@ $('#example').content(hydrate(parseJPRX(cdom)));</code></pre>
142
160
  {
143
161
  button: {
144
162
  id: "7",
145
- children: [#../@id]
163
+ children: [#(../@id)]
146
164
  }
147
165
  }
148
166
 
149
167
  // Support for complex paths with predicates
150
- { span: [#ancestor::div[@data-role='container']/@title] }</code></pre>
168
+ { span: [#(ancestor::div[@data-role='container']/@title)] }</code></pre>
151
169
  </div>
152
170
 
153
171
  <h2 id="safety">Security & Safety</h2>