elit 3.5.7 → 3.5.8
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/Cargo.toml +1 -1
- package/README.md +1 -1
- package/dist/build.d.ts +1 -1
- package/dist/cli.cjs +16 -2
- package/dist/cli.mjs +16 -2
- package/dist/config.d.ts +1 -1
- package/dist/coverage.d.ts +1 -1
- package/dist/desktop-auto-render.cjs +2370 -0
- package/dist/desktop-auto-render.d.ts +13 -0
- package/dist/desktop-auto-render.js +2341 -0
- package/dist/desktop-auto-render.mjs +2344 -0
- package/dist/render-context.cjs +118 -0
- package/dist/render-context.d.ts +39 -0
- package/dist/render-context.js +77 -0
- package/dist/render-context.mjs +87 -0
- package/dist/{server-CNgDUgSZ.d.ts → server-FCdUqabc.d.ts} +1 -1
- package/dist/server.d.ts +1 -1
- package/package.json +21 -1
|
@@ -0,0 +1,2341 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
(() => {
|
|
3
|
+
// src/render-context.ts
|
|
4
|
+
var RUNTIME_TARGET_KEY = "__ELIT_RUNTIME_TARGET__";
|
|
5
|
+
var CAPTURED_RENDER_KEY = "__ELIT_CAPTURED_RENDER__";
|
|
6
|
+
var DESKTOP_RENDER_OPTIONS_KEY = "__ELIT_DESKTOP_RENDER_OPTIONS__";
|
|
7
|
+
var RUNTIME_TARGET_ENV = "ELIT_RUNTIME_TARGET";
|
|
8
|
+
function getGlobalRenderScope() {
|
|
9
|
+
return globalThis;
|
|
10
|
+
}
|
|
11
|
+
function isRenderRuntimeTarget(value) {
|
|
12
|
+
return value === "web" || value === "desktop" || value === "mobile" || value === "unknown";
|
|
13
|
+
}
|
|
14
|
+
function detectRenderRuntimeTarget() {
|
|
15
|
+
const globalScope = getGlobalRenderScope();
|
|
16
|
+
const explicitTarget = globalScope[RUNTIME_TARGET_KEY] ?? globalScope.process?.env?.[RUNTIME_TARGET_ENV];
|
|
17
|
+
if (isRenderRuntimeTarget(explicitTarget)) {
|
|
18
|
+
return explicitTarget;
|
|
19
|
+
}
|
|
20
|
+
if (typeof globalScope.document !== "undefined" && typeof globalScope.window !== "undefined") {
|
|
21
|
+
return "web";
|
|
22
|
+
}
|
|
23
|
+
if (typeof globalScope.createWindow === "function") {
|
|
24
|
+
return "desktop";
|
|
25
|
+
}
|
|
26
|
+
const argv = Array.isArray(globalScope.process?.argv) ? globalScope.process.argv.join(" ") : "";
|
|
27
|
+
if (/\bdesktop\b/i.test(argv)) {
|
|
28
|
+
return "desktop";
|
|
29
|
+
}
|
|
30
|
+
if (/\b(mobile|native)\b/i.test(argv)) {
|
|
31
|
+
return "mobile";
|
|
32
|
+
}
|
|
33
|
+
return "unknown";
|
|
34
|
+
}
|
|
35
|
+
function captureRenderedVNode(rootElement, vNode, target = detectRenderRuntimeTarget()) {
|
|
36
|
+
const globalScope = getGlobalRenderScope();
|
|
37
|
+
globalScope[RUNTIME_TARGET_KEY] = target;
|
|
38
|
+
globalScope[CAPTURED_RENDER_KEY] = {
|
|
39
|
+
rootElement,
|
|
40
|
+
target,
|
|
41
|
+
vNode
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function getCapturedRenderedVNode() {
|
|
45
|
+
return getGlobalRenderScope()[CAPTURED_RENDER_KEY];
|
|
46
|
+
}
|
|
47
|
+
function clearCapturedRenderedVNode() {
|
|
48
|
+
delete getGlobalRenderScope()[CAPTURED_RENDER_KEY];
|
|
49
|
+
}
|
|
50
|
+
function getDesktopRenderOptions() {
|
|
51
|
+
return getGlobalRenderScope()[DESKTOP_RENDER_OPTIONS_KEY];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/dom.ts
|
|
55
|
+
function resolveElement(rootElement) {
|
|
56
|
+
return typeof rootElement === "string" ? document.getElementById(rootElement.replace("#", "")) : rootElement;
|
|
57
|
+
}
|
|
58
|
+
function ensureElement(el, rootElement) {
|
|
59
|
+
if (!el) {
|
|
60
|
+
throw new Error(`Element not found: ${rootElement}`);
|
|
61
|
+
}
|
|
62
|
+
return el;
|
|
63
|
+
}
|
|
64
|
+
function shouldSkipChild(child) {
|
|
65
|
+
return child == null || child === false;
|
|
66
|
+
}
|
|
67
|
+
function isPrimitiveJson(json) {
|
|
68
|
+
return json == null || typeof json === "boolean" || typeof json === "string" || typeof json === "number";
|
|
69
|
+
}
|
|
70
|
+
function normalizeFormControlValue(value) {
|
|
71
|
+
if (Array.isArray(value)) {
|
|
72
|
+
return value.map((entry) => String(entry)).join(",");
|
|
73
|
+
}
|
|
74
|
+
return value == null ? "" : String(value);
|
|
75
|
+
}
|
|
76
|
+
function resolveTextareaValue(tagName, props) {
|
|
77
|
+
return tagName === "textarea" && props.value != null ? normalizeFormControlValue(props.value) : void 0;
|
|
78
|
+
}
|
|
79
|
+
function hasDocumentApi() {
|
|
80
|
+
return typeof document !== "undefined";
|
|
81
|
+
}
|
|
82
|
+
var DomNode = class {
|
|
83
|
+
constructor() {
|
|
84
|
+
this.elementCache = /* @__PURE__ */ new WeakMap();
|
|
85
|
+
this.reactiveNodes = /* @__PURE__ */ new Map();
|
|
86
|
+
}
|
|
87
|
+
createElement(tagName, props = {}, children = []) {
|
|
88
|
+
return { tagName, props, children };
|
|
89
|
+
}
|
|
90
|
+
renderToDOM(vNode, parent) {
|
|
91
|
+
if (vNode == null || vNode === false) return;
|
|
92
|
+
if (typeof vNode !== "object") {
|
|
93
|
+
parent.appendChild(document.createTextNode(String(vNode)));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (this.isState(vNode)) {
|
|
97
|
+
const textNode = document.createTextNode(String(vNode.value ?? ""));
|
|
98
|
+
parent.appendChild(textNode);
|
|
99
|
+
vNode.subscribe((newValue) => {
|
|
100
|
+
textNode.textContent = String(newValue ?? "");
|
|
101
|
+
});
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (Array.isArray(vNode)) {
|
|
105
|
+
for (const child of vNode) {
|
|
106
|
+
this.renderToDOM(child, parent);
|
|
107
|
+
}
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const { tagName, props, children } = vNode;
|
|
111
|
+
const textareaValue = resolveTextareaValue(tagName, props);
|
|
112
|
+
if (!tagName) {
|
|
113
|
+
for (const child of children) {
|
|
114
|
+
if (shouldSkipChild(child)) continue;
|
|
115
|
+
if (Array.isArray(child)) {
|
|
116
|
+
for (const c of child) {
|
|
117
|
+
!shouldSkipChild(c) && this.renderToDOM(c, parent);
|
|
118
|
+
}
|
|
119
|
+
} else {
|
|
120
|
+
this.renderToDOM(child, parent);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const isSVG = tagName === "svg" || tagName[0] === "s" && tagName[1] === "v" && tagName[2] === "g" || parent.namespaceURI === "http://www.w3.org/2000/svg";
|
|
126
|
+
const el = isSVG ? document.createElementNS("http://www.w3.org/2000/svg", tagName.replace("svg", "").toLowerCase() || tagName) : document.createElement(tagName);
|
|
127
|
+
for (const key in props) {
|
|
128
|
+
const value = props[key];
|
|
129
|
+
if (value == null || value === false) continue;
|
|
130
|
+
const c = key.charCodeAt(0);
|
|
131
|
+
if (c === 99 && (key.length < 6 || key[5] === "N")) {
|
|
132
|
+
const classValue = Array.isArray(value) ? value.join(" ") : value;
|
|
133
|
+
isSVG ? el.setAttribute("class", classValue) : el.className = classValue;
|
|
134
|
+
} else if (c === 115 && key.length === 5) {
|
|
135
|
+
if (typeof value === "string") {
|
|
136
|
+
el.style.cssText = value;
|
|
137
|
+
} else {
|
|
138
|
+
const s = el.style;
|
|
139
|
+
for (const k in value) s[k] = value[k];
|
|
140
|
+
}
|
|
141
|
+
} else if (c === 111 && key.charCodeAt(1) === 110) {
|
|
142
|
+
el[key.toLowerCase()] = value;
|
|
143
|
+
} else if (c === 100 && key.length > 20) {
|
|
144
|
+
el.innerHTML = value.__html;
|
|
145
|
+
} else if (c === 114 && key === "ref") {
|
|
146
|
+
setTimeout(() => {
|
|
147
|
+
typeof value === "function" ? value(el) : value.current = el;
|
|
148
|
+
}, 0);
|
|
149
|
+
} else if (textareaValue !== void 0 && key === "value") {
|
|
150
|
+
continue;
|
|
151
|
+
} else {
|
|
152
|
+
el.setAttribute(key, value === true ? "" : String(value));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const renderableChildren = textareaValue === void 0 ? children : [];
|
|
156
|
+
const len = renderableChildren.length;
|
|
157
|
+
if (!len) {
|
|
158
|
+
if (textareaValue !== void 0) {
|
|
159
|
+
el.value = textareaValue;
|
|
160
|
+
}
|
|
161
|
+
parent.appendChild(el);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const renderChildren = (target) => {
|
|
165
|
+
for (let i = 0; i < len; i++) {
|
|
166
|
+
const child = renderableChildren[i];
|
|
167
|
+
if (shouldSkipChild(child)) continue;
|
|
168
|
+
if (Array.isArray(child)) {
|
|
169
|
+
for (let j = 0, cLen = child.length; j < cLen; j++) {
|
|
170
|
+
const c = child[j];
|
|
171
|
+
!shouldSkipChild(c) && this.renderToDOM(c, target);
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
this.renderToDOM(child, target);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
if (len > 30) {
|
|
179
|
+
const fragment = document.createDocumentFragment();
|
|
180
|
+
renderChildren(fragment);
|
|
181
|
+
el.appendChild(fragment);
|
|
182
|
+
} else {
|
|
183
|
+
renderChildren(el);
|
|
184
|
+
}
|
|
185
|
+
parent.appendChild(el);
|
|
186
|
+
}
|
|
187
|
+
render(rootElement, vNode) {
|
|
188
|
+
if (!hasDocumentApi()) {
|
|
189
|
+
const runtimeTarget = detectRenderRuntimeTarget();
|
|
190
|
+
if (runtimeTarget === "desktop" || runtimeTarget === "mobile") {
|
|
191
|
+
captureRenderedVNode(rootElement, vNode, runtimeTarget);
|
|
192
|
+
return {};
|
|
193
|
+
}
|
|
194
|
+
throw new Error("render() requires a DOM or an Elit desktop/mobile runtime target.");
|
|
195
|
+
}
|
|
196
|
+
const el = ensureElement(resolveElement(rootElement), rootElement);
|
|
197
|
+
el.innerHTML = "";
|
|
198
|
+
if (vNode.children && vNode.children.length > 500) {
|
|
199
|
+
const fragment = document.createDocumentFragment();
|
|
200
|
+
this.renderToDOM(vNode, fragment);
|
|
201
|
+
el.appendChild(fragment);
|
|
202
|
+
} else {
|
|
203
|
+
this.renderToDOM(vNode, el);
|
|
204
|
+
}
|
|
205
|
+
return el;
|
|
206
|
+
}
|
|
207
|
+
batchRender(rootElement, vNodes) {
|
|
208
|
+
const el = ensureElement(resolveElement(rootElement), rootElement);
|
|
209
|
+
const len = vNodes.length;
|
|
210
|
+
if (len > 3e3) {
|
|
211
|
+
const fragment = document.createDocumentFragment();
|
|
212
|
+
let processed = 0;
|
|
213
|
+
const chunkSize = 1500;
|
|
214
|
+
const processChunk = () => {
|
|
215
|
+
const end = Math.min(processed + chunkSize, len);
|
|
216
|
+
for (let i = processed; i < end; i++) {
|
|
217
|
+
this.renderToDOM(vNodes[i], fragment);
|
|
218
|
+
}
|
|
219
|
+
processed = end;
|
|
220
|
+
if (processed >= len) {
|
|
221
|
+
el.appendChild(fragment);
|
|
222
|
+
} else {
|
|
223
|
+
requestAnimationFrame(processChunk);
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
processChunk();
|
|
227
|
+
} else {
|
|
228
|
+
const fragment = document.createDocumentFragment();
|
|
229
|
+
for (let i = 0; i < len; i++) {
|
|
230
|
+
this.renderToDOM(vNodes[i], fragment);
|
|
231
|
+
}
|
|
232
|
+
el.appendChild(fragment);
|
|
233
|
+
}
|
|
234
|
+
return el;
|
|
235
|
+
}
|
|
236
|
+
renderChunked(rootElement, vNodes, chunkSize = 5e3, onProgress) {
|
|
237
|
+
const el = ensureElement(resolveElement(rootElement), rootElement);
|
|
238
|
+
const len = vNodes.length;
|
|
239
|
+
let index = 0;
|
|
240
|
+
const renderChunk = () => {
|
|
241
|
+
const end = Math.min(index + chunkSize, len);
|
|
242
|
+
const fragment = document.createDocumentFragment();
|
|
243
|
+
for (let i = index; i < end; i++) {
|
|
244
|
+
this.renderToDOM(vNodes[i], fragment);
|
|
245
|
+
}
|
|
246
|
+
el.appendChild(fragment);
|
|
247
|
+
index = end;
|
|
248
|
+
if (onProgress) onProgress(index, len);
|
|
249
|
+
if (index < len) {
|
|
250
|
+
requestAnimationFrame(renderChunk);
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
requestAnimationFrame(renderChunk);
|
|
254
|
+
return el;
|
|
255
|
+
}
|
|
256
|
+
renderToHead(...vNodes) {
|
|
257
|
+
const head = document.head;
|
|
258
|
+
if (head) {
|
|
259
|
+
for (const vNode of vNodes.flat()) {
|
|
260
|
+
vNode && this.renderToDOM(vNode, head);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return head;
|
|
264
|
+
}
|
|
265
|
+
addStyle(cssText) {
|
|
266
|
+
const el = document.createElement("style");
|
|
267
|
+
el.textContent = cssText;
|
|
268
|
+
return document.head.appendChild(el);
|
|
269
|
+
}
|
|
270
|
+
addMeta(attrs) {
|
|
271
|
+
const el = document.createElement("meta");
|
|
272
|
+
for (const k in attrs) el.setAttribute(k, attrs[k]);
|
|
273
|
+
return document.head.appendChild(el);
|
|
274
|
+
}
|
|
275
|
+
addLink(attrs) {
|
|
276
|
+
const el = document.createElement("link");
|
|
277
|
+
for (const k in attrs) el.setAttribute(k, attrs[k]);
|
|
278
|
+
return document.head.appendChild(el);
|
|
279
|
+
}
|
|
280
|
+
setTitle(text) {
|
|
281
|
+
return document.title = text;
|
|
282
|
+
}
|
|
283
|
+
// Reactive State Management
|
|
284
|
+
createState(initialValue, options = {}) {
|
|
285
|
+
let value = initialValue;
|
|
286
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
287
|
+
let updateTimer = null;
|
|
288
|
+
const { throttle = 0, deep = false } = options;
|
|
289
|
+
const notify = () => listeners.forEach((fn) => fn(value));
|
|
290
|
+
const scheduleUpdate = () => {
|
|
291
|
+
if (throttle > 0) {
|
|
292
|
+
if (!updateTimer) {
|
|
293
|
+
updateTimer = setTimeout(() => {
|
|
294
|
+
updateTimer = null;
|
|
295
|
+
notify();
|
|
296
|
+
}, throttle);
|
|
297
|
+
}
|
|
298
|
+
} else {
|
|
299
|
+
notify();
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
return {
|
|
303
|
+
get value() {
|
|
304
|
+
return value;
|
|
305
|
+
},
|
|
306
|
+
set value(newValue) {
|
|
307
|
+
const changed = deep ? JSON.stringify(value) !== JSON.stringify(newValue) : value !== newValue;
|
|
308
|
+
if (changed) {
|
|
309
|
+
value = newValue;
|
|
310
|
+
scheduleUpdate();
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
subscribe(fn) {
|
|
314
|
+
listeners.add(fn);
|
|
315
|
+
return () => listeners.delete(fn);
|
|
316
|
+
},
|
|
317
|
+
destroy() {
|
|
318
|
+
listeners.clear();
|
|
319
|
+
updateTimer && clearTimeout(updateTimer);
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
computed(states, computeFn) {
|
|
324
|
+
const values = states.map((s) => s.value);
|
|
325
|
+
const result = this.createState(computeFn(...values));
|
|
326
|
+
states.forEach((state, index) => {
|
|
327
|
+
state.subscribe((newValue) => {
|
|
328
|
+
values[index] = newValue;
|
|
329
|
+
result.value = computeFn(...values);
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
return result;
|
|
333
|
+
}
|
|
334
|
+
effect(stateFn) {
|
|
335
|
+
stateFn();
|
|
336
|
+
}
|
|
337
|
+
// Virtual scrolling helper for large lists
|
|
338
|
+
createVirtualList(container2, items, renderItem, itemHeight = 50, bufferSize = 5) {
|
|
339
|
+
const viewportHeight = container2.clientHeight;
|
|
340
|
+
const totalHeight = items.length * itemHeight;
|
|
341
|
+
let scrollTop = 0;
|
|
342
|
+
const getVisibleRange = () => {
|
|
343
|
+
const start = Math.max(0, Math.floor(scrollTop / itemHeight) - bufferSize);
|
|
344
|
+
const end = Math.min(items.length, Math.ceil((scrollTop + viewportHeight) / itemHeight) + bufferSize);
|
|
345
|
+
return { start, end };
|
|
346
|
+
};
|
|
347
|
+
const render2 = () => {
|
|
348
|
+
const { start, end } = getVisibleRange();
|
|
349
|
+
const wrapper = document.createElement("div");
|
|
350
|
+
wrapper.style.cssText = `height:${totalHeight}px;position:relative`;
|
|
351
|
+
for (let i = start; i < end; i++) {
|
|
352
|
+
const itemEl = document.createElement("div");
|
|
353
|
+
itemEl.style.cssText = `position:absolute;top:${i * itemHeight}px;height:${itemHeight}px;width:100%`;
|
|
354
|
+
this.renderToDOM(renderItem(items[i], i), itemEl);
|
|
355
|
+
wrapper.appendChild(itemEl);
|
|
356
|
+
}
|
|
357
|
+
container2.innerHTML = "";
|
|
358
|
+
container2.appendChild(wrapper);
|
|
359
|
+
};
|
|
360
|
+
const scrollHandler = () => {
|
|
361
|
+
scrollTop = container2.scrollTop;
|
|
362
|
+
requestAnimationFrame(render2);
|
|
363
|
+
};
|
|
364
|
+
container2.addEventListener("scroll", scrollHandler);
|
|
365
|
+
render2();
|
|
366
|
+
return {
|
|
367
|
+
render: render2,
|
|
368
|
+
destroy: () => {
|
|
369
|
+
container2.removeEventListener("scroll", scrollHandler);
|
|
370
|
+
container2.innerHTML = "";
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
// Lazy load components
|
|
375
|
+
lazy(loadFn) {
|
|
376
|
+
let component = null;
|
|
377
|
+
let loading = false;
|
|
378
|
+
return async (...args) => {
|
|
379
|
+
if (!component && !loading) {
|
|
380
|
+
loading = true;
|
|
381
|
+
component = await loadFn();
|
|
382
|
+
loading = false;
|
|
383
|
+
}
|
|
384
|
+
return component ? component(...args) : { tagName: "div", props: { class: "loading" }, children: ["Loading..."] };
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
// Memory management - cleanup unused elements
|
|
388
|
+
cleanupUnusedElements(root) {
|
|
389
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
|
|
390
|
+
const toRemove = [];
|
|
391
|
+
while (walker.nextNode()) {
|
|
392
|
+
const node = walker.currentNode;
|
|
393
|
+
if (node.id && node.id.startsWith("r") && !this.elementCache.has(node)) {
|
|
394
|
+
toRemove.push(node);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
toRemove.forEach((el) => el.remove());
|
|
398
|
+
return toRemove.length;
|
|
399
|
+
}
|
|
400
|
+
// Server-Side Rendering - convert VNode to HTML string
|
|
401
|
+
renderToString(vNode, options = {}) {
|
|
402
|
+
const { pretty = false, indent = 0 } = options;
|
|
403
|
+
const indentStr = pretty ? " ".repeat(indent) : "";
|
|
404
|
+
const newLine = pretty ? "\n" : "";
|
|
405
|
+
let resolvedVNode = this.resolveStateValue(vNode);
|
|
406
|
+
resolvedVNode = this.unwrapReactive(resolvedVNode);
|
|
407
|
+
if (Array.isArray(resolvedVNode)) {
|
|
408
|
+
return resolvedVNode.map((child) => this.renderToString(child, options)).join("");
|
|
409
|
+
}
|
|
410
|
+
if (typeof resolvedVNode !== "object" || resolvedVNode === null) {
|
|
411
|
+
if (resolvedVNode === null || resolvedVNode === void 0 || resolvedVNode === false) {
|
|
412
|
+
return "";
|
|
413
|
+
}
|
|
414
|
+
return this.escapeHtml(String(resolvedVNode));
|
|
415
|
+
}
|
|
416
|
+
const { tagName, props, children } = resolvedVNode;
|
|
417
|
+
const textareaValue = resolveTextareaValue(tagName, props);
|
|
418
|
+
const isSelfClosing = this.isSelfClosingTag(tagName);
|
|
419
|
+
let html = `${indentStr}<${tagName}`;
|
|
420
|
+
const attrs = this.propsToAttributes(props, tagName);
|
|
421
|
+
if (attrs) {
|
|
422
|
+
html += ` ${attrs}`;
|
|
423
|
+
}
|
|
424
|
+
if (isSelfClosing) {
|
|
425
|
+
html += ` />${newLine}`;
|
|
426
|
+
return html;
|
|
427
|
+
}
|
|
428
|
+
html += ">";
|
|
429
|
+
if (textareaValue !== void 0) {
|
|
430
|
+
html += this.escapeHtml(textareaValue);
|
|
431
|
+
html += `</${tagName}>${newLine}`;
|
|
432
|
+
return html;
|
|
433
|
+
}
|
|
434
|
+
if (props.dangerouslySetInnerHTML) {
|
|
435
|
+
html += props.dangerouslySetInnerHTML.__html;
|
|
436
|
+
html += `</${tagName}>${newLine}`;
|
|
437
|
+
return html;
|
|
438
|
+
}
|
|
439
|
+
if (children && children.length > 0) {
|
|
440
|
+
const resolvedChildren = children.map((c) => {
|
|
441
|
+
const resolved = this.resolveStateValue(c);
|
|
442
|
+
return this.unwrapReactive(resolved);
|
|
443
|
+
});
|
|
444
|
+
const hasComplexChildren = resolvedChildren.some(
|
|
445
|
+
(c) => typeof c === "object" && c !== null && !Array.isArray(c) && "tagName" in c
|
|
446
|
+
);
|
|
447
|
+
if (pretty && hasComplexChildren) {
|
|
448
|
+
html += newLine;
|
|
449
|
+
for (const child of resolvedChildren) {
|
|
450
|
+
if (shouldSkipChild(child)) continue;
|
|
451
|
+
if (Array.isArray(child)) {
|
|
452
|
+
for (const c of child) {
|
|
453
|
+
if (!shouldSkipChild(c)) {
|
|
454
|
+
html += this.renderToString(c, { pretty, indent: indent + 1 });
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
} else {
|
|
458
|
+
html += this.renderToString(child, { pretty, indent: indent + 1 });
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
html += indentStr;
|
|
462
|
+
} else {
|
|
463
|
+
for (const child of resolvedChildren) {
|
|
464
|
+
if (shouldSkipChild(child)) continue;
|
|
465
|
+
if (Array.isArray(child)) {
|
|
466
|
+
for (const c of child) {
|
|
467
|
+
if (!shouldSkipChild(c)) {
|
|
468
|
+
html += this.renderToString(c, { pretty: false, indent: 0 });
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
} else {
|
|
472
|
+
html += this.renderToString(child, { pretty: false, indent: 0 });
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
html += `</${tagName}>${newLine}`;
|
|
478
|
+
return html;
|
|
479
|
+
}
|
|
480
|
+
resolveStateValue(value) {
|
|
481
|
+
if (value && typeof value === "object" && "value" in value && "subscribe" in value) {
|
|
482
|
+
return value.value;
|
|
483
|
+
}
|
|
484
|
+
return value;
|
|
485
|
+
}
|
|
486
|
+
isReactiveWrapper(vNode) {
|
|
487
|
+
if (!vNode || typeof vNode !== "object" || !vNode.tagName) {
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
490
|
+
return vNode.tagName === "span" && vNode.props?.id && typeof vNode.props.id === "string" && vNode.props.id.match(/^r[a-z0-9]{9}$/);
|
|
491
|
+
}
|
|
492
|
+
unwrapReactive(vNode) {
|
|
493
|
+
if (!this.isReactiveWrapper(vNode)) {
|
|
494
|
+
return vNode;
|
|
495
|
+
}
|
|
496
|
+
const children = vNode.children;
|
|
497
|
+
if (!children || children.length === 0) {
|
|
498
|
+
return "";
|
|
499
|
+
}
|
|
500
|
+
if (children.length === 1) {
|
|
501
|
+
const child = children[0];
|
|
502
|
+
if (child && typeof child === "object" && child.tagName === "span") {
|
|
503
|
+
const props = child.props;
|
|
504
|
+
const hasNoProps = !props || Object.keys(props).length === 0;
|
|
505
|
+
const hasSingleStringChild = child.children && child.children.length === 1 && typeof child.children[0] === "string";
|
|
506
|
+
if (hasNoProps && hasSingleStringChild) {
|
|
507
|
+
return child.children[0];
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
return this.unwrapReactive(child);
|
|
511
|
+
}
|
|
512
|
+
return children.map((c) => this.unwrapReactive(c));
|
|
513
|
+
}
|
|
514
|
+
escapeHtml(text) {
|
|
515
|
+
const htmlEscapes = {
|
|
516
|
+
"&": "&",
|
|
517
|
+
"<": "<",
|
|
518
|
+
">": ">",
|
|
519
|
+
'"': """,
|
|
520
|
+
"'": "'"
|
|
521
|
+
};
|
|
522
|
+
return text.replace(/[&<>"']/g, (char) => htmlEscapes[char]);
|
|
523
|
+
}
|
|
524
|
+
isSelfClosingTag(tagName) {
|
|
525
|
+
const selfClosingTags = /* @__PURE__ */ new Set([
|
|
526
|
+
"area",
|
|
527
|
+
"base",
|
|
528
|
+
"br",
|
|
529
|
+
"col",
|
|
530
|
+
"embed",
|
|
531
|
+
"hr",
|
|
532
|
+
"img",
|
|
533
|
+
"input",
|
|
534
|
+
"link",
|
|
535
|
+
"meta",
|
|
536
|
+
"param",
|
|
537
|
+
"source",
|
|
538
|
+
"track",
|
|
539
|
+
"wbr"
|
|
540
|
+
]);
|
|
541
|
+
return selfClosingTags.has(tagName.toLowerCase());
|
|
542
|
+
}
|
|
543
|
+
propsToAttributes(props, tagName) {
|
|
544
|
+
const attrs = [];
|
|
545
|
+
for (const key in props) {
|
|
546
|
+
if (key === "children" || key === "dangerouslySetInnerHTML" || key === "ref" || tagName === "textarea" && key === "value") {
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
let value = props[key];
|
|
550
|
+
value = this.resolveStateValue(value);
|
|
551
|
+
if (value == null || value === false) continue;
|
|
552
|
+
if (key.startsWith("on") && typeof value === "function") {
|
|
553
|
+
continue;
|
|
554
|
+
}
|
|
555
|
+
if (key === "className" || key === "class") {
|
|
556
|
+
const className = Array.isArray(value) ? value.join(" ") : value;
|
|
557
|
+
if (className) {
|
|
558
|
+
attrs.push(`class="${this.escapeHtml(String(className))}"`);
|
|
559
|
+
}
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
if (key === "style") {
|
|
563
|
+
const styleStr = this.styleToString(value);
|
|
564
|
+
if (styleStr) {
|
|
565
|
+
attrs.push(`style="${this.escapeHtml(styleStr)}"`);
|
|
566
|
+
}
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
if (value === true) {
|
|
570
|
+
attrs.push(key);
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
573
|
+
attrs.push(`${key}="${this.escapeHtml(String(value))}"`);
|
|
574
|
+
}
|
|
575
|
+
return attrs.join(" ");
|
|
576
|
+
}
|
|
577
|
+
styleToString(style) {
|
|
578
|
+
if (typeof style === "string") {
|
|
579
|
+
return style;
|
|
580
|
+
}
|
|
581
|
+
if (typeof style === "object" && style !== null) {
|
|
582
|
+
const styles2 = [];
|
|
583
|
+
for (const key in style) {
|
|
584
|
+
const cssKey = key.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
585
|
+
styles2.push(`${cssKey}:${style[key]}`);
|
|
586
|
+
}
|
|
587
|
+
return styles2.join(";");
|
|
588
|
+
}
|
|
589
|
+
return "";
|
|
590
|
+
}
|
|
591
|
+
isState(value) {
|
|
592
|
+
return value && typeof value === "object" && "value" in value && "subscribe" in value && typeof value.subscribe === "function";
|
|
593
|
+
}
|
|
594
|
+
createReactiveChild(state, renderFn) {
|
|
595
|
+
const currentValue = renderFn(state.value);
|
|
596
|
+
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
597
|
+
const entry = { node: null, renderFn };
|
|
598
|
+
this.reactiveNodes.set(state, entry);
|
|
599
|
+
state.subscribe(() => {
|
|
600
|
+
if (entry.node && entry.node.parentNode) {
|
|
601
|
+
const newValue = renderFn(state.value);
|
|
602
|
+
entry.node.textContent = String(newValue ?? "");
|
|
603
|
+
}
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
return currentValue;
|
|
607
|
+
}
|
|
608
|
+
jsonToVNode(json) {
|
|
609
|
+
if (this.isState(json)) {
|
|
610
|
+
return this.createReactiveChild(json, (v) => v);
|
|
611
|
+
}
|
|
612
|
+
if (isPrimitiveJson(json)) {
|
|
613
|
+
return json;
|
|
614
|
+
}
|
|
615
|
+
const { tag, attributes = {}, children } = json;
|
|
616
|
+
const props = {};
|
|
617
|
+
for (const key in attributes) {
|
|
618
|
+
const value = attributes[key];
|
|
619
|
+
if (key === "class") {
|
|
620
|
+
props.className = this.isState(value) ? value.value : value;
|
|
621
|
+
} else {
|
|
622
|
+
props[key] = this.isState(value) ? value.value : value;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
const childrenArray = [];
|
|
626
|
+
if (children != null) {
|
|
627
|
+
if (Array.isArray(children)) {
|
|
628
|
+
for (const child of children) {
|
|
629
|
+
if (this.isState(child)) {
|
|
630
|
+
childrenArray.push(this.createReactiveChild(child, (v) => v));
|
|
631
|
+
} else {
|
|
632
|
+
const converted = this.jsonToVNode(child);
|
|
633
|
+
if (converted != null && converted !== false) {
|
|
634
|
+
childrenArray.push(converted);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
} else if (this.isState(children)) {
|
|
639
|
+
childrenArray.push(this.createReactiveChild(children, (v) => v));
|
|
640
|
+
} else if (typeof children === "object" && "tag" in children) {
|
|
641
|
+
const converted = this.jsonToVNode(children);
|
|
642
|
+
if (converted != null && converted !== false) {
|
|
643
|
+
childrenArray.push(converted);
|
|
644
|
+
}
|
|
645
|
+
} else {
|
|
646
|
+
childrenArray.push(children);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
return { tagName: tag, props, children: childrenArray };
|
|
650
|
+
}
|
|
651
|
+
vNodeJsonToVNode(json) {
|
|
652
|
+
if (this.isState(json)) {
|
|
653
|
+
return this.createReactiveChild(json, (v) => v);
|
|
654
|
+
}
|
|
655
|
+
if (isPrimitiveJson(json)) {
|
|
656
|
+
return json;
|
|
657
|
+
}
|
|
658
|
+
const { tagName, props = {}, children = [] } = json;
|
|
659
|
+
const resolvedProps = {};
|
|
660
|
+
for (const key in props) {
|
|
661
|
+
const value = props[key];
|
|
662
|
+
resolvedProps[key] = this.isState(value) ? value.value : value;
|
|
663
|
+
}
|
|
664
|
+
const childrenArray = [];
|
|
665
|
+
for (const child of children) {
|
|
666
|
+
if (this.isState(child)) {
|
|
667
|
+
childrenArray.push(this.createReactiveChild(child, (v) => v));
|
|
668
|
+
} else {
|
|
669
|
+
const converted = this.vNodeJsonToVNode(child);
|
|
670
|
+
if (converted != null && converted !== false) {
|
|
671
|
+
childrenArray.push(converted);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
return { tagName, props: resolvedProps, children: childrenArray };
|
|
676
|
+
}
|
|
677
|
+
renderJson(rootElement, json) {
|
|
678
|
+
const vNode = this.jsonToVNode(json);
|
|
679
|
+
if (!vNode || typeof vNode !== "object" || !("tagName" in vNode)) {
|
|
680
|
+
throw new Error("Invalid JSON structure");
|
|
681
|
+
}
|
|
682
|
+
return this.render(rootElement, vNode);
|
|
683
|
+
}
|
|
684
|
+
renderVNode(rootElement, json) {
|
|
685
|
+
const vNode = this.vNodeJsonToVNode(json);
|
|
686
|
+
if (!vNode || typeof vNode !== "object" || !("tagName" in vNode)) {
|
|
687
|
+
throw new Error("Invalid VNode JSON structure");
|
|
688
|
+
}
|
|
689
|
+
return this.render(rootElement, vNode);
|
|
690
|
+
}
|
|
691
|
+
renderJsonToString(json, options = {}) {
|
|
692
|
+
const vNode = this.jsonToVNode(json);
|
|
693
|
+
return this.renderToString(vNode, options);
|
|
694
|
+
}
|
|
695
|
+
renderVNodeToString(json, options = {}) {
|
|
696
|
+
const vNode = this.vNodeJsonToVNode(json);
|
|
697
|
+
return this.renderToString(vNode, options);
|
|
698
|
+
}
|
|
699
|
+
// Generate complete HTML document as string (for SSR)
|
|
700
|
+
renderToHTMLDocument(vNode, options = {}) {
|
|
701
|
+
const { title = "", meta = [], links = [], scripts = [], styles: styles2 = [], lang = "en", head = "", bodyAttrs = {}, pretty = false } = options;
|
|
702
|
+
const nl = pretty ? "\n" : "";
|
|
703
|
+
const indent = pretty ? " " : "";
|
|
704
|
+
const indent2 = pretty ? " " : "";
|
|
705
|
+
let html = `<!DOCTYPE html>${nl}<html lang="${lang}">${nl}${indent}<head>${nl}${indent2}<meta charset="UTF-8">${nl}${indent2}<meta name="viewport" content="width=device-width, initial-scale=1.0">${nl}`;
|
|
706
|
+
if (title) html += `${indent2}<title>${this.escapeHtml(title)}</title>${nl}`;
|
|
707
|
+
for (const m of meta) {
|
|
708
|
+
html += `${indent2}<meta`;
|
|
709
|
+
for (const k in m) html += ` ${k}="${this.escapeHtml(m[k])}"`;
|
|
710
|
+
html += `>${nl}`;
|
|
711
|
+
}
|
|
712
|
+
for (const l of links) {
|
|
713
|
+
html += `${indent2}<link`;
|
|
714
|
+
for (const k in l) html += ` ${k}="${this.escapeHtml(l[k])}"`;
|
|
715
|
+
html += `>${nl}`;
|
|
716
|
+
}
|
|
717
|
+
for (const s of styles2) {
|
|
718
|
+
if (s.href) {
|
|
719
|
+
html += `${indent2}<link rel="stylesheet" href="${this.escapeHtml(s.href)}">${nl}`;
|
|
720
|
+
} else if (s.content) {
|
|
721
|
+
html += `${indent2}<style>${s.content}</style>${nl}`;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
if (head) html += head + nl;
|
|
725
|
+
html += `${indent}</head>${nl}${indent}<body`;
|
|
726
|
+
for (const k in bodyAttrs) html += ` ${k}="${this.escapeHtml(bodyAttrs[k])}"`;
|
|
727
|
+
html += `>${nl}`;
|
|
728
|
+
html += this.renderToString(vNode, { pretty, indent: 2 });
|
|
729
|
+
for (const script of scripts) {
|
|
730
|
+
html += `${indent2}<script`;
|
|
731
|
+
if (script.type) html += ` type="${this.escapeHtml(script.type)}"`;
|
|
732
|
+
if (script.async) html += ` async`;
|
|
733
|
+
if (script.defer) html += ` defer`;
|
|
734
|
+
if (script.src) {
|
|
735
|
+
html += ` src="${this.escapeHtml(script.src)}"></script>${nl}`;
|
|
736
|
+
} else if (script.content) {
|
|
737
|
+
html += `>${script.content}</script>${nl}`;
|
|
738
|
+
} else {
|
|
739
|
+
html += `></script>${nl}`;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
html += `${indent}</body>${nl}</html>`;
|
|
743
|
+
return html;
|
|
744
|
+
}
|
|
745
|
+
// Expose elementCache for reactive updates
|
|
746
|
+
getElementCache() {
|
|
747
|
+
return this.elementCache;
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
var dom = new DomNode();
|
|
751
|
+
var render = dom.render.bind(dom);
|
|
752
|
+
var renderToString = dom.renderToString.bind(dom);
|
|
753
|
+
|
|
754
|
+
// src/style.ts
|
|
755
|
+
var ELIT_SHARED_STYLE_STORE_KEY = "__elitSharedStyleStore__";
|
|
756
|
+
function createStyleStore() {
|
|
757
|
+
return {
|
|
758
|
+
variables: [],
|
|
759
|
+
rules: [],
|
|
760
|
+
mediaRules: [],
|
|
761
|
+
keyframes: [],
|
|
762
|
+
fontFaces: [],
|
|
763
|
+
imports: [],
|
|
764
|
+
containerRules: [],
|
|
765
|
+
supportsRules: [],
|
|
766
|
+
layerRules: [],
|
|
767
|
+
layerOrder: []
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
function getSharedStyleStore() {
|
|
771
|
+
const globalScope = globalThis;
|
|
772
|
+
if (!globalScope[ELIT_SHARED_STYLE_STORE_KEY]) {
|
|
773
|
+
globalScope[ELIT_SHARED_STYLE_STORE_KEY] = createStyleStore();
|
|
774
|
+
}
|
|
775
|
+
return globalScope[ELIT_SHARED_STYLE_STORE_KEY];
|
|
776
|
+
}
|
|
777
|
+
var CreateStyle = class {
|
|
778
|
+
constructor(store) {
|
|
779
|
+
this.variables = [];
|
|
780
|
+
this.rules = [];
|
|
781
|
+
this.mediaRules = [];
|
|
782
|
+
this.keyframes = [];
|
|
783
|
+
this.fontFaces = [];
|
|
784
|
+
this.imports = [];
|
|
785
|
+
this.containerRules = [];
|
|
786
|
+
this.supportsRules = [];
|
|
787
|
+
this.layerRules = [];
|
|
788
|
+
this._layerOrder = [];
|
|
789
|
+
this.parsedSelectorChainCache = /* @__PURE__ */ new Map();
|
|
790
|
+
if (!store) {
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
this.variables = store.variables;
|
|
794
|
+
this.rules = store.rules;
|
|
795
|
+
this.mediaRules = store.mediaRules;
|
|
796
|
+
this.keyframes = store.keyframes;
|
|
797
|
+
this.fontFaces = store.fontFaces;
|
|
798
|
+
this.imports = store.imports;
|
|
799
|
+
this.containerRules = store.containerRules;
|
|
800
|
+
this.supportsRules = store.supportsRules;
|
|
801
|
+
this.layerRules = store.layerRules;
|
|
802
|
+
this._layerOrder = store.layerOrder;
|
|
803
|
+
}
|
|
804
|
+
// CSS Variables
|
|
805
|
+
addVar(name, value) {
|
|
806
|
+
const cssVar = {
|
|
807
|
+
name: name.startsWith("--") ? name : `--${name}`,
|
|
808
|
+
value,
|
|
809
|
+
toString() {
|
|
810
|
+
return `var(${this.name})`;
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
this.variables.push(cssVar);
|
|
814
|
+
return cssVar;
|
|
815
|
+
}
|
|
816
|
+
var(variable, fallback) {
|
|
817
|
+
const varName = typeof variable === "string" ? variable.startsWith("--") ? variable : `--${variable}` : variable.name;
|
|
818
|
+
return fallback ? `var(${varName}, ${fallback})` : `var(${varName})`;
|
|
819
|
+
}
|
|
820
|
+
// Basic Selectors
|
|
821
|
+
addTag(tag, styles2) {
|
|
822
|
+
const rule = { selector: tag, styles: styles2, type: "tag" };
|
|
823
|
+
this.rules.push(rule);
|
|
824
|
+
return rule;
|
|
825
|
+
}
|
|
826
|
+
addClass(name, styles2) {
|
|
827
|
+
const selector = name.startsWith(".") ? name : `.${name}`;
|
|
828
|
+
const rule = { selector, styles: styles2, type: "class" };
|
|
829
|
+
this.rules.push(rule);
|
|
830
|
+
return rule;
|
|
831
|
+
}
|
|
832
|
+
addId(name, styles2) {
|
|
833
|
+
const selector = name.startsWith("#") ? name : `#${name}`;
|
|
834
|
+
const rule = { selector, styles: styles2, type: "id" };
|
|
835
|
+
this.rules.push(rule);
|
|
836
|
+
return rule;
|
|
837
|
+
}
|
|
838
|
+
// Pseudo Selectors
|
|
839
|
+
addPseudoClass(pseudo, styles2, baseSelector) {
|
|
840
|
+
const pseudoClass = pseudo.startsWith(":") ? pseudo : `:${pseudo}`;
|
|
841
|
+
const selector = baseSelector ? `${baseSelector}${pseudoClass}` : pseudoClass;
|
|
842
|
+
const rule = { selector, styles: styles2, type: "pseudo-class" };
|
|
843
|
+
this.rules.push(rule);
|
|
844
|
+
return rule;
|
|
845
|
+
}
|
|
846
|
+
addPseudoElement(pseudo, styles2, baseSelector) {
|
|
847
|
+
const pseudoElement = pseudo.startsWith("::") ? pseudo : `::${pseudo}`;
|
|
848
|
+
const selector = baseSelector ? `${baseSelector}${pseudoElement}` : pseudoElement;
|
|
849
|
+
const rule = { selector, styles: styles2, type: "pseudo-element" };
|
|
850
|
+
this.rules.push(rule);
|
|
851
|
+
return rule;
|
|
852
|
+
}
|
|
853
|
+
// Attribute Selectors
|
|
854
|
+
addAttribute(attr, styles2, baseSelector) {
|
|
855
|
+
const attrSelector = attr.startsWith("[") ? attr : `[${attr}]`;
|
|
856
|
+
const selector = baseSelector ? `${baseSelector}${attrSelector}` : attrSelector;
|
|
857
|
+
const rule = { selector, styles: styles2, type: "attribute" };
|
|
858
|
+
this.rules.push(rule);
|
|
859
|
+
return rule;
|
|
860
|
+
}
|
|
861
|
+
attrEquals(attr, value, styles2, baseSelector) {
|
|
862
|
+
return this.addAttribute(`${attr}="${value}"`, styles2, baseSelector);
|
|
863
|
+
}
|
|
864
|
+
attrContainsWord(attr, value, styles2, baseSelector) {
|
|
865
|
+
return this.addAttribute(`${attr}~="${value}"`, styles2, baseSelector);
|
|
866
|
+
}
|
|
867
|
+
attrStartsWith(attr, value, styles2, baseSelector) {
|
|
868
|
+
return this.addAttribute(`${attr}^="${value}"`, styles2, baseSelector);
|
|
869
|
+
}
|
|
870
|
+
attrEndsWith(attr, value, styles2, baseSelector) {
|
|
871
|
+
return this.addAttribute(`${attr}$="${value}"`, styles2, baseSelector);
|
|
872
|
+
}
|
|
873
|
+
attrContains(attr, value, styles2, baseSelector) {
|
|
874
|
+
return this.addAttribute(`${attr}*="${value}"`, styles2, baseSelector);
|
|
875
|
+
}
|
|
876
|
+
// Combinator Selectors
|
|
877
|
+
descendant(ancestor, descendant2, styles2) {
|
|
878
|
+
return this.createAndAddRule(`${ancestor} ${descendant2}`, styles2);
|
|
879
|
+
}
|
|
880
|
+
child(parent, childSel, styles2) {
|
|
881
|
+
return this.createAndAddRule(`${parent} > ${childSel}`, styles2);
|
|
882
|
+
}
|
|
883
|
+
adjacentSibling(element, sibling, styles2) {
|
|
884
|
+
return this.createAndAddRule(`${element} + ${sibling}`, styles2);
|
|
885
|
+
}
|
|
886
|
+
generalSibling(element, sibling, styles2) {
|
|
887
|
+
return this.createAndAddRule(`${element} ~ ${sibling}`, styles2);
|
|
888
|
+
}
|
|
889
|
+
multiple(selectors, styles2) {
|
|
890
|
+
return this.createAndAddRule(selectors.join(", "), styles2);
|
|
891
|
+
}
|
|
892
|
+
// Nesting (BEM-style)
|
|
893
|
+
addName(name, styles2) {
|
|
894
|
+
const selector = name.startsWith("--") ? `&${name}` : `&--${name}`;
|
|
895
|
+
const rule = { selector, styles: styles2, type: "name" };
|
|
896
|
+
return rule;
|
|
897
|
+
}
|
|
898
|
+
nesting(parentRule, ...childRules) {
|
|
899
|
+
parentRule.nested = childRules;
|
|
900
|
+
return parentRule;
|
|
901
|
+
}
|
|
902
|
+
// @keyframes - Animations
|
|
903
|
+
keyframe(name, steps) {
|
|
904
|
+
const keyframeSteps = Object.entries(steps).map(([step, styles2]) => ({
|
|
905
|
+
step: step === "from" ? "from" : step === "to" ? "to" : `${step}%`,
|
|
906
|
+
styles: styles2
|
|
907
|
+
}));
|
|
908
|
+
const kf = { name, steps: keyframeSteps };
|
|
909
|
+
this.keyframes.push(kf);
|
|
910
|
+
return kf;
|
|
911
|
+
}
|
|
912
|
+
keyframeFromTo(name, from, to) {
|
|
913
|
+
return this.keyframe(name, { from, to });
|
|
914
|
+
}
|
|
915
|
+
// @font-face - Custom Fonts
|
|
916
|
+
fontFace(options) {
|
|
917
|
+
this.fontFaces.push(options);
|
|
918
|
+
return options;
|
|
919
|
+
}
|
|
920
|
+
// @import - Import Stylesheets
|
|
921
|
+
import(url, mediaQuery) {
|
|
922
|
+
const importRule = mediaQuery ? `@import url("${url}") ${mediaQuery};` : `@import url("${url}");`;
|
|
923
|
+
this.imports.push(importRule);
|
|
924
|
+
return importRule;
|
|
925
|
+
}
|
|
926
|
+
// @media - Media Queries
|
|
927
|
+
media(type, condition, rules) {
|
|
928
|
+
const mediaRule = { type, condition, rules: this.rulesToCSSRules(rules) };
|
|
929
|
+
this.mediaRules.push(mediaRule);
|
|
930
|
+
return mediaRule;
|
|
931
|
+
}
|
|
932
|
+
mediaScreen(condition, rules) {
|
|
933
|
+
return this.media("screen", condition, rules);
|
|
934
|
+
}
|
|
935
|
+
mediaPrint(rules) {
|
|
936
|
+
return this.media("print", "", rules);
|
|
937
|
+
}
|
|
938
|
+
mediaMinWidth(minWidth, rules) {
|
|
939
|
+
return this.media("screen", `min-width: ${minWidth}`, rules);
|
|
940
|
+
}
|
|
941
|
+
mediaMaxWidth(maxWidth, rules) {
|
|
942
|
+
return this.media("screen", `max-width: ${maxWidth}`, rules);
|
|
943
|
+
}
|
|
944
|
+
mediaDark(rules) {
|
|
945
|
+
const mediaRule = { type: "", condition: "prefers-color-scheme: dark", rules: this.rulesToCSSRules(rules) };
|
|
946
|
+
this.mediaRules.push(mediaRule);
|
|
947
|
+
return mediaRule;
|
|
948
|
+
}
|
|
949
|
+
mediaLight(rules) {
|
|
950
|
+
const mediaRule = { type: "", condition: "prefers-color-scheme: light", rules: this.rulesToCSSRules(rules) };
|
|
951
|
+
this.mediaRules.push(mediaRule);
|
|
952
|
+
return mediaRule;
|
|
953
|
+
}
|
|
954
|
+
mediaReducedMotion(rules) {
|
|
955
|
+
const mediaRule = { type: "", condition: "prefers-reduced-motion: reduce", rules: this.rulesToCSSRules(rules) };
|
|
956
|
+
this.mediaRules.push(mediaRule);
|
|
957
|
+
return mediaRule;
|
|
958
|
+
}
|
|
959
|
+
// @container - Container Queries
|
|
960
|
+
container(condition, rules, name) {
|
|
961
|
+
const containerRule = { name, condition, rules: this.rulesToCSSRules(rules) };
|
|
962
|
+
this.containerRules.push(containerRule);
|
|
963
|
+
return containerRule;
|
|
964
|
+
}
|
|
965
|
+
addContainer(name, styles2) {
|
|
966
|
+
const containerStyles = { ...styles2, containerName: name };
|
|
967
|
+
return this.addClass(name, containerStyles);
|
|
968
|
+
}
|
|
969
|
+
// @supports - Feature Queries
|
|
970
|
+
supports(condition, rules) {
|
|
971
|
+
const supportsRule = { condition, rules: this.rulesToCSSRules(rules) };
|
|
972
|
+
this.supportsRules.push(supportsRule);
|
|
973
|
+
return supportsRule;
|
|
974
|
+
}
|
|
975
|
+
// @layer - Cascade Layers
|
|
976
|
+
layerOrder(...layers) {
|
|
977
|
+
this._layerOrder = layers;
|
|
978
|
+
}
|
|
979
|
+
layer(name, rules) {
|
|
980
|
+
const layerRule = { name, rules: this.rulesToCSSRules(rules) };
|
|
981
|
+
this.layerRules.push(layerRule);
|
|
982
|
+
return layerRule;
|
|
983
|
+
}
|
|
984
|
+
// Custom Rules
|
|
985
|
+
add(rules) {
|
|
986
|
+
const cssRules = Object.entries(rules).map(([selector, styles2]) => {
|
|
987
|
+
const rule = { selector, styles: styles2, type: "custom" };
|
|
988
|
+
this.rules.push(rule);
|
|
989
|
+
return rule;
|
|
990
|
+
});
|
|
991
|
+
return cssRules;
|
|
992
|
+
}
|
|
993
|
+
important(value) {
|
|
994
|
+
return `${value} !important`;
|
|
995
|
+
}
|
|
996
|
+
getVariables() {
|
|
997
|
+
return Object.fromEntries(this.variables.map((variable) => [variable.name, variable.value]));
|
|
998
|
+
}
|
|
999
|
+
resolveVariableReferences(value, variables) {
|
|
1000
|
+
let resolved = value;
|
|
1001
|
+
for (let index = 0; index < 8; index++) {
|
|
1002
|
+
let replaced = false;
|
|
1003
|
+
resolved = resolved.replace(/var\(\s*(--[\w-]+)\s*(?:,\s*([^\)]+))?\)/g, (match, name, fallback) => {
|
|
1004
|
+
const variableValue = variables[name];
|
|
1005
|
+
if (variableValue !== void 0) {
|
|
1006
|
+
replaced = true;
|
|
1007
|
+
return variableValue;
|
|
1008
|
+
}
|
|
1009
|
+
if (fallback !== void 0) {
|
|
1010
|
+
replaced = true;
|
|
1011
|
+
return fallback.trim();
|
|
1012
|
+
}
|
|
1013
|
+
return match;
|
|
1014
|
+
});
|
|
1015
|
+
if (!replaced) {
|
|
1016
|
+
break;
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
return resolved.replace(/\s*!important\s*$/i, "").trim();
|
|
1020
|
+
}
|
|
1021
|
+
normalizeTargetIdentity(target) {
|
|
1022
|
+
return {
|
|
1023
|
+
tagName: typeof target.tagName === "string" && target.tagName.trim() ? target.tagName.trim().toLowerCase() : void 0,
|
|
1024
|
+
classNames: Array.isArray(target.classNames) ? target.classNames.map((className) => className.trim()).filter(Boolean) : [],
|
|
1025
|
+
attributes: target.attributes ? Object.fromEntries(
|
|
1026
|
+
Object.entries(target.attributes).filter(([, value]) => value !== void 0 && value !== null && value !== false).map(([name, value]) => [name.toLowerCase(), String(value)])
|
|
1027
|
+
) : {},
|
|
1028
|
+
pseudoStates: Array.isArray(target.pseudoStates) ? [...new Set(target.pseudoStates.map((pseudoState) => pseudoState.trim().toLowerCase()).filter(Boolean))] : [],
|
|
1029
|
+
childIndex: typeof target.childIndex === "number" && Number.isFinite(target.childIndex) ? target.childIndex : void 0,
|
|
1030
|
+
siblingCount: typeof target.siblingCount === "number" && Number.isFinite(target.siblingCount) ? target.siblingCount : void 0,
|
|
1031
|
+
sameTypeIndex: typeof target.sameTypeIndex === "number" && Number.isFinite(target.sameTypeIndex) ? target.sameTypeIndex : void 0,
|
|
1032
|
+
sameTypeCount: typeof target.sameTypeCount === "number" && Number.isFinite(target.sameTypeCount) ? target.sameTypeCount : void 0,
|
|
1033
|
+
containerNames: Array.isArray(target.containerNames) ? [...new Set(target.containerNames.map((containerName) => containerName.trim().toLowerCase()).filter(Boolean))] : [],
|
|
1034
|
+
containerWidth: typeof target.containerWidth === "number" && Number.isFinite(target.containerWidth) ? target.containerWidth : void 0,
|
|
1035
|
+
isContainer: target.isContainer === true,
|
|
1036
|
+
isScopeReference: target.isScopeReference === true
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
normalizeTarget(target) {
|
|
1040
|
+
const cached = this.nativeTargetNormalizationCache?.get(target);
|
|
1041
|
+
if (cached) {
|
|
1042
|
+
return cached;
|
|
1043
|
+
}
|
|
1044
|
+
const normalized = {
|
|
1045
|
+
...this.normalizeTargetIdentity(target),
|
|
1046
|
+
previousSiblings: [],
|
|
1047
|
+
nextSiblings: [],
|
|
1048
|
+
children: []
|
|
1049
|
+
};
|
|
1050
|
+
this.nativeTargetNormalizationCache?.set(target, normalized);
|
|
1051
|
+
normalized.previousSiblings = Array.isArray(target.previousSiblings) ? target.previousSiblings.map((sibling) => this.normalizeTarget(sibling)) : [];
|
|
1052
|
+
normalized.nextSiblings = Array.isArray(target.nextSiblings) ? target.nextSiblings.map((sibling) => this.normalizeTarget(sibling)) : [];
|
|
1053
|
+
normalized.children = Array.isArray(target.children) ? target.children.map((child) => this.normalizeTarget(child)) : [];
|
|
1054
|
+
return normalized;
|
|
1055
|
+
}
|
|
1056
|
+
withNativeTargetNormalizationCache(callback) {
|
|
1057
|
+
const previousCache = this.nativeTargetNormalizationCache;
|
|
1058
|
+
this.nativeTargetNormalizationCache = /* @__PURE__ */ new WeakMap();
|
|
1059
|
+
try {
|
|
1060
|
+
return callback();
|
|
1061
|
+
} finally {
|
|
1062
|
+
this.nativeTargetNormalizationCache = previousCache;
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
splitConditionalClauses(value, operator) {
|
|
1066
|
+
const clauses = [];
|
|
1067
|
+
let token = "";
|
|
1068
|
+
let depth = 0;
|
|
1069
|
+
for (let index = 0; index < value.length; index++) {
|
|
1070
|
+
const char = value[index];
|
|
1071
|
+
if (char === "(") {
|
|
1072
|
+
depth += 1;
|
|
1073
|
+
} else if (char === ")" && depth > 0) {
|
|
1074
|
+
depth -= 1;
|
|
1075
|
+
}
|
|
1076
|
+
const operatorToken = ` ${operator} `;
|
|
1077
|
+
if (depth === 0 && value.slice(index, index + operatorToken.length).toLowerCase() === operatorToken) {
|
|
1078
|
+
const trimmed = token.trim();
|
|
1079
|
+
if (trimmed) {
|
|
1080
|
+
clauses.push(trimmed);
|
|
1081
|
+
}
|
|
1082
|
+
token = "";
|
|
1083
|
+
index += operatorToken.length - 1;
|
|
1084
|
+
continue;
|
|
1085
|
+
}
|
|
1086
|
+
token += char;
|
|
1087
|
+
}
|
|
1088
|
+
const trailing = token.trim();
|
|
1089
|
+
if (trailing) {
|
|
1090
|
+
clauses.push(trailing);
|
|
1091
|
+
}
|
|
1092
|
+
return clauses;
|
|
1093
|
+
}
|
|
1094
|
+
splitSelectorList(value) {
|
|
1095
|
+
const selectors = [];
|
|
1096
|
+
let token = "";
|
|
1097
|
+
let attributeDepth = 0;
|
|
1098
|
+
let parenthesisDepth = 0;
|
|
1099
|
+
let quoted;
|
|
1100
|
+
for (let index = 0; index < value.length; index++) {
|
|
1101
|
+
const char = value[index];
|
|
1102
|
+
if (quoted) {
|
|
1103
|
+
token += char;
|
|
1104
|
+
if (char === quoted && value[index - 1] !== "\\") {
|
|
1105
|
+
quoted = void 0;
|
|
1106
|
+
}
|
|
1107
|
+
continue;
|
|
1108
|
+
}
|
|
1109
|
+
if (char === '"' || char === "'") {
|
|
1110
|
+
quoted = char;
|
|
1111
|
+
token += char;
|
|
1112
|
+
continue;
|
|
1113
|
+
}
|
|
1114
|
+
if (char === "[") {
|
|
1115
|
+
attributeDepth += 1;
|
|
1116
|
+
token += char;
|
|
1117
|
+
continue;
|
|
1118
|
+
}
|
|
1119
|
+
if (char === "]" && attributeDepth > 0) {
|
|
1120
|
+
attributeDepth -= 1;
|
|
1121
|
+
token += char;
|
|
1122
|
+
continue;
|
|
1123
|
+
}
|
|
1124
|
+
if (attributeDepth === 0 && char === "(") {
|
|
1125
|
+
parenthesisDepth += 1;
|
|
1126
|
+
token += char;
|
|
1127
|
+
continue;
|
|
1128
|
+
}
|
|
1129
|
+
if (attributeDepth === 0 && char === ")" && parenthesisDepth > 0) {
|
|
1130
|
+
parenthesisDepth -= 1;
|
|
1131
|
+
token += char;
|
|
1132
|
+
continue;
|
|
1133
|
+
}
|
|
1134
|
+
if (attributeDepth === 0 && parenthesisDepth === 0 && char === ",") {
|
|
1135
|
+
const trimmed = token.trim();
|
|
1136
|
+
if (trimmed) {
|
|
1137
|
+
selectors.push(trimmed);
|
|
1138
|
+
}
|
|
1139
|
+
token = "";
|
|
1140
|
+
continue;
|
|
1141
|
+
}
|
|
1142
|
+
token += char;
|
|
1143
|
+
}
|
|
1144
|
+
const trailing = token.trim();
|
|
1145
|
+
if (trailing) {
|
|
1146
|
+
selectors.push(trailing);
|
|
1147
|
+
}
|
|
1148
|
+
return selectors;
|
|
1149
|
+
}
|
|
1150
|
+
parsePseudoSelectorToken(token, startIndex) {
|
|
1151
|
+
if (token[startIndex] !== ":" || token[startIndex + 1] === ":") {
|
|
1152
|
+
return void 0;
|
|
1153
|
+
}
|
|
1154
|
+
let cursor = startIndex + 1;
|
|
1155
|
+
const nameMatch = token.slice(cursor).match(/^([_a-zA-Z][-_a-zA-Z0-9]*)/);
|
|
1156
|
+
if (!nameMatch) {
|
|
1157
|
+
return void 0;
|
|
1158
|
+
}
|
|
1159
|
+
const pseudoName = nameMatch[1].toLowerCase();
|
|
1160
|
+
cursor += nameMatch[0].length;
|
|
1161
|
+
if (token[cursor] !== "(") {
|
|
1162
|
+
return { value: pseudoName, nextIndex: cursor };
|
|
1163
|
+
}
|
|
1164
|
+
const argumentStart = cursor + 1;
|
|
1165
|
+
let attributeDepth = 0;
|
|
1166
|
+
let parenthesisDepth = 1;
|
|
1167
|
+
let quoted;
|
|
1168
|
+
cursor += 1;
|
|
1169
|
+
while (cursor < token.length) {
|
|
1170
|
+
const char = token[cursor];
|
|
1171
|
+
if (quoted) {
|
|
1172
|
+
if (char === quoted && token[cursor - 1] !== "\\") {
|
|
1173
|
+
quoted = void 0;
|
|
1174
|
+
}
|
|
1175
|
+
cursor += 1;
|
|
1176
|
+
continue;
|
|
1177
|
+
}
|
|
1178
|
+
if (char === '"' || char === "'") {
|
|
1179
|
+
quoted = char;
|
|
1180
|
+
cursor += 1;
|
|
1181
|
+
continue;
|
|
1182
|
+
}
|
|
1183
|
+
if (char === "[") {
|
|
1184
|
+
attributeDepth += 1;
|
|
1185
|
+
cursor += 1;
|
|
1186
|
+
continue;
|
|
1187
|
+
}
|
|
1188
|
+
if (char === "]" && attributeDepth > 0) {
|
|
1189
|
+
attributeDepth -= 1;
|
|
1190
|
+
cursor += 1;
|
|
1191
|
+
continue;
|
|
1192
|
+
}
|
|
1193
|
+
if (attributeDepth === 0 && char === "(") {
|
|
1194
|
+
parenthesisDepth += 1;
|
|
1195
|
+
cursor += 1;
|
|
1196
|
+
continue;
|
|
1197
|
+
}
|
|
1198
|
+
if (attributeDepth === 0 && char === ")") {
|
|
1199
|
+
parenthesisDepth -= 1;
|
|
1200
|
+
if (parenthesisDepth === 0) {
|
|
1201
|
+
const pseudoArgument = token.slice(argumentStart, cursor).trim();
|
|
1202
|
+
return {
|
|
1203
|
+
value: pseudoArgument.length > 0 ? `${pseudoName}(${pseudoArgument})` : `${pseudoName}()`,
|
|
1204
|
+
nextIndex: cursor + 1
|
|
1205
|
+
};
|
|
1206
|
+
}
|
|
1207
|
+
cursor += 1;
|
|
1208
|
+
continue;
|
|
1209
|
+
}
|
|
1210
|
+
cursor += 1;
|
|
1211
|
+
}
|
|
1212
|
+
return void 0;
|
|
1213
|
+
}
|
|
1214
|
+
matchesSupportsDeclaration(property, value) {
|
|
1215
|
+
const normalizedProperty = property.trim().toLowerCase();
|
|
1216
|
+
const normalizedValue = value.trim().toLowerCase();
|
|
1217
|
+
const supportedProperties = /* @__PURE__ */ new Set([
|
|
1218
|
+
"align-items",
|
|
1219
|
+
"background",
|
|
1220
|
+
"background-color",
|
|
1221
|
+
"backdrop-filter",
|
|
1222
|
+
"border",
|
|
1223
|
+
"border-radius",
|
|
1224
|
+
"box-shadow",
|
|
1225
|
+
"color",
|
|
1226
|
+
"column-gap",
|
|
1227
|
+
"container-name",
|
|
1228
|
+
"container-type",
|
|
1229
|
+
"display",
|
|
1230
|
+
"flex",
|
|
1231
|
+
"flex-direction",
|
|
1232
|
+
"flex-grow",
|
|
1233
|
+
"flex-wrap",
|
|
1234
|
+
"font-family",
|
|
1235
|
+
"font-size",
|
|
1236
|
+
"font-weight",
|
|
1237
|
+
"gap",
|
|
1238
|
+
"grid-template-columns",
|
|
1239
|
+
"height",
|
|
1240
|
+
"justify-content",
|
|
1241
|
+
"letter-spacing",
|
|
1242
|
+
"line-height",
|
|
1243
|
+
"margin",
|
|
1244
|
+
"margin-bottom",
|
|
1245
|
+
"margin-left",
|
|
1246
|
+
"margin-right",
|
|
1247
|
+
"margin-top",
|
|
1248
|
+
"max-height",
|
|
1249
|
+
"max-width",
|
|
1250
|
+
"min-height",
|
|
1251
|
+
"min-width",
|
|
1252
|
+
"padding",
|
|
1253
|
+
"padding-bottom",
|
|
1254
|
+
"padding-end",
|
|
1255
|
+
"padding-horizontal",
|
|
1256
|
+
"padding-left",
|
|
1257
|
+
"padding-right",
|
|
1258
|
+
"padding-start",
|
|
1259
|
+
"padding-top",
|
|
1260
|
+
"padding-vertical",
|
|
1261
|
+
"row-gap",
|
|
1262
|
+
"text-align",
|
|
1263
|
+
"text-decoration",
|
|
1264
|
+
"text-transform",
|
|
1265
|
+
"width"
|
|
1266
|
+
]);
|
|
1267
|
+
if (!supportedProperties.has(normalizedProperty)) {
|
|
1268
|
+
return false;
|
|
1269
|
+
}
|
|
1270
|
+
if (normalizedProperty === "display") {
|
|
1271
|
+
return (/* @__PURE__ */ new Set(["block", "flex", "grid", "inline", "inline-block", "inline-flex", "inline-grid"])).has(normalizedValue);
|
|
1272
|
+
}
|
|
1273
|
+
if (normalizedProperty === "backdrop-filter") {
|
|
1274
|
+
return /blur\(/.test(normalizedValue);
|
|
1275
|
+
}
|
|
1276
|
+
if (normalizedProperty === "container-type") {
|
|
1277
|
+
return (/* @__PURE__ */ new Set(["inline-size", "size"])).has(normalizedValue);
|
|
1278
|
+
}
|
|
1279
|
+
return true;
|
|
1280
|
+
}
|
|
1281
|
+
matchesSupportsCondition(condition) {
|
|
1282
|
+
const normalized = condition.trim().replace(/^\(+|\)+$/g, "").trim();
|
|
1283
|
+
if (!normalized) {
|
|
1284
|
+
return true;
|
|
1285
|
+
}
|
|
1286
|
+
if (normalized.toLowerCase().startsWith("not ")) {
|
|
1287
|
+
return !this.matchesSupportsCondition(normalized.slice(4));
|
|
1288
|
+
}
|
|
1289
|
+
const orClauses = this.splitConditionalClauses(normalized, "or");
|
|
1290
|
+
if (orClauses.length > 1) {
|
|
1291
|
+
return orClauses.some((clause) => this.matchesSupportsCondition(clause));
|
|
1292
|
+
}
|
|
1293
|
+
const andClauses = this.splitConditionalClauses(normalized, "and");
|
|
1294
|
+
if (andClauses.length > 1) {
|
|
1295
|
+
return andClauses.every((clause) => this.matchesSupportsCondition(clause));
|
|
1296
|
+
}
|
|
1297
|
+
const declarationMatch = normalized.match(/^([a-z-]+)\s*:\s*(.+)$/i);
|
|
1298
|
+
if (!declarationMatch) {
|
|
1299
|
+
return false;
|
|
1300
|
+
}
|
|
1301
|
+
return this.matchesSupportsDeclaration(declarationMatch[1], declarationMatch[2]);
|
|
1302
|
+
}
|
|
1303
|
+
findMatchingContainerTarget(ancestors, name) {
|
|
1304
|
+
const normalizedName = typeof name === "string" && name.trim() ? name.trim().toLowerCase() : void 0;
|
|
1305
|
+
for (let index = ancestors.length - 1; index >= 0; index--) {
|
|
1306
|
+
const ancestor = ancestors[index];
|
|
1307
|
+
if (!ancestor?.isContainer || ancestor.containerWidth === void 0) {
|
|
1308
|
+
continue;
|
|
1309
|
+
}
|
|
1310
|
+
if (!normalizedName) {
|
|
1311
|
+
return ancestor;
|
|
1312
|
+
}
|
|
1313
|
+
if ((ancestor.containerNames ?? []).includes(normalizedName)) {
|
|
1314
|
+
return ancestor;
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
return void 0;
|
|
1318
|
+
}
|
|
1319
|
+
matchesContainerCondition(condition, containerWidth) {
|
|
1320
|
+
const normalized = condition.trim().replace(/^\(+|\)+$/g, "").trim().toLowerCase();
|
|
1321
|
+
if (!normalized) {
|
|
1322
|
+
return true;
|
|
1323
|
+
}
|
|
1324
|
+
if (normalized.startsWith("not ")) {
|
|
1325
|
+
return !this.matchesContainerCondition(normalized.slice(4), containerWidth);
|
|
1326
|
+
}
|
|
1327
|
+
const orClauses = this.splitConditionalClauses(normalized, "or");
|
|
1328
|
+
if (orClauses.length > 1) {
|
|
1329
|
+
return orClauses.some((clause) => this.matchesContainerCondition(clause, containerWidth));
|
|
1330
|
+
}
|
|
1331
|
+
const andClauses = this.splitConditionalClauses(normalized, "and");
|
|
1332
|
+
if (andClauses.length > 1) {
|
|
1333
|
+
return andClauses.every((clause) => this.matchesContainerCondition(clause, containerWidth));
|
|
1334
|
+
}
|
|
1335
|
+
if (normalized.startsWith("min-width:")) {
|
|
1336
|
+
const minWidth = this.parseMediaLength(normalized.slice("min-width:".length));
|
|
1337
|
+
return minWidth !== void 0 && containerWidth >= minWidth;
|
|
1338
|
+
}
|
|
1339
|
+
if (normalized.startsWith("max-width:")) {
|
|
1340
|
+
const maxWidth = this.parseMediaLength(normalized.slice("max-width:".length));
|
|
1341
|
+
return maxWidth !== void 0 && containerWidth <= maxWidth;
|
|
1342
|
+
}
|
|
1343
|
+
return false;
|
|
1344
|
+
}
|
|
1345
|
+
parseSimpleSelectorToken(token) {
|
|
1346
|
+
const trimmed = token.trim();
|
|
1347
|
+
if (!trimmed || /[*&]/.test(trimmed)) {
|
|
1348
|
+
return void 0;
|
|
1349
|
+
}
|
|
1350
|
+
let cursor = 0;
|
|
1351
|
+
let tagName;
|
|
1352
|
+
let idName;
|
|
1353
|
+
const classNames = [];
|
|
1354
|
+
const attributes = [];
|
|
1355
|
+
const pseudoClasses = [];
|
|
1356
|
+
const tagMatch = trimmed.slice(cursor).match(/^([_a-zA-Z][-_a-zA-Z0-9]*)/);
|
|
1357
|
+
if (tagMatch) {
|
|
1358
|
+
tagName = tagMatch[1].toLowerCase();
|
|
1359
|
+
cursor += tagMatch[0].length;
|
|
1360
|
+
}
|
|
1361
|
+
while (cursor < trimmed.length) {
|
|
1362
|
+
const char = trimmed[cursor];
|
|
1363
|
+
if (char === ".") {
|
|
1364
|
+
const classMatch = trimmed.slice(cursor).match(/^\.([_a-zA-Z][-_a-zA-Z0-9]*)/);
|
|
1365
|
+
if (!classMatch) {
|
|
1366
|
+
return void 0;
|
|
1367
|
+
}
|
|
1368
|
+
classNames.push(classMatch[1]);
|
|
1369
|
+
cursor += classMatch[0].length;
|
|
1370
|
+
continue;
|
|
1371
|
+
}
|
|
1372
|
+
if (char === "#") {
|
|
1373
|
+
const idMatch = trimmed.slice(cursor).match(/^#([_a-zA-Z][-_a-zA-Z0-9]*)/);
|
|
1374
|
+
if (!idMatch || idName) {
|
|
1375
|
+
return void 0;
|
|
1376
|
+
}
|
|
1377
|
+
idName = idMatch[1];
|
|
1378
|
+
cursor += idMatch[0].length;
|
|
1379
|
+
continue;
|
|
1380
|
+
}
|
|
1381
|
+
if (char === "[") {
|
|
1382
|
+
const endIndex = trimmed.indexOf("]", cursor + 1);
|
|
1383
|
+
if (endIndex === -1) {
|
|
1384
|
+
return void 0;
|
|
1385
|
+
}
|
|
1386
|
+
const rawAttribute = trimmed.slice(cursor + 1, endIndex).trim();
|
|
1387
|
+
const attrMatch = rawAttribute.match(/^([_a-zA-Z][-_a-zA-Z0-9]*)(?:\s*(=|~=|\^=|\$=|\*=)\s*(?:"([^"]*)"|'([^']*)'|([^\s"']+)))?$/);
|
|
1388
|
+
if (!attrMatch) {
|
|
1389
|
+
return void 0;
|
|
1390
|
+
}
|
|
1391
|
+
attributes.push({
|
|
1392
|
+
name: attrMatch[1].toLowerCase(),
|
|
1393
|
+
operator: attrMatch[2],
|
|
1394
|
+
value: attrMatch[3] ?? attrMatch[4] ?? attrMatch[5]
|
|
1395
|
+
});
|
|
1396
|
+
cursor = endIndex + 1;
|
|
1397
|
+
continue;
|
|
1398
|
+
}
|
|
1399
|
+
if (char === ":") {
|
|
1400
|
+
const pseudoToken = this.parsePseudoSelectorToken(trimmed, cursor);
|
|
1401
|
+
if (!pseudoToken) {
|
|
1402
|
+
return void 0;
|
|
1403
|
+
}
|
|
1404
|
+
pseudoClasses.push(pseudoToken.value);
|
|
1405
|
+
cursor = pseudoToken.nextIndex;
|
|
1406
|
+
continue;
|
|
1407
|
+
}
|
|
1408
|
+
return void 0;
|
|
1409
|
+
}
|
|
1410
|
+
if (!tagName && !idName && classNames.length === 0 && attributes.length === 0 && pseudoClasses.length === 0) {
|
|
1411
|
+
return void 0;
|
|
1412
|
+
}
|
|
1413
|
+
return { tagName, idName, classNames, attributes, pseudoClasses };
|
|
1414
|
+
}
|
|
1415
|
+
extractSupportedSelectorChains(selector) {
|
|
1416
|
+
const cached = this.parsedSelectorChainCache.get(selector);
|
|
1417
|
+
if (cached) {
|
|
1418
|
+
return cached;
|
|
1419
|
+
}
|
|
1420
|
+
const parsedChains = this.splitSelectorList(selector).map((segment) => segment.trim()).map((segment) => {
|
|
1421
|
+
const chain = [];
|
|
1422
|
+
let token = "";
|
|
1423
|
+
let combinator = "descendant";
|
|
1424
|
+
let invalid = false;
|
|
1425
|
+
let attributeDepth = 0;
|
|
1426
|
+
let parenthesisDepth = 0;
|
|
1427
|
+
let quoted;
|
|
1428
|
+
const flushToken = () => {
|
|
1429
|
+
const trimmedToken = token.trim();
|
|
1430
|
+
token = "";
|
|
1431
|
+
if (!trimmedToken || invalid) {
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
const parsed = this.parseSimpleSelectorToken(trimmedToken);
|
|
1435
|
+
if (!parsed) {
|
|
1436
|
+
invalid = true;
|
|
1437
|
+
return;
|
|
1438
|
+
}
|
|
1439
|
+
if (chain.length > 0) {
|
|
1440
|
+
parsed.combinator = combinator;
|
|
1441
|
+
}
|
|
1442
|
+
chain.push(parsed);
|
|
1443
|
+
combinator = "descendant";
|
|
1444
|
+
};
|
|
1445
|
+
for (let index = 0; index < segment.length; index++) {
|
|
1446
|
+
const char = segment[index];
|
|
1447
|
+
if (quoted) {
|
|
1448
|
+
token += char;
|
|
1449
|
+
if (char === quoted && segment[index - 1] !== "\\") {
|
|
1450
|
+
quoted = void 0;
|
|
1451
|
+
}
|
|
1452
|
+
continue;
|
|
1453
|
+
}
|
|
1454
|
+
if (char === '"' || char === "'") {
|
|
1455
|
+
quoted = char;
|
|
1456
|
+
token += char;
|
|
1457
|
+
continue;
|
|
1458
|
+
}
|
|
1459
|
+
if (char === "[") {
|
|
1460
|
+
attributeDepth += 1;
|
|
1461
|
+
token += char;
|
|
1462
|
+
continue;
|
|
1463
|
+
}
|
|
1464
|
+
if (char === "]") {
|
|
1465
|
+
if (attributeDepth > 0) {
|
|
1466
|
+
attributeDepth -= 1;
|
|
1467
|
+
}
|
|
1468
|
+
token += char;
|
|
1469
|
+
continue;
|
|
1470
|
+
}
|
|
1471
|
+
if (attributeDepth === 0 && char === "(") {
|
|
1472
|
+
parenthesisDepth += 1;
|
|
1473
|
+
token += char;
|
|
1474
|
+
continue;
|
|
1475
|
+
}
|
|
1476
|
+
if (attributeDepth === 0 && char === ")" && parenthesisDepth > 0) {
|
|
1477
|
+
parenthesisDepth -= 1;
|
|
1478
|
+
token += char;
|
|
1479
|
+
continue;
|
|
1480
|
+
}
|
|
1481
|
+
if (attributeDepth === 0 && parenthesisDepth === 0 && (char === ">" || char === "+" || char === "~")) {
|
|
1482
|
+
flushToken();
|
|
1483
|
+
if (invalid) break;
|
|
1484
|
+
combinator = char === ">" ? "child" : char === "+" ? "adjacent-sibling" : "general-sibling";
|
|
1485
|
+
continue;
|
|
1486
|
+
}
|
|
1487
|
+
if (attributeDepth === 0 && parenthesisDepth === 0 && /\s/.test(char)) {
|
|
1488
|
+
flushToken();
|
|
1489
|
+
if (invalid) break;
|
|
1490
|
+
continue;
|
|
1491
|
+
}
|
|
1492
|
+
token += char;
|
|
1493
|
+
}
|
|
1494
|
+
flushToken();
|
|
1495
|
+
if (invalid || chain.length === 0) {
|
|
1496
|
+
return void 0;
|
|
1497
|
+
}
|
|
1498
|
+
return chain.some((part) => Boolean(part.tagName) || Boolean(part.idName) || part.classNames.length > 0 || part.attributes.length > 0 || part.pseudoClasses.length > 0) ? chain : void 0;
|
|
1499
|
+
}).filter((segment) => Array.isArray(segment) && segment.length > 0);
|
|
1500
|
+
this.parsedSelectorChainCache.set(selector, parsedChains);
|
|
1501
|
+
return parsedChains;
|
|
1502
|
+
}
|
|
1503
|
+
matchesAttributeSelector(targetValue, selector) {
|
|
1504
|
+
if (selector.operator === void 0) {
|
|
1505
|
+
return targetValue !== void 0;
|
|
1506
|
+
}
|
|
1507
|
+
if (targetValue === void 0 || selector.value === void 0) {
|
|
1508
|
+
return false;
|
|
1509
|
+
}
|
|
1510
|
+
switch (selector.operator) {
|
|
1511
|
+
case "=":
|
|
1512
|
+
return targetValue === selector.value;
|
|
1513
|
+
case "~=":
|
|
1514
|
+
return targetValue.split(/\s+/).includes(selector.value);
|
|
1515
|
+
case "^=":
|
|
1516
|
+
return targetValue.startsWith(selector.value);
|
|
1517
|
+
case "$=":
|
|
1518
|
+
return targetValue.endsWith(selector.value);
|
|
1519
|
+
case "*=":
|
|
1520
|
+
return targetValue.includes(selector.value);
|
|
1521
|
+
default:
|
|
1522
|
+
return false;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
matchesSelectorPart(target, selector) {
|
|
1526
|
+
if (selector.tagName && target.tagName !== selector.tagName) {
|
|
1527
|
+
return false;
|
|
1528
|
+
}
|
|
1529
|
+
const attributes = target.attributes;
|
|
1530
|
+
if (selector.idName && attributes?.id !== selector.idName) {
|
|
1531
|
+
return false;
|
|
1532
|
+
}
|
|
1533
|
+
const classSet = new Set(target.classNames ?? []);
|
|
1534
|
+
if (!selector.classNames.every((className) => classSet.has(className))) {
|
|
1535
|
+
return false;
|
|
1536
|
+
}
|
|
1537
|
+
if (!selector.attributes.every((attribute) => this.matchesAttributeSelector(attributes?.[attribute.name], attribute))) {
|
|
1538
|
+
return false;
|
|
1539
|
+
}
|
|
1540
|
+
return selector.pseudoClasses.every((pseudoClass) => this.matchesPseudoClass(target, pseudoClass));
|
|
1541
|
+
}
|
|
1542
|
+
matchesPseudoClass(target, pseudoClass) {
|
|
1543
|
+
const rawPseudoClass = pseudoClass.trim();
|
|
1544
|
+
const normalized = rawPseudoClass.toLowerCase();
|
|
1545
|
+
const pseudoStates = new Set((target.pseudoStates ?? []).map((state) => state.trim().toLowerCase()));
|
|
1546
|
+
if (pseudoStates.has(normalized)) {
|
|
1547
|
+
return true;
|
|
1548
|
+
}
|
|
1549
|
+
const functionalMatch = rawPseudoClass.match(/^([_a-zA-Z][-_a-zA-Z0-9]*)(?:\((.*)\))?$/);
|
|
1550
|
+
const pseudoName = functionalMatch?.[1] ?? normalized;
|
|
1551
|
+
const pseudoArgument = functionalMatch?.[2]?.trim();
|
|
1552
|
+
const attributes = target.attributes;
|
|
1553
|
+
switch (pseudoName) {
|
|
1554
|
+
case "scope":
|
|
1555
|
+
return target.isScopeReference === true;
|
|
1556
|
+
case "checked":
|
|
1557
|
+
return attributes?.checked !== void 0 && attributes.checked !== "false";
|
|
1558
|
+
case "disabled":
|
|
1559
|
+
return attributes?.disabled !== void 0 && attributes.disabled !== "false";
|
|
1560
|
+
case "selected":
|
|
1561
|
+
return attributes?.selected !== void 0 && attributes.selected !== "false" || attributes?.["aria-current"] !== void 0;
|
|
1562
|
+
case "first-child":
|
|
1563
|
+
return target.childIndex === 1;
|
|
1564
|
+
case "last-child":
|
|
1565
|
+
return target.childIndex !== void 0 && target.siblingCount !== void 0 && target.childIndex === target.siblingCount;
|
|
1566
|
+
case "only-child":
|
|
1567
|
+
return target.childIndex !== void 0 && target.siblingCount !== void 0 && target.siblingCount === 1;
|
|
1568
|
+
case "first-of-type":
|
|
1569
|
+
return target.sameTypeIndex === 1;
|
|
1570
|
+
case "last-of-type":
|
|
1571
|
+
return target.sameTypeIndex !== void 0 && target.sameTypeCount !== void 0 && target.sameTypeIndex === target.sameTypeCount;
|
|
1572
|
+
case "only-of-type":
|
|
1573
|
+
return target.sameTypeIndex !== void 0 && target.sameTypeCount !== void 0 && target.sameTypeCount === 1;
|
|
1574
|
+
case "nth-child":
|
|
1575
|
+
return target.childIndex !== void 0 && typeof pseudoArgument === "string" && this.matchesNthChildExpression(pseudoArgument, target.childIndex);
|
|
1576
|
+
case "nth-last-child":
|
|
1577
|
+
return target.childIndex !== void 0 && target.siblingCount !== void 0 && typeof pseudoArgument === "string" && this.matchesNthChildExpression(pseudoArgument, target.siblingCount - target.childIndex + 1);
|
|
1578
|
+
case "nth-of-type":
|
|
1579
|
+
return target.sameTypeIndex !== void 0 && typeof pseudoArgument === "string" && this.matchesNthChildExpression(pseudoArgument, target.sameTypeIndex);
|
|
1580
|
+
case "nth-last-of-type":
|
|
1581
|
+
return target.sameTypeIndex !== void 0 && target.sameTypeCount !== void 0 && typeof pseudoArgument === "string" && this.matchesNthChildExpression(pseudoArgument, target.sameTypeCount - target.sameTypeIndex + 1);
|
|
1582
|
+
case "has":
|
|
1583
|
+
return typeof pseudoArgument === "string" && this.matchesHasPseudoClass(target, pseudoArgument);
|
|
1584
|
+
case "not": {
|
|
1585
|
+
if (!pseudoArgument) {
|
|
1586
|
+
return false;
|
|
1587
|
+
}
|
|
1588
|
+
const selectorArguments = this.splitSelectorList(pseudoArgument);
|
|
1589
|
+
if (selectorArguments.length === 0) {
|
|
1590
|
+
return false;
|
|
1591
|
+
}
|
|
1592
|
+
const parsedSelectors = [];
|
|
1593
|
+
for (const selectorArgument of selectorArguments) {
|
|
1594
|
+
const parsedSelector = this.parseSimpleSelectorToken(selectorArgument);
|
|
1595
|
+
if (!parsedSelector) {
|
|
1596
|
+
return false;
|
|
1597
|
+
}
|
|
1598
|
+
parsedSelectors.push(parsedSelector);
|
|
1599
|
+
}
|
|
1600
|
+
return parsedSelectors.every((selectorPart) => !this.matchesSelectorPart(target, selectorPart));
|
|
1601
|
+
}
|
|
1602
|
+
default:
|
|
1603
|
+
return false;
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
matchesNthChildExpression(expression, childIndex) {
|
|
1607
|
+
const normalized = expression.trim().toLowerCase().replace(/\s+/g, "");
|
|
1608
|
+
if (!normalized) {
|
|
1609
|
+
return false;
|
|
1610
|
+
}
|
|
1611
|
+
if (normalized === "odd") {
|
|
1612
|
+
return childIndex % 2 === 1;
|
|
1613
|
+
}
|
|
1614
|
+
if (normalized === "even") {
|
|
1615
|
+
return childIndex % 2 === 0;
|
|
1616
|
+
}
|
|
1617
|
+
if (/^[+-]?\d+$/.test(normalized)) {
|
|
1618
|
+
return childIndex === Number(normalized);
|
|
1619
|
+
}
|
|
1620
|
+
const patternMatch = normalized.match(/^([+-]?\d*)n(?:([+-]?\d+))?$/);
|
|
1621
|
+
if (!patternMatch) {
|
|
1622
|
+
return false;
|
|
1623
|
+
}
|
|
1624
|
+
const coefficientToken = patternMatch[1];
|
|
1625
|
+
const offsetToken = patternMatch[2];
|
|
1626
|
+
const coefficient = coefficientToken === "" || coefficientToken === "+" ? 1 : coefficientToken === "-" ? -1 : Number(coefficientToken);
|
|
1627
|
+
const offset = offsetToken !== void 0 ? Number(offsetToken) : 0;
|
|
1628
|
+
if (!Number.isFinite(coefficient) || !Number.isFinite(offset)) {
|
|
1629
|
+
return false;
|
|
1630
|
+
}
|
|
1631
|
+
if (coefficient === 0) {
|
|
1632
|
+
return childIndex === offset;
|
|
1633
|
+
}
|
|
1634
|
+
const delta = childIndex - offset;
|
|
1635
|
+
const step = Math.abs(coefficient);
|
|
1636
|
+
if (delta % step !== 0) {
|
|
1637
|
+
return false;
|
|
1638
|
+
}
|
|
1639
|
+
const n = delta / coefficient;
|
|
1640
|
+
return Number.isInteger(n) && n >= 0;
|
|
1641
|
+
}
|
|
1642
|
+
matchesHasPseudoClass(target, pseudoArgument) {
|
|
1643
|
+
const selectorArguments = this.splitSelectorList(pseudoArgument);
|
|
1644
|
+
if (selectorArguments.length === 0) {
|
|
1645
|
+
return false;
|
|
1646
|
+
}
|
|
1647
|
+
return selectorArguments.some((selectorArgument) => this.matchesRelativeHasSelector(target, selectorArgument));
|
|
1648
|
+
}
|
|
1649
|
+
matchesRelativeHasSelector(target, selectorArgument) {
|
|
1650
|
+
const trimmedSelector = selectorArgument.trim();
|
|
1651
|
+
if (!trimmedSelector) {
|
|
1652
|
+
return false;
|
|
1653
|
+
}
|
|
1654
|
+
const scopeRelativeSelector = this.toHasScopeRelativeSelector(trimmedSelector);
|
|
1655
|
+
if (!scopeRelativeSelector) {
|
|
1656
|
+
return false;
|
|
1657
|
+
}
|
|
1658
|
+
const selectorChains = this.extractSupportedSelectorChains(scopeRelativeSelector);
|
|
1659
|
+
if (selectorChains.length === 0) {
|
|
1660
|
+
return false;
|
|
1661
|
+
}
|
|
1662
|
+
const scopeTarget = this.normalizeTarget({ ...target, isScopeReference: true });
|
|
1663
|
+
if (trimmedSelector.startsWith("+") || trimmedSelector.startsWith("~")) {
|
|
1664
|
+
const scopedSiblings = this.buildScopedFollowingSiblingTargets(scopeTarget, target.nextSiblings ?? []);
|
|
1665
|
+
return selectorChains.some(
|
|
1666
|
+
(selectorChain) => scopedSiblings.some((sibling) => this.matchesSelectorChainInSubtree(sibling, [], selectorChain))
|
|
1667
|
+
);
|
|
1668
|
+
}
|
|
1669
|
+
return selectorChains.some(
|
|
1670
|
+
(selectorChain) => (target.children ?? []).some((child) => this.matchesSelectorChainInSubtree(child, [scopeTarget], selectorChain))
|
|
1671
|
+
);
|
|
1672
|
+
}
|
|
1673
|
+
toHasScopeRelativeSelector(selectorArgument) {
|
|
1674
|
+
const trimmedSelector = selectorArgument.trim();
|
|
1675
|
+
if (!trimmedSelector) {
|
|
1676
|
+
return void 0;
|
|
1677
|
+
}
|
|
1678
|
+
return trimmedSelector.startsWith(">") || trimmedSelector.startsWith("+") || trimmedSelector.startsWith("~") ? `:scope${trimmedSelector}` : `:scope ${trimmedSelector}`;
|
|
1679
|
+
}
|
|
1680
|
+
buildScopedFollowingSiblingTargets(scopeTarget, nextSiblings) {
|
|
1681
|
+
const scopedSiblings = [];
|
|
1682
|
+
for (const sibling of nextSiblings) {
|
|
1683
|
+
const scopedSibling = this.normalizeTarget({
|
|
1684
|
+
...sibling,
|
|
1685
|
+
previousSiblings: [scopeTarget, ...scopedSiblings]
|
|
1686
|
+
});
|
|
1687
|
+
scopedSiblings.push(scopedSibling);
|
|
1688
|
+
}
|
|
1689
|
+
return scopedSiblings;
|
|
1690
|
+
}
|
|
1691
|
+
matchesSelectorChainInSubtree(target, ancestors, chain) {
|
|
1692
|
+
if (this.matchesSelectorChain(target, ancestors, chain)) {
|
|
1693
|
+
return true;
|
|
1694
|
+
}
|
|
1695
|
+
const nextAncestors = [...ancestors, target];
|
|
1696
|
+
return (target.children ?? []).some((child) => this.matchesSelectorChainInSubtree(child, nextAncestors, chain));
|
|
1697
|
+
}
|
|
1698
|
+
matchesSelectorChain(target, ancestors, chain) {
|
|
1699
|
+
const initialCursor = {
|
|
1700
|
+
target,
|
|
1701
|
+
ancestorIndex: ancestors.length - 1,
|
|
1702
|
+
previousSiblings: Array.isArray(target.previousSiblings) ? target.previousSiblings : []
|
|
1703
|
+
};
|
|
1704
|
+
return this.matchesSelectorChainFromCursor(chain, chain.length - 1, ancestors, initialCursor);
|
|
1705
|
+
}
|
|
1706
|
+
getAncestorCursor(ancestors, ancestorIndex) {
|
|
1707
|
+
if (ancestorIndex < 0 || ancestorIndex >= ancestors.length) {
|
|
1708
|
+
return void 0;
|
|
1709
|
+
}
|
|
1710
|
+
const ancestor = ancestors[ancestorIndex];
|
|
1711
|
+
return {
|
|
1712
|
+
target: ancestor,
|
|
1713
|
+
ancestorIndex: ancestorIndex - 1,
|
|
1714
|
+
previousSiblings: Array.isArray(ancestor.previousSiblings) ? ancestor.previousSiblings : []
|
|
1715
|
+
};
|
|
1716
|
+
}
|
|
1717
|
+
matchesSelectorChainFromCursor(chain, chainIndex, ancestors, cursor) {
|
|
1718
|
+
if (!this.matchesSelectorPart(cursor.target, chain[chainIndex])) {
|
|
1719
|
+
return false;
|
|
1720
|
+
}
|
|
1721
|
+
if (chainIndex === 0) {
|
|
1722
|
+
return true;
|
|
1723
|
+
}
|
|
1724
|
+
const combinator = chain[chainIndex].combinator ?? "descendant";
|
|
1725
|
+
switch (combinator) {
|
|
1726
|
+
case "child": {
|
|
1727
|
+
const parentCursor = this.getAncestorCursor(ancestors, cursor.ancestorIndex);
|
|
1728
|
+
return parentCursor ? this.matchesSelectorChainFromCursor(chain, chainIndex - 1, ancestors, parentCursor) : false;
|
|
1729
|
+
}
|
|
1730
|
+
case "adjacent-sibling": {
|
|
1731
|
+
const siblingIndex = cursor.previousSiblings.length - 1;
|
|
1732
|
+
if (siblingIndex < 0) {
|
|
1733
|
+
return false;
|
|
1734
|
+
}
|
|
1735
|
+
return this.matchesSelectorChainFromCursor(chain, chainIndex - 1, ancestors, {
|
|
1736
|
+
target: cursor.previousSiblings[siblingIndex],
|
|
1737
|
+
ancestorIndex: cursor.ancestorIndex,
|
|
1738
|
+
previousSiblings: cursor.previousSiblings.slice(0, siblingIndex)
|
|
1739
|
+
});
|
|
1740
|
+
}
|
|
1741
|
+
case "general-sibling": {
|
|
1742
|
+
for (let siblingIndex = cursor.previousSiblings.length - 1; siblingIndex >= 0; siblingIndex--) {
|
|
1743
|
+
if (this.matchesSelectorChainFromCursor(chain, chainIndex - 1, ancestors, {
|
|
1744
|
+
target: cursor.previousSiblings[siblingIndex],
|
|
1745
|
+
ancestorIndex: cursor.ancestorIndex,
|
|
1746
|
+
previousSiblings: cursor.previousSiblings.slice(0, siblingIndex)
|
|
1747
|
+
})) {
|
|
1748
|
+
return true;
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
return false;
|
|
1752
|
+
}
|
|
1753
|
+
case "descendant":
|
|
1754
|
+
default: {
|
|
1755
|
+
for (let ancestorIndex = cursor.ancestorIndex; ancestorIndex >= 0; ancestorIndex--) {
|
|
1756
|
+
const ancestorCursor = this.getAncestorCursor(ancestors, ancestorIndex);
|
|
1757
|
+
if (ancestorCursor && this.matchesSelectorChainFromCursor(chain, chainIndex - 1, ancestors, ancestorCursor)) {
|
|
1758
|
+
return true;
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
return false;
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
parseMediaLength(value) {
|
|
1766
|
+
const match = value.trim().match(/^(-?\d+(?:\.\d+)?)(px|rem|em)?$/i);
|
|
1767
|
+
if (!match) {
|
|
1768
|
+
return void 0;
|
|
1769
|
+
}
|
|
1770
|
+
const numericValue = Number(match[1]);
|
|
1771
|
+
const unit = (match[2] ?? "px").toLowerCase();
|
|
1772
|
+
if (unit === "rem" || unit === "em") {
|
|
1773
|
+
return numericValue * 16;
|
|
1774
|
+
}
|
|
1775
|
+
return numericValue;
|
|
1776
|
+
}
|
|
1777
|
+
matchesMediaCondition(condition, options) {
|
|
1778
|
+
const normalized = condition.trim().replace(/^\(+|\)+$/g, "").trim().toLowerCase();
|
|
1779
|
+
if (!normalized) {
|
|
1780
|
+
return true;
|
|
1781
|
+
}
|
|
1782
|
+
if (normalized.startsWith("min-width:")) {
|
|
1783
|
+
const minWidth = this.parseMediaLength(normalized.slice("min-width:".length));
|
|
1784
|
+
return minWidth !== void 0 && options.viewportWidth !== void 0 && options.viewportWidth >= minWidth;
|
|
1785
|
+
}
|
|
1786
|
+
if (normalized.startsWith("max-width:")) {
|
|
1787
|
+
const maxWidth = this.parseMediaLength(normalized.slice("max-width:".length));
|
|
1788
|
+
return maxWidth !== void 0 && options.viewportWidth !== void 0 && options.viewportWidth <= maxWidth;
|
|
1789
|
+
}
|
|
1790
|
+
if (normalized === "prefers-color-scheme: dark") {
|
|
1791
|
+
return options.colorScheme === "dark";
|
|
1792
|
+
}
|
|
1793
|
+
if (normalized === "prefers-color-scheme: light") {
|
|
1794
|
+
return (options.colorScheme ?? "light") === "light";
|
|
1795
|
+
}
|
|
1796
|
+
if (normalized === "prefers-reduced-motion: reduce") {
|
|
1797
|
+
return options.reducedMotion === true;
|
|
1798
|
+
}
|
|
1799
|
+
return false;
|
|
1800
|
+
}
|
|
1801
|
+
matchesMediaRule(rule, options) {
|
|
1802
|
+
const mediaType = options.mediaType ?? "screen";
|
|
1803
|
+
if (rule.type && rule.type !== mediaType && rule.type !== "all") {
|
|
1804
|
+
return false;
|
|
1805
|
+
}
|
|
1806
|
+
if (!rule.condition.trim()) {
|
|
1807
|
+
return true;
|
|
1808
|
+
}
|
|
1809
|
+
return rule.condition.split(/\band\b/i).map((part) => part.trim()).filter(Boolean).every((part) => this.matchesMediaCondition(part, options));
|
|
1810
|
+
}
|
|
1811
|
+
getOrderedLayerNames() {
|
|
1812
|
+
const orderedLayerNames = [];
|
|
1813
|
+
for (const layerName of this._layerOrder) {
|
|
1814
|
+
const normalizedName = layerName.trim();
|
|
1815
|
+
if (normalizedName && !orderedLayerNames.includes(normalizedName)) {
|
|
1816
|
+
orderedLayerNames.push(normalizedName);
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
for (const layerRule of this.layerRules) {
|
|
1820
|
+
const normalizedName = layerRule.name.trim();
|
|
1821
|
+
if (normalizedName && !orderedLayerNames.includes(normalizedName)) {
|
|
1822
|
+
orderedLayerNames.push(normalizedName);
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
return orderedLayerNames;
|
|
1826
|
+
}
|
|
1827
|
+
resolveNativeStyles(target, ancestors = [], options = {}) {
|
|
1828
|
+
return this.withNativeTargetNormalizationCache(() => {
|
|
1829
|
+
const normalizedTarget = this.normalizeTarget(target);
|
|
1830
|
+
if (!normalizedTarget.tagName && (!normalizedTarget.classNames || normalizedTarget.classNames.length === 0)) {
|
|
1831
|
+
return {};
|
|
1832
|
+
}
|
|
1833
|
+
const normalizedAncestors = ancestors.map((ancestor) => this.normalizeTarget(ancestor));
|
|
1834
|
+
const variables = this.getVariables();
|
|
1835
|
+
const resolved = {};
|
|
1836
|
+
const applyRules = (rules) => {
|
|
1837
|
+
for (const rule of rules) {
|
|
1838
|
+
const selectorChains = this.extractSupportedSelectorChains(rule.selector);
|
|
1839
|
+
if (selectorChains.length === 0) {
|
|
1840
|
+
continue;
|
|
1841
|
+
}
|
|
1842
|
+
const matches = selectorChains.some((selectorChain) => this.matchesSelectorChain(normalizedTarget, normalizedAncestors, selectorChain));
|
|
1843
|
+
if (!matches) {
|
|
1844
|
+
continue;
|
|
1845
|
+
}
|
|
1846
|
+
for (const [property, value] of Object.entries(rule.styles)) {
|
|
1847
|
+
resolved[property] = typeof value === "string" ? this.resolveVariableReferences(value, variables) : value;
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
};
|
|
1851
|
+
for (const layerName of this.getOrderedLayerNames()) {
|
|
1852
|
+
for (const layerRule of this.layerRules) {
|
|
1853
|
+
if (layerRule.name.trim() === layerName) {
|
|
1854
|
+
applyRules(layerRule.rules);
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
applyRules(this.rules);
|
|
1859
|
+
for (const supportsRule of this.supportsRules) {
|
|
1860
|
+
if (this.matchesSupportsCondition(supportsRule.condition)) {
|
|
1861
|
+
applyRules(supportsRule.rules);
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
for (const containerRule of this.containerRules) {
|
|
1865
|
+
const matchingContainer = this.findMatchingContainerTarget(normalizedAncestors, containerRule.name);
|
|
1866
|
+
if (matchingContainer && this.matchesContainerCondition(containerRule.condition, matchingContainer.containerWidth)) {
|
|
1867
|
+
applyRules(containerRule.rules);
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
for (const mediaRule of this.mediaRules) {
|
|
1871
|
+
if (this.matchesMediaRule(mediaRule, options)) {
|
|
1872
|
+
applyRules(mediaRule.rules);
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
return resolved;
|
|
1876
|
+
});
|
|
1877
|
+
}
|
|
1878
|
+
resolveClassStyles(classNames) {
|
|
1879
|
+
return this.resolveNativeStyles({ classNames });
|
|
1880
|
+
}
|
|
1881
|
+
// Utility Methods
|
|
1882
|
+
toKebabCase(str) {
|
|
1883
|
+
return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
1884
|
+
}
|
|
1885
|
+
// Helper: Create and add rule (eliminates duplication in combinator selectors)
|
|
1886
|
+
createAndAddRule(selector, styles2, type = "custom") {
|
|
1887
|
+
const rule = { selector, styles: styles2, type };
|
|
1888
|
+
this.rules.push(rule);
|
|
1889
|
+
return rule;
|
|
1890
|
+
}
|
|
1891
|
+
// Helper: Convert rules object to CSSRule array (eliminates duplication in media/container/supports/layer)
|
|
1892
|
+
rulesToCSSRules(rules) {
|
|
1893
|
+
return Object.entries(rules).map(([selector, styles2]) => ({
|
|
1894
|
+
selector,
|
|
1895
|
+
styles: styles2,
|
|
1896
|
+
type: "custom"
|
|
1897
|
+
}));
|
|
1898
|
+
}
|
|
1899
|
+
// Helper: Render rules with indentation (eliminates duplication in render methods)
|
|
1900
|
+
renderRulesWithIndent(rules, indent = " ") {
|
|
1901
|
+
return rules.map((rule) => this.renderRule(rule, indent)).join("\n");
|
|
1902
|
+
}
|
|
1903
|
+
stylesToString(styles2, indent = " ") {
|
|
1904
|
+
return Object.entries(styles2).map(([prop, value]) => {
|
|
1905
|
+
const cssValue = typeof value === "object" && value !== null && "name" in value ? `var(${value.name})` : value;
|
|
1906
|
+
return `${indent}${this.toKebabCase(prop)}: ${cssValue};`;
|
|
1907
|
+
}).join("\n");
|
|
1908
|
+
}
|
|
1909
|
+
renderRule(rule, indent = "") {
|
|
1910
|
+
let css = `${indent}${rule.selector} {
|
|
1911
|
+
${this.stylesToString(rule.styles, indent + " ")}
|
|
1912
|
+
`;
|
|
1913
|
+
if (rule.nested && rule.nested.length > 0) {
|
|
1914
|
+
for (const nestedRule of rule.nested) {
|
|
1915
|
+
const nestedSelector = nestedRule.selector.startsWith("&") ? nestedRule.selector.replace(/&/g, rule.selector) : `${rule.selector} ${nestedRule.selector}`;
|
|
1916
|
+
css += `
|
|
1917
|
+
${indent}${nestedSelector} {
|
|
1918
|
+
${this.stylesToString(nestedRule.styles, indent + " ")}
|
|
1919
|
+
${indent}}
|
|
1920
|
+
`;
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
css += `${indent}}`;
|
|
1924
|
+
return css;
|
|
1925
|
+
}
|
|
1926
|
+
renderMediaRule(media) {
|
|
1927
|
+
const condition = media.type && media.condition ? `${media.type} and (${media.condition})` : media.type ? media.type : `(${media.condition})`;
|
|
1928
|
+
return `@media ${condition} {
|
|
1929
|
+
${this.renderRulesWithIndent(media.rules)}
|
|
1930
|
+
}`;
|
|
1931
|
+
}
|
|
1932
|
+
renderKeyframes(kf) {
|
|
1933
|
+
let css = `@keyframes ${kf.name} {
|
|
1934
|
+
`;
|
|
1935
|
+
for (const step of kf.steps) {
|
|
1936
|
+
css += ` ${step.step} {
|
|
1937
|
+
${this.stylesToString(step.styles, " ")}
|
|
1938
|
+
}
|
|
1939
|
+
`;
|
|
1940
|
+
}
|
|
1941
|
+
css += "}";
|
|
1942
|
+
return css;
|
|
1943
|
+
}
|
|
1944
|
+
renderFontFace(ff) {
|
|
1945
|
+
let css = "@font-face {\n";
|
|
1946
|
+
css += ` font-family: "${ff.fontFamily}";
|
|
1947
|
+
`;
|
|
1948
|
+
css += ` src: ${ff.src};
|
|
1949
|
+
`;
|
|
1950
|
+
if (ff.fontWeight) css += ` font-weight: ${ff.fontWeight};
|
|
1951
|
+
`;
|
|
1952
|
+
if (ff.fontStyle) css += ` font-style: ${ff.fontStyle};
|
|
1953
|
+
`;
|
|
1954
|
+
if (ff.fontDisplay) css += ` font-display: ${ff.fontDisplay};
|
|
1955
|
+
`;
|
|
1956
|
+
if (ff.unicodeRange) css += ` unicode-range: ${ff.unicodeRange};
|
|
1957
|
+
`;
|
|
1958
|
+
css += "}";
|
|
1959
|
+
return css;
|
|
1960
|
+
}
|
|
1961
|
+
renderContainerRule(container2) {
|
|
1962
|
+
const nameStr = container2.name ? `${container2.name} ` : "";
|
|
1963
|
+
return `@container ${nameStr}(${container2.condition}) {
|
|
1964
|
+
${this.renderRulesWithIndent(container2.rules)}
|
|
1965
|
+
}`;
|
|
1966
|
+
}
|
|
1967
|
+
renderSupportsRule(supports) {
|
|
1968
|
+
return `@supports (${supports.condition}) {
|
|
1969
|
+
${this.renderRulesWithIndent(supports.rules)}
|
|
1970
|
+
}`;
|
|
1971
|
+
}
|
|
1972
|
+
renderLayerRule(layer2) {
|
|
1973
|
+
return `@layer ${layer2.name} {
|
|
1974
|
+
${this.renderRulesWithIndent(layer2.rules)}
|
|
1975
|
+
}`;
|
|
1976
|
+
}
|
|
1977
|
+
// Render Output
|
|
1978
|
+
render(...additionalRules) {
|
|
1979
|
+
const parts = [];
|
|
1980
|
+
if (this.imports.length > 0) {
|
|
1981
|
+
parts.push(this.imports.join("\n"));
|
|
1982
|
+
}
|
|
1983
|
+
if (this._layerOrder.length > 0) {
|
|
1984
|
+
parts.push(`@layer ${this._layerOrder.join(", ")};`);
|
|
1985
|
+
}
|
|
1986
|
+
if (this.variables.length > 0) {
|
|
1987
|
+
const varDeclarations = this.variables.map((v) => ` ${v.name}: ${v.value};`).join("\n");
|
|
1988
|
+
parts.push(`:root {
|
|
1989
|
+
${varDeclarations}
|
|
1990
|
+
}`);
|
|
1991
|
+
}
|
|
1992
|
+
for (const ff of this.fontFaces) {
|
|
1993
|
+
parts.push(this.renderFontFace(ff));
|
|
1994
|
+
}
|
|
1995
|
+
for (const kf of this.keyframes) {
|
|
1996
|
+
parts.push(this.renderKeyframes(kf));
|
|
1997
|
+
}
|
|
1998
|
+
const allRules = [...this.rules];
|
|
1999
|
+
const allMediaRules = [...this.mediaRules];
|
|
2000
|
+
const allKeyframes = [];
|
|
2001
|
+
const allContainerRules = [...this.containerRules];
|
|
2002
|
+
const allSupportsRules = [...this.supportsRules];
|
|
2003
|
+
const allLayerRules = [...this.layerRules];
|
|
2004
|
+
for (const item of additionalRules) {
|
|
2005
|
+
if (!item) continue;
|
|
2006
|
+
if (Array.isArray(item)) {
|
|
2007
|
+
allRules.push(...item);
|
|
2008
|
+
} else if ("condition" in item && "rules" in item && !("name" in item && "steps" in item)) {
|
|
2009
|
+
if ("type" in item) {
|
|
2010
|
+
allMediaRules.push(item);
|
|
2011
|
+
} else if ("name" in item && typeof item.name === "string") {
|
|
2012
|
+
allContainerRules.push(item);
|
|
2013
|
+
} else {
|
|
2014
|
+
allSupportsRules.push(item);
|
|
2015
|
+
}
|
|
2016
|
+
} else if ("name" in item && "steps" in item) {
|
|
2017
|
+
allKeyframes.push(item);
|
|
2018
|
+
} else if ("name" in item && "rules" in item) {
|
|
2019
|
+
allLayerRules.push(item);
|
|
2020
|
+
} else {
|
|
2021
|
+
allRules.push(item);
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
for (const kf of allKeyframes) {
|
|
2025
|
+
parts.push(this.renderKeyframes(kf));
|
|
2026
|
+
}
|
|
2027
|
+
for (const layer2 of allLayerRules) {
|
|
2028
|
+
parts.push(this.renderLayerRule(layer2));
|
|
2029
|
+
}
|
|
2030
|
+
for (const rule of allRules) {
|
|
2031
|
+
parts.push(this.renderRule(rule));
|
|
2032
|
+
}
|
|
2033
|
+
for (const supports of allSupportsRules) {
|
|
2034
|
+
parts.push(this.renderSupportsRule(supports));
|
|
2035
|
+
}
|
|
2036
|
+
for (const container2 of allContainerRules) {
|
|
2037
|
+
parts.push(this.renderContainerRule(container2));
|
|
2038
|
+
}
|
|
2039
|
+
for (const media of allMediaRules) {
|
|
2040
|
+
parts.push(this.renderMediaRule(media));
|
|
2041
|
+
}
|
|
2042
|
+
return parts.join("\n\n");
|
|
2043
|
+
}
|
|
2044
|
+
inject(styleId) {
|
|
2045
|
+
const css = this.render();
|
|
2046
|
+
const style = document.createElement("style");
|
|
2047
|
+
if (styleId) style.id = styleId;
|
|
2048
|
+
style.textContent = css;
|
|
2049
|
+
document.head.appendChild(style);
|
|
2050
|
+
return style;
|
|
2051
|
+
}
|
|
2052
|
+
clear() {
|
|
2053
|
+
this.variables.length = 0;
|
|
2054
|
+
this.rules.length = 0;
|
|
2055
|
+
this.mediaRules.length = 0;
|
|
2056
|
+
this.keyframes.length = 0;
|
|
2057
|
+
this.fontFaces.length = 0;
|
|
2058
|
+
this.imports.length = 0;
|
|
2059
|
+
this.containerRules.length = 0;
|
|
2060
|
+
this.supportsRules.length = 0;
|
|
2061
|
+
this.layerRules.length = 0;
|
|
2062
|
+
this._layerOrder.length = 0;
|
|
2063
|
+
}
|
|
2064
|
+
};
|
|
2065
|
+
var styles = new CreateStyle(getSharedStyleStore());
|
|
2066
|
+
var {
|
|
2067
|
+
addVar,
|
|
2068
|
+
var: getVar,
|
|
2069
|
+
addTag,
|
|
2070
|
+
addClass,
|
|
2071
|
+
addId,
|
|
2072
|
+
addPseudoClass,
|
|
2073
|
+
addPseudoElement,
|
|
2074
|
+
addAttribute,
|
|
2075
|
+
attrEquals,
|
|
2076
|
+
attrContainsWord,
|
|
2077
|
+
attrStartsWith,
|
|
2078
|
+
attrEndsWith,
|
|
2079
|
+
attrContains,
|
|
2080
|
+
descendant,
|
|
2081
|
+
child: childStyle,
|
|
2082
|
+
adjacentSibling,
|
|
2083
|
+
generalSibling,
|
|
2084
|
+
multiple: multipleStyle,
|
|
2085
|
+
addName,
|
|
2086
|
+
nesting,
|
|
2087
|
+
keyframe,
|
|
2088
|
+
keyframeFromTo,
|
|
2089
|
+
fontFace,
|
|
2090
|
+
import: importStyle,
|
|
2091
|
+
media: mediaStyle,
|
|
2092
|
+
mediaScreen,
|
|
2093
|
+
mediaPrint,
|
|
2094
|
+
mediaMinWidth,
|
|
2095
|
+
mediaMaxWidth,
|
|
2096
|
+
mediaDark,
|
|
2097
|
+
mediaLight,
|
|
2098
|
+
mediaReducedMotion,
|
|
2099
|
+
container,
|
|
2100
|
+
addContainer,
|
|
2101
|
+
supports: supportsStyle,
|
|
2102
|
+
layerOrder,
|
|
2103
|
+
layer,
|
|
2104
|
+
add: addStyle,
|
|
2105
|
+
important,
|
|
2106
|
+
getVariables: getStyleVariables,
|
|
2107
|
+
resolveClassStyles,
|
|
2108
|
+
render: renderStyle,
|
|
2109
|
+
inject: injectStyle,
|
|
2110
|
+
clear: clearStyle
|
|
2111
|
+
} = styles;
|
|
2112
|
+
|
|
2113
|
+
// src/desktop-auto-render.ts
|
|
2114
|
+
var DESKTOP_RENDER_TRACKED_KEY = "__ELIT_DESKTOP_RENDER_TRACKED__";
|
|
2115
|
+
var DESKTOP_WINDOW_CREATED_KEY = "__ELIT_DESKTOP_WINDOW_CREATED__";
|
|
2116
|
+
var DESKTOP_MESSAGE_HANDLER_KEY = "__ELIT_DESKTOP_MESSAGE_HANDLER__";
|
|
2117
|
+
function getDesktopAutoRenderGlobals() {
|
|
2118
|
+
return globalThis;
|
|
2119
|
+
}
|
|
2120
|
+
function escapeHtml(text) {
|
|
2121
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
2122
|
+
}
|
|
2123
|
+
function escapeStyleText(css) {
|
|
2124
|
+
return css.replace(/<\/style/gi, "<\\/style");
|
|
2125
|
+
}
|
|
2126
|
+
function buildDesktopAutoHtml(options) {
|
|
2127
|
+
const styleTag = options.css ? ` <style>${escapeStyleText(options.css)}</style>` : "";
|
|
2128
|
+
return `<!doctype html>
|
|
2129
|
+
<html lang="en">
|
|
2130
|
+
<head>
|
|
2131
|
+
<meta charset="utf-8" />
|
|
2132
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
2133
|
+
<title>${escapeHtml(options.title)}</title>
|
|
2134
|
+
${styleTag}
|
|
2135
|
+
</head>
|
|
2136
|
+
<body>
|
|
2137
|
+
${options.markup}
|
|
2138
|
+
<script>
|
|
2139
|
+
window.addEventListener('DOMContentLoaded', () => {
|
|
2140
|
+
document.querySelectorAll('[data-desktop-message], [data-elit-action], [data-elit-route]').forEach((element) => {
|
|
2141
|
+
element.addEventListener('click', (event) => {
|
|
2142
|
+
const action = element.getAttribute('data-elit-action');
|
|
2143
|
+
const route = element.getAttribute('data-elit-route');
|
|
2144
|
+
const payload = element.getAttribute('data-elit-payload');
|
|
2145
|
+
const desktopMessage = element.getAttribute('data-desktop-message');
|
|
2146
|
+
|
|
2147
|
+
if (action || route || payload || desktopMessage) {
|
|
2148
|
+
event.preventDefault();
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
if (action || route || payload) {
|
|
2152
|
+
window.ipc?.postMessage(JSON.stringify({ type: 'bridge', action, route, payload }));
|
|
2153
|
+
return;
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
if (desktopMessage) {
|
|
2157
|
+
window.ipc?.postMessage(desktopMessage);
|
|
2158
|
+
}
|
|
2159
|
+
});
|
|
2160
|
+
});
|
|
2161
|
+
|
|
2162
|
+
window.ipc?.postMessage('desktop:ready');
|
|
2163
|
+
});
|
|
2164
|
+
</script>
|
|
2165
|
+
</body>
|
|
2166
|
+
</html>`;
|
|
2167
|
+
}
|
|
2168
|
+
function resolveDesktopBridgePayloadRoute(payload) {
|
|
2169
|
+
if (!payload) {
|
|
2170
|
+
return void 0;
|
|
2171
|
+
}
|
|
2172
|
+
try {
|
|
2173
|
+
const parsed = JSON.parse(payload);
|
|
2174
|
+
if (typeof parsed === "string" && parsed.trim()) {
|
|
2175
|
+
return parsed.trim();
|
|
2176
|
+
}
|
|
2177
|
+
if (parsed && typeof parsed === "object" && "route" in parsed && typeof parsed.route === "string" && parsed.route.trim()) {
|
|
2178
|
+
return parsed.route.trim();
|
|
2179
|
+
}
|
|
2180
|
+
} catch {
|
|
2181
|
+
}
|
|
2182
|
+
return void 0;
|
|
2183
|
+
}
|
|
2184
|
+
function installDesktopRenderTracking() {
|
|
2185
|
+
const globalScope = getDesktopAutoRenderGlobals();
|
|
2186
|
+
globalScope[DESKTOP_WINDOW_CREATED_KEY] = false;
|
|
2187
|
+
if (globalScope[DESKTOP_RENDER_TRACKED_KEY] || typeof globalScope.createWindow !== "function") {
|
|
2188
|
+
return;
|
|
2189
|
+
}
|
|
2190
|
+
const originalCreateWindow = globalScope.createWindow.bind(globalScope);
|
|
2191
|
+
globalScope.createWindow = (options) => {
|
|
2192
|
+
globalScope[DESKTOP_WINDOW_CREATED_KEY] = true;
|
|
2193
|
+
return originalCreateWindow(options);
|
|
2194
|
+
};
|
|
2195
|
+
globalScope[DESKTOP_RENDER_TRACKED_KEY] = true;
|
|
2196
|
+
}
|
|
2197
|
+
function installDesktopMessageHandler(options) {
|
|
2198
|
+
const globalScope = getDesktopAutoRenderGlobals();
|
|
2199
|
+
let routeHistory = [];
|
|
2200
|
+
let routeIndex = -1;
|
|
2201
|
+
const applyResolvedTitle = (suffix) => {
|
|
2202
|
+
if (suffix) {
|
|
2203
|
+
globalScope.windowSetTitle?.(`${options.title} - ${suffix}`);
|
|
2204
|
+
return;
|
|
2205
|
+
}
|
|
2206
|
+
const activeRoute = routeIndex >= 0 ? routeHistory[routeIndex] : void 0;
|
|
2207
|
+
globalScope.windowSetTitle?.(activeRoute ? `${options.title} - ${activeRoute}` : options.title);
|
|
2208
|
+
};
|
|
2209
|
+
const navigateToRoute = (route) => {
|
|
2210
|
+
const normalizedRoute = route.trim();
|
|
2211
|
+
if (!normalizedRoute) {
|
|
2212
|
+
return;
|
|
2213
|
+
}
|
|
2214
|
+
if (routeIndex < routeHistory.length - 1) {
|
|
2215
|
+
routeHistory = routeHistory.slice(0, routeIndex + 1);
|
|
2216
|
+
}
|
|
2217
|
+
routeHistory.push(normalizedRoute);
|
|
2218
|
+
routeIndex = routeHistory.length - 1;
|
|
2219
|
+
applyResolvedTitle();
|
|
2220
|
+
};
|
|
2221
|
+
const navigateBack = () => {
|
|
2222
|
+
if (routeIndex > 0) {
|
|
2223
|
+
routeIndex -= 1;
|
|
2224
|
+
} else {
|
|
2225
|
+
routeIndex = -1;
|
|
2226
|
+
}
|
|
2227
|
+
applyResolvedTitle();
|
|
2228
|
+
};
|
|
2229
|
+
const navigateForward = () => {
|
|
2230
|
+
if (routeIndex + 1 >= routeHistory.length) {
|
|
2231
|
+
return;
|
|
2232
|
+
}
|
|
2233
|
+
routeIndex += 1;
|
|
2234
|
+
applyResolvedTitle();
|
|
2235
|
+
};
|
|
2236
|
+
const clearRoute = () => {
|
|
2237
|
+
routeIndex = -1;
|
|
2238
|
+
applyResolvedTitle();
|
|
2239
|
+
};
|
|
2240
|
+
if (globalScope[DESKTOP_MESSAGE_HANDLER_KEY] || typeof globalScope.onMessage !== "function") {
|
|
2241
|
+
return;
|
|
2242
|
+
}
|
|
2243
|
+
globalScope.onMessage((message) => {
|
|
2244
|
+
try {
|
|
2245
|
+
const bridgeMessage = JSON.parse(message);
|
|
2246
|
+
if (bridgeMessage.type === "bridge") {
|
|
2247
|
+
if (bridgeMessage.action === "desktop:ping") {
|
|
2248
|
+
applyResolvedTitle("IPC OK");
|
|
2249
|
+
return;
|
|
2250
|
+
}
|
|
2251
|
+
if (bridgeMessage.action === "desktop:quit") {
|
|
2252
|
+
globalScope.windowQuit?.();
|
|
2253
|
+
return;
|
|
2254
|
+
}
|
|
2255
|
+
if (bridgeMessage.action === "desktop:back") {
|
|
2256
|
+
navigateBack();
|
|
2257
|
+
return;
|
|
2258
|
+
}
|
|
2259
|
+
if (bridgeMessage.action === "desktop:forward") {
|
|
2260
|
+
navigateForward();
|
|
2261
|
+
return;
|
|
2262
|
+
}
|
|
2263
|
+
if (bridgeMessage.action === "desktop:clear-route") {
|
|
2264
|
+
clearRoute();
|
|
2265
|
+
return;
|
|
2266
|
+
}
|
|
2267
|
+
const nextRoute = typeof bridgeMessage.route === "string" && bridgeMessage.route.trim() ? bridgeMessage.route.trim() : bridgeMessage.action === "desktop:navigate" ? resolveDesktopBridgePayloadRoute(bridgeMessage.payload) : void 0;
|
|
2268
|
+
if (nextRoute) {
|
|
2269
|
+
navigateToRoute(nextRoute);
|
|
2270
|
+
return;
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
} catch {
|
|
2274
|
+
}
|
|
2275
|
+
if (message === "desktop:ready") {
|
|
2276
|
+
applyResolvedTitle();
|
|
2277
|
+
if (options.autoClose) {
|
|
2278
|
+
globalScope.windowQuit?.();
|
|
2279
|
+
}
|
|
2280
|
+
return;
|
|
2281
|
+
}
|
|
2282
|
+
if (message === "desktop:ping") {
|
|
2283
|
+
applyResolvedTitle("IPC OK");
|
|
2284
|
+
return;
|
|
2285
|
+
}
|
|
2286
|
+
if (message === "desktop:quit") {
|
|
2287
|
+
globalScope.windowQuit?.();
|
|
2288
|
+
return;
|
|
2289
|
+
}
|
|
2290
|
+
if (message === "desktop:back") {
|
|
2291
|
+
navigateBack();
|
|
2292
|
+
return;
|
|
2293
|
+
}
|
|
2294
|
+
if (message === "desktop:forward") {
|
|
2295
|
+
navigateForward();
|
|
2296
|
+
return;
|
|
2297
|
+
}
|
|
2298
|
+
if (message === "desktop:clear-route") {
|
|
2299
|
+
clearRoute();
|
|
2300
|
+
}
|
|
2301
|
+
});
|
|
2302
|
+
globalScope[DESKTOP_MESSAGE_HANDLER_KEY] = true;
|
|
2303
|
+
}
|
|
2304
|
+
function completeDesktopAutoRender(options = {}) {
|
|
2305
|
+
installDesktopRenderTracking();
|
|
2306
|
+
const globalScope = getDesktopAutoRenderGlobals();
|
|
2307
|
+
const capturedRender = getCapturedRenderedVNode();
|
|
2308
|
+
if (!capturedRender || capturedRender.target !== "desktop") {
|
|
2309
|
+
return;
|
|
2310
|
+
}
|
|
2311
|
+
if (globalScope[DESKTOP_WINDOW_CREATED_KEY] || typeof globalScope.createWindow !== "function") {
|
|
2312
|
+
return;
|
|
2313
|
+
}
|
|
2314
|
+
const resolvedOptions = {
|
|
2315
|
+
title: "Elit Desktop",
|
|
2316
|
+
width: 1080,
|
|
2317
|
+
height: 720,
|
|
2318
|
+
center: true,
|
|
2319
|
+
autoClose: false,
|
|
2320
|
+
...options,
|
|
2321
|
+
...getDesktopRenderOptions()
|
|
2322
|
+
};
|
|
2323
|
+
installDesktopMessageHandler({
|
|
2324
|
+
title: resolvedOptions.title,
|
|
2325
|
+
autoClose: resolvedOptions.autoClose
|
|
2326
|
+
});
|
|
2327
|
+
globalScope.createWindow({
|
|
2328
|
+
title: resolvedOptions.title,
|
|
2329
|
+
width: resolvedOptions.width,
|
|
2330
|
+
height: resolvedOptions.height,
|
|
2331
|
+
center: resolvedOptions.center,
|
|
2332
|
+
icon: resolvedOptions.icon,
|
|
2333
|
+
html: buildDesktopAutoHtml({
|
|
2334
|
+
css: styles.render(),
|
|
2335
|
+
markup: renderToString(capturedRender.vNode),
|
|
2336
|
+
title: resolvedOptions.title
|
|
2337
|
+
})
|
|
2338
|
+
});
|
|
2339
|
+
clearCapturedRenderedVNode();
|
|
2340
|
+
}
|
|
2341
|
+
})();
|