@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/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.118",
4
+ "version": "0.0.119",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs.js",
7
7
  "module": "dist/index.es.js",
@@ -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"
@@ -441,7 +441,7 @@ function ProjectCard({
441
441
  <Typography
442
442
  variant="body2"
443
443
  color="text.secondary"
444
- sx={{ mb: 2 }}
444
+ sx={{ mb: 2, minHeight: "60px" }}
445
445
  >
446
446
  {getProjectDescription(project.readme)}
447
447
  </Typography>
@@ -7,7 +7,7 @@ import {
7
7
  Tooltip,
8
8
  Typography,
9
9
  } from "@mui/material";
10
- import SearchIcon from "@mui/icons-material/Search";
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
- <SearchIcon
144
- sx={{ fontSize: "18px", color: "#666666" }}
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, Button, Stack } from "@mui/material";
2
- import { styled } from "@mui/material/styles";
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
- display: "flex",
15
- flexWrap: "wrap",
16
- gap: theme.spacing(1),
17
- borderBottom: "1px solid #f0f0f0",
18
- minHeight: 48,
19
- paddingBottom: "8px",
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
- ({ theme, selected }) => ({
24
- minHeight: 32,
25
- padding: theme.spacing(0.75, 2),
26
- borderRadius: "6px",
27
- background: selected ? "#f8f9fa" : "transparent",
28
- color: selected ? "#495057" : "#666666",
29
- fontWeight: selected ? 600 : 500,
30
- border: selected ? "1px solid #e9ecef" : "1px solid transparent",
31
- boxShadow: "none",
32
- textTransform: "none",
33
- fontSize: "15px",
34
- "&:hover": {
35
- background: selected ? "#f8f9fa" : "#fafafa",
36
- border: selected ? "1px solid #e9ecef" : "1px solid #f0f0f0",
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
- effectiveData.sourceInfos.length > 0 && (
118
- <MultiRowTabBar>
119
- {effectiveData.sourceInfos.map((source, idx) => {
120
- let sourceInfo;
121
- try {
122
- sourceInfo = JSON.parse(source);
123
- } catch {
124
- sourceInfo = { name: String(idx) };
125
- }
126
- return (
127
- <MultiRowTab
128
- key={sourceInfo.name || idx}
129
- selected={selectedTab === idx}
130
- onClick={() => {
131
- setSelectedTab(idx);
132
- if (onSourceChange) {
133
- onSourceChange(idx);
134
- }
135
- }}
136
- >
137
- {sourceInfo.name || `Source ${idx + 1}`}
138
- </MultiRowTab>
139
- );
140
- })}
141
- </MultiRowTabBar>
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 += `${key}/`;
225
+ const childPath = path + key + "/";
226
226
  treeViewItems.push({
227
- id: path,
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
- path,
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: projectName } = parseResourceUri(resourceUri);
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 && data.data?.readme && (
37
+ {isSuccess && readmeContent && (
31
38
  <PackageCard>
32
39
  <PackageCardContent>
33
40
  <PackageSectionTitle>Readme</PackageSectionTitle>
34
- <Box sx={{ mt: 1 }}>
35
- <Markdown>{data.data.readme}</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
@@ -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 = decodeURI(pathParts[i + 1]) || undefined;
26
+ parsedResource.modelPath =
27
+ decodeURI(pathParts.slice(i + 1).join("/")) || undefined;
27
28
  }
28
29
  }
29
30
 
@@ -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[0] || cleanText;
18
+ const firstParagraph = paragraphs[1] || cleanText;
19
19
 
20
20
  // Limit to ~120 characters
21
21
  if (firstParagraph.length <= 120) {