@lvce-editor/preview-worker 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -417,6 +417,100 @@ const IpcChildWithModuleWorkerAndMessagePort$1 = {
417
417
  listen: listen$6,
418
418
  wrap: wrap$e
419
419
  };
420
+ const addListener = (emitter, type, callback) => {
421
+ if ('addEventListener' in emitter) {
422
+ emitter.addEventListener(type, callback);
423
+ } else {
424
+ emitter.on(type, callback);
425
+ }
426
+ };
427
+ const removeListener = (emitter, type, callback) => {
428
+ if ('removeEventListener' in emitter) {
429
+ emitter.removeEventListener(type, callback);
430
+ } else {
431
+ emitter.off(type, callback);
432
+ }
433
+ };
434
+ const getFirstEvent = (eventEmitter, eventMap) => {
435
+ const {
436
+ promise,
437
+ resolve
438
+ } = Promise.withResolvers();
439
+ const listenerMap = Object.create(null);
440
+ const cleanup = value => {
441
+ for (const event of Object.keys(eventMap)) {
442
+ removeListener(eventEmitter, event, listenerMap[event]);
443
+ }
444
+ resolve(value);
445
+ };
446
+ for (const [event, type] of Object.entries(eventMap)) {
447
+ const listener = event => {
448
+ cleanup({
449
+ event,
450
+ type
451
+ });
452
+ };
453
+ addListener(eventEmitter, event, listener);
454
+ listenerMap[event] = listener;
455
+ }
456
+ return promise;
457
+ };
458
+ const Message$1 = 3;
459
+ const create$5$1 = async ({
460
+ isMessagePortOpen,
461
+ messagePort
462
+ }) => {
463
+ if (!isMessagePort(messagePort)) {
464
+ throw new IpcError('port must be of type MessagePort');
465
+ }
466
+ if (isMessagePortOpen) {
467
+ return messagePort;
468
+ }
469
+ const eventPromise = getFirstEvent(messagePort, {
470
+ message: Message$1
471
+ });
472
+ messagePort.start();
473
+ const {
474
+ event,
475
+ type
476
+ } = await eventPromise;
477
+ if (type !== Message$1) {
478
+ throw new IpcError('Failed to wait for ipc message');
479
+ }
480
+ if (event.data !== readyMessage) {
481
+ throw new IpcError('unexpected first message');
482
+ }
483
+ return messagePort;
484
+ };
485
+ const signal$1 = messagePort => {
486
+ messagePort.start();
487
+ };
488
+ class IpcParentWithMessagePort extends Ipc {
489
+ getData = getData$2;
490
+ send(message) {
491
+ this._rawIpc.postMessage(message);
492
+ }
493
+ sendAndTransfer(message) {
494
+ const transfer = getTransferrables(message);
495
+ this._rawIpc.postMessage(message, transfer);
496
+ }
497
+ dispose() {
498
+ this._rawIpc.close();
499
+ }
500
+ onMessage(callback) {
501
+ this._rawIpc.addEventListener('message', callback);
502
+ }
503
+ onClose(callback) {}
504
+ }
505
+ const wrap$5 = messagePort => {
506
+ return new IpcParentWithMessagePort(messagePort);
507
+ };
508
+ const IpcParentWithMessagePort$1 = {
509
+ __proto__: null,
510
+ create: create$5$1,
511
+ signal: signal$1,
512
+ wrap: wrap$5
513
+ };
420
514
 
421
515
  const Two$1 = '2.0';
422
516
  const callbacks = Object.create(null);
@@ -632,7 +726,7 @@ const getErrorResponse = (id, error, preparePrettyError, logError) => {
632
726
  const errorProperty = getErrorProperty(error, prettyError);
633
727
  return create$1$1(id, errorProperty);
634
728
  };
635
- const create$3 = (message, result) => {
729
+ const create$4 = (message, result) => {
636
730
  return {
637
731
  jsonrpc: Two$1,
638
732
  id: message.id,
@@ -641,7 +735,7 @@ const create$3 = (message, result) => {
641
735
  };
642
736
  const getSuccessResponse = (message, result) => {
643
737
  const resultProperty = result ?? null;
644
- return create$3(message, resultProperty);
738
+ return create$4(message, resultProperty);
645
739
  };
646
740
  const getErrorResponseSimple = (id, error) => {
647
741
  return {
@@ -869,6 +963,86 @@ const listen$1 = async (module, options) => {
869
963
  const ipc = module.wrap(rawIpc);
870
964
  return ipc;
871
965
  };
966
+
967
+ /* eslint-disable @typescript-eslint/no-misused-promises */
968
+
969
+ const createSharedLazyRpc = factory => {
970
+ let rpcPromise;
971
+ const getOrCreate = () => {
972
+ if (!rpcPromise) {
973
+ rpcPromise = factory();
974
+ }
975
+ return rpcPromise;
976
+ };
977
+ return {
978
+ async dispose() {
979
+ const rpc = await getOrCreate();
980
+ await rpc.dispose();
981
+ },
982
+ async invoke(method, ...params) {
983
+ const rpc = await getOrCreate();
984
+ return rpc.invoke(method, ...params);
985
+ },
986
+ async invokeAndTransfer(method, ...params) {
987
+ const rpc = await getOrCreate();
988
+ return rpc.invokeAndTransfer(method, ...params);
989
+ },
990
+ async send(method, ...params) {
991
+ const rpc = await getOrCreate();
992
+ rpc.send(method, ...params);
993
+ }
994
+ };
995
+ };
996
+ const create$j = async ({
997
+ commandMap,
998
+ isMessagePortOpen,
999
+ send
1000
+ }) => {
1001
+ return createSharedLazyRpc(() => {
1002
+ return create$3({
1003
+ commandMap,
1004
+ isMessagePortOpen,
1005
+ send
1006
+ });
1007
+ });
1008
+ };
1009
+ const LazyTransferMessagePortRpcParent = {
1010
+ __proto__: null,
1011
+ create: create$j
1012
+ };
1013
+ const create$5 = async ({
1014
+ commandMap,
1015
+ isMessagePortOpen = true,
1016
+ messagePort
1017
+ }) => {
1018
+ // TODO create a commandMap per rpc instance
1019
+ register(commandMap);
1020
+ const rawIpc = await IpcParentWithMessagePort$1.create({
1021
+ isMessagePortOpen,
1022
+ messagePort
1023
+ });
1024
+ const ipc = IpcParentWithMessagePort$1.wrap(rawIpc);
1025
+ handleIpc(ipc);
1026
+ const rpc = createRpc(ipc);
1027
+ messagePort.start();
1028
+ return rpc;
1029
+ };
1030
+ const create$3 = async ({
1031
+ commandMap,
1032
+ isMessagePortOpen,
1033
+ send
1034
+ }) => {
1035
+ const {
1036
+ port1,
1037
+ port2
1038
+ } = new MessageChannel();
1039
+ await send(port1);
1040
+ return create$5({
1041
+ commandMap,
1042
+ isMessagePortOpen,
1043
+ messagePort: port2
1044
+ });
1045
+ };
872
1046
  const create$2$1 = async ({
873
1047
  commandMap
874
1048
  }) => {
@@ -903,15 +1077,25 @@ const createMockRpc = ({
903
1077
  return mockRpc;
904
1078
  };
905
1079
 
1080
+ const Button$1 = 1;
906
1081
  const Div$1 = 4;
907
1082
  const H1$1 = 5;
1083
+ const Input$1 = 6;
908
1084
  const Span$1 = 8;
1085
+ const Table$1 = 9;
1086
+ const TBody$1 = 10;
1087
+ const Td$1 = 11;
909
1088
  const Text$1 = 12;
1089
+ const Th$1 = 13;
1090
+ const THead$1 = 14;
1091
+ const Tr$1 = 15;
910
1092
  const Img$1 = 17;
1093
+ const Del$1 = 21;
911
1094
  const H2$1 = 22;
912
1095
  const H3$1 = 23;
913
1096
  const H4$1 = 24;
914
1097
  const H5$1 = 25;
1098
+ const H6$1 = 26;
915
1099
  const Article$1 = 27;
916
1100
  const Aside$1 = 28;
917
1101
  const Footer$1 = 29;
@@ -935,17 +1119,26 @@ const Cite$1 = 56;
935
1119
  const Data$1 = 57;
936
1120
  const Time$1 = 58;
937
1121
  const Tfoot$1 = 59;
1122
+ const Ul$1 = 60;
1123
+ const TextArea$1 = 62;
1124
+ const Select$1 = 63;
1125
+ const Option$1 = 64;
1126
+ const Code$1 = 65;
1127
+ const Label$1 = 66;
1128
+ const Dt$1 = 67;
1129
+ const Iframe$1 = 68;
938
1130
  const Reference = 100;
939
1131
 
940
1132
  const TargetName = 'event.target.name';
941
1133
 
1134
+ const EditorWorker = 99;
942
1135
  const RendererWorker = 1;
943
1136
 
944
1137
  const SetDom2 = 'Viewlet.setDom2';
945
1138
  const SetPatches = 'Viewlet.setPatches';
946
1139
 
947
1140
  const rpcs = Object.create(null);
948
- const set$2 = (id, rpc) => {
1141
+ const set$3 = (id, rpc) => {
949
1142
  rpcs[id] = rpc;
950
1143
  };
951
1144
  const get$1 = id => {
@@ -978,7 +1171,7 @@ const create$2 = rpcId => {
978
1171
  const mockRpc = createMockRpc({
979
1172
  commandMap
980
1173
  });
981
- set$2(rpcId, mockRpc);
1174
+ set$3(rpcId, mockRpc);
982
1175
  // @ts-ignore
983
1176
  mockRpc[Symbol.dispose] = () => {
984
1177
  remove(rpcId);
@@ -987,15 +1180,25 @@ const create$2 = rpcId => {
987
1180
  return mockRpc;
988
1181
  },
989
1182
  set(rpc) {
990
- set$2(rpcId, rpc);
1183
+ set$3(rpcId, rpc);
991
1184
  }
992
1185
  };
993
1186
  };
994
1187
 
1188
+ const {
1189
+ invoke: invoke$1,
1190
+ set: set$2
1191
+ } = create$2(EditorWorker);
1192
+
995
1193
  const {
996
1194
  invoke,
1195
+ invokeAndTransfer,
997
1196
  set: set$1
998
1197
  } = create$2(RendererWorker);
1198
+ const sendMessagePortToEditorWorker = async (port, rpcId) => {
1199
+ const command = 'HandleMessagePort.handleMessagePort';
1200
+ await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToEditorWorker', port, command, rpcId);
1201
+ };
999
1202
  const readFile = async uri => {
1000
1203
  return invoke('FileSystem.readFile', uri);
1001
1204
  };
@@ -1092,6 +1295,7 @@ const terminate = () => {
1092
1295
  const {
1093
1296
  get,
1094
1297
  getCommandIds,
1298
+ getKeys: getKeys$1,
1095
1299
  registerCommands,
1096
1300
  set,
1097
1301
  wrapCommand,
@@ -1106,6 +1310,7 @@ const create = (uid, uri, x, y, width, height, platform, assetDir) => {
1106
1310
  errorMessage: '',
1107
1311
  initial: true,
1108
1312
  parsedDom: [],
1313
+ parsedNodesChildNodeCount: 0,
1109
1314
  platform,
1110
1315
  uid,
1111
1316
  uri,
@@ -1115,7 +1320,7 @@ const create = (uid, uri, x, y, width, height, platform, assetDir) => {
1115
1320
  };
1116
1321
 
1117
1322
  const isEqual = (oldState, newState) => {
1118
- return oldState.warningCount === newState.warningCount && oldState.initial === newState.initial && oldState.content === newState.content && oldState.parsedDom === newState.parsedDom;
1323
+ return oldState.warningCount === newState.warningCount && oldState.initial === newState.initial && oldState.content === newState.content && oldState.parsedDom === newState.parsedDom && oldState.parsedNodesChildNodeCount === newState.parsedNodesChildNodeCount;
1119
1324
  };
1120
1325
 
1121
1326
  const RenderItems = 4;
@@ -1471,6 +1676,25 @@ const Cite = 'cite';
1471
1676
  const Data = 'data';
1472
1677
  const Time = 'time';
1473
1678
  const Tfoot = 'tfoot';
1679
+ const Button = 'button';
1680
+ const Input = 'input';
1681
+ const Code = 'code';
1682
+ const Form = 'form';
1683
+ const Table = 'table';
1684
+ const TBody = 'tbody';
1685
+ const THead = 'thead';
1686
+ const Tr = 'tr';
1687
+ const Td = 'td';
1688
+ const Th = 'th';
1689
+ const Ul = 'ul';
1690
+ const TextArea = 'textarea';
1691
+ const Select = 'select';
1692
+ const Option = 'option';
1693
+ const Label = 'label';
1694
+ const Dt = 'dt';
1695
+ const Iframe = 'iframe';
1696
+ const Del = 'del';
1697
+ const H6 = 'h6';
1474
1698
 
1475
1699
  const getVirtualDomTag = text => {
1476
1700
  switch (text) {
@@ -1484,22 +1708,32 @@ const getVirtualDomTag = text => {
1484
1708
  return Aside$1;
1485
1709
  case Br:
1486
1710
  return Br$1;
1711
+ case Button:
1712
+ return Button$1;
1487
1713
  case Cite:
1488
1714
  return Cite$1;
1715
+ case Code:
1716
+ return Code$1;
1489
1717
  case Data:
1490
1718
  return Data$1;
1491
1719
  case Dd:
1492
1720
  return Dd$1;
1721
+ case Del:
1722
+ return Del$1;
1493
1723
  case Div:
1494
1724
  return Div$1;
1495
1725
  case Dl:
1496
1726
  return Dl$1;
1727
+ case Dt:
1728
+ return Dt$1;
1497
1729
  case Figcaption:
1498
1730
  return Figcaption$1;
1499
1731
  case Figure:
1500
1732
  return Figure$1;
1501
1733
  case Footer:
1502
1734
  return Footer$1;
1735
+ case Form:
1736
+ return Div$1;
1503
1737
  case H1:
1504
1738
  return H1$1;
1505
1739
  case H2:
@@ -1510,18 +1744,28 @@ const getVirtualDomTag = text => {
1510
1744
  return H4$1;
1511
1745
  case H5:
1512
1746
  return H5$1;
1747
+ case H6:
1748
+ return H6$1;
1513
1749
  case Header:
1514
1750
  return Header$1;
1515
1751
  case Hr:
1516
1752
  return Hr$1;
1753
+ case Iframe:
1754
+ return Iframe$1;
1517
1755
  case Img:
1518
1756
  return Img$1;
1757
+ case Input:
1758
+ return Input$1;
1759
+ case Label:
1760
+ return Label$1;
1519
1761
  case Li:
1520
1762
  return Li$1;
1521
1763
  case Nav:
1522
1764
  return Nav$1;
1523
1765
  case Ol:
1524
1766
  return Ol$1;
1767
+ case Option:
1768
+ return Option$1;
1525
1769
  case P:
1526
1770
  return P$1;
1527
1771
  case Pre:
@@ -1530,12 +1774,30 @@ const getVirtualDomTag = text => {
1530
1774
  return Search$1;
1531
1775
  case Section:
1532
1776
  return Section$1;
1777
+ case Select:
1778
+ return Select$1;
1533
1779
  case Span:
1534
1780
  return Span$1;
1781
+ case Table:
1782
+ return Table$1;
1783
+ case TBody:
1784
+ return TBody$1;
1785
+ case Td:
1786
+ return Td$1;
1787
+ case TextArea:
1788
+ return TextArea$1;
1535
1789
  case Tfoot:
1536
1790
  return Tfoot$1;
1791
+ case Th:
1792
+ return Th$1;
1793
+ case THead:
1794
+ return THead$1;
1537
1795
  case Time:
1538
1796
  return Time$1;
1797
+ case Tr:
1798
+ return Tr$1;
1799
+ case Ul:
1800
+ return Ul$1;
1539
1801
  default:
1540
1802
  return Div$1;
1541
1803
  }
@@ -1564,9 +1826,29 @@ const EndCommentTag = 19;
1564
1826
  const Text = 20;
1565
1827
  const CommentStart = 21;
1566
1828
 
1829
+ const isDefaultAllowedAttribute = (attributeName, defaultAllowedAttributes) => {
1830
+ // Allow data-* attributes
1831
+ if (attributeName.startsWith('data-')) {
1832
+ return true;
1833
+ }
1834
+ // Allow aria-* attributes
1835
+ if (attributeName.startsWith('aria-')) {
1836
+ return true;
1837
+ }
1838
+ // Allow role attribute
1839
+ if (attributeName === 'role') {
1840
+ return true;
1841
+ }
1842
+ // Check if in default list
1843
+ return defaultAllowedAttributes.includes(attributeName);
1844
+ };
1845
+
1567
1846
  const isSelfClosingTag = tag => {
1568
1847
  switch (tag) {
1848
+ case Br:
1849
+ case Hr:
1569
1850
  case Img:
1851
+ case Input:
1570
1852
  return true;
1571
1853
  default:
1572
1854
  return false;
@@ -1835,9 +2117,14 @@ const tokenizeHtml = text => {
1835
2117
  return tokens;
1836
2118
  };
1837
2119
 
1838
- const parseHtml = (html, allowedAttributes) => {
2120
+ const parseHtml = (html, allowedAttributes = [], defaultAllowedAttributes = []) => {
1839
2121
  string(html);
1840
2122
  array(allowedAttributes);
2123
+ array(defaultAllowedAttributes);
2124
+
2125
+ // Combine default allowed attributes with any additional ones provided
2126
+ const allAllowedAttributes = new Set([...defaultAllowedAttributes, ...allowedAttributes]);
2127
+ const useBuiltInDefaults = allowedAttributes.length === 0;
1841
2128
  const tokens = tokenizeHtml(html);
1842
2129
  const dom = [];
1843
2130
  const root = {
@@ -1847,44 +2134,159 @@ const parseHtml = (html, allowedAttributes) => {
1847
2134
  let current = root;
1848
2135
  const stack = [root];
1849
2136
  let attributeName = '';
2137
+ let lastTagWasSelfClosing = false;
1850
2138
  for (const token of tokens) {
1851
2139
  switch (token.type) {
1852
2140
  case AttributeName:
1853
2141
  attributeName = token.text;
1854
- if (attributeName === 'class') {
1855
- attributeName = 'className';
1856
- }
1857
2142
  break;
1858
2143
  case AttributeValue:
1859
- if (allowedAttributes.includes(attributeName)) {
1860
- current[attributeName] = token.text;
2144
+ if (allAllowedAttributes.has(attributeName) || useBuiltInDefaults && isDefaultAllowedAttribute(attributeName, defaultAllowedAttributes)) {
2145
+ const finalAttributeName = attributeName === 'class' ? 'className' : attributeName;
2146
+ current[finalAttributeName] = token.text;
1861
2147
  }
1862
2148
  attributeName = '';
1863
2149
  break;
2150
+ case ClosingAngleBracket:
2151
+ // Handle boolean attributes (attributes without values)
2152
+ if (attributeName && (allAllowedAttributes.has(attributeName) || useBuiltInDefaults && isDefaultAllowedAttribute(attributeName, defaultAllowedAttributes))) {
2153
+ const finalAttributeName = attributeName === 'class' ? 'className' : attributeName;
2154
+ current[finalAttributeName] = attributeName;
2155
+ }
2156
+ attributeName = '';
2157
+ // Return to parent if the current tag is self-closing
2158
+ if (lastTagWasSelfClosing) {
2159
+ current = stack.at(-1) || root;
2160
+ lastTagWasSelfClosing = false;
2161
+ }
2162
+ break;
1864
2163
  case Content:
1865
2164
  current.childCount++;
1866
2165
  dom.push(text(parseText(token.text)));
1867
2166
  break;
1868
2167
  case TagNameEnd:
1869
- stack.pop();
2168
+ if (stack.length > 1) {
2169
+ stack.pop();
2170
+ }
1870
2171
  current = stack.at(-1) || root;
1871
2172
  break;
1872
2173
  case TagNameStart:
1873
2174
  current.childCount++;
1874
- current = {
2175
+ const newNode = {
1875
2176
  childCount: 0,
1876
2177
  type: getVirtualDomTag(token.text)
1877
2178
  };
1878
- dom.push(current);
1879
- if (!isSelfClosingTag(token.text)) {
2179
+ dom.push(newNode);
2180
+ current = newNode;
2181
+ lastTagWasSelfClosing = isSelfClosingTag(token.text);
2182
+ if (!lastTagWasSelfClosing) {
1880
2183
  stack.push(current);
1881
2184
  }
1882
2185
  break;
2186
+ case WhitespaceInsideOpeningTag:
2187
+ // Handle boolean attributes (attributes without values)
2188
+ if (attributeName && (allAllowedAttributes.has(attributeName) || useBuiltInDefaults && isDefaultAllowedAttribute(attributeName, defaultAllowedAttributes))) {
2189
+ const finalAttributeName = attributeName === 'class' ? 'className' : attributeName;
2190
+ current[finalAttributeName] = attributeName;
2191
+ }
2192
+ attributeName = '';
2193
+ break;
1883
2194
  }
1884
2195
  }
2196
+ try {
2197
+ Object.defineProperty(dom, 'rootChildCount', {
2198
+ configurable: true,
2199
+ enumerable: false,
2200
+ value: root.childCount
2201
+ });
2202
+ } catch {
2203
+ dom.rootChildCount = root.childCount;
2204
+ }
1885
2205
  return dom;
1886
2206
  };
1887
2207
 
2208
+ const handleEditorChanged = async () => {
2209
+ // Get all preview instance keys
2210
+ const previewKeys = getKeys$1();
2211
+
2212
+ // Get all editor keys from the editor worker
2213
+ const editorKeys = await invoke$1('Editor.getKeys');
2214
+
2215
+ // For each preview instance
2216
+ for (const previewUid of previewKeys) {
2217
+ const {
2218
+ newState: state
2219
+ } = get(previewUid);
2220
+
2221
+ // Skip if no URI is set
2222
+ if (!state.uri) {
2223
+ continue;
2224
+ }
2225
+
2226
+ // Find the editor that matches our preview's URI
2227
+ let matchingEditorUid = null;
2228
+ for (const editorKey of editorKeys) {
2229
+ const editorUid = Number.parseFloat(editorKey);
2230
+ const editorUri = await invoke$1('Editor.getUri', editorUid);
2231
+ if (editorUri === state.uri) {
2232
+ matchingEditorUid = editorUid;
2233
+ break;
2234
+ }
2235
+ }
2236
+
2237
+ // If we found a matching editor, get its text and update the preview
2238
+ if (matchingEditorUid !== null) {
2239
+ try {
2240
+ const content = await invoke$1('Editor.getText', matchingEditorUid);
2241
+ const parsedDom = parseHtml(content, []);
2242
+ const updatedState = {
2243
+ ...state,
2244
+ content,
2245
+ errorMessage: '',
2246
+ parsedDom
2247
+ };
2248
+ set(previewUid, state, updatedState);
2249
+ } catch (error) {
2250
+ // If getting text fails, update with error message
2251
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
2252
+ const updatedState = {
2253
+ ...state,
2254
+ content: '',
2255
+ errorMessage,
2256
+ parsedDom: []
2257
+ };
2258
+ set(previewUid, state, updatedState);
2259
+ }
2260
+ }
2261
+ }
2262
+
2263
+ // Rerender all previews after updates are complete
2264
+ await invoke('Preview.rerender');
2265
+ };
2266
+
2267
+ const getParsedNodesChildNodeCount = parsedDom => {
2268
+ array(parsedDom);
2269
+ const rootCountFromParse = parsedDom.rootChildCount;
2270
+ if (typeof rootCountFromParse === 'number') {
2271
+ return rootCountFromParse;
2272
+ }
2273
+ let rootChildCount = 0;
2274
+ let i = 0;
2275
+ while (i < parsedDom.length) {
2276
+ rootChildCount++;
2277
+
2278
+ // skip the entire subtree of the current node
2279
+ let toSkip = parsedDom[i].childCount;
2280
+ i++;
2281
+ while (toSkip > 0 && i < parsedDom.length) {
2282
+ toSkip -= 1;
2283
+ toSkip += parsedDom[i].childCount;
2284
+ i++;
2285
+ }
2286
+ }
2287
+ return rootChildCount;
2288
+ };
2289
+
1888
2290
  const updateContent = async (state, uri) => {
1889
2291
  try {
1890
2292
  // Read the file content using RendererWorker RPC
@@ -1892,11 +2294,13 @@ const updateContent = async (state, uri) => {
1892
2294
  const content = await readFile(uri);
1893
2295
 
1894
2296
  // Parse the content into virtual DOM
1895
- const parsedDom = parseHtml(content, []);
2297
+ const parsedDom = parseHtml(content);
2298
+ const parsedNodesChildNodeCount = getParsedNodesChildNodeCount(parsedDom);
1896
2299
  return {
1897
2300
  content,
1898
2301
  errorMessage: '',
1899
- parsedDom
2302
+ parsedDom,
2303
+ parsedNodesChildNodeCount
1900
2304
  };
1901
2305
  } catch (error) {
1902
2306
  // If file reading or parsing fails, return empty content and parsedDom with error message
@@ -1904,7 +2308,8 @@ const updateContent = async (state, uri) => {
1904
2308
  return {
1905
2309
  content: '',
1906
2310
  errorMessage,
1907
- parsedDom: []
2311
+ parsedDom: [],
2312
+ parsedNodesChildNodeCount: 0
1908
2313
  };
1909
2314
  }
1910
2315
  };
@@ -1913,21 +2318,50 @@ const handleFileEdited = async state => {
1913
2318
  const {
1914
2319
  content,
1915
2320
  errorMessage,
1916
- parsedDom
2321
+ parsedDom,
2322
+ parsedNodesChildNodeCount
1917
2323
  } = await updateContent(state, state.uri);
1918
2324
  return {
1919
2325
  ...state,
1920
2326
  content,
1921
2327
  errorMessage,
1922
- parsedDom
2328
+ parsedDom,
2329
+ parsedNodesChildNodeCount
1923
2330
  };
1924
2331
  };
1925
2332
 
1926
2333
  const loadContent = async state => {
2334
+ // Try to register to receive editor change notifications from the editor worker.
2335
+ // Use dynamic access and ignore errors so this is safe in environments where
2336
+ // the EditorWorker / ListenerType are not available (e.g. unit tests).
2337
+ const EditorChange = 1;
2338
+ const rpcId = 9112;
2339
+ try {
2340
+ await invoke$1('Listener.register', EditorChange, rpcId);
2341
+ } catch (error) {
2342
+ console.error(error);
2343
+ }
2344
+
2345
+ // Read and parse file contents if we have a URI
2346
+ const {
2347
+ content,
2348
+ errorMessage,
2349
+ parsedDom,
2350
+ parsedNodesChildNodeCount
2351
+ } = state.uri ? await updateContent(state, state.uri) : {
2352
+ content: state.content,
2353
+ errorMessage: state.errorMessage,
2354
+ parsedDom: state.parsedDom,
2355
+ parsedNodesChildNodeCount: state.parsedNodesChildNodeCount
2356
+ };
1927
2357
  return {
1928
2358
  ...state,
2359
+ content,
1929
2360
  errorCount: 0,
2361
+ errorMessage,
1930
2362
  initial: false,
2363
+ parsedDom,
2364
+ parsedNodesChildNodeCount,
1931
2365
  warningCount: 1
1932
2366
  };
1933
2367
  };
@@ -1947,18 +2381,22 @@ const getEmptyPreviewDom = () => {
1947
2381
  };
1948
2382
 
1949
2383
  const getPreviewDom = state => {
1950
- if (!state.uri) {
2384
+ const {
2385
+ parsedDom,
2386
+ parsedNodesChildNodeCount,
2387
+ uri
2388
+ } = state;
2389
+ if (!uri) {
1951
2390
  return getEmptyPreviewDom();
1952
2391
  }
1953
2392
 
1954
2393
  // If parsedDom is available, render it as children of the wrapper
1955
- if (state.parsedDom && state.parsedDom.length > 0) {
2394
+ if (parsedDom && parsedDom.length > 0) {
1956
2395
  return [{
1957
- childCount: 1,
1958
- // TODO
2396
+ childCount: parsedNodesChildNodeCount,
1959
2397
  className: 'Viewlet Preview',
1960
2398
  type: Div$1
1961
- }, ...state.parsedDom];
2399
+ }, ...parsedDom];
1962
2400
  }
1963
2401
  return [{
1964
2402
  childCount: 1,
@@ -2034,6 +2472,18 @@ const renderEventListeners = () => {
2034
2472
  }];
2035
2473
  };
2036
2474
 
2475
+ const rerender = state => {
2476
+ // Create a new copy of parsedDom array to trigger diff
2477
+ const parsedDom = [...state.parsedDom];
2478
+
2479
+ // Return a new state object with the copied parsedDom
2480
+ // This will cause DiffItems.isEqual to return false since parsedDom reference changed
2481
+ return {
2482
+ ...state,
2483
+ parsedDom
2484
+ };
2485
+ };
2486
+
2037
2487
  const resize = (state, dimensions) => {
2038
2488
  return {
2039
2489
  ...state,
@@ -2051,18 +2501,21 @@ const setUri = async (state, uri) => {
2051
2501
  const {
2052
2502
  content,
2053
2503
  errorMessage,
2054
- parsedDom
2504
+ parsedDom,
2505
+ parsedNodesChildNodeCount
2055
2506
  } = await updateContent(state, uri);
2056
2507
  return {
2057
2508
  ...state,
2058
2509
  content,
2059
2510
  errorMessage,
2060
2511
  parsedDom,
2512
+ parsedNodesChildNodeCount,
2061
2513
  uri
2062
2514
  };
2063
2515
  };
2064
2516
 
2065
2517
  const commandMap = {
2518
+ handleEditorChanged: handleEditorChanged,
2066
2519
  'Preview.create': create,
2067
2520
  'Preview.diff2': diff2,
2068
2521
  'Preview.getCommandIds': getCommandIds,
@@ -2070,6 +2523,7 @@ const commandMap = {
2070
2523
  'Preview.loadContent': wrapCommand(loadContent),
2071
2524
  'Preview.render2': render2,
2072
2525
  'Preview.renderEventListeners': renderEventListeners,
2526
+ 'Preview.rerender': wrapCommand(rerender),
2073
2527
  'Preview.resize': wrapCommand(resize),
2074
2528
  'Preview.saveState': wrapGetter(saveState),
2075
2529
  'Preview.setUri': wrapCommand(setUri),
@@ -2082,6 +2536,11 @@ const listen = async () => {
2082
2536
  commandMap: commandMap
2083
2537
  });
2084
2538
  set$1(rpc);
2539
+ const editorRpc = await LazyTransferMessagePortRpcParent.create({
2540
+ commandMap: {},
2541
+ send: port => sendMessagePortToEditorWorker(port, 9112)
2542
+ });
2543
+ set$2(editorRpc);
2085
2544
  };
2086
2545
 
2087
2546
  const main = async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/preview-worker",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "Preview Worker",
5
5
  "repository": {
6
6
  "type": "git",