@lvce-editor/preview-worker 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/previewWorkerMain.js +400 -209
- package/package.json +1 -1
|
@@ -514,7 +514,7 @@ const IpcParentWithMessagePort$1 = {
|
|
|
514
514
|
|
|
515
515
|
const Two$1 = '2.0';
|
|
516
516
|
const callbacks$1 = Object.create(null);
|
|
517
|
-
const get$
|
|
517
|
+
const get$3 = id => {
|
|
518
518
|
return callbacks$1[id];
|
|
519
519
|
};
|
|
520
520
|
const remove$1 = id => {
|
|
@@ -663,7 +663,7 @@ const warn = (...args) => {
|
|
|
663
663
|
console.warn(...args);
|
|
664
664
|
};
|
|
665
665
|
const resolve = (id, response) => {
|
|
666
|
-
const fn = get$
|
|
666
|
+
const fn = get$3(id);
|
|
667
667
|
if (!fn) {
|
|
668
668
|
console.log(response);
|
|
669
669
|
warn(`callback ${id} may already be disposed`);
|
|
@@ -1131,8 +1131,6 @@ const Style = 72;
|
|
|
1131
1131
|
const Html = 73;
|
|
1132
1132
|
const Reference = 100;
|
|
1133
1133
|
|
|
1134
|
-
const TargetName = 'event.target.name';
|
|
1135
|
-
|
|
1136
1134
|
const EditorWorker = 99;
|
|
1137
1135
|
const RendererWorker = 1;
|
|
1138
1136
|
|
|
@@ -1140,10 +1138,10 @@ const SetDom2 = 'Viewlet.setDom2';
|
|
|
1140
1138
|
const SetPatches = 'Viewlet.setPatches';
|
|
1141
1139
|
|
|
1142
1140
|
const rpcs = Object.create(null);
|
|
1143
|
-
const set$
|
|
1141
|
+
const set$4 = (id, rpc) => {
|
|
1144
1142
|
rpcs[id] = rpc;
|
|
1145
1143
|
};
|
|
1146
|
-
const get$
|
|
1144
|
+
const get$2 = id => {
|
|
1147
1145
|
return rpcs[id];
|
|
1148
1146
|
};
|
|
1149
1147
|
const remove = id => {
|
|
@@ -1154,18 +1152,18 @@ const remove = id => {
|
|
|
1154
1152
|
const create$2 = rpcId => {
|
|
1155
1153
|
return {
|
|
1156
1154
|
async dispose() {
|
|
1157
|
-
const rpc = get$
|
|
1155
|
+
const rpc = get$2(rpcId);
|
|
1158
1156
|
await rpc.dispose();
|
|
1159
1157
|
},
|
|
1160
1158
|
// @ts-ignore
|
|
1161
1159
|
invoke(method, ...params) {
|
|
1162
|
-
const rpc = get$
|
|
1160
|
+
const rpc = get$2(rpcId);
|
|
1163
1161
|
// @ts-ignore
|
|
1164
1162
|
return rpc.invoke(method, ...params);
|
|
1165
1163
|
},
|
|
1166
1164
|
// @ts-ignore
|
|
1167
1165
|
invokeAndTransfer(method, ...params) {
|
|
1168
|
-
const rpc = get$
|
|
1166
|
+
const rpc = get$2(rpcId);
|
|
1169
1167
|
// @ts-ignore
|
|
1170
1168
|
return rpc.invokeAndTransfer(method, ...params);
|
|
1171
1169
|
},
|
|
@@ -1173,7 +1171,7 @@ const create$2 = rpcId => {
|
|
|
1173
1171
|
const mockRpc = createMockRpc({
|
|
1174
1172
|
commandMap
|
|
1175
1173
|
});
|
|
1176
|
-
set$
|
|
1174
|
+
set$4(rpcId, mockRpc);
|
|
1177
1175
|
// @ts-ignore
|
|
1178
1176
|
mockRpc[Symbol.dispose] = () => {
|
|
1179
1177
|
remove(rpcId);
|
|
@@ -1182,20 +1180,20 @@ const create$2 = rpcId => {
|
|
|
1182
1180
|
return mockRpc;
|
|
1183
1181
|
},
|
|
1184
1182
|
set(rpc) {
|
|
1185
|
-
set$
|
|
1183
|
+
set$4(rpcId, rpc);
|
|
1186
1184
|
}
|
|
1187
1185
|
};
|
|
1188
1186
|
};
|
|
1189
1187
|
|
|
1190
1188
|
const {
|
|
1191
1189
|
invoke: invoke$1,
|
|
1192
|
-
set: set$
|
|
1190
|
+
set: set$3
|
|
1193
1191
|
} = create$2(EditorWorker);
|
|
1194
1192
|
|
|
1195
1193
|
const {
|
|
1196
1194
|
invoke,
|
|
1197
1195
|
invokeAndTransfer,
|
|
1198
|
-
set: set$
|
|
1196
|
+
set: set$2
|
|
1199
1197
|
} = create$2(RendererWorker);
|
|
1200
1198
|
const sendMessagePortToEditorWorker = async (port, rpcId) => {
|
|
1201
1199
|
const command = 'HandleMessagePort.handleMessagePort';
|
|
@@ -1295,11 +1293,11 @@ const terminate = () => {
|
|
|
1295
1293
|
};
|
|
1296
1294
|
|
|
1297
1295
|
const {
|
|
1298
|
-
get,
|
|
1296
|
+
get: get$1,
|
|
1299
1297
|
getCommandIds,
|
|
1300
1298
|
getKeys: getKeys$1,
|
|
1301
1299
|
registerCommands,
|
|
1302
|
-
set,
|
|
1300
|
+
set: set$1,
|
|
1303
1301
|
wrapCommand,
|
|
1304
1302
|
wrapGetter
|
|
1305
1303
|
} = create$1();
|
|
@@ -1320,7 +1318,7 @@ const create = (uid, uri, x, y, width, height, platform, assetDir) => {
|
|
|
1320
1318
|
uri,
|
|
1321
1319
|
warningCount: 0
|
|
1322
1320
|
};
|
|
1323
|
-
set(uid, state, state);
|
|
1321
|
+
set$1(uid, state, state);
|
|
1324
1322
|
};
|
|
1325
1323
|
|
|
1326
1324
|
const iEqual = (oldState, newState) => {
|
|
@@ -1361,11 +1359,73 @@ const diff2 = uid => {
|
|
|
1361
1359
|
const {
|
|
1362
1360
|
newState,
|
|
1363
1361
|
oldState
|
|
1364
|
-
} = get(uid);
|
|
1362
|
+
} = get$1(uid);
|
|
1365
1363
|
const result = diff(oldState, newState);
|
|
1366
1364
|
return result;
|
|
1367
1365
|
};
|
|
1368
1366
|
|
|
1367
|
+
const dispatchEvent = (element, event) => {
|
|
1368
|
+
element.dispatchEvent(event);
|
|
1369
|
+
|
|
1370
|
+
// Also invoke direct on* handler if set (e.g. element.onclick = function(){})
|
|
1371
|
+
const handlerName = `on${event.type}`;
|
|
1372
|
+
const handler = element[handlerName];
|
|
1373
|
+
if (typeof handler === 'function') {
|
|
1374
|
+
handler.call(element, event);
|
|
1375
|
+
} else if (handler === null || handler === undefined) {
|
|
1376
|
+
// Check if there's an inline HTML attribute that wasn't converted to a property
|
|
1377
|
+
const attrValue = element.getAttribute(handlerName);
|
|
1378
|
+
if (attrValue && typeof attrValue === 'string' && element.ownerDocument && element.ownerDocument.defaultView) {
|
|
1379
|
+
const window = element.ownerDocument.defaultView;
|
|
1380
|
+
// Handle inline event handlers like onclick="someFunction(2)"
|
|
1381
|
+
// Evaluate in the context of the window so functions are in scope
|
|
1382
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
1383
|
+
const fn = new Function('event', `with(this) { ${attrValue} }`);
|
|
1384
|
+
fn.call(window, event);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
};
|
|
1388
|
+
|
|
1389
|
+
const dispatchClickEvent = (element, window) => {
|
|
1390
|
+
const clickEvent = new window.MouseEvent('click', {
|
|
1391
|
+
bubbles: true
|
|
1392
|
+
});
|
|
1393
|
+
dispatchEvent(element, clickEvent);
|
|
1394
|
+
};
|
|
1395
|
+
|
|
1396
|
+
const getParsedNodesChildNodeCount = parsedDom => {
|
|
1397
|
+
array(parsedDom);
|
|
1398
|
+
const rootCountFromParse = parsedDom.rootChildCount;
|
|
1399
|
+
if (typeof rootCountFromParse === 'number') {
|
|
1400
|
+
return rootCountFromParse;
|
|
1401
|
+
}
|
|
1402
|
+
let rootChildCount = 0;
|
|
1403
|
+
let i = 0;
|
|
1404
|
+
while (i < parsedDom.length) {
|
|
1405
|
+
rootChildCount++;
|
|
1406
|
+
|
|
1407
|
+
// skip the entire subtree of the current node
|
|
1408
|
+
let toSkip = parsedDom[i].childCount;
|
|
1409
|
+
i++;
|
|
1410
|
+
while (toSkip > 0 && i < parsedDom.length) {
|
|
1411
|
+
toSkip -= 1;
|
|
1412
|
+
toSkip += parsedDom[i].childCount;
|
|
1413
|
+
i++;
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
return rootChildCount;
|
|
1417
|
+
};
|
|
1418
|
+
|
|
1419
|
+
/* eslint-disable @typescript-eslint/prefer-readonly-parameter-types */
|
|
1420
|
+
|
|
1421
|
+
const states = new Map();
|
|
1422
|
+
const get = uid => {
|
|
1423
|
+
return states.get(uid);
|
|
1424
|
+
};
|
|
1425
|
+
const set = (uid, instance) => {
|
|
1426
|
+
states.set(uid, instance);
|
|
1427
|
+
};
|
|
1428
|
+
|
|
1369
1429
|
const text = data => {
|
|
1370
1430
|
return {
|
|
1371
1431
|
childCount: 0,
|
|
@@ -1824,29 +1884,6 @@ const getVirtualDomTag = text => {
|
|
|
1824
1884
|
}
|
|
1825
1885
|
};
|
|
1826
1886
|
|
|
1827
|
-
const None = 0;
|
|
1828
|
-
const OpeningAngleBracket = 1;
|
|
1829
|
-
const ClosingAngleBracket = 2;
|
|
1830
|
-
const TagNameStart = 3;
|
|
1831
|
-
const TagNameEnd = 4;
|
|
1832
|
-
const Content = 5;
|
|
1833
|
-
const ClosingTagSlash = 6;
|
|
1834
|
-
const WhitespaceInsideOpeningTag = 7;
|
|
1835
|
-
const AttributeName = 8;
|
|
1836
|
-
const AttributeEqualSign = 9;
|
|
1837
|
-
const AttributeQuoteStart = 10;
|
|
1838
|
-
const AttributeValue = 11;
|
|
1839
|
-
const AttributeQuoteEnd = 12;
|
|
1840
|
-
const WhitespaceAfterClosingTagSlash = 13;
|
|
1841
|
-
const WhitespaceAfterOpeningTagOpenAngleBracket = 14;
|
|
1842
|
-
const ExclamationMark = 15;
|
|
1843
|
-
const Doctype = 16;
|
|
1844
|
-
const StartCommentDashes = 17;
|
|
1845
|
-
const Comment = 18;
|
|
1846
|
-
const EndCommentTag = 19;
|
|
1847
|
-
const Text = 20;
|
|
1848
|
-
const CommentStart = 21;
|
|
1849
|
-
|
|
1850
1887
|
/* eslint-disable @cspell/spellchecker */
|
|
1851
1888
|
// Common HTML attributes that are safe to allow by default
|
|
1852
1889
|
const commonAllowedAttributes = new Set([
|
|
@@ -1885,6 +1922,211 @@ const isDefaultAllowedAttribute = (attributeName, defaultAllowedAttributes) => {
|
|
|
1885
1922
|
return defaultAllowedAttributes.includes(attributeName);
|
|
1886
1923
|
};
|
|
1887
1924
|
|
|
1925
|
+
// Tags to skip entirely during serialization
|
|
1926
|
+
const TAGS_TO_SKIP = new Set(['script', 'meta', 'title']);
|
|
1927
|
+
|
|
1928
|
+
// Tags to skip but process children
|
|
1929
|
+
const TAGS_TO_SKIP_TAG_ONLY$1 = new Set(['html', 'body', 'head']);
|
|
1930
|
+
|
|
1931
|
+
// Tags where we extract content as CSS
|
|
1932
|
+
const CSS_TAGS = new Set(['style']);
|
|
1933
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
1934
|
+
const serializeNode = (node, dom, css, context) => {
|
|
1935
|
+
const {
|
|
1936
|
+
nodeType
|
|
1937
|
+
} = node;
|
|
1938
|
+
|
|
1939
|
+
// Text node
|
|
1940
|
+
if (nodeType === 3) {
|
|
1941
|
+
const textContent = node.textContent || '';
|
|
1942
|
+
if (textContent) {
|
|
1943
|
+
dom.push(text(textContent));
|
|
1944
|
+
return 1;
|
|
1945
|
+
}
|
|
1946
|
+
return 0;
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
// Not an element node — skip
|
|
1950
|
+
if (nodeType !== 1) {
|
|
1951
|
+
return 0;
|
|
1952
|
+
}
|
|
1953
|
+
const tagName = (node.tagName || '').toLowerCase();
|
|
1954
|
+
|
|
1955
|
+
// Extract CSS from style elements
|
|
1956
|
+
if (CSS_TAGS.has(tagName)) {
|
|
1957
|
+
const styleContent = node.textContent || '';
|
|
1958
|
+
if (styleContent.trim()) {
|
|
1959
|
+
css.push(styleContent);
|
|
1960
|
+
}
|
|
1961
|
+
return 0;
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
// Skip certain tags entirely
|
|
1965
|
+
if (TAGS_TO_SKIP.has(tagName)) {
|
|
1966
|
+
return 0;
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
// For html/body tags, serialize children only
|
|
1970
|
+
if (TAGS_TO_SKIP_TAG_ONLY$1.has(tagName)) {
|
|
1971
|
+
let childCount = 0;
|
|
1972
|
+
const {
|
|
1973
|
+
childNodes
|
|
1974
|
+
} = node;
|
|
1975
|
+
for (let i = 0; i < childNodes.length; i++) {
|
|
1976
|
+
childCount += serializeNode(childNodes[i], dom, css, context);
|
|
1977
|
+
}
|
|
1978
|
+
return childCount;
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
// Normal element - create a VirtualDomNode
|
|
1982
|
+
const newNode = {
|
|
1983
|
+
childCount: 0,
|
|
1984
|
+
type: getVirtualDomTag(tagName)
|
|
1985
|
+
};
|
|
1986
|
+
|
|
1987
|
+
// Copy allowed attributes
|
|
1988
|
+
const {
|
|
1989
|
+
attributes
|
|
1990
|
+
} = node;
|
|
1991
|
+
if (attributes) {
|
|
1992
|
+
for (let i = 0; i < attributes.length; i++) {
|
|
1993
|
+
const attr = attributes[i];
|
|
1994
|
+
const attrName = attr.name;
|
|
1995
|
+
if (isDefaultAllowedAttribute(attrName, [])) {
|
|
1996
|
+
let finalName = attrName;
|
|
1997
|
+
if (attrName === 'class') {
|
|
1998
|
+
finalName = 'className';
|
|
1999
|
+
} else if (attrName === 'type') {
|
|
2000
|
+
finalName = 'inputType';
|
|
2001
|
+
}
|
|
2002
|
+
newNode[finalName] = attr.value;
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
// Assign element tracking ID for interactivity
|
|
2008
|
+
if (context.elementMap) {
|
|
2009
|
+
const hdId = String(context.nextId++);
|
|
2010
|
+
newNode['data-id'] = hdId;
|
|
2011
|
+
context.elementMap.set(hdId, node);
|
|
2012
|
+
}
|
|
2013
|
+
dom.push(newNode);
|
|
2014
|
+
|
|
2015
|
+
// Serialize children
|
|
2016
|
+
let childCount = 0;
|
|
2017
|
+
const {
|
|
2018
|
+
childNodes
|
|
2019
|
+
} = node;
|
|
2020
|
+
for (let i = 0; i < childNodes.length; i++) {
|
|
2021
|
+
childCount += serializeNode(childNodes[i], dom, css, context);
|
|
2022
|
+
}
|
|
2023
|
+
newNode.childCount = childCount;
|
|
2024
|
+
return 1;
|
|
2025
|
+
};
|
|
2026
|
+
|
|
2027
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
2028
|
+
const serialize = (document, elementMap) => {
|
|
2029
|
+
const dom = [];
|
|
2030
|
+
const css = [];
|
|
2031
|
+
const context = {
|
|
2032
|
+
elementMap,
|
|
2033
|
+
nextId: 0
|
|
2034
|
+
};
|
|
2035
|
+
|
|
2036
|
+
// Start from document.documentElement (the <html> element)
|
|
2037
|
+
const root = document.documentElement || document.body;
|
|
2038
|
+
if (!root) {
|
|
2039
|
+
return {
|
|
2040
|
+
css,
|
|
2041
|
+
dom
|
|
2042
|
+
};
|
|
2043
|
+
}
|
|
2044
|
+
let rootChildCount = 0;
|
|
2045
|
+
const {
|
|
2046
|
+
childNodes
|
|
2047
|
+
} = root;
|
|
2048
|
+
for (let i = 0; i < childNodes.length; i++) {
|
|
2049
|
+
rootChildCount += serializeNode(childNodes[i], dom, css, context);
|
|
2050
|
+
}
|
|
2051
|
+
try {
|
|
2052
|
+
Object.defineProperty(dom, 'rootChildCount', {
|
|
2053
|
+
configurable: true,
|
|
2054
|
+
enumerable: false,
|
|
2055
|
+
value: rootChildCount
|
|
2056
|
+
});
|
|
2057
|
+
} catch {
|
|
2058
|
+
dom.rootChildCount = rootChildCount;
|
|
2059
|
+
}
|
|
2060
|
+
return {
|
|
2061
|
+
css,
|
|
2062
|
+
dom
|
|
2063
|
+
};
|
|
2064
|
+
};
|
|
2065
|
+
|
|
2066
|
+
const handleClick = (state, hdId) => {
|
|
2067
|
+
// console.log('click,', hdId)
|
|
2068
|
+
if (!hdId) {
|
|
2069
|
+
return state;
|
|
2070
|
+
}
|
|
2071
|
+
const happyDomInstance = get(state.uid);
|
|
2072
|
+
if (!happyDomInstance) {
|
|
2073
|
+
return state;
|
|
2074
|
+
}
|
|
2075
|
+
const element = happyDomInstance.elementMap.get(hdId);
|
|
2076
|
+
if (!element) {
|
|
2077
|
+
return state;
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
// console.log({ element })
|
|
2081
|
+
// Dispatch click event in happy-dom so event listeners fire
|
|
2082
|
+
dispatchClickEvent(element, happyDomInstance.window);
|
|
2083
|
+
|
|
2084
|
+
// Re-serialize the (potentially mutated) DOM
|
|
2085
|
+
const elementMap = new Map();
|
|
2086
|
+
const serialized = serialize(happyDomInstance.document, elementMap);
|
|
2087
|
+
|
|
2088
|
+
// Update happy-dom state with new element map
|
|
2089
|
+
set(state.uid, {
|
|
2090
|
+
document: happyDomInstance.document,
|
|
2091
|
+
elementMap,
|
|
2092
|
+
window: happyDomInstance.window
|
|
2093
|
+
});
|
|
2094
|
+
const parsedDom = serialized.dom;
|
|
2095
|
+
const {
|
|
2096
|
+
css
|
|
2097
|
+
} = serialized;
|
|
2098
|
+
const parsedNodesChildNodeCount = getParsedNodesChildNodeCount(parsedDom);
|
|
2099
|
+
return {
|
|
2100
|
+
...state,
|
|
2101
|
+
css,
|
|
2102
|
+
parsedDom,
|
|
2103
|
+
parsedNodesChildNodeCount
|
|
2104
|
+
};
|
|
2105
|
+
};
|
|
2106
|
+
|
|
2107
|
+
const None = 0;
|
|
2108
|
+
const OpeningAngleBracket = 1;
|
|
2109
|
+
const ClosingAngleBracket = 2;
|
|
2110
|
+
const TagNameStart = 3;
|
|
2111
|
+
const TagNameEnd = 4;
|
|
2112
|
+
const Content = 5;
|
|
2113
|
+
const ClosingTagSlash = 6;
|
|
2114
|
+
const WhitespaceInsideOpeningTag = 7;
|
|
2115
|
+
const AttributeName = 8;
|
|
2116
|
+
const AttributeEqualSign = 9;
|
|
2117
|
+
const AttributeQuoteStart = 10;
|
|
2118
|
+
const AttributeValue = 11;
|
|
2119
|
+
const AttributeQuoteEnd = 12;
|
|
2120
|
+
const WhitespaceAfterClosingTagSlash = 13;
|
|
2121
|
+
const WhitespaceAfterOpeningTagOpenAngleBracket = 14;
|
|
2122
|
+
const ExclamationMark = 15;
|
|
2123
|
+
const Doctype = 16;
|
|
2124
|
+
const StartCommentDashes = 17;
|
|
2125
|
+
const Comment = 18;
|
|
2126
|
+
const EndCommentTag = 19;
|
|
2127
|
+
const Text = 20;
|
|
2128
|
+
const CommentStart = 21;
|
|
2129
|
+
|
|
1888
2130
|
const isSelfClosingTag = tag => {
|
|
1889
2131
|
switch (tag.toLowerCase()) {
|
|
1890
2132
|
case 'area':
|
|
@@ -2237,7 +2479,7 @@ const tokenizeHtml = text => {
|
|
|
2237
2479
|
const TAGS_TO_SKIP_COMPLETELY = new Set(['meta', 'title']);
|
|
2238
2480
|
|
|
2239
2481
|
// Tags that should have their opening/closing tags skipped but content processed
|
|
2240
|
-
const TAGS_TO_SKIP_TAG_ONLY
|
|
2482
|
+
const TAGS_TO_SKIP_TAG_ONLY = new Set(['html', 'head']);
|
|
2241
2483
|
|
|
2242
2484
|
// Tags where we capture content as CSS
|
|
2243
2485
|
const TAGS_TO_CAPTURE_AS_CSS = new Set(['style']);
|
|
@@ -2334,7 +2576,7 @@ const parseHtml = (html, allowedAttributes = [], defaultAllowedAttributes = [])
|
|
|
2334
2576
|
} else if (TAGS_TO_SKIP_COMPLETELY.has(tagNameToClose)) {
|
|
2335
2577
|
// We were skipping this content, so decrement skipDepth
|
|
2336
2578
|
skipDepth--;
|
|
2337
|
-
} else if (TAGS_TO_SKIP_TAG_ONLY
|
|
2579
|
+
} else if (TAGS_TO_SKIP_TAG_ONLY.has(tagNameToClose)) ; else {
|
|
2338
2580
|
// Normal tag - pop from stack
|
|
2339
2581
|
if (stack.length > 1) {
|
|
2340
2582
|
stack.pop();
|
|
@@ -2368,7 +2610,7 @@ const parseHtml = (html, allowedAttributes = [], defaultAllowedAttributes = [])
|
|
|
2368
2610
|
// For self-closing tags like meta, we just skip them without tracking
|
|
2369
2611
|
}
|
|
2370
2612
|
// Check if this tag should have its opening/closing tags skipped (html, head)
|
|
2371
|
-
else if (TAGS_TO_SKIP_TAG_ONLY
|
|
2613
|
+
else if (TAGS_TO_SKIP_TAG_ONLY.has(tagNameLower)) {
|
|
2372
2614
|
if (!lastTagWasSelfClosing) {
|
|
2373
2615
|
// Track the tag name for matching the closing tag
|
|
2374
2616
|
tagStack.push(token.text);
|
|
@@ -2428,7 +2670,7 @@ const handleEditorChanged = async () => {
|
|
|
2428
2670
|
for (const previewUid of previewKeys) {
|
|
2429
2671
|
const {
|
|
2430
2672
|
newState: state
|
|
2431
|
-
} = get(previewUid);
|
|
2673
|
+
} = get$1(previewUid);
|
|
2432
2674
|
|
|
2433
2675
|
// Skip if no URI is set
|
|
2434
2676
|
if (!state.uri) {
|
|
@@ -2459,7 +2701,7 @@ const handleEditorChanged = async () => {
|
|
|
2459
2701
|
parsedDom: parseResult.dom,
|
|
2460
2702
|
scripts: parseResult.scripts
|
|
2461
2703
|
};
|
|
2462
|
-
set(previewUid, state, updatedState);
|
|
2704
|
+
set$1(previewUid, state, updatedState);
|
|
2463
2705
|
} catch (error) {
|
|
2464
2706
|
// If getting text fails, update with error message
|
|
2465
2707
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
@@ -2471,7 +2713,7 @@ const handleEditorChanged = async () => {
|
|
|
2471
2713
|
parsedDom: [],
|
|
2472
2714
|
scripts: []
|
|
2473
2715
|
};
|
|
2474
|
-
set(previewUid, state, updatedState);
|
|
2716
|
+
set$1(previewUid, state, updatedState);
|
|
2475
2717
|
}
|
|
2476
2718
|
}
|
|
2477
2719
|
}
|
|
@@ -90049,8 +90291,7 @@ class Window extends BrowserWindow {
|
|
|
90049
90291
|
}
|
|
90050
90292
|
}
|
|
90051
90293
|
|
|
90052
|
-
|
|
90053
|
-
const executeScripts = (rawHtml, scripts) => {
|
|
90294
|
+
const createWindow = rawHtml => {
|
|
90054
90295
|
const window = new Window({
|
|
90055
90296
|
url: 'https://localhost:3000'
|
|
90056
90297
|
});
|
|
@@ -90060,167 +90301,45 @@ const executeScripts = (rawHtml, scripts) => {
|
|
|
90060
90301
|
|
|
90061
90302
|
// Parse the raw HTML into the happy-dom document
|
|
90062
90303
|
document.documentElement.innerHTML = rawHtml;
|
|
90304
|
+
return {
|
|
90305
|
+
document,
|
|
90306
|
+
window
|
|
90307
|
+
};
|
|
90308
|
+
};
|
|
90063
90309
|
|
|
90310
|
+
const alert = message => {
|
|
90311
|
+
void invoke('ConfirmPrompt.prompt', message);
|
|
90312
|
+
};
|
|
90313
|
+
|
|
90314
|
+
const getTopLevelFunctionNames = script => {
|
|
90315
|
+
const names = [];
|
|
90316
|
+
const regex = /(?:^|[\n;])\s*function\s+([a-zA-Z_$][\w$]*)/g;
|
|
90317
|
+
let match;
|
|
90318
|
+
while ((match = regex.exec(script)) !== null) {
|
|
90319
|
+
names.push(match[1]);
|
|
90320
|
+
}
|
|
90321
|
+
return names;
|
|
90322
|
+
};
|
|
90323
|
+
|
|
90324
|
+
/* eslint-disable @typescript-eslint/no-implied-eval */
|
|
90325
|
+
const executeScripts = (window, document, scripts) => {
|
|
90326
|
+
window.alert = alert;
|
|
90327
|
+
// @ts-ignore
|
|
90328
|
+
globalThis.alert = alert;
|
|
90064
90329
|
// Execute each script with the happy-dom window and document as context
|
|
90065
90330
|
for (const scriptContent of scripts) {
|
|
90066
90331
|
try {
|
|
90067
|
-
|
|
90332
|
+
// In a browser, top-level function declarations in <script> tags become
|
|
90333
|
+
// properties on window. Since new Function() creates a local scope, we
|
|
90334
|
+
// extract function names and explicitly assign them to window after execution.
|
|
90335
|
+
const functionNames = getTopLevelFunctionNames(scriptContent);
|
|
90336
|
+
const suffix = functionNames.map(name => `\nwindow['${name}'] = ${name};`).join('');
|
|
90337
|
+
const fn = new Function('window', 'document', 'console', scriptContent + suffix);
|
|
90068
90338
|
fn(window, document, console);
|
|
90069
90339
|
} catch (error) {
|
|
90070
90340
|
console.warn('[preview-worker] Script execution error:', error);
|
|
90071
90341
|
}
|
|
90072
90342
|
}
|
|
90073
|
-
return document;
|
|
90074
|
-
};
|
|
90075
|
-
|
|
90076
|
-
const getParsedNodesChildNodeCount = parsedDom => {
|
|
90077
|
-
array(parsedDom);
|
|
90078
|
-
const rootCountFromParse = parsedDom.rootChildCount;
|
|
90079
|
-
if (typeof rootCountFromParse === 'number') {
|
|
90080
|
-
return rootCountFromParse;
|
|
90081
|
-
}
|
|
90082
|
-
let rootChildCount = 0;
|
|
90083
|
-
let i = 0;
|
|
90084
|
-
while (i < parsedDom.length) {
|
|
90085
|
-
rootChildCount++;
|
|
90086
|
-
|
|
90087
|
-
// skip the entire subtree of the current node
|
|
90088
|
-
let toSkip = parsedDom[i].childCount;
|
|
90089
|
-
i++;
|
|
90090
|
-
while (toSkip > 0 && i < parsedDom.length) {
|
|
90091
|
-
toSkip -= 1;
|
|
90092
|
-
toSkip += parsedDom[i].childCount;
|
|
90093
|
-
i++;
|
|
90094
|
-
}
|
|
90095
|
-
}
|
|
90096
|
-
return rootChildCount;
|
|
90097
|
-
};
|
|
90098
|
-
|
|
90099
|
-
// Tags to skip entirely during serialization
|
|
90100
|
-
const TAGS_TO_SKIP = new Set(['script', 'meta', 'title']);
|
|
90101
|
-
|
|
90102
|
-
// Tags to skip but process children
|
|
90103
|
-
const TAGS_TO_SKIP_TAG_ONLY = new Set(['html', 'body', 'head']);
|
|
90104
|
-
|
|
90105
|
-
// Tags where we extract content as CSS
|
|
90106
|
-
const CSS_TAGS = new Set(['style']);
|
|
90107
|
-
const serializeNode = (node, dom, css) => {
|
|
90108
|
-
const {
|
|
90109
|
-
nodeType
|
|
90110
|
-
} = node;
|
|
90111
|
-
|
|
90112
|
-
// Text node
|
|
90113
|
-
if (nodeType === 3) {
|
|
90114
|
-
const textContent = node.textContent || '';
|
|
90115
|
-
if (textContent) {
|
|
90116
|
-
dom.push(text(textContent));
|
|
90117
|
-
return 1;
|
|
90118
|
-
}
|
|
90119
|
-
return 0;
|
|
90120
|
-
}
|
|
90121
|
-
|
|
90122
|
-
// Not an element node — skip
|
|
90123
|
-
if (nodeType !== 1) {
|
|
90124
|
-
return 0;
|
|
90125
|
-
}
|
|
90126
|
-
const tagName = (node.tagName || '').toLowerCase();
|
|
90127
|
-
|
|
90128
|
-
// Extract CSS from style elements
|
|
90129
|
-
if (CSS_TAGS.has(tagName)) {
|
|
90130
|
-
const styleContent = node.textContent || '';
|
|
90131
|
-
if (styleContent.trim()) {
|
|
90132
|
-
css.push(styleContent);
|
|
90133
|
-
}
|
|
90134
|
-
return 0;
|
|
90135
|
-
}
|
|
90136
|
-
|
|
90137
|
-
// Skip certain tags entirely
|
|
90138
|
-
if (TAGS_TO_SKIP.has(tagName)) {
|
|
90139
|
-
return 0;
|
|
90140
|
-
}
|
|
90141
|
-
|
|
90142
|
-
// For html/body tags, serialize children only
|
|
90143
|
-
if (TAGS_TO_SKIP_TAG_ONLY.has(tagName)) {
|
|
90144
|
-
let childCount = 0;
|
|
90145
|
-
const {
|
|
90146
|
-
childNodes
|
|
90147
|
-
} = node;
|
|
90148
|
-
for (let i = 0; i < childNodes.length; i++) {
|
|
90149
|
-
childCount += serializeNode(childNodes[i], dom, css);
|
|
90150
|
-
}
|
|
90151
|
-
return childCount;
|
|
90152
|
-
}
|
|
90153
|
-
|
|
90154
|
-
// Normal element - create a VirtualDomNode
|
|
90155
|
-
const newNode = {
|
|
90156
|
-
childCount: 0,
|
|
90157
|
-
type: getVirtualDomTag(tagName)
|
|
90158
|
-
};
|
|
90159
|
-
|
|
90160
|
-
// Copy allowed attributes
|
|
90161
|
-
const {
|
|
90162
|
-
attributes
|
|
90163
|
-
} = node;
|
|
90164
|
-
if (attributes) {
|
|
90165
|
-
for (let i = 0; i < attributes.length; i++) {
|
|
90166
|
-
const attr = attributes[i];
|
|
90167
|
-
const attrName = attr.name;
|
|
90168
|
-
if (isDefaultAllowedAttribute(attrName, [])) {
|
|
90169
|
-
let finalName = attrName;
|
|
90170
|
-
if (attrName === 'class') {
|
|
90171
|
-
finalName = 'className';
|
|
90172
|
-
} else if (attrName === 'type') {
|
|
90173
|
-
finalName = 'inputType';
|
|
90174
|
-
}
|
|
90175
|
-
newNode[finalName] = attr.value;
|
|
90176
|
-
}
|
|
90177
|
-
}
|
|
90178
|
-
}
|
|
90179
|
-
dom.push(newNode);
|
|
90180
|
-
|
|
90181
|
-
// Serialize children
|
|
90182
|
-
let childCount = 0;
|
|
90183
|
-
const {
|
|
90184
|
-
childNodes
|
|
90185
|
-
} = node;
|
|
90186
|
-
for (let i = 0; i < childNodes.length; i++) {
|
|
90187
|
-
childCount += serializeNode(childNodes[i], dom, css);
|
|
90188
|
-
}
|
|
90189
|
-
newNode.childCount = childCount;
|
|
90190
|
-
return 1;
|
|
90191
|
-
};
|
|
90192
|
-
const serialize = document => {
|
|
90193
|
-
const dom = [];
|
|
90194
|
-
const css = [];
|
|
90195
|
-
|
|
90196
|
-
// Start from document.documentElement (the <html> element)
|
|
90197
|
-
const root = document.documentElement || document.body;
|
|
90198
|
-
if (!root) {
|
|
90199
|
-
return {
|
|
90200
|
-
css,
|
|
90201
|
-
dom
|
|
90202
|
-
};
|
|
90203
|
-
}
|
|
90204
|
-
let rootChildCount = 0;
|
|
90205
|
-
const {
|
|
90206
|
-
childNodes
|
|
90207
|
-
} = root;
|
|
90208
|
-
for (let i = 0; i < childNodes.length; i++) {
|
|
90209
|
-
rootChildCount += serializeNode(childNodes[i], dom, css);
|
|
90210
|
-
}
|
|
90211
|
-
try {
|
|
90212
|
-
Object.defineProperty(dom, 'rootChildCount', {
|
|
90213
|
-
configurable: true,
|
|
90214
|
-
enumerable: false,
|
|
90215
|
-
value: rootChildCount
|
|
90216
|
-
});
|
|
90217
|
-
} catch {
|
|
90218
|
-
dom.rootChildCount = rootChildCount;
|
|
90219
|
-
}
|
|
90220
|
-
return {
|
|
90221
|
-
css,
|
|
90222
|
-
dom
|
|
90223
|
-
};
|
|
90224
90343
|
};
|
|
90225
90344
|
|
|
90226
90345
|
/* eslint-disable prefer-destructuring */
|
|
@@ -90244,10 +90363,20 @@ const updateContent = async (state, uri) => {
|
|
|
90244
90363
|
// If scripts are present, execute them via happy-dom and re-serialize the DOM
|
|
90245
90364
|
if (scripts.length > 0) {
|
|
90246
90365
|
try {
|
|
90247
|
-
const
|
|
90248
|
-
|
|
90366
|
+
const {
|
|
90367
|
+
document: happyDomDocument,
|
|
90368
|
+
window: happyDomWindow
|
|
90369
|
+
} = createWindow(content);
|
|
90370
|
+
executeScripts(happyDomWindow, happyDomDocument, scripts);
|
|
90371
|
+
const elementMap = new Map();
|
|
90372
|
+
const serialized = serialize(happyDomDocument, elementMap);
|
|
90249
90373
|
parsedDom = serialized.dom;
|
|
90250
90374
|
css = serialized.css;
|
|
90375
|
+
set(state.uid, {
|
|
90376
|
+
document: happyDomDocument,
|
|
90377
|
+
elementMap,
|
|
90378
|
+
window: happyDomWindow
|
|
90379
|
+
});
|
|
90251
90380
|
} catch {
|
|
90252
90381
|
// If script execution fails, fall back to static HTML parsing
|
|
90253
90382
|
}
|
|
@@ -90295,6 +90424,56 @@ const handleFileEdited = async state => {
|
|
|
90295
90424
|
};
|
|
90296
90425
|
};
|
|
90297
90426
|
|
|
90427
|
+
const dispatchInputEvent = (element, window) => {
|
|
90428
|
+
const inputEvent = new window.Event('input', {
|
|
90429
|
+
bubbles: true
|
|
90430
|
+
});
|
|
90431
|
+
dispatchEvent(element, inputEvent);
|
|
90432
|
+
};
|
|
90433
|
+
|
|
90434
|
+
const handleInput = (state, hdId, value) => {
|
|
90435
|
+
// console.log('input,', hdId, value)
|
|
90436
|
+
if (!hdId) {
|
|
90437
|
+
return state;
|
|
90438
|
+
}
|
|
90439
|
+
const happyDomInstance = get(state.uid);
|
|
90440
|
+
if (!happyDomInstance) {
|
|
90441
|
+
return state;
|
|
90442
|
+
}
|
|
90443
|
+
const element = happyDomInstance.elementMap.get(hdId);
|
|
90444
|
+
if (!element) {
|
|
90445
|
+
return state;
|
|
90446
|
+
}
|
|
90447
|
+
|
|
90448
|
+
// console.log({ element })
|
|
90449
|
+
// Update the element's value from the preview
|
|
90450
|
+
element.value = value;
|
|
90451
|
+
// Dispatch input event in happy-dom so event listeners fire
|
|
90452
|
+
dispatchInputEvent(element, happyDomInstance.window);
|
|
90453
|
+
|
|
90454
|
+
// Re-serialize the (potentially mutated) DOM
|
|
90455
|
+
const elementMap = new Map();
|
|
90456
|
+
const serialized = serialize(happyDomInstance.document, elementMap);
|
|
90457
|
+
|
|
90458
|
+
// Update happy-dom state with new element map
|
|
90459
|
+
set(state.uid, {
|
|
90460
|
+
document: happyDomInstance.document,
|
|
90461
|
+
elementMap,
|
|
90462
|
+
window: happyDomInstance.window
|
|
90463
|
+
});
|
|
90464
|
+
const parsedDom = serialized.dom;
|
|
90465
|
+
const {
|
|
90466
|
+
css
|
|
90467
|
+
} = serialized;
|
|
90468
|
+
const parsedNodesChildNodeCount = getParsedNodesChildNodeCount(parsedDom);
|
|
90469
|
+
return {
|
|
90470
|
+
...state,
|
|
90471
|
+
css,
|
|
90472
|
+
parsedDom,
|
|
90473
|
+
parsedNodesChildNodeCount
|
|
90474
|
+
};
|
|
90475
|
+
};
|
|
90476
|
+
|
|
90298
90477
|
const loadContent = async state => {
|
|
90299
90478
|
// Try to register to receive editor change notifications from the editor worker.
|
|
90300
90479
|
// Use dynamic access and ignore errors so this is safe in environments where
|
|
@@ -90382,6 +90561,9 @@ const renderCss = (oldState, newState) => {
|
|
|
90382
90561
|
return ['Viewlet.setCss', uid, cssString];
|
|
90383
90562
|
};
|
|
90384
90563
|
|
|
90564
|
+
const HandleInput = 4;
|
|
90565
|
+
const HandleClick = 11;
|
|
90566
|
+
|
|
90385
90567
|
const getEmptyPreviewDom = () => {
|
|
90386
90568
|
return [{
|
|
90387
90569
|
childCount: 1,
|
|
@@ -90411,12 +90593,16 @@ const getPreviewDom = state => {
|
|
|
90411
90593
|
return [{
|
|
90412
90594
|
childCount: parsedNodesChildNodeCount,
|
|
90413
90595
|
className: 'Viewlet Preview',
|
|
90596
|
+
onClick: HandleClick,
|
|
90597
|
+
onInput: HandleInput,
|
|
90414
90598
|
type: Div$1
|
|
90415
90599
|
}, ...parsedDom];
|
|
90416
90600
|
}
|
|
90417
90601
|
return [{
|
|
90418
90602
|
childCount: 1,
|
|
90419
90603
|
className: 'Viewlet Preview',
|
|
90604
|
+
onClick: HandleClick,
|
|
90605
|
+
onInput: HandleInput,
|
|
90420
90606
|
type: Div$1
|
|
90421
90607
|
}, {
|
|
90422
90608
|
childCount: 1,
|
|
@@ -90475,18 +90661,21 @@ const render2 = (uid, diffResult) => {
|
|
|
90475
90661
|
const {
|
|
90476
90662
|
newState,
|
|
90477
90663
|
oldState
|
|
90478
|
-
} = get(uid);
|
|
90479
|
-
set(uid, newState, newState);
|
|
90664
|
+
} = get$1(uid);
|
|
90665
|
+
set$1(uid, newState, newState);
|
|
90480
90666
|
const commands = applyRender(oldState, newState, diffResult);
|
|
90481
90667
|
return commands;
|
|
90482
90668
|
};
|
|
90483
90669
|
|
|
90484
|
-
const HandleClick = 11;
|
|
90485
|
-
|
|
90486
90670
|
const renderEventListeners = () => {
|
|
90487
90671
|
return [{
|
|
90672
|
+
capture: true,
|
|
90488
90673
|
name: HandleClick,
|
|
90489
|
-
params: ['handleClick',
|
|
90674
|
+
params: ['handleClick', 'event.target.dataset.id']
|
|
90675
|
+
}, {
|
|
90676
|
+
capture: true,
|
|
90677
|
+
name: HandleInput,
|
|
90678
|
+
params: ['handleInput', 'event.target.dataset.id', 'event.target.value']
|
|
90490
90679
|
}];
|
|
90491
90680
|
};
|
|
90492
90681
|
|
|
@@ -90541,7 +90730,9 @@ const commandMap = {
|
|
|
90541
90730
|
'Preview.create': create,
|
|
90542
90731
|
'Preview.diff2': diff2,
|
|
90543
90732
|
'Preview.getCommandIds': getCommandIds,
|
|
90733
|
+
'Preview.handleClick': wrapCommand(handleClick),
|
|
90544
90734
|
'Preview.handleFileEdited': wrapCommand(handleFileEdited),
|
|
90735
|
+
'Preview.handleInput': wrapCommand(handleInput),
|
|
90545
90736
|
'Preview.loadContent': wrapCommand(loadContent),
|
|
90546
90737
|
'Preview.render2': render2,
|
|
90547
90738
|
'Preview.renderEventListeners': renderEventListeners,
|
|
@@ -90557,12 +90748,12 @@ const listen = async () => {
|
|
|
90557
90748
|
const rpc = await WebWorkerRpcClient.create({
|
|
90558
90749
|
commandMap: commandMap
|
|
90559
90750
|
});
|
|
90560
|
-
set$
|
|
90751
|
+
set$2(rpc);
|
|
90561
90752
|
const editorRpc = await LazyTransferMessagePortRpcParent.create({
|
|
90562
90753
|
commandMap: {},
|
|
90563
90754
|
send: port => sendMessagePortToEditorWorker(port, 9112)
|
|
90564
90755
|
});
|
|
90565
|
-
set$
|
|
90756
|
+
set$3(editorRpc);
|
|
90566
90757
|
};
|
|
90567
90758
|
|
|
90568
90759
|
const main = async () => {
|