@marimo-team/islands 0.25.3-dev192 → 0.25.3-dev196

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.
Files changed (154) hide show
  1. package/dist/{ConnectedDataExplorerComponent-BBbOElk3.js → ConnectedDataExplorerComponent-BRuwLOIx.js} +9 -9
  2. package/dist/{ErrorBoundary-Dw6uRKVk.js → ErrorBoundary-C3Xv3yzV.js} +2 -2
  3. package/dist/{abnfDiagram-VCTEODGH-DhMT6Uhd.js → abnfDiagram-VCTEODGH-LlJTOOU_.js} +2 -2
  4. package/dist/{architectureDiagram-5GKGNRK7-BVbEiJLq.js → architectureDiagram-5GKGNRK7-9PQtGLLZ.js} +1 -1
  5. package/dist/{arrays-R6JsImiT.js → arrays-DA6KkLfq.js} +6 -12
  6. package/dist/assets/__vite-browser-external-BCJsx8QS.js +1 -0
  7. package/dist/assets/{worker-C6e9ERyd.js → worker-DodjWT_Z.js} +2 -2
  8. package/dist/{capabilities-CI4xUPsp.js → capabilities-CvWU-ey_.js} +1 -1
  9. package/dist/{chat-ui-BguxTQEL.js → chat-ui-Bl2euMBD.js} +93 -93
  10. package/dist/{chunk-IMKFNOWR-By5hSbJJ.js → chunk-IMKFNOWR-DcXv-BD5.js} +1 -1
  11. package/dist/{chunk-SHT3W25Y-DFrEYG54.js → chunk-SHT3W25Y-VwnELN23.js} +1 -1
  12. package/dist/{chunk-SVP7TREG-DFXGc9ew.js → chunk-SVP7TREG-DgQpI5Zi.js} +1 -1
  13. package/dist/{chunk-TICWLB2K-DZdNIpoZ.js → chunk-TICWLB2K-CTg6VTJT.js} +1 -1
  14. package/dist/{classDiagram-ZZMXUADV-Cgj_eEQA.js → classDiagram-ZZMXUADV-UzmcFrnW.js} +2 -2
  15. package/dist/{classDiagram-v2-VYDZK3BY-DLAUrSqj.js → classDiagram-v2-VYDZK3BY-DhjiI6IQ.js} +2 -2
  16. package/dist/{code-visibility-v_7ijLtx.js → code-visibility-BCcqmP58.js} +38 -42
  17. package/dist/{common-BUwWHpnK.js → common-DrNB2bY2.js} +849 -849
  18. package/dist/{cynefinDiagram-5FMLGOSQ-Cikf9AKa.js → cynefinDiagram-5FMLGOSQ-O4ShIbJX.js} +1 -1
  19. package/dist/{dagre-GXQ25YYZ-C5KEi5_l.js → dagre-GXQ25YYZ-dtHhhcPu.js} +1 -1
  20. package/dist/{data-grid-overlay-editor-4IZymnXZ.js → data-grid-overlay-editor-TGXV_Hf4.js} +1 -1
  21. package/dist/{diagram-S7CK7UJ4-BaoBTQjR.js → diagram-S7CK7UJ4-ppPYVZ6W.js} +1 -1
  22. package/dist/{diagram-UQ7AKVKN-DDufBsUA.js → diagram-UQ7AKVKN-CMZDf--k.js} +1 -1
  23. package/dist/{diagram-VX7I27RA-CY6RALJ8.js → diagram-VX7I27RA-CFvJdx-C.js} +1 -1
  24. package/dist/{diagram-Z3DM3KII-Bc-LHsD6.js → diagram-Z3DM3KII-Dt6_uFbv.js} +1 -1
  25. package/dist/{dropdown-menu-drDjxJyV.js → dropdown-menu-CeaOsjWZ.js} +2 -2
  26. package/dist/{ebnfDiagram-PWID7BFC-Cz0J-tcC.js → ebnfDiagram-PWID7BFC-DpRFrKK-.js} +2 -2
  27. package/dist/{erDiagram-RLTQ6QDP-DTXTDgFu.js → erDiagram-RLTQ6QDP-CqAU4b0u.js} +1 -1
  28. package/dist/{error-entries-CZ1NQ6Kv.js → error-entries-CYGwr5-O.js} +5596 -6012
  29. package/dist/feedback-button-PTiPn2GP.js +374 -0
  30. package/dist/{flowDiagram-HODETNUW-RbwWf2zc.js → flowDiagram-HODETNUW-i8-Gw8HO.js} +2 -2
  31. package/dist/{formats-DbXnijtH.js → formats-BtoHHy42.js} +1 -1
  32. package/dist/{glide-data-editor-DU8_EDG5.js → glide-data-editor-BSyexjkB.js} +5 -5
  33. package/dist/{globals-BeBDOk22.js → globals-B7gkJAxm.js} +2 -2
  34. package/dist/{html-to-image-D_4_hQ8U.js → html-to-image-Cy5UDpQJ.js} +56 -56
  35. package/dist/{infoDiagram-27XIBGKW-DlmzZjmI.js → infoDiagram-27XIBGKW-DpfOKdLL.js} +1 -1
  36. package/dist/{ishikawaDiagram-5VMMS53U-EN3TYP_q.js → ishikawaDiagram-5VMMS53U-Dg7mrMG7.js} +1 -1
  37. package/dist/{kanban-definition-UXKFOSKX-CK_-Ru7Q.js → kanban-definition-UXKFOSKX-QB6e3Jrj.js} +1 -1
  38. package/dist/main.js +5875 -5876
  39. package/dist/{mermaid-DU9ZRmkG.js → mermaid-CeWG4Egr.js} +48 -48
  40. package/dist/{mindmap-definition-YA3MSWOX-C-hqgUvF.js → mindmap-definition-YA3MSWOX-BTz3uTGe.js} +1 -1
  41. package/dist/{number-overlay-editor-BoIkyh6H.js → number-overlay-editor-Bb91yQ46.js} +1 -1
  42. package/dist/{once-eG8Vmvjd.js → once-DWGO2mQ2.js} +1 -1
  43. package/dist/{pegDiagram-XKGWAZYB-BiMejYPB.js → pegDiagram-XKGWAZYB-3ZIEa3mB.js} +2 -2
  44. package/dist/{pieDiagram-E7YTZNPT-B1zCZ1eq.js → pieDiagram-E7YTZNPT-BJAlk046.js} +1 -1
  45. package/dist/{process-output-DxSCHBWp.js → process-output-q9MTAIMX.js} +2 -2
  46. package/dist/{railroadDiagram-O6MQD6OU-BLcfaASl.js → railroadDiagram-O6MQD6OU-Dn3bsSRa.js} +2 -2
  47. package/dist/{requirementDiagram-BXWQKSXE-DtBOf52-.js → requirementDiagram-BXWQKSXE-wcOmQQCL.js} +1 -1
  48. package/dist/{reveal-component-5M3a2rpj.js → reveal-component-vTdjiyDv.js} +17 -17
  49. package/dist/{sequenceDiagram-WJ2MYXX4-DIwF19_n.js → sequenceDiagram-WJ2MYXX4-DSh_qxoF.js} +1 -1
  50. package/dist/{spec-DZDnnlgT.js → spec-C7Fos-If.js} +1 -1
  51. package/dist/{stateDiagram-D77RDMKH-sWk1AdYL.js → stateDiagram-D77RDMKH-DH_H8NMH.js} +2 -2
  52. package/dist/{stateDiagram-v2-MP3YSRHH-CiNo-OI7.js → stateDiagram-v2-MP3YSRHH-D_JM7MEK.js} +2 -2
  53. package/dist/style.css +1 -1
  54. package/dist/{swimlanes-42K2YHIH-Bj3hiOY5.js → swimlanes-42K2YHIH-CfoTJO-9.js} +1 -1
  55. package/dist/{swimlanesDiagram-VR7AAH4N-BzEo2r_N.js → swimlanesDiagram-VR7AAH4N-Z_QhvK0G.js} +2 -2
  56. package/dist/{swiper-component-BkCm-kLg.js → swiper-component-D2geL1Ku.js} +1 -1
  57. package/dist/{timeline-definition-24CTP7MA-CrPQk42H.js → timeline-definition-24CTP7MA-BFtdScaL.js} +1 -1
  58. package/dist/{toDate-Cu2pIZcC.js → toDate-CQAFSwos.js} +24 -24
  59. package/dist/{useAsyncData-DEhLdRIu.js → useAsyncData-BeyZyjso.js} +1 -1
  60. package/dist/{useDeepCompareMemoize-RjZGVhBV.js → useDeepCompareMemoize-DOQgae0W.js} +1 -1
  61. package/dist/{useIframeCapabilities-CSdWn6NP.js → useIframeCapabilities-DNvaj9wM.js} +1 -1
  62. package/dist/{useLifecycle-DtcB2s1z.js → useLifecycle-n2lnCFS8.js} +1 -1
  63. package/dist/{useTheme-CoEtr4mK.js → useTheme-CY777LXN.js} +303 -99
  64. package/dist/{vega-component-BhjTrP9-.js → vega-component-0nQzA-I8.js} +6 -6
  65. package/dist/{vennDiagram-4TSXK5OY-BaYMoIpj.js → vennDiagram-4TSXK5OY-BXY8IGCA.js} +1 -1
  66. package/dist/{wardleyDiagram-VM6X3IG4-DjHeuj8p.js → wardleyDiagram-VM6X3IG4-D0tFe6eC.js} +1 -1
  67. package/dist/{xychartDiagram-S5SC5T6Z-Bc_hcuXS.js → xychartDiagram-S5SC5T6Z-Dx8wFM-y.js} +1 -1
  68. package/package.json +1 -1
  69. package/src/__mocks__/notebook.ts +0 -1
  70. package/src/__mocks__/partial.ts +26 -0
  71. package/src/__tests__/main.test.tsx +24 -1
  72. package/src/components/app-config/app-config-form.tsx +5 -9
  73. package/src/components/editor/__tests__/data-attributes.test.tsx +0 -1
  74. package/src/components/editor/__tests__/output-persistence.test.tsx +0 -1
  75. package/src/components/editor/actions/useCellActionButton.tsx +6 -5
  76. package/src/components/editor/actions/useConfigActions.tsx +3 -4
  77. package/src/components/editor/actions/useNotebookActions.tsx +5 -4
  78. package/src/components/editor/cell/code/cell-editor.tsx +13 -6
  79. package/src/components/editor/cell/collapse.tsx +6 -1
  80. package/src/components/editor/chrome/components/feedback-button.tsx +1 -7
  81. package/src/components/editor/controls/Controls.tsx +1 -1
  82. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +8 -6
  83. package/src/components/editor/navigation/__tests__/navigation.test.ts +15 -15
  84. package/src/components/editor/navigation/__tests__/selection.test.ts +44 -21
  85. package/src/components/editor/navigation/focus-utils.ts +19 -0
  86. package/src/components/editor/navigation/multi-cell-action-toolbar.tsx +6 -13
  87. package/src/components/editor/navigation/navigation.ts +52 -43
  88. package/src/components/editor/navigation/selection.ts +30 -29
  89. package/src/components/editor/notebook-cell.tsx +10 -18
  90. package/src/components/editor/renderers/cell-array.tsx +6 -5
  91. package/src/components/editor/renderers/slides-layout/__tests__/plugin.test.ts +0 -1
  92. package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +1 -1
  93. package/src/components/pages/edit-page.tsx +0 -13
  94. package/src/components/scratchpad/scratchpad.tsx +0 -1
  95. package/src/components/slides/reveal-component.tsx +1 -1
  96. package/src/components/slides/slide-cell-view.tsx +0 -1
  97. package/src/components/sort/SortableCellsProvider.tsx +6 -5
  98. package/src/core/MarimoApp.tsx +1 -1
  99. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +0 -37
  100. package/src/core/cells/__tests__/actions.test.ts +190 -80
  101. package/src/core/cells/__tests__/arrange.test.ts +263 -0
  102. package/src/core/cells/__tests__/cells.test.ts +11 -1101
  103. package/src/core/cells/__tests__/document.test.ts +3 -32
  104. package/src/core/cells/__tests__/editor.test.ts +57 -0
  105. package/src/core/cells/__tests__/harness.ts +82 -0
  106. package/src/core/cells/__tests__/layout.test.ts +189 -0
  107. package/src/core/cells/__tests__/notebook-outline.test.ts +7 -1
  108. package/src/core/cells/__tests__/session.test.ts +329 -174
  109. package/src/core/cells/__tests__/utils.test.ts +0 -1
  110. package/src/core/cells/actions.ts +247 -82
  111. package/src/core/cells/arrange.ts +341 -0
  112. package/src/core/cells/cells.ts +27 -516
  113. package/src/core/cells/document.ts +6 -5
  114. package/src/core/cells/edit.ts +30 -0
  115. package/src/core/cells/editor.ts +82 -0
  116. package/src/core/cells/layout.ts +152 -0
  117. package/src/core/cells/session.ts +75 -52
  118. package/src/core/cells/types.ts +0 -5
  119. package/src/core/cells/utils.ts +6 -4
  120. package/src/core/config/__tests__/app-config.test.ts +64 -0
  121. package/src/core/config/__tests__/config-schema.test.ts +1 -1
  122. package/src/core/config/config-schema.ts +6 -2
  123. package/src/core/config/config.ts +26 -12
  124. package/src/core/islands/bootstrap.ts +0 -1
  125. package/src/core/kernel/handlers.ts +1 -13
  126. package/src/core/meta/__tests__/code-visibility.test.tsx +78 -70
  127. package/src/core/meta/code-visibility.ts +18 -9
  128. package/src/core/notebook/__tests__/operations.test.ts +51 -0
  129. package/src/core/notebook/document/__tests__/codemirror.test.ts +33 -7
  130. package/src/core/notebook/index.ts +2 -1
  131. package/src/core/notebook/operations.ts +30 -0
  132. package/src/core/saving/__tests__/state.test.ts +111 -0
  133. package/src/core/saving/save-component.tsx +19 -33
  134. package/src/core/saving/state.ts +38 -39
  135. package/src/core/saving/useAutoSave.ts +8 -35
  136. package/src/core/session/__tests__/backend.test.ts +1 -0
  137. package/src/core/session/__tests__/useNotebookReplica.hook.test.tsx +1 -0
  138. package/src/core/session/active-notebook.ts +11 -0
  139. package/src/core/session/backend.ts +17 -19
  140. package/src/core/session/embedded.ts +7 -0
  141. package/src/core/session/useNotebookReplica.ts +3 -1
  142. package/src/core/session/useNotebookSession.tsx +7 -8
  143. package/src/core/transports/__tests__/static.test.ts +139 -8
  144. package/src/core/transports/static.ts +98 -16
  145. package/src/core/wasm/__tests__/bridge.test.ts +1 -0
  146. package/src/mount.tsx +19 -10
  147. package/src/stories/cell.stories.tsx +0 -2
  148. package/src/stories/layout/vertical/one-column.stories.tsx +0 -2
  149. package/src/utils/__tests__/id-tree.test.ts +20 -1548
  150. package/src/utils/id-tree.tsx +12 -537
  151. package/dist/assets/__vite-browser-external-C8gQho07.js +0 -1
  152. package/dist/feedback-button-MpeoIn1D.js +0 -379
  153. package/src/components/editor/navigation/__tests__/state.test.ts +0 -81
  154. package/src/components/editor/navigation/state.ts +0 -65
@@ -36,11 +36,6 @@ import {
36
36
  type NotebookState,
37
37
  notebookAtom,
38
38
  } from "../cells";
39
- import {
40
- focusAndScrollCellIntoView,
41
- scrollToBottom,
42
- scrollToTop,
43
- } from "../scrollCellIntoView";
44
39
  import {
45
40
  type CellData,
46
41
  createCell,
@@ -67,7 +62,6 @@ vi.mock("../scrollCellIntoView", async (importOriginal) => {
67
62
  });
68
63
 
69
64
  const FIRST_COLUMN = 0;
70
- const SECOND_COLUMN = 1;
71
65
 
72
66
  const { initialNotebookState, reducer, createActions } = exportedForTesting;
73
67
 
@@ -203,10 +197,6 @@ describe("cell reducer", () => {
203
197
  cellRuntime: { ...state.cellRuntime, [id]: createCellRuntimeState() },
204
198
  cellHandles: { ...state.cellHandles, [id]: createRef() },
205
199
  scrollKey: autoFocus ? id : null,
206
- untouchedNewCells:
207
- hideCode && autoFocus
208
- ? new Set([...state.untouchedNewCells, id])
209
- : state.untouchedNewCells,
210
200
  };
211
201
  mountEditors();
212
202
  return id;
@@ -247,306 +237,6 @@ describe("cell reducer", () => {
247
237
  `);
248
238
  });
249
239
 
250
- it("can move a cell", () => {
251
- seedCell({
252
- cellId: firstCellId,
253
- before: false,
254
- });
255
- expect(formatCells(state)).toMatchInlineSnapshot(`
256
- "
257
- [0] ''
258
-
259
- [1] ''
260
- "
261
- `);
262
-
263
- // move first cell to the end
264
- actions.moveCell({
265
- cellId: firstCellId,
266
- before: false,
267
- });
268
- expect(formatCells(state)).toMatchInlineSnapshot(`
269
- "
270
- [1] ''
271
-
272
- [0] ''
273
- "
274
- `);
275
-
276
- // move it back
277
- actions.moveCell({
278
- cellId: firstCellId,
279
- before: true,
280
- });
281
- expect(formatCells(state)).toMatchInlineSnapshot(`
282
- "
283
- [0] ''
284
-
285
- [1] ''
286
- "
287
- `);
288
-
289
- // Add a column breakpoint to test left/right movement
290
- actions.addColumnBreakpoint({ cellId: cellId("1") });
291
- expect(formatCells(state)).toMatchInlineSnapshot(`
292
- "
293
- > col 0
294
- [0] ''
295
-
296
- > col 1
297
- [1] ''
298
- "
299
- `);
300
-
301
- // Move cell right
302
- actions.moveCell({
303
- cellId: firstCellId,
304
- direction: "right",
305
- });
306
- expect(formatCells(state)).toMatchInlineSnapshot(`
307
- "
308
- > col 0
309
-
310
-
311
- > col 1
312
- [0] ''
313
-
314
- [1] ''
315
- "
316
- `);
317
-
318
- // Move cell left
319
- actions.moveCell({
320
- cellId: firstCellId,
321
- direction: "left",
322
- });
323
- expect(formatCells(state)).toMatchInlineSnapshot(`
324
- "
325
- > col 0
326
- [0] ''
327
-
328
- > col 1
329
- [1] ''
330
- "
331
- `);
332
-
333
- // Try to move cell left when it's already in leftmost column (should noop)
334
- actions.moveCell({
335
- cellId: firstCellId,
336
- direction: "left",
337
- });
338
- expect(formatCells(state)).toMatchInlineSnapshot(`
339
- "
340
- > col 0
341
- [0] ''
342
-
343
- > col 1
344
- [1] ''
345
- "
346
- `);
347
-
348
- // Try to move cell right when it's already in rightmost column (should noop)
349
- actions.moveCell({
350
- cellId: cellId("1"),
351
- direction: "right",
352
- });
353
- expect(formatCells(state)).toMatchInlineSnapshot(`
354
- "
355
- > col 0
356
- [0] ''
357
-
358
- > col 1
359
- [1] ''
360
- "
361
- `);
362
- });
363
-
364
- it("can drag and drop a cell", () => {
365
- seedCell({
366
- cellId: firstCellId,
367
- before: false,
368
- });
369
- seedCell({
370
- cellId: cellId("1"),
371
- before: false,
372
- });
373
- expect(formatCells(state)).toMatchInlineSnapshot(`
374
- "
375
- [0] ''
376
-
377
- [1] ''
378
-
379
- [2] ''
380
- "
381
- `);
382
-
383
- // drag first cell to the end
384
- actions.dropCellOverCell({
385
- cellId: firstCellId,
386
- overCellId: cellId("2"),
387
- });
388
- expect(formatCells(state)).toMatchInlineSnapshot(`
389
- "
390
- [1] ''
391
-
392
- [2] ''
393
-
394
- [0] ''
395
- "
396
- `);
397
-
398
- // drag it back to the middle
399
- actions.dropCellOverCell({
400
- cellId: firstCellId,
401
- overCellId: cellId("2"),
402
- });
403
- expect(formatCells(state)).toMatchInlineSnapshot(`
404
- "
405
- [1] ''
406
-
407
- [0] ''
408
-
409
- [2] ''
410
- "
411
- `);
412
- });
413
-
414
- it("can move a cell to an exact index within a column", () => {
415
- seedCell({
416
- cellId: firstCellId,
417
- before: false,
418
- });
419
- seedCell({
420
- cellId: cellId("1"),
421
- before: false,
422
- });
423
-
424
- const columnId = state.cellIds.atOrThrow(0).id;
425
- actions.moveCellToIndex({
426
- cellId: firstCellId,
427
- columnId,
428
- index: 3,
429
- });
430
-
431
- expect(formatCells(state)).toMatchInlineSnapshot(`
432
- "
433
- [1] ''
434
-
435
- [2] ''
436
-
437
- [0] ''
438
- "
439
- `);
440
- });
441
-
442
- it("can move a cell to an exact index across columns", () => {
443
- seedCell({
444
- cellId: firstCellId,
445
- before: false,
446
- });
447
- seedCell({
448
- cellId: cellId("1"),
449
- before: false,
450
- });
451
- actions.addColumnBreakpoint({ cellId: cellId("1") });
452
-
453
- const secondColumnId = state.cellIds.atOrThrow(1).id;
454
- actions.moveCellToIndex({
455
- cellId: firstCellId,
456
- columnId: secondColumnId,
457
- index: 1,
458
- });
459
-
460
- expect(formatCells(state)).toMatchInlineSnapshot(`
461
- "
462
- > col 0
463
-
464
-
465
- > col 1
466
- [1] ''
467
-
468
- [0] ''
469
-
470
- [2] ''
471
- "
472
- `);
473
- });
474
-
475
- it("moveCellToIndex is a no-op when moving to the same position", () => {
476
- seedCell({
477
- cellId: firstCellId,
478
- before: false,
479
- });
480
- seedCell({
481
- cellId: cellId("1"),
482
- before: false,
483
- });
484
-
485
- const columnId = state.cellIds.atOrThrow(0).id;
486
- const before = formatCells(state);
487
-
488
- actions.moveCellToIndex({
489
- cellId: firstCellId,
490
- columnId,
491
- index: 0,
492
- });
493
-
494
- expect(formatCells(state)).toBe(before);
495
- });
496
-
497
- it("moveCellToIndex is a no-op for an invalid columnId", () => {
498
- seedCell({
499
- cellId: firstCellId,
500
- before: false,
501
- });
502
-
503
- const before = formatCells(state);
504
-
505
- actions.moveCellToIndex({
506
- cellId: firstCellId,
507
- columnId: "nonexistent-column" as CellColumnId,
508
- index: 0,
509
- });
510
-
511
- expect(formatCells(state)).toBe(before);
512
- });
513
-
514
- it("can move multiple cells relative to target", () => {
515
- seedCell({
516
- cellId: firstCellId,
517
- before: false,
518
- });
519
- seedCell({
520
- cellId: cellId("1"),
521
- before: false,
522
- });
523
- expect(formatCells(state)).toMatchInlineSnapshot(`
524
- "
525
- [0] ''
526
-
527
- [1] ''
528
-
529
- [2] ''
530
- "
531
- `);
532
-
533
- // Move first two cells after the third
534
- actions.moveCellsRelativeTo({
535
- cellIds: [firstCellId, cellId("1")],
536
- targetCellId: cellId("2"),
537
- position: "after",
538
- });
539
- expect(formatCells(state)).toMatchInlineSnapshot(`
540
- "
541
- [2] ''
542
-
543
- [0] ''
544
-
545
- [1] ''
546
- "
547
- `);
548
- });
549
-
550
240
  it("keeps error evidence after an edit and a new queued run", () => {
551
241
  const code = "raise ValueError('old failure')";
552
242
  const output: OutputMessage = {
@@ -1439,61 +1129,13 @@ describe("cell reducer", () => {
1439
1129
  expect(cell).toMatchSnapshot("console received while idle");
1440
1130
  });
1441
1131
 
1442
- it("can send a cell to the top", () => {
1443
- seedCell({ cellId: firstCellId, before: false });
1444
- seedCell({ cellId: cellId("1"), before: false });
1445
- actions.sendToTop({ cellId: cellId("2") });
1446
- expect(formatCells(state)).toMatchInlineSnapshot(`
1447
- "
1448
- [2] ''
1132
+ it("can fold and unfold all cells", () => {
1133
+ actions.foldAll();
1134
+ expect(foldAllBulk).toHaveBeenCalled();
1449
1135
 
1450
- [0] ''
1451
-
1452
- [1] ''
1453
- "
1454
- `);
1455
- });
1456
-
1457
- it("can send a cell to the bottom", () => {
1458
- seedCell({ cellId: firstCellId, before: false });
1459
- seedCell({ cellId: cellId("1"), before: false });
1460
- actions.sendToBottom({ cellId: firstCellId });
1461
- expect(formatCells(state)).toMatchInlineSnapshot(`
1462
- "
1463
- [1] ''
1464
-
1465
- [2] ''
1466
-
1467
- [0] ''
1468
- "
1469
- `);
1470
- });
1471
-
1472
- it("can focus cells", () => {
1473
- seedCell({ cellId: firstCellId, before: false });
1474
- seedCell({ cellId: cellId("1"), before: false });
1475
-
1476
- actions.focusCell({ cellId: cellId("1"), where: "before" });
1477
- expect(focusAndScrollCellIntoView).toHaveBeenCalledWith(
1478
- expect.objectContaining({
1479
- cellId: cellId("0"),
1480
- }),
1481
- );
1482
-
1483
- actions.focusTopCell();
1484
- expect(scrollToTop).toHaveBeenCalled();
1485
-
1486
- actions.focusBottomCell();
1487
- expect(scrollToBottom).toHaveBeenCalled();
1488
- });
1489
-
1490
- it("can fold and unfold all cells", () => {
1491
- actions.foldAll();
1492
- expect(foldAllBulk).toHaveBeenCalled();
1493
-
1494
- actions.unfoldAll();
1495
- expect(unfoldAllBulk).toHaveBeenCalled();
1496
- });
1136
+ actions.unfoldAll();
1137
+ expect(unfoldAllBulk).toHaveBeenCalled();
1138
+ });
1497
1139
 
1498
1140
  it("can clear logs", () => {
1499
1141
  state.cellLogs = [
@@ -1514,311 +1156,6 @@ describe("cell reducer", () => {
1514
1156
  expect(state.cellLogs).toEqual([]);
1515
1157
  });
1516
1158
 
1517
- it("can collapse and expand cells", () => {
1518
- seedCell({ cellId: firstCellId, before: false });
1519
- seedCell({
1520
- cellId: cellId("1"),
1521
- before: false,
1522
- code: "# Header",
1523
- });
1524
- seedCell({
1525
- cellId: cellId("2"),
1526
- before: false,
1527
- code: "## Subheader",
1528
- });
1529
-
1530
- const id = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(1);
1531
- state.cellRuntime[id] = {
1532
- ...state.cellRuntime[id],
1533
- outline: {
1534
- items: [{ name: "Header", level: 1, by: { id: "header" } }],
1535
- },
1536
- };
1537
-
1538
- actions.collapseCell({ cellId: id });
1539
- expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(id)).toBe(true);
1540
-
1541
- actions.expandCell({ cellId: id });
1542
- expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(id)).toBe(false);
1543
- });
1544
-
1545
- it("can collapse and expand all cells in multiple columns", () => {
1546
- seedCell({ cellId: firstCellId, before: false });
1547
- seedCell({
1548
- cellId: cellId("1"),
1549
- before: false,
1550
- code: "# First Column Header",
1551
- });
1552
- seedCell({
1553
- cellId: cellId("2"),
1554
- before: false,
1555
- code: "## First Column Subheader",
1556
- });
1557
-
1558
- actions.addColumnBreakpoint({ cellId: cellId("2") });
1559
-
1560
- seedCell({
1561
- cellId: cellId("3"),
1562
- before: false,
1563
- code: "# Second Column Header",
1564
- });
1565
- seedCell({
1566
- cellId: cellId("4"),
1567
- before: false,
1568
- code: "## Second Column Subheader",
1569
- });
1570
-
1571
- const firstColumnHeaderId = state.cellIds
1572
- .atOrThrow(FIRST_COLUMN)
1573
- .atOrThrow(1);
1574
- state.cellRuntime[firstColumnHeaderId] = {
1575
- ...state.cellRuntime[firstColumnHeaderId],
1576
- outline: {
1577
- items: [
1578
- { name: "First Column Header", level: 1, by: { id: "header" } },
1579
- ],
1580
- },
1581
- };
1582
-
1583
- const secondColumnHeaderId = state.cellIds
1584
- .atOrThrow(SECOND_COLUMN)
1585
- .atOrThrow(0);
1586
- state.cellRuntime[secondColumnHeaderId] = {
1587
- ...state.cellRuntime[secondColumnHeaderId],
1588
- outline: {
1589
- items: [
1590
- { name: "Second Column Header", level: 1, by: { id: "header" } },
1591
- ],
1592
- },
1593
- };
1594
-
1595
- actions.collapseAllCells();
1596
- expect(
1597
- state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(firstColumnHeaderId),
1598
- ).toBe(true);
1599
- expect(
1600
- state.cellIds.atOrThrow(SECOND_COLUMN).isCollapsed(secondColumnHeaderId),
1601
- ).toBe(true);
1602
-
1603
- actions.expandAllCells();
1604
- expect(
1605
- state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(firstColumnHeaderId),
1606
- ).toBe(false);
1607
- expect(
1608
- state.cellIds.atOrThrow(SECOND_COLUMN).isCollapsed(secondColumnHeaderId),
1609
- ).toBe(false);
1610
- });
1611
-
1612
- it("can collapse and expand nested cells in one call", () => {
1613
- seedCell({ cellId: firstCellId, before: false });
1614
- seedCell({
1615
- cellId: cellId("1"),
1616
- before: false,
1617
- code: "# Header",
1618
- });
1619
- seedCell({
1620
- cellId: cellId("2"),
1621
- before: false,
1622
- code: "## Subheader",
1623
- });
1624
- seedCell({
1625
- cellId: cellId("3"),
1626
- before: false,
1627
- code: "### Subsubheader",
1628
- });
1629
-
1630
- const headerId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(1);
1631
- state.cellRuntime[headerId] = {
1632
- ...state.cellRuntime[headerId],
1633
- outline: {
1634
- items: [{ name: "Header", level: 1, by: { id: "header" } }],
1635
- },
1636
- };
1637
-
1638
- const subheaderId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(2);
1639
- state.cellRuntime[subheaderId] = {
1640
- ...state.cellRuntime[subheaderId],
1641
- outline: {
1642
- items: [{ name: "Subheader", level: 2, by: { id: "subheader" } }],
1643
- },
1644
- };
1645
-
1646
- // Check if both the parent and child are collapsed
1647
- actions.collapseAllCells();
1648
- expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(headerId)).toBe(
1649
- true,
1650
- );
1651
- actions.expandCell({ cellId: headerId });
1652
- expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(subheaderId)).toBe(
1653
- true,
1654
- );
1655
- actions.collapseCell({ cellId: headerId });
1656
-
1657
- // Check if both the parent and child are expanded
1658
- actions.expandAllCells();
1659
- expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(headerId)).toBe(
1660
- false,
1661
- );
1662
- expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(subheaderId)).toBe(
1663
- false,
1664
- );
1665
- });
1666
-
1667
- it("can collapse all cells when nested heading ranges end at the same cell", () => {
1668
- seedCell({ cellId: firstCellId, before: false });
1669
- seedCell({
1670
- cellId: cellId("1"),
1671
- before: false,
1672
- code: "# Header",
1673
- });
1674
- seedCell({
1675
- cellId: cellId("2"),
1676
- before: false,
1677
- code: "## Subheader",
1678
- });
1679
- seedCell({
1680
- cellId: cellId("3"),
1681
- before: false,
1682
- code: "### Subsubheader",
1683
- });
1684
-
1685
- const headerId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(1);
1686
- state.cellRuntime[headerId] = {
1687
- ...state.cellRuntime[headerId],
1688
- outline: {
1689
- items: [{ name: "Header", level: 1, by: { id: "header" } }],
1690
- },
1691
- };
1692
-
1693
- const subheaderId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(2);
1694
- state.cellRuntime[subheaderId] = {
1695
- ...state.cellRuntime[subheaderId],
1696
- outline: {
1697
- items: [{ name: "Subheader", level: 2, by: { id: "subheader" } }],
1698
- },
1699
- };
1700
-
1701
- const subsubheaderId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(3);
1702
- state.cellRuntime[subsubheaderId] = {
1703
- ...state.cellRuntime[subsubheaderId],
1704
- outline: {
1705
- items: [{ name: "Subsubheader", level: 3, by: { id: "subsubheader" } }],
1706
- },
1707
- };
1708
-
1709
- // The header, subheader and subsubheader ranges all end at the last
1710
- // cell. Collapsing all used to throw "Node ... not found in tree" and
1711
- // leave the whole notebook unchanged.
1712
- actions.collapseAllCells();
1713
- expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(headerId)).toBe(
1714
- true,
1715
- );
1716
- expect(state.cellIds.atOrThrow(FIRST_COLUMN).topLevelIds).toEqual([
1717
- firstCellId,
1718
- headerId,
1719
- ]);
1720
-
1721
- // Each level is nested under its parent, collapsed
1722
- actions.expandCell({ cellId: headerId });
1723
- expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(subheaderId)).toBe(
1724
- true,
1725
- );
1726
- actions.expandCell({ cellId: subheaderId });
1727
- expect(
1728
- state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(subsubheaderId),
1729
- ).toBe(true);
1730
- });
1731
-
1732
- it("can collapse all cells when a deep chain is followed by a later section", () => {
1733
- seedCell({ cellId: firstCellId, before: false });
1734
- seedCell({
1735
- cellId: cellId("1"),
1736
- before: false,
1737
- code: "# Header",
1738
- });
1739
- seedCell({
1740
- cellId: cellId("2"),
1741
- before: false,
1742
- code: "## Section A",
1743
- });
1744
- seedCell({
1745
- cellId: cellId("3"),
1746
- before: false,
1747
- code: "### Sub of A",
1748
- });
1749
- seedCell({
1750
- cellId: cellId("4"),
1751
- before: false,
1752
- code: "## Section B",
1753
- });
1754
-
1755
- const headerId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(1);
1756
- state.cellRuntime[headerId] = {
1757
- ...state.cellRuntime[headerId],
1758
- outline: {
1759
- items: [{ name: "Header", level: 1, by: { id: "header" } }],
1760
- },
1761
- };
1762
-
1763
- const sectionAId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(2);
1764
- state.cellRuntime[sectionAId] = {
1765
- ...state.cellRuntime[sectionAId],
1766
- outline: {
1767
- items: [{ name: "Section A", level: 2, by: { id: "section-a" } }],
1768
- },
1769
- };
1770
-
1771
- const subsectionId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(3);
1772
- state.cellRuntime[subsectionId] = {
1773
- ...state.cellRuntime[subsectionId],
1774
- outline: {
1775
- items: [{ name: "Sub of A", level: 3, by: { id: "sub-of-a" } }],
1776
- },
1777
- };
1778
-
1779
- const sectionBId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(4);
1780
- state.cellRuntime[sectionBId] = {
1781
- ...state.cellRuntime[sectionBId],
1782
- outline: {
1783
- items: [{ name: "Section B", level: 2, by: { id: "section-b" } }],
1784
- },
1785
- };
1786
-
1787
- actions.collapseAllCells();
1788
- expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(headerId)).toBe(
1789
- true,
1790
- );
1791
- expect(state.cellIds.atOrThrow(FIRST_COLUMN).topLevelIds).toEqual([
1792
- firstCellId,
1793
- headerId,
1794
- ]);
1795
-
1796
- // Both sections sit under the header, collapsed, with the subsection
1797
- // nested under section A
1798
- actions.expandCell({ cellId: headerId });
1799
- expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(sectionAId)).toBe(
1800
- true,
1801
- );
1802
- expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(sectionBId)).toBe(
1803
- true,
1804
- );
1805
- actions.expandCell({ cellId: sectionAId });
1806
- expect(
1807
- state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(subsectionId),
1808
- ).toBe(true);
1809
- });
1810
-
1811
- it("can show hidden cells", () => {
1812
- seedCell({ cellId: firstCellId, before: false });
1813
- seedCell({ cellId: cellId("1"), before: false });
1814
- actions.collapseCell({ cellId: firstCellId });
1815
-
1816
- actions.showCellIfHidden({ cellId: cellId("1") });
1817
- expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(firstCellId)).toBe(
1818
- false,
1819
- );
1820
- });
1821
-
1822
1159
  it("can split and undo split cells", () => {
1823
1160
  seedCell({
1824
1161
  cellId: firstCellId,
@@ -1998,79 +1335,6 @@ describe("cell reducer", () => {
1998
1335
  expect(state).toEqual(initialState);
1999
1336
  });
2000
1337
 
2001
- it("can drop a cell over another cell", () => {
2002
- seedCell({ cellId: firstCellId, before: false });
2003
- seedCell({ cellId: cellId("1"), before: false });
2004
- seedCell({ cellId: cellId("2"), before: false });
2005
-
2006
- expect(formatCells(state)).toMatchInlineSnapshot(`
2007
- "
2008
- [0] ''
2009
-
2010
- [1] ''
2011
-
2012
- [2] ''
2013
-
2014
- [3] ''
2015
- "
2016
- `);
2017
-
2018
- actions.dropCellOverCell({
2019
- cellId: cellId("0"),
2020
- overCellId: cellId("3"),
2021
- });
2022
-
2023
- expect(formatCells(state)).toMatchInlineSnapshot(`
2024
- "
2025
- [1] ''
2026
-
2027
- [2] ''
2028
-
2029
- [3] ''
2030
-
2031
- [0] ''
2032
- "
2033
- `);
2034
- });
2035
-
2036
- it("can drop a cell over a new column", () => {
2037
- seedCell({ cellId: firstCellId, before: false });
2038
- seedCell({ cellId: cellId("1"), before: false });
2039
-
2040
- expect(state.cellIds.getColumns().length).toBe(1);
2041
- expect(formatCells(state)).toMatchInlineSnapshot(`
2042
- "
2043
- [0] ''
2044
-
2045
- [1] ''
2046
-
2047
- [2] ''
2048
- "
2049
- `);
2050
-
2051
- actions.dropOverNewColumn({ cellId: cellId("1") });
2052
-
2053
- expect(state.cellIds.getColumns().length).toBe(2);
2054
- expect(formatCells(state)).toMatchInlineSnapshot(`
2055
- "
2056
- > col 0
2057
- [0] ''
2058
-
2059
- [2] ''
2060
-
2061
- > col 1
2062
- [1] ''
2063
- "
2064
- `);
2065
-
2066
- // Check that the cells are in the correct columns
2067
- expect(state.cellIds.getColumns()[0].topLevelIds).toEqual([
2068
- cellId("0"),
2069
- cellId("2"),
2070
- ]);
2071
- expect(state.cellIds.getColumns()[1].topLevelIds).toEqual([cellId("1")]);
2072
- });
2073
-
2074
1338
  it("can drop a column over another column", () => {
2075
1339
  seedCell({ cellId: firstCellId, before: false });
2076
1340
  seedCell({ cellId: cellId("1"), before: false });
@@ -2129,7 +1393,11 @@ describe("cell reducer", () => {
2129
1393
  seedCell({ cellId: cellId("1"), before: false });
2130
1394
  actions.addColumnBreakpoint({ cellId: cellId("1") });
2131
1395
  actions.addColumnBreakpoint({ cellId: cellId("2") });
2132
- actions.dropOverNewColumn({ cellId: cellId("2") });
1396
+ // A column emptied by a drag stays until the drop compacts it.
1397
+ state = {
1398
+ ...state,
1399
+ cellIds: state.cellIds.addColumn(state.cellIds.getColumnIds()[1]),
1400
+ };
2133
1401
 
2134
1402
  expect(state.cellIds.getColumns().length).toBe(4);
2135
1403
  expect(formatCells(state)).toMatchInlineSnapshot(`
@@ -2342,279 +1610,6 @@ describe("cell reducer", () => {
2342
1610
  expect(state.cellRuntime[cell2Id].output).toBeNull();
2343
1611
  });
2344
1612
 
2345
- describe("untouched cells functionality", () => {
2346
- it("starts with empty untouchedNewCells set", () => {
2347
- expect(state.untouchedNewCells).toEqual(new Set());
2348
- });
2349
-
2350
- it("can mark a cell as touched", () => {
2351
- // Create a cell with hideCode
2352
- seedCell({
2353
- cellId: "__end__",
2354
- before: false,
2355
- hideCode: true,
2356
- });
2357
-
2358
- const newCellId =
2359
- state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
2360
- expect(state.untouchedNewCells.has(newCellId)).toBe(true);
2361
-
2362
- // Mark it as touched
2363
- actions.markTouched({ cellId: newCellId });
2364
-
2365
- expect(state.untouchedNewCells.has(newCellId)).toBe(false);
2366
- });
2367
-
2368
- it("markTouched is idempotent", () => {
2369
- // Create a cell with hideCode
2370
- seedCell({
2371
- cellId: "__end__",
2372
- before: false,
2373
- hideCode: true,
2374
- });
2375
-
2376
- const newCellId =
2377
- state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
2378
- expect(state.untouchedNewCells.has(newCellId)).toBe(true);
2379
-
2380
- // Mark it as touched multiple times
2381
- actions.markTouched({ cellId: newCellId });
2382
- actions.markTouched({ cellId: newCellId });
2383
- actions.markTouched({ cellId: newCellId });
2384
-
2385
- expect(state.untouchedNewCells.has(newCellId)).toBe(false);
2386
- });
2387
-
2388
- it("can mark a non-existent cell as touched without error", () => {
2389
- const nonExistentCellId = cellId("non-existent");
2390
-
2391
- expect(() => {
2392
- actions.markTouched({ cellId: nonExistentCellId });
2393
- }).not.toThrow();
2394
-
2395
- expect(state.untouchedNewCells.has(nonExistentCellId)).toBe(false);
2396
- });
2397
-
2398
- it("can handle multiple untouched cells", () => {
2399
- // Create multiple cells with hideCode
2400
- seedCell({
2401
- cellId: "__end__",
2402
- before: false,
2403
- hideCode: true,
2404
- });
2405
- seedCell({
2406
- cellId: "__end__",
2407
- before: false,
2408
- hideCode: true,
2409
- });
2410
- seedCell({
2411
- cellId: "__end__",
2412
- before: false,
2413
- hideCode: false,
2414
- });
2415
-
2416
- const cellIds = state.cellIds.inOrderIds;
2417
- const cell1Id = cellIds[cellIds.length - 3];
2418
- const cell2Id = cellIds[cellIds.length - 2];
2419
- const cell3Id = cellIds[cellIds.length - 1];
2420
-
2421
- expect(state.untouchedNewCells.has(cell1Id)).toBe(true);
2422
- expect(state.untouchedNewCells.has(cell2Id)).toBe(true);
2423
- expect(state.untouchedNewCells.has(cell3Id)).toBe(false);
2424
-
2425
- // Mark one as touched
2426
- actions.markTouched({ cellId: cell1Id });
2427
-
2428
- expect(state.untouchedNewCells.has(cell1Id)).toBe(false);
2429
- expect(state.untouchedNewCells.has(cell2Id)).toBe(true);
2430
- expect(state.untouchedNewCells.has(cell3Id)).toBe(false);
2431
- });
2432
-
2433
- it("preserves untouched state when cells are moved", () => {
2434
- // Create cells with hideCode
2435
- seedCell({
2436
- cellId: "__end__",
2437
- before: false,
2438
- hideCode: true,
2439
- });
2440
- seedCell({
2441
- cellId: "__end__",
2442
- before: false,
2443
- hideCode: true,
2444
- });
2445
-
2446
- const cellIds = state.cellIds.inOrderIds;
2447
- const cell1Id = cellIds[cellIds.length - 2];
2448
- const cell2Id = cellIds[cellIds.length - 1];
2449
-
2450
- expect(state.untouchedNewCells.has(cell1Id)).toBe(true);
2451
- expect(state.untouchedNewCells.has(cell2Id)).toBe(true);
2452
-
2453
- // Move cell1 to the end
2454
- actions.moveCell({
2455
- cellId: cell1Id,
2456
- before: false,
2457
- });
2458
-
2459
- // Both cells should still be untouched
2460
- expect(state.untouchedNewCells.has(cell1Id)).toBe(true);
2461
- expect(state.untouchedNewCells.has(cell2Id)).toBe(true);
2462
- });
2463
-
2464
- it("untouched cell behavior with hide_code config", () => {
2465
- // Create a cell with hideCode and hide_code config
2466
- seedCell({
2467
- cellId: "__end__",
2468
- before: false,
2469
- hideCode: true,
2470
- });
2471
-
2472
- const newCellId =
2473
- state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
2474
-
2475
- // The document hides the cell's code
2476
- state.cellData[newCellId] = {
2477
- ...state.cellData[newCellId],
2478
- config: { ...state.cellData[newCellId].config, hide_code: true },
2479
- };
2480
-
2481
- // Cell should be untouched and have hide_code config
2482
- expect(state.untouchedNewCells.has(newCellId)).toBe(true);
2483
- expect(state.cellData[newCellId].config.hide_code).toBe(true);
2484
-
2485
- // Code should not be hidden because cell is untouched
2486
- expect(exportedForTesting.isCellCodeHidden(state, newCellId)).toBe(false);
2487
-
2488
- // Mark as touched
2489
- actions.markTouched({ cellId: newCellId });
2490
-
2491
- // Now code should be hidden
2492
- expect(state.untouchedNewCells.has(newCellId)).toBe(false);
2493
- expect(exportedForTesting.isCellCodeHidden(state, newCellId)).toBe(true);
2494
- });
2495
-
2496
- it("can mark an existing cell as untouched", () => {
2497
- // Create a cell without hideCode (not in untouchedNewCells)
2498
- seedCell({
2499
- cellId: "__end__",
2500
- before: false,
2501
- hideCode: false,
2502
- });
2503
-
2504
- const newCellId =
2505
- state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
2506
- expect(state.untouchedNewCells.has(newCellId)).toBe(false);
2507
-
2508
- // Mark it as untouched
2509
- actions.markUntouched({ cellId: newCellId });
2510
-
2511
- expect(state.untouchedNewCells.has(newCellId)).toBe(true);
2512
- });
2513
-
2514
- it("markUntouched is idempotent", () => {
2515
- // Create a cell without hideCode
2516
- seedCell({
2517
- cellId: "__end__",
2518
- before: false,
2519
- hideCode: false,
2520
- });
2521
-
2522
- const newCellId =
2523
- state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
2524
- expect(state.untouchedNewCells.has(newCellId)).toBe(false);
2525
-
2526
- // Mark as untouched multiple times
2527
- actions.markUntouched({ cellId: newCellId });
2528
- actions.markUntouched({ cellId: newCellId });
2529
- actions.markUntouched({ cellId: newCellId });
2530
-
2531
- expect(state.untouchedNewCells.has(newCellId)).toBe(true);
2532
- });
2533
-
2534
- it("markUntouched does not affect already untouched cells", () => {
2535
- // Create a cell with hideCode (already in untouchedNewCells)
2536
- seedCell({
2537
- cellId: "__end__",
2538
- before: false,
2539
- hideCode: true,
2540
- });
2541
-
2542
- const newCellId =
2543
- state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
2544
- expect(state.untouchedNewCells.has(newCellId)).toBe(true);
2545
-
2546
- // Calling markUntouched should not change anything
2547
- actions.markUntouched({ cellId: newCellId });
2548
-
2549
- expect(state.untouchedNewCells.has(newCellId)).toBe(true);
2550
- });
2551
-
2552
- it("markTouched and markUntouched can toggle cell state", () => {
2553
- // Create a cell without hideCode
2554
- seedCell({
2555
- cellId: "__end__",
2556
- before: false,
2557
- hideCode: false,
2558
- });
2559
-
2560
- const newCellId =
2561
- state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
2562
-
2563
- // Initially not untouched
2564
- expect(state.untouchedNewCells.has(newCellId)).toBe(false);
2565
-
2566
- // Mark as untouched
2567
- actions.markUntouched({ cellId: newCellId });
2568
- expect(state.untouchedNewCells.has(newCellId)).toBe(true);
2569
-
2570
- // Mark as touched
2571
- actions.markTouched({ cellId: newCellId });
2572
- expect(state.untouchedNewCells.has(newCellId)).toBe(false);
2573
-
2574
- // Mark as untouched again
2575
- actions.markUntouched({ cellId: newCellId });
2576
- expect(state.untouchedNewCells.has(newCellId)).toBe(true);
2577
- });
2578
-
2579
- it("markUntouched works for markdown cell conversion scenario", () => {
2580
- // Simulates converting a Python cell to Markdown
2581
- // 1. Create a regular cell (no hideCode)
2582
- seedCell({
2583
- cellId: "__end__",
2584
- before: false,
2585
- hideCode: false,
2586
- });
2587
-
2588
- const cellId =
2589
- state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
2590
-
2591
- // Cell starts without hide_code and not in untouchedNewCells
2592
- expect(state.cellData[cellId].config.hide_code).toBe(false);
2593
- expect(state.untouchedNewCells.has(cellId)).toBe(false);
2594
- expect(exportedForTesting.isCellCodeHidden(state, cellId)).toBe(false);
2595
-
2596
- // 2. Convert to markdown: the document hides the code, and the cell
2597
- // is marked untouched
2598
- state.cellData[cellId] = {
2599
- ...state.cellData[cellId],
2600
- config: { ...state.cellData[cellId].config, hide_code: true },
2601
- };
2602
- actions.markUntouched({ cellId });
2603
-
2604
- // Code should NOT be hidden because cell is untouched (user can edit)
2605
- expect(state.cellData[cellId].config.hide_code).toBe(true);
2606
- expect(state.untouchedNewCells.has(cellId)).toBe(true);
2607
- expect(exportedForTesting.isCellCodeHidden(state, cellId)).toBe(false);
2608
-
2609
- // 3. User blurs the cell (markTouched)
2610
- actions.markTouched({ cellId });
2611
-
2612
- // Now code should be hidden
2613
- expect(state.untouchedNewCells.has(cellId)).toBe(false);
2614
- expect(exportedForTesting.isCellCodeHidden(state, cellId)).toBe(true);
2615
- });
2616
- });
2617
-
2618
1613
  describe("releaseCellAtoms", () => {
2619
1614
  it("atom families cache atoms until explicitly removed", () => {
2620
1615
  const { cellDataAtom, cellRuntimeAtom, cellHandleAtom } =
@@ -2639,91 +1634,6 @@ describe("cell reducer", () => {
2639
1634
  });
2640
1635
  });
2641
1636
 
2642
- describe("isCellCodeHidden", () => {
2643
- const state = initialNotebookState();
2644
- const firstCellId = state.cellIds.inOrderIds[0];
2645
-
2646
- it("returns false when hide_code is false and cell is not untouched", () => {
2647
- const testCellId = cellId("test-cell");
2648
- const testState: NotebookState = {
2649
- ...state,
2650
- cellData: {
2651
- ...state.cellData,
2652
- [testCellId]: {
2653
- ...state.cellData[firstCellId],
2654
- id: testCellId,
2655
- config: { hide_code: false, disabled: false, column: null },
2656
- },
2657
- },
2658
- untouchedNewCells: new Set(),
2659
- };
2660
-
2661
- expect(exportedForTesting.isCellCodeHidden(testState, testCellId)).toBe(
2662
- false,
2663
- );
2664
- });
2665
-
2666
- it("returns true when hide_code is true and cell is not untouched", () => {
2667
- const testCellId = cellId("test-cell");
2668
- const testState: NotebookState = {
2669
- ...state,
2670
- cellData: {
2671
- ...state.cellData,
2672
- [testCellId]: {
2673
- ...state.cellData[firstCellId],
2674
- id: testCellId,
2675
- config: { hide_code: true, disabled: false, column: null },
2676
- },
2677
- },
2678
- untouchedNewCells: new Set(),
2679
- };
2680
-
2681
- expect(exportedForTesting.isCellCodeHidden(testState, testCellId)).toBe(
2682
- true,
2683
- );
2684
- });
2685
-
2686
- it("returns false when hide_code is true but cell is untouched", () => {
2687
- const testCellId = cellId("test-cell");
2688
- const testState: NotebookState = {
2689
- ...state,
2690
- cellData: {
2691
- ...state.cellData,
2692
- [testCellId]: {
2693
- ...state.cellData[firstCellId],
2694
- id: testCellId,
2695
- config: { hide_code: true, disabled: false, column: null },
2696
- },
2697
- },
2698
- untouchedNewCells: new Set([testCellId]),
2699
- };
2700
-
2701
- expect(exportedForTesting.isCellCodeHidden(testState, testCellId)).toBe(
2702
- false,
2703
- );
2704
- });
2705
-
2706
- it("returns false when hide_code is false and cell is untouched", () => {
2707
- const testCellId = cellId("test-cell");
2708
- const testState: NotebookState = {
2709
- ...state,
2710
- cellData: {
2711
- ...state.cellData,
2712
- [testCellId]: {
2713
- ...state.cellData[firstCellId],
2714
- id: testCellId,
2715
- config: { hide_code: false, disabled: false, column: null },
2716
- },
2717
- },
2718
- untouchedNewCells: new Set([testCellId]),
2719
- };
2720
-
2721
- expect(exportedForTesting.isCellCodeHidden(testState, testCellId)).toBe(
2722
- false,
2723
- );
2724
- });
2725
- });
2726
-
2727
1637
  describe("createTracebackInfoAtom", () => {
2728
1638
  const store = createStore();
2729
1639