@malloy-publisher/sdk 0.0.80 → 0.0.82

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 (53) hide show
  1. package/dist/components/Model/Model.d.ts +3 -1
  2. package/dist/components/Model/ModelExplorer.d.ts +19 -0
  3. package/dist/components/Model/index.d.ts +3 -0
  4. package/dist/components/Model/useModelData.d.ts +8 -0
  5. package/dist/components/Package/index.d.ts +1 -0
  6. package/dist/components/Project/index.d.ts +1 -0
  7. package/dist/components/Workbook/BrowserWorkbookStorage.d.ts +11 -0
  8. package/dist/components/{MutableNotebook → Workbook}/EditableMalloyCell.d.ts +2 -2
  9. package/dist/components/{MutableNotebook → Workbook}/MutableCell.d.ts +3 -3
  10. package/dist/components/Workbook/Workbook.d.ts +9 -0
  11. package/dist/components/Workbook/WorkbookList.d.ts +7 -0
  12. package/dist/components/Workbook/WorkbookManager.d.ts +87 -0
  13. package/dist/components/Workbook/WorkbookStorage.d.ts +18 -0
  14. package/dist/components/Workbook/WorkbookStorageProvider.d.ts +12 -0
  15. package/dist/components/Workbook/index.d.ts +7 -0
  16. package/dist/components/index.d.ts +3 -1
  17. package/dist/index.cjs.js +67 -67
  18. package/dist/index.es.js +9481 -9329
  19. package/package.json +3 -2
  20. package/src/components/AnalyzePackageButton.tsx +121 -24
  21. package/src/components/Model/Model.tsx +19 -110
  22. package/src/components/Model/ModelExplorer.tsx +138 -0
  23. package/src/components/Model/SourcesExplorer.tsx +65 -79
  24. package/src/components/Model/index.ts +3 -0
  25. package/src/components/Model/useModelData.ts +38 -0
  26. package/src/components/Package/index.ts +1 -0
  27. package/src/components/Project/index.ts +1 -0
  28. package/src/components/Workbook/BrowserWorkbookStorage.ts +100 -0
  29. package/src/components/{MutableNotebook → Workbook}/EditableMalloyCell.tsx +2 -2
  30. package/src/components/{MutableNotebook → Workbook}/MutableCell.tsx +3 -3
  31. package/src/components/{MutableNotebook/MutableNotebook.tsx → Workbook/Workbook.tsx} +81 -57
  32. package/src/components/Workbook/WorkbookList.tsx +111 -0
  33. package/src/components/Workbook/WorkbookManager.ts +230 -0
  34. package/src/components/Workbook/WorkbookStorage.ts +54 -0
  35. package/src/components/Workbook/WorkbookStorageProvider.tsx +37 -0
  36. package/src/components/Workbook/index.ts +7 -0
  37. package/src/components/index.ts +3 -1
  38. package/src/components/styles.ts +0 -3
  39. package/dist/components/MutableNotebook/BrowserNotebookStorage.d.ts +0 -9
  40. package/dist/components/MutableNotebook/MutableNotebook.d.ts +0 -8
  41. package/dist/components/MutableNotebook/MutableNotebookList.d.ts +0 -6
  42. package/dist/components/MutableNotebook/NotebookStorage.d.ts +0 -11
  43. package/dist/components/MutableNotebook/NotebookStorageProvider.d.ts +0 -14
  44. package/dist/components/MutableNotebook/index.d.ts +0 -5
  45. package/dist/components/NotebookManager.d.ts +0 -86
  46. package/src/components/MutableNotebook/BrowserNotebookStorage.ts +0 -58
  47. package/src/components/MutableNotebook/MutableNotebookList.tsx +0 -69
  48. package/src/components/MutableNotebook/NotebookStorage.ts +0 -27
  49. package/src/components/MutableNotebook/NotebookStorageProvider.tsx +0 -43
  50. package/src/components/MutableNotebook/index.ts +0 -8
  51. package/src/components/NotebookManager.ts +0 -225
  52. /package/dist/components/{MutableNotebook → Workbook}/ModelPicker.d.ts +0 -0
  53. /package/src/components/{MutableNotebook → Workbook}/ModelPicker.tsx +0 -0
@@ -235,7 +235,7 @@ function SourceExplorerComponentInner({
235
235
 
236
236
  const onQueryChange = React.useCallback(
237
237
  (malloyQuery: Malloy.Query) => {
238
- setQuery({ ...query, malloyQuery });
238
+ setQuery({ ...query, malloyQuery, malloyResult: undefined });
239
239
  },
240
240
  [query],
241
241
  );
@@ -243,88 +243,75 @@ function SourceExplorerComponentInner({
243
243
  if (oldSourceInfo !== sourceAndPath.sourceInfo.name) {
244
244
  return <div>Loading...</div>;
245
245
  }
246
-
247
246
  return (
248
- <StyledExplorerPage key={sourceAndPath.sourceInfo.name}>
249
- <StyledExplorerContent>
250
- <MalloyExplorerProvider
251
- source={sourceAndPath.sourceInfo}
252
- query={query?.malloyQuery}
253
- onQueryChange={onQueryChange}
254
- focusedNestViewPath={focusedNestViewPath}
255
- onFocusedNestViewPathChange={setFocusedNestViewPath}
256
- onDrill={(params) => {
257
- console.info(params);
247
+ <StyledExplorerContent key={sourceAndPath.sourceInfo.name}>
248
+ <MalloyExplorerProvider
249
+ source={sourceAndPath.sourceInfo}
250
+ query={query?.malloyQuery}
251
+ onQueryChange={onQueryChange}
252
+ focusedNestViewPath={focusedNestViewPath}
253
+ onFocusedNestViewPathChange={setFocusedNestViewPath}
254
+ onDrill={(params) => {
255
+ console.info(params);
256
+ }}
257
+ >
258
+ <div
259
+ style={{
260
+ display: "flex",
261
+ height: "100%",
262
+ overflowY: "auto",
258
263
  }}
259
264
  >
260
- <div
261
- style={{
262
- display: "flex",
263
- flexDirection: "column",
264
- height: "100%",
265
- }}
265
+ <ResizableCollapsiblePanel
266
+ isInitiallyExpanded={true}
267
+ initialWidth={180}
268
+ minWidth={180}
269
+ icon="database"
270
+ title={sourceAndPath.sourceInfo.name}
266
271
  >
267
- <div
268
- style={{
269
- display: "flex",
270
- height: "100%",
271
- overflowY: "auto",
272
+ <SourcePanel
273
+ onRefresh={() => setQuery(emptyQueryExplorerResult())}
274
+ />
275
+ </ResizableCollapsiblePanel>
276
+ <ResizableCollapsiblePanel
277
+ isInitiallyExpanded={true}
278
+ initialWidth={280}
279
+ minWidth={280}
280
+ icon="filterSliders"
281
+ title="Query"
282
+ >
283
+ <QueryPanel
284
+ runQuery={() => {
285
+ mutation.mutate();
272
286
  }}
273
- >
274
- <ResizableCollapsiblePanel
275
- isInitiallyExpanded={true}
276
- initialWidth={180}
277
- minWidth={180}
278
- icon="database"
279
- title={sourceAndPath.sourceInfo.name}
280
- >
281
- <SourcePanel
282
- onRefresh={() =>
283
- setQuery(emptyQueryExplorerResult())
284
- }
285
- />
286
- </ResizableCollapsiblePanel>
287
- <ResizableCollapsiblePanel
288
- isInitiallyExpanded={true}
289
- initialWidth={280}
290
- minWidth={280}
291
- icon="filterSliders"
292
- title="Query"
293
- >
294
- <QueryPanel
295
- runQuery={() => {
296
- mutation.mutate();
297
- }}
298
- />
299
- </ResizableCollapsiblePanel>
300
- <ResultPanel
301
- source={sourceAndPath.sourceInfo}
302
- draftQuery={query?.malloyQuery}
303
- setDraftQuery={(malloyQuery) =>
304
- setQuery({ ...query, malloyQuery: malloyQuery })
305
- }
306
- submittedQuery={
307
- query?.malloyQuery
308
- ? {
309
- executionState: mutation.isPending
310
- ? "running"
311
- : "finished",
312
- response: {
313
- result: query.malloyResult,
314
- },
315
- query: query.malloyQuery,
316
- queryResolutionStartMillis: Date.now(),
317
- onCancel: mutation.reset,
318
- }
319
- : undefined
320
- }
321
- options={{ showRawQuery: true }}
322
- />
323
- </div>
324
- </div>
325
- </MalloyExplorerProvider>
326
- </StyledExplorerContent>
327
- </StyledExplorerPage>
287
+ />
288
+ </ResizableCollapsiblePanel>
289
+ <ResultPanel
290
+ source={sourceAndPath.sourceInfo}
291
+ draftQuery={query?.malloyQuery}
292
+ setDraftQuery={(malloyQuery) =>
293
+ setQuery({ ...query, malloyQuery: malloyQuery })
294
+ }
295
+ submittedQuery={
296
+ query?.malloyQuery
297
+ ? {
298
+ executionState: mutation.isPending
299
+ ? "running"
300
+ : "finished",
301
+ response: {
302
+ result: query.malloyResult,
303
+ },
304
+ query: query.malloyQuery,
305
+ queryResolutionStartMillis: Date.now(),
306
+ onCancel: mutation.reset,
307
+ }
308
+ : undefined
309
+ }
310
+ options={{ showRawQuery: true }}
311
+ />
312
+ </div>
313
+ </MalloyExplorerProvider>
314
+ </StyledExplorerContent>
328
315
  );
329
316
  }
330
317
 
@@ -366,7 +353,6 @@ export function SourceExplorerComponent(props: SourceExplorerComponentProps) {
366
353
  <StyledExplorerContent>
367
354
  <div
368
355
  style={{
369
- display: "flex",
370
356
  alignItems: "center",
371
357
  justifyContent: "center",
372
358
  height: "200px",
@@ -1,4 +1,7 @@
1
1
  export { default as Model } from "./Model";
2
+ export { ModelExplorer } from "./ModelExplorer";
2
3
  export { SourcesExplorer } from "./SourcesExplorer";
3
4
  export { SourceExplorerComponent } from "./SourcesExplorer";
4
5
  export type { SourceAndPath } from "./SourcesExplorer";
6
+ export { useModelData } from "./useModelData";
7
+ export type { ModelExplorerProps } from "./ModelExplorer";
@@ -0,0 +1,38 @@
1
+ import { Configuration, ModelsApi, CompiledModel } from "../../client";
2
+ import { usePackage } from "../Package/PackageProvider";
3
+ import { useQueryWithApiError } from "../../hooks/useQueryWithApiError";
4
+
5
+ const modelsApi = new ModelsApi(new Configuration());
6
+
7
+ /**
8
+ * Custom hook for fetching model data. Combines usePackage context with
9
+ * useQueryWithApiError to fetch a compiled model.
10
+ */
11
+ export function useModelData(modelPath: string, versionId?: string) {
12
+ const {
13
+ projectName,
14
+ packageName,
15
+ versionId: packageVersionId,
16
+ } = usePackage();
17
+ const effectiveVersionId = versionId || packageVersionId;
18
+
19
+ return useQueryWithApiError<CompiledModel>({
20
+ queryKey: [
21
+ "package",
22
+ projectName,
23
+ packageName,
24
+ modelPath,
25
+ effectiveVersionId,
26
+ ],
27
+ queryFn: async (config) => {
28
+ const response = await modelsApi.getModel(
29
+ projectName,
30
+ packageName,
31
+ modelPath,
32
+ effectiveVersionId,
33
+ config,
34
+ );
35
+ return response.data;
36
+ },
37
+ });
38
+ }
@@ -5,3 +5,4 @@ export {
5
5
  PackageProvider as PublisherPackageProvider,
6
6
  usePackage as usePublisherPackage,
7
7
  } from "./PackageProvider";
8
+ export { default as Models } from "./Models";
@@ -1 +1,2 @@
1
1
  export { default as Project, ProjectProvider, useProject } from "./Project";
2
+ export { default as Packages } from "./Packages";
@@ -0,0 +1,100 @@
1
+ import { PackageContextProps } from "../Package";
2
+ import type {
3
+ WorkbookLocator,
4
+ WorkbookStorage,
5
+ Workspace,
6
+ } from "./WorkbookStorage";
7
+
8
+ const LOCAL_WORKSPACE_NAME = "Local";
9
+
10
+ export class BrowserWorkbookStorage implements WorkbookStorage {
11
+ private makeKey(context: PackageContextProps, path?: string): string {
12
+ let key = `BROWSER_NOTEBOOK_STORAGE/${context.projectName}/${context.packageName}`;
13
+ if (path) {
14
+ key += `/${path}`;
15
+ }
16
+ return key;
17
+ }
18
+
19
+ async listWorkspaces(
20
+ context: PackageContextProps,
21
+ writeableOnly: boolean,
22
+ ): Promise<Workspace[]> {
23
+ // Parameters not used in browser storage implementation
24
+ void context;
25
+ void writeableOnly;
26
+ return [
27
+ {
28
+ name: LOCAL_WORKSPACE_NAME,
29
+ description: "Stored locally- only accessible to this browser",
30
+ writeable: true,
31
+ },
32
+ ];
33
+ }
34
+
35
+ async listWorkbooks(
36
+ workspace: Workspace,
37
+ context: PackageContextProps,
38
+ ): Promise<WorkbookLocator[]> {
39
+ const prefix = this.makeKey(context);
40
+ const keys: WorkbookLocator[] = [];
41
+ for (let i = 0; i < localStorage.length; i++) {
42
+ const key = localStorage.key(i);
43
+ if (key && key.startsWith(prefix + "/")) {
44
+ // Extract the notebook path after the prefix
45
+ const notebookPath = key.substring(prefix.length + 1);
46
+ keys.push({ path: notebookPath, workspace: LOCAL_WORKSPACE_NAME });
47
+ }
48
+ }
49
+ return keys;
50
+ }
51
+
52
+ async getWorkbook(
53
+ context: PackageContextProps,
54
+ path: WorkbookLocator,
55
+ ): Promise<string> {
56
+ const key = this.makeKey(context, path.path);
57
+ const notebook = localStorage.getItem(key);
58
+ if (notebook === null) {
59
+ throw new Error(`Notebook not found at path: ${path}`);
60
+ }
61
+ return notebook;
62
+ }
63
+
64
+ async deleteWorkbook(
65
+ context: PackageContextProps,
66
+ path: WorkbookLocator,
67
+ ): Promise<void> {
68
+ const key = this.makeKey(context, path.path);
69
+ if (localStorage.getItem(key) === null) {
70
+ throw new Error(`Notebook not found at path: ${path}`);
71
+ }
72
+ localStorage.removeItem(key);
73
+ }
74
+
75
+ async saveWorkbook(
76
+ context: PackageContextProps,
77
+ path: WorkbookLocator,
78
+ notebook: string,
79
+ ): Promise<void> {
80
+ console.log("saveWorkbook", context, path, notebook);
81
+ const key = this.makeKey(context, path.path);
82
+ console.log("saveWorkbook", key);
83
+ localStorage.setItem(key, notebook);
84
+ }
85
+
86
+ async moveWorkbook(
87
+ context: PackageContextProps,
88
+ from: WorkbookLocator,
89
+ to: WorkbookLocator,
90
+ ): Promise<void> {
91
+ const fromKey = this.makeKey(context, from.path);
92
+ const toKey = this.makeKey(context, to.path);
93
+ const notebook = localStorage.getItem(fromKey);
94
+ if (notebook === null) {
95
+ throw new Error(`Notebook not found at path: ${from.path}`);
96
+ }
97
+ localStorage.setItem(toKey, notebook);
98
+ localStorage.removeItem(fromKey);
99
+ }
100
+ }
@@ -1,9 +1,9 @@
1
1
  import { SourceAndPath, SourcesExplorer } from "../Model";
2
2
  import { QueryExplorerResult } from "../Model/SourcesExplorer";
3
- import { NotebookCellValue } from "../NotebookManager";
3
+ import { WorkbookCellValue } from "./WorkbookManager";
4
4
 
5
5
  interface EditableMalloyCellProps {
6
- cell: NotebookCellValue;
6
+ cell: WorkbookCellValue;
7
7
  sourceAndPaths: SourceAndPath[];
8
8
  onQueryChange: (query: QueryExplorerResult) => void;
9
9
  onSourceChange?: (index: number) => void;
@@ -24,20 +24,20 @@ import {
24
24
  QueryExplorerResult,
25
25
  SourceAndPath,
26
26
  } from "../Model/SourcesExplorer";
27
- import { NotebookCellValue } from "../NotebookManager";
27
+ import { WorkbookCellValue } from "./WorkbookManager";
28
28
  import ResultContainer from "../RenderedResult/ResultContainer";
29
29
  import { StyledCard } from "../styles";
30
30
  import { EditableMalloyCell } from "./EditableMalloyCell";
31
31
 
32
32
  interface NotebookCellProps {
33
- cell: NotebookCellValue;
33
+ cell: WorkbookCellValue;
34
34
  expandCodeCell?: boolean;
35
35
  expandEmbedding?: boolean;
36
36
  hideEmbeddingIcons?: boolean;
37
37
  editingMalloy?: boolean;
38
38
  editingMarkdown?: boolean;
39
39
  sourceAndPaths: SourceAndPath[];
40
- onCellChange: (cell: NotebookCellValue) => void;
40
+ onCellChange: (cell: WorkbookCellValue) => void;
41
41
  onClose: () => void;
42
42
  onEdit: () => void;
43
43
  onDelete: () => void;
@@ -20,21 +20,22 @@ import React from "react";
20
20
  import { Configuration, ModelsApi } from "../../client";
21
21
  import { useRouterClickHandler } from "../click_helper";
22
22
  import { SourceAndPath } from "../Model/SourcesExplorer";
23
- import { NotebookManager } from "../NotebookManager";
23
+ import { WorkbookManager } from "./WorkbookManager";
24
24
  import { usePackage } from "../Package";
25
25
  import { useServer } from "../ServerProvider";
26
26
  import { StyledCard, StyledCardContent, StyledCardMedia } from "../styles";
27
27
  import { MutableCell } from "./MutableCell";
28
- import { useNotebookStorage } from "./NotebookStorageProvider";
28
+ import { useWorkbookStorage } from "./WorkbookStorageProvider";
29
29
 
30
30
  import * as Malloy from "@malloydata/malloy-interfaces";
31
31
  import { ModelPicker } from "./ModelPicker";
32
32
  import { getAxiosConfig } from "../../hooks";
33
+ import { WorkbookLocator } from "./WorkbookStorage";
33
34
 
34
35
  const modelsApi = new ModelsApi(new Configuration());
35
36
 
36
- interface MutableNotebookProps {
37
- notebookPath?: string;
37
+ interface WorkbookProps {
38
+ workbookPath?: WorkbookLocator;
38
39
  expandCodeCells?: boolean;
39
40
  expandEmbeddings?: boolean;
40
41
  hideEmbeddingIcons?: boolean;
@@ -45,28 +46,32 @@ interface PathToSources {
45
46
  sourceInfos: Malloy.SourceInfo[];
46
47
  }
47
48
 
48
- export default function MutableNotebook({
49
- notebookPath,
49
+ export default function Workbook({
50
+ workbookPath,
50
51
  expandCodeCells,
51
52
  expandEmbeddings,
52
53
  hideEmbeddingIcons,
53
- }: MutableNotebookProps) {
54
+ }: WorkbookProps) {
54
55
  const navigate = useRouterClickHandler();
55
- const { projectName, packageName, versionId } = usePackage();
56
+ const packageContext = usePackage();
57
+ const { projectName, packageName, versionId } = packageContext;
56
58
  const { server, getAccessToken } = useServer();
57
- const { notebookStorage, userContext } = useNotebookStorage();
59
+ const { workbookStorage } = useWorkbookStorage();
60
+ const [lastError, setLastError] = React.useState<string | undefined>(
61
+ undefined,
62
+ );
58
63
  if (!projectName || !packageName) {
59
64
  throw new Error(
60
65
  "Project and package must be provided via PubliserPackageProvider",
61
66
  );
62
67
  }
63
- if (!notebookStorage || !userContext) {
68
+ if (!workbookStorage) {
64
69
  throw new Error(
65
- "Notebook storage and user context must be provided via NotebookStorageProvider",
70
+ "Workbook storage be provided via WorkbookStorageProvider",
66
71
  );
67
72
  }
68
- const [notebookData, setNotebookData] = React.useState<
69
- NotebookManager | undefined
73
+ const [workbookData, setWorkbookData] = React.useState<
74
+ WorkbookManager | undefined
70
75
  >();
71
76
  const [editingMalloyIndex, setEditingMalloyIndex] = React.useState<
72
77
  number | undefined
@@ -87,11 +92,11 @@ export default function MutableNotebook({
87
92
  setMenuIndex(null);
88
93
  };
89
94
  const handleAddCell = (isMarkdown: boolean, index: number) => {
90
- notebookData.insertCell(index, {
95
+ workbookData.insertCell(index, {
91
96
  isMarkdown,
92
97
  value: "",
93
98
  });
94
- saveNotebook();
99
+ saveWorkbook();
95
100
  if (isMarkdown) {
96
101
  setEditingMarkdownIndex(index);
97
102
  } else {
@@ -105,9 +110,16 @@ export default function MutableNotebook({
105
110
  setDeleteDialogOpen(true);
106
111
  };
107
112
 
108
- const handleDeleteConfirm = (event?: React.MouseEvent) => {
109
- if (notebookPath && notebookStorage && userContext) {
110
- notebookStorage.deleteNotebook(userContext, notebookPath);
113
+ const handleDeleteConfirm = async (event?: React.MouseEvent) => {
114
+ if (workbookPath && workbookStorage && packageContext) {
115
+ await workbookStorage
116
+ .deleteWorkbook(packageContext, workbookPath)
117
+ .then(() => {
118
+ setLastError(undefined);
119
+ })
120
+ .catch((error) => {
121
+ setLastError(`Error deleting workbook: ${error.message}`);
122
+ });
111
123
  }
112
124
  setDeleteDialogOpen(false);
113
125
  navigate(`/${projectName}/${packageName}`, event);
@@ -117,12 +129,17 @@ export default function MutableNotebook({
117
129
  setDeleteDialogOpen(false);
118
130
  };
119
131
 
120
- const saveNotebook = React.useCallback(() => {
121
- setNotebookData(notebookData.saveNotebook());
122
- }, [notebookData]);
132
+ const saveWorkbook = React.useCallback(async () => {
133
+ try {
134
+ setWorkbookData(await workbookData.saveWorkbook());
135
+ setLastError(undefined);
136
+ } catch (error) {
137
+ setLastError(`Error saving workbook: ${error.message}`);
138
+ }
139
+ }, [workbookData]);
123
140
  React.useEffect(() => {
124
141
  // Load SourceInfos from selected models and sync PathsToSources
125
- if (!notebookData) {
142
+ if (!workbookData) {
126
143
  return;
127
144
  }
128
145
 
@@ -136,7 +153,7 @@ export default function MutableNotebook({
136
153
  const newSourceAndPaths = [];
137
154
  const promises = [];
138
155
 
139
- for (const model of notebookData.getModels()) {
156
+ for (const model of workbookData.getModels()) {
140
157
  if (!modelPathToSourceInfo.has(model)) {
141
158
  console.log("Fetching model from Publisher", model);
142
159
  promises.push(
@@ -173,9 +190,9 @@ export default function MutableNotebook({
173
190
 
174
191
  fetchModels();
175
192
  }, [
176
- // Note this cannot depend on sourceAndPaths because it will cause an infinite loop.
193
+ // Work this cannot depend on sourceAndPaths because it will cause an infinite loop.
177
194
  getAccessToken,
178
- notebookData,
195
+ workbookData,
179
196
  packageName,
180
197
  projectName,
181
198
  server,
@@ -183,19 +200,19 @@ export default function MutableNotebook({
183
200
  ]);
184
201
 
185
202
  React.useEffect(() => {
186
- if (!notebookPath) {
203
+ if (!workbookPath) {
187
204
  return;
188
205
  }
189
- setNotebookData(
190
- NotebookManager.loadNotebook(
191
- notebookStorage,
192
- userContext,
193
- notebookPath,
194
- ),
195
- );
196
- }, [notebookPath, notebookStorage, userContext]);
206
+ WorkbookManager.loadWorkbook(
207
+ workbookStorage,
208
+ packageContext,
209
+ workbookPath,
210
+ ).then((workbookData) => {
211
+ setWorkbookData(workbookData);
212
+ });
213
+ }, [workbookPath, workbookStorage, packageContext]);
197
214
 
198
- if (!notebookData) {
215
+ if (!workbookData) {
199
216
  return <div>Loading...</div>;
200
217
  }
201
218
  const getSourceList = (sourceAndPaths: PathToSources[]): SourceAndPath[] => {
@@ -246,14 +263,21 @@ export default function MutableNotebook({
246
263
  flex: 2,
247
264
  }}
248
265
  >
249
- {plusButton(false, notebookData.getCells().length)}
250
- {plusButton(true, notebookData.getCells().length)}
266
+ {plusButton(false, workbookData.getCells().length)}
267
+ {plusButton(true, workbookData.getCells().length)}
251
268
  </Box>
252
269
  );
253
270
 
254
271
  return (
255
272
  <StyledCard variant="outlined">
256
273
  <StyledCardContent>
274
+ {lastError && (
275
+ <Box sx={{ mb: 2 }}>
276
+ <Typography color="error" variant="body2">
277
+ {lastError}
278
+ </Typography>
279
+ </Box>
280
+ )}
257
281
  <Stack
258
282
  sx={{
259
283
  flexDirection: "row",
@@ -281,7 +305,7 @@ export default function MutableNotebook({
281
305
  ml: 1,
282
306
  }}
283
307
  >
284
- {`${projectName} > ${packageName} > ${notebookPath}`}
308
+ {`${projectName} > ${packageName} > ${workbookPath.path}`}
285
309
  </Typography>
286
310
  </Stack>
287
311
  <Stack sx={{ display: "flex", flexDirection: "row", gap: 1 }}>
@@ -293,7 +317,7 @@ export default function MutableNotebook({
293
317
  mb: 1,
294
318
  }}
295
319
  >
296
- <ExportMalloyButton notebookData={notebookData} />
320
+ <ExportMalloyButton workbookData={workbookData} />
297
321
  </Box>
298
322
  <Box
299
323
  sx={{
@@ -314,12 +338,12 @@ export default function MutableNotebook({
314
338
  open={deleteDialogOpen}
315
339
  onClose={handleDeleteCancel}
316
340
  >
317
- <DialogTitle>Delete Notebook</DialogTitle>
341
+ <DialogTitle>Delete Workbook</DialogTitle>
318
342
  <DialogContent>
319
343
  <DialogContentText>
320
- Are you sure you want to delete the notebook
344
+ Are you sure you want to delete the workbook
321
345
  &quot;
322
- {notebookPath}&quot;? This action cannot be
346
+ {workbookPath.path}&quot;? This action cannot be
323
347
  undone.
324
348
  </DialogContentText>
325
349
  </DialogContent>
@@ -355,10 +379,10 @@ export default function MutableNotebook({
355
379
  >
356
380
  <Box sx={{ flex: 1 }}>
357
381
  <ModelPicker
358
- initialSelectedModels={notebookData.getModels()}
382
+ initialSelectedModels={workbookData.getModels()}
359
383
  onModelChange={(models) => {
360
- setNotebookData(notebookData.setModels(models));
361
- saveNotebook();
384
+ setWorkbookData(workbookData.setModels(models));
385
+ saveWorkbook();
362
386
  }}
363
387
  />
364
388
  </Box>
@@ -367,7 +391,7 @@ export default function MutableNotebook({
367
391
  </StyledCardContent>
368
392
  <StyledCardMedia>
369
393
  <Stack>
370
- {notebookData.getCells().length === 0 && (
394
+ {workbookData.getCells().length === 0 && (
371
395
  <>
372
396
  <Typography
373
397
  sx={{
@@ -387,12 +411,12 @@ export default function MutableNotebook({
387
411
  </Typography>
388
412
  </>
389
413
  )}
390
- {notebookData.getCells().map((cell, index) => (
414
+ {workbookData.getCells().map((cell, index) => (
391
415
  <React.Fragment
392
- key={`${index}-${notebookData.getCells().length}`}
416
+ key={`${index}-${workbookData.getCells().length}`}
393
417
  >
394
418
  <MutableCell
395
- key={`${index}-${cell.isMarkdown}-${notebookPath}-${projectName}-${packageName}`}
419
+ key={`${index}-${cell.isMarkdown}-${workbookPath}-${projectName}-${packageName}`}
396
420
  cell={cell}
397
421
  addButtonCallback={(isMarkdown) =>
398
422
  plusButton(isMarkdown, index)
@@ -404,12 +428,12 @@ export default function MutableNotebook({
404
428
  editingMarkdown={editingMarkdownIndex === index}
405
429
  editingMalloy={editingMalloyIndex === index}
406
430
  onDelete={() => {
407
- setNotebookData(notebookData.deleteCell(index));
408
- saveNotebook();
431
+ setWorkbookData(workbookData.deleteCell(index));
432
+ saveWorkbook();
409
433
  }}
410
434
  onCellChange={(cell) => {
411
- setNotebookData(notebookData.setCell(index, cell));
412
- saveNotebook();
435
+ setWorkbookData(workbookData.setCell(index, cell));
436
+ saveWorkbook();
413
437
  }}
414
438
  onEdit={() => {
415
439
  if (cell.isMarkdown) {
@@ -459,14 +483,14 @@ export default function MutableNotebook({
459
483
  }
460
484
 
461
485
  function ExportMalloyButton({
462
- notebookData,
486
+ workbookData,
463
487
  }: {
464
- notebookData: NotebookManager;
488
+ workbookData: WorkbookManager;
465
489
  }) {
466
490
  const [copied, setCopied] = React.useState(false);
467
491
  const handleExport = async () => {
468
- if (!notebookData) return;
469
- const malloy = notebookData.toMalloyNotebook();
492
+ if (!workbookData) return;
493
+ const malloy = workbookData.toMalloyWorkbook();
470
494
  try {
471
495
  await navigator.clipboard.writeText(malloy);
472
496
  setCopied(true);