@malloy-publisher/sdk 0.0.118 → 0.0.119
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/index.cjs.js +118 -118
- package/dist/index.es.js +39884 -36907
- package/package.json +1 -1
- package/src/components/Home/DeleteProjectDialog.tsx +10 -0
- package/src/components/Home/Home.tsx +1 -1
- package/src/components/Model/Model.tsx +10 -4
- package/src/components/Model/ModelExplorer.tsx +91 -56
- package/src/components/Package/FileTreeView.tsx +3 -3
- package/src/components/Project/About.tsx +63 -8
- package/src/components/Project/DeletePackageDialog.tsx +10 -0
- package/src/utils/formatting.ts +2 -1
- package/src/utils/parsing.ts +1 -1
package/package.json
CHANGED
|
@@ -84,6 +84,16 @@ export default function DeleteProjectDialog({
|
|
|
84
84
|
</Typography>
|
|
85
85
|
</DialogContent>
|
|
86
86
|
<DialogActions>
|
|
87
|
+
<Button
|
|
88
|
+
variant="outlined"
|
|
89
|
+
onClick={handleClose}
|
|
90
|
+
style={{
|
|
91
|
+
borderColor: "#14b3cb",
|
|
92
|
+
color: "#14b3cb",
|
|
93
|
+
}}
|
|
94
|
+
>
|
|
95
|
+
Cancel
|
|
96
|
+
</Button>
|
|
87
97
|
<Button
|
|
88
98
|
loading={deleteProject.isPending}
|
|
89
99
|
variant="contained"
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
Tooltip,
|
|
8
8
|
Typography,
|
|
9
9
|
} from "@mui/material";
|
|
10
|
-
import
|
|
10
|
+
import ZoomOutMapIcon from "@mui/icons-material/ZoomOutMap";
|
|
11
11
|
import ShareIcon from "@mui/icons-material/Share";
|
|
12
12
|
import "@malloydata/malloy-explorer/styles.css";
|
|
13
13
|
import { QueryExplorerResult } from "./SourcesExplorer";
|
|
@@ -140,9 +140,15 @@ export default function Model({ onChange, resourceUri }: ModelProps) {
|
|
|
140
140
|
}}
|
|
141
141
|
onClick={() => setDialogOpen(true)}
|
|
142
142
|
>
|
|
143
|
-
<
|
|
144
|
-
|
|
145
|
-
|
|
143
|
+
<Tooltip title="Expanded view">
|
|
144
|
+
<ZoomOutMapIcon
|
|
145
|
+
sx={{
|
|
146
|
+
fontSize: "18px",
|
|
147
|
+
color: "#666666",
|
|
148
|
+
marginBottom: "5px",
|
|
149
|
+
}}
|
|
150
|
+
/>
|
|
151
|
+
</Tooltip>
|
|
146
152
|
</IconButton>
|
|
147
153
|
</Stack>
|
|
148
154
|
)}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Box,
|
|
2
|
-
import
|
|
1
|
+
import { Box, Stack } from "@mui/material";
|
|
2
|
+
import Autocomplete from "@mui/material/Autocomplete";
|
|
3
|
+
import TextField from "@mui/material/TextField";
|
|
3
4
|
import React from "react";
|
|
4
5
|
import { CompiledModel } from "../../client";
|
|
5
6
|
import { parseResourceUri } from "../../utils/formatting";
|
|
@@ -10,33 +11,33 @@ import { QueryExplorerResult, SourcesExplorer } from "./SourcesExplorer";
|
|
|
10
11
|
import { useModelData } from "./useModelData";
|
|
11
12
|
|
|
12
13
|
// Add a styled component for the multi-row tab bar
|
|
13
|
-
const MultiRowTabBar = styled(Box)(({ theme }) => ({
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}));
|
|
14
|
+
// const MultiRowTabBar = styled(Box)(({ theme }) => ({
|
|
15
|
+
// display: "flex",
|
|
16
|
+
// flexWrap: "wrap",
|
|
17
|
+
// gap: theme.spacing(1),
|
|
18
|
+
// borderBottom: "1px solid #f0f0f0",
|
|
19
|
+
// minHeight: 48,
|
|
20
|
+
// paddingBottom: "8px",
|
|
21
|
+
// }));
|
|
21
22
|
|
|
22
|
-
const MultiRowTab = styled(Button)<{ selected?: boolean }>(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
);
|
|
23
|
+
// const MultiRowTab = styled(Button)<{ selected?: boolean }>(
|
|
24
|
+
// ({ theme, selected }) => ({
|
|
25
|
+
// minHeight: 32,
|
|
26
|
+
// padding: theme.spacing(0.75, 2),
|
|
27
|
+
// borderRadius: "6px",
|
|
28
|
+
// background: selected ? "#f8f9fa" : "transparent",
|
|
29
|
+
// color: selected ? "#495057" : "#666666",
|
|
30
|
+
// fontWeight: selected ? 600 : 500,
|
|
31
|
+
// border: selected ? "1px solid #e9ecef" : "1px solid transparent",
|
|
32
|
+
// boxShadow: "none",
|
|
33
|
+
// textTransform: "none",
|
|
34
|
+
// fontSize: "15px",
|
|
35
|
+
// "&:hover": {
|
|
36
|
+
// background: selected ? "#f8f9fa" : "#fafafa",
|
|
37
|
+
// border: selected ? "1px solid #e9ecef" : "1px solid #f0f0f0",
|
|
38
|
+
// },
|
|
39
|
+
// }),
|
|
40
|
+
// );
|
|
40
41
|
|
|
41
42
|
export interface ModelExplorerProps {
|
|
42
43
|
data?: CompiledModel;
|
|
@@ -103,43 +104,77 @@ export function ModelExplorer({
|
|
|
103
104
|
return <Loading text="Loading..." />;
|
|
104
105
|
}
|
|
105
106
|
|
|
107
|
+
const sourceOptions = (effectiveData?.sourceInfos || []).map(
|
|
108
|
+
(source, idx) => {
|
|
109
|
+
try {
|
|
110
|
+
const parsed = JSON.parse(source);
|
|
111
|
+
return parsed?.name || `Source ${idx + 1}`;
|
|
112
|
+
} catch {
|
|
113
|
+
return `Source ${idx + 1}`;
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
);
|
|
117
|
+
const selectedName = sourceOptions[selectedTab] || "";
|
|
118
|
+
|
|
106
119
|
return (
|
|
107
120
|
<StyledCard variant="outlined">
|
|
108
121
|
<StyledCardContent>
|
|
109
122
|
<Stack
|
|
110
123
|
sx={{
|
|
111
124
|
flexDirection: "row",
|
|
112
|
-
justifyContent: "space-between",
|
|
113
125
|
}}
|
|
114
126
|
>
|
|
115
127
|
{/* Render the tabs for source selection */}
|
|
116
|
-
{Array.isArray(effectiveData.sourceInfos) &&
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
128
|
+
{/* {Array.isArray(effectiveData.sourceInfos) &&
|
|
129
|
+
effectiveData.sourceInfos.length > 0 && (
|
|
130
|
+
<MultiRowTabBar>
|
|
131
|
+
{effectiveData.sourceInfos.map((source, idx) => {
|
|
132
|
+
let sourceInfo;
|
|
133
|
+
try {
|
|
134
|
+
sourceInfo = JSON.parse(source);
|
|
135
|
+
} catch {
|
|
136
|
+
sourceInfo = { name: String(idx) };
|
|
137
|
+
}
|
|
138
|
+
return (
|
|
139
|
+
<MultiRowTab
|
|
140
|
+
key={sourceInfo.name || idx}
|
|
141
|
+
selected={selectedTab === idx}
|
|
142
|
+
onClick={() => {
|
|
143
|
+
setSelectedTab(idx);
|
|
144
|
+
if (onSourceChange) {
|
|
145
|
+
onSourceChange(idx);
|
|
146
|
+
}
|
|
147
|
+
}}
|
|
148
|
+
>
|
|
149
|
+
{sourceInfo.name || `Source ${idx + 1}`}
|
|
150
|
+
</MultiRowTab>
|
|
151
|
+
);
|
|
152
|
+
})}
|
|
153
|
+
</MultiRowTabBar>
|
|
154
|
+
)} */}
|
|
155
|
+
<Autocomplete
|
|
156
|
+
size="small"
|
|
157
|
+
id="size-small-standard"
|
|
158
|
+
disablePortal
|
|
159
|
+
options={sourceOptions}
|
|
160
|
+
value={selectedName}
|
|
161
|
+
onChange={(_e, newValue) => {
|
|
162
|
+
if (!newValue) return;
|
|
163
|
+
|
|
164
|
+
const idx = sourceOptions.indexOf(newValue);
|
|
165
|
+
if (idx >= 0) {
|
|
166
|
+
setSelectedTab(idx);
|
|
167
|
+
onSourceChange?.(idx);
|
|
168
|
+
}
|
|
169
|
+
}}
|
|
170
|
+
renderInput={(params) => <TextField {...params} />}
|
|
171
|
+
style={{
|
|
172
|
+
width: 350,
|
|
173
|
+
marginTop: "3px",
|
|
174
|
+
marginLeft: "6px",
|
|
175
|
+
marginBottom: "8px",
|
|
176
|
+
}}
|
|
177
|
+
/>
|
|
143
178
|
</Stack>
|
|
144
179
|
</StyledCardContent>
|
|
145
180
|
<StyledCardMedia>
|
|
@@ -222,16 +222,16 @@ function getTreeViewRecursive(
|
|
|
222
222
|
});
|
|
223
223
|
} else {
|
|
224
224
|
// This is a directory.
|
|
225
|
-
path
|
|
225
|
+
const childPath = path + key + "/";
|
|
226
226
|
treeViewItems.push({
|
|
227
|
-
id:
|
|
227
|
+
id: childPath,
|
|
228
228
|
label: key,
|
|
229
229
|
fileType: "directory",
|
|
230
230
|
selectable: true,
|
|
231
231
|
link: undefined,
|
|
232
232
|
children: getTreeViewRecursive(
|
|
233
233
|
value as Map<string, unknown>,
|
|
234
|
-
|
|
234
|
+
childPath,
|
|
235
235
|
onClickNode,
|
|
236
236
|
),
|
|
237
237
|
});
|
|
@@ -1,38 +1,93 @@
|
|
|
1
|
-
import { Box } from "@mui/material";
|
|
1
|
+
import { Box, Button } from "@mui/material";
|
|
2
2
|
import Markdown from "markdown-to-jsx";
|
|
3
|
+
import { useState } from "react";
|
|
3
4
|
import { useQueryWithApiError } from "../../hooks/useQueryWithApiError";
|
|
5
|
+
import { parseResourceUri } from "../../utils/formatting";
|
|
4
6
|
import { ApiErrorDisplay } from "../ApiErrorDisplay";
|
|
5
7
|
import { Loading } from "../Loading";
|
|
8
|
+
import { useServer } from "../ServerProvider";
|
|
6
9
|
import {
|
|
7
10
|
PackageCard,
|
|
8
11
|
PackageCardContent,
|
|
9
12
|
PackageSectionTitle,
|
|
10
13
|
} from "../styles";
|
|
11
|
-
import { parseResourceUri } from "../../utils/formatting";
|
|
12
|
-
import { useServer } from "../ServerProvider";
|
|
13
|
-
|
|
14
14
|
interface AboutProps {
|
|
15
15
|
resourceUri: string;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export default function About({ resourceUri }: AboutProps) {
|
|
19
|
-
const { projectName
|
|
19
|
+
const { projectName } = parseResourceUri(resourceUri);
|
|
20
20
|
const { apiClients } = useServer();
|
|
21
|
+
const [expanded, setExpanded] = useState(false);
|
|
22
|
+
const wordLimit = 90;
|
|
21
23
|
|
|
22
24
|
const { data, isSuccess, isError, error } = useQueryWithApiError({
|
|
23
25
|
queryKey: ["about", projectName],
|
|
24
26
|
queryFn: () => apiClients.projects.getProject(projectName, false),
|
|
25
27
|
});
|
|
26
28
|
|
|
29
|
+
const readmeContent = data?.data?.readme || "";
|
|
30
|
+
const words = readmeContent.split(/\s+/);
|
|
31
|
+
const shouldTruncate = words.length > wordLimit;
|
|
32
|
+
const preview = words.slice(0, wordLimit).join(" ");
|
|
33
|
+
|
|
27
34
|
return (
|
|
28
35
|
<>
|
|
29
36
|
{!isSuccess && !isError && <Loading text="Fetching About..." />}
|
|
30
|
-
{isSuccess &&
|
|
37
|
+
{isSuccess && readmeContent && (
|
|
31
38
|
<PackageCard>
|
|
32
39
|
<PackageCardContent>
|
|
33
40
|
<PackageSectionTitle>Readme</PackageSectionTitle>
|
|
34
|
-
<Box sx={{ mt: 1 }}>
|
|
35
|
-
<Markdown
|
|
41
|
+
<Box sx={{ mt: 1, fontSize: "0.875rem", lineHeight: 1.6 }}>
|
|
42
|
+
<Markdown
|
|
43
|
+
options={{
|
|
44
|
+
overrides: {
|
|
45
|
+
h1: {
|
|
46
|
+
component: "p",
|
|
47
|
+
props: {
|
|
48
|
+
style: {
|
|
49
|
+
fontSize: "inherit",
|
|
50
|
+
fontWeight: "italic",
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
h2: {
|
|
55
|
+
component: "p",
|
|
56
|
+
props: {
|
|
57
|
+
style: {
|
|
58
|
+
fontSize: "inherit",
|
|
59
|
+
fontWeight: "italic",
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
h3: {
|
|
64
|
+
component: "p",
|
|
65
|
+
props: {
|
|
66
|
+
style: {
|
|
67
|
+
fontSize: "inherit",
|
|
68
|
+
fontWeight: "italic",
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
}}
|
|
74
|
+
>
|
|
75
|
+
{expanded || !shouldTruncate ? readmeContent : preview}
|
|
76
|
+
</Markdown>
|
|
77
|
+
|
|
78
|
+
{shouldTruncate && (
|
|
79
|
+
<Box sx={{ mt: 1 }}>
|
|
80
|
+
{" "}
|
|
81
|
+
{/* separate line */}
|
|
82
|
+
<Button
|
|
83
|
+
variant="text"
|
|
84
|
+
size="small"
|
|
85
|
+
onClick={() => setExpanded(!expanded)}
|
|
86
|
+
>
|
|
87
|
+
{expanded ? "Read less" : "Read more"}
|
|
88
|
+
</Button>
|
|
89
|
+
</Box>
|
|
90
|
+
)}
|
|
36
91
|
</Box>
|
|
37
92
|
</PackageCardContent>
|
|
38
93
|
</PackageCard>
|
|
@@ -86,6 +86,16 @@ export default function DeletePackageDialog({
|
|
|
86
86
|
</Typography>
|
|
87
87
|
</DialogContent>
|
|
88
88
|
<DialogActions>
|
|
89
|
+
<Button
|
|
90
|
+
variant="outlined"
|
|
91
|
+
onClick={handleClose}
|
|
92
|
+
style={{
|
|
93
|
+
borderColor: "#14b3cb",
|
|
94
|
+
color: "#14b3cb",
|
|
95
|
+
}}
|
|
96
|
+
>
|
|
97
|
+
Cancel
|
|
98
|
+
</Button>
|
|
89
99
|
<Button
|
|
90
100
|
variant="contained"
|
|
91
101
|
autoFocus
|
package/src/utils/formatting.ts
CHANGED
|
@@ -23,7 +23,8 @@ export const parseResourceUri = (resourceUri: string) => {
|
|
|
23
23
|
parsedResource.connectionName =
|
|
24
24
|
decodeURI(pathParts[i + 1]) || undefined;
|
|
25
25
|
} else if (part === "models") {
|
|
26
|
-
parsedResource.modelPath =
|
|
26
|
+
parsedResource.modelPath =
|
|
27
|
+
decodeURI(pathParts.slice(i + 1).join("/")) || undefined;
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
30
|
|
package/src/utils/parsing.ts
CHANGED
|
@@ -15,7 +15,7 @@ export const getProjectDescription = (readme: string | undefined): string => {
|
|
|
15
15
|
|
|
16
16
|
// Get first paragraph (split by double newlines)
|
|
17
17
|
const paragraphs = cleanText.split(/\n\s*\n/);
|
|
18
|
-
const firstParagraph = paragraphs[
|
|
18
|
+
const firstParagraph = paragraphs[1] || cleanText;
|
|
19
19
|
|
|
20
20
|
// Limit to ~120 characters
|
|
21
21
|
if (firstParagraph.length <= 120) {
|