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

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 (135) hide show
  1. package/dist/{ConnectedDataExplorerComponent-BRuwLOIx.js → ConnectedDataExplorerComponent-BsWiIPFk.js} +4 -4
  2. package/dist/{ErrorBoundary-C3Xv3yzV.js → ErrorBoundary-D1Z3KAf_.js} +2 -2
  3. package/dist/{abnfDiagram-VCTEODGH-LlJTOOU_.js → abnfDiagram-VCTEODGH-I2x4Yu25.js} +2 -2
  4. package/dist/{architectureDiagram-5GKGNRK7-9PQtGLLZ.js → architectureDiagram-5GKGNRK7-1bSQMKzK.js} +1 -1
  5. package/dist/{arrays-DA6KkLfq.js → arrays-DoQ-AILx.js} +7 -21
  6. package/dist/{capabilities-CvWU-ey_.js → capabilities-C2QUNRXi.js} +1 -1
  7. package/dist/{chat-ui-Bl2euMBD.js → chat-ui-BAyHM_GD.js} +524 -524
  8. package/dist/{chunk-IMKFNOWR-DcXv-BD5.js → chunk-IMKFNOWR-fsCGAJ9G.js} +1 -1
  9. package/dist/{chunk-SHT3W25Y-VwnELN23.js → chunk-SHT3W25Y-D2STW4hj.js} +1 -1
  10. package/dist/{chunk-SVP7TREG-DgQpI5Zi.js → chunk-SVP7TREG-CiY7cQG-.js} +1 -1
  11. package/dist/{chunk-TICWLB2K-CTg6VTJT.js → chunk-TICWLB2K-C_t5RBp9.js} +1 -1
  12. package/dist/{classDiagram-ZZMXUADV-UzmcFrnW.js → classDiagram-ZZMXUADV-B2JvZAP4.js} +2 -2
  13. package/dist/{classDiagram-v2-VYDZK3BY-DhjiI6IQ.js → classDiagram-v2-VYDZK3BY-L8VsZG5A.js} +2 -2
  14. package/dist/{code-visibility-BCcqmP58.js → code-visibility-CInEQd1e.js} +19 -19
  15. package/dist/{common-DrNB2bY2.js → common-W5HfVJPN.js} +4362 -4362
  16. package/dist/{cynefinDiagram-5FMLGOSQ-O4ShIbJX.js → cynefinDiagram-5FMLGOSQ-DdllC264.js} +1 -1
  17. package/dist/{dagre-GXQ25YYZ-dtHhhcPu.js → dagre-GXQ25YYZ-BaWHw4_f.js} +1 -1
  18. package/dist/{data-grid-overlay-editor-TGXV_Hf4.js → data-grid-overlay-editor-BQCaV0fI.js} +1 -1
  19. package/dist/{diagram-S7CK7UJ4-ppPYVZ6W.js → diagram-S7CK7UJ4-pFt-tUDe.js} +1 -1
  20. package/dist/{diagram-UQ7AKVKN-CMZDf--k.js → diagram-UQ7AKVKN-DqGJiBp4.js} +1 -1
  21. package/dist/{diagram-VX7I27RA-CFvJdx-C.js → diagram-VX7I27RA-Dr-yzMjf.js} +1 -1
  22. package/dist/{diagram-Z3DM3KII-Dt6_uFbv.js → diagram-Z3DM3KII-8xzJzv-w.js} +1 -1
  23. package/dist/{dropdown-menu-CeaOsjWZ.js → dropdown-menu-NlnsmNA6.js} +2 -2
  24. package/dist/{ebnfDiagram-PWID7BFC-DpRFrKK-.js → ebnfDiagram-PWID7BFC-Br04Wy-E.js} +2 -2
  25. package/dist/{erDiagram-RLTQ6QDP-CqAU4b0u.js → erDiagram-RLTQ6QDP-D5-XVgvv.js} +1 -1
  26. package/dist/{error-entries-CYGwr5-O.js → error-entries-BacCWMWj.js} +4678 -5028
  27. package/dist/{feedback-button-PTiPn2GP.js → feedback-button-DLXUcQ7l.js} +113 -113
  28. package/dist/{flowDiagram-HODETNUW-i8-Gw8HO.js → flowDiagram-HODETNUW-CVl2kREF.js} +2 -2
  29. package/dist/{formats-BtoHHy42.js → formats-BCYthwyc.js} +1 -1
  30. package/dist/{glide-data-editor-BSyexjkB.js → glide-data-editor-D5YaHPKx.js} +5 -5
  31. package/dist/{globals-B7gkJAxm.js → globals-DjX1uxzy.js} +2 -2
  32. package/dist/{html-to-image-Cy5UDpQJ.js → html-to-image-DAmgSo5P.js} +624 -573
  33. package/dist/{infoDiagram-27XIBGKW-DpfOKdLL.js → infoDiagram-27XIBGKW-BJ4vNcZE.js} +1 -1
  34. package/dist/{ishikawaDiagram-5VMMS53U-Dg7mrMG7.js → ishikawaDiagram-5VMMS53U-B_Jy1Dsm.js} +1 -1
  35. package/dist/{kanban-definition-UXKFOSKX-QB6e3Jrj.js → kanban-definition-UXKFOSKX-C6MkS9vw.js} +1 -1
  36. package/dist/main.js +1117 -1117
  37. package/dist/{mermaid-CeWG4Egr.js → mermaid-Bh918qrq.js} +34 -34
  38. package/dist/{mindmap-definition-YA3MSWOX-BTz3uTGe.js → mindmap-definition-YA3MSWOX-BP3ZTuir.js} +1 -1
  39. package/dist/{number-overlay-editor-Bb91yQ46.js → number-overlay-editor-3C_K6w_0.js} +1 -1
  40. package/dist/{once-DWGO2mQ2.js → once-NnpX_HnX.js} +1 -1
  41. package/dist/{pegDiagram-XKGWAZYB-3ZIEa3mB.js → pegDiagram-XKGWAZYB-nNGOQtnt.js} +2 -2
  42. package/dist/{pieDiagram-E7YTZNPT-BJAlk046.js → pieDiagram-E7YTZNPT-CpRz5BZz.js} +1 -1
  43. package/dist/{process-output-q9MTAIMX.js → process-output-DweUmAmV.js} +3 -3
  44. package/dist/{railroadDiagram-O6MQD6OU-Dn3bsSRa.js → railroadDiagram-O6MQD6OU-BqOEN4I0.js} +2 -2
  45. package/dist/{requirementDiagram-BXWQKSXE-wcOmQQCL.js → requirementDiagram-BXWQKSXE-CEfg6vu9.js} +1 -1
  46. package/dist/{reveal-component-vTdjiyDv.js → reveal-component-JjJRTT9d.js} +17 -17
  47. package/dist/{sequenceDiagram-WJ2MYXX4-DSh_qxoF.js → sequenceDiagram-WJ2MYXX4-IOmpQcOi.js} +1 -1
  48. package/dist/{spec-C7Fos-If.js → spec-Dkp64vVQ.js} +1 -1
  49. package/dist/{stateDiagram-D77RDMKH-DH_H8NMH.js → stateDiagram-D77RDMKH-tgU-EY9_.js} +2 -2
  50. package/dist/{stateDiagram-v2-MP3YSRHH-D_JM7MEK.js → stateDiagram-v2-MP3YSRHH-BofhG6q1.js} +2 -2
  51. package/dist/{swimlanes-42K2YHIH-CfoTJO-9.js → swimlanes-42K2YHIH-DW4vWDBd.js} +1 -1
  52. package/dist/{swimlanesDiagram-VR7AAH4N-Z_QhvK0G.js → swimlanesDiagram-VR7AAH4N-BJcrqUJr.js} +2 -2
  53. package/dist/{swiper-component-D2geL1Ku.js → swiper-component-BVfnhbnt.js} +1 -1
  54. package/dist/{timeline-definition-24CTP7MA-BFtdScaL.js → timeline-definition-24CTP7MA-D_QU3NGs.js} +1 -1
  55. package/dist/{toDate-CQAFSwos.js → toDate-BNDEXbfV.js} +30 -30
  56. package/dist/{useAsyncData-BeyZyjso.js → useAsyncData-ASDni9vH.js} +1 -1
  57. package/dist/{useDeepCompareMemoize-DOQgae0W.js → useDeepCompareMemoize-B1Ogyf53.js} +1 -1
  58. package/dist/{useIframeCapabilities-DNvaj9wM.js → useIframeCapabilities-6a_yDNXp.js} +1 -1
  59. package/dist/{useLifecycle-n2lnCFS8.js → useLifecycle-Bfpa2m7k.js} +1 -1
  60. package/dist/{useTheme-CY777LXN.js → useTheme-5TwyOh3B.js} +66 -66
  61. package/dist/{vega-component-0nQzA-I8.js → vega-component-BHS9UOtC.js} +19 -19
  62. package/dist/{vennDiagram-4TSXK5OY-BXY8IGCA.js → vennDiagram-4TSXK5OY-YFYsNIAh.js} +1 -1
  63. package/dist/{wardleyDiagram-VM6X3IG4-D0tFe6eC.js → wardleyDiagram-VM6X3IG4-i_n8mAWC.js} +1 -1
  64. package/dist/{xychartDiagram-S5SC5T6Z-Dx8wFM-y.js → xychartDiagram-S5SC5T6Z-BYGN95wW.js} +1 -1
  65. package/package.json +1 -1
  66. package/src/__mocks__/document.tsx +13 -4
  67. package/src/__mocks__/notebook.ts +1 -2
  68. package/src/components/datasources/datasources.tsx +1 -1
  69. package/src/components/dependency-graph/custom-node.tsx +1 -1
  70. package/src/components/dependency-graph/minimap-content.tsx +12 -14
  71. package/src/components/editor/actions/useSetCodeVisibility.ts +1 -1
  72. package/src/components/editor/actions/useSetOutputsExpanded.ts +1 -1
  73. package/src/components/editor/alerts/stdin-blocking-alert.tsx +1 -1
  74. package/src/components/editor/cell/code/cell-editor.tsx +1 -1
  75. package/src/components/editor/cell/useSplitCell.tsx +8 -5
  76. package/src/components/editor/chrome/panels/dependency-graph-panel.tsx +1 -1
  77. package/src/components/editor/chrome/panels/session-panel.tsx +1 -1
  78. package/src/components/editor/chrome/panels/variable-panel.tsx +1 -5
  79. package/src/components/editor/chrome/wrapper/minimap-state.ts +1 -1
  80. package/src/components/editor/columns/__tests__/cell-column.test.tsx +1 -1
  81. package/src/components/editor/columns/cell-column.tsx +1 -1
  82. package/src/components/editor/columns/sortable-column.tsx +1 -1
  83. package/src/components/editor/links/__tests__/cell-link.test.tsx +1 -1
  84. package/src/components/editor/links/cell-link-list.tsx +1 -1
  85. package/src/components/editor/links/cell-link.tsx +1 -1
  86. package/src/components/editor/navigation/__tests__/navigation.test.ts +20 -6
  87. package/src/components/editor/navigation/__tests__/selection.test.ts +1 -2
  88. package/src/components/editor/navigation/clipboard.ts +5 -3
  89. package/src/components/editor/renderers/cell-array.tsx +7 -12
  90. package/src/components/editor/renderers/slides-layout/slides-layout.tsx +1 -1
  91. package/src/components/editor/renderers/vertical-layout/__tests__/useFocusFirstEditor.test.ts +4 -4
  92. package/src/components/editor/renderers/vertical-layout/useFocusFirstEditor.ts +1 -1
  93. package/src/components/editor/stdin-blocking-alert.tsx +1 -1
  94. package/src/components/slides/__tests__/minimap-actions.test.tsx +13 -4
  95. package/src/components/slides/__tests__/minimap.test.ts +38 -19
  96. package/src/components/slides/minimap.tsx +11 -10
  97. package/src/components/sort/SortableCellsProvider.tsx +2 -21
  98. package/src/components/tracing/tracing.tsx +1 -1
  99. package/src/core/ai/__tests__/staged-cells.test.ts +1 -1
  100. package/src/core/ai/context/providers/__tests__/cell-output.test.ts +8 -12
  101. package/src/core/ai/context/providers/cell-output.ts +2 -2
  102. package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +34 -66
  103. package/src/core/ai/tools/edit-notebook-tool.ts +12 -10
  104. package/src/core/cells/__tests__/actions.test.ts +64 -25
  105. package/src/core/cells/__tests__/arrange.test.ts +156 -4
  106. package/src/core/cells/__tests__/cells.test.ts +12 -290
  107. package/src/core/cells/__tests__/document.test.ts +2 -46
  108. package/src/core/cells/__tests__/harness.ts +28 -4
  109. package/src/core/cells/__tests__/layout.test.ts +42 -54
  110. package/src/core/cells/__tests__/session.test.ts +0 -2
  111. package/src/core/cells/__tests__/utils.test.ts +9 -107
  112. package/src/core/cells/actions.ts +112 -26
  113. package/src/core/cells/add-missing-import.ts +1 -1
  114. package/src/core/cells/arrange.ts +206 -13
  115. package/src/core/cells/cells.ts +28 -256
  116. package/src/core/cells/document.ts +18 -122
  117. package/src/core/cells/layout.ts +79 -10
  118. package/src/core/cells/pending-delete-service.ts +1 -1
  119. package/src/core/cells/utils.ts +4 -4
  120. package/src/core/codemirror/copilot/__tests__/getCodes.test.ts +6 -7
  121. package/src/core/codemirror/copilot/getCodes.ts +2 -2
  122. package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +1 -2
  123. package/src/core/codemirror/lsp/utils.ts +1 -1
  124. package/src/core/documentation/doc-lookup.ts +1 -1
  125. package/src/core/edit-app.tsx +2 -5
  126. package/src/core/errors/error-entries.ts +1 -1
  127. package/src/core/islands/__tests__/bootstrap.test.ts +2 -6
  128. package/src/core/islands/components/web-components.tsx +1 -1
  129. package/src/core/notebook/document/__tests__/codemirror.test.ts +8 -0
  130. package/src/core/session/__tests__/useNotebookSession.hook.test.tsx +1 -1
  131. package/src/stories/cell.stories.tsx +1 -2
  132. package/src/stories/layout/vertical/one-column.stories.tsx +1 -2
  133. package/src/stories/runtime-story.tsx +2 -3
  134. package/src/utils/__tests__/id-tree.test.ts +0 -617
  135. package/src/utils/id-tree.tsx +0 -609
@@ -1,609 +0,0 @@
1
- /* Copyright 2026 Marimo. All rights reserved. */
2
-
3
- import { Memoize } from "typescript-memoize";
4
- import { reorderColumnSizes } from "@/components/editor/columns/storage";
5
- import { SETUP_CELL_ID } from "@/core/cells/ids";
6
- import { arrayDelete, arrayInsert, arrayMove } from "./arrays";
7
- import { Logger } from "./Logger";
8
-
9
- /**
10
- * Branded number to help with type safety
11
- */
12
- export type CellColumnIndex = number & { __brand: "CellColumnIndex" };
13
-
14
- /**
15
- * Branded string to help with type safety
16
- */
17
- export type CellColumnId = string & { __brand: "CellColumnId" };
18
- /**
19
- * Weakly-branded number, since making `__brand` required causes type errors
20
- */
21
- export type CellIndex = number & { __brand?: "CellIndex" };
22
-
23
- /**
24
- * Tree data structure for handling ids with nested children
25
- */
26
- export class TreeNode<T> {
27
- public value: T;
28
-
29
- public constructor(value: T) {
30
- this.value = value;
31
- }
32
-
33
- public toString(): string {
34
- return String(this.value);
35
- }
36
-
37
- public equals(other: TreeNode<T>): boolean {
38
- return this.value === other.value;
39
- }
40
- }
41
-
42
- let uniqueId = 0;
43
-
44
- export class CollapsibleTree<T> {
45
- public readonly nodes: TreeNode<T>[];
46
- public readonly id: CellColumnId;
47
-
48
- private constructor(nodes: TreeNode<T>[], id: CellColumnId) {
49
- this.nodes = nodes;
50
- this.id = id;
51
- }
52
-
53
- /** A column of `ids`, keeping `id` when a column is rebuilt. */
54
- public static from<T>(ids: T[], id?: CellColumnId): CollapsibleTree<T> {
55
- return new CollapsibleTree(
56
- ids.map((id) => new TreeNode(id)),
57
- id ?? (`tree_${uniqueId++}` as CellColumnId),
58
- );
59
- }
60
-
61
- public withNodes(nodes: TreeNode<T>[]): CollapsibleTree<T> {
62
- return new CollapsibleTree(nodes, this.id);
63
- }
64
-
65
- @Memoize()
66
- public get topLevelIds(): T[] {
67
- return this.nodes.map((n) => n.value);
68
- }
69
-
70
- public get inOrderIds(): T[] {
71
- return this.topLevelIds;
72
- }
73
-
74
- @Memoize()
75
- public get idSet(): Set<T> {
76
- return new Set(this.inOrderIds);
77
- }
78
-
79
- public get length(): number {
80
- return this.nodes.length;
81
- }
82
-
83
- @Memoize()
84
- public get _nodeMap(): Map<T, TreeNode<T>> {
85
- const result = new Map<T, TreeNode<T>>();
86
- for (const node of this.nodes) {
87
- result.set(node.value, node);
88
- }
89
- return result;
90
- }
91
-
92
- /**
93
- * Get the index of the given node, or throw
94
- */
95
- public indexOfOrThrow(id: T): CellIndex {
96
- const index = this.nodes.findIndex((n) => n.value === id);
97
- if (index === -1) {
98
- throw new Error(
99
- `Node ${id} not found in tree. Valid ids: ${this.topLevelIds}`,
100
- );
101
- }
102
- return index as CellIndex;
103
- }
104
-
105
- /**
106
- * Get the top level nodes in the given range.
107
- * This does not include descendants.
108
- */
109
- public slice(start: number, end: number): T[] {
110
- return this.nodes.slice(start, end).map((n) => n.value);
111
- }
112
-
113
- /**
114
- * Get the node at the given index
115
- */
116
- public at(index: number): T | undefined {
117
- return this.nodes.at(index)?.value;
118
- }
119
-
120
- /**
121
- * Get the node at the given index
122
- */
123
- public atOrThrow(index: number): T {
124
- const node = this.nodes.at(index);
125
- if (node === undefined) {
126
- throw new Error(`Node at index ${index} not found in tree`);
127
- }
128
- return node.value;
129
- }
130
-
131
- /**
132
- * Get the first node, or throw
133
- */
134
- public first(): T {
135
- return this.atOrThrow(0);
136
- }
137
-
138
- /**
139
- * Get the last node, or throw
140
- */
141
- public last(): T {
142
- return this.atOrThrow(this.nodes.length - 1);
143
- }
144
-
145
- /**
146
- * Get the next node after the given node, does not wrap.
147
- */
148
- public after(id: T): T | undefined {
149
- const index = this.indexOfOrThrow(id);
150
- if (index === this.nodes.length - 1) {
151
- return undefined;
152
- }
153
- return this.at(index + 1);
154
- }
155
-
156
- /**
157
- * Get the previous node before the given node, does not wrap.
158
- */
159
- public before(id: T): T | undefined {
160
- const index = this.indexOfOrThrow(id);
161
- if (index === 0) {
162
- return undefined;
163
- }
164
- return this.at(index - 1);
165
- }
166
-
167
- /**
168
- * Insert a node at the given index
169
- */
170
- public insert(id: T, index: number): CollapsibleTree<T> {
171
- return this.withNodes(arrayInsert(this.nodes, index, new TreeNode(id)));
172
- }
173
-
174
- /**
175
- * Insert a node at the end
176
- */
177
- public insertAtEnd(id: T): CollapsibleTree<T> {
178
- return this.insert(id, this.nodes.length);
179
- }
180
-
181
- /**
182
- * Insert a node at the start
183
- */
184
- public insertAtStart(id: T): CollapsibleTree<T> {
185
- return this.insert(id, 0);
186
- }
187
-
188
- public deleteAtIndex(idx: number): CollapsibleTree<T> {
189
- this.atOrThrow(idx);
190
- return this.withNodes(arrayDelete(this.nodes, idx));
191
- }
192
-
193
- public delete(id: T): CollapsibleTree<T> {
194
- const index = this.indexOfOrThrow(id);
195
- return this.deleteAtIndex(index);
196
- }
197
-
198
- /**
199
- * Split the tree into two trees
200
- * @param id the id of the node to split at
201
- * @returns a tuple of the left and right trees
202
- */
203
- public split(id: T): [CollapsibleTree<T>, CollapsibleTree<T> | undefined] {
204
- const index = this.nodes.findIndex((n) => n.value === id);
205
- if (index === -1) {
206
- throw new Error(`Node ${id} not found in tree`);
207
- }
208
- const leftNodes = this.nodes.slice(0, index);
209
- const rightNodes = this.nodes.slice(index);
210
- if (leftNodes.length === 0) {
211
- return [CollapsibleTree.from<T>([]), this.withNodes(rightNodes)];
212
- }
213
- const left = this.withNodes(leftNodes);
214
- const right = CollapsibleTree.from<T>([]).withNodes(rightNodes);
215
- return [left, right];
216
- }
217
-
218
- public equals(other: CollapsibleTree<T>): boolean {
219
- return (
220
- this.nodes.length === other.nodes.length &&
221
- this.nodes.every((n, i) => n.value === other.nodes[i].value)
222
- );
223
- }
224
-
225
- public toString(): string {
226
- return this.nodes.map((node) => `${node.toString()}\n`).join("");
227
- }
228
- }
229
-
230
- export class MultiColumn<T> {
231
- private readonly columns: readonly CollapsibleTree<T>[];
232
-
233
- public constructor(columns: readonly CollapsibleTree<T>[]) {
234
- this.columns = columns;
235
-
236
- // Ensure there is always at least one column
237
- if (columns.length === 0) {
238
- this.columns = [CollapsibleTree.from([])];
239
- }
240
- }
241
-
242
- public static from<T>(idsList: T[][]): MultiColumn<T> {
243
- return new MultiColumn(idsList.map((ids) => CollapsibleTree.from(ids)));
244
- }
245
-
246
- /**
247
- * Create a new MultiColumn from idsList,
248
- * attempting to preserve structure from previous MultiColumn if possible.
249
- */
250
- public static fromWithPreviousShape<T>(
251
- idsList: T[],
252
- previousShape: MultiColumn<T>,
253
- ): MultiColumn<T> {
254
- if (!previousShape) {
255
- return MultiColumn.from([idsList]);
256
- }
257
-
258
- // Get previous columns for reference
259
- const previousColumns = previousShape.getColumns();
260
-
261
- // Split the ids into their respective columns
262
- const splitIds: T[][] = Array.from(
263
- { length: previousColumns.length },
264
- () => [],
265
- );
266
- let lastUsedColumnIdx = 0;
267
- for (const id of idsList) {
268
- const column = previousColumns.findIndex((c) => c.idSet.has(id));
269
- if (column === -1) {
270
- // No column found, use the last used column
271
- splitIds[lastUsedColumnIdx].push(id);
272
- } else {
273
- lastUsedColumnIdx = column;
274
- splitIds[column].push(id);
275
- }
276
- }
277
-
278
- const newColumns = splitIds.map((ids, idx) =>
279
- CollapsibleTree.from(ids, previousColumns[idx].id),
280
- );
281
-
282
- return new MultiColumn(newColumns);
283
- }
284
-
285
- @Memoize()
286
- public isEmpty(): boolean {
287
- if (this.columns.length === 0) {
288
- return true;
289
- }
290
- return this.columns.every((c) => c.nodes.length === 0);
291
- }
292
-
293
- public static fromIdsAndColumns<T>(
294
- idAndColumns: [T, number | undefined | null][],
295
- ): MultiColumn<T> {
296
- // If column is undefined, use the previous column
297
- // Ensure there is always at least one column
298
- const numColumns = Math.max(
299
- 1,
300
- ...idAndColumns.map(([, column]) => (column ?? 0) + 1),
301
- );
302
- const idsList: T[][] = Array.from({ length: numColumns }, () => []);
303
-
304
- let prevColumn = 0;
305
- for (const [id, column] of idAndColumns) {
306
- // Avoid negative column indices
307
- if (column === undefined || column === null || column < 0) {
308
- idsList[prevColumn].push(id);
309
- } else {
310
- idsList[column].push(id);
311
- prevColumn = column;
312
- }
313
- }
314
-
315
- return MultiColumn.from(idsList);
316
- }
317
-
318
- @Memoize()
319
- public get topLevelIds(): T[][] {
320
- return this.columns.map((c) => c.topLevelIds);
321
- }
322
-
323
- public get iterateTopLevelIds(): Iterable<T> {
324
- const columns = this.columns;
325
-
326
- function* iter() {
327
- for (const column of columns) {
328
- for (const id of column.topLevelIds) {
329
- yield id;
330
- }
331
- }
332
- }
333
-
334
- return iter();
335
- }
336
-
337
- @Memoize()
338
- public get inOrderIds(): T[] {
339
- return this.columns.flatMap((c) => c.inOrderIds);
340
- }
341
-
342
- public get colLength(): number {
343
- return this.columns.length;
344
- }
345
-
346
- @Memoize()
347
- public get idLength(): number {
348
- return this.columns.reduce((acc, c) => acc + c.nodes.length, 0);
349
- }
350
-
351
- @Memoize()
352
- public get _columnMap(): Map<CellColumnId, CollapsibleTree<T>> {
353
- return new Map(this.columns.map((c) => [c.id, c]));
354
- }
355
-
356
- public at(idx: number): CollapsibleTree<T> | undefined {
357
- return this.columns[idx];
358
- }
359
-
360
- public get(columnId: CellColumnId): CollapsibleTree<T> | undefined {
361
- return this._columnMap.get(columnId);
362
- }
363
-
364
- public atOrThrow(idx: number): CollapsibleTree<T> {
365
- const column = this.columns[idx];
366
- if (!column) {
367
- throw new Error(`Column ${idx} not found`);
368
- }
369
- return column;
370
- }
371
-
372
- public hasOnlyOneColumn(): boolean {
373
- return this.columns.length === 1;
374
- }
375
-
376
- public getColumns(): readonly CollapsibleTree<T>[] {
377
- return this.columns;
378
- }
379
-
380
- public hasOnlyOneId(): boolean {
381
- return this.idLength === 1;
382
- }
383
-
384
- @Memoize()
385
- public getColumnIds(): CellColumnId[] {
386
- return this.columns.map((c) => c.id);
387
- }
388
-
389
- public indexOf(column: CollapsibleTree<T>): number {
390
- return this.columns.indexOf(column);
391
- }
392
-
393
- public addColumn(
394
- columnId: CellColumnId,
395
- initialIds: T[] = [],
396
- ): MultiColumn<T> {
397
- return new MultiColumn(
398
- this.columns.flatMap((c) => {
399
- if (c.id === columnId) {
400
- return [c, CollapsibleTree.from(initialIds)];
401
- }
402
- return [c];
403
- }),
404
- );
405
- }
406
-
407
- public insertBreakpoint(cellId: T): MultiColumn<T> {
408
- const column = this.findWithId(cellId);
409
- const [left, right] = column.split(cellId);
410
- const newColumns = this.columns.flatMap((c) => {
411
- if (c === column) {
412
- return [left, right].filter(Boolean);
413
- }
414
- return [c];
415
- });
416
- return new MultiColumn(newColumns);
417
- }
418
-
419
- public delete(columnId: CellColumnId): MultiColumn<T> {
420
- // Move cells to preceding column
421
- // If its the first column, move the cells to the next column
422
-
423
- // Noop if there is only one column
424
- if (this.columns.length <= 1) {
425
- return this;
426
- }
427
-
428
- const columnIndex = this.indexOfOrThrow(columnId);
429
- const targetColumnIndex = columnIndex === 0 ? 1 : columnIndex - 1;
430
-
431
- const columns = [...this.columns];
432
- const column = columns[columnIndex];
433
- columns[targetColumnIndex] = column.withNodes([
434
- ...columns[targetColumnIndex].nodes,
435
- ...column.nodes,
436
- ]);
437
- columns.splice(columnIndex, 1);
438
- return new MultiColumn(columns);
439
- }
440
-
441
- public mergeAllColumns(): MultiColumn<T> {
442
- if (this.columns.length <= 1) {
443
- return this;
444
- }
445
-
446
- const nodes = this.columns.flatMap((c) => c.nodes);
447
- const firstColumn = this.columns[0];
448
- return new MultiColumn([firstColumn.withNodes(nodes)]);
449
- }
450
-
451
- public indexOfOrThrow(id: CellColumnId): number {
452
- const index = this.columns.findIndex((c) => c.id === id);
453
- if (index === -1) {
454
- throw new Error(
455
- `Column ${id} not found. Possible values: ${this.getColumnIds().join(", ")}`,
456
- );
457
- }
458
- return index;
459
- }
460
-
461
- public moveColumn(
462
- fromCol: CellColumnId,
463
- toCol: CellColumnId | "_left_" | "_right_",
464
- ): MultiColumn<T> {
465
- if (fromCol === toCol) {
466
- return this;
467
- }
468
- const fromIdx = this.indexOfOrThrow(fromCol);
469
- const toIdx =
470
- toCol === "_left_"
471
- ? fromIdx - 1
472
- : toCol === "_right_"
473
- ? fromIdx + 1
474
- : this.indexOfOrThrow(toCol);
475
-
476
- reorderColumnSizes(fromIdx, toIdx);
477
- return new MultiColumn(arrayMove([...this.columns], fromIdx, toIdx));
478
- }
479
-
480
- public findWithId(id: T): CollapsibleTree<T> {
481
- const found = this.columns.find((c) => {
482
- return c.inOrderIds.includes(id);
483
- });
484
- if (!found) {
485
- Logger.log(
486
- `Possible values: ${this.columns.map((c) => c.inOrderIds).join(", ")}`,
487
- );
488
- throw new Error(`Cell ${id} not found in any column`);
489
- }
490
- return found;
491
- }
492
-
493
- /**
494
- * Transform the column containing the given cell id
495
- * @param id the id of the cell to transform
496
- * @param fn the function to transform the column
497
- * @returns new MultiColumn with the transformed column
498
- * If the column was not updated, we return the object.
499
- */
500
- public transformWithCellId(
501
- id: T,
502
- fn: (tree: CollapsibleTree<T>) => CollapsibleTree<T>,
503
- ): MultiColumn<T> {
504
- let didChange = false;
505
-
506
- const columns = this.columns.map((c) => {
507
- if (c.inOrderIds.includes(id)) {
508
- const newColumn = fn(c);
509
- if (c !== newColumn) {
510
- didChange = true;
511
- }
512
- return newColumn;
513
- }
514
- return c;
515
- });
516
-
517
- // Avoid unnecessary re-renders
518
- if (!didChange) {
519
- return this;
520
- }
521
-
522
- return new MultiColumn(columns);
523
- }
524
-
525
- public setupCellExists(): boolean {
526
- return this.columns.some((c) => c.topLevelIds.includes(SETUP_CELL_ID as T));
527
- }
528
-
529
- public insertId(id: T, col: CellColumnId, index: CellIndex): MultiColumn<T> {
530
- return this.transform(col, (c) => c.insert(id, index));
531
- }
532
-
533
- public compact(): MultiColumn<T> {
534
- // Don't compact if there's only one column
535
- if (this.columns.length === 1) {
536
- return this;
537
- }
538
- // If no need to compact, return the same tree
539
- // to avoid unnecessary re-renders
540
- const someEmpty = this.columns.some((c) => c.nodes.length === 0);
541
- if (!someEmpty) {
542
- return this;
543
- }
544
-
545
- return new MultiColumn(this.columns.filter((c) => c.nodes.length > 0));
546
- }
547
-
548
- /**
549
- * Transform the column with the given column id
550
- */
551
- public transform(
552
- columnId: CellColumnId,
553
- fn: (tree: CollapsibleTree<T>) => CollapsibleTree<T>,
554
- ): MultiColumn<T> {
555
- // Use columnMap for faster lookup
556
- const column = this._columnMap.get(columnId);
557
- if (!column) {
558
- Logger.warn(`Column ${columnId} not found`);
559
- return this;
560
- }
561
-
562
- const newColumn = fn(column);
563
- if (column === newColumn) {
564
- return this;
565
- }
566
-
567
- const newColumns = this.columns.map((c) => {
568
- if (c.id === columnId) {
569
- return newColumn;
570
- }
571
- return c;
572
- });
573
-
574
- return new MultiColumn(newColumns);
575
- }
576
-
577
- /**
578
- * Apply a transformation function to all columns
579
- * @param fn The function to transform each column
580
- * @returns A new MultiColumn if any changes were made, otherwise this
581
- */
582
- public transformAll(
583
- fn: (tree: CollapsibleTree<T>) => CollapsibleTree<T>,
584
- ): MultiColumn<T> {
585
- let didChange = false;
586
-
587
- // Apply the transformation to all columns
588
- const newColumns = this.columns.map((column) => {
589
- const newColumn = fn(column);
590
- if (!column.equals(newColumn)) {
591
- didChange = true;
592
- }
593
- return newColumn;
594
- });
595
-
596
- // Avoid unnecessary re-renders if nothing changed
597
- if (!didChange) {
598
- return this;
599
- }
600
-
601
- return new MultiColumn(newColumns);
602
- }
603
- }
604
-
605
- export const visibleForTesting = {
606
- reset: () => {
607
- uniqueId = 0;
608
- },
609
- };