@malloy-publisher/sdk 0.0.80 → 0.0.81

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 (45) hide show
  1. package/dist/components/Model/Model.d.ts +3 -1
  2. package/dist/components/Package/index.d.ts +1 -0
  3. package/dist/components/Project/index.d.ts +1 -0
  4. package/dist/components/Workbook/BrowserWorkbookStorage.d.ts +11 -0
  5. package/dist/components/{MutableNotebook → Workbook}/EditableMalloyCell.d.ts +2 -2
  6. package/dist/components/{MutableNotebook → Workbook}/MutableCell.d.ts +3 -3
  7. package/dist/components/Workbook/Workbook.d.ts +9 -0
  8. package/dist/components/Workbook/WorkbookList.d.ts +7 -0
  9. package/dist/components/Workbook/WorkbookManager.d.ts +87 -0
  10. package/dist/components/Workbook/WorkbookStorage.d.ts +18 -0
  11. package/dist/components/Workbook/WorkbookStorageProvider.d.ts +12 -0
  12. package/dist/components/Workbook/index.d.ts +7 -0
  13. package/dist/components/index.d.ts +3 -1
  14. package/dist/index.cjs.js +62 -62
  15. package/dist/index.es.js +6466 -6337
  16. package/package.json +3 -2
  17. package/src/components/AnalyzePackageButton.tsx +121 -24
  18. package/src/components/Model/Model.tsx +7 -1
  19. package/src/components/Package/index.ts +1 -0
  20. package/src/components/Project/index.ts +1 -0
  21. package/src/components/Workbook/BrowserWorkbookStorage.ts +100 -0
  22. package/src/components/{MutableNotebook → Workbook}/EditableMalloyCell.tsx +2 -2
  23. package/src/components/{MutableNotebook → Workbook}/MutableCell.tsx +3 -3
  24. package/src/components/{MutableNotebook/MutableNotebook.tsx → Workbook/Workbook.tsx} +81 -57
  25. package/src/components/Workbook/WorkbookList.tsx +111 -0
  26. package/src/components/Workbook/WorkbookManager.ts +230 -0
  27. package/src/components/Workbook/WorkbookStorage.ts +54 -0
  28. package/src/components/Workbook/WorkbookStorageProvider.tsx +37 -0
  29. package/src/components/Workbook/index.ts +7 -0
  30. package/src/components/index.ts +3 -1
  31. package/dist/components/MutableNotebook/BrowserNotebookStorage.d.ts +0 -9
  32. package/dist/components/MutableNotebook/MutableNotebook.d.ts +0 -8
  33. package/dist/components/MutableNotebook/MutableNotebookList.d.ts +0 -6
  34. package/dist/components/MutableNotebook/NotebookStorage.d.ts +0 -11
  35. package/dist/components/MutableNotebook/NotebookStorageProvider.d.ts +0 -14
  36. package/dist/components/MutableNotebook/index.d.ts +0 -5
  37. package/dist/components/NotebookManager.d.ts +0 -86
  38. package/src/components/MutableNotebook/BrowserNotebookStorage.ts +0 -58
  39. package/src/components/MutableNotebook/MutableNotebookList.tsx +0 -69
  40. package/src/components/MutableNotebook/NotebookStorage.ts +0 -27
  41. package/src/components/MutableNotebook/NotebookStorageProvider.tsx +0 -43
  42. package/src/components/MutableNotebook/index.ts +0 -8
  43. package/src/components/NotebookManager.ts +0 -225
  44. /package/dist/components/{MutableNotebook → Workbook}/ModelPicker.d.ts +0 -0
  45. /package/src/components/{MutableNotebook → Workbook}/ModelPicker.tsx +0 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@malloy-publisher/sdk",
3
3
  "description": "Malloy Publisher SDK",
4
- "version": "0.0.80",
4
+ "version": "0.0.81",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs.js",
7
7
  "module": "dist/index.es.js",
@@ -49,7 +49,8 @@
49
49
  "@tanstack/react-query": "^5.59.16",
50
50
  "@uiw/react-md-editor": "^4.0.6",
51
51
  "axios": "^1.7.7",
52
- "markdown-to-jsx": "^7.7.6"
52
+ "markdown-to-jsx": "^7.7.6",
53
+ "duckdb": "^1.3.1"
53
54
  },
54
55
  "devDependencies": {
55
56
  "@openapitools/openapi-generator-cli": "^2.20.2",
@@ -1,30 +1,38 @@
1
1
  import { Add, Launch } from "@mui/icons-material";
2
2
  import {
3
+ Box,
3
4
  Button,
4
5
  Dialog,
5
6
  DialogContent,
6
7
  DialogTitle,
7
8
  FormControl,
9
+ InputLabel,
8
10
  ListItemIcon,
9
11
  ListItemText,
10
12
  Menu,
11
13
  MenuItem,
14
+ Select,
12
15
  Stack,
13
16
  TextField,
14
17
  Typography,
15
18
  } from "@mui/material";
16
19
  import React from "react";
17
20
  import { useRouterClickHandler } from "./click_helper";
18
- import {
19
- BrowserNotebookStorage,
20
- MutableNotebookList,
21
- NotebookStorageProvider,
22
- } from "./MutableNotebook";
21
+ import { WorkbookList } from "./Workbook";
22
+ import { useWorkbookStorage } from "./Workbook/WorkbookStorageProvider";
23
+ import type { WorkbookLocator, Workspace } from "./Workbook/WorkbookStorage";
23
24
  import { useParams } from "react-router-dom";
25
+ import { PackageContextProps, PackageProvider } from "./Package";
24
26
 
25
27
  export function AnalyzePackageButton() {
26
- const { projectName, packageName } = useParams();
28
+ const packageContext = useParams() as unknown as PackageContextProps;
29
+ const { packageName, projectName } = packageContext;
30
+ const { workbookStorage } = useWorkbookStorage();
27
31
  const [workbookName, setWorkbookName] = React.useState("");
32
+ const [selectedWorkspace, setSelectedWorkspace] = React.useState("");
33
+ const [availableWorkspaces, setAvailableWorkspaces] = React.useState<
34
+ Workspace[]
35
+ >([]);
28
36
  const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
29
37
  const [newDialogOpen, setNewDialogOpen] = React.useState(false);
30
38
  const [openDialogOpen, setOpenDialogOpen] = React.useState(false);
@@ -42,25 +50,52 @@ export function AnalyzePackageButton() {
42
50
  };
43
51
  const handleNewDialogClose = () => {
44
52
  setNewDialogOpen(false);
53
+ setWorkbookName("");
54
+ setSelectedWorkspace("");
45
55
  };
46
56
 
47
- const handleNotebookClick = (notebook: string, event: React.MouseEvent) => {
57
+ // Fetch available workspaces when the new dialog opens
58
+ React.useEffect(() => {
59
+ if (newDialogOpen && workbookStorage && packageContext) {
60
+ workbookStorage
61
+ .listWorkspaces(packageContext, true) // Only show writeable workspaces
62
+ .then((workspaces) => {
63
+ setAvailableWorkspaces(workspaces);
64
+ // If only one workspace, select it by default
65
+ if (workspaces.length === 1) {
66
+ setSelectedWorkspace(workspaces[0].name);
67
+ }
68
+ })
69
+ .catch((error) => {
70
+ console.error("Error fetching workspaces:", error);
71
+ setAvailableWorkspaces([]);
72
+ });
73
+ }
74
+ }, [newDialogOpen, workbookStorage, packageContext]);
75
+
76
+ const handleWorkbookClick = (
77
+ workbook: WorkbookLocator,
78
+ event: React.MouseEvent,
79
+ ) => {
48
80
  setOpenDialogOpen(false);
49
- // Navigate to the ScratchNotebookPage with anchor text for notebookPath
81
+ // Navigate to the WorkbookPage with anchor text for notebookPath
50
82
  navigate(
51
- `/${projectName}/${packageName}/scratchNotebook/${encodeURIComponent(notebook)}`,
83
+ `/${projectName}/${packageName}/workbook/${encodeURIComponent(workbook.workspace)}/${encodeURIComponent(workbook.path)}`,
52
84
  event,
53
85
  );
54
86
  };
55
87
 
56
- const createNotebookClick = (event?: React.MouseEvent) => {
88
+ const createWorkbookClick = (event?: React.MouseEvent) => {
57
89
  setNewDialogOpen(false);
58
- // Navigate to the ScratchNotebookPage with anchor text for notebookPath
90
+ // Include workspace in the workbook path if selected
91
+ const workbookPath = `${encodeURIComponent(selectedWorkspace)}/${encodeURIComponent(workbookName)}`;
92
+ // Navigate to the WorkbookPage with anchor text for notebookPath
59
93
  navigate(
60
- `/${projectName}/${packageName}/scratchNotebook/${encodeURIComponent(workbookName)}`,
94
+ `/${projectName}/${packageName}/workbook/${workbookPath}`,
61
95
  event,
62
96
  );
63
97
  setWorkbookName("");
98
+ setSelectedWorkspace("");
64
99
  };
65
100
 
66
101
  if (!projectName || !packageName) {
@@ -143,7 +178,10 @@ export function AnalyzePackageButton() {
143
178
  fullWidth
144
179
  >
145
180
  <DialogTitle sx={{ pb: 1, pt: 2, px: 2 }}>
146
- <Typography variant="h6" fontWeight={600} sx={{ mb: 0.5 }}>
181
+ <Typography
182
+ fontWeight={600}
183
+ sx={{ fontSize: "1.5rem", mb: 0.5 }}
184
+ >
147
185
  Create New Workbook
148
186
  </Typography>
149
187
  <Typography variant="body2" color="text.secondary">
@@ -152,6 +190,61 @@ export function AnalyzePackageButton() {
152
190
  </DialogTitle>
153
191
  <DialogContent sx={{ px: 2, pb: 2 }}>
154
192
  <Stack spacing={2} sx={{ mt: 1 }}>
193
+ {availableWorkspaces.length > 1 ? (
194
+ <FormControl fullWidth size="small">
195
+ <InputLabel>Workspace</InputLabel>
196
+ <Select
197
+ value={selectedWorkspace}
198
+ label="Workspace"
199
+ onChange={(e) =>
200
+ setSelectedWorkspace(e.target.value)
201
+ }
202
+ >
203
+ {availableWorkspaces.map((workspace) => (
204
+ <MenuItem
205
+ key={workspace.name}
206
+ value={workspace.name}
207
+ >
208
+ <Box>
209
+ <Typography
210
+ variant="body2"
211
+ fontWeight={500}
212
+ >
213
+ {workspace.name}
214
+ </Typography>
215
+ <Typography
216
+ variant="caption"
217
+ color="text.secondary"
218
+ >
219
+ {workspace.description}
220
+ </Typography>
221
+ </Box>
222
+ </MenuItem>
223
+ ))}
224
+ </Select>
225
+ </FormControl>
226
+ ) : availableWorkspaces.length === 1 ? (
227
+ <Box
228
+ sx={{
229
+ p: 2,
230
+ border: 1,
231
+ borderColor: "divider",
232
+ borderRadius: 1,
233
+ backgroundColor: "grey.50",
234
+ }}
235
+ >
236
+ <Typography
237
+ variant="body2"
238
+ fontWeight={500}
239
+ gutterBottom
240
+ >
241
+ Workspace: {availableWorkspaces[0].name}
242
+ </Typography>
243
+ <Typography variant="caption" color="text.secondary">
244
+ {availableWorkspaces[0].description}
245
+ </Typography>
246
+ </Box>
247
+ ) : null}
155
248
  <FormControl fullWidth>
156
249
  <TextField
157
250
  label="Workbook Name"
@@ -172,9 +265,13 @@ export function AnalyzePackageButton() {
172
265
  Cancel
173
266
  </Button>
174
267
  <Button
175
- onClick={(event) => createNotebookClick(event)}
268
+ onClick={(event) => createWorkbookClick(event)}
176
269
  variant="contained"
177
- disabled={!workbookName.trim()}
270
+ disabled={
271
+ !workbookName.trim() ||
272
+ (availableWorkspaces.length > 1 &&
273
+ !selectedWorkspace)
274
+ }
178
275
  size="small"
179
276
  >
180
277
  Create Workbook
@@ -192,7 +289,10 @@ export function AnalyzePackageButton() {
192
289
  fullWidth
193
290
  >
194
291
  <DialogTitle sx={{ pb: 1, pt: 2, px: 2 }}>
195
- <Typography variant="h6" fontWeight={600} sx={{ mb: 0.5 }}>
292
+ <Typography
293
+ fontWeight={600}
294
+ sx={{ mb: 0.5, fontSize: "1.5rem" }}
295
+ >
196
296
  Open Workbook
197
297
  </Typography>
198
298
  <Typography variant="body2" color="text.secondary">
@@ -200,15 +300,12 @@ export function AnalyzePackageButton() {
200
300
  </Typography>
201
301
  </DialogTitle>
202
302
  <DialogContent sx={{ px: 2, pb: 2 }}>
203
- <NotebookStorageProvider
204
- notebookStorage={new BrowserNotebookStorage()}
205
- userContext={{
206
- project: projectName,
207
- package: packageName,
208
- }}
303
+ <PackageProvider
304
+ projectName={projectName}
305
+ packageName={packageName}
209
306
  >
210
- <MutableNotebookList onNotebookClick={handleNotebookClick} />
211
- </NotebookStorageProvider>
307
+ <WorkbookList onWorkbookClick={handleWorkbookClick} />
308
+ </PackageProvider>
212
309
  </DialogContent>
213
310
  </Dialog>
214
311
  </>
@@ -21,7 +21,10 @@ import { ApiErrorDisplay } from "../ApiErrorDisplay";
21
21
 
22
22
  import "@malloydata/malloy-explorer/styles.css";
23
23
  import { usePackage } from "../Package/PackageProvider";
24
- import { SourceExplorerComponent } from "./SourcesExplorer";
24
+ import {
25
+ QueryExplorerResult,
26
+ SourceExplorerComponent,
27
+ } from "./SourcesExplorer";
25
28
  import { Loading } from "../Loading";
26
29
  import { useQueryWithApiError } from "../../hooks/useQueryWithApiError";
27
30
 
@@ -34,6 +37,7 @@ interface ModelProps {
34
37
  hideResultIcons?: boolean;
35
38
  expandEmbeddings?: boolean;
36
39
  hideEmbeddingIcons?: boolean;
40
+ onChange?: (query: QueryExplorerResult) => void;
37
41
  }
38
42
 
39
43
  // Note: For this to properly render outside of publisher,
@@ -46,6 +50,7 @@ export default function Model({
46
50
  hideResultIcons,
47
51
  expandEmbeddings,
48
52
  hideEmbeddingIcons,
53
+ onChange,
49
54
  }: ModelProps) {
50
55
  const [embeddingExpanded, setEmbeddingExpanded] =
51
56
  React.useState<boolean>(false);
@@ -203,6 +208,7 @@ export default function Model({
203
208
  data.sourceInfos[selectedTab],
204
209
  ),
205
210
  }}
211
+ onChange={onChange}
206
212
  />
207
213
  )}
208
214
  {data.queries?.length > 0 && (
@@ -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;