@jackuait/blok 0.4.1-beta.12 → 0.4.1-beta.13

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.
@@ -1,4 +1,4 @@
1
- import { e as i } from "./blok-BU6NwVkN.mjs";
1
+ import { e as i } from "./blok-Xfgk2kCJ.mjs";
2
2
  const l = async (e, r) => {
3
3
  const n = (await import("./i18next-CugVlwWp.mjs")).default.createInstance(), s = {
4
4
  lng: e,
@@ -1,4 +1,4 @@
1
- import { t as f, q as i } from "./inline-tool-convert-CLUxkCe_.mjs";
1
+ import { t as f, q as i } from "./inline-tool-convert-DhHW7EYl.mjs";
2
2
  const a = {
3
3
  wrapper: i(
4
4
  "fixed z-[2] bottom-5 left-5",
@@ -18,7 +18,7 @@ let nt = (o = 21) => {
18
18
  return t;
19
19
  };
20
20
  var ot = /* @__PURE__ */ ((o) => (o.VERBOSE = "VERBOSE", o.INFO = "INFO", o.WARN = "WARN", o.ERROR = "ERROR", o))(ot || {});
21
- const rt = () => "0.4.1-beta.12", Ct = {
21
+ const rt = () => "0.4.1-beta.13", Ct = {
22
22
  BACKSPACE: 8,
23
23
  TAB: 9,
24
24
  ENTER: 13,
package/dist/full.mjs CHANGED
@@ -10,10 +10,10 @@ var e = (a, l, o) => l in a ? n(a, l, { enumerable: !0, configurable: !0, writab
10
10
  d.call(l, o) && e(a, o, l[o]);
11
11
  return a;
12
12
  }, r = (a, l) => t(a, c(l));
13
- import { B as v, v as A } from "./chunks/blok-BU6NwVkN.mjs";
13
+ import { B as v, v as A } from "./chunks/blok-Xfgk2kCJ.mjs";
14
14
  import { List as p, Header as f, Paragraph as I, Link as k, Italic as u, Bold as B } from "./tools.mjs";
15
15
  import { defaultBlockTools as H, defaultInlineTools as P } from "./tools.mjs";
16
- import { D as _ } from "./chunks/inline-tool-convert-CLUxkCe_.mjs";
16
+ import { D as _ } from "./chunks/inline-tool-convert-DhHW7EYl.mjs";
17
17
  const m = {
18
18
  paragraph: {
19
19
  class: I,
package/dist/tools.mjs CHANGED
@@ -2,7 +2,7 @@ var nt = Object.defineProperty, rt = Object.defineProperties;
2
2
  var st = Object.getOwnPropertyDescriptors;
3
3
  var K = Object.getOwnPropertySymbols;
4
4
  var ot = Object.prototype.hasOwnProperty, it = Object.prototype.propertyIsEnumerable;
5
- var U = (f, t, e) => t in f ? nt(f, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : f[t] = e, w = (f, t) => {
5
+ var U = (f, t, e) => t in f ? nt(f, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : f[t] = e, O = (f, t) => {
6
6
  for (var e in t || (t = {}))
7
7
  ot.call(t, e) && U(f, e, t[e]);
8
8
  if (K)
@@ -10,8 +10,8 @@ var U = (f, t, e) => t in f ? nt(f, t, { enumerable: !0, configurable: !0, writa
10
10
  it.call(t, e) && U(f, e, t[e]);
11
11
  return f;
12
12
  }, P = (f, t) => rt(f, st(t));
13
- import { t as x, D as m, a9 as et, aa as at, ab as lt, A as ct, ac as dt, ad as ut, ae as ht, af as ft, ag as pt, ah as mt, ai as G, aj as j, ak as $, f as A, al as gt, am as Et, S as H, P as Tt, an as Ct, l as At, J as yt } from "./chunks/inline-tool-convert-CLUxkCe_.mjs";
14
- import { a0 as Dt } from "./chunks/inline-tool-convert-CLUxkCe_.mjs";
13
+ import { t as x, D as m, a9 as et, aa as at, ab as lt, A as ct, ac as dt, ad as ut, ae as ht, af as ft, ag as pt, ah as mt, ai as G, aj as j, ak as $, f as A, al as gt, am as Et, S as H, P as Tt, an as Ct, l as At, J as yt } from "./chunks/inline-tool-convert-DhHW7EYl.mjs";
14
+ import { a0 as Dt } from "./chunks/inline-tool-convert-DhHW7EYl.mjs";
15
15
  const W = [
16
16
  "empty:before:pointer-events-none",
17
17
  "empty:before:text-gray-text",
@@ -555,15 +555,32 @@ const u = class u {
555
555
  })));
556
556
  }, this.api = n, this.readOnly = r, this._settings = e || {}, this._data = this.normalizeData(t), s && (this.blockId = s.id), this._data.style === "ordered" && this.api.events.on("block changed", this.handleBlockChanged);
557
557
  }
558
+ /**
559
+ * Legacy list item structure for backward compatibility
560
+ */
561
+ static isLegacyFormat(t) {
562
+ return typeof t == "object" && t !== null && "items" in t && Array.isArray(t.items);
563
+ }
558
564
  normalizeData(t) {
559
565
  var n;
560
566
  const e = this._settings.defaultStyle || "unordered";
561
- return !t || typeof t != "object" ? {
562
- text: "",
563
- style: e,
564
- checked: !1,
565
- depth: 0
566
- } : w({
567
+ if (!t || typeof t != "object")
568
+ return {
569
+ text: "",
570
+ style: e,
571
+ checked: !1,
572
+ depth: 0
573
+ };
574
+ if (u.isLegacyFormat(t)) {
575
+ const r = t.items[0], s = (r == null ? void 0 : r.content) || "", o = (r == null ? void 0 : r.checked) || !1;
576
+ return O({
577
+ text: s,
578
+ style: t.style || e,
579
+ checked: !!o,
580
+ depth: 0
581
+ }, t.start !== void 0 && t.start !== 1 ? { start: t.start } : {});
582
+ }
583
+ return O({
567
584
  text: t.text || "",
568
585
  style: t.style || e,
569
586
  checked: !!t.checked,
@@ -1155,7 +1172,7 @@ const u = class u {
1155
1172
  charCount: r.charCount,
1156
1173
  node: s,
1157
1174
  offset: e - r.charCount
1158
- } : P(w({}, r), {
1175
+ } : P(O({}, r), {
1159
1176
  charCount: r.charCount + o
1160
1177
  });
1161
1178
  },
@@ -1202,7 +1219,7 @@ const u = class u {
1202
1219
  this.syncContentFromDOM();
1203
1220
  const s = n + 1;
1204
1221
  this._data.depth = s;
1205
- const o = await this.api.blocks.update(this.blockId || "", P(w({}, this._data), {
1222
+ const o = await this.api.blocks.update(this.blockId || "", P(O({}, this._data), {
1206
1223
  depth: s
1207
1224
  }));
1208
1225
  this.setCaretToBlockContent(o);
@@ -1213,7 +1230,7 @@ const u = class u {
1213
1230
  this.syncContentFromDOM();
1214
1231
  const e = t - 1;
1215
1232
  this._data.depth = e;
1216
- const n = await this.api.blocks.update(this.blockId || "", P(w({}, this._data), {
1233
+ const n = await this.api.blocks.update(this.blockId || "", P(O({}, this._data), {
1217
1234
  depth: e
1218
1235
  }));
1219
1236
  this.setCaretToBlockContent(n);
@@ -2090,7 +2107,7 @@ const i = class i {
2090
2107
  return;
2091
2108
  const s = `strong[${i.DATA_ATTR_COLLAPSED_ACTIVE}="true"]`;
2092
2109
  r.querySelectorAll(s).forEach((l) => {
2093
- var k, I, F, z, q;
2110
+ var k, I, _, z, q;
2094
2111
  const d = l.getAttribute(i.DATA_ATTR_PREV_LENGTH), c = l.previousSibling;
2095
2112
  if (!d || !c || c.nodeType !== Node.TEXT_NODE)
2096
2113
  return;
@@ -2105,13 +2122,13 @@ const i = class i {
2105
2122
  const C = E.match(/^[\u00A0\s]+/);
2106
2123
  if (C && !l.hasAttribute(i.DATA_ATTR_LEADING_WHITESPACE) && l.setAttribute(i.DATA_ATTR_LEADING_WHITESPACE, C[0]), E.length === 0)
2107
2124
  return;
2108
- const T = (I = l.textContent) != null ? I : "", N = T + E, B = (F = l.getAttribute(i.DATA_ATTR_LEADING_WHITESPACE)) != null ? F : "", R = B.length > 0 && T.length === 0 && !N.startsWith(B) ? B + N : N, b = document.createTextNode(R);
2125
+ const T = (I = l.textContent) != null ? I : "", N = T + E, B = (_ = l.getAttribute(i.DATA_ATTR_LEADING_WHITESPACE)) != null ? _ : "", R = B.length > 0 && T.length === 0 && !N.startsWith(B) ? B + N : N, b = document.createTextNode(R);
2109
2126
  for (; l.firstChild; )
2110
2127
  l.removeChild(l.firstChild);
2111
2128
  if (l.appendChild(b), !(t != null && t.isCollapsed) || !i.isNodeWithin(t.focusNode, p))
2112
2129
  return;
2113
- const S = document.createRange(), _ = (q = (z = b.textContent) == null ? void 0 : z.length) != null ? q : 0;
2114
- S.setStart(b, _), S.collapse(!0), t.removeAllRanges(), t.addRange(S);
2130
+ const S = document.createRange(), F = (q = (z = b.textContent) == null ? void 0 : z.length) != null ? q : 0;
2131
+ S.setStart(b, F), S.collapse(!0), t.removeAllRanges(), t.addRange(S);
2115
2132
  });
2116
2133
  }
2117
2134
  /**
@@ -2320,7 +2337,7 @@ const i = class i {
2320
2337
  if (!r)
2321
2338
  return;
2322
2339
  r.querySelectorAll(`strong[${i.DATA_ATTR_COLLAPSED_LENGTH}]`).forEach((a) => {
2323
- var v, R, b, S, _;
2340
+ var v, R, b, S, F;
2324
2341
  const l = a, d = l.getAttribute(i.DATA_ATTR_COLLAPSED_LENGTH);
2325
2342
  if (!d)
2326
2343
  return;
@@ -2331,11 +2348,11 @@ const i = class i {
2331
2348
  if (B && T) {
2332
2349
  const k = N.slice(0, E), I = N.slice(E);
2333
2350
  T.textContent = k;
2334
- const F = document.createTextNode(I);
2335
- (b = l.parentNode) == null || b.insertBefore(F, l.nextSibling);
2351
+ const _ = document.createTextNode(I);
2352
+ (b = l.parentNode) == null || b.insertBefore(_, l.nextSibling);
2336
2353
  }
2337
2354
  if (B && l.removeAttribute(i.DATA_ATTR_PREV_LENGTH), t != null && t.isCollapsed && g && i.isNodeWithin(t.focusNode, l)) {
2338
- const k = document.createRange(), I = (_ = (S = g.textContent) == null ? void 0 : S.length) != null ? _ : 0;
2355
+ const k = document.createRange(), I = (F = (S = g.textContent) == null ? void 0 : S.length) != null ? F : 0;
2339
2356
  k.setStart(g, I), k.collapse(!0), t.removeAllRanges(), t.addRange(k);
2340
2357
  }
2341
2358
  p && l.removeAttribute(i.DATA_ATTR_COLLAPSED_LENGTH);
@@ -2602,7 +2619,7 @@ const i = class i {
2602
2619
  };
2603
2620
  i.isInline = !0, i.title = "Bold", i.titleKey = "bold", i.shortcutListenerRegistered = !1, i.selectionListenerRegistered = !1, i.inputListenerRegistered = !1, i.beforeInputListenerRegistered = !1, i.globalListenersInitialized = i.initializeGlobalListeners(), i.collapsedExitRecords = /* @__PURE__ */ new Set(), i.markerSequence = 0, i.isProcessingMutation = !1, i.DATA_ATTR_COLLAPSED_LENGTH = "data-blok-bold-collapsed-length", i.DATA_ATTR_COLLAPSED_ACTIVE = "data-blok-bold-collapsed-active", i.DATA_ATTR_PREV_LENGTH = "data-blok-bold-prev-length", i.DATA_ATTR_LEADING_WHITESPACE = "data-blok-bold-leading-ws", i.instances = /* @__PURE__ */ new Set(), i.pendingBoundaryCaretAdjustments = /* @__PURE__ */ new WeakSet(), i.shortcut = "CMD+B";
2604
2621
  let Z = i;
2605
- const O = class O {
2622
+ const D = class D {
2606
2623
  /**
2607
2624
  * Sanitizer Rule
2608
2625
  * Leave <i> and <em> tags
@@ -2873,9 +2890,9 @@ const O = class O {
2873
2890
  n.insertBefore(a, e.nextSibling), n.insertBefore(t, a);
2874
2891
  }
2875
2892
  };
2876
- O.isInline = !0, O.title = "Italic", O.titleKey = "italic", O.shortcut = "CMD+I";
2877
- let Q = O;
2878
- const D = class D {
2893
+ D.isInline = !0, D.title = "Italic", D.titleKey = "italic", D.shortcut = "CMD+I";
2894
+ let Q = D;
2895
+ const w = class w {
2879
2896
  /**
2880
2897
  * @param api - Blok API
2881
2898
  */
@@ -3102,8 +3119,8 @@ const D = class D {
3102
3119
  t && t.setAttribute(e, n ? "true" : "false");
3103
3120
  }
3104
3121
  };
3105
- D.isInline = !0, D.title = "Link", D.titleKey = "link", D.shortcut = "CMD+K";
3106
- let tt = D;
3122
+ w.isInline = !0, w.title = "Link", w.titleKey = "link", w.shortcut = "CMD+K";
3123
+ let tt = w;
3107
3124
  const vt = {
3108
3125
  paragraph: { preserveBlank: !0 },
3109
3126
  header: {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jackuait/blok",
3
- "version": "0.4.1-beta.12",
3
+ "version": "0.4.1-beta.13",
4
4
  "description": "Blok — headless, highly extensible rich text editor built for developers who need to implement a block-based editing experience (similar to Notion) without building it from scratch",
5
5
  "module": "dist/blok.mjs",
6
6
  "types": "./types/index.d.ts",
@@ -96,14 +96,12 @@
96
96
  "verify:package:local": "npm pack && node scripts/verify-published-package.mjs --local",
97
97
  "verify:version": "node scripts/verify-version.mjs",
98
98
  "unpublish": "node scripts/unpublish-package.mjs",
99
- "bundle:track": "node scripts/track-bundle-size.mjs --verbose",
100
- "bundle:variants": "node scripts/build-bundle-variants.mjs --verbose",
101
- "bundle:trends": "node scripts/view-bundle-trends.mjs --trends",
102
- "bundle:history": "node scripts/view-bundle-trends.mjs",
103
99
  "perf:analyze": "node scripts/analyze-performance.mjs",
104
100
  "perf:compare": "node scripts/analyze-performance.mjs --baseline",
105
101
  "perf:dashboard": "node scripts/generate-performance-dashboard.mjs",
106
- "e2e:validate-categories": "node scripts/validate-test-categories.mjs"
102
+ "e2e:validate-categories": "node scripts/validate-test-categories.mjs",
103
+ "size": "size-limit",
104
+ "size:why": "size-limit --why"
107
105
  },
108
106
  "author": "JackUait",
109
107
  "contributors": [
@@ -123,6 +121,8 @@
123
121
  "@playwright/test": "1.57.0",
124
122
  "@semantic-release/changelog": "^6.0.3",
125
123
  "@semantic-release/git": "^10.0.1",
124
+ "@size-limit/esbuild-why": "^12.0.0",
125
+ "@size-limit/preset-small-lib": "^12.0.0",
126
126
  "@storybook/addon-a11y": "10.1.1",
127
127
  "@storybook/addon-vitest": "10.1.1",
128
128
  "@storybook/global": "5.0.0",
@@ -150,6 +150,7 @@
150
150
  "postcss": "8.5.6",
151
151
  "rollup-plugin-license": "3.6.0",
152
152
  "semantic-release": "^25.0.2",
153
+ "size-limit": "^12.0.0",
153
154
  "storybook": "10.1.1",
154
155
  "tailwindcss": "3",
155
156
  "tslint": "6.1.3",
@@ -163,5 +164,22 @@
163
164
  "dependencies": {
164
165
  "i18next": "^25.7.3",
165
166
  "nanoid": "^5.1.6"
166
- }
167
+ },
168
+ "size-limit": [
169
+ {
170
+ "name": "Minimum (core only)",
171
+ "path": "src/variants/blok-minimum.ts",
172
+ "limit": "300 KB"
173
+ },
174
+ {
175
+ "name": "Normal (with tools)",
176
+ "path": "src/blok.ts",
177
+ "limit": "300 KB"
178
+ },
179
+ {
180
+ "name": "Maximum (all locales)",
181
+ "path": "src/variants/blok-maximum.ts",
182
+ "limit": "300 KB"
183
+ }
184
+ ]
167
185
  }
@@ -789,6 +789,16 @@ export class UI extends Module<UINodes> {
789
789
  return;
790
790
  }
791
791
 
792
+ /**
793
+ * Close BlockSettings first if it's open, regardless of selection state.
794
+ * This prevents navigation mode from being enabled when the user closes block tunes with Escape.
795
+ */
796
+ if (this.Blok.BlockSettings.opened) {
797
+ this.Blok.BlockSettings.close();
798
+
799
+ return;
800
+ }
801
+
792
802
  /**
793
803
  * Clear blocks selection by ESC (but not when entering navigation mode)
794
804
  */
@@ -806,12 +816,6 @@ export class UI extends Module<UINodes> {
806
816
  return;
807
817
  }
808
818
 
809
- if (this.Blok.BlockSettings.opened) {
810
- this.Blok.BlockSettings.close();
811
-
812
- return;
813
- }
814
-
815
819
  /**
816
820
  * If a nested popover is open (like convert-to dropdown),
817
821
  * close only the nested popover, not the entire inline toolbar.
@@ -109,37 +109,49 @@ const expandListItems = (
109
109
 
110
110
  childIds.push(itemId);
111
111
 
112
- // Recursively expand nested items first to get their IDs
113
- const nestedChildIds = item.items && item.items.length > 0
114
- ? expandListItems(item.items, itemId, depth + 1, style, undefined, tunes, blocks)
115
- : [];
116
-
117
112
  // Determine if we should include start (only for first root item of ordered lists)
118
113
  const includeStart = style === 'ordered' && depth === 0 && index === 0 && start !== undefined && start !== 1;
119
114
 
120
- // Create the list_item block
115
+ // Check if there are nested items (we'll get their IDs after pushing the parent)
116
+ const hasNestedItems = item.items && item.items.length > 0;
117
+
118
+ // Create the list block (flat model - each item is a separate 'list' block)
119
+ // We'll update with content IDs after processing children
121
120
  const itemBlock: OutputBlockData = {
122
121
  id: itemId,
123
- type: 'list_item',
122
+ type: 'list',
124
123
  data: {
125
124
  text: item.content,
126
125
  checked: item.checked,
127
126
  style,
127
+ ...(depth > 0 ? { depth } : {}),
128
128
  ...(includeStart ? { start } : {}),
129
129
  },
130
130
  ...(tunes !== undefined ? { tunes } : {}),
131
131
  ...(parentId !== undefined ? { parent: parentId } : {}),
132
- ...(nestedChildIds.length > 0 ? { content: nestedChildIds } : {}),
133
132
  };
134
133
 
134
+ // Push parent block first to maintain correct order (parent before children)
135
135
  blocks.push(itemBlock);
136
+
137
+ // Now recursively expand nested items (they will be added after the parent)
138
+ if (!hasNestedItems) {
139
+ return;
140
+ }
141
+
142
+ const nestedChildIds = expandListItems(item.items!, itemId, depth + 1, style, undefined, tunes, blocks);
143
+
144
+ // Update the parent block with content IDs (only if children exist)
145
+ if (nestedChildIds.length > 0) {
146
+ itemBlock.content = nestedChildIds;
147
+ }
136
148
  });
137
149
 
138
150
  return childIds;
139
151
  };
140
152
 
141
153
  /**
142
- * Expand a List block with nested items into flat list_item blocks
154
+ * Expand a List block with nested items into flat list blocks
143
155
  */
144
156
  const expandListToHierarchical = (
145
157
  listData: LegacyListData,
@@ -217,7 +229,7 @@ const collectChildItems = (
217
229
 
218
230
  for (const childId of contentIds) {
219
231
  const childBlock = blockMap.get(childId);
220
- const isListItem = childBlock && childBlock.type === 'list_item';
232
+ const isListItem = childBlock && childBlock.type === 'list';
221
233
 
222
234
  if (isListItem) {
223
235
  const items = collectListItems(childBlock, blockMap, processedIds);
@@ -269,7 +281,7 @@ const collectListItems = (
269
281
  };
270
282
 
271
283
  /**
272
- * Process a root list_item block and convert to a legacy List block
284
+ * Process a root list block (flat model) and convert to a legacy List block
273
285
  */
274
286
  const processRootListItem = (
275
287
  block: OutputBlockData,
@@ -294,6 +306,13 @@ const processRootListItem = (
294
306
  return listBlock;
295
307
  };
296
308
 
309
+ /**
310
+ * Check if a block is a flat-model list block (has 'text' field instead of 'items')
311
+ */
312
+ const isFlatModelListBlock = (block: OutputBlockData): boolean => {
313
+ return block.type === 'list' && block.data?.text !== undefined && block.data?.items === undefined;
314
+ };
315
+
297
316
  /**
298
317
  * Collapse hierarchical flat-with-references format back to legacy nested format
299
318
  * @param blocks - array of flat blocks with parent/content references
@@ -302,24 +321,21 @@ const processRootListItem = (
302
321
  export const collapseToLegacy = (blocks: OutputBlockData[]): OutputBlockData[] => {
303
322
  // Build a map of blocks by ID for quick lookup
304
323
  const blockMap = new Map<BlockId, OutputBlockData>();
305
- const listItemBlocks: OutputBlockData[] = [];
306
324
 
307
325
  for (const block of blocks) {
308
326
  if (block.id) {
309
327
  blockMap.set(block.id, block);
310
328
  }
311
-
312
- if (block.type === 'list_item') {
313
- listItemBlocks.push(block);
314
- }
315
329
  }
316
330
 
317
- // If no list_item blocks, just strip hierarchy fields and return
318
- if (listItemBlocks.length === 0) {
331
+ // If no flat-model list blocks, just strip hierarchy fields and return
332
+ const hasFlatListBlocks = blocks.some(isFlatModelListBlock);
333
+
334
+ if (!hasFlatListBlocks) {
319
335
  return blocks.map(stripHierarchyFields);
320
336
  }
321
337
 
322
- // Process blocks, converting root list_items to List blocks
338
+ // Process blocks, converting root flat-model list blocks to legacy List blocks
323
339
  const result: OutputBlockData[] = [];
324
340
  const processedIds = new Set<BlockId>();
325
341
 
@@ -330,8 +346,9 @@ export const collapseToLegacy = (blocks: OutputBlockData[]): OutputBlockData[] =
330
346
  continue;
331
347
  }
332
348
 
333
- const isRootListItem = block.type === 'list_item' && !block.parent;
334
- const isNonListItem = block.type !== 'list_item';
349
+ const isFlatListBlock = isFlatModelListBlock(block);
350
+ const isRootListItem = isFlatListBlock && !block.parent;
351
+ const isNonListItem = !isFlatListBlock;
335
352
 
336
353
  if (isRootListItem) {
337
354
  const listBlock = processRootListItem(block, blockMap, processedIds);
@@ -185,6 +185,13 @@ export class ListItem implements BlockTool {
185
185
 
186
186
  sanitize?: ToolSanitizerConfig | undefined;
187
187
 
188
+ /**
189
+ * Legacy list item structure for backward compatibility
190
+ */
191
+ private static isLegacyFormat(data: unknown): data is { items: Array<{ content: string; checked?: boolean }>, style?: ListItemStyle, start?: number } {
192
+ return typeof data === 'object' && data !== null && 'items' in data && Array.isArray((data as { items: unknown }).items);
193
+ }
194
+
188
195
  private normalizeData(data: ListItemData | Record<string, never>): ListItemData {
189
196
  const defaultStyle = this._settings.defaultStyle || 'unordered';
190
197
 
@@ -197,6 +204,22 @@ export class ListItem implements BlockTool {
197
204
  };
198
205
  }
199
206
 
207
+ // Handle legacy format with items[] array - extract first item's content
208
+ // This provides backward compatibility when legacy data is passed directly to the tool
209
+ if (ListItem.isLegacyFormat(data)) {
210
+ const firstItem = data.items[0];
211
+ const text = firstItem?.content || '';
212
+ const checked = firstItem?.checked || false;
213
+
214
+ return {
215
+ text,
216
+ style: data.style || defaultStyle,
217
+ checked: Boolean(checked),
218
+ depth: 0,
219
+ ...(data.start !== undefined && data.start !== 1 ? { start: data.start } : {}),
220
+ };
221
+ }
222
+
200
223
  return {
201
224
  text: data.text || '',
202
225
  style: data.style || defaultStyle,