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/lightview-all.js CHANGED
@@ -1,29 +1,48 @@
1
1
  (function() {
2
2
  "use strict";
3
+ var _a, _b;
3
4
  const _LV = globalThis.__LIGHTVIEW_INTERNALS__ || (globalThis.__LIGHTVIEW_INTERNALS__ = {
4
5
  currentEffect: null,
5
6
  registry: /* @__PURE__ */ new Map(),
6
- dependencyMap: /* @__PURE__ */ new WeakMap()
7
- // Tracking signals -> subscribers
7
+ // Global name -> Signal/Proxy
8
+ localRegistries: /* @__PURE__ */ new WeakMap(),
9
+ // Object/Element -> Map(name -> Signal/Proxy)
10
+ futureSignals: /* @__PURE__ */ new Map(),
11
+ // name -> Set of (signal) => void
12
+ schemas: /* @__PURE__ */ new Map(),
13
+ // name -> Schema (Draft 7+ or Shorthand)
14
+ parents: /* @__PURE__ */ new WeakMap(),
15
+ // Proxy -> Parent (Proxy/Element)
16
+ helpers: /* @__PURE__ */ new Map(),
17
+ // name -> function (used for transforms and expressions)
18
+ hooks: {
19
+ validate: (value, schema) => true
20
+ // Hook for extensions (like JPRX) to provide full validation
21
+ }
8
22
  });
9
- const signal = (initialValue, optionsOrName) => {
10
- let name = typeof optionsOrName === "string" ? optionsOrName : optionsOrName == null ? void 0 : optionsOrName.name;
23
+ const lookup$1 = (name, scope) => {
24
+ let current = scope;
25
+ while (current && typeof current === "object") {
26
+ const registry2 = _LV.localRegistries.get(current);
27
+ if (registry2 && registry2.has(name)) return registry2.get(name);
28
+ current = current.parentElement || _LV.parents.get(current);
29
+ }
30
+ return _LV.registry.get(name);
31
+ };
32
+ const signal$1 = (initialValue, optionsOrName) => {
33
+ const name = typeof optionsOrName === "string" ? optionsOrName : optionsOrName == null ? void 0 : optionsOrName.name;
11
34
  const storage = optionsOrName == null ? void 0 : optionsOrName.storage;
35
+ const scope = optionsOrName == null ? void 0 : optionsOrName.scope;
12
36
  if (name && storage) {
13
37
  try {
14
38
  const stored = storage.getItem(name);
15
- if (stored !== null) {
16
- initialValue = JSON.parse(stored);
17
- }
39
+ if (stored !== null) initialValue = JSON.parse(stored);
18
40
  } catch (e) {
19
41
  }
20
42
  }
21
43
  let value = initialValue;
22
44
  const subscribers = /* @__PURE__ */ new Set();
23
- const f = (...args) => {
24
- if (args.length === 0) return f.value;
25
- f.value = args[0];
26
- };
45
+ const f = (...args) => args.length === 0 ? f.value : f.value = args[0];
27
46
  Object.defineProperty(f, "value", {
28
47
  get() {
29
48
  if (_LV.currentEffect) {
@@ -46,23 +65,41 @@
46
65
  }
47
66
  });
48
67
  if (name) {
49
- if (_LV.registry.has(name)) {
50
- if (_LV.registry.get(name) !== f) {
51
- throw new Error(`Lightview: A signal or state with the name "${name}" is already registered.`);
52
- }
53
- } else {
54
- _LV.registry.set(name, f);
68
+ const registry2 = scope && typeof scope === "object" ? _LV.localRegistries.get(scope) || _LV.localRegistries.set(scope, /* @__PURE__ */ new Map()).get(scope) : _LV.registry;
69
+ if (registry2 && registry2.has(name) && registry2.get(name) !== f) {
70
+ throw new Error(`Lightview: A signal or state with the name "${name}" is already registered.`);
71
+ }
72
+ if (registry2) registry2.set(name, f);
73
+ const futures = _LV.futureSignals.get(name);
74
+ if (futures) {
75
+ futures.forEach((resolve) => resolve(f));
55
76
  }
56
77
  }
57
78
  return f;
58
79
  };
59
- const getSignal = (name, defaultValue) => {
60
- if (!_LV.registry.has(name) && defaultValue !== void 0) {
61
- return signal(defaultValue, name);
62
- }
63
- return _LV.registry.get(name);
80
+ const getSignal = (name, defaultValueOrOptions) => {
81
+ const options = typeof defaultValueOrOptions === "object" && defaultValueOrOptions !== null ? defaultValueOrOptions : { defaultValue: defaultValueOrOptions };
82
+ const { scope, defaultValue } = options;
83
+ const existing = lookup$1(name, scope);
84
+ if (existing) return existing;
85
+ if (defaultValue !== void 0) return signal$1(defaultValue, { name, scope });
86
+ const future = signal$1(void 0);
87
+ const handler = (realSignal) => {
88
+ const hasValue = realSignal && (typeof realSignal === "object" || typeof realSignal === "function") && "value" in realSignal;
89
+ if (hasValue) {
90
+ future.value = realSignal.value;
91
+ effect(() => {
92
+ future.value = realSignal.value;
93
+ });
94
+ } else {
95
+ future.value = realSignal;
96
+ }
97
+ };
98
+ if (!_LV.futureSignals.has(name)) _LV.futureSignals.set(name, /* @__PURE__ */ new Set());
99
+ _LV.futureSignals.get(name).add(handler);
100
+ return future;
64
101
  };
65
- signal.get = getSignal;
102
+ signal$1.get = getSignal;
66
103
  const effect = (fn) => {
67
104
  const execute = () => {
68
105
  if (!execute.active || execute.running) return;
@@ -89,16 +126,45 @@
89
126
  return execute;
90
127
  };
91
128
  const computed = (fn) => {
92
- const sig = signal(void 0);
129
+ const sig = signal$1(void 0);
93
130
  effect(() => {
94
131
  sig.value = fn();
95
132
  });
96
133
  return sig;
97
134
  };
98
135
  const getRegistry$1 = () => _LV.registry;
136
+ const internals = _LV;
99
137
  const stateCache = /* @__PURE__ */ new WeakMap();
100
138
  const stateSignals = /* @__PURE__ */ new WeakMap();
101
- const parents = /* @__PURE__ */ new WeakMap();
139
+ const stateSchemas = /* @__PURE__ */ new WeakMap();
140
+ const { parents, schemas, hooks } = internals;
141
+ const validate = (target, prop, value, schema) => {
142
+ var _a2, _b2;
143
+ const current = target[prop];
144
+ const type = typeof current;
145
+ const isNew = !(prop in target);
146
+ let behavior = schema;
147
+ if (typeof schema === "object" && schema !== null) behavior = schema.type;
148
+ if (behavior === "auto" && isNew) throw new Error(`Lightview: Cannot add new property "${prop}" to fixed 'auto' state.`);
149
+ if (behavior === "polymorphic" || typeof behavior === "object" && (behavior == null ? void 0 : behavior.coerce)) {
150
+ if (type === "number") return Number(value);
151
+ if (type === "boolean") return Boolean(value);
152
+ if (type === "string") return String(value);
153
+ } else if (behavior === "auto" || behavior === "dynamic") {
154
+ if (!isNew && typeof value !== type) {
155
+ throw new Error(`Lightview: Type mismatch for "${prop}". Expected ${type}, got ${typeof value}.`);
156
+ }
157
+ }
158
+ if (typeof schema === "object" && schema !== null && schema.transform) {
159
+ const trans = schema.transform;
160
+ const transformFn = typeof trans === "function" ? trans : internals.helpers.get(trans) || ((_b2 = (_a2 = globalThis.Lightview) == null ? void 0 : _a2.helpers) == null ? void 0 : _b2[trans]);
161
+ if (transformFn) value = transformFn(value);
162
+ }
163
+ if (hooks.validate(value, schema) === false) {
164
+ throw new Error(`Lightview: Validation failed for "${prop}".`);
165
+ }
166
+ return value;
167
+ };
102
168
  const protoMethods = (proto, test) => Object.getOwnPropertyNames(proto).filter((k) => typeof proto[k] === "function" && test(k));
103
169
  const DATE_TRACKING = protoMethods(Date.prototype, (k) => /^(to|get|valueOf)/.test(k));
104
170
  const DATE_MUTATING = protoMethods(Date.prototype, (k) => /^set/.test(k));
@@ -138,31 +204,33 @@
138
204
  const proxyGet = (target, prop, receiver, signals) => {
139
205
  if (prop === "__parent__") return parents.get(receiver);
140
206
  if (!signals.has(prop)) {
141
- signals.set(prop, signal(Reflect.get(target, prop, receiver)));
207
+ signals.set(prop, signal$1(Reflect.get(target, prop, receiver)));
142
208
  }
143
- const signal$1 = signals.get(prop);
144
- const val = signal$1.value;
209
+ const signal2 = signals.get(prop);
210
+ const val = signal2.value;
145
211
  if (typeof val === "object" && val !== null) {
146
- const childProxy = state(val);
212
+ const childProxy = state$1(val);
147
213
  parents.set(childProxy, receiver);
148
214
  return childProxy;
149
215
  }
150
216
  return val;
151
217
  };
152
218
  const proxySet = (target, prop, value, receiver, signals) => {
219
+ const schema = stateSchemas.get(receiver);
220
+ const validatedValue = schema ? validate(target, prop, value, schema) : value;
153
221
  if (!signals.has(prop)) {
154
- signals.set(prop, signal(Reflect.get(target, prop, receiver)));
222
+ signals.set(prop, signal$1(Reflect.get(target, prop, receiver)));
155
223
  }
156
- const success = Reflect.set(target, prop, value, receiver);
157
- const signal$1 = signals.get(prop);
158
- if (success && signal$1) signal$1.value = value;
224
+ const success = Reflect.set(target, prop, validatedValue, receiver);
225
+ const signal2 = signals.get(prop);
226
+ if (success && signal2) signal2.value = validatedValue;
159
227
  return success;
160
228
  };
161
229
  const createSpecialProxy = (obj, monitor, trackingProps = []) => {
162
230
  const signals = getOrSet(stateSignals, obj, () => /* @__PURE__ */ new Map());
163
231
  if (!signals.has(monitor)) {
164
232
  const initialValue = typeof obj[monitor] === "function" ? obj[monitor].call(obj) : obj[monitor];
165
- signals.set(monitor, signal(initialValue));
233
+ signals.set(monitor, signal$1(initialValue));
166
234
  }
167
235
  const isDate = obj instanceof Date;
168
236
  const isArray = Array.isArray(obj);
@@ -184,7 +252,7 @@
184
252
  if (isArray && ARRAY_ITERATION.includes(prop) && typeof args[0] === "function") {
185
253
  const originalCallback = args[0];
186
254
  args[0] = function(element2, index2, array) {
187
- const wrappedElement = typeof element2 === "object" && element2 !== null ? state(element2) : element2;
255
+ const wrappedElement = typeof element2 === "object" && element2 !== null ? state$1(element2) : element2;
188
256
  if (wrappedElement && typeof wrappedElement === "object") {
189
257
  parents.set(wrappedElement, receiver);
190
258
  }
@@ -225,10 +293,12 @@
225
293
  }
226
294
  });
227
295
  };
228
- const state = (obj, optionsOrName) => {
296
+ const state$1 = (obj, optionsOrName) => {
229
297
  if (typeof obj !== "object" || obj === null) return obj;
230
298
  const name = typeof optionsOrName === "string" ? optionsOrName : optionsOrName == null ? void 0 : optionsOrName.name;
231
299
  const storage = optionsOrName == null ? void 0 : optionsOrName.storage;
300
+ const scope = optionsOrName == null ? void 0 : optionsOrName.scope;
301
+ const schema = optionsOrName == null ? void 0 : optionsOrName.schema;
232
302
  if (name && storage) {
233
303
  try {
234
304
  const item = storage.getItem(name);
@@ -257,6 +327,7 @@
257
327
  stateCache.set(obj, proxy);
258
328
  } else return obj;
259
329
  }
330
+ if (schema) stateSchemas.set(proxy, schema);
260
331
  if (name && storage) {
261
332
  effect(() => {
262
333
  try {
@@ -266,25 +337,33 @@
266
337
  });
267
338
  }
268
339
  if (name) {
269
- const registry2 = getRegistry$1();
270
- if (registry2.has(name)) {
271
- if (registry2.get(name) !== proxy) {
272
- throw new Error(`Lightview: A signal or state with the name "${name}" is already registered.`);
273
- }
274
- } else {
275
- registry2.set(name, proxy);
340
+ const registry2 = scope && typeof scope === "object" ? internals.localRegistries.get(scope) || internals.localRegistries.set(scope, /* @__PURE__ */ new Map()).get(scope) : getRegistry$1();
341
+ if (registry2 && registry2.has(name) && registry2.get(name) !== proxy) {
342
+ throw new Error(`Lightview: A signal or state with the name "${name}" is already registered.`);
343
+ }
344
+ if (registry2) registry2.set(name, proxy);
345
+ const futures = internals.futureSignals.get(name);
346
+ if (futures) {
347
+ futures.forEach((resolve) => resolve(proxy));
276
348
  }
277
349
  }
278
350
  return proxy;
279
351
  };
280
- const getState = (name, defaultValue) => {
281
- const registry2 = getRegistry$1();
282
- if (!registry2.has(name) && defaultValue !== void 0) {
283
- return state(defaultValue, name);
284
- }
285
- return registry2.get(name);
352
+ const getState = (name, defaultValueOrOptions) => {
353
+ const options = typeof defaultValueOrOptions === "object" && defaultValueOrOptions !== null ? defaultValueOrOptions : { defaultValue: defaultValueOrOptions };
354
+ const { scope, defaultValue } = options;
355
+ const existing = lookup$1(name, scope);
356
+ if (existing) return existing;
357
+ if (defaultValue !== void 0) return state$1(defaultValue, { name, scope });
358
+ const future = signal$1(void 0);
359
+ const handler = (realState) => {
360
+ future.value = realState;
361
+ };
362
+ if (!internals.futureSignals.has(name)) internals.futureSignals.set(name, /* @__PURE__ */ new Set());
363
+ internals.futureSignals.get(name).add(handler);
364
+ return future;
286
365
  };
287
- state.get = getState;
366
+ state$1.get = getState;
288
367
  const core = {
289
368
  get currentEffect() {
290
369
  return (globalThis.__LIGHTVIEW_INTERNALS__ || (globalThis.__LIGHTVIEW_INTERNALS__ = {})).currentEffect;
@@ -293,6 +372,38 @@
293
372
  const nodeState = /* @__PURE__ */ new WeakMap();
294
373
  const nodeStateFactory = () => ({ effects: [], onmount: null, onunmount: null });
295
374
  const registry = getRegistry$1();
375
+ const scrollMemory = /* @__PURE__ */ new Map();
376
+ const initScrollMemory = () => {
377
+ if (typeof document === "undefined") return;
378
+ document.addEventListener("scroll", (e) => {
379
+ const el = e.target;
380
+ if (el === document || el === document.documentElement) return;
381
+ const key = el.id || el.getAttribute && el.getAttribute("data-preserve-scroll");
382
+ if (key) {
383
+ scrollMemory.set(key, { top: el.scrollTop, left: el.scrollLeft });
384
+ }
385
+ }, true);
386
+ };
387
+ if (typeof document !== "undefined") {
388
+ if (document.readyState === "loading") {
389
+ document.addEventListener("DOMContentLoaded", initScrollMemory);
390
+ } else {
391
+ initScrollMemory();
392
+ }
393
+ }
394
+ const saveScrolls = () => new Map(scrollMemory);
395
+ const restoreScrolls = (map2, root = document) => {
396
+ if (!map2 || map2.size === 0) return;
397
+ requestAnimationFrame(() => {
398
+ map2.forEach((pos, key) => {
399
+ const node = document.getElementById(key) || document.querySelector(`[data-preserve-scroll="${key}"]`);
400
+ if (node) {
401
+ node.scrollTop = pos.top;
402
+ node.scrollLeft = pos.left;
403
+ }
404
+ });
405
+ });
406
+ };
296
407
  const trackEffect = (node, effectFn) => {
297
408
  const state2 = getOrSet(nodeState, node, nodeStateFactory);
298
409
  if (!state2.effects) state2.effects = [];
@@ -490,6 +601,26 @@
490
601
  domNode.setAttribute(key, value);
491
602
  }
492
603
  reactiveAttrs[key] = value;
604
+ } else if (typeof value === "object" && value !== null && Lightview.hooks.processAttribute) {
605
+ const processed = Lightview.hooks.processAttribute(domNode, key, value);
606
+ if (processed !== void 0) {
607
+ reactiveAttrs[key] = processed;
608
+ } else if (key === "style") {
609
+ Object.entries(value).forEach(([styleKey, styleValue]) => {
610
+ if (typeof styleValue === "function") {
611
+ const runner = effect(() => {
612
+ domNode.style[styleKey] = styleValue();
613
+ });
614
+ trackEffect(domNode, runner);
615
+ } else {
616
+ domNode.style[styleKey] = styleValue;
617
+ }
618
+ });
619
+ reactiveAttrs[key] = value;
620
+ } else {
621
+ setAttributeValue(domNode, key, value);
622
+ reactiveAttrs[key] = value;
623
+ }
493
624
  } else if (typeof value === "function") {
494
625
  const runner = effect(() => {
495
626
  const result = value();
@@ -501,18 +632,6 @@
501
632
  });
502
633
  trackEffect(domNode, runner);
503
634
  reactiveAttrs[key] = value;
504
- } else if (key === "style" && typeof value === "object") {
505
- Object.entries(value).forEach(([styleKey, styleValue]) => {
506
- if (typeof styleValue === "function") {
507
- const runner = effect(() => {
508
- domNode.style[styleKey] = styleValue();
509
- });
510
- trackEffect(domNode, runner);
511
- } else {
512
- domNode.style[styleKey] = styleValue;
513
- }
514
- });
515
- reactiveAttrs[key] = value;
516
635
  } else {
517
636
  setAttributeValue(domNode, key, value);
518
637
  reactiveAttrs[key] = value;
@@ -691,8 +810,11 @@
691
810
  }
692
811
  });
693
812
  const Lightview = {
694
- signal,
695
- get: signal.get,
813
+ state: state$1,
814
+ getState,
815
+ registerSchema: (name, definition) => internals.schemas.set(name, definition),
816
+ signal: signal$1,
817
+ get: signal$1.get,
696
818
  computed,
697
819
  effect,
698
820
  registry,
@@ -705,14 +827,24 @@
705
827
  hooks: {
706
828
  onNonStandardHref: null,
707
829
  processChild: null,
708
- validateUrl: null
830
+ processAttribute: null,
831
+ validateUrl: null,
832
+ validate: (value, schema) => internals.hooks.validate(value, schema)
709
833
  },
710
834
  // Internals exposed for extensions
711
835
  internals: {
712
836
  core,
713
837
  domToElement,
714
838
  wrapDomElement,
715
- setupChildren
839
+ setupChildren,
840
+ trackEffect,
841
+ saveScrolls,
842
+ restoreScrolls,
843
+ localRegistries: internals.localRegistries,
844
+ futureSignals: internals.futureSignals,
845
+ schemas: internals.schemas,
846
+ parents: internals.parents,
847
+ hooks: internals.hooks
716
848
  }
717
849
  };
718
850
  if (typeof module !== "undefined" && module.exports) {
@@ -723,8 +855,8 @@
723
855
  globalThis.addEventListener("click", (e) => {
724
856
  const path = e.composedPath();
725
857
  const link = path.find((el) => {
726
- var _a, _b;
727
- return el.tagName === "A" && ((_b = (_a = el.getAttribute) == null ? void 0 : _a.call(el, "href")) == null ? void 0 : _b.startsWith("#"));
858
+ var _a2, _b2;
859
+ return el.tagName === "A" && ((_b2 = (_a2 = el.getAttribute) == null ? void 0 : _a2.call(el, "href")) == null ? void 0 : _b2.startsWith("#"));
728
860
  });
729
861
  if (link && !e.defaultPrevented) {
730
862
  const href = link.getAttribute("href");
@@ -749,23 +881,23 @@
749
881
  });
750
882
  if (typeof MutationObserver !== "undefined") {
751
883
  const walkNodes = (node, fn) => {
752
- var _a;
884
+ var _a2;
753
885
  fn(node);
754
- (_a = node.childNodes) == null ? void 0 : _a.forEach((n) => walkNodes(n, fn));
886
+ (_a2 = node.childNodes) == null ? void 0 : _a2.forEach((n) => walkNodes(n, fn));
755
887
  if (node.shadowRoot) walkNodes(node.shadowRoot, fn);
756
888
  };
757
889
  const cleanupNode = (node) => walkNodes(node, (n) => {
758
- var _a, _b;
890
+ var _a2, _b2;
759
891
  const s = nodeState.get(n);
760
892
  if (s) {
761
- (_a = s.effects) == null ? void 0 : _a.forEach((e) => e.stop());
762
- (_b = s.onunmount) == null ? void 0 : _b.call(s, n);
893
+ (_a2 = s.effects) == null ? void 0 : _a2.forEach((e) => e.stop());
894
+ (_b2 = s.onunmount) == null ? void 0 : _b2.call(s, n);
763
895
  nodeState.delete(n);
764
896
  }
765
897
  });
766
898
  const mountNode = (node) => walkNodes(node, (n) => {
767
- var _a, _b;
768
- (_b = (_a = nodeState.get(n)) == null ? void 0 : _a.onmount) == null ? void 0 : _b.call(_a, n);
899
+ var _a2, _b2;
900
+ (_b2 = (_a2 = nodeState.get(n)) == null ? void 0 : _a2.onmount) == null ? void 0 : _b2.call(_a2, n);
769
901
  });
770
902
  const observer = new MutationObserver((mutations) => {
771
903
  mutations.forEach((mutation) => {
@@ -819,7 +951,7 @@
819
951
  return keys.length === 1 && isValidTagName(keys[0]) && typeof obj[keys[0]] === "object";
820
952
  };
821
953
  const convertObjectDOM = (obj) => {
822
- var _a, _b;
954
+ var _a2, _b2;
823
955
  if (typeof obj !== "object" || obj === null) return obj;
824
956
  if (Array.isArray(obj)) return obj.map(convertObjectDOM);
825
957
  if (obj.tag) return { ...obj, children: obj.children ? convertObjectDOM(obj.children) : [] };
@@ -827,7 +959,7 @@
827
959
  const tagKey = Object.keys(obj)[0];
828
960
  const content = obj[tagKey];
829
961
  const LV = typeof window !== "undefined" ? globalThis.Lightview : typeof globalThis !== "undefined" ? globalThis.Lightview : null;
830
- const tag = ((_b = (_a = LV == null ? void 0 : LV.tags) == null ? void 0 : _a._customTags) == null ? void 0 : _b[tagKey]) || tagKey;
962
+ const tag = ((_b2 = (_a2 = LV == null ? void 0 : LV.tags) == null ? void 0 : _a2._customTags) == null ? void 0 : _b2[tagKey]) || tagKey;
831
963
  const { children, ...attributes } = content;
832
964
  return { tag, attributes, children: children ? convertObjectDOM(children) : [] };
833
965
  };
@@ -889,7 +1021,7 @@
889
1021
  return null;
890
1022
  }
891
1023
  };
892
- const themeSignal = signal(
1024
+ const themeSignal = signal$1(
893
1025
  typeof document !== "undefined" && document.documentElement.getAttribute("data-theme") || getSavedTheme() || "light"
894
1026
  );
895
1027
  const setTheme = (themeName) => {
@@ -1115,12 +1247,12 @@
1115
1247
  const frag = document.createDocumentFragment();
1116
1248
  frag.appendChild(createMarker(markerId, false));
1117
1249
  elements.forEach((c) => {
1118
- var _a, _b, _c;
1250
+ var _a2, _b2, _c;
1119
1251
  if (typeof c === "string") frag.appendChild(document.createTextNode(c));
1120
1252
  else if (c.domEl) frag.appendChild(c.domEl);
1121
1253
  else if (c instanceof Node) frag.appendChild(c);
1122
1254
  else {
1123
- const v = ((_c = (_a = globalThis.Lightview) == null ? void 0 : (_b = _a.hooks).processChild) == null ? void 0 : _c.call(_b, c)) || c;
1255
+ const v = ((_c = (_a2 = globalThis.Lightview) == null ? void 0 : (_b2 = _a2.hooks).processChild) == null ? void 0 : _c.call(_b2, c)) || c;
1124
1256
  if (v.tag) {
1125
1257
  const n = element2(v.tag, v.attributes || {}, v.children || []);
1126
1258
  if (n == null ? void 0 : n.domEl) frag.appendChild(n.domEl);
@@ -1137,10 +1269,10 @@
1137
1269
  };
1138
1270
  const isPath = (s) => typeof s === "string" && !isDangerousProtocol(s) && /^(https?:|\.|\/|[\w])|(\.(html|json|[vo]dom|cdomc?))$/i.test(s);
1139
1271
  const fetchContent = async (src) => {
1140
- var _a;
1272
+ var _a2;
1141
1273
  try {
1142
1274
  const LV = globalThis.Lightview;
1143
- if (((_a = LV == null ? void 0 : LV.hooks) == null ? void 0 : _a.validateUrl) && !LV.hooks.validateUrl(src)) {
1275
+ if (((_a2 = LV == null ? void 0 : LV.hooks) == null ? void 0 : _a2.validateUrl) && !LV.hooks.validateUrl(src)) {
1144
1276
  console.warn(`[LightviewX] Fetch blocked by validateUrl hook: ${src}`);
1145
1277
  return null;
1146
1278
  }
@@ -1165,10 +1297,10 @@
1165
1297
  }
1166
1298
  };
1167
1299
  const parseElements = (content, isJson, isHtml, el, element2, isCdom = false, ext = "") => {
1168
- var _a;
1300
+ var _a2;
1169
1301
  if (isJson) return Array.isArray(content) ? content : [content];
1170
1302
  if (isCdom && ext === "cdomc") {
1171
- const parser = (_a = globalThis.LightviewCDOM) == null ? void 0 : _a.parseCDOMC;
1303
+ const parser = (_a2 = globalThis.LightviewCDOM) == null ? void 0 : _a2.parseCDOMC;
1172
1304
  if (parser) {
1173
1305
  try {
1174
1306
  const obj = parser(content);
@@ -1201,11 +1333,14 @@
1201
1333
  return null;
1202
1334
  }
1203
1335
  };
1204
- const updateTargetContent = (el, elements, raw, loc, contentHash, { element: element2, setupChildren: setupChildren2 }, targetHash = null) => {
1336
+ const updateTargetContent = (el, elements, raw, loc, contentHash, options, targetHash = null) => {
1337
+ var _a2;
1338
+ const { element: element2, setupChildren: setupChildren2, saveScrolls: saveScrolls2, restoreScrolls: restoreScrolls2 } = { ...options, ...(_a2 = globalThis.Lightview) == null ? void 0 : _a2.internals };
1205
1339
  const markerId = `${loc}-${contentHash.slice(0, 8)}`;
1206
1340
  let track = getOrSet(insertedContentMap, el.domEl, () => ({}));
1207
1341
  if (track[loc]) removeInsertedContent(el.domEl, `${loc}-${track[loc].slice(0, 8)}`);
1208
1342
  track[loc] = contentHash;
1343
+ const scrollMap = saveScrolls2 ? saveScrolls2() : null;
1209
1344
  const performScroll = (root) => {
1210
1345
  if (!targetHash) return;
1211
1346
  requestAnimationFrame(() => {
@@ -1219,18 +1354,24 @@
1219
1354
  });
1220
1355
  });
1221
1356
  };
1357
+ const runRestore = (root) => {
1358
+ if (restoreScrolls2 && scrollMap) restoreScrolls2(scrollMap, root);
1359
+ };
1222
1360
  if (loc === "shadow") {
1223
1361
  if (!el.domEl.shadowRoot) el.domEl.attachShadow({ mode: "open" });
1224
1362
  setupChildren2(elements, el.domEl.shadowRoot);
1225
1363
  executeScripts(el.domEl.shadowRoot);
1226
1364
  performScroll(el.domEl.shadowRoot);
1365
+ runRestore(el.domEl.shadowRoot);
1227
1366
  } else if (loc === "innerhtml") {
1228
1367
  el.children = elements;
1229
1368
  executeScripts(el.domEl);
1230
1369
  performScroll(document);
1370
+ runRestore(el.domEl);
1231
1371
  } else {
1232
1372
  insert(elements, el.domEl, loc, markerId, { element: element2, setupChildren: setupChildren2 });
1233
1373
  performScroll(document);
1374
+ runRestore(el.domEl);
1234
1375
  }
1235
1376
  };
1236
1377
  const handleSrcAttribute = async (el, src, tagName, { element: element2, setupChildren: setupChildren2 }) => {
@@ -1263,9 +1404,9 @@
1263
1404
  if (root) {
1264
1405
  requestAnimationFrame(() => {
1265
1406
  requestAnimationFrame(() => {
1266
- var _a;
1407
+ var _a2;
1267
1408
  const id = targetHash.startsWith("#") ? targetHash.slice(1) : targetHash;
1268
- const target = root.getElementById ? root.getElementById(id) : (_a = root.querySelector) == null ? void 0 : _a.call(root, `#${id}`);
1409
+ const target = root.getElementById ? root.getElementById(id) : (_a2 = root.querySelector) == null ? void 0 : _a2.call(root, `#${id}`);
1269
1410
  if (target) {
1270
1411
  target.style.scrollMarginTop = "calc(var(--site-nav-height, 0px) + 2rem)";
1271
1412
  target.scrollIntoView({ behavior: "smooth", block: "start", inline: "start" });
@@ -1292,7 +1433,7 @@
1292
1433
  return { selector: targetStr, location: null };
1293
1434
  };
1294
1435
  const handleNonStandardHref = (e, { domToElement: domToElement2, wrapDomElement: wrapDomElement2 }) => {
1295
- var _a;
1436
+ var _a2;
1296
1437
  const clickedEl = e.target.closest("[href]");
1297
1438
  if (!clickedEl) return;
1298
1439
  const tagName = clickedEl.tagName.toLowerCase();
@@ -1300,7 +1441,7 @@
1300
1441
  e.preventDefault();
1301
1442
  const href = clickedEl.getAttribute("href");
1302
1443
  const LV = globalThis.Lightview;
1303
- if (href && (isDangerousProtocol(href) || ((_a = LV == null ? void 0 : LV.hooks) == null ? void 0 : _a.validateUrl) && !LV.hooks.validateUrl(href))) {
1444
+ if (href && (isDangerousProtocol(href) || ((_a2 = LV == null ? void 0 : LV.hooks) == null ? void 0 : _a2.validateUrl) && !LV.hooks.validateUrl(href))) {
1304
1445
  console.warn(`[LightviewX] Navigation or fetch blocked by security policy: ${href}`);
1305
1446
  return;
1306
1447
  }
@@ -1460,9 +1601,9 @@
1460
1601
  return { events, exclusions, calls };
1461
1602
  };
1462
1603
  const globalBeforeInterceptor = async (e) => {
1463
- var _a, _b;
1604
+ var _a2, _b2;
1464
1605
  if (e[BYPASS_FLAG]) return;
1465
- const target = (_b = (_a = e.target).closest) == null ? void 0 : _b.call(_a, "[lv-before]");
1606
+ const target = (_b2 = (_a2 = e.target).closest) == null ? void 0 : _b2.call(_a2, "[lv-before]");
1466
1607
  if (!target) return;
1467
1608
  const { events, exclusions, calls } = parseBeforeAttribute(target.getAttribute("lv-before"));
1468
1609
  const isExcluded = exclusions.includes(e.type);
@@ -1677,7 +1818,7 @@
1677
1818
  }
1678
1819
  }
1679
1820
  return processTemplateChild(child, {
1680
- state,
1821
+ state: state$1,
1681
1822
  signal: LV.signal
1682
1823
  });
1683
1824
  };
@@ -1858,7 +1999,7 @@
1858
1999
  }).filter(Boolean);
1859
2000
  }
1860
2001
  render() {
1861
- var _a, _b;
2002
+ var _a2, _b2;
1862
2003
  const props = { useShadow: false };
1863
2004
  for (const attr of this.attributes) {
1864
2005
  const name = attr.name.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
@@ -1882,7 +2023,7 @@
1882
2023
  const vdomChildren = this.parseChildrenToVDOM();
1883
2024
  const children = Object.keys(childElements).length > 0 ? vdomChildren : [{ tag: globalThis.Lightview.tags.slot }];
1884
2025
  const result = Component(props, ...children);
1885
- if (((_b = (_a = globalThis.Lightview) == null ? void 0 : _a.internals) == null ? void 0 : _b.setupChildren) && this.themeWrapper) {
2026
+ if (((_b2 = (_a2 = globalThis.Lightview) == null ? void 0 : _a2.internals) == null ? void 0 : _b2.setupChildren) && this.themeWrapper) {
1886
2027
  this.themeWrapper.innerHTML = "";
1887
2028
  globalThis.Lightview.internals.setupChildren([result], this.themeWrapper);
1888
2029
  }
@@ -1895,8 +2036,91 @@
1895
2036
  }
1896
2037
  };
1897
2038
  };
2039
+ const validateJSONSchema = (value, schema) => {
2040
+ var _a2;
2041
+ if (!schema) return true;
2042
+ const errors = [];
2043
+ const internals2 = (_a2 = globalThis.Lightview) == null ? void 0 : _a2.internals;
2044
+ const check = (val, sch, path = "") => {
2045
+ var _a3;
2046
+ if (!sch) return true;
2047
+ if (typeof sch === "string") {
2048
+ const registered = (_a3 = internals2 == null ? void 0 : internals2.schemas) == null ? void 0 : _a3.get(sch);
2049
+ if (registered) return check(val, registered, path);
2050
+ return true;
2051
+ }
2052
+ const type = sch.type;
2053
+ const getType = (v) => {
2054
+ if (v === null) return "null";
2055
+ if (Array.isArray(v)) return "array";
2056
+ return typeof v;
2057
+ };
2058
+ const currentType = getType(val);
2059
+ if (type && type !== currentType) {
2060
+ if (type === "integer" && Number.isInteger(val)) ;
2061
+ else if (!(type === "number" && typeof val === "number")) {
2062
+ errors.push({ path, message: `Expected type ${type}, got ${currentType}`, keyword: "type" });
2063
+ return false;
2064
+ }
2065
+ }
2066
+ if (currentType === "string") {
2067
+ if (sch.minLength !== void 0 && val.length < sch.minLength) errors.push({ path, keyword: "minLength" });
2068
+ if (sch.maxLength !== void 0 && val.length > sch.maxLength) errors.push({ path, keyword: "maxLength" });
2069
+ if (sch.pattern !== void 0 && !new RegExp(sch.pattern).test(val)) errors.push({ path, keyword: "pattern" });
2070
+ if (sch.format === "email" && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val)) errors.push({ path, keyword: "format" });
2071
+ }
2072
+ if (currentType === "number") {
2073
+ if (sch.minimum !== void 0 && val < sch.minimum) errors.push({ path, keyword: "minimum" });
2074
+ if (sch.maximum !== void 0 && val > sch.maximum) errors.push({ path, keyword: "maximum" });
2075
+ if (sch.multipleOf !== void 0 && val % sch.multipleOf !== 0) errors.push({ path, keyword: "multipleOf" });
2076
+ }
2077
+ if (currentType === "object") {
2078
+ if (sch.required && Array.isArray(sch.required)) {
2079
+ for (const key of sch.required) {
2080
+ if (!(key in val)) errors.push({ path: path ? `${path}.${key}` : key, keyword: "required" });
2081
+ }
2082
+ }
2083
+ if (sch.properties) {
2084
+ for (const key in sch.properties) {
2085
+ if (key in val) check(val[key], sch.properties[key], path ? `${path}.${key}` : key);
2086
+ }
2087
+ }
2088
+ if (sch.additionalProperties === false) {
2089
+ for (const key in val) {
2090
+ if (!sch.properties || !(key in sch.properties)) errors.push({ path: path ? `${path}.${key}` : key, keyword: "additionalProperties" });
2091
+ }
2092
+ }
2093
+ }
2094
+ if (currentType === "array") {
2095
+ if (sch.minItems !== void 0 && val.length < sch.minItems) errors.push({ path, keyword: "minItems" });
2096
+ if (sch.maxItems !== void 0 && val.length > sch.maxItems) errors.push({ path, keyword: "maxItems" });
2097
+ if (sch.uniqueItems && new Set(val).size !== val.length) errors.push({ path, keyword: "uniqueItems" });
2098
+ if (sch.items) {
2099
+ val.forEach((item, i) => check(item, sch.items, `${path}[${i}]`));
2100
+ }
2101
+ }
2102
+ if (sch.const !== void 0 && val !== sch.const) errors.push({ path, keyword: "const" });
2103
+ if (sch.enum && !sch.enum.includes(val)) errors.push({ path, keyword: "enum" });
2104
+ return errors.length === 0;
2105
+ };
2106
+ const valid = check(value, schema);
2107
+ return valid || errors;
2108
+ };
2109
+ const lvInternals = globalThis.__LIGHTVIEW_INTERNALS__ || ((_a = globalThis.Lightview) == null ? void 0 : _a.internals);
2110
+ if (lvInternals) {
2111
+ const hooks2 = lvInternals.hooks || ((_b = globalThis.Lightview) == null ? void 0 : _b.hooks);
2112
+ if (hooks2) {
2113
+ hooks2.validate = (value, schema) => {
2114
+ const result = validateJSONSchema(value, schema);
2115
+ if (result === true) return true;
2116
+ const msg = result.map((e) => `${e.path || "root"}: failed ${e.keyword}${e.message ? " (" + e.message + ")" : ""}`).join(", ");
2117
+ throw new Error(`Lightview Validation Error: ${msg}`);
2118
+ };
2119
+ }
2120
+ if (globalThis.Lightview) globalThis.Lightview.validate = validateJSONSchema;
2121
+ }
1898
2122
  const LightviewX = {
1899
- state,
2123
+ state: state$1,
1900
2124
  themeSignal,
1901
2125
  setTheme,
1902
2126
  registerStyleSheet,
@@ -1912,6 +2136,7 @@
1912
2136
  preloadComponentCSS,
1913
2137
  createCustomElement,
1914
2138
  customElementWrapper,
2139
+ validate: validateJSONSchema,
1915
2140
  internals: {
1916
2141
  handleSrcAttribute,
1917
2142
  parseElements
@@ -1959,23 +2184,26 @@
1959
2184
  };
1960
2185
  const registerHelper = (name, fn, options = {}) => {
1961
2186
  helpers.set(name, fn);
2187
+ if (globalThis.__LIGHTVIEW_INTERNALS__) {
2188
+ globalThis.__LIGHTVIEW_INTERNALS__.helpers.set(name, fn);
2189
+ }
1962
2190
  if (options) helperOptions.set(name, options);
1963
2191
  };
1964
2192
  const registerOperator = (helperName, symbol, position, precedence) => {
1965
- var _a;
2193
+ var _a2;
1966
2194
  if (!["prefix", "postfix", "infix"].includes(position)) {
1967
2195
  throw new Error(`Invalid operator position: ${position}. Must be 'prefix', 'postfix', or 'infix'.`);
1968
2196
  }
1969
2197
  if (!helpers.has(helperName)) {
1970
- (_a = globalThis.console) == null ? void 0 : _a.warn(`LightviewCDOM: Operator "${symbol}" registered for helper "${helperName}" which is not yet registered.`);
2198
+ (_a2 = globalThis.console) == null ? void 0 : _a2.warn(`LightviewCDOM: Operator "${symbol}" registered for helper "${helperName}" which is not yet registered.`);
1971
2199
  }
1972
2200
  const prec = precedence ?? DEFAULT_PRECEDENCE[position];
1973
2201
  operators[position].set(symbol, { helper: helperName, precedence: prec });
1974
2202
  };
1975
2203
  const getLV = () => globalThis.Lightview || null;
1976
2204
  const getRegistry = () => {
1977
- var _a;
1978
- return ((_a = getLV()) == null ? void 0 : _a.registry) || null;
2205
+ var _a2;
2206
+ return ((_a2 = getLV()) == null ? void 0 : _a2.registry) || null;
1979
2207
  };
1980
2208
  class BindingTarget {
1981
2209
  constructor(parent, key) {
@@ -2032,19 +2260,12 @@
2032
2260
  if (typeof path !== "string") return path;
2033
2261
  const registry2 = getRegistry();
2034
2262
  if (path === ".") return unwrapSignal(context);
2035
- if (path.startsWith("$/")) {
2263
+ if (path.startsWith("=/")) {
2036
2264
  const [rootName, ...rest] = path.slice(2).split("/");
2037
- let cur = context;
2038
- while (cur) {
2039
- const localState = cur.__state__;
2040
- if (localState && rootName in localState) {
2041
- return traverse(localState[rootName], rest);
2042
- }
2043
- cur = cur.__parent__;
2044
- }
2045
- const rootSignal = registry2 == null ? void 0 : registry2.get(rootName);
2046
- if (!rootSignal) return void 0;
2047
- return traverse(rootSignal, rest);
2265
+ const LV = getLV();
2266
+ const root = LV ? LV.get(rootName, { scope: (context == null ? void 0 : context.__node__) || context }) : registry2 == null ? void 0 : registry2.get(rootName);
2267
+ if (!root) return void 0;
2268
+ return traverse(root, rest);
2048
2269
  }
2049
2270
  if (path.startsWith("./")) {
2050
2271
  return traverse(context, path.slice(2).split("/"));
@@ -2067,20 +2288,13 @@
2067
2288
  if (typeof path !== "string") return path;
2068
2289
  const registry2 = getRegistry();
2069
2290
  if (path === ".") return context;
2070
- if (path.startsWith("$/")) {
2291
+ if (path.startsWith("=/")) {
2071
2292
  const segments = path.slice(2).split(/[/.]/);
2072
2293
  const rootName = segments.shift();
2073
- let cur = context;
2074
- while (cur) {
2075
- const localState = cur.__state__;
2076
- if (localState && rootName in localState) {
2077
- return traverseAsContext(localState[rootName], segments);
2078
- }
2079
- cur = cur.__parent__;
2080
- }
2081
- const rootSignal = registry2 == null ? void 0 : registry2.get(rootName);
2082
- if (!rootSignal) return void 0;
2083
- return traverseAsContext(rootSignal, segments);
2294
+ const LV = getLV();
2295
+ const root = LV ? LV.get(rootName, { scope: (context == null ? void 0 : context.__node__) || context }) : registry2 == null ? void 0 : registry2.get(rootName);
2296
+ if (!root) return void 0;
2297
+ return traverseAsContext(root, segments);
2084
2298
  }
2085
2299
  if (path.startsWith("./")) {
2086
2300
  return traverseAsContext(context, path.slice(2).split(/[\/.]/));
@@ -2108,6 +2322,7 @@
2108
2322
  return this.fn(context);
2109
2323
  }
2110
2324
  }
2325
+ const isNode = (val) => val && typeof val === "object" && globalThis.Node && val instanceof globalThis.Node;
2111
2326
  const resolveArgument = (arg, context, globalMode = false) => {
2112
2327
  if (arg.startsWith("'") && arg.endsWith("'") || arg.startsWith('"') && arg.endsWith('"')) {
2113
2328
  return { value: arg.slice(1, -1), isLiteral: true };
@@ -2128,9 +2343,21 @@
2128
2343
  isLazy: true
2129
2344
  };
2130
2345
  }
2346
+ if (arg === "$this" || arg.startsWith("$this/") || arg.startsWith("$this.")) {
2347
+ return {
2348
+ value: new LazyValue((context2) => {
2349
+ const node = (context2 == null ? void 0 : context2.__node__) || context2;
2350
+ if (arg === "$this") return node;
2351
+ const path = arg.startsWith("$this.") ? arg.slice(6) : arg.slice(6);
2352
+ return resolvePath(path, node);
2353
+ }),
2354
+ isLazy: true
2355
+ };
2356
+ }
2131
2357
  if (arg === "$event" || arg.startsWith("$event/") || arg.startsWith("$event.")) {
2132
2358
  return {
2133
- value: new LazyValue((event) => {
2359
+ value: new LazyValue((context2) => {
2360
+ const event = (context2 == null ? void 0 : context2.$event) || (context2 == null ? void 0 : context2.event) || context2;
2134
2361
  if (arg === "$event") return event;
2135
2362
  const path = arg.startsWith("$event.") ? arg.slice(7) : arg.slice(7);
2136
2363
  return resolvePath(path, event);
@@ -2143,11 +2370,23 @@
2143
2370
  const data = parseJPRX(arg);
2144
2371
  const resolveTemplate = (node, context2) => {
2145
2372
  if (typeof node === "string") {
2146
- if (node.startsWith("$")) {
2373
+ if (node.startsWith("=")) {
2147
2374
  const res = resolveExpression(node, context2);
2148
2375
  const final = res instanceof LazyValue ? res.resolve(context2) : res;
2149
2376
  return unwrapSignal(final);
2150
2377
  }
2378
+ if (node === "$this" || node.startsWith("$this/") || node.startsWith("$this.")) {
2379
+ const path = node.startsWith("$this.") || node.startsWith("$this/") ? node.slice(6) : node.slice(6);
2380
+ const ctxNode = (context2 == null ? void 0 : context2.__node__) || context2;
2381
+ const res = node === "$this" ? ctxNode : resolvePath(path, ctxNode);
2382
+ return unwrapSignal(res);
2383
+ }
2384
+ if (node === "$event" || node.startsWith("$event/") || node.startsWith("$event.")) {
2385
+ const path = node.startsWith("$event.") || node.startsWith("$event/") ? node.slice(7) : node.slice(7);
2386
+ const event = (context2 == null ? void 0 : context2.$event) || (context2 == null ? void 0 : context2.event) || (context2 && !isNode(context2) ? context2 : null);
2387
+ const res = node === "$event" ? event : resolvePath(path, event);
2388
+ return unwrapSignal(res);
2389
+ }
2151
2390
  if (node === "_" || node.startsWith("_/") || node.startsWith("_.")) {
2152
2391
  const path = node.startsWith("_.") || node.startsWith("_/") ? node.slice(2) : node.slice(2);
2153
2392
  const res = node === "_" ? context2 : resolvePath(path, context2);
@@ -2165,7 +2404,7 @@
2165
2404
  };
2166
2405
  const hasReactive = (obj) => {
2167
2406
  if (typeof obj === "string") {
2168
- return obj.startsWith("$") || obj.startsWith("_") || obj.startsWith("../");
2407
+ return obj.startsWith("=") || obj.startsWith("_") || obj.startsWith("../");
2169
2408
  }
2170
2409
  if (Array.isArray(obj)) return obj.some(hasReactive);
2171
2410
  if (obj && typeof obj === "object") return Object.values(obj).some(hasReactive);
@@ -2184,9 +2423,9 @@
2184
2423
  if (arg.includes("(")) {
2185
2424
  let nestedExpr = arg;
2186
2425
  if (arg.startsWith("/")) {
2187
- nestedExpr = "$" + arg;
2188
- } else if (globalMode && !arg.startsWith("$") && !arg.startsWith("./")) {
2189
- nestedExpr = `$/${arg}`;
2426
+ nestedExpr = "=" + arg;
2427
+ } else if (globalMode && !arg.startsWith("=") && !arg.startsWith("./")) {
2428
+ nestedExpr = `=/${arg}`;
2190
2429
  }
2191
2430
  const val = resolveExpression(nestedExpr, context);
2192
2431
  if (val instanceof LazyValue) {
@@ -2196,11 +2435,11 @@
2196
2435
  }
2197
2436
  let normalizedPath;
2198
2437
  if (arg.startsWith("/")) {
2199
- normalizedPath = "$" + arg;
2200
- } else if (arg.startsWith("$") || arg.startsWith("./") || arg.startsWith("../")) {
2438
+ normalizedPath = "=" + arg;
2439
+ } else if (arg.startsWith("=") || arg.startsWith("./") || arg.startsWith("../")) {
2201
2440
  normalizedPath = arg;
2202
2441
  } else if (globalMode) {
2203
- normalizedPath = `$/${arg}`;
2442
+ normalizedPath = `=/${arg}`;
2204
2443
  } else {
2205
2444
  normalizedPath = `./${arg}`;
2206
2445
  }
@@ -2245,6 +2484,8 @@
2245
2484
  // ... suffix
2246
2485
  PLACEHOLDER: "PLACEHOLDER",
2247
2486
  // _, _/path
2487
+ THIS: "THIS",
2488
+ // $this
2248
2489
  EVENT: "EVENT",
2249
2490
  // $event, $event.target
2250
2491
  EOF: "EOF"
@@ -2267,15 +2508,16 @@
2267
2508
  i++;
2268
2509
  continue;
2269
2510
  }
2270
- if (expr[i] === "$" && i + 1 < len2) {
2271
- let isOpAfter = false;
2272
- for (const op of opSymbols) {
2511
+ if (expr[i] === "=" && i + 1 < len2) {
2512
+ const prefixOps = [...operators.prefix.keys()].sort((a, b) => b.length - a.length);
2513
+ let isPrefixOp = false;
2514
+ for (const op of prefixOps) {
2273
2515
  if (expr.slice(i + 1, i + 1 + op.length) === op) {
2274
- isOpAfter = true;
2516
+ isPrefixOp = true;
2275
2517
  break;
2276
2518
  }
2277
2519
  }
2278
- if (isOpAfter) {
2520
+ if (isPrefixOp) {
2279
2521
  i++;
2280
2522
  continue;
2281
2523
  }
@@ -2311,7 +2553,7 @@
2311
2553
  continue;
2312
2554
  }
2313
2555
  const validBefore = /[\s)]/.test(before) || i === 0 || tokens.length === 0 || tokens[tokens.length - 1].type === TokenType.LPAREN || tokens[tokens.length - 1].type === TokenType.COMMA || tokens[tokens.length - 1].type === TokenType.OPERATOR;
2314
- const validAfter = /[\s($./'"0-9_]/.test(after) || i + op.length >= len2 || opSymbols.some((o) => expr.slice(i + op.length).startsWith(o));
2556
+ const validAfter = /[\s(=./'"0-9_]/.test(after) || i + op.length >= len2 || opSymbols.some((o) => expr.slice(i + op.length).startsWith(o));
2315
2557
  if (validBefore || validAfter) {
2316
2558
  matchedOp = op;
2317
2559
  break;
@@ -2367,6 +2609,16 @@
2367
2609
  tokens.push({ type: TokenType.PLACEHOLDER, value: placeholder });
2368
2610
  continue;
2369
2611
  }
2612
+ if (expr.slice(i, i + 5) === "$this") {
2613
+ let thisPath = "$this";
2614
+ i += 5;
2615
+ while (i < len2 && /[a-zA-Z0-9_./]/.test(expr[i])) {
2616
+ thisPath += expr[i];
2617
+ i++;
2618
+ }
2619
+ tokens.push({ type: TokenType.THIS, value: thisPath });
2620
+ continue;
2621
+ }
2370
2622
  if (expr.slice(i, i + 6) === "$event") {
2371
2623
  let eventPath = "$event";
2372
2624
  i += 6;
@@ -2377,7 +2629,7 @@
2377
2629
  tokens.push({ type: TokenType.EVENT, value: eventPath });
2378
2630
  continue;
2379
2631
  }
2380
- if (expr[i] === "$" || expr[i] === "." || expr[i] === "/") {
2632
+ if (expr[i] === "=" || expr[i] === "." || expr[i] === "/") {
2381
2633
  let path = "";
2382
2634
  while (i < len2) {
2383
2635
  let isOp = false;
@@ -2437,7 +2689,7 @@
2437
2689
  const hasOperatorSyntax = (expr) => {
2438
2690
  if (!expr || typeof expr !== "string") return false;
2439
2691
  if (expr.includes("(")) return false;
2440
- if (/^\$(\+\+|--|!!)\/?/.test(expr)) {
2692
+ if (/^=(\+\+|--|!!)\/?/.test(expr)) {
2441
2693
  return true;
2442
2694
  }
2443
2695
  if (/(\+\+|--)$/.test(expr)) {
@@ -2500,6 +2752,9 @@
2500
2752
  tok = this.peek();
2501
2753
  continue;
2502
2754
  }
2755
+ if (!operators.postfix.has(tok.value) && !operators.infix.has(tok.value)) {
2756
+ break;
2757
+ }
2503
2758
  this.consume();
2504
2759
  const nextTok = this.peek();
2505
2760
  if (nextTok.type === TokenType.PATH || nextTok.type === TokenType.LITERAL || nextTok.type === TokenType.LPAREN || nextTok.type === TokenType.PLACEHOLDER || nextTok.type === TokenType.EVENT || nextTok.type === TokenType.OPERATOR && operators.prefix.has(nextTok.value)) {
@@ -2537,6 +2792,10 @@
2537
2792
  this.consume();
2538
2793
  return { type: "Placeholder", value: tok.value };
2539
2794
  }
2795
+ if (tok.type === TokenType.THIS) {
2796
+ this.consume();
2797
+ return { type: "This", value: tok.value };
2798
+ }
2540
2799
  if (tok.type === TokenType.EVENT) {
2541
2800
  this.consume();
2542
2801
  return { type: "Event", value: tok.value };
@@ -2572,8 +2831,17 @@
2572
2831
  return resolvePath(path, item);
2573
2832
  });
2574
2833
  }
2834
+ case "This": {
2835
+ return new LazyValue((context2) => {
2836
+ const node = (context2 == null ? void 0 : context2.__node__) || context2;
2837
+ if (ast.value === "$this") return node;
2838
+ const path = ast.value.startsWith("$this.") ? ast.value.slice(6) : ast.value.slice(6);
2839
+ return resolvePath(path, node);
2840
+ });
2841
+ }
2575
2842
  case "Event": {
2576
- return new LazyValue((event) => {
2843
+ return new LazyValue((context2) => {
2844
+ const event = (context2 == null ? void 0 : context2.$event) || (context2 == null ? void 0 : context2.event) || context2;
2577
2845
  if (ast.value === "$event") return event;
2578
2846
  const path = ast.value.startsWith("$event.") ? ast.value.slice(7) : ast.value.slice(7);
2579
2847
  return resolvePath(path, event);
@@ -2621,7 +2889,12 @@
2621
2889
  const opts = helperOptions.get(opInfo.helper) || {};
2622
2890
  const left = evaluateAST(ast.left, context, opts.pathAware);
2623
2891
  const right = evaluateAST(ast.right, context, false);
2624
- return helper(unwrapSignal(left), unwrapSignal(right));
2892
+ const finalArgs = [];
2893
+ if (Array.isArray(left) && ast.left.type === "Explosion") finalArgs.push(...left);
2894
+ else finalArgs.push(unwrapSignal(left));
2895
+ if (Array.isArray(right) && ast.right.type === "Explosion") finalArgs.push(...right);
2896
+ else finalArgs.push(unwrapSignal(right));
2897
+ return helper(...finalArgs);
2625
2898
  }
2626
2899
  default:
2627
2900
  throw new Error(`JPRX: Unknown AST node type: ${ast.type}`);
@@ -2634,13 +2907,13 @@
2634
2907
  return evaluateAST(ast, context);
2635
2908
  };
2636
2909
  const resolveExpression = (expr, context) => {
2637
- var _a, _b;
2910
+ var _a2, _b2;
2638
2911
  if (typeof expr !== "string") return expr;
2639
2912
  if (hasOperatorSyntax(expr)) {
2640
2913
  try {
2641
2914
  return parseWithPratt(expr, context);
2642
2915
  } catch (e) {
2643
- (_a = globalThis.console) == null ? void 0 : _a.warn("JPRX: Pratt parser failed, falling back to legacy:", e.message);
2916
+ (_a2 = globalThis.console) == null ? void 0 : _a2.warn("JPRX: Pratt parser failed, falling back to legacy:", e.message);
2644
2917
  }
2645
2918
  }
2646
2919
  const funcStart = expr.indexOf("(");
@@ -2648,19 +2921,19 @@
2648
2921
  const fullPath = expr.slice(0, funcStart).trim();
2649
2922
  const argsStr = expr.slice(funcStart + 1, -1);
2650
2923
  const segments = fullPath.split("/");
2651
- let funcName = segments.pop().replace(/^\$/, "");
2924
+ let funcName = segments.pop().replace(/^=/, "");
2652
2925
  if (funcName === "" && (segments.length > 0 || fullPath === "/")) {
2653
2926
  funcName = "/";
2654
2927
  }
2655
2928
  const navPath = segments.join("/");
2656
- const isGlobalExpr = expr.startsWith("$/") || expr.startsWith("$");
2929
+ const isGlobalExpr = expr.startsWith("=/") || expr.startsWith("=");
2657
2930
  let baseContext = context;
2658
- if (navPath && navPath !== "$") {
2931
+ if (navPath && navPath !== "=") {
2659
2932
  baseContext = resolvePathAsContext(navPath, context);
2660
2933
  }
2661
2934
  const helper = helpers.get(funcName);
2662
2935
  if (!helper) {
2663
- (_b = globalThis.console) == null ? void 0 : _b.warn(`LightviewCDOM: Helper "${funcName}" not found.`);
2936
+ (_b2 = globalThis.console) == null ? void 0 : _b2.warn(`LightviewCDOM: Helper "${funcName}" not found.`);
2664
2937
  return expr;
2665
2938
  }
2666
2939
  const options = helperOptions.get(funcName) || {};
@@ -2688,7 +2961,7 @@
2688
2961
  let hasLazy = false;
2689
2962
  for (let i = 0; i < argsList.length; i++) {
2690
2963
  const arg = argsList[i];
2691
- const useGlobalMode = isGlobalExpr && (navPath === "$" || !navPath);
2964
+ const useGlobalMode = isGlobalExpr && (navPath === "=" || !navPath);
2692
2965
  const res = resolveArgument(arg, baseContext, useGlobalMode);
2693
2966
  if (res.isLazy) hasLazy = true;
2694
2967
  const shouldUnwrap = !(options.pathAware && i === 0);
@@ -2712,7 +2985,7 @@
2712
2985
  return helper(...finalArgs);
2713
2986
  });
2714
2987
  }
2715
- const result = helper(...resolvedArgs);
2988
+ const result = helper.apply((context == null ? void 0 : context.__node__) || null, resolvedArgs);
2716
2989
  return unwrapSignal(result);
2717
2990
  }
2718
2991
  return unwrapSignal(resolvePath(expr, context));
@@ -2834,7 +3107,7 @@
2834
3107
  i++;
2835
3108
  }
2836
3109
  const word = input.slice(start, i);
2837
- if (word.startsWith("$")) {
3110
+ if (word.startsWith("=")) {
2838
3111
  return word;
2839
3112
  }
2840
3113
  if (word === "true") return true;
@@ -2920,7 +3193,7 @@
2920
3193
  return res;
2921
3194
  };
2922
3195
  const parseJPRX = (input) => {
2923
- var _a, _b;
3196
+ var _a2, _b2;
2924
3197
  let result = "";
2925
3198
  let i = 0;
2926
3199
  const len2 = input.length;
@@ -2972,7 +3245,7 @@
2972
3245
  i++;
2973
3246
  continue;
2974
3247
  }
2975
- if (char === "$") {
3248
+ if (char === "=") {
2976
3249
  let expr = "";
2977
3250
  let parenDepth = 0;
2978
3251
  let braceDepth = 0;
@@ -3001,7 +3274,7 @@
3001
3274
  result += JSON.stringify(expr);
3002
3275
  continue;
3003
3276
  }
3004
- if (/[a-zA-Z_./]/.test(char)) {
3277
+ if (/[a-zA-Z_$/./]/.test(char)) {
3005
3278
  let word = "";
3006
3279
  while (i < len2 && /[a-zA-Z0-9_$/.-]/.test(input[i])) {
3007
3280
  word += input[i];
@@ -3037,8 +3310,8 @@
3037
3310
  try {
3038
3311
  return JSON.parse(result);
3039
3312
  } catch (e) {
3040
- (_a = globalThis.console) == null ? void 0 : _a.error("parseJPRX: JSON parse failed", e);
3041
- (_b = globalThis.console) == null ? void 0 : _b.error("Transformed input:", result);
3313
+ (_a2 = globalThis.console) == null ? void 0 : _a2.error("parseJPRX: JSON parse failed", e);
3314
+ (_b2 = globalThis.console) == null ? void 0 : _b2.error("Transformed input:", result);
3042
3315
  throw e;
3043
3316
  }
3044
3317
  };
@@ -3391,6 +3664,21 @@
3391
3664
  if (typeof current === "object" && current !== null) return set(target, {});
3392
3665
  return set(target, null);
3393
3666
  };
3667
+ function state(val, options) {
3668
+ if (globalThis.Lightview) {
3669
+ const finalOptions = typeof options === "string" ? { name: options } : options;
3670
+ return globalThis.Lightview.state(val, finalOptions);
3671
+ }
3672
+ throw new Error("JPRX: $state requires a UI library implementation.");
3673
+ }
3674
+ function signal(val, options) {
3675
+ if (globalThis.Lightview) {
3676
+ const finalOptions = typeof options === "string" ? { name: options } : options;
3677
+ return globalThis.Lightview.signal(val, finalOptions);
3678
+ }
3679
+ throw new Error("JPRX: $signal requires a UI library implementation.");
3680
+ }
3681
+ const bind = (path, options) => ({ __JPRX_BIND__: true, path, options });
3394
3682
  const registerStateHelpers = (register) => {
3395
3683
  const opts = { pathAware: true };
3396
3684
  register("set", set, opts);
@@ -3404,6 +3692,9 @@
3404
3692
  register("pop", pop, opts);
3405
3693
  register("assign", assign, opts);
3406
3694
  register("clear", clear, opts);
3695
+ register("state", state);
3696
+ register("signal", signal);
3697
+ register("bind", bind);
3407
3698
  };
3408
3699
  const fetchHelper = (url, options = {}) => {
3409
3700
  const fetchOptions = { ...options };
@@ -3441,6 +3732,74 @@
3441
3732
  registerStatsHelpers(registerHelper);
3442
3733
  registerStateHelpers((name, fn) => registerHelper(name, fn, { pathAware: true }));
3443
3734
  registerNetworkHelpers(registerHelper);
3735
+ registerHelper("move", (selector, location = "beforeend") => {
3736
+ return {
3737
+ isLazy: true,
3738
+ resolve: (eventOrNode) => {
3739
+ const isEvent = eventOrNode && typeof eventOrNode === "object" && "target" in eventOrNode;
3740
+ const node = isEvent ? eventOrNode.currentTarget || eventOrNode.target : eventOrNode;
3741
+ if (!(node instanceof Node) || !selector) return;
3742
+ const target = document.querySelector(selector);
3743
+ if (!target) {
3744
+ console.warn(`[Lightview-CDOM] move target not found: ${selector}`);
3745
+ return;
3746
+ }
3747
+ if (node.id) {
3748
+ const escapedId = CSS.escape(node.id);
3749
+ if (target.id === node.id && target !== node) {
3750
+ target.replaceWith(node);
3751
+ return;
3752
+ }
3753
+ const existing = target.querySelector(`#${escapedId}`);
3754
+ if (existing && existing !== node) {
3755
+ existing.replaceWith(node);
3756
+ return;
3757
+ }
3758
+ }
3759
+ globalThis.Lightview.$(target).content(node, location);
3760
+ }
3761
+ };
3762
+ }, { pathAware: true });
3763
+ registerHelper("mount", async (url, options = {}) => {
3764
+ const { target = "body", location = "beforeend" } = options;
3765
+ try {
3766
+ const fetchOptions = { ...options };
3767
+ delete fetchOptions.target;
3768
+ delete fetchOptions.location;
3769
+ const headers = { ...fetchOptions.headers };
3770
+ let body = fetchOptions.body;
3771
+ if (body !== void 0) {
3772
+ if (body !== null && typeof body === "object") {
3773
+ body = JSON.stringify(body);
3774
+ if (!headers["Content-Type"]) headers["Content-Type"] = "application/json";
3775
+ } else {
3776
+ body = String(body);
3777
+ if (!headers["Content-Type"]) headers["Content-Type"] = "text/plain";
3778
+ }
3779
+ fetchOptions.body = body;
3780
+ fetchOptions.headers = headers;
3781
+ }
3782
+ const response = await globalThis.fetch(url, fetchOptions);
3783
+ const contentType = response.headers.get("Content-Type") || "";
3784
+ const text = await response.text();
3785
+ let content = text;
3786
+ const isCDOM = contentType.includes("application/cdom") || contentType.includes("application/jprx") || contentType.includes("application/vdom") || contentType.includes("application/odom") || url.endsWith(".cdom") || url.endsWith(".jprx") || url.endsWith(".vdom") || url.endsWith(".odom");
3787
+ if (isCDOM || contentType.includes("application/json") && text.trim().startsWith("{")) {
3788
+ try {
3789
+ content = hydrate(parseJPRX(text));
3790
+ } catch (e) {
3791
+ }
3792
+ }
3793
+ const targetEl = document.querySelector(target);
3794
+ if (targetEl) {
3795
+ globalThis.Lightview.$(targetEl).content(content, location);
3796
+ } else {
3797
+ console.warn(`[Lightview-CDOM] $mount target not found: ${target}`);
3798
+ }
3799
+ } catch (err) {
3800
+ console.error(`[Lightview-CDOM] $mount failed for ${url}:`, err);
3801
+ }
3802
+ });
3444
3803
  registerOperator("increment", "++", "prefix", 80);
3445
3804
  registerOperator("increment", "++", "postfix", 80);
3446
3805
  registerOperator("decrement", "--", "prefix", 80);
@@ -3455,309 +3814,135 @@
3455
3814
  registerOperator("gte", ">=", "infix", 40);
3456
3815
  registerOperator("lte", "<=", "infix", 40);
3457
3816
  registerOperator("neq", "!=", "infix", 40);
3458
- const localStates = /* @__PURE__ */ new WeakMap();
3459
3817
  const getContext = (node, event = null) => {
3460
- const chain = [];
3461
- let cur = node;
3462
- const ShadowRoot2 = globalThis.ShadowRoot;
3463
- while (cur) {
3464
- const local = localStates.get(cur) || (cur && typeof cur === "object" ? cur.__state__ : null);
3465
- if (local) chain.unshift(local);
3466
- cur = cur.parentElement || (cur && typeof cur === "object" ? cur.__parent__ : null) || (ShadowRoot2 && cur.parentNode instanceof ShadowRoot2 ? cur.parentNode.host : null);
3467
- }
3468
- const globalRegistry = getRegistry$1();
3469
- const handler = {
3470
- get(target, prop, receiver) {
3471
- var _a;
3818
+ return new Proxy({}, {
3819
+ get(_, prop) {
3472
3820
  if (prop === "$event" || prop === "event") return event;
3473
- if (prop === "__parent__") return void 0;
3474
- for (let i = chain.length - 1; i >= 0; i--) {
3475
- const s = chain[i];
3476
- if (prop in s) return s[prop];
3477
- }
3478
- if (globalRegistry && globalRegistry.has(prop)) return unwrapSignal(globalRegistry.get(prop));
3479
- const globalState = (_a = globalThis.Lightview) == null ? void 0 : _a.state;
3480
- if (globalState && prop in globalState) return unwrapSignal(globalState[prop]);
3481
- return void 0;
3821
+ if (prop === "$this" || prop === "this" || prop === "__node__") return node;
3822
+ return unwrapSignal(globalThis.Lightview.getState(prop, { scope: node }));
3482
3823
  },
3483
- set(target, prop, value, receiver) {
3484
- var _a;
3485
- for (let i = chain.length - 1; i >= 0; i--) {
3486
- const s = chain[i];
3487
- if (prop in s) {
3488
- s[prop] = value;
3489
- return true;
3490
- }
3491
- }
3492
- if (chain.length > 0) {
3493
- chain[chain.length - 1][prop] = value;
3494
- return true;
3495
- }
3496
- const globalState = (_a = globalThis.Lightview) == null ? void 0 : _a.state;
3497
- if (globalState && prop in globalState) {
3498
- globalState[prop] = value;
3824
+ set(_, prop, value) {
3825
+ const res = globalThis.Lightview.getState(prop, { scope: node });
3826
+ if (res && (typeof res === "object" || typeof res === "function") && "value" in res) {
3827
+ res.value = value;
3499
3828
  return true;
3500
3829
  }
3501
- if (globalRegistry && globalRegistry.has(prop)) {
3502
- const s = globalRegistry.get(prop);
3503
- if (s && (typeof s === "object" || typeof s === "function") && "value" in s) {
3504
- s.value = value;
3505
- return true;
3506
- }
3507
- }
3508
3830
  return false;
3509
- },
3510
- has(target, prop) {
3511
- var _a;
3512
- const exists = prop === "$event" || prop === "event" || !!chain.find((s) => prop in s);
3513
- const inGlobal = ((_a = globalThis.Lightview) == null ? void 0 : _a.state) && prop in globalThis.Lightview.state || globalRegistry && globalRegistry.has(prop);
3514
- return exists || inGlobal;
3515
- },
3516
- ownKeys(target) {
3517
- var _a;
3518
- const keys = /* @__PURE__ */ new Set();
3519
- if (event) {
3520
- keys.add("$event");
3521
- keys.add("event");
3522
- }
3523
- for (const s of chain) {
3524
- for (const key in s) keys.add(key);
3525
- }
3526
- const globalState = (_a = globalThis.Lightview) == null ? void 0 : _a.state;
3527
- if (globalState) {
3528
- for (const key in globalState) keys.add(key);
3529
- }
3530
- return Array.from(keys);
3531
- },
3532
- getOwnPropertyDescriptor(target, prop) {
3533
- return { enumerable: true, configurable: true };
3534
3831
  }
3535
- };
3536
- return new Proxy({}, handler);
3537
- };
3538
- const handleCDOMState = (node) => {
3539
- var _a;
3540
- const attr = node["cdom-state"] || node.getAttribute && node.getAttribute("cdom-state");
3541
- if (!attr || localStates.has(node)) return;
3542
- try {
3543
- const data = typeof attr === "object" ? attr : JSON.parse(attr);
3544
- const s = state(data);
3545
- localStates.set(node, s);
3546
- if (node && typeof node === "object") {
3547
- node.__state__ = s;
3548
- }
3549
- } catch (e) {
3550
- (_a = globalThis.console) == null ? void 0 : _a.error("LightviewCDOM: Failed to parse cdom-state", e);
3551
- }
3832
+ });
3552
3833
  };
3553
- const handleCDOMBind = (node) => {
3554
- const path = node["cdom-bind"] || node.getAttribute("cdom-bind");
3555
- if (!path) return;
3556
- const type = node.type || "";
3557
- const tagName = node.tagName.toLowerCase();
3558
- let prop = "value";
3559
- let event = "input";
3560
- if (type === "checkbox" || type === "radio") {
3561
- prop = "checked";
3562
- event = "change";
3563
- } else if (tagName === "select") {
3564
- event = "change";
3565
- }
3566
- const context = getContext(node);
3567
- let target = resolvePathAsContext(path, context);
3568
- if (target && target.isBindingTarget && target.value === void 0) {
3569
- const val = node[prop];
3570
- if (val !== void 0 && val !== "") {
3571
- set(context, { [target.key]: val });
3572
- target = resolvePathAsContext(path, context);
3573
- }
3834
+ globalThis.Lightview.hooks.processAttribute = (domNode, key, value) => {
3835
+ if (value == null ? void 0 : value.__JPRX_BIND__) {
3836
+ const { path, options } = value;
3837
+ const type = domNode.type || "";
3838
+ const tagName = domNode.tagName.toLowerCase();
3839
+ let prop = "value";
3840
+ let event = "input";
3841
+ if (type === "checkbox" || type === "radio") {
3842
+ prop = "checked";
3843
+ event = "change";
3844
+ } else if (tagName === "select") {
3845
+ event = "change";
3846
+ }
3847
+ const res = globalThis.Lightview.get(path.replace(/^=/, ""), { scope: domNode });
3848
+ const runner = globalThis.Lightview.effect(() => {
3849
+ const val = unwrapSignal(res);
3850
+ if (domNode[prop] !== val) {
3851
+ domNode[prop] = val === void 0 ? "" : val;
3852
+ }
3853
+ });
3854
+ globalThis.Lightview.internals.trackEffect(domNode, runner);
3855
+ domNode.addEventListener(event, () => {
3856
+ if (res && "value" in res) res.value = domNode[prop];
3857
+ });
3858
+ return unwrapSignal(res) ?? domNode[prop];
3574
3859
  }
3575
- effect(() => {
3576
- const val = unwrapSignal(target);
3577
- if (node[prop] !== val) {
3578
- node[prop] = val === void 0 ? "" : val;
3579
- }
3580
- });
3581
- node.addEventListener(event, () => {
3582
- const val = node[prop];
3583
- if (target && target.isBindingTarget) {
3584
- target.value = val;
3585
- } else {
3586
- set(context, { [path]: val });
3587
- }
3588
- });
3860
+ return void 0;
3589
3861
  };
3590
3862
  const activate = (root = document.body) => {
3591
- const walk = (node) => {
3592
- if (node.nodeType === 1) {
3593
- if (node.hasAttribute("cdom-state")) handleCDOMState(node);
3594
- if (node.hasAttribute("cdom-bind")) handleCDOMBind(node);
3595
- }
3596
- let child = node.firstChild;
3597
- while (child) {
3598
- walk(child);
3599
- child = child.nextSibling;
3600
- }
3601
- };
3602
- walk(root);
3863
+ };
3864
+ const makeEventHandler = (expr) => (eventOrNode) => {
3865
+ const isEvent = eventOrNode && typeof eventOrNode === "object" && "target" in eventOrNode;
3866
+ const target = isEvent ? eventOrNode.currentTarget || eventOrNode.target : eventOrNode;
3867
+ const context = getContext(target, isEvent ? eventOrNode : null);
3868
+ const result = resolveExpression(expr, context);
3869
+ if (result && typeof result === "object" && result.isLazy) return result.resolve(eventOrNode);
3870
+ return result;
3603
3871
  };
3604
3872
  const hydrate = (node, parent = null) => {
3873
+ var _a2, _b2, _c;
3605
3874
  if (!node) return node;
3606
- if (typeof node === "string" && node.startsWith("$")) {
3875
+ if (typeof node === "string" && node.startsWith("'=")) {
3876
+ return node.slice(1);
3877
+ }
3878
+ if (typeof node === "string" && node.startsWith("=")) {
3607
3879
  return parseExpression(node, parent);
3608
3880
  }
3881
+ if (typeof node !== "object") return node;
3609
3882
  if (Array.isArray(node)) {
3610
3883
  return node.map((item) => hydrate(item, parent));
3611
3884
  }
3612
- if (node instanceof String) {
3613
- return node.toString();
3885
+ if (node instanceof String) return node.toString();
3886
+ if (parent && !("__parent__" in node)) {
3887
+ Object.defineProperty(node, "__parent__", { value: parent, enumerable: false, writable: true });
3888
+ (_c = (_b2 = (_a2 = globalThis.Lightview) == null ? void 0 : _a2.internals) == null ? void 0 : _b2.parents) == null ? void 0 : _c.set(node, parent);
3614
3889
  }
3615
- if (typeof node === "object" && node !== null) {
3616
- if (parent && !("__parent__" in node)) {
3617
- Object.defineProperty(node, "__parent__", {
3618
- value: parent,
3619
- enumerable: false,
3620
- writable: true,
3621
- configurable: true
3622
- });
3890
+ if (!node.tag) {
3891
+ let potentialTag = null;
3892
+ const reserved = ["children", "attributes", "tag", "__parent__"];
3893
+ for (const key in node) {
3894
+ if (reserved.includes(key) || key.startsWith("on")) continue;
3895
+ potentialTag = key;
3896
+ break;
3623
3897
  }
3624
- if (!node.tag) {
3625
- let potentialTag = null;
3626
- for (const key in node) {
3627
- if (key === "children" || key === "attributes" || key === "tag" || key.startsWith("cdom-") || key.startsWith("on") || key === "__parent__") {
3628
- continue;
3629
- }
3630
- const attrNames = [
3631
- // Form/input attributes
3632
- "type",
3633
- "name",
3634
- "value",
3635
- "placeholder",
3636
- "step",
3637
- "min",
3638
- "max",
3639
- "pattern",
3640
- "disabled",
3641
- "checked",
3642
- "selected",
3643
- "readonly",
3644
- "required",
3645
- "multiple",
3646
- "rows",
3647
- "cols",
3648
- "size",
3649
- "maxlength",
3650
- "minlength",
3651
- "autocomplete",
3652
- // Common element attributes
3653
- "id",
3654
- "class",
3655
- "className",
3656
- "style",
3657
- "title",
3658
- "tabindex",
3659
- "role",
3660
- "href",
3661
- "src",
3662
- "alt",
3663
- "width",
3664
- "height",
3665
- "target",
3666
- "rel",
3667
- // Data attributes
3668
- "data",
3669
- "label",
3670
- "text",
3671
- "description",
3672
- "content",
3673
- // Common data property names
3674
- "price",
3675
- "qty",
3676
- "items",
3677
- "count",
3678
- "total",
3679
- "amount",
3680
- "url"
3681
- ];
3682
- if (attrNames.includes(key)) {
3683
- continue;
3898
+ if (potentialTag) {
3899
+ const content = node[potentialTag];
3900
+ node.tag = potentialTag;
3901
+ if (Array.isArray(content)) {
3902
+ node.children = content;
3903
+ } else if (typeof content === "object") {
3904
+ node.attributes = node.attributes || {};
3905
+ for (const k in content) {
3906
+ if (k === "children") node.children = content[k];
3907
+ else node.attributes[k] = content[k];
3684
3908
  }
3685
- potentialTag = key;
3686
- break;
3687
- }
3688
- if (potentialTag) {
3689
- const content = node[potentialTag];
3690
- if (content !== void 0 && content !== null) {
3691
- node.tag = potentialTag;
3692
- if (Array.isArray(content)) {
3693
- node.children = content;
3694
- } else if (typeof content === "object") {
3695
- node.attributes = node.attributes || {};
3696
- for (const k in content) {
3697
- if (k === "children") {
3698
- node.children = content[k];
3699
- } else if (k.startsWith("cdom-")) {
3700
- node[k] = content[k];
3701
- } else {
3702
- node.attributes[k] = content[k];
3703
- }
3704
- }
3909
+ } else node.children = [content];
3910
+ delete node[potentialTag];
3911
+ }
3912
+ }
3913
+ for (const key in node) {
3914
+ if (key === "tag" || key === "__parent__") continue;
3915
+ const value = node[key];
3916
+ if (key === "attributes" && typeof value === "object" && value !== null) {
3917
+ for (const attrKey in value) {
3918
+ const attrVal = value[attrKey];
3919
+ if (typeof attrVal === "string" && attrVal.startsWith("'=")) {
3920
+ value[attrKey] = attrVal.slice(1);
3921
+ } else if (typeof attrVal === "string" && attrVal.startsWith("=")) {
3922
+ if (attrKey.startsWith("on")) {
3923
+ value[attrKey] = makeEventHandler(attrVal);
3705
3924
  } else {
3706
- node.children = [content];
3925
+ value[attrKey] = parseExpression(attrVal, node);
3707
3926
  }
3708
- delete node[potentialTag];
3927
+ } else if (typeof attrVal === "object" && attrVal !== null) {
3928
+ value[attrKey] = hydrate(attrVal, node);
3709
3929
  }
3710
3930
  }
3931
+ continue;
3711
3932
  }
3712
- if (node["cdom-state"]) {
3713
- handleCDOMState(node);
3714
- }
3715
- for (const key in node) {
3716
- const value = node[key];
3717
- if (key === "cdom-state") {
3718
- continue;
3719
- }
3720
- if (typeof value === "string" && value.startsWith("$")) {
3721
- if (key.startsWith("on")) {
3722
- node[key] = (event) => {
3723
- const element2 = event.currentTarget;
3724
- const context = getContext(element2, event);
3725
- const result = resolveExpression(value, context);
3726
- if (result && typeof result === "object" && result.isLazy && typeof result.resolve === "function") {
3727
- return result.resolve(event);
3728
- }
3729
- return result;
3730
- };
3731
- } else if (key === "children") {
3732
- node[key] = [parseExpression(value, node)];
3733
- } else {
3734
- node[key] = parseExpression(value, node);
3735
- }
3736
- } else if (key === "attributes" && typeof value === "object" && value !== null) {
3737
- for (const attrKey in value) {
3738
- const attrValue = value[attrKey];
3739
- if (typeof attrValue === "string" && attrValue.startsWith("$")) {
3740
- if (attrKey.startsWith("on")) {
3741
- value[attrKey] = (event) => {
3742
- const element2 = event.currentTarget;
3743
- const context = getContext(element2, event);
3744
- const result = resolveExpression(attrValue, context);
3745
- if (result && typeof result === "object" && result.isLazy && typeof result.resolve === "function") {
3746
- return result.resolve(event);
3747
- }
3748
- return result;
3749
- };
3750
- } else {
3751
- value[attrKey] = parseExpression(attrValue, node);
3752
- }
3753
- }
3754
- }
3755
- node[key] = value;
3933
+ if (typeof value === "string" && value.startsWith("'=")) {
3934
+ node[key] = value.slice(1);
3935
+ } else if (typeof value === "string" && value.startsWith("=")) {
3936
+ if (key === "onmount" || key === "onunmount" || key.startsWith("on")) {
3937
+ node[key] = makeEventHandler(value);
3938
+ } else if (key === "children") {
3939
+ node[key] = [parseExpression(value, node)];
3756
3940
  } else {
3757
- node[key] = hydrate(value, node);
3941
+ node[key] = parseExpression(value, node);
3758
3942
  }
3943
+ } else {
3944
+ node[key] = hydrate(value, node);
3759
3945
  }
3760
- return node;
3761
3946
  }
3762
3947
  return node;
3763
3948
  };
@@ -3772,8 +3957,10 @@
3772
3957
  parseJPRX,
3773
3958
  unwrapSignal,
3774
3959
  getContext,
3775
- handleCDOMState,
3776
- handleCDOMBind,
3960
+ handleCDOMState: () => {
3961
+ },
3962
+ handleCDOMBind: () => {
3963
+ },
3777
3964
  activate,
3778
3965
  hydrate,
3779
3966
  version: "1.0.0"