@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.
- package/dist/components/Model/Model.d.ts +3 -1
- package/dist/components/Model/ModelExplorer.d.ts +19 -0
- package/dist/components/Model/index.d.ts +3 -0
- package/dist/components/Model/useModelData.d.ts +8 -0
- package/dist/components/Package/index.d.ts +1 -0
- package/dist/components/Project/index.d.ts +1 -0
- package/dist/components/Workbook/BrowserWorkbookStorage.d.ts +11 -0
- package/dist/components/{MutableNotebook → Workbook}/EditableMalloyCell.d.ts +2 -2
- package/dist/components/{MutableNotebook → Workbook}/MutableCell.d.ts +3 -3
- package/dist/components/Workbook/Workbook.d.ts +9 -0
- package/dist/components/Workbook/WorkbookList.d.ts +7 -0
- package/dist/components/Workbook/WorkbookManager.d.ts +87 -0
- package/dist/components/Workbook/WorkbookStorage.d.ts +18 -0
- package/dist/components/Workbook/WorkbookStorageProvider.d.ts +12 -0
- package/dist/components/Workbook/index.d.ts +7 -0
- package/dist/components/index.d.ts +3 -1
- package/dist/index.cjs.js +67 -67
- package/dist/index.es.js +9481 -9329
- package/package.json +3 -2
- package/src/components/AnalyzePackageButton.tsx +121 -24
- package/src/components/Model/Model.tsx +19 -110
- package/src/components/Model/ModelExplorer.tsx +138 -0
- package/src/components/Model/SourcesExplorer.tsx +65 -79
- package/src/components/Model/index.ts +3 -0
- package/src/components/Model/useModelData.ts +38 -0
- package/src/components/Package/index.ts +1 -0
- package/src/components/Project/index.ts +1 -0
- package/src/components/Workbook/BrowserWorkbookStorage.ts +100 -0
- package/src/components/{MutableNotebook → Workbook}/EditableMalloyCell.tsx +2 -2
- package/src/components/{MutableNotebook → Workbook}/MutableCell.tsx +3 -3
- package/src/components/{MutableNotebook/MutableNotebook.tsx → Workbook/Workbook.tsx} +81 -57
- package/src/components/Workbook/WorkbookList.tsx +111 -0
- package/src/components/Workbook/WorkbookManager.ts +230 -0
- package/src/components/Workbook/WorkbookStorage.ts +54 -0
- package/src/components/Workbook/WorkbookStorageProvider.tsx +37 -0
- package/src/components/Workbook/index.ts +7 -0
- package/src/components/index.ts +3 -1
- package/src/components/styles.ts +0 -3
- package/dist/components/MutableNotebook/BrowserNotebookStorage.d.ts +0 -9
- package/dist/components/MutableNotebook/MutableNotebook.d.ts +0 -8
- package/dist/components/MutableNotebook/MutableNotebookList.d.ts +0 -6
- package/dist/components/MutableNotebook/NotebookStorage.d.ts +0 -11
- package/dist/components/MutableNotebook/NotebookStorageProvider.d.ts +0 -14
- package/dist/components/MutableNotebook/index.d.ts +0 -5
- package/dist/components/NotebookManager.d.ts +0 -86
- package/src/components/MutableNotebook/BrowserNotebookStorage.ts +0 -58
- package/src/components/MutableNotebook/MutableNotebookList.tsx +0 -69
- package/src/components/MutableNotebook/NotebookStorage.ts +0 -27
- package/src/components/MutableNotebook/NotebookStorageProvider.tsx +0 -43
- package/src/components/MutableNotebook/index.ts +0 -8
- package/src/components/NotebookManager.ts +0 -225
- /package/dist/components/{MutableNotebook → Workbook}/ModelPicker.d.ts +0 -0
- /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.
|
|
4
|
+
"version": "0.0.82",
|
|
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
|
-
|
|
20
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
81
|
+
// Navigate to the WorkbookPage with anchor text for notebookPath
|
|
50
82
|
navigate(
|
|
51
|
-
`/${projectName}/${packageName}/
|
|
83
|
+
`/${projectName}/${packageName}/workbook/${encodeURIComponent(workbook.workspace)}/${encodeURIComponent(workbook.path)}`,
|
|
52
84
|
event,
|
|
53
85
|
);
|
|
54
86
|
};
|
|
55
87
|
|
|
56
|
-
const
|
|
88
|
+
const createWorkbookClick = (event?: React.MouseEvent) => {
|
|
57
89
|
setNewDialogOpen(false);
|
|
58
|
-
//
|
|
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}/
|
|
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
|
|
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) =>
|
|
268
|
+
onClick={(event) => createWorkbookClick(event)}
|
|
176
269
|
variant="contained"
|
|
177
|
-
disabled={
|
|
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
|
|
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
|
-
<
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
project: projectName,
|
|
207
|
-
package: packageName,
|
|
208
|
-
}}
|
|
303
|
+
<PackageProvider
|
|
304
|
+
projectName={projectName}
|
|
305
|
+
packageName={packageName}
|
|
209
306
|
>
|
|
210
|
-
<
|
|
211
|
-
</
|
|
307
|
+
<WorkbookList onWorkbookClick={handleWorkbookClick} />
|
|
308
|
+
</PackageProvider>
|
|
212
309
|
</DialogContent>
|
|
213
310
|
</Dialog>
|
|
214
311
|
</>
|
|
@@ -1,31 +1,24 @@
|
|
|
1
1
|
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
|
|
2
2
|
import LinkOutlinedIcon from "@mui/icons-material/LinkOutlined";
|
|
3
3
|
import {
|
|
4
|
-
Box,
|
|
5
4
|
CardActions,
|
|
6
5
|
Collapse,
|
|
7
6
|
Divider,
|
|
8
7
|
IconButton,
|
|
9
8
|
Stack,
|
|
10
|
-
Tab,
|
|
11
|
-
Tabs,
|
|
12
9
|
Tooltip,
|
|
13
10
|
Typography,
|
|
14
11
|
} from "@mui/material";
|
|
15
12
|
import React, { useEffect } from "react";
|
|
16
|
-
import { Configuration, ModelsApi, CompiledModel } from "../../client";
|
|
17
13
|
import { highlight } from "../highlighter";
|
|
18
|
-
import { StyledCard, StyledCardContent
|
|
19
|
-
import { ModelCell } from "./ModelCell";
|
|
14
|
+
import { StyledCard, StyledCardContent } from "../styles";
|
|
20
15
|
import { ApiErrorDisplay } from "../ApiErrorDisplay";
|
|
21
16
|
|
|
22
17
|
import "@malloydata/malloy-explorer/styles.css";
|
|
23
|
-
import {
|
|
24
|
-
import { SourceExplorerComponent } from "./SourcesExplorer";
|
|
18
|
+
import { QueryExplorerResult } from "./SourcesExplorer";
|
|
25
19
|
import { Loading } from "../Loading";
|
|
26
|
-
import {
|
|
27
|
-
|
|
28
|
-
const modelsApi = new ModelsApi(new Configuration());
|
|
20
|
+
import { ModelExplorer } from "./ModelExplorer";
|
|
21
|
+
import { useModelData } from "./useModelData";
|
|
29
22
|
|
|
30
23
|
interface ModelProps {
|
|
31
24
|
modelPath: string;
|
|
@@ -34,6 +27,7 @@ interface ModelProps {
|
|
|
34
27
|
hideResultIcons?: boolean;
|
|
35
28
|
expandEmbeddings?: boolean;
|
|
36
29
|
hideEmbeddingIcons?: boolean;
|
|
30
|
+
onChange?: (query: QueryExplorerResult) => void;
|
|
37
31
|
}
|
|
38
32
|
|
|
39
33
|
// Note: For this to properly render outside of publisher,
|
|
@@ -42,18 +36,19 @@ interface ModelProps {
|
|
|
42
36
|
|
|
43
37
|
export default function Model({
|
|
44
38
|
modelPath,
|
|
39
|
+
versionId,
|
|
45
40
|
expandResults,
|
|
46
41
|
hideResultIcons,
|
|
47
42
|
expandEmbeddings,
|
|
48
43
|
hideEmbeddingIcons,
|
|
44
|
+
onChange,
|
|
49
45
|
}: ModelProps) {
|
|
50
46
|
const [embeddingExpanded, setEmbeddingExpanded] =
|
|
51
47
|
React.useState<boolean>(false);
|
|
52
48
|
const [highlightedEmbedCode, setHighlightedEmbedCode] =
|
|
53
49
|
React.useState<string>();
|
|
54
|
-
const [selectedTab, setSelectedTab] = React.useState(0);
|
|
55
50
|
|
|
56
|
-
const {
|
|
51
|
+
const { isError, isLoading, error } = useModelData(modelPath, versionId);
|
|
57
52
|
const modelCodeSnippet = getModelCodeSnippet(modelPath);
|
|
58
53
|
useEffect(() => {
|
|
59
54
|
highlight(modelCodeSnippet, "typescript").then((code) => {
|
|
@@ -61,33 +56,13 @@ export default function Model({
|
|
|
61
56
|
});
|
|
62
57
|
}, [embeddingExpanded, modelCodeSnippet]);
|
|
63
58
|
|
|
64
|
-
const { data, isError, isLoading, error } =
|
|
65
|
-
useQueryWithApiError<CompiledModel>({
|
|
66
|
-
queryKey: ["package", projectName, packageName, modelPath, versionId],
|
|
67
|
-
queryFn: async (config) => {
|
|
68
|
-
const response = await modelsApi.getModel(
|
|
69
|
-
projectName,
|
|
70
|
-
packageName,
|
|
71
|
-
modelPath,
|
|
72
|
-
versionId,
|
|
73
|
-
config,
|
|
74
|
-
);
|
|
75
|
-
return response.data;
|
|
76
|
-
},
|
|
77
|
-
});
|
|
78
|
-
|
|
79
59
|
if (isLoading) {
|
|
80
60
|
return <Loading text="Fetching Model..." />;
|
|
81
61
|
}
|
|
82
62
|
|
|
83
63
|
if (isError) {
|
|
84
64
|
console.log("error", error);
|
|
85
|
-
return
|
|
86
|
-
<ApiErrorDisplay
|
|
87
|
-
error={error}
|
|
88
|
-
context={`${packageName} > ${modelPath}`}
|
|
89
|
-
/>
|
|
90
|
-
);
|
|
65
|
+
return <ApiErrorDisplay error={error} context={`Model > ${modelPath}`} />;
|
|
91
66
|
}
|
|
92
67
|
return (
|
|
93
68
|
<StyledCard variant="outlined">
|
|
@@ -95,39 +70,9 @@ export default function Model({
|
|
|
95
70
|
<Stack
|
|
96
71
|
sx={{
|
|
97
72
|
flexDirection: "row",
|
|
98
|
-
justifyContent: "
|
|
73
|
+
justifyContent: "flex-end",
|
|
99
74
|
}}
|
|
100
75
|
>
|
|
101
|
-
{Array.isArray(data.sourceInfos) &&
|
|
102
|
-
data.sourceInfos.length > 0 && (
|
|
103
|
-
<Tabs
|
|
104
|
-
value={selectedTab}
|
|
105
|
-
onChange={(_, newValue) => setSelectedTab(newValue)}
|
|
106
|
-
variant="scrollable"
|
|
107
|
-
scrollButtons="auto"
|
|
108
|
-
sx={{
|
|
109
|
-
borderBottom: 1,
|
|
110
|
-
borderColor: "divider",
|
|
111
|
-
minHeight: 36,
|
|
112
|
-
}}
|
|
113
|
-
>
|
|
114
|
-
{data.sourceInfos.map((source, idx) => {
|
|
115
|
-
let sourceInfo;
|
|
116
|
-
try {
|
|
117
|
-
sourceInfo = JSON.parse(source);
|
|
118
|
-
} catch {
|
|
119
|
-
sourceInfo = { name: String(idx) };
|
|
120
|
-
}
|
|
121
|
-
return (
|
|
122
|
-
<Tab
|
|
123
|
-
key={sourceInfo.name || idx}
|
|
124
|
-
label={sourceInfo.name || `Source ${idx + 1}`}
|
|
125
|
-
sx={{ minHeight: 36 }}
|
|
126
|
-
/>
|
|
127
|
-
);
|
|
128
|
-
})}
|
|
129
|
-
</Tabs>
|
|
130
|
-
)}
|
|
131
76
|
{!hideEmbeddingIcons && (
|
|
132
77
|
<CardActions
|
|
133
78
|
sx={{
|
|
@@ -191,51 +136,15 @@ export default function Model({
|
|
|
191
136
|
</Collapse>
|
|
192
137
|
<Divider />
|
|
193
138
|
</StyledCardContent>
|
|
194
|
-
<
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
data.sourceInfos[selectedTab],
|
|
204
|
-
),
|
|
205
|
-
}}
|
|
206
|
-
/>
|
|
207
|
-
)}
|
|
208
|
-
{data.queries?.length > 0 && (
|
|
209
|
-
<StyledCard
|
|
210
|
-
variant="outlined"
|
|
211
|
-
sx={{ padding: "0px 10px 0px 10px" }}
|
|
212
|
-
>
|
|
213
|
-
<StyledCardContent sx={{ p: "10px" }}>
|
|
214
|
-
<Typography variant="subtitle1">
|
|
215
|
-
Named Queries
|
|
216
|
-
</Typography>
|
|
217
|
-
</StyledCardContent>
|
|
218
|
-
<Stack spacing={1} component="section">
|
|
219
|
-
{data.queries.map((query) => (
|
|
220
|
-
<ModelCell
|
|
221
|
-
key={query.name}
|
|
222
|
-
modelPath={modelPath}
|
|
223
|
-
queryName={query.name}
|
|
224
|
-
expandResult={expandResults}
|
|
225
|
-
hideResultIcon={hideResultIcons}
|
|
226
|
-
expandEmbedding={expandEmbeddings}
|
|
227
|
-
hideEmbeddingIcon={hideEmbeddingIcons}
|
|
228
|
-
noView={true}
|
|
229
|
-
annotations={query.annotations}
|
|
230
|
-
/>
|
|
231
|
-
))}
|
|
232
|
-
</Stack>
|
|
233
|
-
<Box height="10px" />
|
|
234
|
-
</StyledCard>
|
|
235
|
-
)}
|
|
236
|
-
<Box height="5px" />
|
|
237
|
-
</Stack>
|
|
238
|
-
</StyledCardMedia>
|
|
139
|
+
<ModelExplorer
|
|
140
|
+
modelPath={modelPath}
|
|
141
|
+
versionId={versionId}
|
|
142
|
+
expandResults={expandResults}
|
|
143
|
+
hideResultIcons={hideResultIcons}
|
|
144
|
+
expandEmbeddings={expandEmbeddings}
|
|
145
|
+
hideEmbeddingIcons={hideEmbeddingIcons}
|
|
146
|
+
onChange={onChange}
|
|
147
|
+
/>
|
|
239
148
|
</StyledCard>
|
|
240
149
|
);
|
|
241
150
|
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Box, Stack, Typography, Tabs, Tab } from "@mui/material";
|
|
3
|
+
import { StyledCard, StyledCardContent } from "../styles";
|
|
4
|
+
import { ModelCell } from "./ModelCell";
|
|
5
|
+
import {
|
|
6
|
+
QueryExplorerResult,
|
|
7
|
+
SourceExplorerComponent,
|
|
8
|
+
} from "./SourcesExplorer";
|
|
9
|
+
import { ApiErrorDisplay } from "../ApiErrorDisplay";
|
|
10
|
+
import { Loading } from "../Loading";
|
|
11
|
+
import { useModelData } from "./useModelData";
|
|
12
|
+
|
|
13
|
+
export interface ModelExplorerProps {
|
|
14
|
+
modelPath: string;
|
|
15
|
+
versionId?: string;
|
|
16
|
+
/** Display options forwarded to ModelCell */
|
|
17
|
+
expandResults?: boolean;
|
|
18
|
+
hideResultIcons?: boolean;
|
|
19
|
+
expandEmbeddings?: boolean;
|
|
20
|
+
hideEmbeddingIcons?: boolean;
|
|
21
|
+
/** Callback when the explorer changes (e.g. when a query is selected). */
|
|
22
|
+
onChange?: (query: QueryExplorerResult) => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* ModelExplorer renders the main explorer UI for a Malloy model. It shows the
|
|
27
|
+
* selected source (via `SourceExplorerComponent`) along with the list of named
|
|
28
|
+
* queries for that model. This logic was originally embedded inside `Model.tsx`
|
|
29
|
+
* but has been extracted for easier reuse.
|
|
30
|
+
*/
|
|
31
|
+
export function ModelExplorer({
|
|
32
|
+
modelPath,
|
|
33
|
+
versionId,
|
|
34
|
+
expandResults,
|
|
35
|
+
hideResultIcons,
|
|
36
|
+
expandEmbeddings,
|
|
37
|
+
hideEmbeddingIcons,
|
|
38
|
+
onChange,
|
|
39
|
+
}: ModelExplorerProps) {
|
|
40
|
+
const [selectedTab, setSelectedTab] = React.useState(0);
|
|
41
|
+
|
|
42
|
+
const { data, isError, isLoading, error } = useModelData(
|
|
43
|
+
modelPath,
|
|
44
|
+
versionId,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
if (isLoading) {
|
|
48
|
+
return <Loading text="Fetching Model..." />;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (isError) {
|
|
52
|
+
console.log("error", error);
|
|
53
|
+
return (
|
|
54
|
+
<ApiErrorDisplay
|
|
55
|
+
error={error}
|
|
56
|
+
context={`ModelExplorer > ${modelPath}`}
|
|
57
|
+
/>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<Stack spacing={2} component="section">
|
|
63
|
+
{/* Render the tabs for source selection */}
|
|
64
|
+
{Array.isArray(data.sourceInfos) && data.sourceInfos.length > 0 && (
|
|
65
|
+
<Stack sx={{ flexDirection: "row", justifyContent: "flex-start" }}>
|
|
66
|
+
<Tabs
|
|
67
|
+
value={selectedTab}
|
|
68
|
+
onChange={(_, newValue) => setSelectedTab(newValue)}
|
|
69
|
+
variant="scrollable"
|
|
70
|
+
scrollButtons="auto"
|
|
71
|
+
sx={{
|
|
72
|
+
borderBottom: 1,
|
|
73
|
+
borderColor: "divider",
|
|
74
|
+
minHeight: 36,
|
|
75
|
+
}}
|
|
76
|
+
>
|
|
77
|
+
{data.sourceInfos.map((source, idx) => {
|
|
78
|
+
let sourceInfo;
|
|
79
|
+
try {
|
|
80
|
+
sourceInfo = JSON.parse(source);
|
|
81
|
+
} catch {
|
|
82
|
+
sourceInfo = { name: String(idx) };
|
|
83
|
+
}
|
|
84
|
+
return (
|
|
85
|
+
<Tab
|
|
86
|
+
key={sourceInfo.name || idx}
|
|
87
|
+
label={sourceInfo.name || `Source ${idx + 1}`}
|
|
88
|
+
sx={{ minHeight: 36 }}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
})}
|
|
92
|
+
</Tabs>
|
|
93
|
+
</Stack>
|
|
94
|
+
)}
|
|
95
|
+
|
|
96
|
+
{/* Render the selected source info */}
|
|
97
|
+
{Array.isArray(data.sourceInfos) && data.sourceInfos.length > 0 && (
|
|
98
|
+
<SourceExplorerComponent
|
|
99
|
+
sourceAndPath={{
|
|
100
|
+
modelPath,
|
|
101
|
+
sourceInfo: JSON.parse(data.sourceInfos[selectedTab]),
|
|
102
|
+
}}
|
|
103
|
+
onChange={onChange}
|
|
104
|
+
/>
|
|
105
|
+
)}
|
|
106
|
+
|
|
107
|
+
{/* Render the named queries */}
|
|
108
|
+
{data.queries?.length > 0 && (
|
|
109
|
+
<StyledCard
|
|
110
|
+
variant="outlined"
|
|
111
|
+
sx={{ padding: "0px 10px 0px 10px" }}
|
|
112
|
+
>
|
|
113
|
+
<StyledCardContent sx={{ p: "10px" }}>
|
|
114
|
+
<Typography variant="subtitle1">Named Queries</Typography>
|
|
115
|
+
</StyledCardContent>
|
|
116
|
+
|
|
117
|
+
<Stack spacing={1} component="section">
|
|
118
|
+
{data.queries.map((query) => (
|
|
119
|
+
<ModelCell
|
|
120
|
+
key={query.name}
|
|
121
|
+
modelPath={modelPath}
|
|
122
|
+
queryName={query.name}
|
|
123
|
+
expandResult={expandResults}
|
|
124
|
+
hideResultIcon={hideResultIcons}
|
|
125
|
+
expandEmbedding={expandEmbeddings}
|
|
126
|
+
hideEmbeddingIcon={hideEmbeddingIcons}
|
|
127
|
+
noView={true}
|
|
128
|
+
annotations={query.annotations}
|
|
129
|
+
/>
|
|
130
|
+
))}
|
|
131
|
+
</Stack>
|
|
132
|
+
<Box height="10px" />
|
|
133
|
+
</StyledCard>
|
|
134
|
+
)}
|
|
135
|
+
<Box height="5px" />
|
|
136
|
+
</Stack>
|
|
137
|
+
);
|
|
138
|
+
}
|