lightview 2.3.8 → 2.4.7

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 (45) hide show
  1. package/.gemini/CODE_ANALYSIS_AND_IMPROVEMENT_PLAN.md +56 -0
  2. package/AI-GUIDANCE.md +274 -0
  3. package/README.md +35 -0
  4. package/build_tmp/lightview-cdom.js +3934 -0
  5. package/build_tmp/lightview-router.js +185 -0
  6. package/build_tmp/lightview-x.js +1739 -0
  7. package/build_tmp/lightview.js +740 -0
  8. package/components/data-display/diff.js +36 -4
  9. package/docs/api/hypermedia.html +75 -5
  10. package/docs/api/index.html +3 -3
  11. package/docs/api/nav.html +0 -16
  12. package/docs/articles/html-vs-json-partials.md +102 -0
  13. package/docs/articles/lightview-vs-htmx.md +610 -0
  14. package/docs/assets/styles/site.css +16 -7
  15. package/docs/benchmarks/bau-tagged-fragment.js +41 -0
  16. package/docs/benchmarks/tagged-fragment.js +36 -0
  17. package/docs/cdom.html +127 -88
  18. package/docs/components/chart.html +157 -210
  19. package/docs/components/component-nav.html +1 -1
  20. package/docs/components/diff.html +33 -21
  21. package/docs/components/gallery.html +107 -4
  22. package/docs/components/index.css +18 -3
  23. package/docs/components/index.html +20 -9
  24. package/docs/dom-benchmark.html +771 -0
  25. package/docs/getting-started/index.html +42 -2
  26. package/docs/hypermedia/index.html +391 -0
  27. package/docs/hypermedia/nav.html +17 -0
  28. package/docs/index.html +136 -17
  29. package/index.html +59 -10
  30. package/lightview-all.js +223 -67
  31. package/lightview-cdom.js +1 -2
  32. package/lightview-x.js +144 -13
  33. package/lightview.js +85 -277
  34. package/package.json +2 -2
  35. package/src/lightview-cdom.js +1 -5
  36. package/src/lightview-x.js +158 -27
  37. package/src/lightview.js +94 -60
  38. package/docs/articles/calculator-no-javascript-hackernoon.md +0 -283
  39. package/docs/articles/calculator-no-javascript.md +0 -290
  40. package/docs/articles/part1-reference.md +0 -236
  41. package/lightview.js.bak +0 -1
  42. package/test-xpath.html +0 -63
  43. package/test_error.txt +0 -0
  44. package/test_output.txt +0 -0
  45. package/test_output_full.txt +0 -0
package/lightview.js CHANGED
@@ -133,65 +133,10 @@
133
133
  };
134
134
  const getRegistry = () => _LV.registry;
135
135
  const internals = _LV;
136
- const stateCache = /* @__PURE__ */ new WeakMap();
137
- const stateSignals = /* @__PURE__ */ new WeakMap();
138
- const stateSchemas = /* @__PURE__ */ new WeakMap();
139
136
  const { parents, schemas, hooks } = internals;
140
- const validate = (target, prop, value, schema) => {
141
- var _a, _b;
142
- const current = target[prop];
143
- const type = typeof current;
144
- const isNew = !(prop in target);
145
- let behavior = schema;
146
- if (typeof schema === "object" && schema !== null) behavior = schema.type;
147
- if (behavior === "auto" && isNew) throw new Error(`Lightview: Cannot add new property "${prop}" to fixed 'auto' state.`);
148
- if (behavior === "polymorphic" || typeof behavior === "object" && (behavior == null ? void 0 : behavior.coerce)) {
149
- if (type === "number") return Number(value);
150
- if (type === "boolean") return Boolean(value);
151
- if (type === "string") return String(value);
152
- } else if (behavior === "auto" || behavior === "dynamic") {
153
- if (!isNew && typeof value !== type) {
154
- throw new Error(`Lightview: Type mismatch for "${prop}". Expected ${type}, got ${typeof value}.`);
155
- }
156
- }
157
- if (typeof schema === "object" && schema !== null && schema.transform) {
158
- const trans = schema.transform;
159
- const transformFn = typeof trans === "function" ? trans : internals.helpers.get(trans) || ((_b = (_a = globalThis.Lightview) == null ? void 0 : _a.helpers) == null ? void 0 : _b[trans]);
160
- if (transformFn) value = transformFn(value);
161
- }
162
- if (hooks.validate(value, schema) === false) {
163
- throw new Error(`Lightview: Validation failed for "${prop}".`);
164
- }
165
- return value;
166
- };
167
137
  const protoMethods = (proto, test) => Object.getOwnPropertyNames(proto).filter((k) => typeof proto[k] === "function" && test(k));
168
- const DATE_TRACKING = protoMethods(Date.prototype, (k) => /^(to|get|valueOf)/.test(k));
169
- const DATE_MUTATING = protoMethods(Date.prototype, (k) => /^set/.test(k));
170
- const ARRAY_TRACKING = [
171
- "map",
172
- "forEach",
173
- "filter",
174
- "find",
175
- "findIndex",
176
- "some",
177
- "every",
178
- "reduce",
179
- "reduceRight",
180
- "includes",
181
- "indexOf",
182
- "lastIndexOf",
183
- "join",
184
- "slice",
185
- "concat",
186
- "flat",
187
- "flatMap",
188
- "at",
189
- "entries",
190
- "keys",
191
- "values"
192
- ];
193
- const ARRAY_MUTATING = ["push", "pop", "shift", "unshift", "splice", "sort", "reverse", "fill", "copyWithin"];
194
- const ARRAY_ITERATION = ["map", "forEach", "filter", "find", "findIndex", "some", "every", "flatMap"];
138
+ protoMethods(Date.prototype, (k) => /^(to|get|valueOf)/.test(k));
139
+ protoMethods(Date.prototype, (k) => /^set/.test(k));
195
140
  const getOrSet = (map, key, factory) => {
196
141
  let v = map.get(key);
197
142
  if (!v) {
@@ -200,169 +145,6 @@
200
145
  }
201
146
  return v;
202
147
  };
203
- const proxyGet = (target, prop, receiver, signals) => {
204
- if (prop === "__parent__") return parents.get(receiver);
205
- if (!signals.has(prop)) {
206
- signals.set(prop, signal(Reflect.get(target, prop, receiver)));
207
- }
208
- const signal$1 = signals.get(prop);
209
- const val = signal$1.value;
210
- if (typeof val === "object" && val !== null) {
211
- const childProxy = state(val);
212
- parents.set(childProxy, receiver);
213
- return childProxy;
214
- }
215
- return val;
216
- };
217
- const proxySet = (target, prop, value, receiver, signals) => {
218
- const schema = stateSchemas.get(receiver);
219
- const validatedValue = schema ? validate(target, prop, value, schema) : value;
220
- if (!signals.has(prop)) {
221
- signals.set(prop, signal(Reflect.get(target, prop, receiver)));
222
- }
223
- const success = Reflect.set(target, prop, validatedValue, receiver);
224
- const signal$1 = signals.get(prop);
225
- if (success && signal$1) signal$1.value = validatedValue;
226
- return success;
227
- };
228
- const createSpecialProxy = (obj, monitor, trackingProps = []) => {
229
- const signals = getOrSet(stateSignals, obj, () => /* @__PURE__ */ new Map());
230
- if (!signals.has(monitor)) {
231
- const initialValue = typeof obj[monitor] === "function" ? obj[monitor].call(obj) : obj[monitor];
232
- signals.set(monitor, signal(initialValue));
233
- }
234
- const isDate = obj instanceof Date;
235
- const isArray = Array.isArray(obj);
236
- const trackingMethods = isDate ? DATE_TRACKING : isArray ? ARRAY_TRACKING : trackingProps;
237
- const mutatingMethods = isDate ? DATE_MUTATING : isArray ? ARRAY_MUTATING : [];
238
- return new Proxy(obj, {
239
- get(target, prop, receiver) {
240
- if (prop === "__parent__") return parents.get(receiver);
241
- const value = target[prop];
242
- if (typeof value === "function") {
243
- const isTracking = trackingMethods.includes(prop);
244
- const isMutating = mutatingMethods.includes(prop);
245
- return function(...args) {
246
- if (isTracking) {
247
- const sig = signals.get(monitor);
248
- if (sig) void sig.value;
249
- }
250
- const startValue = typeof target[monitor] === "function" ? target[monitor].call(target) : target[monitor];
251
- if (isArray && ARRAY_ITERATION.includes(prop) && typeof args[0] === "function") {
252
- const originalCallback = args[0];
253
- args[0] = function(element2, index, array) {
254
- const wrappedElement = typeof element2 === "object" && element2 !== null ? state(element2) : element2;
255
- if (wrappedElement && typeof wrappedElement === "object") {
256
- parents.set(wrappedElement, receiver);
257
- }
258
- return originalCallback.call(this, wrappedElement, index, array);
259
- };
260
- }
261
- const result = value.apply(target, args);
262
- const endValue = typeof target[monitor] === "function" ? target[monitor].call(target) : target[monitor];
263
- if (startValue !== endValue || isMutating) {
264
- const sig = signals.get(monitor);
265
- if (sig && sig.value !== endValue) {
266
- sig.value = endValue;
267
- }
268
- }
269
- return result;
270
- };
271
- }
272
- if (prop === monitor) {
273
- const sig = signals.get(monitor);
274
- return sig ? sig.value : Reflect.get(target, prop, receiver);
275
- }
276
- if (isArray && !isNaN(parseInt(prop))) {
277
- const monitorSig = signals.get(monitor);
278
- if (monitorSig) void monitorSig.value;
279
- }
280
- return proxyGet(target, prop, receiver, signals);
281
- },
282
- set(target, prop, value, receiver) {
283
- if (prop === monitor) {
284
- const success = Reflect.set(target, prop, value, receiver);
285
- if (success) {
286
- const sig = signals.get(monitor);
287
- if (sig) sig.value = value;
288
- }
289
- return success;
290
- }
291
- return proxySet(target, prop, value, receiver, signals);
292
- }
293
- });
294
- };
295
- const state = (obj, optionsOrName) => {
296
- if (typeof obj !== "object" || obj === null) return obj;
297
- const name = typeof optionsOrName === "string" ? optionsOrName : optionsOrName == null ? void 0 : optionsOrName.name;
298
- const storage = optionsOrName == null ? void 0 : optionsOrName.storage;
299
- const scope = optionsOrName == null ? void 0 : optionsOrName.scope;
300
- const schema = optionsOrName == null ? void 0 : optionsOrName.schema;
301
- if (name && storage) {
302
- try {
303
- const item = storage.getItem(name);
304
- if (item) {
305
- const loaded = JSON.parse(item);
306
- Array.isArray(obj) && Array.isArray(loaded) ? (obj.length = 0, obj.push(...loaded)) : Object.assign(obj, loaded);
307
- }
308
- } catch (e) {
309
- }
310
- }
311
- let proxy = stateCache.get(obj);
312
- if (!proxy) {
313
- const isArray = Array.isArray(obj), isDate = obj instanceof Date;
314
- const isSpecial = isArray || isDate;
315
- const monitor = isArray ? "length" : isDate ? "getTime" : null;
316
- if (isSpecial || !(obj instanceof RegExp || obj instanceof Map || obj instanceof Set || obj instanceof WeakMap || obj instanceof WeakSet)) {
317
- proxy = isSpecial ? createSpecialProxy(obj, monitor) : new Proxy(obj, {
318
- get(t, p, r) {
319
- if (p === "__parent__") return parents.get(r);
320
- return proxyGet(t, p, r, getOrSet(stateSignals, t, () => /* @__PURE__ */ new Map()));
321
- },
322
- set(t, p, v, r) {
323
- return proxySet(t, p, v, r, getOrSet(stateSignals, t, () => /* @__PURE__ */ new Map()));
324
- }
325
- });
326
- stateCache.set(obj, proxy);
327
- } else return obj;
328
- }
329
- if (schema) stateSchemas.set(proxy, schema);
330
- if (name && storage) {
331
- effect(() => {
332
- try {
333
- storage.setItem(name, JSON.stringify(proxy));
334
- } catch (e) {
335
- }
336
- });
337
- }
338
- if (name) {
339
- const registry2 = scope && typeof scope === "object" ? internals.localRegistries.get(scope) || internals.localRegistries.set(scope, /* @__PURE__ */ new Map()).get(scope) : getRegistry();
340
- if (registry2 && registry2.has(name) && registry2.get(name) !== proxy) {
341
- throw new Error(`Lightview: A signal or state with the name "${name}" is already registered.`);
342
- }
343
- if (registry2) registry2.set(name, proxy);
344
- const futures = internals.futureSignals.get(name);
345
- if (futures) {
346
- futures.forEach((resolve) => resolve(proxy));
347
- }
348
- }
349
- return proxy;
350
- };
351
- const getState = (name, defaultValueOrOptions) => {
352
- const options = typeof defaultValueOrOptions === "object" && defaultValueOrOptions !== null ? defaultValueOrOptions : { defaultValue: defaultValueOrOptions };
353
- const { scope, defaultValue } = options;
354
- const existing = lookup(name, scope);
355
- if (existing) return existing;
356
- if (defaultValue !== void 0) return state(defaultValue, { name, scope });
357
- const future = signal(void 0);
358
- const handler = (realState) => {
359
- future.value = realState;
360
- };
361
- if (!internals.futureSignals.has(name)) internals.futureSignals.set(name, /* @__PURE__ */ new Set());
362
- internals.futureSignals.get(name).add(handler);
363
- return future;
364
- };
365
- state.get = getState;
366
148
  const core = {
367
149
  get currentEffect() {
368
150
  return (globalThis.__LIGHTVIEW_INTERNALS__ || (globalThis.__LIGHTVIEW_INTERNALS__ = {})).currentEffect;
@@ -404,9 +186,9 @@
404
186
  });
405
187
  };
406
188
  const trackEffect = (node, effectFn) => {
407
- const state2 = getOrSet(nodeState, node, nodeStateFactory);
408
- if (!state2.effects) state2.effects = [];
409
- state2.effects.push(effectFn);
189
+ const state = getOrSet(nodeState, node, nodeStateFactory);
190
+ if (!state.effects) state.effects = [];
191
+ state.effects.push(effectFn);
410
192
  };
411
193
  const SHADOW_DOM_MARKER = Symbol("lightview.shadowDOM");
412
194
  const createShadowDOMMarker = (attributes, children) => ({
@@ -458,6 +240,7 @@
458
240
  tag,
459
241
  attributes,
460
242
  children,
243
+ isProxy: true,
461
244
  get domEl() {
462
245
  return domNode;
463
246
  }
@@ -466,6 +249,10 @@
466
249
  domToElement.set(domNode, proxy);
467
250
  return proxy;
468
251
  };
252
+ const someRecursive = (item, predicate) => {
253
+ if (Array.isArray(item)) return item.some((i) => someRecursive(i, predicate));
254
+ return predicate(item);
255
+ };
469
256
  const element = (tag, attributes = {}, children = []) => {
470
257
  if (customTags[tag]) tag = customTags[tag];
471
258
  if (typeof tag === "function") {
@@ -486,38 +273,56 @@
486
273
  }
487
274
  };
488
275
  const update = () => {
489
- const flat = (Array.isArray(el.children) ? el.children : [el.children]).flat(Infinity);
490
- const bits = flat.map((c) => {
276
+ const bits = [];
277
+ const walk = (c) => {
278
+ if (Array.isArray(c)) {
279
+ for (let i = 0; i < c.length; i++) walk(c[i]);
280
+ return;
281
+ }
491
282
  const val = typeof c === "function" ? c() : c;
492
- if (val && typeof val === "object" && val.domEl) return val.domEl.textContent;
493
- return val === null || val === void 0 ? "" : String(val);
494
- });
283
+ if (val && typeof val === "object" && val.domEl) bits.push(val.domEl.textContent);
284
+ else bits.push(val === null || val === void 0 ? "" : String(val));
285
+ };
286
+ walk(el.children);
495
287
  domNode2.textContent = bits.join(" ");
496
288
  };
497
- const proxy2 = new Proxy(el, {
289
+ const proxy = new Proxy(el, {
498
290
  set(target, prop, value) {
499
291
  target[prop] = value;
500
292
  if (prop === "children") update();
501
293
  return true;
502
294
  }
503
295
  });
504
- const hasReactive = children.flat(Infinity).some((c) => typeof c === "function");
296
+ const hasReactive = someRecursive(children, (c) => typeof c === "function");
505
297
  if (hasReactive) {
506
298
  const runner = effect(update);
507
299
  trackEffect(domNode2, runner);
508
300
  }
509
301
  update();
510
- return proxy2;
302
+ return proxy;
511
303
  }
512
304
  const isSVG = tag.toLowerCase() === "svg";
513
305
  const wasInSVG = inSVG;
514
306
  if (isSVG) inSVG = true;
515
307
  const domNode = inSVG ? document.createElementNS("http://www.w3.org/2000/svg", tag) : document.createElement(tag);
516
- const proxy = wrapDomElement(domNode, tag, attributes, children);
517
- proxy.attributes = attributes;
518
- proxy.children = children;
308
+ const hasReactiveAttr = Object.values(attributes).some((v) => typeof v === "function");
309
+ const hasReactiveChild = someRecursive(children, (c) => typeof c === "function" || c && c.isProxy);
310
+ if (hasReactiveAttr || hasReactiveChild) {
311
+ const proxy = wrapDomElement(domNode, tag, attributes, children);
312
+ proxy.attributes = attributes;
313
+ proxy.children = children;
314
+ if (isSVG) inSVG = wasInSVG;
315
+ return proxy;
316
+ }
317
+ makeReactiveAttributes(attributes, domNode);
318
+ setupChildren(children, domNode);
519
319
  if (isSVG) inSVG = wasInSVG;
520
- return proxy;
320
+ return {
321
+ tag,
322
+ attributes,
323
+ children,
324
+ domEl: domNode
325
+ };
521
326
  };
522
327
  const processComponentResult = (result) => {
523
328
  if (!result) return null;
@@ -526,7 +331,7 @@
526
331
  }
527
332
  if (result.domEl) return result;
528
333
  const type = typeof result;
529
- if (type === "object" && result instanceof HTMLElement) {
334
+ if (type === "object" && result && result.nodeType === 1) {
530
335
  return wrapDomElement(result, result.tagName.toLowerCase(), {}, []);
531
336
  }
532
337
  if (type === "object" && result instanceof String) {
@@ -538,7 +343,7 @@
538
343
  const template = document.createElement("template");
539
344
  template.innerHTML = result.trim();
540
345
  const content = template.content;
541
- if (content.childNodes.length === 1 && content.firstChild instanceof HTMLElement) {
346
+ if (content.childNodes.length === 1 && content.firstChild && content.firstChild.nodeType === 1) {
542
347
  const el = content.firstChild;
543
348
  return wrapDomElement(el, el.tagName.toLowerCase(), {}, []);
544
349
  } else {
@@ -583,25 +388,26 @@
583
388
  domNode.setAttribute(key, value);
584
389
  }
585
390
  };
586
- const makeReactiveAttributes = (attributes, domNode) => {
391
+ const makeReactiveAttributes = (attributes = {}, domNode) => {
587
392
  const reactiveAttrs = {};
588
- for (let [key, value] of Object.entries(attributes)) {
589
- if (value && typeof value === "object" && value.__xpath__ && value.__static__) {
393
+ for (const key in attributes) {
394
+ const value = attributes[key];
395
+ const type = typeof value;
396
+ if (value && type === "object" && value.__xpath__ && value.__static__) {
590
397
  domNode.setAttribute(`data-xpath-${key}`, value.__xpath__);
591
398
  reactiveAttrs[key] = value;
592
399
  continue;
593
400
  }
594
401
  if (key === "onmount" || key === "onunmount") {
595
- const state2 = getOrSet(nodeState, domNode, nodeStateFactory);
596
- state2[key] = value;
402
+ const state = getOrSet(nodeState, domNode, nodeStateFactory);
403
+ state[key] = value;
597
404
  if (key === "onmount" && domNode.isConnected) {
598
405
  value(domNode);
599
406
  }
600
407
  } else if (key.startsWith("on")) {
601
- if (typeof value === "function") {
602
- const eventName = key.slice(2).toLowerCase();
603
- domNode.addEventListener(eventName, value);
604
- } else if (typeof value === "string") {
408
+ if (type === "function") {
409
+ domNode.addEventListener(key.slice(2).toLowerCase(), value);
410
+ } else if (type === "string") {
605
411
  domNode.setAttribute(key, value);
606
412
  }
607
413
  reactiveAttrs[key] = value;
@@ -610,7 +416,8 @@
610
416
  if (processed !== void 0) {
611
417
  reactiveAttrs[key] = processed;
612
418
  } else if (key === "style") {
613
- Object.entries(value).forEach(([styleKey, styleValue]) => {
419
+ for (const styleKey in entries) {
420
+ const styleValue = entries[styleKey];
614
421
  if (typeof styleValue === "function") {
615
422
  const runner = effect(() => {
616
423
  domNode.style[styleKey] = styleValue();
@@ -619,13 +426,13 @@
619
426
  } else {
620
427
  domNode.style[styleKey] = styleValue;
621
428
  }
622
- });
429
+ }
623
430
  reactiveAttrs[key] = value;
624
431
  } else {
625
432
  setAttributeValue(domNode, key, value);
626
433
  reactiveAttrs[key] = value;
627
434
  }
628
- } else if (typeof value === "function") {
435
+ } else if (type === "function") {
629
436
  const runner = effect(() => {
630
437
  const result = value();
631
438
  if (key === "style" && typeof result === "object") {
@@ -649,21 +456,24 @@
649
456
  }
650
457
  const childElements = [];
651
458
  const isSpecialElement = targetNode.tagName && (targetNode.tagName.toLowerCase() === "script" || targetNode.tagName.toLowerCase() === "style");
652
- const flatChildren = children.flat(Infinity);
653
- for (let child of flatChildren) {
459
+ const walk = (child) => {
460
+ if (Array.isArray(child)) {
461
+ for (let i = 0; i < child.length; i++) walk(child[i]);
462
+ return;
463
+ }
464
+ if (child === null || child === void 0) return;
654
465
  if (Lightview.hooks.processChild && !isSpecialElement) {
655
466
  child = Lightview.hooks.processChild(child) ?? child;
656
467
  }
657
- if (isShadowDOMMarker(child)) {
658
- if (targetNode instanceof ShadowRoot) {
659
- console.warn("Lightview: Cannot nest shadowDOM inside another shadowDOM");
660
- continue;
661
- }
662
- processShadowDOM(child, targetNode);
663
- continue;
664
- }
665
468
  const type = typeof child;
666
- if (type === "function") {
469
+ if (child && type === "object" && child.tag) {
470
+ const childEl = child.domEl ? child : element(child.tag, child.attributes || {}, child.children || []);
471
+ targetNode.appendChild(childEl.domEl);
472
+ childElements.push(childEl);
473
+ } else if (["string", "number", "boolean", "symbol"].includes(type) || child && type === "object" && child instanceof String) {
474
+ targetNode.appendChild(document.createTextNode(child));
475
+ childElements.push(child);
476
+ } else if (type === "function") {
667
477
  const startMarker = document.createComment("lv:s");
668
478
  const endMarker = document.createComment("lv:e");
669
479
  targetNode.appendChild(startMarker);
@@ -692,17 +502,9 @@
692
502
  runner = effect(update);
693
503
  trackEffect(startMarker, runner);
694
504
  childElements.push(child);
695
- } else if (child && typeof child === "object" && child.__xpath__ && child.__static__) {
696
- const textNode = document.createTextNode("");
697
- textNode.__xpathExpr = child.__xpath__;
698
- targetNode.appendChild(textNode);
699
- childElements.push(child);
700
- } else if (["string", "number", "boolean", "symbol"].includes(type) || child && type === "object" && child instanceof String) {
701
- targetNode.appendChild(document.createTextNode(child));
702
- childElements.push(child);
703
505
  } else if (child instanceof Node) {
704
506
  const node = child.domEl || child;
705
- if (node instanceof HTMLElement || node instanceof SVGElement) {
507
+ if (node.nodeType === 1) {
706
508
  const wrapped = wrapDomElement(node, node.tagName.toLowerCase());
707
509
  targetNode.appendChild(node);
708
510
  childElements.push(wrapped);
@@ -710,12 +512,20 @@
710
512
  targetNode.appendChild(node);
711
513
  childElements.push(child);
712
514
  }
713
- } else if (child && type === "object" && child.tag) {
714
- const childEl = child.domEl ? child : element(child.tag, child.attributes || {}, child.children || []);
715
- targetNode.appendChild(childEl.domEl);
716
- childElements.push(childEl);
515
+ } else if (isShadowDOMMarker(child)) {
516
+ if (targetNode instanceof ShadowRoot) {
517
+ console.warn("Lightview: Cannot nest shadowDOM inside another shadowDOM");
518
+ return;
519
+ }
520
+ processShadowDOM(child, targetNode);
521
+ } else if (child && typeof child === "object" && child.__xpath__ && child.__static__) {
522
+ const textNode = document.createTextNode("");
523
+ textNode.__xpathExpr = child.__xpath__;
524
+ targetNode.appendChild(textNode);
525
+ childElements.push(child);
717
526
  }
718
- }
527
+ };
528
+ walk(children);
719
529
  return childElements;
720
530
  };
721
531
  const setupChildrenInTarget = (children, targetNode) => {
@@ -727,7 +537,7 @@
727
537
  const enhance = (selectorOrNode, options = {}) => {
728
538
  const domNode = typeof selectorOrNode === "string" ? document.querySelector(selectorOrNode) : selectorOrNode;
729
539
  const node = domNode.domEl || domNode;
730
- if (!(node instanceof HTMLElement)) return null;
540
+ if (!node || node.nodeType !== 1) return null;
731
541
  const tagName = node.tagName.toLowerCase();
732
542
  let el = domToElement.get(node);
733
543
  if (!el) {
@@ -819,8 +629,6 @@
819
629
  }
820
630
  });
821
631
  const Lightview = {
822
- state,
823
- getState,
824
632
  registerSchema: (name, definition) => internals.schemas.set(name, definition),
825
633
  signal,
826
634
  get: signal.get,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lightview",
3
- "version": "2.3.8",
4
- "description": "A lightweight reactive UI library with features of Bau, Juris, and HTMX",
3
+ "version": "2.4.7",
4
+ "description": "A reactive UI library with features of Bau, Juris, and HTMX plus safe LLM UI generation",
5
5
  "main": "lightview.js",
6
6
  "workspaces": [
7
7
  "jprx"
@@ -432,13 +432,9 @@ const resolveTextNodeXPath = (node) => {
432
432
  try {
433
433
  validateXPath(xpath);
434
434
  const doc = globalThis.document || node.ownerDocument;
435
- // Use the parent node (the element) as the context for evaluation
436
- // This avoids errors in browsers that don't support Text nodes as context nodes
437
- // and keeps evaluation consistent with attributes.
438
- const contextNode = node.parentNode || node;
439
435
  const result = doc.evaluate(
440
436
  xpath,
441
- contextNode,
437
+ node,
442
438
  null,
443
439
  XPathResult.STRING_TYPE,
444
440
  null