diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html
index 51c6b0baf..4a57e25d8 100644
--- a/.storybook/preview-head.html
+++ b/.storybook/preview-head.html
@@ -6,6 +6,69 @@
font-style: normal;
}
+ @font-face {
+ font-family: "Alexandria";
+ src: url("/fonts/Alexandria-Thin.ttf") format("truetype");
+ font-weight: 100;
+ font-style: normal;
+ }
+
+ @font-face {
+ font-family: "Alexandria";
+ src: url("/fonts/Alexandria-ExtraLight.ttf") format("truetype");
+ font-weight: 200;
+ font-style: normal;
+ }
+
+ @font-face {
+ font-family: "Alexandria";
+ src: url("/fonts/Alexandria-Light.ttf") format("truetype");
+ font-weight: 300;
+ font-style: normal;
+ }
+
+ @font-face {
+ font-family: "Alexandria";
+ src: url("/fonts/Alexandria-Regular.ttf") format("truetype");
+ font-weight: 400;
+ font-style: normal;
+ }
+
+ @font-face {
+ font-family: "Alexandria";
+ src: url("/fonts/Alexandria-Medium.ttf") format("truetype");
+ font-weight: 500;
+ font-style: normal;
+ }
+
+ @font-face {
+ font-family: "Alexandria";
+ src: url("/fonts/Alexandria-Medium.ttf") format("truetype");
+ font-weight: 600;
+ font-style: normal;
+ }
+
+ @font-face {
+ font-family: "Alexandria";
+ src: url("/fonts/Alexandria-SemiBold.ttf") format("truetype");
+ font-weight: 700;
+ font-style: normal;
+ }
+
+ @font-face {
+ font-family: "Alexandria";
+ src: url("/fonts/Alexandria-Bold.ttf") format("truetype");
+ font-weight: 800;
+ font-style: normal;
+ }
+
+ @font-face {
+ font-family: "Alexandria";
+ src: url("/fonts/Alexandria-Black.ttf") format("truetype");
+ font-weight: 900;
+ font-style: normal;
+ }
+
#storybook-root {
height: 100%;
}
diff --git a/Dockerfile b/Dockerfile
index 0a9b3f6a2..2e98330e8 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,7 @@
-FROM nginx:1.25.0
+FROM nginx:1.27.2
COPY ./dist/ /usr/share/nginx/html/
COPY ./nginx.conf.template /etc/nginx/conf.d/default.conf.template
RUN rm /usr/share/nginx/html/index.html
-CMD ["/bin/sh" , "-c" , "envsubst '${PLUGIN_API_URL} ${PLUGIN_API_TOKEN}' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'"]
\ No newline at end of file
+CMD ["/bin/sh" , "-c" , "cp /etc/nginx/conf.d/default.conf.template /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'"]
\ No newline at end of file
diff --git a/assets/index.web.ejs b/assets/index.web.ejs
index bfa5ab8a6..e031f38a5 100644
--- a/assets/index.web.ejs
+++ b/assets/index.web.ejs
@@ -9,6 +9,69 @@
font-weight: 500;
font-style: normal;
}
+
+ @font-face {
+ font-family: "Alexandria";
+ src: url("/fonts/Alexandria-Thin.ttf") format("truetype");
+ font-weight: 100;
+ font-style: normal;
+ }
+
+ @font-face {
+ font-family: "Alexandria";
+ src: url("/fonts/Alexandria-ExtraLight.ttf") format("truetype");
+ font-weight: 200;
+ font-style: normal;
+ }
+
+ @font-face {
+ font-family: "Alexandria";
+ src: url("/fonts/Alexandria-Light.ttf") format("truetype");
+ font-weight: 300;
+ font-style: normal;
+ }
+
+ @font-face {
+ font-family: "Alexandria";
+ src: url("/fonts/Alexandria-Regular.ttf") format("truetype");
+ font-weight: 400;
+ font-style: normal;
+ }
+
+ @font-face {
+ font-family: "Alexandria";
+ src: url("/fonts/Alexandria-Medium.ttf") format("truetype");
+ font-weight: 500;
+ font-style: normal;
+ }
+
+ @font-face {
+ font-family: "Alexandria";
+ src: url("/fonts/Alexandria-Medium.ttf") format("truetype");
+ font-weight: 600;
+ font-style: normal;
+ }
+
+ @font-face {
+ font-family: "Alexandria";
+ src: url("/fonts/Alexandria-SemiBold.ttf") format("truetype");
+ font-weight: 700;
+ font-style: normal;
+ }
+
+ @font-face {
+ font-family: "Alexandria";
+ src: url("/fonts/Alexandria-Bold.ttf") format("truetype");
+ font-weight: 800;
+ font-style: normal;
+ }
+
+ @font-face {
+ font-family: "Alexandria";
+ src: url("/fonts/Alexandria-Black.ttf") format("truetype");
+ font-weight: 900;
+ font-style: normal;
+ }
diff --git a/nginx.conf.template b/nginx.conf.template
index efff1e1d5..475513e7d 100644
--- a/nginx.conf.template
+++ b/nginx.conf.template
@@ -1,15 +1,5 @@
-map "${PLUGIN_API_TOKEN}" $headerValue {
- "" "";
- "~*^(.+)$" "Token $1";
-}
-
server {
listen 80;
root /usr/share/nginx/html;
absolute_redirect off;
-
- location /api/ {
- proxy_pass ${PLUGIN_API_URL}/;
- proxy_set_header Authorization $headerValue;
- }
}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 8448bf711..a60e0136e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "digma-ui",
- "version": "1.1.0",
+ "version": "2.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "digma-ui",
- "version": "1.1.0",
+ "version": "2.0.0",
"license": "MIT",
"dependencies": {
"@floating-ui/react": "^0.25.1",
diff --git a/package.json b/package.json
index 29ff8a2b1..72c25ee78 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "digma-ui",
- "version": "1.1.0",
+ "version": "2.0.0",
"description": "Digma UI",
"main": "dist/index.js",
"scripts": {
@@ -15,6 +15,7 @@
"build-storybook": "storybook build",
"build:dashboard:dev": "webpack --config webpack.dev.ts --env app=dashboard",
"build:documentation:dev": "webpack --config webpack.dev.ts --env app=documentation",
+ "build:ide-launcher:dev": "webpack --config webpack.dev.ts --env app=ideLauncher",
"build:installation-wizard:dev": "webpack --config webpack.dev.ts --env app=installationWizard",
"build:main:dev": "webpack --config webpack.dev.ts --env app=main",
"build:notifications:dev": "webpack --config webpack.dev.ts --env app=notifications",
@@ -24,6 +25,7 @@
"build:dev:web": "webpack --config webpack.dev.ts --env platform=Web",
"build:dashboard:prod": "webpack --config webpack.prod.ts --env app=dashboard",
"build:documentation:prod": "webpack --config webpack.prod.ts --env app=documentation",
+ "build:ide-launcher:prod": "webpack --config webpack.prod.ts --env app=ideLauncher",
"build:installation-wizard:prod": "webpack --config webpack.prod.ts --env app=installationWizard",
"build:main:prod": "webpack --config webpack.prod.ts --env app=main",
"build:notifications:prod": "webpack --config webpack.prod.ts --env app=notifications",
diff --git a/public/fonts/Alexandria-Black.ttf b/public/fonts/Alexandria-Black.ttf
new file mode 100644
index 000000000..a16503cb2
Binary files /dev/null and b/public/fonts/Alexandria-Black.ttf differ
diff --git a/public/fonts/Alexandria-Bold.ttf b/public/fonts/Alexandria-Bold.ttf
new file mode 100644
index 000000000..911d48c5b
Binary files /dev/null and b/public/fonts/Alexandria-Bold.ttf differ
diff --git a/public/fonts/Alexandria-ExtraBold.ttf b/public/fonts/Alexandria-ExtraBold.ttf
new file mode 100644
index 000000000..ebb205981
Binary files /dev/null and b/public/fonts/Alexandria-ExtraBold.ttf differ
diff --git a/public/fonts/Alexandria-ExtraLight.ttf b/public/fonts/Alexandria-ExtraLight.ttf
new file mode 100644
index 000000000..3dc2c5454
Binary files /dev/null and b/public/fonts/Alexandria-ExtraLight.ttf differ
diff --git a/public/fonts/Alexandria-Light.ttf b/public/fonts/Alexandria-Light.ttf
new file mode 100644
index 000000000..2982949de
Binary files /dev/null and b/public/fonts/Alexandria-Light.ttf differ
diff --git a/public/fonts/Alexandria-Medium.ttf b/public/fonts/Alexandria-Medium.ttf
new file mode 100644
index 000000000..c98ee3585
Binary files /dev/null and b/public/fonts/Alexandria-Medium.ttf differ
diff --git a/public/fonts/Alexandria-Regular.ttf b/public/fonts/Alexandria-Regular.ttf
new file mode 100644
index 000000000..bd9a4552a
Binary files /dev/null and b/public/fonts/Alexandria-Regular.ttf differ
diff --git a/public/fonts/Alexandria-SemiBold.ttf b/public/fonts/Alexandria-SemiBold.ttf
new file mode 100644
index 000000000..73caf111c
Binary files /dev/null and b/public/fonts/Alexandria-SemiBold.ttf differ
diff --git a/public/fonts/Alexandria-Thin.ttf b/public/fonts/Alexandria-Thin.ttf
new file mode 100644
index 000000000..630aa5451
Binary files /dev/null and b/public/fonts/Alexandria-Thin.ttf differ
diff --git a/src/components/IdeLauncher/IdeLauncher.stories.tsx b/src/components/IdeLauncher/IdeLauncher.stories.tsx
new file mode 100644
index 000000000..5ca0f19cc
--- /dev/null
+++ b/src/components/IdeLauncher/IdeLauncher.stories.tsx
@@ -0,0 +1,19 @@
+import { Meta, StoryObj } from "@storybook/react";
+import { IdeLauncher } from ".";
+
+// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
+const meta: Meta = {
+ title: "IDE Launcher/IdeLauncher",
+ component: IdeLauncher,
+ parameters: {
+ // More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout
+ layout: "fullscreen"
+ }
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args
+export const Default: Story = {};
diff --git a/src/components/IdeLauncher/index.tsx b/src/components/IdeLauncher/index.tsx
new file mode 100644
index 000000000..be52f30ba
--- /dev/null
+++ b/src/components/IdeLauncher/index.tsx
@@ -0,0 +1,171 @@
+import axios from "axios";
+import { useEffect, useState } from "react";
+import { Helmet } from "react-helmet";
+import { useTheme } from "styled-components";
+import { isString } from "../../typeGuards/isString";
+import { getThemeKind } from "../common/App/styles";
+import { Select } from "../common/v3/Select";
+import { SelectItem } from "../common/v3/Select/types";
+import { scanIDEs } from "./scanIDEs";
+import * as s from "./styles";
+
+const SELECT_VALUE_DELIMITER = ":";
+
+const getURLQueryParams = (url: string) => {
+ const searchParams = new URLSearchParams(url);
+ const params: Record = {};
+ for (const [key, value] of searchParams.entries()) {
+ params[key] = value;
+ }
+ return params;
+};
+
+export const IdeLauncher = () => {
+ const theme = useTheme();
+ const themeKind = getThemeKind(theme);
+ const [isInitialLoading, setInitialLoading] = useState(true);
+ // const [isOpening, setIsOpening] = useState(false);
+ const [items, setItems] = useState([]);
+ const params = getURLQueryParams(window.location.search);
+ const isMobile = ["Android", "iPhone", "iPad"].some((x) =>
+ window.navigator.userAgent.includes(x)
+ );
+
+ const handleSelectChange = (value: string | string[]) => {
+ const selectedValue = isString(value) ? value : value[0];
+ const [port, project] = selectedValue.split(SELECT_VALUE_DELIMITER);
+
+ const pluginParams = Object.entries(params).reduce((acc, [key, value]) => {
+ const KEY_PREFIX = "plugin.";
+ if (key.startsWith(KEY_PREFIX)) {
+ const newKey = key.replace(KEY_PREFIX, "");
+ acc[newKey] = value;
+ }
+
+ return acc;
+ }, {} as Record);
+
+ axios
+ .get(`http://localhost:${port}/api/digma/show`, {
+ params: { ...pluginParams, projectName: project }
+ })
+ .then(() => {
+ // TODO: handle response
+ })
+ .catch(() => {
+ // TODO: handle error
+ });
+
+ // setIsOpening(true);
+
+ const itemToSelect = items.find((item) => item.value === selectedValue);
+ setItems(
+ items.map((item) => ({ ...item, selected: item === itemToSelect }))
+ );
+ };
+
+ useEffect(() => {
+ async function getIDEsInfo() {
+ const ideInfo = await scanIDEs();
+ const projects = ideInfo
+ .filter((x) => x.response.isCentralized)
+ .flatMap((info) =>
+ info.response.openProjects.map((project) => ({
+ ...info,
+ project: project,
+ port: info.port
+ }))
+ );
+
+ setItems(
+ projects.map((x) => ({
+ label: `${x.response.name} (${x.project})`,
+ description: `${x.response.name} (${x.project})`,
+ value: [x.port, x.project].join(SELECT_VALUE_DELIMITER),
+ enabled: true,
+ selected: false
+ }))
+ );
+ setInitialLoading(false);
+ }
+
+ if (!isMobile) {
+ void getIDEsInfo();
+ }
+ }, [isMobile]);
+
+ const selectedItem = items.find((item) => item.selected);
+
+ const renderContent = () => {
+ if (isMobile) {
+ return (
+
+ Failed to open
+
+ This cannot be opened on your mobile device. Please switch to a
+ desktop with an IDE installed.
+
+
+ );
+ }
+
+ return isInitialLoading ? (
+ <>Scanning running IDEs...>
+ ) : (
+ <>
+
+ Select an IDE to view the Digma issue
+
+ {items.length > 0 ? (
+ <>
+ Please select the IDE project you'd like to open all
+ endpoints in the {params.environment ?? "selected"} environment.
+ >
+ ) : (
+ <>No IDEs with enabled Digma plugin found>
+ )}
+
+
+ {items.length > 0 && (
+
+
+
+ )}
+ >
+ );
+ };
+
+ return (
+
+
+ IDE Launcher
+
+
+
+
+
+
+
+ {renderContent()}
+
+ © {new Date().getFullYear()}
+
+ digma.ai
+
+ · All Rights Reserved
+
+
+ );
+};
diff --git a/src/components/IdeLauncher/scanIDEs.ts b/src/components/IdeLauncher/scanIDEs.ts
new file mode 100644
index 000000000..5cb23fb2b
--- /dev/null
+++ b/src/components/IdeLauncher/scanIDEs.ts
@@ -0,0 +1,40 @@
+import axios from "axios";
+import { isString } from "../../typeGuards/isString";
+import { PluginInfo } from "./types";
+
+const DEFAULT_PORT = 63342;
+const PORT_RANGE = 20;
+const ABOUT_PATH = "api/digma/about";
+
+export const scanIDEs = async () => {
+ const instances = Array.from(
+ { length: PORT_RANGE },
+ (_, i) => DEFAULT_PORT + i
+ ).map((port) => ({
+ port,
+ url: `http://localhost:${port}/${ABOUT_PATH}`
+ }));
+
+ const responses = await Promise.allSettled(
+ instances.map((x) =>
+ axios
+ .get(x.url)
+ .then((response) => ({ port: x.port, response: response.data }))
+ .catch((error) => ({
+ port: x.port,
+ response: axios.isAxiosError(error)
+ ? `${error.message}`
+ : "Unknown error"
+ }))
+ )
+ );
+
+ const successfulResponses = responses.filter(
+ (x) => x.status === "fulfilled" && !isString(x.value)
+ ) as unknown as PromiseFulfilledResult<{
+ port: number;
+ response: PluginInfo;
+ }>[];
+
+ return successfulResponses.map((x) => x.value);
+};
diff --git a/src/components/IdeLauncher/styles.ts b/src/components/IdeLauncher/styles.ts
new file mode 100644
index 000000000..7da30390f
--- /dev/null
+++ b/src/components/IdeLauncher/styles.ts
@@ -0,0 +1,73 @@
+import styled from "styled-components";
+
+export const Container = styled.div`
+ background: ${({ theme }) => theme.colors.v3.surface.secondary};
+ display: flex;
+ flex-direction: column;
+ min-height: 100%;
+ font-family: Alexandria, sans-serif;
+`;
+
+export const Header = styled.header`
+ padding: 0 32px;
+ height: 100px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+`;
+
+export const Logo = styled.img`
+ width: 126px;
+`;
+
+export const Content = styled.main`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ flex-grow: 1;
+ padding: 88px 60px 0;
+ gap: 32px;
+`;
+
+export const TextContainer = styled.div`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 24px;
+ max-width: 820px;
+ text-align: center;
+`;
+
+export const Title = styled.h1`
+ color: ${({ theme }) => theme.colors.v3.text.primary};
+ font-size: 40px;
+ font-weight: 500;
+`;
+
+export const Description = styled.p`
+ color: ${({ theme }) => theme.colors.v3.text.tertiary};
+ font-size: 24px;
+ font-weight: 400;
+ line-height: 150%;
+`;
+
+export const SelectContainer = styled.div`
+ width: 228px;
+`;
+
+export const Footer = styled.footer`
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 4px;
+ color: ${({ theme }) => theme.colors.v3.text.primary};
+ font-size: 14px;
+ line-height: 30px;
+ opacity: 0.35;
+ padding: 32px 68px;
+`;
+
+export const FooterLink = styled.a`
+ color: inherit;
+ text-decoration: none;
+`;
diff --git a/src/components/IdeLauncher/types.ts b/src/components/IdeLauncher/types.ts
new file mode 100644
index 000000000..2bf3c3e78
--- /dev/null
+++ b/src/components/IdeLauncher/types.ts
@@ -0,0 +1,12 @@
+export interface PluginInfo {
+ name: string;
+ productName: string;
+ edition: string;
+ baselineVersion: number;
+ buildNumber?: string;
+ pluginVersion: string;
+ backendVersion: string;
+ backendDeploymentType: string;
+ isCentralized: boolean;
+ openProjects: string[];
+}
diff --git a/src/components/common/App/index.tsx b/src/components/common/App/index.tsx
index c6ae12cbd..00a88b333 100644
--- a/src/components/common/App/index.tsx
+++ b/src/components/common/App/index.tsx
@@ -4,7 +4,9 @@ import { ThemeProvider } from "styled-components";
import { actions } from "../../../actions";
import { dispatcher } from "../../../dispatcher";
import { Theme } from "../../../globals";
+import { useColorScheme } from "../../../hooks/useColorScheme";
import { logger } from "../../../logging";
+import { platform } from "../../../platform";
import { useStore } from "../../../store/useStore";
import { isBoolean } from "../../../typeGuards/isBoolean";
import { isNull } from "../../../typeGuards/isNull";
@@ -58,7 +60,10 @@ const defaultMainFont = isString(window.mainFont) ? window.mainFont : "";
const defaultCodeFont = isString(window.codeFont) ? window.codeFont : "";
export const App = ({ theme, children, id }: AppProps) => {
- const [currentTheme, setCurrentTheme] = useState(theme ?? getTheme());
+ const colorScheme = useColorScheme();
+ const [currentTheme, setCurrentTheme] = useState(
+ theme ?? getTheme() ?? colorScheme
+ );
const [mainFont, setMainFont] = useState(defaultMainFont);
const [codeFont, setCodeFont] = useState(defaultCodeFont);
const [config, setConfig] = useState(useContext(ConfigContext));
@@ -99,6 +104,12 @@ export const App = ({ theme, children, id }: AppProps) => {
}
}, [theme]);
+ useEffect(() => {
+ if (platform === "Web") {
+ setCurrentTheme(colorScheme);
+ }
+ }, [colorScheme]);
+
useEffect(() => {
const handleSetTheme = (data: unknown) => {
if (isObject(data) && isTheme(data.theme)) {
diff --git a/src/components/common/DismissPanel/index.tsx b/src/components/common/DismissPanel/index.tsx
index 64b411a47..4923e4c66 100644
--- a/src/components/common/DismissPanel/index.tsx
+++ b/src/components/common/DismissPanel/index.tsx
@@ -1,4 +1,4 @@
-import { forwardRef, useState } from "react";
+import { useState } from "react";
import { usePrevious } from "../../../hooks/usePrevious";
import { CrossIcon } from "../icons/CrossIcon";
import { NewButton } from "../v3/NewButton";
@@ -6,7 +6,7 @@ import { Spinner } from "../v3/Spinner";
import * as s from "./styles";
import { DismissPanelProps } from "./types";
-const DismissPanelComponent = ({
+export const DismissPanel = ({
state,
onShow,
onDismiss,
@@ -76,5 +76,3 @@ const DismissPanelComponent = ({
>
);
};
-
-export const DismissPanel = forwardRef(DismissPanelComponent);
diff --git a/src/containers/IdeLauncher/index.tsx b/src/containers/IdeLauncher/index.tsx
new file mode 100644
index 000000000..54a851bd9
--- /dev/null
+++ b/src/containers/IdeLauncher/index.tsx
@@ -0,0 +1,32 @@
+import { createRoot } from "react-dom/client";
+import {
+ cancelMessage,
+ initializeDigmaMessageListener,
+ sendMessage
+} from "../../api";
+import { App } from "../../components/common/App";
+import { IdeLauncher } from "../../components/IdeLauncher";
+import { dispatcher } from "../../dispatcher";
+import { handleUncaughtError } from "../../utils/handleUncaughtError";
+
+const APP_ID = "documentation";
+
+initializeDigmaMessageListener(dispatcher);
+
+window.sendMessageToDigma = sendMessage;
+window.cancelMessageToDigma = cancelMessage;
+
+window.addEventListener("error", (e) => {
+ handleUncaughtError(APP_ID, e);
+});
+
+const rootElement = document.getElementById("root");
+
+if (rootElement) {
+ const root = createRoot(rootElement);
+ root.render(
+
+
+
+ );
+}
diff --git a/src/hooks/useColorScheme.ts b/src/hooks/useColorScheme.ts
new file mode 100644
index 000000000..70a6a526d
--- /dev/null
+++ b/src/hooks/useColorScheme.ts
@@ -0,0 +1,23 @@
+import { useEffect, useState } from "react";
+
+const MEDIA_QUERY = "(prefers-color-scheme: dark)";
+
+export const useColorScheme = () => {
+ const [isDarkTheme, setIsDarkTheme] = useState(
+ window.matchMedia(MEDIA_QUERY).matches
+ );
+
+ useEffect(() => {
+ const handleMediaChange = (e: MediaQueryListEvent) => {
+ setIsDarkTheme(e.matches);
+ };
+
+ const media = window.matchMedia(MEDIA_QUERY);
+
+ media.addEventListener("change", handleMediaChange);
+
+ return () => media.removeEventListener("change", handleMediaChange);
+ }, []);
+
+ return isDarkTheme ? "dark" : "light";
+};
diff --git a/webpack.common.ts b/webpack.common.ts
index 12be47692..a2e02250f 100644
--- a/webpack.common.ts
+++ b/webpack.common.ts
@@ -5,15 +5,29 @@ import path from "path";
import { Configuration as WebpackConfiguration } from "webpack";
import { WebpackEnv, entries } from "./webpackEntries";
+const toKebabCase = (str: string): string =>
+ str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
+
const getConfig = (env: WebpackEnv): WebpackConfiguration => {
- const entriesToBuild = env.app
+ let entriesToBuild: Record = env.app
? { [env.app]: entries[env.app].entry }
- : Object.entries(entries).reduce((acc, [name, entry]) => {
- return {
+ : Object.entries(entries).reduce(
+ (acc, [name, entry]) => ({
...acc,
[name]: entry.entry
- };
- }, {});
+ }),
+ {}
+ );
+
+ if (env.platform === "Web") {
+ entriesToBuild = Object.entries(entriesToBuild).reduce(
+ (acc, [k, v]) => ({
+ ...acc,
+ [toKebabCase(k)]: v
+ }),
+ {}
+ );
+ }
return {
entry: entriesToBuild,
diff --git a/webpackEntries.ts b/webpackEntries.ts
index 04e2eb7a0..2814a43b2 100644
--- a/webpackEntries.ts
+++ b/webpackEntries.ts
@@ -9,6 +9,9 @@ export const entries: AppEntries = {
entry: path.resolve(__dirname, "./src/containers/Documentation/index.tsx"),
environmentVariables: ["documentationPage"]
},
+ ideLauncher: {
+ entry: path.resolve(__dirname, "./src/containers/IdeLauncher/index.tsx")
+ },
installationWizard: {
entry: path.resolve(
__dirname,