@sofereditor/react 0.1.0 → 0.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/index.js CHANGED
@@ -1153,6 +1153,7 @@ function renderInline(delta, keyPrefix) {
1153
1153
  } else if (isImageEmbed(op.insert)) {
1154
1154
  const key = `${keyPrefix}-${offset}`;
1155
1155
  const layout = op.insert.layout ?? "inline";
1156
+ const commentMarkId = op.attributes?.comment?.markId;
1156
1157
  if (layout === "wrap-left" || layout === "wrap-right") {
1157
1158
  wrapAnchors.push(
1158
1159
  /* @__PURE__ */ jsx6(ImageEmbedView, { embed: op.insert, offset, wrapAnchor: true }, `${key}-anchor`)
@@ -1170,7 +1171,14 @@ function renderInline(delta, keyPrefix) {
1170
1171
  )
1171
1172
  );
1172
1173
  } else {
1173
- body.push(/* @__PURE__ */ jsx6(ImageEmbedView, { embed: op.insert, offset }, key));
1174
+ const imgNode = /* @__PURE__ */ jsx6(ImageEmbedView, { embed: op.insert, offset }, key);
1175
+ if (commentMarkId) {
1176
+ body.push(
1177
+ /* @__PURE__ */ jsx6("span", { "data-comment-id": commentMarkId, className: "ed-comment", children: imgNode }, `${key}-cmt`)
1178
+ );
1179
+ } else {
1180
+ body.push(imgNode);
1181
+ }
1174
1182
  }
1175
1183
  offset += 1;
1176
1184
  } else if (op.insert != null) {
@@ -1261,6 +1269,8 @@ function ImageEmbedViewImpl({
1261
1269
  height: embed.height,
1262
1270
  contentEditable: false,
1263
1271
  draggable: false,
1272
+ decoding: "sync",
1273
+ loading: "eager",
1264
1274
  "data-embed": "image",
1265
1275
  "data-embed-offset": offset,
1266
1276
  "data-embed-align": align ?? "",
@@ -1665,8 +1675,9 @@ var A4_PAGE = {
1665
1675
  marginLeft: 96,
1666
1676
  marginRight: 96
1667
1677
  };
1668
- function contentHeight(g) {
1669
- return g.height - g.marginTop - g.marginBottom;
1678
+ function contentHeight(g, pageIndex = 0) {
1679
+ const extra = pageIndex === 0 ? g.firstPageExtraTop ?? 0 : 0;
1680
+ return g.height - g.marginTop - g.marginBottom - extra;
1670
1681
  }
1671
1682
  function contentWidth(g) {
1672
1683
  return g.width - g.marginLeft - g.marginRight;
@@ -1683,14 +1694,19 @@ function defaultPageLayout(topLevelCount) {
1683
1694
  function usePageLayout(rootRef, geometry, topLevelCount, deps) {
1684
1695
  const [phase, setPhase] = useState6("measure");
1685
1696
  const [layout, setLayout] = useState6(() => defaultPageLayout(topLevelCount));
1686
- const lastDepsRef = useRef7([]);
1687
- const currentDeps = [...deps, topLevelCount, geometry];
1688
- if (!shallowArrayEqual(lastDepsRef.current, currentDeps)) {
1697
+ const lastDepsRef = useRef7(null);
1698
+ useLayoutEffect3(() => {
1699
+ const currentDeps = [...deps, topLevelCount, geometry];
1700
+ if (lastDepsRef.current !== null && shallowArrayEqual(lastDepsRef.current, currentDeps)) {
1701
+ return;
1702
+ }
1689
1703
  lastDepsRef.current = currentDeps;
1690
- if (phase !== "measure") setPhase("measure");
1691
- const next = defaultPageLayout(topLevelCount);
1692
- if (!layoutsDeepEqual(layout, next)) setLayout(next);
1693
- }
1704
+ setLayout((prev) => {
1705
+ const next = defaultPageLayout(topLevelCount);
1706
+ return layoutsDeepEqual(prev, next) ? prev : next;
1707
+ });
1708
+ setPhase("measure");
1709
+ });
1694
1710
  useLayoutEffect3(() => {
1695
1711
  if (phase !== "measure") return;
1696
1712
  const root = rootRef.current;
@@ -1700,19 +1716,20 @@ function usePageLayout(rootRef, geometry, topLevelCount, deps) {
1700
1716
  if (!layoutsDeepEqual(layout, next)) setLayout(next);
1701
1717
  setPhase("stable");
1702
1718
  }, [phase, layout, geometry, topLevelCount, rootRef]);
1703
- return layout;
1719
+ return { layout, phase };
1704
1720
  }
1705
1721
  function isUnfragmented(layout) {
1706
1722
  return layout.pages.length === 1 && layout.pages[0].slots.every(
1707
- (s) => s.fragment === void 0 && s.tableFragment === void 0
1723
+ (s) => s.fragment === void 0 && s.tableFragment === void 0 && s.listFragment === void 0
1708
1724
  );
1709
1725
  }
1710
1726
  function computeLayout(root, geometry, topLevelCount) {
1711
1727
  const tops = collectTopLevelElements(root);
1712
1728
  if (tops.length === 0) return defaultPageLayout(topLevelCount);
1713
- const limit = contentHeight(geometry);
1729
+ const limitFor = (pageIndex) => contentHeight(geometry, pageIndex);
1714
1730
  const pages = [{ slots: [] }];
1715
1731
  let currentPage = pages[0];
1732
+ let limit = limitFor(0);
1716
1733
  let used = 0;
1717
1734
  const cap = Math.min(tops.length, topLevelCount);
1718
1735
  for (let i = 0; i < cap; i++) {
@@ -1724,14 +1741,23 @@ function computeLayout(root, geometry, topLevelCount) {
1724
1741
  continue;
1725
1742
  }
1726
1743
  if (isFragmentable(el)) {
1727
- const result = placeFragmentedBlock(el, i, pages, used, limit);
1744
+ const result = placeFragmentedBlock(el, i, pages, used, limitFor);
1728
1745
  currentPage = pages[pages.length - 1];
1746
+ limit = limitFor(pages.length - 1);
1729
1747
  used = result.lastUsed;
1730
1748
  continue;
1731
1749
  }
1732
1750
  if (isTableTopLevel(el)) {
1733
- const result = placeFragmentedTable(el, i, pages, used, limit);
1751
+ const result = placeFragmentedTable(el, i, pages, used, limitFor);
1734
1752
  currentPage = pages[pages.length - 1];
1753
+ limit = limitFor(pages.length - 1);
1754
+ used = result.lastUsed;
1755
+ continue;
1756
+ }
1757
+ if (isListTopLevel(el)) {
1758
+ const result = placeFragmentedList(el, i, pages, used, limitFor);
1759
+ currentPage = pages[pages.length - 1];
1760
+ limit = limitFor(pages.length - 1);
1735
1761
  used = result.lastUsed;
1736
1762
  continue;
1737
1763
  }
@@ -1739,10 +1765,12 @@ function computeLayout(root, geometry, topLevelCount) {
1739
1765
  currentPage.slots.push({ topLevelIndex: i });
1740
1766
  pages.push({ slots: [] });
1741
1767
  currentPage = pages[pages.length - 1];
1768
+ limit = limitFor(pages.length - 1);
1742
1769
  used = 0;
1743
1770
  } else {
1744
1771
  pages.push({ slots: [] });
1745
1772
  currentPage = pages[pages.length - 1];
1773
+ limit = limitFor(pages.length - 1);
1746
1774
  currentPage.slots.push({ topLevelIndex: i });
1747
1775
  used = h;
1748
1776
  }
@@ -1750,7 +1778,8 @@ function computeLayout(root, geometry, topLevelCount) {
1750
1778
  if (pages.length > 1 && pages[pages.length - 1].slots.length === 0) pages.pop();
1751
1779
  return { pages };
1752
1780
  }
1753
- function placeFragmentedBlock(el, topLevelIndex, pages, startUsed, limit) {
1781
+ function placeFragmentedBlock(el, topLevelIndex, pages, startUsed, limitFor) {
1782
+ let limit = limitFor(pages.length - 1);
1754
1783
  const totalChars = getBlockTextLength(el);
1755
1784
  if (totalChars === 0) {
1756
1785
  const h = elTotalHeight(el);
@@ -1758,6 +1787,7 @@ function placeFragmentedBlock(el, topLevelIndex, pages, startUsed, limit) {
1758
1787
  if (startUsed + h > limit && currentPage2.slots.length > 0) {
1759
1788
  pages.push({ slots: [] });
1760
1789
  currentPage2 = pages[pages.length - 1];
1790
+ limit = limitFor(pages.length - 1);
1761
1791
  startUsed = 0;
1762
1792
  }
1763
1793
  currentPage2.slots.push({ topLevelIndex });
@@ -1770,6 +1800,7 @@ function placeFragmentedBlock(el, topLevelIndex, pages, startUsed, limit) {
1770
1800
  if (startUsed + h > limit && currentPage2.slots.length > 0) {
1771
1801
  pages.push({ slots: [] });
1772
1802
  currentPage2 = pages[pages.length - 1];
1803
+ limit = limitFor(pages.length - 1);
1773
1804
  startUsed = 0;
1774
1805
  }
1775
1806
  currentPage2.slots.push({ topLevelIndex });
@@ -1784,6 +1815,7 @@ function placeFragmentedBlock(el, topLevelIndex, pages, startUsed, limit) {
1784
1815
  if (available <= 0) {
1785
1816
  pages.push({ slots: [] });
1786
1817
  currentPage = pages[pages.length - 1];
1818
+ limit = limitFor(pages.length - 1);
1787
1819
  used = 0;
1788
1820
  continue;
1789
1821
  }
@@ -1800,6 +1832,7 @@ function placeFragmentedBlock(el, topLevelIndex, pages, startUsed, limit) {
1800
1832
  } else {
1801
1833
  pages.push({ slots: [] });
1802
1834
  currentPage = pages[pages.length - 1];
1835
+ limit = limitFor(pages.length - 1);
1803
1836
  used = 0;
1804
1837
  continue;
1805
1838
  }
@@ -1817,6 +1850,7 @@ function placeFragmentedBlock(el, topLevelIndex, pages, startUsed, limit) {
1817
1850
  if (lineIdx < lines.length) {
1818
1851
  pages.push({ slots: [] });
1819
1852
  currentPage = pages[pages.length - 1];
1853
+ limit = limitFor(pages.length - 1);
1820
1854
  used = 0;
1821
1855
  }
1822
1856
  }
@@ -1905,7 +1939,8 @@ function isFragmentable(el) {
1905
1939
  function isTableTopLevel(el) {
1906
1940
  return el.querySelector(':scope > table[data-block-type="table"]') !== null;
1907
1941
  }
1908
- function placeFragmentedTable(el, topLevelIndex, pages, startUsed, limit) {
1942
+ function placeFragmentedTable(el, topLevelIndex, pages, startUsed, limitFor) {
1943
+ let limit = limitFor(pages.length - 1);
1909
1944
  const table = el.querySelector(':scope > table[data-block-type="table"]');
1910
1945
  const tbody = table?.querySelector(":scope > tbody");
1911
1946
  const trs = tbody ? Array.from(tbody.children) : [];
@@ -1915,6 +1950,7 @@ function placeFragmentedTable(el, topLevelIndex, pages, startUsed, limit) {
1915
1950
  if (startUsed + h > limit && currentPage2.slots.length > 0) {
1916
1951
  pages.push({ slots: [] });
1917
1952
  currentPage2 = pages[pages.length - 1];
1953
+ limit = limitFor(pages.length - 1);
1918
1954
  startUsed = 0;
1919
1955
  }
1920
1956
  currentPage2.slots.push({ topLevelIndex });
@@ -1946,6 +1982,7 @@ function placeFragmentedTable(el, topLevelIndex, pages, startUsed, limit) {
1946
1982
  if (available <= 0) {
1947
1983
  pages.push({ slots: [] });
1948
1984
  currentPage = pages[pages.length - 1];
1985
+ limit = limitFor(pages.length - 1);
1949
1986
  used = 0;
1950
1987
  continue;
1951
1988
  }
@@ -1964,6 +2001,7 @@ function placeFragmentedTable(el, topLevelIndex, pages, startUsed, limit) {
1964
2001
  } else {
1965
2002
  pages.push({ slots: [] });
1966
2003
  currentPage = pages[pages.length - 1];
2004
+ limit = limitFor(pages.length - 1);
1967
2005
  used = 0;
1968
2006
  continue;
1969
2007
  }
@@ -1981,6 +2019,77 @@ function placeFragmentedTable(el, topLevelIndex, pages, startUsed, limit) {
1981
2019
  if (rowIdx < trs.length) {
1982
2020
  pages.push({ slots: [] });
1983
2021
  currentPage = pages[pages.length - 1];
2022
+ limit = limitFor(pages.length - 1);
2023
+ used = 0;
2024
+ }
2025
+ }
2026
+ return { lastUsed: used };
2027
+ }
2028
+ function isListTopLevel(el) {
2029
+ if (el.tagName !== "OL" && el.tagName !== "UL") return false;
2030
+ return el.classList.contains("ed-list");
2031
+ }
2032
+ function placeFragmentedList(el, topLevelIndex, pages, startUsed, limitFor) {
2033
+ let limit = limitFor(pages.length - 1);
2034
+ const items = [];
2035
+ for (const c of Array.from(el.children)) {
2036
+ if (c instanceof HTMLLIElement && c.classList.contains("ed-listitem")) {
2037
+ items.push(c);
2038
+ }
2039
+ }
2040
+ if (items.length === 0) {
2041
+ const h = elTotalHeight(el);
2042
+ let currentPage2 = pages[pages.length - 1];
2043
+ if (startUsed + h > limit && currentPage2.slots.length > 0) {
2044
+ pages.push({ slots: [] });
2045
+ currentPage2 = pages[pages.length - 1];
2046
+ limit = limitFor(pages.length - 1);
2047
+ startUsed = 0;
2048
+ }
2049
+ currentPage2.slots.push({ topLevelIndex });
2050
+ return { lastUsed: startUsed + h };
2051
+ }
2052
+ const heights = items.map((li) => elTotalHeight(li));
2053
+ const sumHeights = heights.reduce((a, b) => a + b, 0);
2054
+ const wrapperOverhead = Math.max(0, elTotalHeight(el) - sumHeights);
2055
+ let i = 0;
2056
+ let currentPage = pages[pages.length - 1];
2057
+ let used = startUsed;
2058
+ let fragIndex = 0;
2059
+ while (i < items.length) {
2060
+ const available = Math.max(0, limit - used);
2061
+ const isFirstFragment = fragIndex === 0;
2062
+ let bestK = -1;
2063
+ let runningH = isFirstFragment ? wrapperOverhead : 0;
2064
+ for (let k = i; k < items.length; k++) {
2065
+ runningH += heights[k];
2066
+ if (runningH <= available) bestK = k;
2067
+ else break;
2068
+ }
2069
+ if (bestK < 0) {
2070
+ if (currentPage.slots.length === 0) {
2071
+ bestK = i;
2072
+ } else {
2073
+ pages.push({ slots: [] });
2074
+ currentPage = pages[pages.length - 1];
2075
+ limit = limitFor(pages.length - 1);
2076
+ used = 0;
2077
+ continue;
2078
+ }
2079
+ }
2080
+ currentPage.slots.push({
2081
+ topLevelIndex,
2082
+ listFragment: { index: fragIndex, itemStart: i, itemEnd: bestK + 1 }
2083
+ });
2084
+ let consumed = isFirstFragment ? wrapperOverhead : 0;
2085
+ for (let k = i; k <= bestK; k++) consumed += heights[k];
2086
+ used += consumed;
2087
+ i = bestK + 1;
2088
+ fragIndex++;
2089
+ if (i < items.length) {
2090
+ pages.push({ slots: [] });
2091
+ currentPage = pages[pages.length - 1];
2092
+ limit = limitFor(pages.length - 1);
1984
2093
  used = 0;
1985
2094
  }
1986
2095
  }
@@ -2056,6 +2165,14 @@ function layoutsDeepEqual(a, b) {
2056
2165
  return false;
2057
2166
  }
2058
2167
  }
2168
+ const lA = sA[j].listFragment;
2169
+ const lB = sB[j].listFragment;
2170
+ if (lA || lB) {
2171
+ if (!lA || !lB) return false;
2172
+ if (lA.index !== lB.index || lA.itemStart !== lB.itemStart || lA.itemEnd !== lB.itemEnd) {
2173
+ return false;
2174
+ }
2175
+ }
2059
2176
  }
2060
2177
  }
2061
2178
  return true;
@@ -2082,7 +2199,7 @@ function usePageSettings(doc) {
2082
2199
  return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
2083
2200
  }
2084
2201
  function shallowEqual(a, b) {
2085
- return a.width === b.width && a.height === b.height && a.marginTop === b.marginTop && a.marginBottom === b.marginBottom && a.marginLeft === b.marginLeft && a.marginRight === b.marginRight && a.preset === b.preset;
2202
+ return a.width === b.width && a.height === b.height && a.marginTop === b.marginTop && a.marginBottom === b.marginBottom && a.marginLeft === b.marginLeft && a.marginRight === b.marginRight && a.preset === b.preset && a.systemHeaderTitle === b.systemHeaderTitle && a.systemHeaderAuthor === b.systemHeaderAuthor;
2086
2203
  }
2087
2204
 
2088
2205
  // src/useEditor.ts
@@ -2806,8 +2923,33 @@ function Editor({
2806
2923
  }
2807
2924
  return;
2808
2925
  }
2809
- case "insertParagraph":
2810
2926
  case "insertLineBreak": {
2927
+ e.preventDefault();
2928
+ if (ed.getBlockType() === "listItem") {
2929
+ insertText(ctx, "\n", ed.consumePendingMarks());
2930
+ return;
2931
+ }
2932
+ const selEmbed = ed.getSelectedEmbed();
2933
+ if (selEmbed) {
2934
+ const off = selEmbed.offset + 1;
2935
+ ctx.setSelection({
2936
+ anchor: {
2937
+ blockIndex: selEmbed.blockIndex,
2938
+ cellIndex: selEmbed.cellIndex,
2939
+ offset: off
2940
+ },
2941
+ focus: {
2942
+ blockIndex: selEmbed.blockIndex,
2943
+ cellIndex: selEmbed.cellIndex,
2944
+ offset: off
2945
+ }
2946
+ });
2947
+ }
2948
+ if (ed.splitListItem()) return;
2949
+ insertParagraph(ctx);
2950
+ return;
2951
+ }
2952
+ case "insertParagraph": {
2811
2953
  e.preventDefault();
2812
2954
  const selectedEmbed2 = ed.getSelectedEmbed();
2813
2955
  if (selectedEmbed2) {
@@ -2906,6 +3048,20 @@ function Editor({
2906
3048
  else ed.indentList();
2907
3049
  return;
2908
3050
  }
3051
+ if (!mod && (e.key === "Home" || e.key === "End")) {
3052
+ const root = rootRef.current;
3053
+ const sel = window.getSelection();
3054
+ const sm = sel?.modify;
3055
+ if (root && sel && sel.rangeCount > 0 && typeof sm === "function") {
3056
+ e.preventDefault();
3057
+ const alter = e.shiftKey ? "extend" : "move";
3058
+ const direction = e.key === "Home" ? "left" : "right";
3059
+ sm.call(sel, alter, direction, "lineboundary");
3060
+ const modelSel = readDomSelection(root);
3061
+ if (modelSel) ctxRef.current.setSelection(modelSel);
3062
+ }
3063
+ return;
3064
+ }
2909
3065
  if (!mod) return;
2910
3066
  const key = e.key.toLowerCase();
2911
3067
  if (!e.shiftKey && !e.altKey && key === "z") {
@@ -2962,26 +3118,47 @@ function Editor({
2962
3118
  }
2963
3119
  }, []);
2964
3120
  const topLevels = useMemo4(() => groupTopLevels(snapshot.blocks), [snapshot]);
3121
+ const topLevelsStructuralKey = useMemo4(
3122
+ () => topLevels.map(
3123
+ (t) => t.kind === "list" ? `L:${t.listKind}:${t.items.length}:${t.ordinalStart}` : `B:${t.block.type}:${t.block.attrs.level ?? ""}`
3124
+ ).join("|"),
3125
+ [topLevels]
3126
+ );
2965
3127
  const docSettings = usePageSettings(editor.doc);
2966
3128
  const propIsSet = pageGeometry !== void 0;
2967
3129
  const geom = propIsSet ? pageGeometry ?? A4_PAGE : docSettings;
2968
3130
  const paginated = pageGeometry !== null;
2969
3131
  const selectedEmbed = editor.getSelectedEmbed();
2970
- const layout = usePageLayout(
3132
+ const { layout, phase: paginationPhase } = usePageLayout(
2971
3133
  rootRef,
2972
3134
  geom,
2973
3135
  topLevels.length,
2974
- [topLevels, pageGeometry]
3136
+ // Usar a chave estrutural (string) em vez do array `topLevels` direto:
3137
+ // o array reference muda a cada keystroke, mas a chave só muda quando
3138
+ // a SHAPE dos top-levels muda. `usePageLayout` compara deps via
3139
+ // `shallowArrayEqual` — string equality cobre isso.
3140
+ [topLevelsStructuralKey, pageGeometry]
2975
3141
  );
2976
3142
  const effectiveLayout = paginated ? layout : defaultPageLayout(topLevels.length);
2977
- const pageStyle = paginated ? {
2978
- "--ed-page-width": `${geom.width}px`,
2979
- "--ed-page-height": `${geom.height}px`,
2980
- "--ed-page-margin-top": `${geom.marginTop}px`,
2981
- "--ed-page-margin-bottom": `${geom.marginBottom}px`,
2982
- "--ed-page-margin-left": `${geom.marginLeft}px`,
2983
- "--ed-page-margin-right": `${geom.marginRight}px`
2984
- } : void 0;
3143
+ const pageStyle = useMemo4(
3144
+ () => paginated ? {
3145
+ "--ed-page-width": `${geom.width}px`,
3146
+ "--ed-page-height": `${geom.height}px`,
3147
+ "--ed-page-margin-top": `${geom.marginTop}px`,
3148
+ "--ed-page-margin-bottom": `${geom.marginBottom}px`,
3149
+ "--ed-page-margin-left": `${geom.marginLeft}px`,
3150
+ "--ed-page-margin-right": `${geom.marginRight}px`
3151
+ } : void 0,
3152
+ [
3153
+ paginated,
3154
+ geom.width,
3155
+ geom.height,
3156
+ geom.marginTop,
3157
+ geom.marginBottom,
3158
+ geom.marginLeft,
3159
+ geom.marginRight
3160
+ ]
3161
+ );
2985
3162
  const body = /* @__PURE__ */ jsxs8(
2986
3163
  "div",
2987
3164
  {
@@ -2994,6 +3171,7 @@ function Editor({
2994
3171
  role: "textbox",
2995
3172
  "aria-multiline": "true",
2996
3173
  className: (className ?? "ed-root") + (paginated ? " ed-root--paged" : " ed-root--flat"),
3174
+ "data-pagination-phase": paginated ? paginationPhase : void 0,
2997
3175
  style: pageStyle,
2998
3176
  onKeyDown,
2999
3177
  onCompositionStart,
@@ -3004,12 +3182,16 @@ function Editor({
3004
3182
  pageNumber: pi + 1,
3005
3183
  pageCount: effectiveLayout.pages.length
3006
3184
  };
3185
+ const pageInlineStyle = pi === 0 && geom.firstPageExtraTop ? {
3186
+ "--ed-page-margin-top": `${geom.marginTop + geom.firstPageExtraTop}px`
3187
+ } : void 0;
3007
3188
  return /* @__PURE__ */ jsxs8(
3008
3189
  "div",
3009
3190
  {
3010
3191
  className: "ed-page",
3011
3192
  "data-page-number": ctx.pageNumber,
3012
3193
  "data-page-total": ctx.pageCount,
3194
+ style: pageInlineStyle,
3013
3195
  children: [
3014
3196
  renderPageHeader && /* @__PURE__ */ jsx9("div", { className: "ed-page-header", contentEditable: false, children: renderPageHeader(ctx) }),
3015
3197
  /* @__PURE__ */ jsx9("div", { className: "ed-page-content", children: page.slots.map((slot) => renderSlot(slot, topLevels)) }),
@@ -3055,11 +3237,29 @@ function groupTopLevels(snapshot) {
3055
3237
  const groupStart = i;
3056
3238
  const items = [];
3057
3239
  const kind = block.attrs.listKind === "ordered" ? "ordered" : "bullet";
3058
- while (i < snapshot.length && snapshot[i].type === "listItem" && (snapshot[i].attrs.listKind ?? "bullet") === kind) {
3059
- items.push({ block: snapshot[i], index: i });
3240
+ const groupStyle = block.attrs.listStyle;
3241
+ while (i < snapshot.length) {
3242
+ const b = snapshot[i];
3243
+ if (b.type !== "listItem") break;
3244
+ if ((b.attrs.listKind ?? "bullet") !== kind) break;
3245
+ if (i > groupStart) {
3246
+ if (typeof b.attrs.listStart === "number") break;
3247
+ const itemStyle = b.attrs.listStyle;
3248
+ if (itemStyle !== void 0 && itemStyle !== groupStyle) break;
3249
+ }
3250
+ items.push({ block: b, index: i });
3060
3251
  i++;
3061
3252
  }
3062
- out.push({ kind: "list", items, listKind: kind, key: groupStart });
3253
+ const firstAttrs = items[0].block.attrs;
3254
+ const ordinalStart = typeof firstAttrs.listStart === "number" && firstAttrs.listStart > 0 ? firstAttrs.listStart : 1;
3255
+ out.push({
3256
+ kind: "list",
3257
+ items,
3258
+ listKind: kind,
3259
+ ordinalStart,
3260
+ listStyle: groupStyle,
3261
+ key: groupStart
3262
+ });
3063
3263
  continue;
3064
3264
  }
3065
3265
  out.push({ kind: "block", block, blockIndex: i, key: i });
@@ -3097,15 +3297,43 @@ function renderSlot(slot, topLevels) {
3097
3297
  `${top.key}-tf${slot.tableFragment.index}`
3098
3298
  );
3099
3299
  }
3300
+ if (slot.listFragment && top.kind === "list") {
3301
+ return renderListFragment(top, slot.listFragment);
3302
+ }
3100
3303
  return renderTopLevel(top);
3101
3304
  }
3102
3305
  function renderTopLevel(top) {
3103
3306
  if (top.kind === "list") {
3104
3307
  const tree = buildListTree(top.items);
3105
- return renderListTree(tree, top.listKind, `list-${top.key}`);
3308
+ return renderListTree(tree, top, top.ordinalStart, `list-${top.key}`);
3106
3309
  }
3107
3310
  return /* @__PURE__ */ jsx9(NodeView, { block: top.block, index: top.blockIndex }, top.key);
3108
3311
  }
3312
+ function renderListFragment(top, frag) {
3313
+ const ranges = topLevelItemRanges(top.items);
3314
+ const startRange = ranges[frag.itemStart];
3315
+ const endRange = ranges[frag.itemEnd - 1];
3316
+ if (!startRange || !endRange) return renderTopLevel(top);
3317
+ const sliced = top.items.slice(startRange[0], endRange[1]);
3318
+ const ordinalStart = top.ordinalStart + frag.itemStart;
3319
+ const key = `list-${top.key}-f${frag.index}`;
3320
+ const slicedTop = { ...top, items: sliced };
3321
+ const tree = buildListTree(sliced);
3322
+ return renderListTree(tree, slicedTop, ordinalStart, key);
3323
+ }
3324
+ function topLevelItemRanges(items) {
3325
+ const out = [];
3326
+ let currentStart = -1;
3327
+ items.forEach((it, idx) => {
3328
+ const level = clampListLevel2(it.block.attrs.listLevel);
3329
+ if (level === 0) {
3330
+ if (currentStart >= 0) out.push([currentStart, idx]);
3331
+ currentStart = idx;
3332
+ }
3333
+ });
3334
+ if (currentStart >= 0) out.push([currentStart, items.length]);
3335
+ return out;
3336
+ }
3109
3337
  function buildListTree(items) {
3110
3338
  const roots = [];
3111
3339
  const stack = [{ children: roots, level: -1 }];
@@ -3118,9 +3346,28 @@ function buildListTree(items) {
3118
3346
  }
3119
3347
  return roots;
3120
3348
  }
3121
- function renderListTree(tree, kind, key) {
3349
+ function renderListTree(tree, top, start, key) {
3350
+ const className = `ed-list ed-list-${top.listKind}`;
3351
+ const style = top.listStyle ? { listStyleType: top.listStyle } : void 0;
3352
+ const items = tree.map((node) => /* @__PURE__ */ jsx9(NodeView, { block: node.entry.block, index: node.entry.index, children: node.children.length > 0 ? renderNestedListTree(node.children, top.listKind, `nested-${node.entry.index}`) : null }, node.entry.index));
3353
+ if (top.listKind === "ordered") {
3354
+ return /* @__PURE__ */ jsx9(
3355
+ "ol",
3356
+ {
3357
+ className,
3358
+ "data-list-kind": "ordered",
3359
+ start: start > 1 ? start : void 0,
3360
+ style,
3361
+ children: items
3362
+ },
3363
+ key
3364
+ );
3365
+ }
3366
+ return /* @__PURE__ */ jsx9("ul", { className, "data-list-kind": "bullet", style, children: items }, key);
3367
+ }
3368
+ function renderNestedListTree(tree, kind, key) {
3122
3369
  const Tag = kind === "ordered" ? "ol" : "ul";
3123
- return /* @__PURE__ */ jsx9(Tag, { className: `ed-list ed-list-${kind}`, "data-list-kind": kind, children: tree.map((node) => /* @__PURE__ */ jsx9(NodeView, { block: node.entry.block, index: node.entry.index, children: node.children.length > 0 ? renderListTree(node.children, kind, `nested-${node.entry.index}`) : null }, node.entry.index)) }, key);
3370
+ return /* @__PURE__ */ jsx9(Tag, { className: `ed-list ed-list-${kind}`, "data-list-kind": kind, children: tree.map((node) => /* @__PURE__ */ jsx9(NodeView, { block: node.entry.block, index: node.entry.index, children: node.children.length > 0 ? renderNestedListTree(node.children, kind, `nested-${node.entry.index}`) : null }, node.entry.index)) }, key);
3124
3371
  }
3125
3372
  function clampListLevel2(l) {
3126
3373
  if (typeof l !== "number" || !Number.isFinite(l)) return 0;