mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge branch 'master' into file-menu-reorder
This commit is contained in:
commit
660d4016bf
@ -36,11 +36,11 @@ jobs:
|
||||
yarn
|
||||
path: $(YARN_CACHE_FOLDER)
|
||||
displayName: Cache Yarn packages
|
||||
- script: make install-deps
|
||||
- script: make node_modules
|
||||
displayName: Install dependencies
|
||||
- script: make build-npm
|
||||
displayName: Generate npm package
|
||||
- script: make build-extensions
|
||||
- script: make -j2 build-extensions
|
||||
displayName: Build bundled extensions
|
||||
- script: make integration-win
|
||||
displayName: Run integration tests
|
||||
@ -76,11 +76,11 @@ jobs:
|
||||
yarn
|
||||
path: $(YARN_CACHE_FOLDER)
|
||||
displayName: Cache Yarn packages
|
||||
- script: make install-deps
|
||||
- script: make node_modules
|
||||
displayName: Install dependencies
|
||||
- script: make build-npm
|
||||
displayName: Generate npm package
|
||||
- script: make build-extensions
|
||||
- script: make -j2 build-extensions
|
||||
displayName: Build bundled extensions
|
||||
- script: make test
|
||||
displayName: Run tests
|
||||
@ -122,13 +122,13 @@ jobs:
|
||||
yarn
|
||||
path: $(YARN_CACHE_FOLDER)
|
||||
displayName: Cache Yarn packages
|
||||
- script: make install-deps
|
||||
- script: make node_modules
|
||||
displayName: Install dependencies
|
||||
- script: make lint
|
||||
displayName: Lint
|
||||
- script: make build-npm
|
||||
displayName: Generate npm package
|
||||
- script: make build-extensions
|
||||
- script: make -j2 build-extensions
|
||||
displayName: Build bundled extensions
|
||||
- script: make test
|
||||
displayName: Run tests
|
||||
@ -164,4 +164,4 @@ jobs:
|
||||
displayName: Publish npm package
|
||||
condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))"
|
||||
env:
|
||||
NPM_TOKEN: $(NPM_TOKEN)
|
||||
NPM_TOKEN: $(NPM_TOKEN)
|
||||
|
||||
31
.github/workflows/main.yml
vendored
31
.github/workflows/main.yml
vendored
@ -3,7 +3,9 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
jobs:
|
||||
build:
|
||||
name: Deploy docs
|
||||
@ -27,13 +29,12 @@ jobs:
|
||||
- name: Checkout Release from lens
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: lensapp/lens
|
||||
fetch-depth: 0
|
||||
|
||||
- name: git config
|
||||
run: |
|
||||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "GitHub Action"
|
||||
git pull
|
||||
|
||||
- name: Using Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
@ -45,17 +46,19 @@ jobs:
|
||||
yarn install
|
||||
yarn typedocs-extensions-api
|
||||
|
||||
- name: mkdocs deploy latest
|
||||
- name: mkdocs deploy master
|
||||
if: contains(github.ref, 'refs/heads/master')
|
||||
run: |
|
||||
mike deploy --push latest
|
||||
mike deploy --push master
|
||||
|
||||
|
||||
- name: mkdocs deploy new release / tag
|
||||
if: contains(github.ref, 'refs/tags/v')
|
||||
- name: Get the release version
|
||||
if: contains(github.ref, 'refs/tags/v') # && !github.event.release.prerelease (generate pre-release docs until Lens 4.0.0 is GA, see #1408)
|
||||
id: get_version
|
||||
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
|
||||
|
||||
- name: mkdocs deploy new release
|
||||
if: contains(github.ref, 'refs/tags/v') # && !github.event.release.prerelease (generate pre-release docs until Lens 4.0.0 is GA, see #1408)
|
||||
run: |
|
||||
mike deploy --push--update-aliases ${{ github.ref }} latest
|
||||
mike set-default --push ${{ github.ref }}
|
||||
|
||||
|
||||
|
||||
|
||||
mike deploy --push --update-aliases ${{ steps.get_version.outputs.VERSION }} latest
|
||||
mike set-default --push ${{ steps.get_version.outputs.VERSION }}
|
||||
|
||||
|
||||
38
.github/workflows/mkdocs-delete-version.yml
vendored
Normal file
38
.github/workflows/mkdocs-delete-version.yml
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
name: Delete Documentation Version
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version string to be deleted (e.g."v0.0.1")'
|
||||
required: true
|
||||
jobs:
|
||||
build:
|
||||
name: Delete docs Version
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Python 3.7
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install git+https://${{ secrets.GH_TOKEN }}@github.com/lensapp/mkdocs-material-insiders.git
|
||||
pip install mike
|
||||
|
||||
- name: Checkout Release from lens
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: git config
|
||||
run: |
|
||||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "GitHub Action"
|
||||
|
||||
|
||||
- name: mkdocs delete version
|
||||
run: |
|
||||
mike delete --push ${{ github.event.inputs.version }}
|
||||
|
||||
38
.github/workflows/mkdocs-set-default-version.yml
vendored
Normal file
38
.github/workflows/mkdocs-set-default-version.yml
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
name: Update Default Documentation Version
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version string to be default (e.g."v0.0.1")'
|
||||
required: true
|
||||
jobs:
|
||||
build:
|
||||
name: Update default docs Version
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Python 3.7
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install git+https://${{ secrets.GH_TOKEN }}@github.com/lensapp/mkdocs-material-insiders.git
|
||||
pip install mike
|
||||
|
||||
- name: Checkout Release from lens
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: git config
|
||||
run: |
|
||||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "GitHub Action"
|
||||
|
||||
|
||||
- name: mkdocs update default version
|
||||
run: |
|
||||
mike set-default --push ${{ github.event.inputs.version }}
|
||||
|
||||
54
Makefile
54
Makefile
@ -1,4 +1,7 @@
|
||||
EXTENSIONS_DIR = ./extensions
|
||||
extensions = $(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), ${dir})
|
||||
extension_node_modules = $(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), ${dir}/node_modules)
|
||||
extension_dists = $(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), ${dir}/dist)
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
DETECTED_OS := Windows
|
||||
@ -6,29 +9,23 @@ else
|
||||
DETECTED_OS := $(shell uname)
|
||||
endif
|
||||
|
||||
.PHONY: init
|
||||
init: install-deps download-bins compile-dev
|
||||
echo "Init done"
|
||||
|
||||
.PHONY: download-bins
|
||||
download-bins:
|
||||
binaries/client:
|
||||
yarn download-bins
|
||||
|
||||
.PHONY: install-deps
|
||||
install-deps:
|
||||
node_modules:
|
||||
yarn install --frozen-lockfile --verbose
|
||||
yarn check --verify-tree --integrity
|
||||
|
||||
static/build/LensDev.html:
|
||||
yarn compile:renderer
|
||||
|
||||
.PHONY: compile-dev
|
||||
compile-dev:
|
||||
yarn compile:main --cache
|
||||
yarn compile:renderer --cache
|
||||
|
||||
.PHONY: dev
|
||||
dev:
|
||||
ifeq ("$(wildcard static/build/main.js)","")
|
||||
make init
|
||||
endif
|
||||
dev: node_modules binaries/client build-extensions static/build/LensDev.html
|
||||
yarn dev
|
||||
|
||||
.PHONY: lint
|
||||
@ -36,7 +33,7 @@ lint:
|
||||
yarn lint
|
||||
|
||||
.PHONY: test
|
||||
test: download-bins
|
||||
test: binaries/client
|
||||
yarn test
|
||||
|
||||
.PHONY: integration-linux
|
||||
@ -59,20 +56,25 @@ test-app:
|
||||
yarn test
|
||||
|
||||
.PHONY: build
|
||||
build: install-deps download-bins build-extensions
|
||||
build: node_modules binaries/client build-extensions
|
||||
ifeq "$(DETECTED_OS)" "Windows"
|
||||
yarn dist:win
|
||||
else
|
||||
yarn dist
|
||||
endif
|
||||
|
||||
$(extension_node_modules):
|
||||
cd $(@:/node_modules=) && npm install --no-audit --no-fund
|
||||
|
||||
$(extension_dists): src/extensions/npm/extensions/dist
|
||||
cd $(@:/dist=) && npm run build
|
||||
|
||||
.PHONY: build-extensions
|
||||
build-extensions:
|
||||
$(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), (cd $(dir) && npm install && npm run build || exit $?);)
|
||||
build-extensions: $(extension_node_modules) $(extension_dists)
|
||||
|
||||
.PHONY: test-extensions
|
||||
test-extensions:
|
||||
$(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), (cd $(dir) && npm install --dev && npm run test || exit $?);)
|
||||
test-extensions: $(extension_node_modules)
|
||||
$(foreach dir, $(extensions), (cd $(dir) && npm run test || exit $?);)
|
||||
|
||||
.PHONY: copy-extension-themes
|
||||
copy-extension-themes:
|
||||
@ -97,6 +99,16 @@ publish-npm: build-npm
|
||||
npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
|
||||
cd src/extensions/npm/extensions && npm publish --access=public
|
||||
|
||||
.PHONY: clean-extensions
|
||||
clean-extensions:
|
||||
ifeq "$(DETECTED_OS)" "Windows"
|
||||
$(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), if exist $(dir)\dist del /s /q $(dir)\dist)
|
||||
$(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), if exist $(dir)\node_modules del /s /q $(dir)\node_modules)
|
||||
else
|
||||
$(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), rm -rf $(dir)/dist)
|
||||
$(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), rm -rf $(dir)/node_modules)
|
||||
endif
|
||||
|
||||
.PHONY: clean-npm
|
||||
clean-npm:
|
||||
ifeq "$(DETECTED_OS)" "Windows"
|
||||
@ -110,13 +122,13 @@ else
|
||||
endif
|
||||
|
||||
.PHONY: clean
|
||||
clean: clean-npm
|
||||
clean: clean-npm clean-extensions
|
||||
ifeq "$(DETECTED_OS)" "Windows"
|
||||
if exist binaries\client del /s /q binaries\client\*.*
|
||||
if exist binaries\client del /s /q binaries\client
|
||||
if exist dist del /s /q dist\*.*
|
||||
if exist static\build del /s /q static\build\*.*
|
||||
else
|
||||
rm -rf binaries/client/*
|
||||
rm -rf binaries/client
|
||||
rm -rf dist/*
|
||||
rm -rf static/build/*
|
||||
endif
|
||||
|
||||
@ -36,7 +36,6 @@ brew cask install lens
|
||||
|
||||
> Prerequisites: Nodejs v12, make, yarn
|
||||
|
||||
* `make init` - initial compilation, installing deps, etc.
|
||||
* `make dev` - builds and starts the app
|
||||
* `make test` - run tests
|
||||
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
# Extension Guides
|
||||
|
||||
The basics of the Lens Extension API are covered in [Your First Extension](../get-started/your-first-extension.md). In this section detailed code guides and samples are used to explain how to use specific Lens Extension APIs.
|
||||
|
||||
Each guide or sample will include:
|
||||
|
||||
- Clearly commented source code.
|
||||
- Instructions for running the sample extension.
|
||||
- Image of the sample extension's appearance and usage.
|
||||
- Listing of Extension API being used.
|
||||
- Explanation of Extension API concepts.
|
||||
|
||||
## Guides
|
||||
|
||||
| Guide | APIs |
|
||||
| ----- | ----- |
|
||||
| [Main process extension](main-extension.md) | LensMainExtension |
|
||||
| [Renderer process extension](renderer-extension.md) | LensRendererExtension |
|
||||
| [Stores](stores.md) | |
|
||||
| [Components](components.md) | |
|
||||
| [KubeObjectListLayout](kube-object-list-layout.md) | |
|
||||
|
||||
## Samples
|
||||
|
||||
| Sample | APIs |
|
||||
| ----- | ----- |
|
||||
[helloworld](https://github.com/lensapp/lens-extension-samples/tree/master/helloworld-sample) | LensMainExtension <br> LensRendererExtension <br> Component.Icon <br> Component.IconProps |
|
||||
[minikube](https://github.com/lensapp/lens-extension-samples/tree/master/minikube-sample) | LensMainExtension <br> Store.clusterStore <br> Store.workspaceStore |
|
||||
76
docs/extensions/guides/main-extension.md
Normal file
76
docs/extensions/guides/main-extension.md
Normal file
@ -0,0 +1,76 @@
|
||||
# Main Extension
|
||||
|
||||
The main extension api is the interface to Lens' main process (Lens runs in main and renderer processes). It allows you to access, configure, and customize Lens data, add custom application menu items, and generally run custom code in Lens' main process.
|
||||
|
||||
## `LensMainExtension` Class
|
||||
|
||||
To create a main extension simply extend the `LensMainExtension` class:
|
||||
|
||||
``` typescript
|
||||
import { LensMainExtension } from "@k8slens/extensions";
|
||||
|
||||
export default class ExampleExtensionMain extends LensMainExtension {
|
||||
onActivate() {
|
||||
console.log('custom main process extension code started');
|
||||
}
|
||||
|
||||
onDeactivate() {
|
||||
console.log('custom main process extension de-activated');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
There are two methods that you can implement to facilitate running your custom code. `onActivate()` is called when your extension has been successfully enabled. By overriding `onActivate()` you can initiate your custom code. `onDeactivate()` is called when the extension is disabled (typically from the [Lens Extensions Page]()) and when implemented gives you a chance to clean up after your extension, if necessary. The example above simply logs messages when the extension is enabled and disabled. Note that to see standard output from the main process there must be a console connected to it. This is typically achieved by starting Lens from the command prompt.
|
||||
|
||||
The following example is a little more interesting in that it accesses some Lens state data and periodically logs the name of the currently active cluster in Lens.
|
||||
|
||||
``` typescript
|
||||
import { LensMainExtension, Store } from "@k8slens/extensions";
|
||||
|
||||
const clusterStore = Store.clusterStore
|
||||
|
||||
export default class ActiveClusterExtensionMain extends LensMainExtension {
|
||||
|
||||
timer: NodeJS.Timeout
|
||||
|
||||
onActivate() {
|
||||
console.log("Cluster logger activated");
|
||||
this.timer = setInterval(() => {
|
||||
if (!clusterStore.active) {
|
||||
console.log("No active cluster");
|
||||
return;
|
||||
}
|
||||
console.log("active cluster is", clusterStore.active.contextName)
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
onDeactivate() {
|
||||
clearInterval(this.timer)
|
||||
console.log("Cluster logger deactivated");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
See the [Stores](../stores) guide for more details on accessing Lens state data.
|
||||
|
||||
### `appMenus`
|
||||
|
||||
The only UI feature customizable in the main extension api is the application menu. Custom menu items can be inserted and linked to custom functionality, such as navigating to a specific page. The following example demonstrates adding a menu item to the Help menu.
|
||||
|
||||
``` typescript
|
||||
import { LensMainExtension } from "@k8slens/extensions";
|
||||
|
||||
export default class SamplePageMainExtension extends LensMainExtension {
|
||||
appMenus = [
|
||||
{
|
||||
parentId: "help",
|
||||
label: "Sample",
|
||||
click() {
|
||||
console.log("Sample clicked");
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`appMenus` is an array of objects satisfying the `MenuRegistration` interface. `MenuRegistration` extends React's `MenuItemConstructorOptions` interface. `parentId` is the id of the menu to put this menu item under (todo: is this case sensitive and how do we know what the available ids are?), `label` is the text to show on the menu item, and `click()` is called when the menu item is selected. In this example we simply log a message, but typically you would navigate to a specific page or perform some operation. Pages are associated with the [`LensRendererExtension`](renderer-extension.md) class and can be defined when you extend it.
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -30,6 +30,7 @@ nav:
|
||||
- Color Reference: extensions/capabilities/color-reference.md
|
||||
- Extension Guides:
|
||||
- Overview: extensions/guides/README.md
|
||||
- Main Extension: extensions/guides/main-extension.md
|
||||
- Renderer Extension: extensions/guides/renderer-extension.md
|
||||
- Testing and Publishing:
|
||||
- Testing Extensions: extensions/testing-and-publishing/testing.md
|
||||
|
||||
@ -38,15 +38,18 @@ describe("workspace store tests", () => {
|
||||
expect(() => ws.removeWorkspaceById(WorkspaceStore.defaultId)).toThrowError("Cannot remove");
|
||||
})
|
||||
|
||||
it("can update default workspace name", () => {
|
||||
it("can update workspace description", () => {
|
||||
const ws = WorkspaceStore.getInstance<WorkspaceStore>();
|
||||
|
||||
ws.addWorkspace(new Workspace({
|
||||
id: WorkspaceStore.defaultId,
|
||||
const workspace = ws.addWorkspace(new Workspace({
|
||||
id: "foobar",
|
||||
name: "foobar",
|
||||
}));
|
||||
|
||||
expect(ws.currentWorkspace.name).toBe("foobar");
|
||||
workspace.description = "Foobar description";
|
||||
ws.updateWorkspace(workspace);
|
||||
|
||||
expect(ws.getById("foobar").description).toBe("Foobar description");
|
||||
})
|
||||
|
||||
it("can add workspaces", () => {
|
||||
|
||||
@ -153,20 +153,20 @@ export class WorkspaceStore extends BaseStore<WorkspaceStoreModel> {
|
||||
@action
|
||||
addWorkspace(workspace: Workspace) {
|
||||
const { id, name } = workspace;
|
||||
const existingWorkspace = this.getById(id);
|
||||
if (!name.trim() || this.getByName(name.trim())) {
|
||||
return;
|
||||
}
|
||||
if (existingWorkspace) {
|
||||
Object.assign(existingWorkspace, workspace);
|
||||
appEventBus.emit({name: "workspace", action: "update"})
|
||||
} else {
|
||||
appEventBus.emit({name: "workspace", action: "add"})
|
||||
}
|
||||
this.workspaces.set(id, workspace);
|
||||
appEventBus.emit({name: "workspace", action: "add"})
|
||||
return workspace;
|
||||
}
|
||||
|
||||
@action
|
||||
updateWorkspace(workspace: Workspace) {
|
||||
this.workspaces.set(workspace.id, workspace);
|
||||
appEventBus.emit({name: "workspace", action: "update"});
|
||||
}
|
||||
|
||||
@action
|
||||
removeWorkspace(workspace: Workspace) {
|
||||
this.removeWorkspaceById(workspace.id)
|
||||
|
||||
@ -86,6 +86,10 @@ export class Cluster implements ClusterModel, ClusterState {
|
||||
return this.accessible && !this.disconnected;
|
||||
}
|
||||
|
||||
@computed get name() {
|
||||
return this.preferences.clusterName || this.contextName
|
||||
}
|
||||
|
||||
get version(): string {
|
||||
return String(this.metadata?.version) || ""
|
||||
}
|
||||
|
||||
@ -9,8 +9,8 @@ export function exitApp() {
|
||||
const windowManager = WindowManager.getInstance<WindowManager>()
|
||||
const clusterManager = ClusterManager.getInstance<ClusterManager>()
|
||||
appEventBus.emit({ name: "service", action: "close" })
|
||||
windowManager?.hide();
|
||||
clusterManager?.stop();
|
||||
windowManager.hide();
|
||||
clusterManager.stop();
|
||||
logger.info('SERVICE:QUIT');
|
||||
setTimeout(() => {
|
||||
app.exit()
|
||||
|
||||
@ -89,7 +89,7 @@ export function createTrayMenu(windowManager: WindowManager): Menu {
|
||||
label: workspace.name,
|
||||
toolTip: workspace.description,
|
||||
submenu: clusters.map(cluster => {
|
||||
const { id: clusterId, preferences: { clusterName: label }, online, workspace } = cluster;
|
||||
const { id: clusterId, name: label, online, workspace } = cluster;
|
||||
return {
|
||||
label: `${online ? '✓' : '\x20'.repeat(3)/*offset*/}${label}`,
|
||||
toolTip: clusterId,
|
||||
|
||||
@ -20,7 +20,7 @@ export class Workspaces extends React.Component {
|
||||
|
||||
@computed get workspaces(): Workspace[] {
|
||||
const currentWorkspaces: Map<WorkspaceId, Workspace> = new Map()
|
||||
workspaceStore.enabledWorkspacesList.forEach((w) => {
|
||||
workspaceStore.workspacesList.forEach((w) => {
|
||||
currentWorkspaces.set(w.id, w)
|
||||
})
|
||||
const allWorkspaces = new Map([
|
||||
@ -45,9 +45,13 @@ export class Workspaces extends React.Component {
|
||||
}
|
||||
|
||||
saveWorkspace = (id: WorkspaceId) => {
|
||||
const draft = toJS(this.editingWorkspaces.get(id));
|
||||
const workspace = workspaceStore.addWorkspace(draft);
|
||||
if (workspace) {
|
||||
const workspace = new Workspace(this.editingWorkspaces.get(id));
|
||||
if (workspaceStore.getById(id)) {
|
||||
workspaceStore.updateWorkspace(workspace);
|
||||
this.clearEditing(id);
|
||||
return;
|
||||
}
|
||||
if (workspaceStore.addWorkspace(workspace)) {
|
||||
this.clearEditing(id);
|
||||
}
|
||||
}
|
||||
@ -127,7 +131,7 @@ export class Workspaces extends React.Component {
|
||||
validate: value => !workspaceStore.getByName(value.trim())
|
||||
}
|
||||
return (
|
||||
<div key={workspaceId} className={className}>
|
||||
<div key={workspaceId} className={cssNames(className)}>
|
||||
{!isEditing && (
|
||||
<Fragment>
|
||||
<span className="name flex gaps align-center">
|
||||
|
||||
@ -34,8 +34,8 @@ export class ClusterIcon extends React.Component<Props> {
|
||||
cluster, showErrors, showTooltip, errorClass, options, interactive, isActive,
|
||||
children, ...elemProps
|
||||
} = this.props;
|
||||
const { isAdmin, eventCount, preferences, id: clusterId } = cluster;
|
||||
const { clusterName, icon } = preferences;
|
||||
const { isAdmin, name, eventCount, preferences, id: clusterId } = cluster;
|
||||
const { icon } = preferences;
|
||||
const clusterIconId = `cluster-icon-${clusterId}`;
|
||||
const className = cssNames("ClusterIcon flex inline", this.props.className, {
|
||||
interactive: interactive !== undefined ? interactive : !!this.props.onClick,
|
||||
@ -44,9 +44,9 @@ export class ClusterIcon extends React.Component<Props> {
|
||||
return (
|
||||
<div {...elemProps} className={className} id={showTooltip ? clusterIconId : null}>
|
||||
{showTooltip && (
|
||||
<Tooltip targetId={clusterIconId}>{clusterName}</Tooltip>
|
||||
<Tooltip targetId={clusterIconId}>{name}</Tooltip>
|
||||
)}
|
||||
{icon && <img src={icon} alt={clusterName}/>}
|
||||
{icon && <img src={icon} alt={name}/>}
|
||||
{!icon && <Hashicon value={clusterId} options={options}/>}
|
||||
{showErrors && isAdmin && eventCount > 0 && (
|
||||
<Badge
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
.PodLogs {
|
||||
--overlay-bg: #8cc474b8;
|
||||
--overlay-active-bg: orange;
|
||||
|
||||
.logs {
|
||||
@include custom-scrollbar;
|
||||
|
||||
@ -11,14 +14,6 @@
|
||||
background: $logsBackground;
|
||||
flex-grow: 1;
|
||||
|
||||
.find-overlay {
|
||||
position: absolute;
|
||||
border-radius: 2px;
|
||||
background-color: #8cc474;
|
||||
margin-top: 4px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.VirtualList {
|
||||
height: 100%;
|
||||
|
||||
@ -29,19 +24,30 @@
|
||||
font-family: $font-monospace;
|
||||
font-size: smaller;
|
||||
white-space: pre;
|
||||
-webkit-font-smoothing: auto; // Better readability on non-retina screens
|
||||
|
||||
&:hover {
|
||||
background: $logRowHoverBackground;
|
||||
}
|
||||
|
||||
span {
|
||||
-webkit-font-smoothing: auto; // Better readability on non-retina screens
|
||||
}
|
||||
|
||||
span.overlay {
|
||||
border-radius: 2px;
|
||||
background-color: #8cc474b8;
|
||||
-webkit-font-smoothing: auto;
|
||||
background-color: var(--overlay-bg);
|
||||
|
||||
span {
|
||||
background-color: var(--overlay-bg)!important; // Rewriting inline styles from AnsiUp library
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: orange;
|
||||
background-color: var(--overlay-active-bg);
|
||||
|
||||
span {
|
||||
background-color: var(--overlay-active-bg)!important; // Rewriting inline styles from AnsiUp library
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -49,25 +55,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.new-logs-sep {
|
||||
position: relative;
|
||||
display: block;
|
||||
height: 0;
|
||||
border-top: 1px solid $primary;
|
||||
margin: $margin * 2 0;
|
||||
|
||||
&:after {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
content: 'new';
|
||||
background: $primary;
|
||||
color: white;
|
||||
padding: $padding / 3;
|
||||
border-radius: $radius;
|
||||
}
|
||||
}
|
||||
|
||||
.jump-to-bottom {
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import "./pod-logs.scss";
|
||||
import React from "react";
|
||||
import AnsiUp from 'ansi_up';
|
||||
import DOMPurify from "dompurify"
|
||||
import { Trans } from "@lingui/macro";
|
||||
import { action, computed, observable, reaction } from "mobx";
|
||||
import { disposeOnUnmount, observer } from "mobx-react";
|
||||
@ -33,6 +35,7 @@ export class PodLogs extends React.Component<Props> {
|
||||
private logsElement = React.createRef<HTMLDivElement>(); // A reference for outer container in VirtualList
|
||||
private virtualListRef = React.createRef<VirtualList>(); // A reference for VirtualList component
|
||||
private lastLineIsShown = true; // used for proper auto-scroll content after refresh
|
||||
private colorConverter = new AnsiUp();
|
||||
|
||||
componentDidMount() {
|
||||
disposeOnUnmount(this, [
|
||||
@ -185,6 +188,7 @@ export class PodLogs extends React.Component<Props> {
|
||||
const { searchQuery, isActiveOverlay } = searchStore;
|
||||
const item = this.logs[rowIndex];
|
||||
const contents: React.ReactElement[] = [];
|
||||
const ansiToHtml = (ansi: string) => DOMPurify.sanitize(this.colorConverter.ansi_to_html(ansi));
|
||||
if (searchQuery) { // If search is enabled, replace keyword with backgrounded <span>
|
||||
// Case-insensitive search (lowercasing query and keywords in line)
|
||||
const regex = new RegExp(searchStore.escapeRegex(searchQuery), "gi");
|
||||
@ -195,19 +199,26 @@ export class PodLogs extends React.Component<Props> {
|
||||
pieces.forEach((piece, index) => {
|
||||
const active = isActiveOverlay(rowIndex, index);
|
||||
const lastItem = index === pieces.length - 1;
|
||||
const overlayValue = matches.next().value;
|
||||
const overlay = !lastItem ?
|
||||
<span className={cssNames({ active })}>{matches.next().value}</span> :
|
||||
<span
|
||||
className={cssNames("overlay", { active })}
|
||||
dangerouslySetInnerHTML={{ __html: ansiToHtml(overlayValue) }}
|
||||
/> :
|
||||
null
|
||||
contents.push(
|
||||
<React.Fragment key={piece + index}>
|
||||
{piece}{overlay}
|
||||
<span dangerouslySetInnerHTML={{ __html: ansiToHtml(piece) }} />
|
||||
{overlay}
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
}
|
||||
return (
|
||||
<div className={cssNames("LogRow")}>
|
||||
{contents.length > 1 ? contents : item}
|
||||
{contents.length > 1 ? contents : (
|
||||
<span dangerouslySetInnerHTML={{ __html: ansiToHtml(item) }} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -65,7 +65,7 @@ export class MainLayout extends React.Component<MainLayoutProps> {
|
||||
return (
|
||||
<div className={cssNames("MainLayout", className)} style={this.getSidebarSize() as any}>
|
||||
<header className={cssNames("flex gaps align-center", headerClass)}>
|
||||
<span className="cluster">{cluster.preferences.clusterName || cluster.contextName}</span>
|
||||
<span className="cluster">{cluster.name}</span>
|
||||
</header>
|
||||
|
||||
<aside className={cssNames("flex column", { pinned: this.isPinned, accessible: this.isAccessible })}>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user