@marimo-team/frontend 0.15.1-dev34 → 0.15.1-dev36

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 (115) hide show
  1. package/dist/assets/{ConnectedDataExplorerComponent-BcJwEN2q.js → ConnectedDataExplorerComponent-D8km1NYJ.js} +1 -1
  2. package/dist/assets/{ImageComparisonComponent-CnwGwcTi.js → ImageComparisonComponent-CERR01UB.js} +1 -1
  3. package/dist/assets/{VegaLite-Cnk7Ozer.js → VegaLite-DzZIYwlo.js} +1 -1
  4. package/dist/assets/{_baseEach-DJPM6ZzY.js → _baseEach-DO-XcSIV.js} +1 -1
  5. package/dist/assets/_baseMap-Dg1mkEJY.js +1 -0
  6. package/dist/assets/{_baseUniq-CrXjB6JP.js → _baseUniq-urG2vBXB.js} +1 -1
  7. package/dist/assets/{_createAggregator-Cd0noqMc.js → _createAggregator-DBxX1xI5.js} +1 -1
  8. package/dist/assets/{any-language-editor-ZfVYrHPH.js → any-language-editor-BhBzti0Q.js} +1 -1
  9. package/dist/assets/{architectureDiagram-KFL7JDKH-DuJCaD_M.js → architectureDiagram-KFL7JDKH-mgZE9P9E.js} +1 -1
  10. package/dist/assets/{blockDiagram-ZYB65J3Q-B0f3bVJ0.js → blockDiagram-ZYB65J3Q-DhlvqBBN.js} +1 -1
  11. package/dist/assets/{c4Diagram-AAMF2YG6-3srX8_ui.js → c4Diagram-AAMF2YG6-WgF7TmcX.js} +1 -1
  12. package/dist/assets/channel-D1aHTD78.js +1 -0
  13. package/dist/assets/{chunk-ANTBXLJU-CD8Ya77t.js → chunk-ANTBXLJU-Cc0FLBv4.js} +1 -1
  14. package/dist/assets/{chunk-FHKO5MBM-34L0hhh_.js → chunk-FHKO5MBM-TDVJkINs.js} +1 -1
  15. package/dist/assets/{chunk-GLLZNHP4-CRWkmhQj.js → chunk-GLLZNHP4-BFZQsgmM.js} +1 -1
  16. package/dist/assets/{chunk-JBRWN2VN-CB8a_SQv.js → chunk-JBRWN2VN-jewL7EcA.js} +1 -1
  17. package/dist/assets/{chunk-LXBSTHXV-YNRk01ob.js → chunk-LXBSTHXV-DtMvensd.js} +1 -1
  18. package/dist/assets/{chunk-NRVI72HA-nFtyi9pH.js → chunk-NRVI72HA-B7LxIi5N.js} +1 -1
  19. package/dist/assets/{chunk-OMD6QJNC-BXZ4Kapq.js → chunk-OMD6QJNC-D77lwHsv.js} +1 -1
  20. package/dist/assets/{chunk-WVR4S24B-ZnYlHppS.js → chunk-WVR4S24B-BdzaJi_f.js} +1 -1
  21. package/dist/assets/{circle-play-k88jloBa.js → circle-play-DbLJhgWd.js} +1 -1
  22. package/dist/assets/classDiagram-3BZAVTQC-C8Rjm-tQ.js +1 -0
  23. package/dist/assets/classDiagram-v2-QTMF73CY-C8Rjm-tQ.js +1 -0
  24. package/dist/assets/clone-BWlvmMhq.js +1 -0
  25. package/dist/assets/{compile-CDMtSySK.js → compile-ittiAa-p.js} +1 -1
  26. package/dist/assets/{dagre-2BBEFEWP-Bfm20vyd.js → dagre-2BBEFEWP-woDg8Awj.js} +1 -1
  27. package/dist/assets/{data-grid-overlay-editor-CwEfDFcK.js → data-grid-overlay-editor-ncLMoVIb.js} +1 -1
  28. package/dist/assets/{diagram-4IRLE6MV-A9psNOWP.js → diagram-4IRLE6MV-PAN7AATi.js} +1 -1
  29. package/dist/assets/{diagram-GUPCWM2R-B23WLgai.js → diagram-GUPCWM2R-Ce2AHqMP.js} +1 -1
  30. package/dist/assets/{diagram-RP2FKANI-CU5qL55k.js → diagram-RP2FKANI-2WiKea8D.js} +1 -1
  31. package/dist/assets/{edit-page-DYm-s1ff.js → edit-page-CZn2bhY3.js} +42 -42
  32. package/dist/assets/{erDiagram-HZWUO2LU-D31RvIVf.js → erDiagram-HZWUO2LU-D5yTZwmt.js} +1 -1
  33. package/dist/assets/{flowDiagram-THRYKUMA-ClYGAL8Y.js → flowDiagram-THRYKUMA-CgN86sZ8.js} +1 -1
  34. package/dist/assets/{ganttDiagram-WV7ZQ7D5-Cmed9KL9.js → ganttDiagram-WV7ZQ7D5-5skQ2H3l.js} +1 -1
  35. package/dist/assets/{gitGraphDiagram-OJR772UL-CWvdnGVc.js → gitGraphDiagram-OJR772UL-C75De8fN.js} +1 -1
  36. package/dist/assets/{glide-data-editor-CFVz2aZ5.js → glide-data-editor-x0d0Up5U.js} +4 -4
  37. package/dist/assets/{graph-OxTx0_rA.js → graph-Die-6Bfl.js} +1 -1
  38. package/dist/assets/{home-page-Dt3nnRSz.js → home-page-XrEKfDMd.js} +1 -1
  39. package/dist/assets/{index-C0Q8e5FM.js → index-B1-fG89N.js} +1 -1
  40. package/dist/assets/{index-BLXqwIOX.js → index-B3-zxsj0.js} +1 -1
  41. package/dist/assets/{index-DjT-1R5N.js → index-B7JwzNrx.js} +1 -1
  42. package/dist/assets/{index-quG0To4_.js → index-BSJet7Zq.js} +1 -1
  43. package/dist/assets/{index-Bu3XvTHk.js → index-BWBg8Lhk.js} +1 -1
  44. package/dist/assets/{index-DLO62gfd.js → index-BfqMljbw.js} +1 -1
  45. package/dist/assets/{index-DHlxJ27b.js → index-CCZkMG5j.js} +1 -1
  46. package/dist/assets/{index-CQFSXxPp.js → index-CXwX4pUI.js} +133 -133
  47. package/dist/assets/{index-DETsDEp1.js → index-CoAopzxc.js} +1 -1
  48. package/dist/assets/{index-BOFl7de8.js → index-D9ot8WPl.js} +1 -1
  49. package/dist/assets/{index-WltWzc1Z.js → index-D_ebOE-e.js} +1 -1
  50. package/dist/assets/{index-Bd2C9zUZ.js → index-De7juNGF.js} +1 -1
  51. package/dist/assets/{index-KCIhgRrL.js → index-Dh8C39yO.js} +1 -1
  52. package/dist/assets/{index-BUwTI3go.js → index-Dlrab0dK.js} +1 -1
  53. package/dist/assets/{index-4GKIL9qL.js → index-DmHX1Rlv.js} +1 -1
  54. package/dist/assets/{index-Baw8vWOg.js → index-F531CohR.js} +1 -1
  55. package/dist/assets/{index-BM1NAxco.js → index-UAPuSZUk.js} +1 -1
  56. package/dist/assets/{index-DsWckHTg.js → index-jwhWh6uS.js} +1 -1
  57. package/dist/assets/{index-B-P7QUhF.js → index-kraNHKP1.js} +1 -1
  58. package/dist/assets/{index-D4HfKv3U.js → index-v546YXgW.js} +1 -1
  59. package/dist/assets/{infoDiagram-6WOFNB3A-BQOY1VMN.js → infoDiagram-6WOFNB3A-mJ455ogl.js} +1 -1
  60. package/dist/assets/{journeyDiagram-FFXJYRFH-BLEcUGJo.js → journeyDiagram-FFXJYRFH-CrWxkiXG.js} +1 -1
  61. package/dist/assets/{kanban-definition-KOZQBZVT-RanT6u8e.js → kanban-definition-KOZQBZVT-D5haTqpz.js} +1 -1
  62. package/dist/assets/{layout-D4n_CEl8.js → layout-BEc7rmra.js} +1 -1
  63. package/dist/assets/{linear-BbPKk0w3.js → linear-BLmy5c_P.js} +1 -1
  64. package/dist/assets/{links-B7_BzQLv.js → links-BGVSWGyv.js} +1 -1
  65. package/dist/assets/{mermaid-CUkYs7pX.js → mermaid-Bu2fp8N3.js} +4 -4
  66. package/dist/assets/{min-Yuyj_o4k.js → min-BF5a70Ha.js} +1 -1
  67. package/dist/assets/{mindmap-definition-LNHGMQRG-x4MjCJ_j.js → mindmap-definition-LNHGMQRG-jD2ylGBG.js} +1 -1
  68. package/dist/assets/{number-overlay-editor-DeKR_mfB.js → number-overlay-editor-BadGHMrr.js} +1 -1
  69. package/dist/assets/{pieDiagram-DBDJKBY4-ZOMhJp-a.js → pieDiagram-DBDJKBY4-B11UeqxS.js} +1 -1
  70. package/dist/assets/{quadrantDiagram-YPSRARAO-vZeyiYxN.js → quadrantDiagram-YPSRARAO-DznrQXs-.js} +1 -1
  71. package/dist/assets/{react-plotly-BV6-1ruP.js → react-plotly-B6W1vV_l.js} +1 -1
  72. package/dist/assets/{requirementDiagram-EGVEC5DT-DkgbH0oi.js → requirementDiagram-EGVEC5DT-BuWp2ov9.js} +1 -1
  73. package/dist/assets/{run-page-0slxHZwl.js → run-page-BD4YehOu.js} +1 -1
  74. package/dist/assets/{sankeyDiagram-HRAUVNP4-BuFPw2i8.js → sankeyDiagram-HRAUVNP4-BmDCzHkG.js} +1 -1
  75. package/dist/assets/{sequenceDiagram-WFGC7UMF-BKGIaix6.js → sequenceDiagram-WFGC7UMF-B6aCHwAd.js} +1 -1
  76. package/dist/assets/{slides-component-DYhLRND3.js → slides-component-CkHAJhvv.js} +1 -1
  77. package/dist/assets/{sortBy-C_bWmRoh.js → sortBy-BgSF6Z3p.js} +1 -1
  78. package/dist/assets/{stateDiagram-UUKSUZ4H-Cm2zkw0M.js → stateDiagram-UUKSUZ4H-DcGKoXUB.js} +1 -1
  79. package/dist/assets/stateDiagram-v2-EYPG3UTE-BW5Lhcrb.js +1 -0
  80. package/dist/assets/{storage-Dk-ciwQU.js → storage-BX5GfYeC.js} +3 -3
  81. package/dist/assets/{terminal-DrDWxiEU.js → terminal-CpmTRzDD.js} +1 -1
  82. package/dist/assets/{time-DW3BCgky.js → time-igUPFhda.js} +1 -1
  83. package/dist/assets/{timeline-definition-3HZDQTIS-B_ZdpuLp.js → timeline-definition-3HZDQTIS-7b4K28uW.js} +1 -1
  84. package/dist/assets/{tracing-BuGDnWcg.js → tracing-BWeRyJmf.js} +2 -2
  85. package/dist/assets/{trash-BKmRbRfw.js → trash-Bv9cD3VE.js} +1 -1
  86. package/dist/assets/{treemap-75Q7IDZK-BBbbCdps.js → treemap-75Q7IDZK-DPkOPTcz.js} +1 -1
  87. package/dist/assets/{vega-component-DogOWa6S.js → vega-component-CGVDFtwG.js} +1 -1
  88. package/dist/assets/{xychartDiagram-FDP5SA34-3B1j_9pq.js → xychartDiagram-FDP5SA34-BbvKlNzf.js} +1 -1
  89. package/dist/index.html +1 -1
  90. package/package.json +1 -1
  91. package/src/__mocks__/notebook.ts +108 -0
  92. package/src/components/editor/Cell.tsx +16 -17
  93. package/src/components/editor/ai/__tests__/completion-utils.test.ts +119 -139
  94. package/src/components/editor/ai/completion-utils.ts +7 -33
  95. package/src/components/editor/navigation/navigation.ts +7 -7
  96. package/src/components/editor/navigation/state.ts +34 -2
  97. package/src/core/ai/context/__tests__/registry.test.ts +154 -0
  98. package/src/core/ai/context/__tests__/utils.test.ts +174 -0
  99. package/src/core/ai/context/context.ts +3 -1
  100. package/src/core/ai/context/providers/__tests__/__snapshots__/error.test.ts.snap +3 -0
  101. package/src/core/ai/context/providers/__tests__/__snapshots__/tables.test.ts.snap +8 -12
  102. package/src/core/ai/context/providers/__tests__/__snapshots__/variable.test.ts.snap +4 -20
  103. package/src/core/ai/context/providers/__tests__/error.test.ts +328 -0
  104. package/src/core/ai/context/providers/common.ts +1 -0
  105. package/src/core/ai/context/providers/error.ts +165 -0
  106. package/src/core/ai/context/providers/tables.ts +14 -6
  107. package/src/core/ai/context/providers/variable.ts +11 -2
  108. package/src/core/ai/context/utils.ts +49 -0
  109. package/src/core/cells/cells.ts +1 -1
  110. package/dist/assets/_baseMap-u5TY8JMW.js +0 -1
  111. package/dist/assets/channel-BIZI32eZ.js +0 -1
  112. package/dist/assets/classDiagram-3BZAVTQC-DfPpF-gd.js +0 -1
  113. package/dist/assets/classDiagram-v2-QTMF73CY-DfPpF-gd.js +0 -1
  114. package/dist/assets/clone-yHYNh3QG.js +0 -1
  115. package/dist/assets/stateDiagram-v2-EYPG3UTE-DX5NRIHK.js +0 -1
@@ -2,8 +2,7 @@
2
2
  import { closeCompletion, completionStatus } from "@codemirror/autocomplete";
3
3
  import type { EditorView } from "@codemirror/view";
4
4
  import clsx from "clsx";
5
- import { useAtom, useAtomValue, useSetAtom } from "jotai";
6
- import { ScopeProvider } from "jotai-scope";
5
+ import { useAtomValue, useSetAtom } from "jotai";
7
6
  import {
8
7
  HelpCircleIcon,
9
8
  MoreHorizontalIcon,
@@ -78,7 +77,10 @@ import { useRunCell } from "./cell/useRunCells";
78
77
  import { HideCodeButton } from "./code/readonly-python-code";
79
78
  import { cellDomProps } from "./common";
80
79
  import { useCellNavigationProps } from "./navigation/navigation";
81
- import { temporarilyShownCodeAtom } from "./navigation/state";
80
+ import {
81
+ useTemporarilyShownCode,
82
+ useTemporarilyShownCodeActions,
83
+ } from "./navigation/state";
82
84
  import { OutputArea } from "./Output";
83
85
  import { ConsoleOutput } from "./output/ConsoleOutput";
84
86
  import { CellDragHandle, SortableCell } from "./SortableCell";
@@ -157,9 +159,8 @@ function useCellHiddenLogic({
157
159
  editorView: React.RefObject<EditorView | null>;
158
160
  editorViewParentRef: React.RefObject<HTMLDivElement | null>;
159
161
  }) {
160
- const [temporarilyVisible, setTemporarilyVisible] = useAtom(
161
- temporarilyShownCodeAtom,
162
- );
162
+ const temporarilyVisible = useTemporarilyShownCode(cellId);
163
+ const temporarilyShownCodeActions = useTemporarilyShownCodeActions();
163
164
  const isUntouched = useAtomValue(
164
165
  useMemo(() => createUntouchedCellAtom(cellId), [cellId]),
165
166
  );
@@ -179,7 +180,7 @@ function useCellHiddenLogic({
179
180
 
180
181
  // Default to true
181
182
  const focus = opts?.focus ?? true;
182
- setTemporarilyVisible(true);
183
+ temporarilyShownCodeActions.add(cellId);
183
184
 
184
185
  if (focus) {
185
186
  editorView.current?.focus();
@@ -297,16 +298,14 @@ const CellComponent = (props: CellProps) => {
297
298
 
298
299
  if (mode === "edit") {
299
300
  return (
300
- <ScopeProvider atoms={[temporarilyShownCodeAtom]}>
301
- <EditableCellComponent
302
- {...props}
303
- cellId={cellId}
304
- editorView={editorView}
305
- setEditorView={(ev) => {
306
- editorView.current = ev;
307
- }}
308
- />
309
- </ScopeProvider>
301
+ <EditableCellComponent
302
+ {...props}
303
+ cellId={cellId}
304
+ editorView={editorView}
305
+ setEditorView={(ev) => {
306
+ editorView.current = ev;
307
+ }}
308
+ />
310
309
  );
311
310
  }
312
311
 
@@ -52,41 +52,40 @@ describe("getAICompletionBody", () => {
52
52
  const input = "Use @data://dataset1 and @data://dataset2 for analysis";
53
53
  const result = getAICompletionBody({ input });
54
54
 
55
- expect(result).toEqual({
56
- includeOtherCode: "// Some other code",
57
- context: {
58
- schema: [
59
- {
60
- name: "dataset1",
61
- columns: [
62
- { name: "col1", type: "number" },
63
- { name: "col2", type: "string" },
64
- ],
65
- },
66
- {
67
- name: "dataset2",
68
- columns: [
69
- { name: "col3", type: "boolean" },
70
- { name: "col4", type: "date" },
71
- ],
72
- },
73
- ],
74
- variables: [],
75
- },
76
- });
55
+ expect(result).toMatchInlineSnapshot(`
56
+ {
57
+ "context": {
58
+ "plainText": "<data name="dataset1" source="unknown">
59
+ Columns:
60
+ - col1: number
61
+ - col2: string</data>
62
+
63
+ <data name="dataset2" source="unknown">
64
+ Columns:
65
+ - col3: boolean
66
+ - col4: date</data>",
67
+ "schema": [],
68
+ "variables": [],
69
+ },
70
+ "includeOtherCode": "// Some other code",
71
+ }
72
+ `);
77
73
  });
78
74
 
79
75
  it("should handle input with no mentioned datasets", () => {
80
76
  const input = "Perform some analysis without mentioning @data://datasets";
81
77
  const result = getAICompletionBody({ input });
82
78
 
83
- expect(result).toEqual({
84
- includeOtherCode: "// Some other code",
85
- context: {
86
- schema: [],
87
- variables: [],
88
- },
89
- });
79
+ expect(result).toMatchInlineSnapshot(`
80
+ {
81
+ "context": {
82
+ "plainText": "",
83
+ "schema": [],
84
+ "variables": [],
85
+ },
86
+ "includeOtherCode": "// Some other code",
87
+ }
88
+ `);
90
89
  });
91
90
 
92
91
  it("should handle input with non-existent datasets", () => {
@@ -106,21 +105,19 @@ describe("getAICompletionBody", () => {
106
105
  "Use @data://existingDataset and @data://nonExistentDataset for analysis";
107
106
  const result = getAICompletionBody({ input });
108
107
 
109
- expect(result).toEqual({
110
- includeOtherCode: "// Some other code",
111
- context: {
112
- schema: [
113
- {
114
- name: "existingDataset",
115
- columns: [
116
- { name: "col1", type: "number" },
117
- { name: "col2", type: "string" },
118
- ],
119
- },
120
- ],
121
- variables: [],
122
- },
123
- });
108
+ expect(result).toMatchInlineSnapshot(`
109
+ {
110
+ "context": {
111
+ "plainText": "<data name="existingDataset" source="unknown">
112
+ Columns:
113
+ - col1: number
114
+ - col2: string</data>",
115
+ "schema": [],
116
+ "variables": [],
117
+ },
118
+ "includeOtherCode": "// Some other code",
119
+ }
120
+ `);
124
121
  });
125
122
 
126
123
  it("should handle dataset names with dots", () => {
@@ -144,25 +141,23 @@ describe("getAICompletionBody", () => {
144
141
  "Use @data://dataset.with.dots and @data://regular_dataset for analysis";
145
142
  const result = getAICompletionBody({ input });
146
143
 
147
- expect(result).toEqual({
148
- includeOtherCode: "// Some other code",
149
- context: {
150
- schema: [
151
- {
152
- name: "dataset.with.dots",
153
- columns: [
154
- { name: "col1", type: "number" },
155
- { name: "col2", type: "string" },
156
- ],
157
- },
158
- {
159
- name: "regular_dataset",
160
- columns: [{ name: "col3", type: "boolean" }],
161
- },
162
- ],
163
- variables: [],
164
- },
165
- });
144
+ expect(result).toMatchInlineSnapshot(`
145
+ {
146
+ "context": {
147
+ "plainText": "<data name="dataset.with.dots" source="unknown">
148
+ Columns:
149
+ - col1: number
150
+ - col2: string</data>
151
+
152
+ <data name="regular_dataset" source="unknown">
153
+ Columns:
154
+ - col3: boolean</data>",
155
+ "schema": [],
156
+ "variables": [],
157
+ },
158
+ "includeOtherCode": "// Some other code",
159
+ }
160
+ `);
166
161
  });
167
162
 
168
163
  it("should handle connections", () => {
@@ -200,18 +195,18 @@ describe("getAICompletionBody", () => {
200
195
  const input = "Use @data://table1 for analysis";
201
196
  const result = getAICompletionBody({ input });
202
197
 
203
- expect(result).toEqual({
204
- includeOtherCode: "// Some other code",
205
- context: {
206
- schema: [
207
- {
208
- name: "table1",
209
- columns: [{ name: "col1", type: "number" }],
210
- },
211
- ],
212
- variables: [],
213
- },
214
- });
198
+ expect(result).toMatchInlineSnapshot(`
199
+ {
200
+ "context": {
201
+ "plainText": "<data name="table1" source="unknown">
202
+ Columns:
203
+ - col1: number</data>",
204
+ "schema": [],
205
+ "variables": [],
206
+ },
207
+ "includeOtherCode": "// Some other code",
208
+ }
209
+ `);
215
210
  });
216
211
 
217
212
  it("should return the correct completion body with mentioned variables", () => {
@@ -237,24 +232,18 @@ describe("getAICompletionBody", () => {
237
232
  const input = "Use @variable://var1 and @variable://var2 for analysis";
238
233
  const result = getAICompletionBody({ input });
239
234
 
240
- expect(result).toEqual({
241
- includeOtherCode: "// Some other code",
242
- context: {
243
- schema: [],
244
- variables: [
245
- {
246
- name: "var1",
247
- valueType: "string",
248
- previewValue: "string value",
249
- },
250
- {
251
- name: "var2",
252
- valueType: "number",
253
- previewValue: "42",
254
- },
255
- ],
256
- },
257
- });
235
+ expect(result).toMatchInlineSnapshot(`
236
+ {
237
+ "context": {
238
+ "plainText": "<variable name="var1" dataType="string">"string value"</variable>
239
+
240
+ <variable name="var2" dataType="number">"42"</variable>",
241
+ "schema": [],
242
+ "variables": [],
243
+ },
244
+ "includeOtherCode": "// Some other code",
245
+ }
246
+ `);
258
247
  });
259
248
 
260
249
  it("should handle input with both datasets and variables", () => {
@@ -284,27 +273,21 @@ describe("getAICompletionBody", () => {
284
273
  const input = "Use @data://dataset1 and @variable://var1 for analysis";
285
274
  const result = getAICompletionBody({ input });
286
275
 
287
- expect(result).toEqual({
288
- includeOtherCode: "// Some other code",
289
- context: {
290
- schema: [
291
- {
292
- name: "dataset1",
293
- columns: [
294
- { name: "col1", type: "number" },
295
- { name: "col2", type: "string" },
296
- ],
297
- },
298
- ],
299
- variables: [
300
- {
301
- name: "var1",
302
- valueType: "string",
303
- previewValue: "string value",
304
- },
305
- ],
306
- },
307
- });
276
+ expect(result).toMatchInlineSnapshot(`
277
+ {
278
+ "context": {
279
+ "plainText": "<data name="dataset1" source="unknown">
280
+ Columns:
281
+ - col1: number
282
+ - col2: string</data>
283
+
284
+ <variable name="var1" dataType="string">"string value"</variable>",
285
+ "schema": [],
286
+ "variables": [],
287
+ },
288
+ "includeOtherCode": "// Some other code",
289
+ }
290
+ `);
308
291
  });
309
292
 
310
293
  it("should handle non-existent variables", () => {
@@ -324,19 +307,16 @@ describe("getAICompletionBody", () => {
324
307
  "Use @variable://existingVar and @variable://nonExistentVar for analysis";
325
308
  const result = getAICompletionBody({ input });
326
309
 
327
- expect(result).toEqual({
328
- includeOtherCode: "// Some other code",
329
- context: {
330
- schema: [],
331
- variables: [
332
- {
333
- name: "existingVar",
334
- valueType: "string",
335
- previewValue: "string value",
336
- },
337
- ],
338
- },
339
- });
310
+ expect(result).toMatchInlineSnapshot(`
311
+ {
312
+ "context": {
313
+ "plainText": "<variable name="existingVar" dataType="string">"string value"</variable>",
314
+ "schema": [],
315
+ "variables": [],
316
+ },
317
+ "includeOtherCode": "// Some other code",
318
+ }
319
+ `);
340
320
  });
341
321
 
342
322
  it("should prioritize datasets over variables when there's a name conflict", () => {
@@ -363,17 +343,17 @@ describe("getAICompletionBody", () => {
363
343
  const input = "Use @data://conflict for analysis";
364
344
  const result = getAICompletionBody({ input });
365
345
 
366
- expect(result).toEqual({
367
- includeOtherCode: "// Some other code",
368
- context: {
369
- schema: [
370
- {
371
- name: "conflict",
372
- columns: [{ name: "col1", type: "number" }],
373
- },
374
- ],
375
- variables: [],
376
- },
377
- });
346
+ expect(result).toMatchInlineSnapshot(`
347
+ {
348
+ "context": {
349
+ "plainText": "<data name="conflict" source="unknown">
350
+ Columns:
351
+ - col1: number</data>",
352
+ "schema": [],
353
+ "variables": [],
354
+ },
355
+ "includeOtherCode": "// Some other code",
356
+ }
357
+ `);
378
358
  });
379
359
  });
@@ -6,13 +6,9 @@ import type {
6
6
  CompletionSource,
7
7
  } from "@codemirror/autocomplete";
8
8
  import { getAIContextRegistry } from "@/core/ai/context/context";
9
- import type { TableContextItem } from "@/core/ai/context/providers/tables";
10
- import type { VariableContextItem } from "@/core/ai/context/providers/variable";
11
9
  import { getCodes } from "@/core/codemirror/copilot/getCodes";
12
- import type { DataTable } from "@/core/kernel/messages";
13
10
  import type { AiCompletionRequest } from "@/core/network/types";
14
11
  import { store } from "@/core/state/jotai";
15
- import type { Variable } from "@/core/variables/types";
16
12
  import { Logger } from "@/utils/Logger";
17
13
 
18
14
  /**
@@ -23,26 +19,15 @@ export function getAICompletionBody({
23
19
  }: {
24
20
  input: string;
25
21
  }): Omit<AiCompletionRequest, "language" | "prompt" | "code"> {
26
- const { datasets, variables } = extractDatasetsAndVariables(input);
27
- Logger.debug("Included datasets", datasets);
28
- Logger.debug("Included variables", variables);
22
+ const contextString = extractDatasetsAndVariables(input);
23
+ Logger.debug("Included context", contextString);
29
24
 
30
25
  return {
31
26
  includeOtherCode: getCodes(""),
32
27
  context: {
33
- schema: datasets.map((dataset) => ({
34
- name: dataset.name,
35
- columns: dataset.columns.map((column) => ({
36
- name: column.name,
37
- type: column.type,
38
- sampleValues: column.sample_values,
39
- })),
40
- })),
41
- variables: variables.map((variable) => ({
42
- name: variable.name,
43
- valueType: variable.dataType ?? "",
44
- previewValue: variable.value,
45
- })),
28
+ plainText: contextString,
29
+ schema: [],
30
+ variables: [],
46
31
  },
47
32
  };
48
33
  }
@@ -52,21 +37,10 @@ export function getAICompletionBody({
52
37
  * References are with @<name> in the input.
53
38
  * Prioritizes datasets over variables if there's a name conflict.
54
39
  */
55
- function extractDatasetsAndVariables(input: string): {
56
- datasets: DataTable[];
57
- variables: Variable[];
58
- } {
40
+ function extractDatasetsAndVariables(input: string): string {
59
41
  const registry = getAIContextRegistry(store);
60
42
  const contextIds = registry.parseAllContextIds(input);
61
- const contextInfo = registry.getContextInfo(contextIds);
62
- const datasets: DataTable[] = contextInfo
63
- .filter((info): info is TableContextItem => info.type === "data")
64
- .map((info) => info.data);
65
- const variables: Variable[] = contextInfo
66
- .filter((info): info is VariableContextItem => info.type === "variable")
67
- .map((info) => info.data.variable);
68
-
69
- return { datasets, variables };
43
+ return registry.formatContextForAI(contextIds);
70
44
  }
71
45
 
72
46
  /**
@@ -33,7 +33,7 @@ import {
33
33
  useCellSelectionActions,
34
34
  useIsCellSelected,
35
35
  } from "./selection";
36
- import { temporarilyShownCodeAtom } from "./state";
36
+ import { useTemporarilyShownCodeActions } from "./state";
37
37
  import { handleVimKeybinding } from "./vim-bindings";
38
38
 
39
39
  interface HotkeyHandler {
@@ -85,7 +85,7 @@ function addSingleHandler(handler: HotkeyHandler["bulkHandle"]): HotkeyHandler {
85
85
  function useCellFocusProps(cellId: CellId) {
86
86
  const focusActions = useCellFocusActions();
87
87
  const actions = useCellActions();
88
- const setTemporarilyShownCode = useSetAtom(temporarilyShownCodeAtom);
88
+ const temporarilyShownCodeActions = useTemporarilyShownCodeActions();
89
89
 
90
90
  // This occurs at the cell level and descedants.
91
91
  const { focusWithinProps } = useFocusWithin({
@@ -95,7 +95,7 @@ function useCellFocusProps(cellId: CellId) {
95
95
  },
96
96
  onBlurWithin: () => {
97
97
  // On blur, hide the code if it was temporarily shown.
98
- setTemporarilyShownCode(false);
98
+ temporarilyShownCodeActions.remove(cellId);
99
99
  actions.markTouched({ cellId });
100
100
  focusActions.blurCell();
101
101
  },
@@ -132,7 +132,7 @@ export function useCellNavigationProps(
132
132
  const setAiCompletionCell = useSetAtom(aiCompletionCellAtom);
133
133
  const actions = useCellActions();
134
134
  const store = useStore();
135
- const setTemporarilyShownCode = useSetAtom(temporarilyShownCodeAtom);
135
+ const temporarilyShownCodeActions = useTemporarilyShownCodeActions();
136
136
  const runCells = useRunCells();
137
137
  const keymapPreset = useAtomValue(keymapPresetAtom);
138
138
  const { copyCells, pasteAtCell } = useCellClipboard();
@@ -275,7 +275,7 @@ export function useCellNavigationProps(
275
275
  },
276
276
  // Enter will focus the cell editor.
277
277
  Enter: () => {
278
- setTemporarilyShownCode(true);
278
+ temporarilyShownCodeActions.add(cellId);
279
279
  focusCellEditor(store, cellId);
280
280
  selectionActions.clear();
281
281
  return true;
@@ -608,7 +608,7 @@ export function useCellEditorNavigationProps(
608
608
  cellId: CellId,
609
609
  editorView: React.RefObject<EditorView | null>,
610
610
  ) {
611
- const setTemporarilyShownCode = useSetAtom(temporarilyShownCodeAtom);
611
+ const temporarilyShownCodeActions = useTemporarilyShownCodeActions();
612
612
  const keymapPreset = useAtomValue(keymapPresetAtom);
613
613
  const hotkeys = useAtomValue(hotkeysAtom);
614
614
 
@@ -618,7 +618,7 @@ export function useCellEditorNavigationProps(
618
618
  }, [hotkeys]);
619
619
 
620
620
  const exitToCommandMode = () => {
621
- setTemporarilyShownCode(false);
621
+ temporarilyShownCodeActions.remove(cellId);
622
622
  focusCell(cellId);
623
623
  };
624
624
 
@@ -1,5 +1,37 @@
1
1
  /* Copyright 2024 Marimo. All rights reserved. */
2
2
 
3
- import { atom } from "jotai";
3
+ import { atom, useAtomValue } from "jotai";
4
+ import { useMemo } from "react";
5
+ import type { CellId } from "@/core/cells/ids";
6
+ import { createReducerAndAtoms } from "@/utils/createReducer";
4
7
 
5
- export const temporarilyShownCodeAtom = atom<boolean>(false);
8
+ type TemporarilyShownCodeState = Set<CellId>;
9
+
10
+ // This previously used jotai-scope, but unfortunately this bug creates unnecessary work:
11
+ // https://github.com/jotaijs/jotai-scope/issues/25
12
+
13
+ const {
14
+ valueAtom: temporarilyShownCodeAtom,
15
+ useActions: useTemporarilyShownCodeActions,
16
+ } = createReducerAndAtoms(() => new Set<CellId>(), {
17
+ add: (state: TemporarilyShownCodeState, cellId: CellId) => {
18
+ const newState = new Set(state);
19
+ newState.add(cellId);
20
+ return newState;
21
+ },
22
+ remove: (state: TemporarilyShownCodeState, cellId: CellId) => {
23
+ const newState = new Set(state);
24
+ newState.delete(cellId);
25
+ return newState;
26
+ },
27
+ });
28
+
29
+ const createTemporarilyShownCodeAtom = (cellId: CellId) =>
30
+ atom((get) => get(temporarilyShownCodeAtom).has(cellId));
31
+
32
+ export function useTemporarilyShownCode(cellId: CellId) {
33
+ const atom = useMemo(() => createTemporarilyShownCodeAtom(cellId), [cellId]);
34
+ return useAtomValue(atom);
35
+ }
36
+
37
+ export { temporarilyShownCodeAtom, useTemporarilyShownCodeActions };