Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,12 @@
"antd": "5.26.4",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-router-dom": "^6.25.1"
"react-router-dom": "^6.25.1",
"@novnc/novnc": "^1.6.0"
},
"dependencies": {
"@monaco-editor/react": "4.6.0",
"@novnc/novnc": "^1.6.0",
"@reduxjs/toolkit": "2.2.5",
"@xterm/addon-attach": "0.11.0",
"@xterm/addon-fit": "0.10.0",
Expand Down
819 changes: 819 additions & 0 deletions src/components/molecules/Terminals/VMVNC/VMVNC.tsx

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions src/components/molecules/Terminals/VMVNC/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { VMVNC } from './VMVNC'
export type { TVMVNCProps } from './VMVNC'

109 changes: 109 additions & 0 deletions src/components/molecules/Terminals/VMVNC/styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import styled from 'styled-components'

type TContainerProps = {
$substractHeight: number
}

const Container = styled.div<TContainerProps>`
height: calc(100vh - ${({ $substractHeight }) => $substractHeight}px);
display: flex;
flex-direction: column;
background-color: #1e1e1e;
position: relative;

* {
scrollbar-width: thin;
}
`

type TCustomCardProps = {
$isVisible?: boolean
$substractHeight: number
}

const CustomCard = styled.div<TCustomCardProps>`
visibility: ${({ $isVisible }) => ($isVisible ? 'visible' : 'hidden')};
height: 100%;
display: flex;
flex-direction: column;
background-color: #1e1e1e;
position: relative;

* {
scrollbar-width: thin;
}
`

type TFullWidthDivProps = {
$substractHeight: number
}

const FullWidthDiv = styled.div<TFullWidthDivProps>`
display: flex;
justify-content: center;
align-items: center;
flex: 1;
overflow: hidden;
background-color: #000000;
min-width: 0; /* Allow flex item to shrink */
transition: margin-right 0.3s ease;
`

const ContentWrapper = styled.div`
display: flex;
flex: 1;
overflow: hidden;
position: relative;
min-width: 0; /* Allow flex item to shrink */
`

const StatusBar = styled.div`
background-color: #2d2d2d;
color: #ffffff;
padding: 8px 16px;
font-size: 12px;
border-bottom: 1px solid #3d3d3d;
display: flex;
align-items: center;
justify-content: flex-start;
flex-shrink: 0;
z-index: 10;
flex-wrap: wrap;
gap: 4px;
`

const StatusDivider = styled.span`
color: #666666;
margin: 0 8px;
user-select: none;
`

const LoadingContainer = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px;
color: #ffffff;
`

const ErrorContainer = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px;
color: #ffffff;
`

export const Styled = {
Container,
FullWidthDiv,
CustomCard,
StatusBar,
LoadingContainer,
ErrorContainer,
ContentWrapper,
StatusDivider,
}

1 change: 1 addition & 0 deletions src/components/molecules/Terminals/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './PodTerminal'
export * from './NodeTerminal'
export * from './PodLogs'
export * from './PodLogsMonaco'
export * from './VMVNC'
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
PodTerminal,
NodeTerminal,
PodLogs,
VMVNC,
YamlEditorSingleton,
AntdLink,
VisibilityContainer,
Expand Down Expand Up @@ -62,6 +63,7 @@ export const DynamicComponents: TRendererComponents<TDynamicComponentsAppTypeMap
PodTerminal,
NodeTerminal,
PodLogs,
VMVNC,
YamlEditorSingleton,
VisibilityContainer,
ArrayOfObjectsToKeyValues,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { FC } from 'react'
import { Flex, Spin } from 'antd'
import { VMVNC as VNC } from 'components'
import { TDynamicComponentsAppTypeMap } from '../../types'
import { useMultiQuery } from '../../../DynamicRendererWithProviders/multiQueryProvider'
import { usePartsOfUrl } from '../../../DynamicRendererWithProviders/partsOfUrlContext'
import { parseAll } from '../utils'

export const VMVNC: FC<{ data: TDynamicComponentsAppTypeMap['VMVNC']; children?: any }> = ({
data,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
children,
}) => {
const { data: multiQueryData, isLoading: isMultiqueryLoading } = useMultiQuery()

const {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
id,
cluster,
namespace,
vmName,
substractHeight,
...props
} = data

const partsOfUrl = usePartsOfUrl()

const replaceValues = partsOfUrl.partsOfUrl.reduce<Record<string, string | undefined>>((acc, value, index) => {
acc[index.toString()] = value
return acc
}, {})

const clusterPrepared = parseAll({ text: cluster, replaceValues, multiQueryData })

const namespacePrepared = parseAll({ text: namespace, replaceValues, multiQueryData })

const vmNamePrepared = parseAll({ text: vmName, replaceValues, multiQueryData })

if (isMultiqueryLoading) {
return <div>Loading multiquery</div>
}

if (!clusterPrepared || !namespacePrepared || !vmNamePrepared) {
return (
<Flex justify="center">
<Spin />
</Flex>
)
}

return (
<>
<VNC
cluster={clusterPrepared}
namespace={namespacePrepared}
vmName={vmNamePrepared}
substractHeight={substractHeight || 400}
{...props}
/>
{children}
</>
)
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './VMVNC'

Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export * from './EnrichedTable'
export * from './PodTerminal'
export * from './NodeTerminal'
export * from './PodLogs'
export * from './VMVNC'
export * from './YamlEditorSingleton'
export * from './VisibilityContainer'
export * from './ArrayOfObjectsToKeyValues'
Expand Down
7 changes: 7 additions & 0 deletions src/components/organisms/DynamicComponents/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ export type TDynamicComponentsAppTypeMap = {
nodeName: string
substractHeight?: number
}
VMVNC: {
id: number | string
cluster: string
namespace: string
vmName: string
substractHeight?: number
}
PodLogs: {
id: number | string
cluster: string
Expand Down
104 changes: 82 additions & 22 deletions vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import path from 'path'
import dotenv from 'dotenv'
import { defineConfig } from 'vite'
import { defineConfig, Plugin } from 'vite'
import react from '@vitejs/plugin-react-swc'
import { nodePolyfills } from 'vite-plugin-node-polyfills'
import pkg from './package.json'

const { VITE_BASEPREFIX } = process.env

// Note: We handle @novnc/novnc externalization differently for ES and UMD formats
// For ES: it will be bundled via dynamic import (not external)
// For UMD: it will be externalized to avoid top-level await issues
// The external function below handles this by checking if we're building UMD

export default defineConfig({
root: './',
base: VITE_BASEPREFIX || '/toolkit',
Expand All @@ -18,28 +23,83 @@ export default defineConfig({
fileName: format => `openapi-k8s-toolkit.${format}.js`,
},
rollupOptions: {
external: [
'react',
'react-dom',
'react-router-dom',
'@tanstack/react-query',
'@tanstack/react-query-devtools',
'antd',
'@ant-design/icons',
'styled-components',
],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM',
'react-router-dom': 'ReactRouterDOM',
'@tanstack/react-query': 'reactQuery',
'@tanstack/react-query-devtools': 'reactQuery-devtools',
antd: 'antd',
'@ant-design/icons': 'antdIcons',
'styled-components': 'styled',
},
external: (id) => {
// Standard external dependencies
const externals = [
'react',
'react-dom',
'react-router-dom',
'@tanstack/react-query',
'@tanstack/react-query-devtools',
'antd',
'@ant-design/icons',
'styled-components',
]

if (externals.includes(id)) {
return true
}

// For @novnc/novnc:
// - For ES format: don't externalize, let it be bundled via dynamic import
// This allows Vite to create a separate chunk, avoiding top-level await issues
// - For UMD format: we need to externalize to avoid top-level await
// Since external() is called per-output, we check if UMD output exists
// But actually, external() doesn't know which output it's for
// So we'll externalize it for both formats, but the consuming app will bundle it for ES

// Actually, the best approach: don't externalize here
// For ES format, Vite will bundle it via dynamic import
// For UMD format, we'll handle it via the output's globals config
// But that won't prevent bundling...

// Let's try: externalize only if it's clearly a UMD build context
// Since we can't detect format in external(), we'll use a different approach:
// Don't externalize @novnc/novnc - let it be bundled for ES
// For UMD, the build will fail with top-level await, but we'll handle that separately
// Actually, let's externalize it for both to be safe, and let the consuming app handle bundling

// Final approach: externalize @novnc/novnc to prevent top-level await in UMD
// The consuming app's Vite config will resolve it and bundle it properly for ES format
if (id.includes('@novnc/novnc') || id.includes('/novnc/')) {
return true
}

return false
},
output: [
{
format: 'es',
// For ES format, @novnc/novnc will be bundled via dynamic import
// The consuming app's Vite will create a separate chunk for it
// This avoids top-level await issues while still bundling the module
globals: {
react: 'React',
'react-dom': 'ReactDOM',
'react-router-dom': 'ReactRouterDOM',
'@tanstack/react-query': 'reactQuery',
'@tanstack/react-query-devtools': 'reactQuery-devtools',
antd: 'antd',
'@ant-design/icons': 'antdIcons',
'styled-components': 'styled',
},
},
{
format: 'umd',
name: pkg.name,
globals: {
react: 'React',
'react-dom': 'ReactDOM',
'react-router-dom': 'ReactRouterDOM',
'@tanstack/react-query': 'reactQuery',
'@tanstack/react-query-devtools': 'reactQuery-devtools',
antd: 'antd',
'@ant-design/icons': 'antdIcons',
'styled-components': 'styled',
'@novnc/novnc': 'noVNC', // Will need to be loaded separately for UMD
},
},
],
},
sourcemap: true,
minify: false,
Expand Down