mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge branch 'master' into extensions/export-active-theme
This commit is contained in:
commit
96a9209637
@ -84,6 +84,8 @@ jobs:
|
|||||||
displayName: Build bundled extensions
|
displayName: Build bundled extensions
|
||||||
- script: make test
|
- script: make test
|
||||||
displayName: Run tests
|
displayName: Run tests
|
||||||
|
- script: make test-extensions
|
||||||
|
displayName: Run In-tree Extension tests
|
||||||
- script: make integration-mac
|
- script: make integration-mac
|
||||||
displayName: Run integration tests
|
displayName: Run integration tests
|
||||||
- script: make test-extensions
|
- script: make test-extensions
|
||||||
@ -122,8 +124,6 @@ jobs:
|
|||||||
displayName: Cache Yarn packages
|
displayName: Cache Yarn packages
|
||||||
- script: make install-deps
|
- script: make install-deps
|
||||||
displayName: Install dependencies
|
displayName: Install dependencies
|
||||||
- script: make test-extensions
|
|
||||||
displayName: Run In-tree Extension tests
|
|
||||||
- script: make lint
|
- script: make lint
|
||||||
displayName: Lint
|
displayName: Lint
|
||||||
- script: make build-npm
|
- script: make build-npm
|
||||||
@ -132,6 +132,8 @@ jobs:
|
|||||||
displayName: Build bundled extensions
|
displayName: Build bundled extensions
|
||||||
- script: make test
|
- script: make test
|
||||||
displayName: Run tests
|
displayName: Run tests
|
||||||
|
- script: make test-extensions
|
||||||
|
displayName: Run In-tree Extension tests
|
||||||
- bash: |
|
- bash: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install libgconf-2-4 conntrack -y
|
sudo apt-get install libgconf-2-4 conntrack -y
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -15,4 +15,4 @@ src/extensions/*/*.d.ts
|
|||||||
types/extension-api.d.ts
|
types/extension-api.d.ts
|
||||||
types/extension-renderer-api.d.ts
|
types/extension-renderer-api.d.ts
|
||||||
extensions/*/dist
|
extensions/*/dist
|
||||||
docs/extensions/api
|
docs/extensions/api
|
||||||
|
|||||||
7
Makefile
7
Makefile
@ -15,7 +15,8 @@ download-bins:
|
|||||||
yarn download-bins
|
yarn download-bins
|
||||||
|
|
||||||
install-deps:
|
install-deps:
|
||||||
yarn install --frozen-lockfile
|
yarn install --frozen-lockfile --verbose
|
||||||
|
yarn check --verify-tree --integrity
|
||||||
|
|
||||||
compile-dev:
|
compile-dev:
|
||||||
yarn compile:main --cache
|
yarn compile:main --cache
|
||||||
@ -56,10 +57,10 @@ else
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
build-extensions:
|
build-extensions:
|
||||||
$(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), $(MAKE) -C $(dir) build;)
|
$(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), (cd $(dir) && npm install && npm run build || exit $?);)
|
||||||
|
|
||||||
test-extensions:
|
test-extensions:
|
||||||
$(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), $(MAKE) -C $(dir) test;)
|
$(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), (cd $(dir) && npm install --dev && npm run test || exit $?);)
|
||||||
|
|
||||||
build-npm: build-extension-types
|
build-npm: build-extension-types
|
||||||
yarn npm:fix-package-version
|
yarn npm:fix-package-version
|
||||||
|
|||||||
@ -1,130 +1,130 @@
|
|||||||
# Theme color reference
|
# Theme color reference
|
||||||
You can use CSS variables generated from theme `.json` files to style an extension with respect of active theme.
|
You can use theme-based CSS Variables to style an extension according to the active theme.
|
||||||
|
|
||||||
## Base colors
|
## Base colors
|
||||||
- `blue`: blue color.
|
- `--blue`: blue color.
|
||||||
- `magenta`: magenta color.
|
- `--magenta`: magenta color.
|
||||||
- `golden`: gold/yellow color.
|
- `--golden`: gold/yellow color.
|
||||||
- `halfGray`: gray with some apacity applied.
|
- `--halfGray`: gray with some apacity applied.
|
||||||
- `primary`: Lens brand (blue) color.
|
- `--primary`: Lens brand (blue) color.
|
||||||
- `colorSuccess`: successfull operations color.
|
- `--colorSuccess`: successfull operations color.
|
||||||
- `colorOk`: successfull operations (bright version) color.
|
- `--colorOk`: successfull operations (bright version) color.
|
||||||
- `colorInfo`: informational, in-progress color.
|
- `--colorInfo`: informational, in-progress color.
|
||||||
- `colorError`: critical error color.
|
- `--colorError`: critical error color.
|
||||||
- `colorSoftError`: error color.
|
- `--colorSoftError`: error color.
|
||||||
- `colorWarning`: warning color.
|
- `--colorWarning`: warning color.
|
||||||
- `colorVague`: soft gray color for notices, hints etc.
|
- `--colorVague`: soft gray color for notices, hints etc.
|
||||||
- `colorTerminated`: terminated, closed, stale color.
|
- `--colorTerminated`: terminated, closed, stale color.
|
||||||
- `boxShadow`: semi-transparent box-shadow color.
|
- `--boxShadow`: semi-transparent box-shadow color.
|
||||||
|
|
||||||
## Text colors
|
## Text colors
|
||||||
- `textColorPrimary`: foreground text color.
|
- `--textColorPrimary`: foreground text color.
|
||||||
- `textColorSecondary`: foreground text color for different paragraps, parts of text.
|
- `--textColorSecondary`: foreground text color for different paragraps, parts of text.
|
||||||
- `textColorAccent`: foreground text color to highlight its parts.
|
- `--textColorAccent`: foreground text color to highlight its parts.
|
||||||
|
|
||||||
## Border colors
|
## Border colors
|
||||||
- `borderColor`: border color.
|
- `--borderColor`: border color.
|
||||||
- `borderFaintColor`: fainted (lighter or darker, which depends on the theme) border color.
|
- `--borderFaintColor`: fainted (lighter or darker, which depends on the theme) border color.
|
||||||
|
|
||||||
## Layout colors
|
## Layout colors
|
||||||
- `mainBackground`: main background color for the app.
|
- `--mainBackground`: main background color for the app.
|
||||||
- `contentColor`: background color for panels contains some data.
|
- `--contentColor`: background color for panels contains some data.
|
||||||
- `layoutBackground`: background color for layout parts.
|
- `--layoutBackground`: background color for layout parts.
|
||||||
- `layoutTabsBackground`: background color for general tabs.
|
- `--layoutTabsBackground`: background color for general tabs.
|
||||||
- `layoutTabsActiveColor`: foreground color for general tabs.
|
- `--layoutTabsActiveColor`: foreground color for general tabs.
|
||||||
- `layoutTabsLineColor`: background color for lines under general tabs.
|
- `--layoutTabsLineColor`: background color for lines under general tabs.
|
||||||
|
|
||||||
## Sidebar colors
|
## Sidebar colors
|
||||||
- `sidebarLogoBackground`: background color behind logo in sidebar.
|
- `--sidebarLogoBackground`: background color behind logo in sidebar.
|
||||||
- `sidebarActiveColor`: foreground color for active menu items in sidebar.
|
- `--sidebarActiveColor`: foreground color for active menu items in sidebar.
|
||||||
- `sidebarSubmenuActiveColor`: foreground color for active submenu items in sidebar.
|
- `--sidebarSubmenuActiveColor`: foreground color for active submenu items in sidebar.
|
||||||
- `sidebarBackground`: background color for sidebar.
|
- `--sidebarBackground`: background color for sidebar.
|
||||||
|
|
||||||
## Button colors
|
## Button colors
|
||||||
- `buttonPrimaryBackground`: button background color for primary actions.
|
- `--buttonPrimaryBackground`: button background color for primary actions.
|
||||||
- `buttonDefaultBackground`: default button background color.
|
- `--buttonDefaultBackground`: default button background color.
|
||||||
- `buttonAccentBackground`: accent button background color.
|
- `--buttonAccentBackground`: accent button background color.
|
||||||
- `buttonDisabledBackground`: disabled button background color.
|
- `--buttonDisabledBackground`: disabled button background color.
|
||||||
|
|
||||||
## Table colors
|
## Table colors
|
||||||
- `tableBgcStripe`: background color for odd rows in table.
|
- `--tableBgcStripe`: background color for odd rows in table.
|
||||||
- `tableBgcSelected`: background color for selected row in table.
|
- `--tableBgcSelected`: background color for selected row in table.
|
||||||
- `tableHeaderBackground`: background color for table header.
|
- `--tableHeaderBackground`: background color for table header.
|
||||||
- `tableHeaderBorderWidth`: border width under table header.
|
- `--tableHeaderBorderWidth`: border width under table header.
|
||||||
- `tableHeaderBorderColor`: border color for line under table header.
|
- `--tableHeaderBorderColor`: border color for line under table header.
|
||||||
- `tableHeaderColor`: foreground color for table header.
|
- `--tableHeaderColor`: foreground color for table header.
|
||||||
- `tableSelectedRowColor`: foreground color for selected row in table.
|
- `--tableSelectedRowColor`: foreground color for selected row in table.
|
||||||
|
|
||||||
## Dock colors
|
## Dock colors
|
||||||
- `dockHeadBackground`: background color for dock's header.
|
- `--dockHeadBackground`: background color for dock's header.
|
||||||
- `dockInfoBackground`: background color for dock's info panel.
|
- `--dockInfoBackground`: background color for dock's info panel.
|
||||||
- `dockInfoBorderColor`: border color for dock's info panel.
|
- `--dockInfoBorderColor`: border color for dock's info panel.
|
||||||
|
|
||||||
## Helm chart colors
|
## Helm chart colors
|
||||||
- `helmLogoBackground`: background color for chart logo.
|
- `--helmLogoBackground`: background color for chart logo.
|
||||||
- `helmImgBackground`: background color for chart image.
|
- `--helmImgBackground`: background color for chart image.
|
||||||
- `helmStableRepo`: background color for stable repo.
|
- `--helmStableRepo`: background color for stable repo.
|
||||||
- `helmIncubatorRepo`: background color for incubator repo.
|
- `--helmIncubatorRepo`: background color for incubator repo.
|
||||||
- `helmDescriptionHr`: Helm chart description separator line color.
|
- `--helmDescriptionHr`: Helm chart description separator line color.
|
||||||
- `helmDescriptionBlockqouteColor`: Helm chart description blockquote color.
|
- `--helmDescriptionBlockqouteColor`: Helm chart description blockquote color.
|
||||||
- `helmDescriptionBlockqouteBorder`: Helm chart description blockquote border color.
|
- `--helmDescriptionBlockqouteBorder`: Helm chart description blockquote border color.
|
||||||
- `helmDescriptionBlockquoteBackground`: Helm chart description blockquote background color.
|
- `--helmDescriptionBlockquoteBackground`: Helm chart description blockquote background color.
|
||||||
- `helmDescriptionHeaders`: Helm chart description headers color.
|
- `--helmDescriptionHeaders`: Helm chart description headers color.
|
||||||
- `helmDescriptionH6`: Helm chart description header foreground color.
|
- `--helmDescriptionH6`: Helm chart description header foreground color.
|
||||||
- `helmDescriptionTdBorder`: Helm chart description table cell border color.
|
- `--helmDescriptionTdBorder`: Helm chart description table cell border color.
|
||||||
- `helmDescriptionTrBackground`: Helm chart description table row background color.
|
- `--helmDescriptionTrBackground`: Helm chart description table row background color.
|
||||||
- `helmDescriptionCodeBackground`: Helm chart description code background color.
|
- `--helmDescriptionCodeBackground`: Helm chart description code background color.
|
||||||
- `helmDescriptionPreBackground`: Helm chart description pre background color.
|
- `--helmDescriptionPreBackground`: Helm chart description pre background color.
|
||||||
- `helmDescriptionPreColor`: Helm chart description pre foreground color.
|
- `--helmDescriptionPreColor`: Helm chart description pre foreground color.
|
||||||
|
|
||||||
## Terminal colors
|
## Terminal colors
|
||||||
- `terminalBackground`: Terminal background color.
|
- `--terminalBackground`: Terminal background color.
|
||||||
- `terminalForeground`: Terminal foreground color.
|
- `--terminalForeground`: Terminal foreground color.
|
||||||
- `terminalCursor`: Terminal cursor color.
|
- `--terminalCursor`: Terminal cursor color.
|
||||||
- `terminalCursorAccent`: Terminal cursor accent color.
|
- `--terminalCursorAccent`: Terminal cursor accent color.
|
||||||
- `terminalSelection`: Terminal selection background color.
|
- `--terminalSelection`: Terminal selection background color.
|
||||||
- `terminalBlack`: Terminal black color.
|
- `--terminalBlack`: Terminal black color.
|
||||||
- `terminalRed`: Terminal red color.
|
- `--terminalRed`: Terminal red color.
|
||||||
- `terminalGreen`: Terminal green color.
|
- `--terminalGreen`: Terminal green color.
|
||||||
- `terminalYellow`: Terminal yellow color.
|
- `--terminalYellow`: Terminal yellow color.
|
||||||
- `terminalBlue`: Terminal blue color.
|
- `--terminalBlue`: Terminal blue color.
|
||||||
- `terminalMagenta`: Terminal magenta color.
|
- `--terminalMagenta`: Terminal magenta color.
|
||||||
- `terminalCyan`: Terminal cyan color.
|
- `--terminalCyan`: Terminal cyan color.
|
||||||
- `terminalWhite`: Terminal white color.
|
- `--terminalWhite`: Terminal white color.
|
||||||
- `terminalBrightBlack`: Terminal bright black color.
|
- `--terminalBrightBlack`: Terminal bright black color.
|
||||||
- `terminalBrightRed`: Terminal bright red color.
|
- `--terminalBrightRed`: Terminal bright red color.
|
||||||
- `terminalBrightGreen`: Terminal bright green color.
|
- `--terminalBrightGreen`: Terminal bright green color.
|
||||||
- `terminalBrightYellow`: Terminal bright yellow color.
|
- `--terminalBrightYellow`: Terminal bright yellow color.
|
||||||
- `terminalBrightBlue`: Terminal bright blue color.
|
- `--terminalBrightBlue`: Terminal bright blue color.
|
||||||
- `terminalBrightMagenta`: Terminal bright magenta color.
|
- `--terminalBrightMagenta`: Terminal bright magenta color.
|
||||||
- `terminalBrightCyan`: Terminal bright cyan color.
|
- `--terminalBrightCyan`: Terminal bright cyan color.
|
||||||
- `terminalBrightWhite`: Terminal bright white color.
|
- `--terminalBrightWhite`: Terminal bright white color.
|
||||||
|
|
||||||
## Dialog colors
|
## Dialog colors
|
||||||
- `dialogHeaderBackground`: background color for dialog header.
|
- `--dialogHeaderBackground`: background color for dialog header.
|
||||||
- `dialogFooterBackground`: background color for dialog footer.
|
- `--dialogFooterBackground`: background color for dialog footer.
|
||||||
|
|
||||||
## Detail panel (Drawer) colors
|
## Detail panel (Drawer) colors
|
||||||
- `drawerTitleText`: drawer title foreground color.
|
- `--drawerTitleText`: drawer title foreground color.
|
||||||
- `drawerSubtitleBackground`: drawer subtitle foreground color.
|
- `--drawerSubtitleBackground`: drawer subtitle foreground color.
|
||||||
- `drawerItemNameColor`: foreground color for item name in drawer.
|
- `--drawerItemNameColor`: foreground color for item name in drawer.
|
||||||
- `drawerItemValueColor`: foreground color for item value in drawer.
|
- `--drawerItemValueColor`: foreground color for item value in drawer.
|
||||||
|
|
||||||
## Misc colors
|
## Misc colors
|
||||||
- `logsBackground`: background color for pod logs.
|
- `--logsBackground`: background color for pod logs.
|
||||||
- `clusterMenuBackground`: background color for cluster menu.
|
- `--clusterMenuBackground`: background color for cluster menu.
|
||||||
- `clusterMenuBorderColor`: border color for cluster menu.
|
- `--clusterMenuBorderColor`: border color for cluster menu.
|
||||||
- `clusterSettingsBackground`: background color for cluster settings.
|
- `--clusterSettingsBackground`: background color for cluster settings.
|
||||||
- `addClusterIconColor`: add cluster button background color.
|
- `--addClusterIconColor`: add cluster button background color.
|
||||||
- `iconActiveColor`: active cluster icon foreground color.
|
- `--iconActiveColor`: active cluster icon foreground color.
|
||||||
- `iconActiveBackground`: active cluster icon background color.
|
- `--iconActiveBackground`: active cluster icon background color.
|
||||||
- `filterAreaBackground`: page filter area (where selected namespaces are lister) background color.
|
- `--filterAreaBackground`: page filter area (where selected namespaces are lister) background color.
|
||||||
- `chartStripesColor`: bar chart zebra stripes background color.
|
- `--chartStripesColor`: bar chart zebra stripes background color.
|
||||||
- `chartCapacityColor`: background color for capacity values in bar charts.
|
- `--chartCapacityColor`: background color for capacity values in bar charts.
|
||||||
- `pieChartDefaultColor`: default background color for pie chart values.
|
- `--pieChartDefaultColor`: default background color for pie chart values.
|
||||||
- `selectOptionHoveredColor`: foregrond color for selected element in dropdown list.
|
- `--selectOptionHoveredColor`: foregrond color for selected element in dropdown list.
|
||||||
- `lineProgressBackground`: background color for progress line.
|
- `--lineProgressBackground`: background color for progress line.
|
||||||
- `radioActiveBackground`: background color for active radio buttons.
|
- `--radioActiveBackground`: background color for active radio buttons.
|
||||||
- `menuActiveBackground`: background color for active menu items.
|
- `--menuActiveBackground`: background color for active menu items.
|
||||||
|
|
||||||
In most cases you would only need base, text and some of the layout colors.
|
In most cases you would only need base, text and some of the layout colors.
|
||||||
|
|||||||
@ -100,6 +100,8 @@ import { ExamplePage } from "./src/example-page"
|
|||||||
export default class ExampleRendererExtension extends LensRendererExtension {
|
export default class ExampleRendererExtension extends LensRendererExtension {
|
||||||
globalPages = [
|
globalPages = [
|
||||||
{
|
{
|
||||||
|
id: "example",
|
||||||
|
routePath: "/example",
|
||||||
components: {
|
components: {
|
||||||
Page: ExamplePage,
|
Page: ExamplePage,
|
||||||
}
|
}
|
||||||
@ -109,6 +111,7 @@ export default class ExampleRendererExtension extends LensRendererExtension {
|
|||||||
globalPageMenus = [
|
globalPageMenus = [
|
||||||
{
|
{
|
||||||
title: "Example page", // used in icon's tooltip
|
title: "Example page", // used in icon's tooltip
|
||||||
|
target: { pageId: "example" }
|
||||||
components: {
|
components: {
|
||||||
Icon: () => <Component.Icon material="arrow"/>,
|
Icon: () => <Component.Icon material="arrow"/>,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,7 +53,7 @@ When Lens is loaded, it transforms the selected theme `json` file into a list of
|
|||||||
|
|
||||||
When the user changes the theme, the process is repeated, and new CSS Variables appear instead of previous ones.
|
When the user changes the theme, the process is repeated, and new CSS Variables appear instead of previous ones.
|
||||||
|
|
||||||
If you want to follow a selected theme to keep the 'native' Lens look and feel, respecting the light/dark appearance of your extension, you can use the provided variables and build-in Lens components such as buttons, dropdowns, checkboxes etc.
|
If you want to follow a selected theme to keep the 'native' Lens look and feel, respecting the light/dark appearance of your extension, you can use the provided variables and built-in Lens components such as `Button`, `Select`, `Table`, etc.
|
||||||
|
|
||||||
There is a set of CSS Variables available for extensions to use for theming. They are all located inside `:root` and are defined in [app.scss](https://github.com/lensapp/lens/blob/master/src/renderer/components/app.scss):
|
There is a set of CSS Variables available for extensions to use for theming. They are all located inside `:root` and are defined in [app.scss](https://github.com/lensapp/lens/blob/master/src/renderer/components/app.scss):
|
||||||
|
|
||||||
@ -137,7 +137,7 @@ Currently, there is no prescribed way of detecting changes to the theme in JavaS
|
|||||||
|
|
||||||
## Injected styles
|
## Injected styles
|
||||||
|
|
||||||
Every extention is affected by list of default global styles defined in [app.scss](https://github.com/lensapp/lens/blob/master/src/renderer/components/app.scss). These are basic browser resets and element styles like setting the `box-sizing` property for every element, default text and background colors, default font sizes, basic heading formatting, etc.
|
Every extension is affected by list of default global styles defined in [app.scss](https://github.com/lensapp/lens/blob/master/src/renderer/components/app.scss). These are basic browser resets and element styles like setting the `box-sizing` property for every element, default text and background colors, default font sizes, basic heading formatting, etc.
|
||||||
|
|
||||||
Extension may overwrite these if needed. They have low CSS specificity, so overriding them should be fairly easy.
|
Extension may overwrite these if needed. They have low CSS specificity, so overriding them should be fairly easy.
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +0,0 @@
|
|||||||
install-deps:
|
|
||||||
yarn install
|
|
||||||
|
|
||||||
build: install-deps
|
|
||||||
yarn run build
|
|
||||||
|
|
||||||
test:
|
|
||||||
yarn run test
|
|
||||||
@ -2,10 +2,10 @@ import { LensMainExtension } from "@k8slens/extensions";
|
|||||||
|
|
||||||
export default class ExampleExtensionMain extends LensMainExtension {
|
export default class ExampleExtensionMain extends LensMainExtension {
|
||||||
onActivate() {
|
onActivate() {
|
||||||
console.log('EXAMPLE EXTENSION MAIN: ACTIVATED', this.getMeta());
|
console.log('EXAMPLE EXTENSION MAIN: ACTIVATED', this.name, this.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
onDeactivate() {
|
onDeactivate() {
|
||||||
console.log('EXAMPLE EXTENSION MAIN: DEACTIVATED', this.getMeta());
|
console.log('EXAMPLE EXTENSION MAIN: DEACTIVATED', this.name, this.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
3749
extensions/example-extension/package-lock.json
generated
3749
extensions/example-extension/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -11,13 +11,14 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack --config webpack.config.js",
|
"build": "webpack --config webpack.config.js",
|
||||||
"dev": "npm run build --watch",
|
"dev": "npm run build --watch",
|
||||||
"test": "echo NO TESTS"
|
"test": "jest --passWithNoTests --env=jsdom src $@"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react-open-doodles": "^1.0.5"
|
"react-open-doodles": "^1.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
|
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
|
||||||
|
"jest": "^26.6.3",
|
||||||
"ts-loader": "^8.0.4",
|
"ts-loader": "^8.0.4",
|
||||||
"typescript": "^4.0.3",
|
"typescript": "^4.0.3",
|
||||||
"webpack": "^4.44.2"
|
"webpack": "^4.44.2"
|
||||||
|
|||||||
@ -5,11 +5,21 @@ import React from "react"
|
|||||||
export default class ExampleExtension extends LensRendererExtension {
|
export default class ExampleExtension extends LensRendererExtension {
|
||||||
clusterPages = [
|
clusterPages = [
|
||||||
{
|
{
|
||||||
path: "/extension-example",
|
id: "example",
|
||||||
|
routePath: "/extension-example",
|
||||||
title: "Example Extension",
|
title: "Example Extension",
|
||||||
components: {
|
components: {
|
||||||
Page: () => <ExamplePage extension={this}/>,
|
Page: () => <ExamplePage extension={this}/>,
|
||||||
MenuIcon: ExampleIcon,
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
clusterPageMenus = [
|
||||||
|
{
|
||||||
|
target: { pageId: "example", params: {} },
|
||||||
|
title: "Example Extension",
|
||||||
|
components: {
|
||||||
|
Icon: ExampleIcon,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
5028
extensions/example-extension/yarn.lock
Normal file
5028
extensions/example-extension/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,8 +0,0 @@
|
|||||||
install-deps:
|
|
||||||
yarn install
|
|
||||||
|
|
||||||
build: install-deps
|
|
||||||
yarn run build
|
|
||||||
|
|
||||||
test:
|
|
||||||
yarn run test
|
|
||||||
3695
extensions/license-menu-item/package-lock.json
generated
3695
extensions/license-menu-item/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -6,12 +6,13 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack -p",
|
"build": "webpack -p",
|
||||||
"dev": "webpack --watch",
|
"dev": "webpack --watch",
|
||||||
"test": "echo NO TESTS"
|
"test": "jest --passWithNoTests --env=jsdom src $@"
|
||||||
},
|
},
|
||||||
"dependencies": {},
|
"dependencies": {},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/webpack": "^4.41.17",
|
|
||||||
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
|
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
|
||||||
|
"@types/webpack": "^4.41.17",
|
||||||
|
"jest": "^26.6.3",
|
||||||
"mobx": "^5.15.5",
|
"mobx": "^5.15.5",
|
||||||
"react": "^16.13.1",
|
"react": "^16.13.1",
|
||||||
"ts-loader": "^8.0.4",
|
"ts-loader": "^8.0.4",
|
||||||
|
|||||||
5132
extensions/license-menu-item/yarn.lock
Normal file
5132
extensions/license-menu-item/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,8 +0,0 @@
|
|||||||
install-deps:
|
|
||||||
yarn install
|
|
||||||
|
|
||||||
build: install-deps
|
|
||||||
yarn run build
|
|
||||||
|
|
||||||
test:
|
|
||||||
yarn run test
|
|
||||||
3811
extensions/metrics-cluster-feature/package-lock.json
generated
3811
extensions/metrics-cluster-feature/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -10,17 +10,18 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack --config webpack.config.js",
|
"build": "webpack --config webpack.config.js",
|
||||||
"dev": "npm run build --watch",
|
"dev": "npm run build --watch",
|
||||||
"test": "echo NO TESTS"
|
"test": "jest --passWithNoTests --env=jsdom src $@"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"semver": "^7.3.2"
|
"semver": "^7.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
|
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
|
||||||
|
"jest": "^26.6.3",
|
||||||
|
"mobx": "^5.15.5",
|
||||||
|
"react": "^16.13.1",
|
||||||
"ts-loader": "^8.0.4",
|
"ts-loader": "^8.0.4",
|
||||||
"typescript": "^4.0.3",
|
"typescript": "^4.0.3",
|
||||||
"webpack": "^4.44.2",
|
"webpack": "^4.44.2"
|
||||||
"mobx": "^5.15.5",
|
|
||||||
"react": "^16.13.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5058
extensions/metrics-cluster-feature/yarn.lock
Normal file
5058
extensions/metrics-cluster-feature/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,8 +0,0 @@
|
|||||||
install-deps:
|
|
||||||
yarn install
|
|
||||||
|
|
||||||
build: install-deps
|
|
||||||
yarn run build
|
|
||||||
|
|
||||||
test:
|
|
||||||
yarn run test
|
|
||||||
3719
extensions/node-menu/package-lock.json
generated
3719
extensions/node-menu/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -10,15 +10,16 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack --config webpack.config.js",
|
"build": "webpack --config webpack.config.js",
|
||||||
"dev": "npm run build --watch",
|
"dev": "npm run build --watch",
|
||||||
"test": "echo NO TESTS"
|
"test": "jest --passWithNoTests --env=jsdom src $@"
|
||||||
},
|
},
|
||||||
"dependencies": {},
|
"dependencies": {},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
|
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
|
||||||
|
"jest": "^26.6.3",
|
||||||
|
"mobx": "^5.15.5",
|
||||||
|
"react": "^16.13.1",
|
||||||
"ts-loader": "^8.0.4",
|
"ts-loader": "^8.0.4",
|
||||||
"typescript": "^4.0.3",
|
"typescript": "^4.0.3",
|
||||||
"webpack": "^4.44.2",
|
"webpack": "^4.44.2"
|
||||||
"mobx": "^5.15.5",
|
|
||||||
"react": "^16.13.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5058
extensions/node-menu/yarn.lock
Normal file
5058
extensions/node-menu/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,8 +0,0 @@
|
|||||||
install-deps:
|
|
||||||
yarn install
|
|
||||||
|
|
||||||
build: install-deps
|
|
||||||
yarn run build
|
|
||||||
|
|
||||||
test:
|
|
||||||
yarn run test
|
|
||||||
3719
extensions/pod-menu/package-lock.json
generated
3719
extensions/pod-menu/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -10,15 +10,16 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack --config webpack.config.js",
|
"build": "webpack --config webpack.config.js",
|
||||||
"dev": "npm run build --watch",
|
"dev": "npm run build --watch",
|
||||||
"test": "echo NO TESTS"
|
"test": "jest --passWithNoTests --env=jsdom src $@"
|
||||||
},
|
},
|
||||||
"dependencies": {},
|
"dependencies": {},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"ts-loader": "^8.0.4",
|
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
|
||||||
"typescript": "^4.0.3",
|
"jest": "^26.6.3",
|
||||||
"webpack": "^4.44.2",
|
|
||||||
"mobx": "^5.15.5",
|
"mobx": "^5.15.5",
|
||||||
"react": "^16.13.1",
|
"react": "^16.13.1",
|
||||||
"@k8slens/extensions": "file:../../src/extensions/npm/extensions"
|
"ts-loader": "^8.0.4",
|
||||||
|
"typescript": "^4.0.3",
|
||||||
|
"webpack": "^4.44.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5058
extensions/pod-menu/yarn.lock
Normal file
5058
extensions/pod-menu/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,8 +0,0 @@
|
|||||||
install-deps:
|
|
||||||
yarn install
|
|
||||||
|
|
||||||
build: install-deps
|
|
||||||
yarn run build
|
|
||||||
|
|
||||||
test:
|
|
||||||
yarn run test
|
|
||||||
@ -6,7 +6,7 @@ export default class SupportPageMainExtension extends LensMainExtension {
|
|||||||
parentId: "help",
|
parentId: "help",
|
||||||
label: "Support",
|
label: "Support",
|
||||||
click: () => {
|
click: () => {
|
||||||
this.navigate();
|
this.navigate("/support");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
4607
extensions/support-page/package-lock.json
generated
4607
extensions/support-page/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -7,7 +7,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack -p",
|
"build": "webpack -p",
|
||||||
"dev": "webpack --watch",
|
"dev": "webpack --watch",
|
||||||
"test": "echo NO TESTS"
|
"test": "jest --passWithNoTests --env=jsdom src $@"
|
||||||
},
|
},
|
||||||
"dependencies": {},
|
"dependencies": {},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -16,6 +16,7 @@
|
|||||||
"@types/react-router": "^5.1.8",
|
"@types/react-router": "^5.1.8",
|
||||||
"@types/webpack": "^4.41.17",
|
"@types/webpack": "^4.41.17",
|
||||||
"css-loader": "^5.0.0",
|
"css-loader": "^5.0.0",
|
||||||
|
"jest": "^26.6.3",
|
||||||
"mobx": "^5.15.5",
|
"mobx": "^5.15.5",
|
||||||
"react": "^16.13.1",
|
"react": "^16.13.1",
|
||||||
"sass-loader": "^10.0.4",
|
"sass-loader": "^10.0.4",
|
||||||
|
|||||||
@ -5,6 +5,8 @@ import { SupportPage } from "./src/support";
|
|||||||
export default class SupportPageRendererExtension extends LensRendererExtension {
|
export default class SupportPageRendererExtension extends LensRendererExtension {
|
||||||
globalPages: Interface.PageRegistration[] = [
|
globalPages: Interface.PageRegistration[] = [
|
||||||
{
|
{
|
||||||
|
id: "support",
|
||||||
|
routePath: "/support",
|
||||||
components: {
|
components: {
|
||||||
Page: SupportPage,
|
Page: SupportPage,
|
||||||
}
|
}
|
||||||
@ -14,7 +16,7 @@ export default class SupportPageRendererExtension extends LensRendererExtension
|
|||||||
statusBarItems: Interface.StatusBarRegistration[] = [
|
statusBarItems: Interface.StatusBarRegistration[] = [
|
||||||
{
|
{
|
||||||
item: (
|
item: (
|
||||||
<div className="SupportPageIcon flex align-center" onClick={() => this.navigate()}>
|
<div className="SupportPageIcon flex align-center" onClick={() => this.navigate("/support")}>
|
||||||
<Component.Icon interactive material="help" smallest/>
|
<Component.Icon interactive material="help" smallest/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
5319
extensions/support-page/yarn.lock
Normal file
5319
extensions/support-page/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,8 +0,0 @@
|
|||||||
install-deps:
|
|
||||||
yarn install
|
|
||||||
|
|
||||||
build: install-deps
|
|
||||||
yarn run build
|
|
||||||
|
|
||||||
test:
|
|
||||||
yarn run test
|
|
||||||
3504
extensions/telemetry/package-lock.json
generated
3504
extensions/telemetry/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -11,19 +11,20 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack -p",
|
"build": "webpack -p",
|
||||||
"dev": "webpack --watch",
|
"dev": "webpack --watch",
|
||||||
"test": "echo NO TESTS"
|
"test": "jest --passWithNoTests --env=jsdom src $@"
|
||||||
},
|
},
|
||||||
"dependencies": {},
|
"dependencies": {},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
|
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
|
||||||
"@types/analytics-node": "^3.1.3",
|
"@types/analytics-node": "^3.1.3",
|
||||||
|
"analytics-node": "^3.4.0-beta.3",
|
||||||
|
"jest": "^26.6.3",
|
||||||
|
"mobx": "^5.15.5",
|
||||||
|
"node-machine-id": "^1.1.12",
|
||||||
|
"react": "^16.13.1",
|
||||||
"ts-loader": "^8.0.4",
|
"ts-loader": "^8.0.4",
|
||||||
"typescript": "^4.0.3",
|
"typescript": "^4.0.3",
|
||||||
"webpack": "^4.44.2",
|
|
||||||
"mobx": "^5.15.5",
|
|
||||||
"react": "^16.13.1",
|
|
||||||
"node-machine-id": "^1.1.12",
|
|
||||||
"universal-analytics": "^0.4.23",
|
"universal-analytics": "^0.4.23",
|
||||||
"analytics-node": "^3.4.0-beta.3"
|
"webpack": "^4.44.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5171
extensions/telemetry/yarn.lock
Normal file
5171
extensions/telemetry/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
38
package.json
38
package.json
@ -2,7 +2,7 @@
|
|||||||
"name": "kontena-lens",
|
"name": "kontena-lens",
|
||||||
"productName": "Lens",
|
"productName": "Lens",
|
||||||
"description": "Lens - The Kubernetes IDE",
|
"description": "Lens - The Kubernetes IDE",
|
||||||
"version": "4.0.0-beta.1",
|
"version": "4.0.0-beta.2",
|
||||||
"main": "static/build/main.js",
|
"main": "static/build/main.js",
|
||||||
"copyright": "© 2020, Mirantis, Inc.",
|
"copyright": "© 2020, Mirantis, Inc.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -11,35 +11,35 @@
|
|||||||
"email": "info@k8slens.dev"
|
"email": "info@k8slens.dev"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "concurrently -k \"yarn dev-run -C\" yarn:dev:*",
|
"dev": "concurrently -k \"yarn run dev-run -C\" yarn:dev:*",
|
||||||
"dev-build": "concurrently yarn:compile:*",
|
"dev-build": "concurrently yarn:compile:*",
|
||||||
"dev-run": "nodemon --watch static/build/main.js --exec \"electron --inspect .\"",
|
"dev-run": "nodemon --watch static/build/main.js --exec \"electron --inspect .\"",
|
||||||
"dev:main": "yarn compile:main --watch",
|
"dev:main": "yarn run compile:main --watch",
|
||||||
"dev:renderer": "yarn webpack-dev-server --config webpack.renderer.ts",
|
"dev:renderer": "yarn run webpack-dev-server --config webpack.renderer.ts",
|
||||||
"dev:extension-types": "yarn compile:extension-types --watch",
|
"dev:extension-types": "yarn run compile:extension-types --watch",
|
||||||
"compile": "env NODE_ENV=production concurrently yarn:compile:*",
|
"compile": "env NODE_ENV=production concurrently yarn:compile:*",
|
||||||
"compile:main": "webpack --config webpack.main.ts",
|
"compile:main": "yarn run webpack --config webpack.main.ts",
|
||||||
"compile:renderer": "webpack --config webpack.renderer.ts",
|
"compile:renderer": "yarn run webpack --config webpack.renderer.ts",
|
||||||
"compile:i18n": "lingui compile",
|
"compile:i18n": "yarn run lingui compile",
|
||||||
"compile:extension-types": "rollup --config src/extensions/rollup.config.js",
|
"compile:extension-types": "yarn run rollup --config src/extensions/rollup.config.js",
|
||||||
"npm:fix-package-version": "ts-node build/set_npm_version.ts",
|
"npm:fix-package-version": "yarn run ts-node build/set_npm_version.ts",
|
||||||
"build:linux": "yarn compile && electron-builder --linux --dir -c.productName=Lens",
|
"build:linux": "yarn run compile && electron-builder --linux --dir -c.productName=Lens",
|
||||||
"build:mac": "yarn compile && electron-builder --mac --dir -c.productName=Lens",
|
"build:mac": "yarn run compile && electron-builder --mac --dir -c.productName=Lens",
|
||||||
"build:win": "yarn compile && electron-builder --win --dir -c.productName=Lens",
|
"build:win": "yarn run compile && electron-builder --win --dir -c.productName=Lens",
|
||||||
"test": "jest --env=jsdom src $@",
|
"test": "jest --env=jsdom src $@",
|
||||||
"integration": "jest --coverage integration $@",
|
"integration": "jest --coverage integration $@",
|
||||||
"dist": "yarn compile && electron-builder --publish onTag",
|
"dist": "yarn run compile && electron-builder --publish onTag",
|
||||||
"dist:win": "yarn compile && electron-builder --publish onTag --x64 --ia32",
|
"dist:win": "yarn run compile && electron-builder --publish onTag --x64 --ia32",
|
||||||
"dist:dir": "yarn dist --dir -c.compression=store -c.mac.identity=null",
|
"dist:dir": "yarn run dist --dir -c.compression=store -c.mac.identity=null",
|
||||||
"postinstall": "patch-package",
|
"postinstall": "patch-package",
|
||||||
"i18n:extract": "lingui extract",
|
"i18n:extract": "yarn run lingui extract",
|
||||||
"download-bins": "concurrently yarn:download:*",
|
"download-bins": "concurrently yarn:download:*",
|
||||||
"download:kubectl": "yarn run ts-node build/download_kubectl.ts",
|
"download:kubectl": "yarn run ts-node build/download_kubectl.ts",
|
||||||
"download:helm": "yarn run ts-node build/download_helm.ts",
|
"download:helm": "yarn run ts-node build/download_helm.ts",
|
||||||
"build:tray-icons": "yarn run ts-node build/build_tray_icon.ts",
|
"build:tray-icons": "yarn run ts-node build/build_tray_icon.ts",
|
||||||
"lint": "eslint $@ --ext js,ts,tsx --max-warnings=0 src/",
|
"lint": "yarn run eslint $@ --ext js,ts,tsx --max-warnings=0 src/",
|
||||||
"mkdocs-serve-local": "docker build -t mkdocs-serve-local:latest mkdocs/ && docker run --rm -it -p 8000:8000 -v ${PWD}:/docs mkdocs-serve-local:latest",
|
"mkdocs-serve-local": "docker build -t mkdocs-serve-local:latest mkdocs/ && docker run --rm -it -p 8000:8000 -v ${PWD}:/docs mkdocs-serve-local:latest",
|
||||||
"typedocs-extensions-api": "yarn typedoc --ignoreCompilerErrors --readme docs/extensions/typedoc-readme.md.tpl --name @k8slens/extensions --out docs/extensions/api --mode library --excludePrivate --hideBreadcrumbs --includes src/ src/extensions/extension-api.ts"
|
"typedocs-extensions-api": "yarn run typedoc --ignoreCompilerErrors --readme docs/extensions/typedoc-readme.md.tpl --name @k8slens/extensions --out docs/extensions/api --mode library --excludePrivate --hideBreadcrumbs --includes src/ src/extensions/extension-api.ts"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"bundledKubectlVersion": "1.17.11",
|
"bundledKubectlVersion": "1.17.11",
|
||||||
|
|||||||
23
src/extensions/__tests__/lens-extension.test.ts
Normal file
23
src/extensions/__tests__/lens-extension.test.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { LensExtension } from "../lens-extension"
|
||||||
|
|
||||||
|
let ext: LensExtension = null
|
||||||
|
|
||||||
|
describe("lens extension", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
ext = new LensExtension({
|
||||||
|
manifest: {
|
||||||
|
name: "foo-bar",
|
||||||
|
version: "0.1.1"
|
||||||
|
},
|
||||||
|
manifestPath: "/this/is/fake/package.json",
|
||||||
|
isBundled: false,
|
||||||
|
isEnabled: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("name", () => {
|
||||||
|
it("returns name", () => {
|
||||||
|
expect(ext.name).toBe("foo-bar")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -57,29 +57,29 @@ export class ExtensionLoader {
|
|||||||
loadOnMain() {
|
loadOnMain() {
|
||||||
logger.info('[EXTENSIONS-LOADER]: load on main')
|
logger.info('[EXTENSIONS-LOADER]: load on main')
|
||||||
this.autoInitExtensions((ext: LensMainExtension) => [
|
this.autoInitExtensions((ext: LensMainExtension) => [
|
||||||
registries.menuRegistry.add(ext.appMenus, { key: ext })
|
registries.menuRegistry.add(ext.appMenus)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadOnClusterManagerRenderer() {
|
loadOnClusterManagerRenderer() {
|
||||||
logger.info('[EXTENSIONS-LOADER]: load on main renderer (cluster manager)')
|
logger.info('[EXTENSIONS-LOADER]: load on main renderer (cluster manager)')
|
||||||
this.autoInitExtensions((ext: LensRendererExtension) => [
|
this.autoInitExtensions((ext: LensRendererExtension) => [
|
||||||
registries.globalPageRegistry.add(ext.globalPages, { key: ext }),
|
registries.globalPageRegistry.add(ext.globalPages, ext),
|
||||||
registries.globalPageMenuRegistry.add(ext.globalPageMenus, { key: ext }),
|
registries.globalPageMenuRegistry.add(ext.globalPageMenus, ext),
|
||||||
registries.appPreferenceRegistry.add(ext.appPreferences, { key: ext }),
|
registries.appPreferenceRegistry.add(ext.appPreferences),
|
||||||
registries.clusterFeatureRegistry.add(ext.clusterFeatures, { key: ext }),
|
registries.clusterFeatureRegistry.add(ext.clusterFeatures),
|
||||||
registries.statusBarRegistry.add(ext.statusBarItems, { key: ext }),
|
registries.statusBarRegistry.add(ext.statusBarItems),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadOnClusterRenderer() {
|
loadOnClusterRenderer() {
|
||||||
logger.info('[EXTENSIONS-LOADER]: load on cluster renderer (dashboard)')
|
logger.info('[EXTENSIONS-LOADER]: load on cluster renderer (dashboard)')
|
||||||
this.autoInitExtensions((ext: LensRendererExtension) => [
|
this.autoInitExtensions((ext: LensRendererExtension) => [
|
||||||
registries.clusterPageRegistry.add(ext.clusterPages, { key: ext }),
|
registries.clusterPageRegistry.add(ext.clusterPages, ext),
|
||||||
registries.clusterPageMenuRegistry.add(ext.clusterPageMenus, { key: ext }),
|
registries.clusterPageMenuRegistry.add(ext.clusterPageMenus, ext),
|
||||||
registries.kubeObjectMenuRegistry.add(ext.kubeObjectMenuItems, { key: ext }),
|
registries.kubeObjectMenuRegistry.add(ext.kubeObjectMenuItems),
|
||||||
registries.kubeObjectDetailRegistry.add(ext.kubeObjectDetailItems, { key: ext }),
|
registries.kubeObjectDetailRegistry.add(ext.kubeObjectDetailItems),
|
||||||
registries.kubeObjectStatusRegistry.add(ext.kubeObjectStatusTexts, { key: ext })
|
registries.kubeObjectStatusRegistry.add(ext.kubeObjectStatusTexts)
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import type { InstalledExtension } from "./extension-manager";
|
import type { InstalledExtension } from "./extension-manager";
|
||||||
import { action, observable, reaction } from "mobx";
|
import { action, observable, reaction } from "mobx";
|
||||||
import { compile } from "path-to-regexp"
|
|
||||||
import logger from "../main/logger";
|
import logger from "../main/logger";
|
||||||
|
|
||||||
export type LensExtensionId = string; // path to manifest (package.json)
|
export type LensExtensionId = string; // path to manifest (package.json)
|
||||||
@ -15,7 +14,6 @@ export interface LensExtensionManifest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class LensExtension {
|
export class LensExtension {
|
||||||
readonly routePrefix = "/extension/:name"
|
|
||||||
readonly manifest: LensExtensionManifest;
|
readonly manifest: LensExtensionManifest;
|
||||||
readonly manifestPath: string;
|
readonly manifestPath: string;
|
||||||
readonly isBundled: boolean;
|
readonly isBundled: boolean;
|
||||||
@ -44,14 +42,6 @@ export class LensExtension {
|
|||||||
return this.manifest.description
|
return this.manifest.description
|
||||||
}
|
}
|
||||||
|
|
||||||
getPageUrl(baseUrl = "") {
|
|
||||||
return compile(this.routePrefix)({ name: this.name }) + baseUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
getPageRoute(baseRoute = "") {
|
|
||||||
return this.routePrefix + baseRoute;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
async enable() {
|
async enable() {
|
||||||
if (this.isEnabled) return;
|
if (this.isEnabled) return;
|
||||||
|
|||||||
@ -2,13 +2,14 @@ import type { MenuRegistration } from "./registries/menu-registry";
|
|||||||
import { observable } from "mobx";
|
import { observable } from "mobx";
|
||||||
import { LensExtension } from "./lens-extension"
|
import { LensExtension } from "./lens-extension"
|
||||||
import { WindowManager } from "../main/window-manager";
|
import { WindowManager } from "../main/window-manager";
|
||||||
|
import { getPageUrl } from "./registries/page-registry"
|
||||||
|
|
||||||
export class LensMainExtension extends LensExtension {
|
export class LensMainExtension extends LensExtension {
|
||||||
@observable.shallow appMenus: MenuRegistration[] = []
|
@observable.shallow appMenus: MenuRegistration[] = []
|
||||||
|
|
||||||
async navigate(location?: string, frameId?: number) {
|
async navigate(location?: string, frameId?: number) {
|
||||||
const windowManager = WindowManager.getInstance<WindowManager>();
|
const windowManager = WindowManager.getInstance<WindowManager>();
|
||||||
const url = this.getPageUrl(location); // get full path to extension's page
|
const url = getPageUrl(this, location); // get full path to extension's page
|
||||||
await windowManager.navigate(url, frameId);
|
await windowManager.navigate(url, frameId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import type { AppPreferenceRegistration, ClusterFeatureRegistration, KubeObjectDetailRegistration, KubeObjectMenuRegistration, KubeObjectStatusRegistration, PageMenuRegistration, PageRegistration, StatusBarRegistration, } from "./registries"
|
import type { AppPreferenceRegistration, ClusterFeatureRegistration, KubeObjectDetailRegistration, KubeObjectMenuRegistration, KubeObjectStatusRegistration, PageMenuRegistration, PageRegistration, StatusBarRegistration, } from "./registries"
|
||||||
import { observable } from "mobx";
|
import { observable } from "mobx";
|
||||||
import { LensExtension } from "./lens-extension"
|
import { LensExtension } from "./lens-extension"
|
||||||
|
import { getPageUrl } from "./registries/page-registry"
|
||||||
|
|
||||||
export class LensRendererExtension extends LensExtension {
|
export class LensRendererExtension extends LensExtension {
|
||||||
@observable.shallow globalPages: PageRegistration[] = []
|
@observable.shallow globalPages: PageRegistration[] = []
|
||||||
@ -16,6 +17,6 @@ export class LensRendererExtension extends LensExtension {
|
|||||||
|
|
||||||
async navigate(location?: string) {
|
async navigate(location?: string) {
|
||||||
const { navigate } = await import("../renderer/navigation");
|
const { navigate } = await import("../renderer/navigation");
|
||||||
navigate(this.getPageUrl(location));
|
navigate(getPageUrl(this, location));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
82
src/extensions/registries/__tests__/page-registry.test.ts
Normal file
82
src/extensions/registries/__tests__/page-registry.test.ts
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import { getPageUrl, globalPageRegistry } from "../page-registry"
|
||||||
|
import { LensExtension } from "../../lens-extension"
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
let ext: LensExtension = null
|
||||||
|
|
||||||
|
describe("getPageUrl", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
ext = new LensExtension({
|
||||||
|
manifest: {
|
||||||
|
name: "foo-bar",
|
||||||
|
version: "0.1.1"
|
||||||
|
},
|
||||||
|
manifestPath: "/this/is/fake/package.json",
|
||||||
|
isBundled: false,
|
||||||
|
isEnabled: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns a page url for extension", () => {
|
||||||
|
expect(getPageUrl(ext)).toBe("/extension/foo-bar")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("allows to pass base url as parameter", () => {
|
||||||
|
expect(getPageUrl(ext, "/test")).toBe("/extension/foo-bar/test")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("removes @", () => {
|
||||||
|
ext.manifest.name = "@foo/bar"
|
||||||
|
expect(getPageUrl(ext)).toBe("/extension/foo-bar")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("adds / prefix", () => {
|
||||||
|
expect(getPageUrl(ext, "test")).toBe("/extension/foo-bar/test")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("globalPageRegistry", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
ext = new LensExtension({
|
||||||
|
manifest: {
|
||||||
|
name: "@acme/foo-bar",
|
||||||
|
version: "0.1.1"
|
||||||
|
},
|
||||||
|
manifestPath: "/this/is/fake/package.json",
|
||||||
|
isBundled: false,
|
||||||
|
isEnabled: true
|
||||||
|
})
|
||||||
|
globalPageRegistry.add([
|
||||||
|
{
|
||||||
|
id: "test-page",
|
||||||
|
components: {
|
||||||
|
Page: () => React.createElement('Text')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "another-page",
|
||||||
|
components: {
|
||||||
|
Page: () => React.createElement('Text')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
], ext)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("getByPageMenuTarget", () => {
|
||||||
|
it("returns matching page", () => {
|
||||||
|
const page = globalPageRegistry.getByPageMenuTarget({
|
||||||
|
pageId: "test-page",
|
||||||
|
extensionId: ext.name
|
||||||
|
})
|
||||||
|
expect(page.id).toEqual("test-page")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns null if target not found", () => {
|
||||||
|
const page = globalPageRegistry.getByPageMenuTarget({
|
||||||
|
pageId: "wrong-page",
|
||||||
|
extensionId: ext.name
|
||||||
|
})
|
||||||
|
expect(page).toBeNull()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -1,12 +1,12 @@
|
|||||||
import type React from "react"
|
import type React from "react"
|
||||||
import { BaseRegistry, BaseRegistryItem } from "./base-registry";
|
import { BaseRegistry } from "./base-registry";
|
||||||
|
|
||||||
export interface AppPreferenceComponents {
|
export interface AppPreferenceComponents {
|
||||||
Hint: React.ComponentType<any>;
|
Hint: React.ComponentType<any>;
|
||||||
Input: React.ComponentType<any>;
|
Input: React.ComponentType<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AppPreferenceRegistration extends BaseRegistryItem {
|
export interface AppPreferenceRegistration {
|
||||||
title: string;
|
title: string;
|
||||||
components: AppPreferenceComponents;
|
components: AppPreferenceComponents;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,65 +1,24 @@
|
|||||||
// Base class for extensions-api registries
|
// Base class for extensions-api registries
|
||||||
import { action, observable } from "mobx";
|
import { action, observable } from "mobx";
|
||||||
import { LensExtension } from "../lens-extension";
|
|
||||||
import { getRandId } from "../../common/utils";
|
|
||||||
|
|
||||||
export type BaseRegistryKey = LensExtension | null;
|
export class BaseRegistry<T = any> {
|
||||||
export type BaseRegistryItemId = string | symbol;
|
private items = observable<T>([], { deep: false });
|
||||||
|
|
||||||
export interface BaseRegistryItem {
|
getItems(): T[] {
|
||||||
id?: BaseRegistryItemId; // uniq id, generated automatically when not provided
|
return this.items.toJS();
|
||||||
}
|
|
||||||
|
|
||||||
export interface BaseRegistryAddMeta {
|
|
||||||
key?: BaseRegistryKey;
|
|
||||||
merge?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export class BaseRegistry<T extends BaseRegistryItem = any> {
|
|
||||||
private items = observable.map<BaseRegistryKey, T[]>([], { deep: false });
|
|
||||||
|
|
||||||
getItems(): (T & { extension?: LensExtension | null })[] {
|
|
||||||
return Array.from(this.items).map(([ext, items]) => {
|
|
||||||
return items.map(item => ({
|
|
||||||
...item,
|
|
||||||
extension: ext,
|
|
||||||
}))
|
|
||||||
}).flat()
|
|
||||||
}
|
|
||||||
|
|
||||||
getById(itemId: BaseRegistryItemId, key?: BaseRegistryKey): T {
|
|
||||||
const byId = (item: BaseRegistryItem) => item.id === itemId;
|
|
||||||
if (key) {
|
|
||||||
return this.items.get(key)?.find(byId)
|
|
||||||
}
|
|
||||||
return this.getItems().find(byId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
add(items: T | T[], { key = null, merge = true }: BaseRegistryAddMeta = {}) {
|
add(items: T | T[]) {
|
||||||
const normalizedItems = (Array.isArray(items) ? items : [items]).map((item: T) => {
|
const normalizedItems = (Array.isArray(items) ? items : [items])
|
||||||
item.id = item.id || getRandId();
|
this.items.push(...normalizedItems);
|
||||||
return item;
|
return () => this.remove(...normalizedItems);
|
||||||
});
|
|
||||||
if (merge && this.items.has(key)) {
|
|
||||||
const newItems = new Set(this.items.get(key));
|
|
||||||
normalizedItems.forEach(item => newItems.add(item))
|
|
||||||
this.items.set(key, [...newItems]);
|
|
||||||
} else {
|
|
||||||
this.items.set(key, normalizedItems);
|
|
||||||
}
|
|
||||||
return () => this.remove(normalizedItems, key)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
remove(items: T[], key: BaseRegistryKey = null) {
|
remove(...items: T[]) {
|
||||||
const storedItems = this.items.get(key);
|
items.forEach(item => {
|
||||||
if (!storedItems) return;
|
this.items.remove(item); // works because of {deep: false};
|
||||||
const newItems = storedItems.filter(item => !items.includes(item)); // works because of {deep: false};
|
})
|
||||||
if (newItems.length > 0) {
|
|
||||||
this.items.set(key, newItems)
|
|
||||||
} else {
|
|
||||||
this.items.delete(key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import type React from "react"
|
import type React from "react"
|
||||||
import { BaseRegistry, BaseRegistryItem } from "./base-registry";
|
import { BaseRegistry } from "./base-registry";
|
||||||
import { ClusterFeature } from "../cluster-feature";
|
import { ClusterFeature } from "../cluster-feature";
|
||||||
|
|
||||||
export interface ClusterFeatureComponents {
|
export interface ClusterFeatureComponents {
|
||||||
Description: React.ComponentType<any>;
|
Description: React.ComponentType<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ClusterFeatureRegistration extends BaseRegistryItem {
|
export interface ClusterFeatureRegistration {
|
||||||
title: string;
|
title: string;
|
||||||
components: ClusterFeatureComponents
|
components: ClusterFeatureComponents
|
||||||
feature: ClusterFeature
|
feature: ClusterFeature
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import { BaseRegistry, BaseRegistryItem } from "./base-registry";
|
import { BaseRegistry } from "./base-registry";
|
||||||
|
|
||||||
export interface KubeObjectDetailComponents {
|
export interface KubeObjectDetailComponents {
|
||||||
Details: React.ComponentType<any>;
|
Details: React.ComponentType<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface KubeObjectDetailRegistration extends BaseRegistryItem {
|
export interface KubeObjectDetailRegistration {
|
||||||
kind: string;
|
kind: string;
|
||||||
apiVersions: string[];
|
apiVersions: string[];
|
||||||
components: KubeObjectDetailComponents;
|
components: KubeObjectDetailComponents;
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import { BaseRegistry, BaseRegistryItem } from "./base-registry";
|
import { BaseRegistry } from "./base-registry";
|
||||||
|
|
||||||
export interface KubeObjectMenuComponents {
|
export interface KubeObjectMenuComponents {
|
||||||
MenuItem: React.ComponentType<any>;
|
MenuItem: React.ComponentType<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface KubeObjectMenuRegistration extends BaseRegistryItem {
|
export interface KubeObjectMenuRegistration {
|
||||||
kind: string;
|
kind: string;
|
||||||
apiVersions: string[];
|
apiVersions: string[];
|
||||||
components: KubeObjectMenuComponents;
|
components: KubeObjectMenuComponents;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { KubeObject, KubeObjectStatus } from "../renderer-api/k8s-api";
|
import { KubeObject, KubeObjectStatus } from "../renderer-api/k8s-api";
|
||||||
import { BaseRegistry, BaseRegistryItem } from "./base-registry";
|
import { BaseRegistry } from "./base-registry";
|
||||||
|
|
||||||
export interface KubeObjectStatusRegistration extends BaseRegistryItem {
|
export interface KubeObjectStatusRegistration {
|
||||||
kind: string;
|
kind: string;
|
||||||
apiVersions: string[];
|
apiVersions: string[];
|
||||||
resolve: (object: KubeObject) => KubeObjectStatus;
|
resolve: (object: KubeObject) => KubeObjectStatus;
|
||||||
|
|||||||
@ -1,20 +1,21 @@
|
|||||||
// Extensions-api -> Register page menu items
|
// Extensions-api -> Register page menu items
|
||||||
|
|
||||||
import type React from "react";
|
import type React from "react";
|
||||||
|
import { action } from "mobx";
|
||||||
import type { IconProps } from "../../renderer/components/icon";
|
import type { IconProps } from "../../renderer/components/icon";
|
||||||
import { BaseRegistry, BaseRegistryItem, BaseRegistryItemId } from "./base-registry";
|
import { BaseRegistry } from "./base-registry";
|
||||||
|
import { LensExtension } from "../lens-extension";
|
||||||
|
|
||||||
export interface PageMenuRegistration extends BaseRegistryItem {
|
export interface PageMenuTarget {
|
||||||
id: BaseRegistryItemId; // required id from page-registry item to match with
|
pageId: string;
|
||||||
url?: string; // when not provided initial extension's path used, e.g. "/extension/lens-extension-name"
|
extensionId?: string;
|
||||||
title: React.ReactNode;
|
params?: object;
|
||||||
components: PageMenuComponents;
|
|
||||||
subMenus?: PageSubMenuRegistration[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PageSubMenuRegistration {
|
export interface PageMenuRegistration {
|
||||||
url: string;
|
target?: PageMenuTarget;
|
||||||
title: React.ReactNode;
|
title: React.ReactNode;
|
||||||
|
components: PageMenuComponents;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PageMenuComponents {
|
export interface PageMenuComponents {
|
||||||
@ -22,13 +23,18 @@ export interface PageMenuComponents {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class PageMenuRegistry<T extends PageMenuRegistration> extends BaseRegistry<T> {
|
export class PageMenuRegistry<T extends PageMenuRegistration> extends BaseRegistry<T> {
|
||||||
getItems() {
|
|
||||||
return super.getItems().map(item => {
|
@action
|
||||||
item.url = item.extension.getPageUrl(item.url)
|
add(items: T[], ext?: LensExtension) {
|
||||||
return item
|
const normalizedItems = items.map((menu) => {
|
||||||
});
|
if (menu.target && !menu.target.extensionId) {
|
||||||
|
menu.target.extensionId = ext.name
|
||||||
|
}
|
||||||
|
return menu
|
||||||
|
})
|
||||||
|
return super.add(normalizedItems);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const globalPageMenuRegistry = new PageMenuRegistry<Omit<PageMenuRegistration, "subMenus">>();
|
export const globalPageMenuRegistry = new PageMenuRegistry<PageMenuRegistration>();
|
||||||
export const clusterPageMenuRegistry = new PageMenuRegistry();
|
export const clusterPageMenuRegistry = new PageMenuRegistry<PageMenuRegistration>();
|
||||||
|
|||||||
@ -1,33 +1,59 @@
|
|||||||
// Extensions-api -> Custom page registration
|
// Extensions-api -> Custom page registration
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { BaseRegistry, BaseRegistryItem } from "./base-registry";
|
import { action } from "mobx";
|
||||||
|
import { compile } from "path-to-regexp";
|
||||||
|
import { BaseRegistry } from "./base-registry";
|
||||||
|
import { LensExtension } from "../lens-extension"
|
||||||
|
import type { PageMenuTarget } from "./page-menu-registry";
|
||||||
|
|
||||||
export interface PageRegistration extends BaseRegistryItem {
|
export interface PageRegistration {
|
||||||
|
id: string; // will be automatically prefixed with extension name
|
||||||
routePath?: string; // additional (suffix) route path to base extension's route: "/extension/:name"
|
routePath?: string; // additional (suffix) route path to base extension's route: "/extension/:name"
|
||||||
exact?: boolean; // route matching flag, see: https://reactrouter.com/web/api/NavLink/exact-bool
|
exact?: boolean; // route matching flag, see: https://reactrouter.com/web/api/NavLink/exact-bool
|
||||||
components: PageComponents;
|
components: PageComponents;
|
||||||
subPages?: SubPageRegistration[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SubPageRegistration {
|
|
||||||
routePath: string; // required for sub-pages
|
|
||||||
exact?: boolean;
|
|
||||||
components: PageComponents;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PageComponents {
|
export interface PageComponents {
|
||||||
Page: React.ComponentType<any>;
|
Page: React.ComponentType<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const routePrefix = "/extension/:name"
|
||||||
|
|
||||||
|
export function sanitizeExtensioName(name: string) {
|
||||||
|
return name.replace("@", "").replace("/", "-")
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPageUrl(ext: LensExtension, baseUrl = "") {
|
||||||
|
if (baseUrl !== "" && !baseUrl.startsWith("/")) {
|
||||||
|
baseUrl = "/" + baseUrl
|
||||||
|
}
|
||||||
|
const validUrlName = sanitizeExtensioName(ext.name);
|
||||||
|
return compile(routePrefix)({ name: validUrlName }) + baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
export class PageRegistry<T extends PageRegistration> extends BaseRegistry<T> {
|
export class PageRegistry<T extends PageRegistration> extends BaseRegistry<T> {
|
||||||
getItems() {
|
|
||||||
return super.getItems().map(item => {
|
@action
|
||||||
item.routePath = item.extension.getPageRoute(item.routePath)
|
add(items: T[], ext?: LensExtension) {
|
||||||
return item
|
const normalizedItems = items.map((page) => {
|
||||||
});
|
if (!page.routePath) {
|
||||||
|
page.routePath = `/${page.id}`
|
||||||
|
}
|
||||||
|
page.routePath = getPageUrl(ext, page.routePath)
|
||||||
|
return page
|
||||||
|
})
|
||||||
|
return super.add(normalizedItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
getByPageMenuTarget(target: PageMenuTarget) {
|
||||||
|
if (!target) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const routePath = `/extension/${sanitizeExtensioName(target.extensionId)}/`
|
||||||
|
return this.getItems().find((page) => page.routePath.startsWith(routePath) && page.id === target.pageId) || null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const globalPageRegistry = new PageRegistry<Omit<PageRegistration, "subPages">>();
|
export const globalPageRegistry = new PageRegistry<PageRegistration>();
|
||||||
export const clusterPageRegistry = new PageRegistry();
|
export const clusterPageRegistry = new PageRegistry<PageRegistration>();
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
// Extensions API -> Status bar customizations
|
// Extensions API -> Status bar customizations
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { BaseRegistry, BaseRegistryItem } from "./base-registry";
|
import { BaseRegistry } from "./base-registry";
|
||||||
|
|
||||||
export interface StatusBarRegistration extends BaseRegistryItem {
|
export interface StatusBarRegistration {
|
||||||
item?: React.ReactNode;
|
item?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { readFile } from "fs-extra"
|
|||||||
import { Cluster } from "./cluster"
|
import { Cluster } from "./cluster"
|
||||||
import { apiPrefix, appName, publicPath, isDevelopment, webpackDevServerPort } from "../common/vars";
|
import { apiPrefix, appName, publicPath, isDevelopment, webpackDevServerPort } from "../common/vars";
|
||||||
import { helmRoute, kubeconfigRoute, metricsRoute, portForwardRoute, resourceApplierRoute, watchRoute } from "./routes";
|
import { helmRoute, kubeconfigRoute, metricsRoute, portForwardRoute, resourceApplierRoute, watchRoute } from "./routes";
|
||||||
|
import logger from "./logger"
|
||||||
|
|
||||||
export interface RouterRequestOpts {
|
export interface RouterRequestOpts {
|
||||||
req: http.IncomingMessage;
|
req: http.IncomingMessage;
|
||||||
@ -94,7 +95,7 @@ export class Router {
|
|||||||
return mimeTypes[path.extname(filename).slice(1)] || "text/plain"
|
return mimeTypes[path.extname(filename).slice(1)] || "text/plain"
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleStaticFile(filePath: string, res: http.ServerResponse, req: http.IncomingMessage) {
|
async handleStaticFile(filePath: string, res: http.ServerResponse, req: http.IncomingMessage, retryCount = 0) {
|
||||||
const asset = path.join(__static, filePath);
|
const asset = path.join(__static, filePath);
|
||||||
try {
|
try {
|
||||||
const filename = path.basename(req.url);
|
const filename = path.basename(req.url);
|
||||||
@ -112,7 +113,13 @@ export class Router {
|
|||||||
res.write(data);
|
res.write(data);
|
||||||
res.end();
|
res.end();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.handleStaticFile(`${publicPath}/${appName}.html`, res, req);
|
if (retryCount > 5) {
|
||||||
|
logger.error("handleStaticFile:", err.toString())
|
||||||
|
res.statusCode = 404
|
||||||
|
res.end()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.handleStaticFile(`${publicPath}/${appName}.html`, res, req, Math.max(retryCount, 0) + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +127,7 @@ export class Router {
|
|||||||
// Static assets
|
// Static assets
|
||||||
this.router.add(
|
this.router.add(
|
||||||
{ method: 'get', path: '/{path*}' },
|
{ method: 'get', path: '/{path*}' },
|
||||||
({ params, response, path, raw: { req }}: LensApiRequest) => {
|
({ params, response, path, raw: { req } }: LensApiRequest) => {
|
||||||
this.handleStaticFile(params.path, response, req);
|
this.handleStaticFile(params.path, response, req);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
.LandingPage {
|
.LandingPage {
|
||||||
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
|
|||||||
@ -117,7 +117,7 @@ export class Workspaces extends React.Component {
|
|||||||
const isEditing = this.editingWorkspaces.has(workspaceId);
|
const isEditing = this.editingWorkspaces.has(workspaceId);
|
||||||
const editingWorkspace = this.editingWorkspaces.get(workspaceId);
|
const editingWorkspace = this.editingWorkspaces.get(workspaceId);
|
||||||
const managed = !!ownerRef
|
const managed = !!ownerRef
|
||||||
const className = cssNames("workspace flex gaps", {
|
const className = cssNames("workspace flex gaps align-center", {
|
||||||
active: isActive,
|
active: isActive,
|
||||||
editing: isEditing,
|
editing: isEditing,
|
||||||
default: isDefault,
|
default: isDefault,
|
||||||
|
|||||||
@ -74,27 +74,8 @@ export class App extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderExtensionRoutes() {
|
renderExtensionRoutes() {
|
||||||
return clusterPageRegistry.getItems().map(({ id: pageId, components: { Page }, exact, routePath, subPages }) => {
|
return clusterPageRegistry.getItems().map(({ components: { Page }, exact, routePath }) => {
|
||||||
const Component = () => {
|
const Component = () => {
|
||||||
if (subPages) {
|
|
||||||
const tabs: TabLayoutRoute[] = subPages.map(({ exact, routePath, components: { Page } }) => {
|
|
||||||
const menuItem = clusterPageMenuRegistry.getById(pageId);
|
|
||||||
if (!menuItem) return;
|
|
||||||
return {
|
|
||||||
routePath, exact,
|
|
||||||
component: Page,
|
|
||||||
url: menuItem.url,
|
|
||||||
title: menuItem.title,
|
|
||||||
}
|
|
||||||
}).filter(Boolean);
|
|
||||||
if (tabs.length > 0) {
|
|
||||||
return (
|
|
||||||
<Page>
|
|
||||||
<TabLayout tabs={tabs}/>
|
|
||||||
</Page>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return <Page/>
|
return <Page/>
|
||||||
};
|
};
|
||||||
return <Route key={routePath} path={routePath} exact={exact} component={Component}/>
|
return <Route key={routePath} path={routePath} exact={exact} component={Component}/>
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import { remote } from "electron"
|
|||||||
import type { Cluster } from "../../../main/cluster";
|
import type { Cluster } from "../../../main/cluster";
|
||||||
import { DragDropContext, Draggable, DraggableProvided, Droppable, DroppableProvided, DropResult } from "react-beautiful-dnd";
|
import { DragDropContext, Draggable, DraggableProvided, Droppable, DroppableProvided, DropResult } from "react-beautiful-dnd";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { matchPath } from "react-router";
|
|
||||||
import { _i18n } from "../../i18n";
|
import { _i18n } from "../../i18n";
|
||||||
import { t, Trans } from "@lingui/macro";
|
import { t, Trans } from "@lingui/macro";
|
||||||
import { userStore } from "../../../common/user-store";
|
import { userStore } from "../../../common/user-store";
|
||||||
@ -15,7 +14,7 @@ import { ClusterIcon } from "../cluster-icon";
|
|||||||
import { Icon } from "../icon";
|
import { Icon } from "../icon";
|
||||||
import { autobind, cssNames, IClassName } from "../../utils";
|
import { autobind, cssNames, IClassName } from "../../utils";
|
||||||
import { Badge } from "../badge";
|
import { Badge } from "../badge";
|
||||||
import { navigate, navigation } from "../../navigation";
|
import { isActiveRoute, navigate } from "../../navigation";
|
||||||
import { addClusterURL } from "../+add-cluster";
|
import { addClusterURL } from "../+add-cluster";
|
||||||
import { clusterSettingsURL } from "../+cluster-settings";
|
import { clusterSettingsURL } from "../+cluster-settings";
|
||||||
import { landingURL } from "../+landing-page";
|
import { landingURL } from "../+landing-page";
|
||||||
@ -24,6 +23,7 @@ import { ConfirmDialog } from "../confirm-dialog";
|
|||||||
import { clusterIpc } from "../../../common/cluster-ipc";
|
import { clusterIpc } from "../../../common/cluster-ipc";
|
||||||
import { clusterViewURL } from "./cluster-view.route";
|
import { clusterViewURL } from "./cluster-view.route";
|
||||||
import { globalPageMenuRegistry, globalPageRegistry } from "../../../extensions/registries";
|
import { globalPageMenuRegistry, globalPageRegistry } from "../../../extensions/registries";
|
||||||
|
import { compile } from "path-to-regexp";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
className?: IClassName;
|
className?: IClassName;
|
||||||
@ -149,17 +149,16 @@ export class ClustersMenu extends React.Component<Props> {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="extensions">
|
<div className="extensions">
|
||||||
{globalPageMenuRegistry.getItems().map(({ id: menuItemId, title, url, components: { Icon } }) => {
|
{globalPageMenuRegistry.getItems().map(({ title, target, components: { Icon } }) => {
|
||||||
const registeredPage = globalPageRegistry.getById(menuItemId);
|
const registeredPage = globalPageRegistry.getByPageMenuTarget(target);
|
||||||
if (!registeredPage) return;
|
if (!registeredPage) return;
|
||||||
const { routePath, exact } = registeredPage;
|
const { routePath, exact } = registeredPage;
|
||||||
const isActive = !!matchPath(navigation.location.pathname, { path: routePath, exact });
|
|
||||||
return (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
key={routePath}
|
key={routePath}
|
||||||
tooltip={title}
|
tooltip={title}
|
||||||
active={isActive}
|
active={isActiveRoute({ path: routePath, exact })}
|
||||||
onClick={() => navigate(url)}
|
onClick={() => navigate(compile(routePath)(target.params))}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|||||||
@ -30,6 +30,7 @@ import { isActiveRoute } from "../../navigation";
|
|||||||
import { isAllowedResource } from "../../../common/rbac"
|
import { isAllowedResource } from "../../../common/rbac"
|
||||||
import { Spinner } from "../spinner";
|
import { Spinner } from "../spinner";
|
||||||
import { clusterPageMenuRegistry, clusterPageRegistry } from "../../../extensions/registries";
|
import { clusterPageMenuRegistry, clusterPageRegistry } from "../../../extensions/registries";
|
||||||
|
import { compile } from "path-to-regexp";
|
||||||
|
|
||||||
const SidebarContext = React.createContext<SidebarContextValue>({ pinned: false });
|
const SidebarContext = React.createContext<SidebarContextValue>({ pinned: false });
|
||||||
type SidebarContextValue = {
|
type SidebarContextValue = {
|
||||||
@ -191,10 +192,11 @@ export class Sidebar extends React.Component<Props> {
|
|||||||
>
|
>
|
||||||
{this.renderCustomResources()}
|
{this.renderCustomResources()}
|
||||||
</SidebarNavItem>
|
</SidebarNavItem>
|
||||||
{clusterPageMenuRegistry.getItems().map(({ id: menuItemId, title, url, components: { Icon } }) => {
|
{clusterPageMenuRegistry.getItems().map(({ title, target, components: { Icon } }) => {
|
||||||
const registeredPage = clusterPageRegistry.getById(menuItemId);
|
const registeredPage = clusterPageRegistry.getByPageMenuTarget(target);
|
||||||
if (!registeredPage) return;
|
if (!registeredPage) return;
|
||||||
const { routePath, exact } = registeredPage;
|
const { routePath, exact } = registeredPage;
|
||||||
|
const url = compile(routePath)(target.params)
|
||||||
return (
|
return (
|
||||||
<SidebarNavItem
|
<SidebarNavItem
|
||||||
key={url}
|
key={url}
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: $padding * 2;
|
padding: $padding * 2;
|
||||||
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 40%;
|
grid-template-columns: 1fr 40%;
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Here you can find description of changes we've built into each release. While we try our best to make each upgrade automatic and as smooth as possible, there may be some cases where you might need to do something to ensure the application works smoothly. So please read through the release highlights!
|
Here you can find description of changes we've built into each release. While we try our best to make each upgrade automatic and as smooth as possible, there may be some cases where you might need to do something to ensure the application works smoothly. So please read through the release highlights!
|
||||||
|
|
||||||
## 4.0.0-beta.1 (current version)
|
## 4.0.0-beta.2 (current version)
|
||||||
|
|
||||||
- Extension API
|
- Extension API
|
||||||
- Improved pod logs
|
- Improved pod logs
|
||||||
|
|||||||
@ -188,7 +188,7 @@ export function webpackLensRenderer({ showVars = true } = {}): webpack.Configura
|
|||||||
|
|
||||||
isDevelopment && new webpack.HotModuleReplacementPlugin(),
|
isDevelopment && new webpack.HotModuleReplacementPlugin(),
|
||||||
isDevelopment && new ReactRefreshWebpackPlugin(),
|
isDevelopment && new ReactRefreshWebpackPlugin(),
|
||||||
|
|
||||||
].filter(Boolean),
|
].filter(Boolean),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user