From 0e0de1b9efb16e94c84852f6cccccb2014624a6c Mon Sep 17 00:00:00 2001 From: Alexandre Neuwald CTW Date: Mon, 15 Apr 2024 14:26:45 +0100 Subject: [PATCH 1/8] Fix lint:ci problems --- .../LayoutBrowser/UnsavedChangesPrompt.tsx | 8 +++++-- packages/studio-base/src/hooks/usePrompt.tsx | 21 +++++++++++++++---- .../LayoutManager/NamespacedLayoutStorage.ts | 10 +++++---- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/packages/studio-base/src/components/LayoutBrowser/UnsavedChangesPrompt.tsx b/packages/studio-base/src/components/LayoutBrowser/UnsavedChangesPrompt.tsx index 0d661ff173..8c0f4022ed 100644 --- a/packages/studio-base/src/components/LayoutBrowser/UnsavedChangesPrompt.tsx +++ b/packages/studio-base/src/components/LayoutBrowser/UnsavedChangesPrompt.tsx @@ -161,10 +161,14 @@ export function useUnsavedChangesPrompt(): { const [isOnline, setIsOnline] = useState(layoutManager.isOnline); useLayoutEffect(() => { - const onlineListener = () => { setIsOnline(layoutManager.isOnline); }; + const onlineListener = () => { + setIsOnline(layoutManager.isOnline); + }; onlineListener(); layoutManager.on("onlinechange", onlineListener); - return () => { layoutManager.off("onlinechange", onlineListener); }; + return () => { + layoutManager.off("onlinechange", onlineListener); + }; }, [layoutManager]); const unsavedChangesPrompt = useMemo(() => { diff --git a/packages/studio-base/src/hooks/usePrompt.tsx b/packages/studio-base/src/hooks/usePrompt.tsx index a3211d256c..024e672ecc 100644 --- a/packages/studio-base/src/hooks/usePrompt.tsx +++ b/packages/studio-base/src/hooks/usePrompt.tsx @@ -77,11 +77,20 @@ function ModalPrompt({ // Ensure we still call onComplete(undefined) when the component unmounts, if it hasn't been // called already useEffect(() => { - return () => { onComplete(undefined); }; + return () => { + onComplete(undefined); + }; }, [onComplete]); return ( - { onComplete(undefined); }}> + { + onComplete(undefined); + }} + >
@@ -105,7 +114,9 @@ function ModalPrompt({ FormHelperTextProps={{ variant: "standard", }} - onChange={(event) => { setValue(event.target.value); }} + onChange={(event) => { + setValue(event.target.value); + }} /> @@ -113,7 +124,9 @@ function ModalPrompt({ color="inherit" size="large" variant="outlined" - onClick={() => { onComplete(undefined); }} + onClick={() => { + onComplete(undefined); + }} > Cancel diff --git a/packages/studio-base/src/services/LayoutManager/NamespacedLayoutStorage.ts b/packages/studio-base/src/services/LayoutManager/NamespacedLayoutStorage.ts index b35aba3a5d..9ee83699f5 100644 --- a/packages/studio-base/src/services/LayoutManager/NamespacedLayoutStorage.ts +++ b/packages/studio-base/src/services/LayoutManager/NamespacedLayoutStorage.ts @@ -23,9 +23,9 @@ export class NamespacedLayoutStorage { ) { this.#migration = (async function () { if (migrateUnnamespacedLayouts) { - await storage - .migrateUnnamespacedLayouts?.(namespace) - .catch((error) => { log.error("Migration failed:", error); }); + await storage.migrateUnnamespacedLayouts?.(namespace).catch((error) => { + log.error("Migration failed:", error); + }); } if (importFromNamespace != undefined) { @@ -34,7 +34,9 @@ export class NamespacedLayoutStorage { fromNamespace: importFromNamespace, toNamespace: namespace, }) - .catch((error) => { log.error("Import failed:", error); }); + .catch((error) => { + log.error("Import failed:", error); + }); } })(); } From cb8767150295dce785de9aeb76c79b97db1cea08 Mon Sep 17 00:00:00 2001 From: Alexandre Neuwald CTW Date: Mon, 15 Apr 2024 14:50:04 +0100 Subject: [PATCH 2/8] Fix more lint errors on non-used-exports --- .../AccountSettingsSidebar/AccountInfo.tsx | 2 +- .../DataSourceSidebar/EventView.tsx | 152 ------------ .../components/Sidebars/Sidebar.stories.tsx | 185 -------------- .../src/components/Sidebars/Sidebar.tsx | 165 ------------- .../src/components/Sidebars/Sidebars.tsx | 228 ------------------ .../src/context/NativeAppMenuContext.ts | 6 +- .../src/context/NativeWindowContext.ts | 6 +- .../src/context/RemoteLayoutStorageContext.ts | 3 - .../src/context/UserNodeStateContext.tsx | 5 - .../src/context/UserScriptStateContext.tsx | 8 +- .../players/TopicAliasingPlayer/aliasing.ts | 49 ---- .../typescript/templates/index.ts | 40 --- .../typescript/userUtils/markers.ts | 118 --------- .../typescript/userUtils/pointClouds.ts | 51 ---- .../typescript/userUtils/time.ts | 5 - .../transformerWorker/typescript/ros.ts | 2 +- .../src/players/UserScriptPlayer/types.ts | 40 +-- .../src/util/sharedStyleConstants.ts | 19 -- 18 files changed, 7 insertions(+), 1077 deletions(-) delete mode 100644 packages/studio-base/src/components/DataSourceSidebar/EventView.tsx delete mode 100644 packages/studio-base/src/components/Sidebars/Sidebar.stories.tsx delete mode 100644 packages/studio-base/src/components/Sidebars/Sidebar.tsx delete mode 100644 packages/studio-base/src/components/Sidebars/Sidebars.tsx delete mode 100644 packages/studio-base/src/players/UserNodePlayer/nodeTransformerWorker/typescript/templates/index.ts delete mode 100644 packages/studio-base/src/util/sharedStyleConstants.ts diff --git a/packages/studio-base/src/components/AccountSettingsSidebar/AccountInfo.tsx b/packages/studio-base/src/components/AccountSettingsSidebar/AccountInfo.tsx index 5c24627f3e..bf489448ef 100644 --- a/packages/studio-base/src/components/AccountSettingsSidebar/AccountInfo.tsx +++ b/packages/studio-base/src/components/AccountSettingsSidebar/AccountInfo.tsx @@ -16,7 +16,7 @@ import { useConfirm } from "@foxglove/studio-base/hooks/useConfirm"; const log = Logger.getLogger(__filename); -export const AVATAR_ICON_SIZE = 42; +const AVATAR_ICON_SIZE = 42; const useStyles = makeStyles()((theme) => ({ icon: { diff --git a/packages/studio-base/src/components/DataSourceSidebar/EventView.tsx b/packages/studio-base/src/components/DataSourceSidebar/EventView.tsx deleted file mode 100644 index 79210ca94e..0000000000 --- a/packages/studio-base/src/components/DataSourceSidebar/EventView.tsx +++ /dev/null @@ -1,152 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/ - -import { alpha } from "@mui/material"; -import * as _ from "lodash-es"; -import { Fragment } from "react"; -import { makeStyles } from "tss-react/mui"; - -import { HighlightedText } from "@foxglove/studio-base/components/HighlightedText"; -import { - TimelinePositionedEvent, - DataSourceEvent, -} from "@foxglove/studio-base/context/EventsContext"; - -const useStyles = makeStyles()( - (theme, _params, classes) => ({ - spacer: { - cursor: "default", - height: theme.spacing(1), - gridColumn: "span 2", - }, - event: { - display: "contents", - cursor: "pointer", - "&:hover": { - [`.${classes.eventMetadata}`]: { - backgroundColor: alpha(theme.palette.info.main, theme.palette.action.hoverOpacity), - borderColor: theme.palette.info.main, - }, - }, - }, - eventSelected: { - [`.${classes.eventMetadata}`]: { - backgroundColor: alpha(theme.palette.info.main, theme.palette.action.activatedOpacity), - borderColor: theme.palette.info.main, - boxShadow: `0 0 0 1px ${theme.palette.info.main}`, - }, - }, - eventHovered: { - [`.${classes.eventMetadata}`]: { - backgroundColor: alpha(theme.palette.info.main, theme.palette.action.hoverOpacity), - borderColor: theme.palette.info.main, - }, - }, - eventMetadata: { - padding: theme.spacing(1), - backgroundColor: theme.palette.background.default, - borderRight: `1px solid ${theme.palette.divider}`, - borderBottom: `1px solid ${theme.palette.divider}`, - - "&:nth-of-type(odd)": { - borderLeft: `1px solid ${theme.palette.divider}`, - }, - "&:first-of-type": { - borderTop: `1px solid ${theme.palette.divider}`, - borderTopLeftRadius: theme.shape.borderRadius, - }, - "&:nth-of-type(2)": { - borderTop: `1px solid ${theme.palette.divider}`, - borderTopRightRadius: theme.shape.borderRadius, - }, - "&:nth-last-of-type(2)": { - borderBottomRightRadius: theme.shape.borderRadius, - }, - "&:nth-last-of-type(3)": { - borderBottomLeftRadius: theme.shape.borderRadius, - }, - }, - }), -); - -function formatEventDuration(event: DataSourceEvent) { - if (event.durationNanos === "0") { - // instant - return "-"; - } - - if (!event.durationNanos) { - return ""; - } - - const intDuration = BigInt(event.durationNanos); - - if (intDuration >= BigInt(1e9)) { - return `${Number(intDuration / BigInt(1e9))}s`; - } - - if (intDuration >= BigInt(1e6)) { - return `${Number(intDuration / BigInt(1e6))}ms`; - } - - if (intDuration >= BigInt(1e3)) { - return `${Number(intDuration / BigInt(1e3))}µs`; - } - - return `${event.durationNanos}ns`; -} - -function EventViewComponent(params: { - event: TimelinePositionedEvent; - filter: string; - formattedTime: string; - isHovered: boolean; - isSelected: boolean; - onClick: (event: TimelinePositionedEvent) => void; - onHoverStart: (event: TimelinePositionedEvent) => void; - onHoverEnd: (event: TimelinePositionedEvent) => void; -}): JSX.Element { - const { event, filter, formattedTime, isHovered, isSelected, onClick, onHoverStart, onHoverEnd } = - params; - const { classes, cx } = useStyles(); - - const fields = _.compact([ - ["timestamp", formattedTime], - Number(event.event.durationNanos) > 0 && ["duration", formatEventDuration(event.event)], - ...Object.entries(event.event.metadata), - ]); - - return ( -
{ - onClick(event); - }} - onMouseEnter={() => { - onHoverStart(event); - }} - onMouseLeave={() => { - onHoverEnd(event); - }} - > - {fields.map(([key, value]) => ( - -
- -
-
- -
-
- ))} -
-
- ); -} - -export const EventView = React.memo(EventViewComponent); diff --git a/packages/studio-base/src/components/Sidebars/Sidebar.stories.tsx b/packages/studio-base/src/components/Sidebars/Sidebar.stories.tsx deleted file mode 100644 index 88774b305f..0000000000 --- a/packages/studio-base/src/components/Sidebars/Sidebar.stories.tsx +++ /dev/null @@ -1,185 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/ - -import { StoryObj } from "@storybook/react"; -import { screen, userEvent } from "@storybook/testing-library"; -import { PropsWithChildren, useState } from "react"; -import { DndProvider } from "react-dnd"; -import { HTML5Backend } from "react-dnd-html5-backend"; - -import Stack from "@foxglove/studio-base/components/Stack"; - -import { SidebarItem, Sidebars } from "."; - -export default { - title: "components/NewSidebar", - component: Sidebars, - parameters: { - colorScheme: "both-column", - }, -}; - -const longText = - "Max Polzin is a PhD Student at École Polytechnique Fédérale de Lausanne (EPFL)’s Computational Robotics Design and Fabrication Lab, where he focuses on developing novel robots with real-world applications in extreme environments like boreal forests, mountainous slopes or polar glaciers. In addition to his research, Max is dedicated to making the development of advanced robotics more accessible and to promoting robotics software engineering best practices in academic environments. Tell us a little bit about yourself, and how you first became interested in robotics. After finishing my undergraduate degree in telecommunications, I wanted to build things that moved for a change, and thus enrolled in a robotics master program at ETH Zurich . While I learned a lot about the theory of robotics – like how robots require sensors, processors and actuators to function in the world – what I didn’t learn was how to turn an idea, a research concept, or an academic prototype into a product. To learn what it takes to bring a robot from idea to reality, I joined Seervision, a young ETH Zurich spin-off dedicated to developing and selling autonomous cameras. We started as a team of three fresh graduates, with little knowledge about how to make and sell robots. It is an understatement when I say we underestimated how many lessons lay ahead of us. Over the next several years, we got a crash course on real-world robotics development. We learned how to modify and adjust our robots to meet our customer’s needs. As our team grew, we also had to find the right developer tools and adopt modern software development best practices to help us continue building robots at scale. After my experience at Seervision, returning to academia and embarking on a PhD journey was not an easy choice to make. However, in the end, I chose to join EPFL’s newly established CREATE Lab, where I could merge my passions for robots and the outdoors and focus on developing novel robots with applications in extreme environments. What robotics projects are you working on now? As a PhD student, I am dedicated to enhancing the capabilities of robots in unpredictable and complex natural environments. By developing versatile robotic systems equipped with state-of-the-art sensors, advanced control algorithms, and smart design, I aim to tackle tasks that have previously been beyond the reach of both robots and humans. Max Polzin The robot that you see above was built for the project ”Modelling Spatio-temporal Transformations of Glacial Moulins” in collaboration with the Swiss Polar Institute. It rappels itself into moulins – large open holes on a glacier, formed by surface meltwater draining through the ice sheet to the glacier’s rock bed – to explore their geometry. By capturing these measurements, we can foresee the influence of increased surface water melting, due to a changing climate, on the flow velocity of the worlds’ glaciers. Particularly in polar regions, where temperatures rise fastest, an increased glacial flow and the associated freshwater discharge will significantly contribute to the global sea level rise. Our initial evaluation of the robot took place during a four-day field trip to Mont Blanc in the French Alps, where we studied the largest moulin on the Mer de Glace glacier. I was joined by my PhD colleagues from the CREATE Lab, Kai Junge and Francesco Stella, our colleague Steffen Kirchgeorg from the Environmental Robotics Lab, as well as my friend, photographer and mountain guide, Arion Schuler. The goal of this trip was to test the robot in a nearby moulin before venturing to more remote locations, such as those on Greenland’s ice sheet. map of Mer de Glace glacierMap data from the Swiss government. What does a day in the life on this project look like? When we’re not out in the field, I am absorbed in the exciting work of designing and creating robots in our lab at EPFL. This includes developing algorithms to control a robot’s movements and testing their usability and robustness for deployment in extreme environments. As a PhD student, I also have the opportunity to share my lab findings with the wider robotics community through scientific publications. As much as I enjoy this development work, field trip days are definitely the most exciting parts of our project. On a typical field trip, we get up at 7 in the morning. My teammates Kai and Steffen go to get breakfast, while Francesco, Arion, and I check our safety equipment and the robot. packing equipment We pack generator, tent, poles, and ropes into our backpacks, then leave for the first cable car out of Montenvers Station, by the Mer de Glace glacier at Mont Blanc. riding the cable car On our ride up to the glacier, we take some time to review our plans and experiments for the day ahead. Upon arrival at our stop, we embark on a 1.5-hour trek through snowy landscapes to the largest moulin on the Mer de Glace glacier."; - -function Wrapper({ children }: PropsWithChildren): JSX.Element { - return ( - - {children} - - ); -} - -const A = () => A; -const B = () => B; -const C = () => <>{longText}; - -const X = () => X; -const Y = () => Y; -const Z = () => <>{longText}; - -type LeftKey = "a" | "b" | "c"; -type RightKey = "x" | "y" | "z"; - -const LEFT_ITEMS = new Map([ - ["a", { title: "A", component: A }], - ["b", { title: "B", component: B }], - ["c", { title: "C", component: C }], -]); - -const RIGHT_ITEMS = new Map([ - ["x", { title: "X", component: X }], - ["y", { title: "Y", component: Y }], - ["z", { title: "Z", component: Z }], -]); - -function Story({ - label, - defaultLeftKey, - defaultRightKey, -}: { - label?: string; - defaultLeftKey?: LeftKey | undefined; - defaultRightKey?: RightKey | undefined; -}): JSX.Element { - const [selectedRightKey, setSelectedRightKey] = useState(defaultRightKey); - const [selectedLeftKey, setSelectedLeftKey] = useState(defaultLeftKey); - const [leftSidebarSize, setLeftSidebarSize] = useState(); - const [rightSidebarSize, setRightSidebarSize] = useState(); - - return ( - -
- - {label ?? "Main content"} - -
-
- ); -} - -export const LeftOpen: StoryObj = { - render: () => , - name: "Left", -}; - -export const LeftLongText: StoryObj = { - render: () => , - name: "Left (with text overflow)", -}; - -export const LeftClicked: StoryObj = { - render: () => , - name: "Left (tab click interaction)", - parameters: { colorScheme: "dark" }, - - play: async () => { - const leftTab = await screen.findByTestId("b-left"); - await userEvent.click(leftTab); - }, -}; - -export const LeftClosed: StoryObj = { - render: () => ( - - ), - - name: "Left (closed by interaction)", - parameters: { colorScheme: "dark" }, - - play: async () => { - const leftClose = await screen.findByTestId("sidebar-close-left"); - await userEvent.click(leftClose); - }, -}; - -export const RightOpen: StoryObj = { - render: () => , - name: "Right", -}; - -export const RightLongText: StoryObj = { - render: () => , - name: "Right (with text overflow)", -}; - -export const RightClicked: StoryObj = { - render: () => , - name: "Right (tab click interaction)", - parameters: { colorScheme: "dark" }, - - play: async () => { - const rightTab = await screen.findByTestId("y-right"); - await userEvent.click(rightTab); - }, -}; - -export const RightClosed: StoryObj = { - render: () => ( - - ), - - name: "Right (closed by interaction)", - parameters: { colorScheme: "dark" }, - - play: async () => { - const rightClose = await screen.findByTestId("sidebar-close-right"); - await userEvent.click(rightClose); - }, -}; - -export const Default: StoryObj = { - render: () => , -}; - -export const BothOpen: StoryObj = { - render: () => , - name: "Both (opened)", -}; - -export const BothClicked: StoryObj = { - render: () => , - name: "Both (tab click interaction)", - parameters: { colorScheme: "dark" }, - - play: async () => { - const { click } = userEvent.setup(); - const leftTab = await screen.findByTestId("b-left"); - await click(leftTab); - - const rightTab = await screen.findByTestId("y-right"); - await click(rightTab); - }, -}; diff --git a/packages/studio-base/src/components/Sidebars/Sidebar.tsx b/packages/studio-base/src/components/Sidebars/Sidebar.tsx deleted file mode 100644 index e27e0e79ca..0000000000 --- a/packages/studio-base/src/components/Sidebars/Sidebar.tsx +++ /dev/null @@ -1,165 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/ - -import CloseIcon from "@mui/icons-material/Close"; -import { Badge, BadgeProps, Divider, IconButton, Tab, Tabs } from "@mui/material"; -import { makeStyles } from "tss-react/mui"; - -import Stack from "@foxglove/studio-base/components/Stack"; - -const useStyles = makeStyles()((theme) => ({ - root: { - boxSizing: "content-box", - backgroundColor: theme.palette.background.paper, - }, - badgeRoot: { - display: "flex", - alignItems: "baseline", - gap: theme.spacing(1), - }, - badge: { - fontSize: theme.typography.caption.fontSize, - padding: theme.spacing(0.125, 0.75), - borderRadius: 8, - transform: "none", - position: "relative", - }, - badgeInvisible: { - display: "none", - }, - anchorRight: { - borderLeft: `1px solid ${theme.palette.divider}`, - }, - anchorLeft: { - borderRight: `1px solid ${theme.palette.divider}`, - }, - tabs: { - minHeight: "auto", - flex: "1 1 auto", - overflow: "hidden", - paddingLeft: theme.spacing(0.25), - - ".MuiTabs-indicator": { - transform: "scaleX(0.5)", - height: 2, - }, - ".MuiTab-root": { - minHeight: 30, - minWidth: theme.spacing(4), - padding: theme.spacing(0, 1), - color: theme.palette.text.secondary, - fontSize: "0.6875rem", - - "&.Mui-selected": { - color: theme.palette.text.primary, - }, - }, - }, - iconButton: { - padding: theme.spacing(0.91125), // round out the overall height to 30px - color: theme.palette.text.secondary, - borderRadius: 0, - - ":hover": { - color: theme.palette.text.primary, - }, - }, - tabContent: { - flex: "auto", - overflow: "auto", - }, -})); - -export type SidebarItem = { - title: string; - component: React.ComponentType; - badge?: { - color: BadgeProps["color"]; - count: number; - }; -}; - -function Noop(): ReactNull { - return ReactNull; -} - -export function Sidebar({ - items, - anchor, - onClose, - activeTab, - setActiveTab, -}: { - items: Map; - anchor: "right" | "left"; - onClose: () => void; - activeTab: K | undefined; - setActiveTab: (newValue: K) => void; -}): JSX.Element { - const { classes, cx } = useStyles(); - - const SelectedComponent = (activeTab && items.get(activeTab)?.component) ?? Noop; - - return ( - - - { - if (newValue !== activeTab) { - setActiveTab(newValue); - } - }} - > - {Array.from(items.entries(), ([key, item]) => ( - - {item.title} - - } - value={key} - data-testid={`${key}-${anchor}`} - /> - ))} - - - - - - - - {activeTab != undefined && ( -
- -
- )} -
- ); -} diff --git a/packages/studio-base/src/components/Sidebars/Sidebars.tsx b/packages/studio-base/src/components/Sidebars/Sidebars.tsx deleted file mode 100644 index 9aaf825020..0000000000 --- a/packages/studio-base/src/components/Sidebars/Sidebars.tsx +++ /dev/null @@ -1,228 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/ - -import { PropsWithChildren, useCallback, useEffect, useMemo, useState } from "react"; -import { MosaicNode, MosaicWithoutDragDropContext } from "react-mosaic-component"; -import { makeStyles } from "tss-react/mui"; - -import ErrorBoundary from "@foxglove/studio-base/components/ErrorBoundary"; -import Stack from "@foxglove/studio-base/components/Stack"; - -import { Sidebar, SidebarItem } from "./Sidebar"; - -import "react-mosaic-component/react-mosaic-component.css"; - -type LayoutNode = "leftbar" | "children" | "rightbar"; - -const useStyles = makeStyles()({ - mosaicWrapper: { - flex: "1 1 100%", - - // Root drop targets in this top level sidebar mosaic interfere with drag/mouse events from the - // PanelList. We don't allow users to edit the mosaic since it's just used for the sidebar, so we - // can hide the drop targets. - "& > .mosaic > .drop-target-container": { - display: "none !important", - }, - }, -}); - -/** - * Extract existing left split percentage from a layout node or return the default. - */ -function mosiacLeftSidebarSplitPercentage(node: MosaicNode): number | undefined { - if (typeof node !== "object") { - return undefined; - } - if (node.first === "leftbar") { - return node.splitPercentage; - } else { - return ( - mosiacLeftSidebarSplitPercentage(node.first) ?? mosiacLeftSidebarSplitPercentage(node.second) - ); - } -} - -/** - * Extract existing right split percentage from a layout node or return the default. - */ -function mosiacRightSidebarSplitPercentage(node: MosaicNode): number | undefined { - if (typeof node !== "object") { - return undefined; - } - if (node.second === "rightbar") { - return node.splitPercentage; - } else { - return ( - mosiacRightSidebarSplitPercentage(node.first) ?? - mosiacRightSidebarSplitPercentage(node.second) - ); - } -} - -type SidebarProps = PropsWithChildren<{ - leftItems: Map; - selectedLeftKey: LeftKey | undefined; - onSelectLeftKey: (key: LeftKey | undefined) => void; - leftSidebarSize: number | undefined; - setLeftSidebarSize: (size: number | undefined) => void; - - rightItems: Map; - selectedRightKey: RightKey | undefined; - onSelectRightKey: (key: RightKey | undefined) => void; - rightSidebarSize: number | undefined; - setRightSidebarSize: (size: number | undefined) => void; -}>; - -export function Sidebars( - props: SidebarProps, -): JSX.Element { - const { - children, - leftItems, - selectedLeftKey, - onSelectLeftKey, - leftSidebarSize, - setLeftSidebarSize, - rightItems, - selectedRightKey, - onSelectRightKey, - rightSidebarSize, - setRightSidebarSize, - } = props; - - const [mosaicValue, setMosaicValue] = useState>("children"); - const { classes } = useStyles(); - - const leftSidebarOpen = selectedLeftKey != undefined; - const rightSidebarOpen = selectedRightKey != undefined; - - // Select an available item if the selected one is not actually available - useEffect(() => { - if (leftSidebarOpen && !leftItems.has(selectedLeftKey)) { - onSelectLeftKey([...leftItems.keys()][0]); - } - if (rightSidebarOpen && !rightItems.has(selectedRightKey)) { - onSelectRightKey([...rightItems.keys()][0]); - } - }, [ - leftItems, - leftSidebarOpen, - onSelectLeftKey, - onSelectRightKey, - rightItems, - rightSidebarOpen, - selectedLeftKey, - selectedRightKey, - ]); - - useEffect(() => { - const leftTargetWidth = 320; - const rightTargetWidth = 320; - const defaultLeftPercentage = 100 * (leftTargetWidth / window.innerWidth); - const defaultRightPercentage = 100 * (1 - rightTargetWidth / window.innerWidth); - - setMosaicValue((oldValue) => { - let node: MosaicNode = "children"; - if (rightSidebarOpen) { - node = { - direction: "row", - first: node, - second: "rightbar", - splitPercentage: - rightSidebarSize ?? - mosiacRightSidebarSplitPercentage(oldValue) ?? - defaultRightPercentage, - }; - } - if (leftSidebarOpen) { - node = { - direction: "row", - first: "leftbar", - second: node, - splitPercentage: - leftSidebarSize ?? mosiacLeftSidebarSplitPercentage(oldValue) ?? defaultLeftPercentage, - }; - } - return node; - }); - }, [leftSidebarSize, rightSidebarSize, leftSidebarOpen, rightSidebarOpen]); - - const onChangeMosaicValue = useCallback( - (newValue: ReactNull | MosaicNode) => { - if (newValue != undefined) { - setMosaicValue(newValue); - setLeftSidebarSize(mosiacLeftSidebarSplitPercentage(newValue)); - setRightSidebarSize(mosiacRightSidebarSplitPercentage(newValue)); - } - }, - [setLeftSidebarSize, setRightSidebarSize], - ); - - const childrenComponent = useMemo( - () => {children as JSX.Element}, - [children], - ); - - const leftSidebarComponent = useMemo( - () => ( - - - anchor="left" - onClose={() => { - onSelectLeftKey(undefined); - }} - items={leftItems} - activeTab={selectedLeftKey} - setActiveTab={onSelectLeftKey} - /> - - ), - [leftItems, onSelectLeftKey, selectedLeftKey], - ); - - const rightSidebarComponent = useMemo( - () => ( - - - anchor="right" - onClose={() => { - onSelectRightKey(undefined); - }} - items={rightItems} - activeTab={selectedRightKey} - setActiveTab={onSelectRightKey} - /> - - ), - [onSelectRightKey, rightItems, selectedRightKey], - ); - - return ( - - { - // By always rendering the mosaic, even if we are only showing children, we can prevent the - // children from having to re-mount each time the sidebar is opened/closed. - } -
- - className="" - value={mosaicValue} - onChange={onChangeMosaicValue} - renderTile={(id) => { - switch (id) { - case "children": - return childrenComponent; - case "leftbar": - return leftSidebarComponent; - case "rightbar": - return rightSidebarComponent; - } - }} - resize={{ minimumPaneSizePercentage: 10 }} - /> -
-
- ); -} diff --git a/packages/studio-base/src/context/NativeAppMenuContext.ts b/packages/studio-base/src/context/NativeAppMenuContext.ts index 8595e3b564..5ddefbf714 100644 --- a/packages/studio-base/src/context/NativeAppMenuContext.ts +++ b/packages/studio-base/src/context/NativeAppMenuContext.ts @@ -2,7 +2,7 @@ // License, v2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/ -import { createContext, useContext } from "react"; +import { createContext } from "react"; export type NativeAppMenuEvent = | "open" @@ -23,8 +23,4 @@ export interface INativeAppMenu { const NativeAppMenuContext = createContext(undefined); NativeAppMenuContext.displayName = "NativeAppMenuContext"; -export function useNativeAppMenu(): INativeAppMenu | undefined { - return useContext(NativeAppMenuContext); -} - export default NativeAppMenuContext; diff --git a/packages/studio-base/src/context/NativeWindowContext.ts b/packages/studio-base/src/context/NativeWindowContext.ts index d99356a217..c0ba4cf0e8 100644 --- a/packages/studio-base/src/context/NativeWindowContext.ts +++ b/packages/studio-base/src/context/NativeWindowContext.ts @@ -2,7 +2,7 @@ // License, v2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/ -import { createContext, useContext } from "react"; +import { createContext } from "react"; export type NativeWindowEvent = "enter-full-screen" | "leave-full-screen"; @@ -17,8 +17,4 @@ export interface INativeWindow { const NativeWindowContext = createContext(undefined); NativeWindowContext.displayName = "NativeWindowContext"; -export function useNativeWindow(): INativeWindow | undefined { - return useContext(NativeWindowContext); -} - export default NativeWindowContext; diff --git a/packages/studio-base/src/context/RemoteLayoutStorageContext.ts b/packages/studio-base/src/context/RemoteLayoutStorageContext.ts index 6cc102a160..01d128f2ad 100644 --- a/packages/studio-base/src/context/RemoteLayoutStorageContext.ts +++ b/packages/studio-base/src/context/RemoteLayoutStorageContext.ts @@ -12,6 +12,3 @@ RemoteLayoutStorageContext.displayName = "RemoteLayoutStorageContext"; export function useRemoteLayoutStorage(): IRemoteLayoutStorage | undefined { return useContext(RemoteLayoutStorageContext); } - -// ts-prune-ignore-next -export default RemoteLayoutStorageContext; diff --git a/packages/studio-base/src/context/UserNodeStateContext.tsx b/packages/studio-base/src/context/UserNodeStateContext.tsx index f89049e95f..f73c644177 100644 --- a/packages/studio-base/src/context/UserNodeStateContext.tsx +++ b/packages/studio-base/src/context/UserNodeStateContext.tsx @@ -104,8 +104,3 @@ export function UserNodeStateProvider({ children }: React.PropsWithChildren): JS return {children}; } - -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types -export function useUserNodeState() { - return useGuaranteedContext(UserNodeStateContext, "UserNodeStateContext"); -} diff --git a/packages/studio-base/src/context/UserScriptStateContext.tsx b/packages/studio-base/src/context/UserScriptStateContext.tsx index 4b1fd1a374..ee33a3306b 100644 --- a/packages/studio-base/src/context/UserScriptStateContext.tsx +++ b/packages/studio-base/src/context/UserScriptStateContext.tsx @@ -3,9 +3,8 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/ import { createContext, useState } from "react"; -import { StoreApi, createStore, useStore } from "zustand"; +import { StoreApi, createStore } from "zustand"; -import { useGuaranteedContext } from "@foxglove/hooks"; import { generateEmptyTypesLib } from "@foxglove/studio-base/players/UserScriptPlayer/transformerWorker/generateTypesLib"; import { ros_lib_dts } from "@foxglove/studio-base/players/UserScriptPlayer/transformerWorker/typescript/ros"; import { Diagnostic, UserScriptLog } from "@foxglove/studio-base/players/UserScriptPlayer/types"; @@ -107,8 +106,3 @@ export function UserScriptStateProvider({ children }: React.PropsWithChildren): {children} ); } - -export function useUserScriptState(selector: (arg: UserScriptStore) => T): T { - const store = useGuaranteedContext(UserScriptStateContext, "UserScriptStateContext"); - return useStore(store, selector); -} diff --git a/packages/studio-base/src/players/TopicAliasingPlayer/aliasing.ts b/packages/studio-base/src/players/TopicAliasingPlayer/aliasing.ts index 0bc665adf0..0f00910586 100644 --- a/packages/studio-base/src/players/TopicAliasingPlayer/aliasing.ts +++ b/packages/studio-base/src/players/TopicAliasingPlayer/aliasing.ts @@ -188,24 +188,6 @@ function aliasTopicStats( return mappedStats; } -// Inverts a mapping, used to reverse map incoming subscriptions to subscriptions we pass -// through to the wrapped player. -function invertAliasMap(aliasMap: Im): Im { - if (aliasMap === EmptyAliasMap) { - return EmptyAliasMap; - } - - const inverted: TopicAliasMap = new Map(); - for (const [key, values] of aliasMap.entries()) { - for (const value of values) { - const newValues = inverted.get(value) ?? []; - newValues.push(key); - inverted.set(value, newValues); - } - } - return inverted; -} - // Merges multiple aliases into a single unified alias map. Note that a single topic name // can alias to more than one renamed topic if multiple extensions provide an alias for it. // Also returns any problems caused by disallowed aliases. @@ -327,34 +309,3 @@ export function aliasPlayerState( return newState; } - -/** - * Maps an array of subscriptions to a new array with all topic aliases applied. - * - * @param inputs the inputs to the mapping function - * @param subscriptions the subscription payloads to map - * @returns a new array of subscription payloads with mapped topic names - */ -export const aliasSubscriptions = memoizeWeak( - (inputs: Im, subcriptions: SubscribePayload[]): SubscribePayload[] => { - const { aliasMap: mapping } = memos.buildAliases(inputs); - - if (mapping === EmptyAliasMap) { - return subcriptions; - } - - const inverseMapping = invertAliasMap(mapping); - - return subcriptions.flatMap((sub) => { - const mappings = inverseMapping.get(sub.topic); - if (mappings) { - return mappings.map((topic) => ({ - ...sub, - topic, - })); - } else { - return sub; - } - }); - }, -); diff --git a/packages/studio-base/src/players/UserNodePlayer/nodeTransformerWorker/typescript/templates/index.ts b/packages/studio-base/src/players/UserNodePlayer/nodeTransformerWorker/typescript/templates/index.ts deleted file mode 100644 index a43bfa2f17..0000000000 --- a/packages/studio-base/src/players/UserNodePlayer/nodeTransformerWorker/typescript/templates/index.ts +++ /dev/null @@ -1,40 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/ -// -// This file incorporates work covered by the following copyright and -// permission notice: -// -// Copyright 2019-2021 Cruise LLC -// -// This source code is licensed under the Apache License, Version 2.0, -// found at http://www.apache.org/licenses/LICENSE-2.0 -// You may not use this file except in compliance with the License. - -import locationFix from "./locationFix.ts.template"; -import marker from "./marker.ts.template"; -import multipleInputs from "./multipleInputs.ts.template"; -import skeleton from "./skeleton.ts.template"; - -export default [ - { - name: "Skeleton", - description: "An empty script", - template: skeleton, - }, - { - name: "Markers", - description: "A script that publishes one or more markers", - template: marker, - }, - { - name: "Multiple Inputs", - description: "A script that receives inputs on multiple topics", - template: multipleInputs, - }, - { - name: "GPS Location", - description: "A script that publishes foxglove.LocationFix", - template: locationFix, - }, -]; diff --git a/packages/studio-base/src/players/UserNodePlayer/nodeTransformerWorker/typescript/userUtils/markers.ts b/packages/studio-base/src/players/UserNodePlayer/nodeTransformerWorker/typescript/userUtils/markers.ts index 58037651b4..49e21a6410 100644 --- a/packages/studio-base/src/players/UserNodePlayer/nodeTransformerWorker/typescript/userUtils/markers.ts +++ b/packages/studio-base/src/players/UserNodePlayer/nodeTransformerWorker/typescript/userUtils/markers.ts @@ -36,124 +36,6 @@ export interface ImageMarker { outline_colors: RGBA[]; } -/** - * buildRosMarker builds a complete Marker message from an optional set of args. - * - * See https://foxglove.dev/docs/panels/3d for a list of supported Marker types - * - * @param args override any defaults in the marker fields - * @returns an IRosMarker instance with default values for any omitted args - */ -export function buildRosMarker(args?: Partial): IRosMarker { - const { - header, - ns, - id, - type, - action, - pose, - scale, - color, - lifetime, - frame_locked, - points, - colors, - text, - mesh_resource, - mesh_use_embedded_materials, - } = args ?? {}; - - return { - header: header ?? { - frame_id: "", - stamp: { - sec: 0, - nsec: 0, - }, - seq: 0, - }, - ns: ns ?? "", - id: id ?? 0, - type: type ?? 0, - action: action ?? 0, - pose: pose ?? { - position: { - x: 0, - y: 0, - z: 0, - }, - orientation: { - x: 0, - y: 0, - z: 0, - w: 0, - }, - }, - scale: scale ?? { x: 0, y: 0, z: 0 }, - color: color ?? { r: 0, g: 0, b: 0, a: 0 }, - lifetime: lifetime ?? { sec: 0, nsec: 0 }, - frame_locked: frame_locked ?? false, - points: points ?? [], - colors: colors ?? [], - text: text ?? "", - mesh_resource: mesh_resource ?? "", - mesh_use_embedded_materials: mesh_use_embedded_materials ?? false, - }; -} - -/** - * buildImageMarker builds a complete Marker message from an optional set of args. - * - * See https://foxglove.dev/docs/studio/panels/image for a list of supported Marker types - * - * @param args override any defaults in the marker fields - * @returns an ImageMarker instance with default values for any omitted args - */ -export function buildImageMarker(args?: Partial): ImageMarker { - const { - header, - ns, - id, - type, - action, - lifetime, - points, - outline_color, - outline_colors, - filled, - fill_color, - position, - scale, - } = args ?? {}; - - return { - header: header ?? { - frame_id: "", - stamp: { - sec: 0, - nsec: 0, - }, - seq: 0, - }, - ns: ns ?? "", - id: id ?? 0, - type: type ?? 0, - action: action ?? 0, - position: position ?? { - x: 0, - y: 0, - z: 0, - }, - scale: scale ?? 1, - outline_color: outline_color ?? { r: 0, g: 0, b: 0, a: 0 }, - lifetime: lifetime ?? { sec: 0, nsec: 0 }, - points: points ?? [], - outline_colors: outline_colors ?? [], - filled: filled ?? false, - fill_color: fill_color ?? { r: 0, g: 0, b: 0, a: 0 }, - }; -} - /** * Use this class to instantiate marker-like objects with defaulted values. * diff --git a/packages/studio-base/src/players/UserNodePlayer/nodeTransformerWorker/typescript/userUtils/pointClouds.ts b/packages/studio-base/src/players/UserNodePlayer/nodeTransformerWorker/typescript/userUtils/pointClouds.ts index d8ab4f5dcf..7e38dbd668 100644 --- a/packages/studio-base/src/players/UserNodePlayer/nodeTransformerWorker/typescript/userUtils/pointClouds.ts +++ b/packages/studio-base/src/players/UserNodePlayer/nodeTransformerWorker/typescript/userUtils/pointClouds.ts @@ -55,54 +55,3 @@ export const readPoints = (message: sensor_msgs__PointCloud2): Array => } return points; }; - -export function norm({ x, y, z }: Point): number { - return Math.sqrt(x * x + y * y + z * z); -} - -export function setRayDistance(pt: Point, distance: number): Point { - const { x, y, z } = pt; - const scale = distance / norm(pt); - return { - x: x * scale, - y: y * scale, - z: z * scale, - }; -} - -// eslint-disable-next-line @foxglove/no-boolean-parameters -export function convertToRangeView(points: Point[], range: number, makeColors: boolean): RGBA[] { - const colors: RGBA[] = makeColors ? new Array(points.length) : []; - // First pass to get min and max ranges - let maxRange = Number.MIN_VALUE; - if (makeColors) { - for (const point of points) { - maxRange = Math.max(maxRange, norm(point)); - } - } - // actually move the points and generate colors if specified - for (let i = 0; i < points.length; ++i) { - const pt = points[i]!; - if (makeColors) { - const dist = norm(pt); - if (dist <= range) { - // don't go all the way to white - const extent = 0.8; - // closest to target range is lightest, - // closest to AV is darkest - const other = (extent * dist) / range; - colors[i] = { r: 1, g: other, b: other, a: 1 }; - } else { - // don't go all the way to white - const extent = 0.8; - // closest to target range is lightest, - // closest to max range is darkest - const upper = maxRange - range; - const other = extent * (1.0 - dist / upper); - colors[i] = { r: other, g: other, b: 1, a: 1 }; - } - } - points[i] = setRayDistance(pt, range); - } - return colors; -} diff --git a/packages/studio-base/src/players/UserNodePlayer/nodeTransformerWorker/typescript/userUtils/time.ts b/packages/studio-base/src/players/UserNodePlayer/nodeTransformerWorker/typescript/userUtils/time.ts index 39d71269a4..e97ecd5f7b 100644 --- a/packages/studio-base/src/players/UserNodePlayer/nodeTransformerWorker/typescript/userUtils/time.ts +++ b/packages/studio-base/src/players/UserNodePlayer/nodeTransformerWorker/typescript/userUtils/time.ts @@ -1,10 +1,5 @@ import { Time } from "./types"; -/* - * Checks ROS-time equality. - */ -export const areSame = (t1: Time, t2: Time): boolean => t1.sec === t2.sec && t1.nsec === t2.nsec; - /* * Compare two times, returning a negative value if the right is greater or a * positive value if the left is greater or 0 if the times are equal useful to diff --git a/packages/studio-base/src/players/UserScriptPlayer/transformerWorker/typescript/ros.ts b/packages/studio-base/src/players/UserScriptPlayer/transformerWorker/typescript/ros.ts index 660b5079a4..2b942d46d7 100644 --- a/packages/studio-base/src/players/UserScriptPlayer/transformerWorker/typescript/ros.ts +++ b/packages/studio-base/src/players/UserScriptPlayer/transformerWorker/typescript/ros.ts @@ -10,7 +10,7 @@ // This source code is licensed under the Apache License, Version 2.0, // found at http://www.apache.org/licenses/LICENSE-2.0 // You may not use this file except in compliance with the License. -export const ros_lib_filename = "ros/index.d.ts"; + export const ros_lib_dts = ` export declare interface Duration { sec: number; diff --git a/packages/studio-base/src/players/UserScriptPlayer/types.ts b/packages/studio-base/src/players/UserScriptPlayer/types.ts index 311be2dd4f..70d5855574 100644 --- a/packages/studio-base/src/players/UserScriptPlayer/types.ts +++ b/packages/studio-base/src/players/UserScriptPlayer/types.ts @@ -18,14 +18,14 @@ import { RosDatatypes } from "@foxglove/studio-base/types/RosDatatypes"; // make sure to use import type to avoid bringing in the actual implementations to the bundle -export const DiagnosticSeverity = { +const DiagnosticSeverity = { Hint: 1, Info: 2, Warning: 4, Error: 8, }; -export const Sources = { +const Sources = { Typescript: "Typescript", DatatypeExtraction: "DatatypeExtraction", InputTopicsChecker: "InputTopicsChecker", @@ -33,42 +33,6 @@ export const Sources = { Runtime: "Runtime", }; -export const ErrorCodes = { - RUNTIME: 1, - DatatypeExtraction: { - NO_DEFAULT_EXPORT: 1, - NON_FUNC_DEFAULT_EXPORT: 2, - NO_TYPE_RETURN: 3, - BAD_TYPE_RETURN: 4, - UNKNOWN_ERROR: 5, - NO_UNIONS: 6, - NO_FUNCTIONS: 7, - NO_CLASSES: 8, - NO_TYPE_LITERALS: 9, - NO_TUPLES: 10, - NO_INTERSECTION_TYPES: 11, - NO_TYPEOF: 12, - PREFER_ARRAY_LITERALS: 13, - STRICT_MARKERS_RETURN_TYPE: 14, - LIMITED_UNIONS: 15, - NO_NESTED_ANY: 16, - NO_MAPPED_TYPES: 17, - INVALID_PROPERTY: 18, - INVALID_INDEXED_ACCESS: 19, - }, - InputTopicsChecker: { - NO_TOPIC_AVAIL: 1, - NO_INPUTS_EXPORT: 2, - EMPTY_INPUTS_EXPORT: 3, - BAD_INPUTS_TYPE: 4, - }, - OutputTopicChecker: { - NO_OUTPUTS: 1, - NOT_UNIQUE: 2, - EXISTING_TOPIC: 3, - }, -}; - export type Diagnostic = { severity: (typeof DiagnosticSeverity)[keyof typeof DiagnosticSeverity]; message: string; diff --git a/packages/studio-base/src/util/sharedStyleConstants.ts b/packages/studio-base/src/util/sharedStyleConstants.ts deleted file mode 100644 index 3bd11e27dc..0000000000 --- a/packages/studio-base/src/util/sharedStyleConstants.ts +++ /dev/null @@ -1,19 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/ - -export const fonts = { - // We explicitly avoid fallback fonts (such as 'monospace') here to work around a bug in - // Chrome/Chromium on Windows that causes crashes when multiple Workers try to access fonts that - // have not yet been loaded. There is a race against the internal DirectWrite font cache which - // ends up crashing in DWriteFontFamily::GetFirstMatchingFont() or DWriteFont::Create(). - // - // https://bugs.chromium.org/p/chromium/issues/detail?id=1261577 - MONOSPACE: "'IBM Plex Mono'", - SANS_SERIF: "'Inter'", - - // enable font features https://rsms.me/inter/lab - SANS_SERIF_FEATURE_SETTINGS: "'cv08', 'cv10', 'tnum'", - // contextual alternates create undesired changes in Chinese/Japanese - SANS_SERIF_FEATURE_SETTINGS_CJK: "'tnum'", -}; From c08f6e5e894b80eb83a57f84b07fba6931457766 Mon Sep 17 00:00:00 2001 From: Alexandre Neuwald CTW Date: Mon, 15 Apr 2024 14:57:54 +0100 Subject: [PATCH 3/8] dedupe packages --- yarn.lock | 169 +++--------------------------------------------------- 1 file changed, 9 insertions(+), 160 deletions(-) diff --git a/yarn.lock b/yarn.lock index 75071ae77f..1048fe5416 100644 --- a/yarn.lock +++ b/yarn.lock @@ -210,26 +210,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-create-class-features-plugin@npm:^7.22.15, @babel/helper-create-class-features-plugin@npm:^7.23.6": - version: 7.23.10 - resolution: "@babel/helper-create-class-features-plugin@npm:7.23.10" - dependencies: - "@babel/helper-annotate-as-pure": ^7.22.5 - "@babel/helper-environment-visitor": ^7.22.20 - "@babel/helper-function-name": ^7.23.0 - "@babel/helper-member-expression-to-functions": ^7.23.0 - "@babel/helper-optimise-call-expression": ^7.22.5 - "@babel/helper-replace-supers": ^7.22.20 - "@babel/helper-skip-transparent-expression-wrappers": ^7.22.5 - "@babel/helper-split-export-declaration": ^7.22.6 - semver: ^6.3.1 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: ff0730c21f0e73b9e314701bca6568bb5885dff2aa3c32b1e2e3d18ed2818f56851b9ffdbe2e8008c9bb94b265a1443883ae4c1ca5dde278ce71ac4218006d68 - languageName: node - linkType: hard - -"@babel/helper-create-class-features-plugin@npm:^7.24.1": +"@babel/helper-create-class-features-plugin@npm:^7.22.15, @babel/helper-create-class-features-plugin@npm:^7.23.6, @babel/helper-create-class-features-plugin@npm:^7.24.1": version: 7.24.1 resolution: "@babel/helper-create-class-features-plugin@npm:7.24.1" dependencies: @@ -317,7 +298,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-member-expression-to-functions@npm:^7.22.15, @babel/helper-member-expression-to-functions@npm:^7.23.0": +"@babel/helper-member-expression-to-functions@npm:^7.23.0": version: 7.23.0 resolution: "@babel/helper-member-expression-to-functions@npm:7.23.0" dependencies: @@ -359,14 +340,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": - version: 7.22.5 - resolution: "@babel/helper-plugin-utils@npm:7.22.5" - checksum: c0fc7227076b6041acd2f0e818145d2e8c41968cc52fb5ca70eed48e21b8fe6dd88a0a91cbddf4951e33647336eb5ae184747ca706817ca3bef5e9e905151ff5 - languageName: node - linkType: hard - -"@babel/helper-plugin-utils@npm:^7.24.0": +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.24.0, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": version: 7.24.0 resolution: "@babel/helper-plugin-utils@npm:7.24.0" checksum: e2baa0eede34d2fa2265947042aa84d444aa48dc51e9feedea55b67fc1bc3ab051387e18b33ca7748285a6061390831ab82f8a2c767d08470b93500ec727e9b9 @@ -386,20 +360,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-replace-supers@npm:^7.22.20": - version: 7.22.20 - resolution: "@babel/helper-replace-supers@npm:7.22.20" - dependencies: - "@babel/helper-environment-visitor": ^7.22.20 - "@babel/helper-member-expression-to-functions": ^7.22.15 - "@babel/helper-optimise-call-expression": ^7.22.5 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: a0008332e24daedea2e9498733e3c39b389d6d4512637e000f96f62b797e702ee24a407ccbcd7a236a551590a38f31282829a8ef35c50a3c0457d88218cae639 - languageName: node - linkType: hard - -"@babel/helper-replace-supers@npm:^7.24.1": +"@babel/helper-replace-supers@npm:^7.22.20, @babel/helper-replace-supers@npm:^7.24.1": version: 7.24.1 resolution: "@babel/helper-replace-supers@npm:7.24.1" dependencies: @@ -1771,16 +1732,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.19.4, @babel/runtime@npm:^7.22.15, @babel/runtime@npm:^7.22.5, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.8, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": - version: 7.23.9 - resolution: "@babel/runtime@npm:7.23.9" - dependencies: - regenerator-runtime: ^0.14.0 - checksum: 6bbebe8d27c0c2dd275d1ac197fc1a6c00e18dab68cc7aaff0adc3195b45862bae9c4cc58975629004b0213955b2ed91e99eccb3d9b39cabea246c657323d667 - languageName: node - linkType: hard - -"@babel/runtime@npm:^7.21.0": +"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.19.4, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.22.15, @babel/runtime@npm:^7.22.5, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.8, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": version: 7.24.1 resolution: "@babel/runtime@npm:7.24.1" dependencies: @@ -3900,23 +3852,6 @@ __metadata: languageName: node linkType: hard -"@mui/private-theming@npm:^5.15.9": - version: 5.15.9 - resolution: "@mui/private-theming@npm:5.15.9" - dependencies: - "@babel/runtime": ^7.23.9 - "@mui/utils": ^5.15.9 - prop-types: ^15.8.1 - peerDependencies: - "@types/react": ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 15cd0a7d8ba05452ef5f7a6a8856d333f6e6bfc3fe4ca0e242a4e37053856872603e374593f42a297d26353cdc0a8567d8424f8e7912b8bbd7f4507f9f9f2a74 - languageName: node - linkType: hard - "@mui/styled-engine@npm:^5.15.14": version: 5.15.14 resolution: "@mui/styled-engine@npm:5.15.14" @@ -3938,28 +3873,7 @@ __metadata: languageName: node linkType: hard -"@mui/styled-engine@npm:^5.15.9": - version: 5.15.9 - resolution: "@mui/styled-engine@npm:5.15.9" - dependencies: - "@babel/runtime": ^7.23.9 - "@emotion/cache": ^11.11.0 - csstype: ^3.1.3 - prop-types: ^15.8.1 - peerDependencies: - "@emotion/react": ^11.4.1 - "@emotion/styled": ^11.3.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - "@emotion/react": - optional: true - "@emotion/styled": - optional: true - checksum: 0a02a3b83d47277fa38421a6cfe0e8c6451e7d5a4d5b5d389ba970ebc55172040f0f2bd628f0211b659b922965ead416da0453e4bc15e7e83942f03ffa57621d - languageName: node - linkType: hard - -"@mui/system@npm:^5.13.5": +"@mui/system@npm:^5.13.5, @mui/system@npm:^5.15.5": version: 5.15.14 resolution: "@mui/system@npm:5.15.14" dependencies: @@ -3987,47 +3901,7 @@ __metadata: languageName: node linkType: hard -"@mui/system@npm:^5.15.5": - version: 5.15.9 - resolution: "@mui/system@npm:5.15.9" - dependencies: - "@babel/runtime": ^7.23.9 - "@mui/private-theming": ^5.15.9 - "@mui/styled-engine": ^5.15.9 - "@mui/types": ^7.2.13 - "@mui/utils": ^5.15.9 - clsx: ^2.1.0 - csstype: ^3.1.3 - prop-types: ^15.8.1 - peerDependencies: - "@emotion/react": ^11.5.0 - "@emotion/styled": ^11.3.0 - "@types/react": ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - "@emotion/react": - optional: true - "@emotion/styled": - optional: true - "@types/react": - optional: true - checksum: a5bda6f9c2b8e1241ed0f6865539fea65cdac31b61383c0f35bd1eedc838f67c68cce9609af5563aae6f45bec0c8242283fa408a2b989e23bdc9caeba9939950 - languageName: node - linkType: hard - -"@mui/types@npm:^7.2.13": - version: 7.2.13 - resolution: "@mui/types@npm:7.2.13" - peerDependencies: - "@types/react": ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 58dfc96f9654288519ff01d6b54e6a242f05cadad51210deb85710a81be4fa1501a116c8968e2614b16c748fc1f407dc23beeeeae70fa37fceb6c6de876ff70d - languageName: node - linkType: hard - -"@mui/types@npm:^7.2.14, @mui/types@npm:^7.2.4": +"@mui/types@npm:^7.2.13, @mui/types@npm:^7.2.14, @mui/types@npm:^7.2.4": version: 7.2.14 resolution: "@mui/types@npm:7.2.14" peerDependencies: @@ -4039,7 +3913,7 @@ __metadata: languageName: node linkType: hard -"@mui/utils@npm:^5.13.1, @mui/utils@npm:^5.15.14": +"@mui/utils@npm:^5.13.1, @mui/utils@npm:^5.14.16, @mui/utils@npm:^5.15.14, @mui/utils@npm:^5.15.5": version: 5.15.14 resolution: "@mui/utils@npm:5.15.14" dependencies: @@ -4057,24 +3931,6 @@ __metadata: languageName: node linkType: hard -"@mui/utils@npm:^5.14.16, @mui/utils@npm:^5.15.5, @mui/utils@npm:^5.15.9": - version: 5.15.9 - resolution: "@mui/utils@npm:5.15.9" - dependencies: - "@babel/runtime": ^7.23.9 - "@types/prop-types": ^15.7.11 - prop-types: ^15.8.1 - react-is: ^18.2.0 - peerDependencies: - "@types/react": ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 61e520c0a50cbf463d893b011eaea844dcf028e12387eec12378aa870d4342c688bfe5f48de1f4896dc1a0cc9d4224f90470d238105ee45c178cd8b4bbef7cf5 - languageName: node - linkType: hard - "@mui/x-data-grid@npm:6.18.7": version: 6.18.7 resolution: "@mui/x-data-grid@npm:6.18.7" @@ -23752,14 +23608,7 @@ __metadata: languageName: node linkType: hard -"zwitch@npm:^2.0.0": - version: 2.0.2 - resolution: "zwitch@npm:2.0.2" - checksum: 8edd7af8375f12f64d8dbef815af32cd77bd9237d0b013210ba4e3aef25fdc460fe264cd0a19deabe9f86ef0c607240ebac1a336bf4a70bf06ef53e0652de116 - languageName: node - linkType: hard - -"zwitch@npm:^2.0.4": +"zwitch@npm:^2.0.0, zwitch@npm:^2.0.4": version: 2.0.4 resolution: "zwitch@npm:2.0.4" checksum: f22ec5fc2d5f02c423c93d35cdfa83573a3a3bd98c66b927c368ea4d0e7252a500df2a90a6b45522be536a96a73404393c958e945fdba95e6832c200791702b6 From 7f0688ee5a43cc5d4dfc85be4f65d2070bfccaf5 Mon Sep 17 00:00:00 2001 From: Alexandre Neuwald CTW Date: Mon, 15 Apr 2024 15:33:07 +0100 Subject: [PATCH 4/8] Fix lint --- packages/studio-base/src/context/UserNodeStateContext.tsx | 2 +- .../nodeTransformerWorker/typescript/userUtils/pointClouds.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/studio-base/src/context/UserNodeStateContext.tsx b/packages/studio-base/src/context/UserNodeStateContext.tsx index f73c644177..c6cf1e4f5e 100644 --- a/packages/studio-base/src/context/UserNodeStateContext.tsx +++ b/packages/studio-base/src/context/UserNodeStateContext.tsx @@ -4,7 +4,7 @@ import { createContext, useCallback, useState } from "react"; -import { useShallowMemo, useGuaranteedContext } from "@foxglove/hooks"; +import { useShallowMemo } from "@foxglove/hooks"; import { generateEmptyTypesLib } from "@foxglove/studio-base/players/UserNodePlayer/nodeTransformerWorker/generateTypesLib"; import { ros_lib_dts } from "@foxglove/studio-base/players/UserNodePlayer/nodeTransformerWorker/typescript/ros"; import { Diagnostic, UserNodeLog } from "@foxglove/studio-base/players/UserNodePlayer/types"; diff --git a/packages/studio-base/src/players/UserNodePlayer/nodeTransformerWorker/typescript/userUtils/pointClouds.ts b/packages/studio-base/src/players/UserNodePlayer/nodeTransformerWorker/typescript/userUtils/pointClouds.ts index 7e38dbd668..345e97e0ca 100644 --- a/packages/studio-base/src/players/UserNodePlayer/nodeTransformerWorker/typescript/userUtils/pointClouds.ts +++ b/packages/studio-base/src/players/UserNodePlayer/nodeTransformerWorker/typescript/userUtils/pointClouds.ts @@ -1,5 +1,5 @@ import { FieldReader, getReader } from "./readers"; -import { Point, Header, RGBA } from "./types"; +import { Header } from "./types"; interface sensor_msgs__PointField { name: string; From cf9d0447be963999db1930bdd2820cc709d3e6a6 Mon Sep 17 00:00:00 2001 From: Alexandre Neuwald CTW Date: Mon, 15 Apr 2024 17:21:31 +0100 Subject: [PATCH 5/8] fix missing imports --- packages/studio-base/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/studio-base/package.json b/packages/studio-base/package.json index e7f5197838..7d14a6a5e0 100644 --- a/packages/studio-base/package.json +++ b/packages/studio-base/package.json @@ -91,6 +91,7 @@ "@types/uuid": "9.0.7", "@types/wicg-file-system-access": "2020.9.6", "@uiw/react-textarea-code-editor": "3.0.2", + "async-mutex": "0.5.0", "babel-jest": "29.7.0", "babel-plugin-transform-import-meta": "2.2.1", "browserify-zlib": "0.2.0", From 46afab3efe117133436ce31c99774f0df88c92b3 Mon Sep 17 00:00:00 2001 From: Alexandre Neuwald CTW Date: Mon, 15 Apr 2024 17:22:19 +0100 Subject: [PATCH 6/8] fix missing imports --- packages/theme/package.json | 5 +++- yarn.lock | 49 +++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/packages/theme/package.json b/packages/theme/package.json index ee5d9eaedf..661535a581 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -23,6 +23,9 @@ "@mui/x-data-grid": "6.18.7", "@storybook/react": "7.6.10", "@types/react": "18.2.56", - "react": "18.2.0" + "i18next": "23.11.2", + "i18next-browser-languagedetector": "7.2.1", + "react": "18.2.0", + "react-i18next": "14.1.0" } } diff --git a/yarn.lock b/yarn.lock index 1048fe5416..03f5c09112 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2934,6 +2934,7 @@ __metadata: "@types/uuid": 9.0.7 "@types/wicg-file-system-access": 2020.9.6 "@uiw/react-textarea-code-editor": 3.0.2 + async-mutex: 0.5.0 babel-jest: 29.7.0 babel-plugin-transform-import-meta: 2.2.1 browserify-zlib: 0.2.0 @@ -3123,7 +3124,10 @@ __metadata: "@mui/x-data-grid": 6.18.7 "@storybook/react": 7.6.10 "@types/react": 18.2.56 + i18next: 23.11.2 + i18next-browser-languagedetector: 7.2.1 react: 18.2.0 + react-i18next: 14.1.0 languageName: unknown linkType: soft @@ -8717,6 +8721,15 @@ __metadata: languageName: node linkType: hard +"async-mutex@npm:0.5.0": + version: 0.5.0 + resolution: "async-mutex@npm:0.5.0" + dependencies: + tslib: ^2.4.0 + checksum: be1587f4875f3bb15e34e9fcce82eac2966daef4432c8d0046e61947fb9a1b95405284601bc7ce4869319249bc07c75100880191db6af11d1498931ac2a2f9ea + languageName: node + linkType: hard + "async@npm:^3.2.3": version: 3.2.5 resolution: "async@npm:3.2.5" @@ -14377,6 +14390,24 @@ __metadata: languageName: node linkType: hard +"i18next-browser-languagedetector@npm:7.2.1": + version: 7.2.1 + resolution: "i18next-browser-languagedetector@npm:7.2.1" + dependencies: + "@babel/runtime": ^7.23.2 + checksum: 159958be2d8f19444e9378512c36c2bf13a8ab85eddac2fc0000198a03dbc28c73a6f44594ab040b242bdc82dfeabb7c1ab805884b5438ee0a48a8e2b52ca062 + languageName: node + linkType: hard + +"i18next@npm:23.11.2": + version: 23.11.2 + resolution: "i18next@npm:23.11.2" + dependencies: + "@babel/runtime": ^7.23.2 + checksum: c465b9bacf3066b6d112982deb03fa8a2578be38cf1e973749c884401ba1165431655e1a4438f0da08c978ce5525170ec3809968691190c194af80b20e496538 + languageName: node + linkType: hard + "i18next@npm:23.5.1": version: 23.5.1 resolution: "i18next@npm:23.5.1" @@ -19164,6 +19195,24 @@ __metadata: languageName: node linkType: hard +"react-i18next@npm:14.1.0": + version: 14.1.0 + resolution: "react-i18next@npm:14.1.0" + dependencies: + "@babel/runtime": ^7.23.9 + html-parse-stringify: ^3.0.1 + peerDependencies: + i18next: ">= 23.2.3" + react: ">= 16.8.0" + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + checksum: 96fbc4b0919b9f0c639f9f3eb35eecac528174aa97e3b1af469cfdbff4b34e40ae8969c26c0b737691a5fa9b56bb13093524cfca79b93cbd58f1319530da31b2 + languageName: node + linkType: hard + "react-is@npm:18.1.0": version: 18.1.0 resolution: "react-is@npm:18.1.0" From 2504c41d694b42392bc0c6653fee8d41748eb240 Mon Sep 17 00:00:00 2001 From: Alexandre Neuwald CTW Date: Tue, 16 Apr 2024 10:04:45 +0100 Subject: [PATCH 7/8] Fix lint:ci --- .github/workflows/ci.yml | 7 +-- package.json | 1 + .../typescript/userUtils/pointClouds.ts | 56 ++++++++++++++++++- 3 files changed, 57 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e42e71bf27..08431a23e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,12 +22,7 @@ jobs: command: | # lint steps set -x - yarn license-check - yarn dedupe --check - yarn run tsc --noEmit # typecheck files that are not included by webpack or package builds - yarn run lint:ci - yarn run lint:unused-exports - yarn run lint:dependencies + yarn run test:lint - name: packages command: yarn run build:packages - name: web diff --git a/package.json b/package.json index 6b01307538..619a764282 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "lint:unused-exports": "ts-node ./ci/lint-unused-exports.ts", "lint:dependencies": "ts-node ./ci/lint-dependencies.ts .", "test": "jest", + "test:lint": "yarn license-check && yarn dedupe --check && yarn run tsc --noEmit && yarn run lint:ci && yarn run lint:unused-exports && yarn run lint:dependencies ", "test:debug": "cross-env NODE_OPTIONS='--inspect-brk' jest", "test:watch": "jest --watch", "test:integration": "jest --forceExit --projects desktop/integration-test", diff --git a/packages/studio-base/src/players/UserNodePlayer/nodeTransformerWorker/typescript/userUtils/pointClouds.ts b/packages/studio-base/src/players/UserNodePlayer/nodeTransformerWorker/typescript/userUtils/pointClouds.ts index 345e97e0ca..ef9a92ce86 100644 --- a/packages/studio-base/src/players/UserNodePlayer/nodeTransformerWorker/typescript/userUtils/pointClouds.ts +++ b/packages/studio-base/src/players/UserNodePlayer/nodeTransformerWorker/typescript/userUtils/pointClouds.ts @@ -1,5 +1,6 @@ +/* eslint-disable @foxglove/no-boolean-parameters */ import { FieldReader, getReader } from "./readers"; -import { Header } from "./types"; +import { Point, Header, RGBA } from "./types"; interface sensor_msgs__PointField { name: string; @@ -55,3 +56,56 @@ export const readPoints = (message: sensor_msgs__PointCloud2): Array => } return points; }; + +// ts-unused-exports:disable-next-line +export function norm({ x, y, z }: Point): number { + return Math.sqrt(x * x + y * y + z * z); +} + +// ts-unused-exports:disable-next-line +export function setRayDistance(pt: Point, distance: number): Point { + const { x, y, z } = pt; + const scale = distance / norm(pt); + return { + x: x * scale, + y: y * scale, + z: z * scale, + }; +} + +// ts-unused-exports:disable-next-line +export function convertToRangeView(points: Point[], range: number, makeColors: boolean): RGBA[] { + const colors: RGBA[] = makeColors ? new Array(points.length) : []; + // First pass to get min and max ranges + let maxRange = Number.MIN_VALUE; + if (makeColors) { + for (const point of points) { + maxRange = Math.max(maxRange, norm(point)); + } + } + // actually move the points and generate colors if specified + for (let i = 0; i < points.length; ++i) { + const pt = points[i]!; + if (makeColors) { + const dist = norm(pt); + if (dist <= range) { + // don't go all the way to white + const extent = 0.8; + // closest to target range is lightest, + // closest to AV is darkest + const other = (extent * dist) / range; + colors[i] = { r: 1, g: other, b: other, a: 1 }; + } else { + // don't go all the way to white + const extent = 0.8; + // closest to target range is lightest, + // closest to max range is darkest + const upper = maxRange - range; + const other = extent * (1.0 - dist / upper); + colors[i] = { r: other, g: other, b: 1, a: 1 }; + } + } + points[i] = setRayDistance(pt, range); + } + return colors; +} From 65c3000178c59c98549475d729c8c873ac5b6ec7 Mon Sep 17 00:00:00 2001 From: Alexandre Neuwald CTW Date: Tue, 16 Apr 2024 10:13:39 +0100 Subject: [PATCH 8/8] remove improvement --- .github/workflows/ci.yml | 7 ++++++- package.json | 1 - 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 08431a23e3..e42e71bf27 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,12 @@ jobs: command: | # lint steps set -x - yarn run test:lint + yarn license-check + yarn dedupe --check + yarn run tsc --noEmit # typecheck files that are not included by webpack or package builds + yarn run lint:ci + yarn run lint:unused-exports + yarn run lint:dependencies - name: packages command: yarn run build:packages - name: web diff --git a/package.json b/package.json index 619a764282..6b01307538 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,6 @@ "lint:unused-exports": "ts-node ./ci/lint-unused-exports.ts", "lint:dependencies": "ts-node ./ci/lint-dependencies.ts .", "test": "jest", - "test:lint": "yarn license-check && yarn dedupe --check && yarn run tsc --noEmit && yarn run lint:ci && yarn run lint:unused-exports && yarn run lint:dependencies ", "test:debug": "cross-env NODE_OPTIONS='--inspect-brk' jest", "test:watch": "jest --watch", "test:integration": "jest --forceExit --projects desktop/integration-test",