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
|
||||
- script: make test
|
||||
displayName: Run tests
|
||||
- script: make test-extensions
|
||||
displayName: Run In-tree Extension tests
|
||||
- script: make integration-mac
|
||||
displayName: Run integration tests
|
||||
- script: make test-extensions
|
||||
@ -122,8 +124,6 @@ jobs:
|
||||
displayName: Cache Yarn packages
|
||||
- script: make install-deps
|
||||
displayName: Install dependencies
|
||||
- script: make test-extensions
|
||||
displayName: Run In-tree Extension tests
|
||||
- script: make lint
|
||||
displayName: Lint
|
||||
- script: make build-npm
|
||||
@ -132,6 +132,8 @@ jobs:
|
||||
displayName: Build bundled extensions
|
||||
- script: make test
|
||||
displayName: Run tests
|
||||
- script: make test-extensions
|
||||
displayName: Run In-tree Extension tests
|
||||
- bash: |
|
||||
sudo apt-get update
|
||||
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-renderer-api.d.ts
|
||||
extensions/*/dist
|
||||
docs/extensions/api
|
||||
docs/extensions/api
|
||||
|
||||
7
Makefile
7
Makefile
@ -15,7 +15,8 @@ download-bins:
|
||||
yarn download-bins
|
||||
|
||||
install-deps:
|
||||
yarn install --frozen-lockfile
|
||||
yarn install --frozen-lockfile --verbose
|
||||
yarn check --verify-tree --integrity
|
||||
|
||||
compile-dev:
|
||||
yarn compile:main --cache
|
||||
@ -56,10 +57,10 @@ else
|
||||
endif
|
||||
|
||||
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:
|
||||
$(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
|
||||
yarn npm:fix-package-version
|
||||
|
||||
@ -1,130 +1,130 @@
|
||||
# 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
|
||||
- `blue`: blue color.
|
||||
- `magenta`: magenta color.
|
||||
- `golden`: gold/yellow color.
|
||||
- `halfGray`: gray with some apacity applied.
|
||||
- `primary`: Lens brand (blue) color.
|
||||
- `colorSuccess`: successfull operations color.
|
||||
- `colorOk`: successfull operations (bright version) color.
|
||||
- `colorInfo`: informational, in-progress color.
|
||||
- `colorError`: critical error color.
|
||||
- `colorSoftError`: error color.
|
||||
- `colorWarning`: warning color.
|
||||
- `colorVague`: soft gray color for notices, hints etc.
|
||||
- `colorTerminated`: terminated, closed, stale color.
|
||||
- `boxShadow`: semi-transparent box-shadow color.
|
||||
- `--blue`: blue color.
|
||||
- `--magenta`: magenta color.
|
||||
- `--golden`: gold/yellow color.
|
||||
- `--halfGray`: gray with some apacity applied.
|
||||
- `--primary`: Lens brand (blue) color.
|
||||
- `--colorSuccess`: successfull operations color.
|
||||
- `--colorOk`: successfull operations (bright version) color.
|
||||
- `--colorInfo`: informational, in-progress color.
|
||||
- `--colorError`: critical error color.
|
||||
- `--colorSoftError`: error color.
|
||||
- `--colorWarning`: warning color.
|
||||
- `--colorVague`: soft gray color for notices, hints etc.
|
||||
- `--colorTerminated`: terminated, closed, stale color.
|
||||
- `--boxShadow`: semi-transparent box-shadow color.
|
||||
|
||||
## Text colors
|
||||
- `textColorPrimary`: foreground text color.
|
||||
- `textColorSecondary`: foreground text color for different paragraps, parts of text.
|
||||
- `textColorAccent`: foreground text color to highlight its parts.
|
||||
- `--textColorPrimary`: foreground text color.
|
||||
- `--textColorSecondary`: foreground text color for different paragraps, parts of text.
|
||||
- `--textColorAccent`: foreground text color to highlight its parts.
|
||||
|
||||
## Border colors
|
||||
- `borderColor`: border color.
|
||||
- `borderFaintColor`: fainted (lighter or darker, which depends on the theme) border color.
|
||||
- `--borderColor`: border color.
|
||||
- `--borderFaintColor`: fainted (lighter or darker, which depends on the theme) border color.
|
||||
|
||||
## Layout colors
|
||||
- `mainBackground`: main background color for the app.
|
||||
- `contentColor`: background color for panels contains some data.
|
||||
- `layoutBackground`: background color for layout parts.
|
||||
- `layoutTabsBackground`: background color for general tabs.
|
||||
- `layoutTabsActiveColor`: foreground color for general tabs.
|
||||
- `layoutTabsLineColor`: background color for lines under general tabs.
|
||||
- `--mainBackground`: main background color for the app.
|
||||
- `--contentColor`: background color for panels contains some data.
|
||||
- `--layoutBackground`: background color for layout parts.
|
||||
- `--layoutTabsBackground`: background color for general tabs.
|
||||
- `--layoutTabsActiveColor`: foreground color for general tabs.
|
||||
- `--layoutTabsLineColor`: background color for lines under general tabs.
|
||||
|
||||
## Sidebar colors
|
||||
- `sidebarLogoBackground`: background color behind logo in sidebar.
|
||||
- `sidebarActiveColor`: foreground color for active menu items in sidebar.
|
||||
- `sidebarSubmenuActiveColor`: foreground color for active submenu items in sidebar.
|
||||
- `sidebarBackground`: background color for sidebar.
|
||||
- `--sidebarLogoBackground`: background color behind logo in sidebar.
|
||||
- `--sidebarActiveColor`: foreground color for active menu items in sidebar.
|
||||
- `--sidebarSubmenuActiveColor`: foreground color for active submenu items in sidebar.
|
||||
- `--sidebarBackground`: background color for sidebar.
|
||||
|
||||
## Button colors
|
||||
- `buttonPrimaryBackground`: button background color for primary actions.
|
||||
- `buttonDefaultBackground`: default button background color.
|
||||
- `buttonAccentBackground`: accent button background color.
|
||||
- `buttonDisabledBackground`: disabled button background color.
|
||||
- `--buttonPrimaryBackground`: button background color for primary actions.
|
||||
- `--buttonDefaultBackground`: default button background color.
|
||||
- `--buttonAccentBackground`: accent button background color.
|
||||
- `--buttonDisabledBackground`: disabled button background color.
|
||||
|
||||
## Table colors
|
||||
- `tableBgcStripe`: background color for odd rows in table.
|
||||
- `tableBgcSelected`: background color for selected row in table.
|
||||
- `tableHeaderBackground`: background color for table header.
|
||||
- `tableHeaderBorderWidth`: border width under table header.
|
||||
- `tableHeaderBorderColor`: border color for line under table header.
|
||||
- `tableHeaderColor`: foreground color for table header.
|
||||
- `tableSelectedRowColor`: foreground color for selected row in table.
|
||||
- `--tableBgcStripe`: background color for odd rows in table.
|
||||
- `--tableBgcSelected`: background color for selected row in table.
|
||||
- `--tableHeaderBackground`: background color for table header.
|
||||
- `--tableHeaderBorderWidth`: border width under table header.
|
||||
- `--tableHeaderBorderColor`: border color for line under table header.
|
||||
- `--tableHeaderColor`: foreground color for table header.
|
||||
- `--tableSelectedRowColor`: foreground color for selected row in table.
|
||||
|
||||
## Dock colors
|
||||
- `dockHeadBackground`: background color for dock's header.
|
||||
- `dockInfoBackground`: background color for dock's info panel.
|
||||
- `dockInfoBorderColor`: border color for dock's info panel.
|
||||
- `--dockHeadBackground`: background color for dock's header.
|
||||
- `--dockInfoBackground`: background color for dock's info panel.
|
||||
- `--dockInfoBorderColor`: border color for dock's info panel.
|
||||
|
||||
## Helm chart colors
|
||||
- `helmLogoBackground`: background color for chart logo.
|
||||
- `helmImgBackground`: background color for chart image.
|
||||
- `helmStableRepo`: background color for stable repo.
|
||||
- `helmIncubatorRepo`: background color for incubator repo.
|
||||
- `helmDescriptionHr`: Helm chart description separator line color.
|
||||
- `helmDescriptionBlockqouteColor`: Helm chart description blockquote color.
|
||||
- `helmDescriptionBlockqouteBorder`: Helm chart description blockquote border color.
|
||||
- `helmDescriptionBlockquoteBackground`: Helm chart description blockquote background color.
|
||||
- `helmDescriptionHeaders`: Helm chart description headers color.
|
||||
- `helmDescriptionH6`: Helm chart description header foreground color.
|
||||
- `helmDescriptionTdBorder`: Helm chart description table cell border color.
|
||||
- `helmDescriptionTrBackground`: Helm chart description table row background color.
|
||||
- `helmDescriptionCodeBackground`: Helm chart description code background color.
|
||||
- `helmDescriptionPreBackground`: Helm chart description pre background color.
|
||||
- `helmDescriptionPreColor`: Helm chart description pre foreground color.
|
||||
- `--helmLogoBackground`: background color for chart logo.
|
||||
- `--helmImgBackground`: background color for chart image.
|
||||
- `--helmStableRepo`: background color for stable repo.
|
||||
- `--helmIncubatorRepo`: background color for incubator repo.
|
||||
- `--helmDescriptionHr`: Helm chart description separator line color.
|
||||
- `--helmDescriptionBlockqouteColor`: Helm chart description blockquote color.
|
||||
- `--helmDescriptionBlockqouteBorder`: Helm chart description blockquote border color.
|
||||
- `--helmDescriptionBlockquoteBackground`: Helm chart description blockquote background color.
|
||||
- `--helmDescriptionHeaders`: Helm chart description headers color.
|
||||
- `--helmDescriptionH6`: Helm chart description header foreground color.
|
||||
- `--helmDescriptionTdBorder`: Helm chart description table cell border color.
|
||||
- `--helmDescriptionTrBackground`: Helm chart description table row background color.
|
||||
- `--helmDescriptionCodeBackground`: Helm chart description code background color.
|
||||
- `--helmDescriptionPreBackground`: Helm chart description pre background color.
|
||||
- `--helmDescriptionPreColor`: Helm chart description pre foreground color.
|
||||
|
||||
## Terminal colors
|
||||
- `terminalBackground`: Terminal background color.
|
||||
- `terminalForeground`: Terminal foreground color.
|
||||
- `terminalCursor`: Terminal cursor color.
|
||||
- `terminalCursorAccent`: Terminal cursor accent color.
|
||||
- `terminalSelection`: Terminal selection background color.
|
||||
- `terminalBlack`: Terminal black color.
|
||||
- `terminalRed`: Terminal red color.
|
||||
- `terminalGreen`: Terminal green color.
|
||||
- `terminalYellow`: Terminal yellow color.
|
||||
- `terminalBlue`: Terminal blue color.
|
||||
- `terminalMagenta`: Terminal magenta color.
|
||||
- `terminalCyan`: Terminal cyan color.
|
||||
- `terminalWhite`: Terminal white color.
|
||||
- `terminalBrightBlack`: Terminal bright black color.
|
||||
- `terminalBrightRed`: Terminal bright red color.
|
||||
- `terminalBrightGreen`: Terminal bright green color.
|
||||
- `terminalBrightYellow`: Terminal bright yellow color.
|
||||
- `terminalBrightBlue`: Terminal bright blue color.
|
||||
- `terminalBrightMagenta`: Terminal bright magenta color.
|
||||
- `terminalBrightCyan`: Terminal bright cyan color.
|
||||
- `terminalBrightWhite`: Terminal bright white color.
|
||||
- `--terminalBackground`: Terminal background color.
|
||||
- `--terminalForeground`: Terminal foreground color.
|
||||
- `--terminalCursor`: Terminal cursor color.
|
||||
- `--terminalCursorAccent`: Terminal cursor accent color.
|
||||
- `--terminalSelection`: Terminal selection background color.
|
||||
- `--terminalBlack`: Terminal black color.
|
||||
- `--terminalRed`: Terminal red color.
|
||||
- `--terminalGreen`: Terminal green color.
|
||||
- `--terminalYellow`: Terminal yellow color.
|
||||
- `--terminalBlue`: Terminal blue color.
|
||||
- `--terminalMagenta`: Terminal magenta color.
|
||||
- `--terminalCyan`: Terminal cyan color.
|
||||
- `--terminalWhite`: Terminal white color.
|
||||
- `--terminalBrightBlack`: Terminal bright black color.
|
||||
- `--terminalBrightRed`: Terminal bright red color.
|
||||
- `--terminalBrightGreen`: Terminal bright green color.
|
||||
- `--terminalBrightYellow`: Terminal bright yellow color.
|
||||
- `--terminalBrightBlue`: Terminal bright blue color.
|
||||
- `--terminalBrightMagenta`: Terminal bright magenta color.
|
||||
- `--terminalBrightCyan`: Terminal bright cyan color.
|
||||
- `--terminalBrightWhite`: Terminal bright white color.
|
||||
|
||||
## Dialog colors
|
||||
- `dialogHeaderBackground`: background color for dialog header.
|
||||
- `dialogFooterBackground`: background color for dialog footer.
|
||||
- `--dialogHeaderBackground`: background color for dialog header.
|
||||
- `--dialogFooterBackground`: background color for dialog footer.
|
||||
|
||||
## Detail panel (Drawer) colors
|
||||
- `drawerTitleText`: drawer title foreground color.
|
||||
- `drawerSubtitleBackground`: drawer subtitle foreground color.
|
||||
- `drawerItemNameColor`: foreground color for item name in drawer.
|
||||
- `drawerItemValueColor`: foreground color for item value in drawer.
|
||||
- `--drawerTitleText`: drawer title foreground color.
|
||||
- `--drawerSubtitleBackground`: drawer subtitle foreground color.
|
||||
- `--drawerItemNameColor`: foreground color for item name in drawer.
|
||||
- `--drawerItemValueColor`: foreground color for item value in drawer.
|
||||
|
||||
## Misc colors
|
||||
- `logsBackground`: background color for pod logs.
|
||||
- `clusterMenuBackground`: background color for cluster menu.
|
||||
- `clusterMenuBorderColor`: border color for cluster menu.
|
||||
- `clusterSettingsBackground`: background color for cluster settings.
|
||||
- `addClusterIconColor`: add cluster button background color.
|
||||
- `iconActiveColor`: active cluster icon foreground color.
|
||||
- `iconActiveBackground`: active cluster icon background color.
|
||||
- `filterAreaBackground`: page filter area (where selected namespaces are lister) background color.
|
||||
- `chartStripesColor`: bar chart zebra stripes background color.
|
||||
- `chartCapacityColor`: background color for capacity values in bar charts.
|
||||
- `pieChartDefaultColor`: default background color for pie chart values.
|
||||
- `selectOptionHoveredColor`: foregrond color for selected element in dropdown list.
|
||||
- `lineProgressBackground`: background color for progress line.
|
||||
- `radioActiveBackground`: background color for active radio buttons.
|
||||
- `menuActiveBackground`: background color for active menu items.
|
||||
- `--logsBackground`: background color for pod logs.
|
||||
- `--clusterMenuBackground`: background color for cluster menu.
|
||||
- `--clusterMenuBorderColor`: border color for cluster menu.
|
||||
- `--clusterSettingsBackground`: background color for cluster settings.
|
||||
- `--addClusterIconColor`: add cluster button background color.
|
||||
- `--iconActiveColor`: active cluster icon foreground color.
|
||||
- `--iconActiveBackground`: active cluster icon background color.
|
||||
- `--filterAreaBackground`: page filter area (where selected namespaces are lister) background color.
|
||||
- `--chartStripesColor`: bar chart zebra stripes background color.
|
||||
- `--chartCapacityColor`: background color for capacity values in bar charts.
|
||||
- `--pieChartDefaultColor`: default background color for pie chart values.
|
||||
- `--selectOptionHoveredColor`: foregrond color for selected element in dropdown list.
|
||||
- `--lineProgressBackground`: background color for progress line.
|
||||
- `--radioActiveBackground`: background color for active radio buttons.
|
||||
- `--menuActiveBackground`: background color for active menu items.
|
||||
|
||||
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 {
|
||||
globalPages = [
|
||||
{
|
||||
id: "example",
|
||||
routePath: "/example",
|
||||
components: {
|
||||
Page: ExamplePage,
|
||||
}
|
||||
@ -109,6 +111,7 @@ export default class ExampleRendererExtension extends LensRendererExtension {
|
||||
globalPageMenus = [
|
||||
{
|
||||
title: "Example page", // used in icon's tooltip
|
||||
target: { pageId: "example" }
|
||||
components: {
|
||||
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.
|
||||
|
||||
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):
|
||||
|
||||
@ -137,7 +137,7 @@ Currently, there is no prescribed way of detecting changes to the theme in JavaS
|
||||
|
||||
## 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.
|
||||
|
||||
|
||||
@ -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 {
|
||||
onActivate() {
|
||||
console.log('EXAMPLE EXTENSION MAIN: ACTIVATED', this.getMeta());
|
||||
console.log('EXAMPLE EXTENSION MAIN: ACTIVATED', this.name, this.id);
|
||||
}
|
||||
|
||||
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": {
|
||||
"build": "webpack --config webpack.config.js",
|
||||
"dev": "npm run build --watch",
|
||||
"test": "echo NO TESTS"
|
||||
"test": "jest --passWithNoTests --env=jsdom src $@"
|
||||
},
|
||||
"dependencies": {
|
||||
"react-open-doodles": "^1.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
|
||||
"jest": "^26.6.3",
|
||||
"ts-loader": "^8.0.4",
|
||||
"typescript": "^4.0.3",
|
||||
"webpack": "^4.44.2"
|
||||
|
||||
@ -5,11 +5,21 @@ import React from "react"
|
||||
export default class ExampleExtension extends LensRendererExtension {
|
||||
clusterPages = [
|
||||
{
|
||||
path: "/extension-example",
|
||||
id: "example",
|
||||
routePath: "/extension-example",
|
||||
title: "Example Extension",
|
||||
components: {
|
||||
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": {
|
||||
"build": "webpack -p",
|
||||
"dev": "webpack --watch",
|
||||
"test": "echo NO TESTS"
|
||||
"test": "jest --passWithNoTests --env=jsdom src $@"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@types/webpack": "^4.41.17",
|
||||
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
|
||||
"@types/webpack": "^4.41.17",
|
||||
"jest": "^26.6.3",
|
||||
"mobx": "^5.15.5",
|
||||
"react": "^16.13.1",
|
||||
"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": {
|
||||
"build": "webpack --config webpack.config.js",
|
||||
"dev": "npm run build --watch",
|
||||
"test": "echo NO TESTS"
|
||||
"test": "jest --passWithNoTests --env=jsdom src $@"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": "^7.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
|
||||
"jest": "^26.6.3",
|
||||
"mobx": "^5.15.5",
|
||||
"react": "^16.13.1",
|
||||
"ts-loader": "^8.0.4",
|
||||
"typescript": "^4.0.3",
|
||||
"webpack": "^4.44.2",
|
||||
"mobx": "^5.15.5",
|
||||
"react": "^16.13.1"
|
||||
"webpack": "^4.44.2"
|
||||
}
|
||||
}
|
||||
|
||||
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": {
|
||||
"build": "webpack --config webpack.config.js",
|
||||
"dev": "npm run build --watch",
|
||||
"test": "echo NO TESTS"
|
||||
"test": "jest --passWithNoTests --env=jsdom src $@"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
|
||||
"jest": "^26.6.3",
|
||||
"mobx": "^5.15.5",
|
||||
"react": "^16.13.1",
|
||||
"ts-loader": "^8.0.4",
|
||||
"typescript": "^4.0.3",
|
||||
"webpack": "^4.44.2",
|
||||
"mobx": "^5.15.5",
|
||||
"react": "^16.13.1"
|
||||
"webpack": "^4.44.2"
|
||||
}
|
||||
}
|
||||
|
||||
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": {
|
||||
"build": "webpack --config webpack.config.js",
|
||||
"dev": "npm run build --watch",
|
||||
"test": "echo NO TESTS"
|
||||
"test": "jest --passWithNoTests --env=jsdom src $@"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"ts-loader": "^8.0.4",
|
||||
"typescript": "^4.0.3",
|
||||
"webpack": "^4.44.2",
|
||||
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
|
||||
"jest": "^26.6.3",
|
||||
"mobx": "^5.15.5",
|
||||
"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",
|
||||
label: "Support",
|
||||
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": {
|
||||
"build": "webpack -p",
|
||||
"dev": "webpack --watch",
|
||||
"test": "echo NO TESTS"
|
||||
"test": "jest --passWithNoTests --env=jsdom src $@"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
@ -16,6 +16,7 @@
|
||||
"@types/react-router": "^5.1.8",
|
||||
"@types/webpack": "^4.41.17",
|
||||
"css-loader": "^5.0.0",
|
||||
"jest": "^26.6.3",
|
||||
"mobx": "^5.15.5",
|
||||
"react": "^16.13.1",
|
||||
"sass-loader": "^10.0.4",
|
||||
|
||||
@ -5,6 +5,8 @@ import { SupportPage } from "./src/support";
|
||||
export default class SupportPageRendererExtension extends LensRendererExtension {
|
||||
globalPages: Interface.PageRegistration[] = [
|
||||
{
|
||||
id: "support",
|
||||
routePath: "/support",
|
||||
components: {
|
||||
Page: SupportPage,
|
||||
}
|
||||
@ -14,7 +16,7 @@ export default class SupportPageRendererExtension extends LensRendererExtension
|
||||
statusBarItems: Interface.StatusBarRegistration[] = [
|
||||
{
|
||||
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/>
|
||||
</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": {
|
||||
"build": "webpack -p",
|
||||
"dev": "webpack --watch",
|
||||
"test": "echo NO TESTS"
|
||||
"test": "jest --passWithNoTests --env=jsdom src $@"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
|
||||
"@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",
|
||||
"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",
|
||||
"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",
|
||||
"productName": "Lens",
|
||||
"description": "Lens - The Kubernetes IDE",
|
||||
"version": "4.0.0-beta.1",
|
||||
"version": "4.0.0-beta.2",
|
||||
"main": "static/build/main.js",
|
||||
"copyright": "© 2020, Mirantis, Inc.",
|
||||
"license": "MIT",
|
||||
@ -11,35 +11,35 @@
|
||||
"email": "info@k8slens.dev"
|
||||
},
|
||||
"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-run": "nodemon --watch static/build/main.js --exec \"electron --inspect .\"",
|
||||
"dev:main": "yarn compile:main --watch",
|
||||
"dev:renderer": "yarn webpack-dev-server --config webpack.renderer.ts",
|
||||
"dev:extension-types": "yarn compile:extension-types --watch",
|
||||
"dev:main": "yarn run compile:main --watch",
|
||||
"dev:renderer": "yarn run webpack-dev-server --config webpack.renderer.ts",
|
||||
"dev:extension-types": "yarn run compile:extension-types --watch",
|
||||
"compile": "env NODE_ENV=production concurrently yarn:compile:*",
|
||||
"compile:main": "webpack --config webpack.main.ts",
|
||||
"compile:renderer": "webpack --config webpack.renderer.ts",
|
||||
"compile:i18n": "lingui compile",
|
||||
"compile:extension-types": "rollup --config src/extensions/rollup.config.js",
|
||||
"npm:fix-package-version": "ts-node build/set_npm_version.ts",
|
||||
"build:linux": "yarn compile && electron-builder --linux --dir -c.productName=Lens",
|
||||
"build:mac": "yarn compile && electron-builder --mac --dir -c.productName=Lens",
|
||||
"build:win": "yarn compile && electron-builder --win --dir -c.productName=Lens",
|
||||
"compile:main": "yarn run webpack --config webpack.main.ts",
|
||||
"compile:renderer": "yarn run webpack --config webpack.renderer.ts",
|
||||
"compile:i18n": "yarn run lingui compile",
|
||||
"compile:extension-types": "yarn run rollup --config src/extensions/rollup.config.js",
|
||||
"npm:fix-package-version": "yarn run ts-node build/set_npm_version.ts",
|
||||
"build:linux": "yarn run compile && electron-builder --linux --dir -c.productName=Lens",
|
||||
"build:mac": "yarn run compile && electron-builder --mac --dir -c.productName=Lens",
|
||||
"build:win": "yarn run compile && electron-builder --win --dir -c.productName=Lens",
|
||||
"test": "jest --env=jsdom src $@",
|
||||
"integration": "jest --coverage integration $@",
|
||||
"dist": "yarn compile && electron-builder --publish onTag",
|
||||
"dist:win": "yarn compile && electron-builder --publish onTag --x64 --ia32",
|
||||
"dist:dir": "yarn dist --dir -c.compression=store -c.mac.identity=null",
|
||||
"dist": "yarn run compile && electron-builder --publish onTag",
|
||||
"dist:win": "yarn run compile && electron-builder --publish onTag --x64 --ia32",
|
||||
"dist:dir": "yarn run dist --dir -c.compression=store -c.mac.identity=null",
|
||||
"postinstall": "patch-package",
|
||||
"i18n:extract": "lingui extract",
|
||||
"i18n:extract": "yarn run lingui extract",
|
||||
"download-bins": "concurrently yarn:download:*",
|
||||
"download:kubectl": "yarn run ts-node build/download_kubectl.ts",
|
||||
"download:helm": "yarn run ts-node build/download_helm.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",
|
||||
"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": {
|
||||
"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() {
|
||||
logger.info('[EXTENSIONS-LOADER]: load on main')
|
||||
this.autoInitExtensions((ext: LensMainExtension) => [
|
||||
registries.menuRegistry.add(ext.appMenus, { key: ext })
|
||||
registries.menuRegistry.add(ext.appMenus)
|
||||
]);
|
||||
}
|
||||
|
||||
loadOnClusterManagerRenderer() {
|
||||
logger.info('[EXTENSIONS-LOADER]: load on main renderer (cluster manager)')
|
||||
this.autoInitExtensions((ext: LensRendererExtension) => [
|
||||
registries.globalPageRegistry.add(ext.globalPages, { key: ext }),
|
||||
registries.globalPageMenuRegistry.add(ext.globalPageMenus, { key: ext }),
|
||||
registries.appPreferenceRegistry.add(ext.appPreferences, { key: ext }),
|
||||
registries.clusterFeatureRegistry.add(ext.clusterFeatures, { key: ext }),
|
||||
registries.statusBarRegistry.add(ext.statusBarItems, { key: ext }),
|
||||
registries.globalPageRegistry.add(ext.globalPages, ext),
|
||||
registries.globalPageMenuRegistry.add(ext.globalPageMenus, ext),
|
||||
registries.appPreferenceRegistry.add(ext.appPreferences),
|
||||
registries.clusterFeatureRegistry.add(ext.clusterFeatures),
|
||||
registries.statusBarRegistry.add(ext.statusBarItems),
|
||||
]);
|
||||
}
|
||||
|
||||
loadOnClusterRenderer() {
|
||||
logger.info('[EXTENSIONS-LOADER]: load on cluster renderer (dashboard)')
|
||||
this.autoInitExtensions((ext: LensRendererExtension) => [
|
||||
registries.clusterPageRegistry.add(ext.clusterPages, { key: ext }),
|
||||
registries.clusterPageMenuRegistry.add(ext.clusterPageMenus, { key: ext }),
|
||||
registries.kubeObjectMenuRegistry.add(ext.kubeObjectMenuItems, { key: ext }),
|
||||
registries.kubeObjectDetailRegistry.add(ext.kubeObjectDetailItems, { key: ext }),
|
||||
registries.kubeObjectStatusRegistry.add(ext.kubeObjectStatusTexts, { key: ext })
|
||||
registries.clusterPageRegistry.add(ext.clusterPages, ext),
|
||||
registries.clusterPageMenuRegistry.add(ext.clusterPageMenus, ext),
|
||||
registries.kubeObjectMenuRegistry.add(ext.kubeObjectMenuItems),
|
||||
registries.kubeObjectDetailRegistry.add(ext.kubeObjectDetailItems),
|
||||
registries.kubeObjectStatusRegistry.add(ext.kubeObjectStatusTexts)
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import type { InstalledExtension } from "./extension-manager";
|
||||
import { action, observable, reaction } from "mobx";
|
||||
import { compile } from "path-to-regexp"
|
||||
import logger from "../main/logger";
|
||||
|
||||
export type LensExtensionId = string; // path to manifest (package.json)
|
||||
@ -15,7 +14,6 @@ export interface LensExtensionManifest {
|
||||
}
|
||||
|
||||
export class LensExtension {
|
||||
readonly routePrefix = "/extension/:name"
|
||||
readonly manifest: LensExtensionManifest;
|
||||
readonly manifestPath: string;
|
||||
readonly isBundled: boolean;
|
||||
@ -44,14 +42,6 @@ export class LensExtension {
|
||||
return this.manifest.description
|
||||
}
|
||||
|
||||
getPageUrl(baseUrl = "") {
|
||||
return compile(this.routePrefix)({ name: this.name }) + baseUrl;
|
||||
}
|
||||
|
||||
getPageRoute(baseRoute = "") {
|
||||
return this.routePrefix + baseRoute;
|
||||
}
|
||||
|
||||
@action
|
||||
async enable() {
|
||||
if (this.isEnabled) return;
|
||||
|
||||
@ -2,13 +2,14 @@ import type { MenuRegistration } from "./registries/menu-registry";
|
||||
import { observable } from "mobx";
|
||||
import { LensExtension } from "./lens-extension"
|
||||
import { WindowManager } from "../main/window-manager";
|
||||
import { getPageUrl } from "./registries/page-registry"
|
||||
|
||||
export class LensMainExtension extends LensExtension {
|
||||
@observable.shallow appMenus: MenuRegistration[] = []
|
||||
|
||||
async navigate(location?: string, frameId?: number) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import type { AppPreferenceRegistration, ClusterFeatureRegistration, KubeObjectDetailRegistration, KubeObjectMenuRegistration, KubeObjectStatusRegistration, PageMenuRegistration, PageRegistration, StatusBarRegistration, } from "./registries"
|
||||
import { observable } from "mobx";
|
||||
import { LensExtension } from "./lens-extension"
|
||||
import { getPageUrl } from "./registries/page-registry"
|
||||
|
||||
export class LensRendererExtension extends LensExtension {
|
||||
@observable.shallow globalPages: PageRegistration[] = []
|
||||
@ -16,6 +17,6 @@ export class LensRendererExtension extends LensExtension {
|
||||
|
||||
async navigate(location?: string) {
|
||||
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 { BaseRegistry, BaseRegistryItem } from "./base-registry";
|
||||
import { BaseRegistry } from "./base-registry";
|
||||
|
||||
export interface AppPreferenceComponents {
|
||||
Hint: React.ComponentType<any>;
|
||||
Input: React.ComponentType<any>;
|
||||
}
|
||||
|
||||
export interface AppPreferenceRegistration extends BaseRegistryItem {
|
||||
export interface AppPreferenceRegistration {
|
||||
title: string;
|
||||
components: AppPreferenceComponents;
|
||||
}
|
||||
|
||||
@ -1,65 +1,24 @@
|
||||
// Base class for extensions-api registries
|
||||
import { action, observable } from "mobx";
|
||||
import { LensExtension } from "../lens-extension";
|
||||
import { getRandId } from "../../common/utils";
|
||||
|
||||
export type BaseRegistryKey = LensExtension | null;
|
||||
export type BaseRegistryItemId = string | symbol;
|
||||
export class BaseRegistry<T = any> {
|
||||
private items = observable<T>([], { deep: false });
|
||||
|
||||
export interface BaseRegistryItem {
|
||||
id?: BaseRegistryItemId; // uniq id, generated automatically when not provided
|
||||
}
|
||||
|
||||
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);
|
||||
getItems(): T[] {
|
||||
return this.items.toJS();
|
||||
}
|
||||
|
||||
@action
|
||||
add(items: T | T[], { key = null, merge = true }: BaseRegistryAddMeta = {}) {
|
||||
const normalizedItems = (Array.isArray(items) ? items : [items]).map((item: T) => {
|
||||
item.id = item.id || getRandId();
|
||||
return item;
|
||||
});
|
||||
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)
|
||||
add(items: T | T[]) {
|
||||
const normalizedItems = (Array.isArray(items) ? items : [items])
|
||||
this.items.push(...normalizedItems);
|
||||
return () => this.remove(...normalizedItems);
|
||||
}
|
||||
|
||||
@action
|
||||
remove(items: T[], key: BaseRegistryKey = null) {
|
||||
const storedItems = this.items.get(key);
|
||||
if (!storedItems) return;
|
||||
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);
|
||||
}
|
||||
remove(...items: T[]) {
|
||||
items.forEach(item => {
|
||||
this.items.remove(item); // works because of {deep: false};
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import type React from "react"
|
||||
import { BaseRegistry, BaseRegistryItem } from "./base-registry";
|
||||
import { BaseRegistry } from "./base-registry";
|
||||
import { ClusterFeature } from "../cluster-feature";
|
||||
|
||||
export interface ClusterFeatureComponents {
|
||||
Description: React.ComponentType<any>;
|
||||
}
|
||||
|
||||
export interface ClusterFeatureRegistration extends BaseRegistryItem {
|
||||
export interface ClusterFeatureRegistration {
|
||||
title: string;
|
||||
components: ClusterFeatureComponents
|
||||
feature: ClusterFeature
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import React from "react"
|
||||
import { BaseRegistry, BaseRegistryItem } from "./base-registry";
|
||||
import { BaseRegistry } from "./base-registry";
|
||||
|
||||
export interface KubeObjectDetailComponents {
|
||||
Details: React.ComponentType<any>;
|
||||
}
|
||||
|
||||
export interface KubeObjectDetailRegistration extends BaseRegistryItem {
|
||||
export interface KubeObjectDetailRegistration {
|
||||
kind: string;
|
||||
apiVersions: string[];
|
||||
components: KubeObjectDetailComponents;
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import React from "react"
|
||||
import { BaseRegistry, BaseRegistryItem } from "./base-registry";
|
||||
import { BaseRegistry } from "./base-registry";
|
||||
|
||||
export interface KubeObjectMenuComponents {
|
||||
MenuItem: React.ComponentType<any>;
|
||||
}
|
||||
|
||||
export interface KubeObjectMenuRegistration extends BaseRegistryItem {
|
||||
export interface KubeObjectMenuRegistration {
|
||||
kind: string;
|
||||
apiVersions: string[];
|
||||
components: KubeObjectMenuComponents;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
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;
|
||||
apiVersions: string[];
|
||||
resolve: (object: KubeObject) => KubeObjectStatus;
|
||||
|
||||
@ -1,20 +1,21 @@
|
||||
// Extensions-api -> Register page menu items
|
||||
|
||||
import type React from "react";
|
||||
import { action } from "mobx";
|
||||
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 {
|
||||
id: BaseRegistryItemId; // required id from page-registry item to match with
|
||||
url?: string; // when not provided initial extension's path used, e.g. "/extension/lens-extension-name"
|
||||
title: React.ReactNode;
|
||||
components: PageMenuComponents;
|
||||
subMenus?: PageSubMenuRegistration[];
|
||||
export interface PageMenuTarget {
|
||||
pageId: string;
|
||||
extensionId?: string;
|
||||
params?: object;
|
||||
}
|
||||
|
||||
export interface PageSubMenuRegistration {
|
||||
url: string;
|
||||
export interface PageMenuRegistration {
|
||||
target?: PageMenuTarget;
|
||||
title: React.ReactNode;
|
||||
components: PageMenuComponents;
|
||||
}
|
||||
|
||||
export interface PageMenuComponents {
|
||||
@ -22,13 +23,18 @@ export interface PageMenuComponents {
|
||||
}
|
||||
|
||||
export class PageMenuRegistry<T extends PageMenuRegistration> extends BaseRegistry<T> {
|
||||
getItems() {
|
||||
return super.getItems().map(item => {
|
||||
item.url = item.extension.getPageUrl(item.url)
|
||||
return item
|
||||
});
|
||||
|
||||
@action
|
||||
add(items: T[], ext?: LensExtension) {
|
||||
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 clusterPageMenuRegistry = new PageMenuRegistry();
|
||||
export const globalPageMenuRegistry = new PageMenuRegistry<PageMenuRegistration>();
|
||||
export const clusterPageMenuRegistry = new PageMenuRegistry<PageMenuRegistration>();
|
||||
|
||||
@ -1,33 +1,59 @@
|
||||
// Extensions-api -> Custom page registration
|
||||
|
||||
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"
|
||||
exact?: boolean; // route matching flag, see: https://reactrouter.com/web/api/NavLink/exact-bool
|
||||
components: PageComponents;
|
||||
subPages?: SubPageRegistration[];
|
||||
}
|
||||
|
||||
export interface SubPageRegistration {
|
||||
routePath: string; // required for sub-pages
|
||||
exact?: boolean;
|
||||
components: PageComponents;
|
||||
}
|
||||
|
||||
export interface PageComponents {
|
||||
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> {
|
||||
getItems() {
|
||||
return super.getItems().map(item => {
|
||||
item.routePath = item.extension.getPageRoute(item.routePath)
|
||||
return item
|
||||
});
|
||||
|
||||
@action
|
||||
add(items: T[], ext?: LensExtension) {
|
||||
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 clusterPageRegistry = new PageRegistry();
|
||||
export const globalPageRegistry = new PageRegistry<PageRegistration>();
|
||||
export const clusterPageRegistry = new PageRegistry<PageRegistration>();
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
// Extensions API -> Status bar customizations
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import { readFile } from "fs-extra"
|
||||
import { Cluster } from "./cluster"
|
||||
import { apiPrefix, appName, publicPath, isDevelopment, webpackDevServerPort } from "../common/vars";
|
||||
import { helmRoute, kubeconfigRoute, metricsRoute, portForwardRoute, resourceApplierRoute, watchRoute } from "./routes";
|
||||
import logger from "./logger"
|
||||
|
||||
export interface RouterRequestOpts {
|
||||
req: http.IncomingMessage;
|
||||
@ -94,7 +95,7 @@ export class Router {
|
||||
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);
|
||||
try {
|
||||
const filename = path.basename(req.url);
|
||||
@ -112,7 +113,13 @@ export class Router {
|
||||
res.write(data);
|
||||
res.end();
|
||||
} 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
|
||||
this.router.add(
|
||||
{ method: 'get', path: '/{path*}' },
|
||||
({ params, response, path, raw: { req }}: LensApiRequest) => {
|
||||
({ params, response, path, raw: { req } }: LensApiRequest) => {
|
||||
this.handleStaticFile(params.path, response, req);
|
||||
});
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
.LandingPage {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
z-index: 0;
|
||||
|
||||
@ -117,7 +117,7 @@ export class Workspaces extends React.Component {
|
||||
const isEditing = this.editingWorkspaces.has(workspaceId);
|
||||
const editingWorkspace = this.editingWorkspaces.get(workspaceId);
|
||||
const managed = !!ownerRef
|
||||
const className = cssNames("workspace flex gaps", {
|
||||
const className = cssNames("workspace flex gaps align-center", {
|
||||
active: isActive,
|
||||
editing: isEditing,
|
||||
default: isDefault,
|
||||
|
||||
@ -74,27 +74,8 @@ export class App extends React.Component {
|
||||
}
|
||||
|
||||
renderExtensionRoutes() {
|
||||
return clusterPageRegistry.getItems().map(({ id: pageId, components: { Page }, exact, routePath, subPages }) => {
|
||||
return clusterPageRegistry.getItems().map(({ components: { Page }, exact, routePath }) => {
|
||||
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 <Route key={routePath} path={routePath} exact={exact} component={Component}/>
|
||||
|
||||
@ -5,7 +5,6 @@ import { remote } from "electron"
|
||||
import type { Cluster } from "../../../main/cluster";
|
||||
import { DragDropContext, Draggable, DraggableProvided, Droppable, DroppableProvided, DropResult } from "react-beautiful-dnd";
|
||||
import { observer } from "mobx-react";
|
||||
import { matchPath } from "react-router";
|
||||
import { _i18n } from "../../i18n";
|
||||
import { t, Trans } from "@lingui/macro";
|
||||
import { userStore } from "../../../common/user-store";
|
||||
@ -15,7 +14,7 @@ import { ClusterIcon } from "../cluster-icon";
|
||||
import { Icon } from "../icon";
|
||||
import { autobind, cssNames, IClassName } from "../../utils";
|
||||
import { Badge } from "../badge";
|
||||
import { navigate, navigation } from "../../navigation";
|
||||
import { isActiveRoute, navigate } from "../../navigation";
|
||||
import { addClusterURL } from "../+add-cluster";
|
||||
import { clusterSettingsURL } from "../+cluster-settings";
|
||||
import { landingURL } from "../+landing-page";
|
||||
@ -24,6 +23,7 @@ import { ConfirmDialog } from "../confirm-dialog";
|
||||
import { clusterIpc } from "../../../common/cluster-ipc";
|
||||
import { clusterViewURL } from "./cluster-view.route";
|
||||
import { globalPageMenuRegistry, globalPageRegistry } from "../../../extensions/registries";
|
||||
import { compile } from "path-to-regexp";
|
||||
|
||||
interface Props {
|
||||
className?: IClassName;
|
||||
@ -149,17 +149,16 @@ export class ClustersMenu extends React.Component<Props> {
|
||||
)}
|
||||
</div>
|
||||
<div className="extensions">
|
||||
{globalPageMenuRegistry.getItems().map(({ id: menuItemId, title, url, components: { Icon } }) => {
|
||||
const registeredPage = globalPageRegistry.getById(menuItemId);
|
||||
{globalPageMenuRegistry.getItems().map(({ title, target, components: { Icon } }) => {
|
||||
const registeredPage = globalPageRegistry.getByPageMenuTarget(target);
|
||||
if (!registeredPage) return;
|
||||
const { routePath, exact } = registeredPage;
|
||||
const isActive = !!matchPath(navigation.location.pathname, { path: routePath, exact });
|
||||
return (
|
||||
<Icon
|
||||
key={routePath}
|
||||
tooltip={title}
|
||||
active={isActive}
|
||||
onClick={() => navigate(url)}
|
||||
active={isActiveRoute({ path: routePath, exact })}
|
||||
onClick={() => navigate(compile(routePath)(target.params))}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
|
||||
@ -30,6 +30,7 @@ import { isActiveRoute } from "../../navigation";
|
||||
import { isAllowedResource } from "../../../common/rbac"
|
||||
import { Spinner } from "../spinner";
|
||||
import { clusterPageMenuRegistry, clusterPageRegistry } from "../../../extensions/registries";
|
||||
import { compile } from "path-to-regexp";
|
||||
|
||||
const SidebarContext = React.createContext<SidebarContextValue>({ pinned: false });
|
||||
type SidebarContextValue = {
|
||||
@ -191,10 +192,11 @@ export class Sidebar extends React.Component<Props> {
|
||||
>
|
||||
{this.renderCustomResources()}
|
||||
</SidebarNavItem>
|
||||
{clusterPageMenuRegistry.getItems().map(({ id: menuItemId, title, url, components: { Icon } }) => {
|
||||
const registeredPage = clusterPageRegistry.getById(menuItemId);
|
||||
{clusterPageMenuRegistry.getItems().map(({ title, target, components: { Icon } }) => {
|
||||
const registeredPage = clusterPageRegistry.getByPageMenuTarget(target);
|
||||
if (!registeredPage) return;
|
||||
const { routePath, exact } = registeredPage;
|
||||
const url = compile(routePath)(target.params)
|
||||
return (
|
||||
<SidebarNavItem
|
||||
key={url}
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
|
||||
position: relative;
|
||||
padding: $padding * 2;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: grid;
|
||||
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!
|
||||
|
||||
## 4.0.0-beta.1 (current version)
|
||||
## 4.0.0-beta.2 (current version)
|
||||
|
||||
- Extension API
|
||||
- Improved pod logs
|
||||
|
||||
@ -188,7 +188,7 @@ export function webpackLensRenderer({ showVars = true } = {}): webpack.Configura
|
||||
|
||||
isDevelopment && new webpack.HotModuleReplacementPlugin(),
|
||||
isDevelopment && new ReactRefreshWebpackPlugin(),
|
||||
|
||||
|
||||
].filter(Boolean),
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user