1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Fixed merge conflict in mkdocs.yml and fixed typo in contributing/promotion.md

Signed-off-by: Paul Williams <pawilliams@mirantis.com>
This commit is contained in:
Paul Williams 2020-11-10 12:27:24 -08:00
commit c273c0b57c
38 changed files with 282 additions and 182 deletions

View File

@ -8,6 +8,9 @@ jobs:
build: build:
name: Deploy docs name: Deploy docs
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps: steps:
- name: Set up Python 3.7 - name: Set up Python 3.7
uses: actions/setup-python@v2 uses: actions/setup-python@v2
@ -32,6 +35,17 @@ jobs:
git config --local user.name "GitHub Action" git config --local user.name "GitHub Action"
git pull git pull
- name: Using Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Generate Extensions API Reference using typedocs
run: |
yarn install
yarn typedocs-extensions-api
for filename in docs/extensions/api/**/*.md; do [ -e "$filename" ] || continue; sed -i '1s/^/---\ntitle: API Reference\n---\n/' $filename; done
- name: mkdocs deploy latest - name: mkdocs deploy latest
run: | run: |

1
.gitignore vendored
View File

@ -15,3 +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

View File

@ -1,4 +1,4 @@
# Promotiion # Promotion
Help promote Lens! If you are not a developer (or even if you are), you can still contribute to the project, a lot, by helping us promote it. As we are a free open source project, the community is our most important asset, so here are some ways that you can help the project continue to grow. Help promote Lens! If you are not a developer (or even if you are), you can still contribute to the project, a lot, by helping us promote it. As we are a free open source project, the community is our most important asset, so here are some ways that you can help the project continue to grow.

View File

@ -1,3 +0,0 @@
# Lens Extension API Reference
TBD

View File

@ -245,7 +245,7 @@ import { LensRendererExtension } from "@k8slens/extensions";
import { CustomKindDetails, CustomKindDetailsProps } from "./src/custom-kind-details" import { CustomKindDetails, CustomKindDetailsProps } from "./src/custom-kind-details"
export default class ExampleExtension extends LensRendererExtension { export default class ExampleExtension extends LensRendererExtension {
kubeObjectMenuItems = [ kubeObjectDetailItems = [
{ {
kind: "CustomKind", kind: "CustomKind",
apiVersions: ["custom.acme.org/v1"], apiVersions: ["custom.acme.org/v1"],

View File

@ -35,7 +35,7 @@ nav:
- Testing extensions: extensions/testing-and-publishing/testing.md - Testing extensions: extensions/testing-and-publishing/testing.md
- Publishing extensions: extensions/testing-and-publishing/publishing.md - Publishing extensions: extensions/testing-and-publishing/publishing.md
- Bundling extensions: extensions/testing-and-publishing/bundling.md - Bundling extensions: extensions/testing-and-publishing/bundling.md
- API reference: extensions/api/README.md - API reference: extensions/api/modules/_src_extensions_extension_api_.md
- Contributing: - Contributing:
- Overview: contributing/README.md - Overview: contributing/README.md
- Development: contributing/development.md - Development: contributing/development.md

View File

@ -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-alpha.4", "version": "4.0.0-alpha.5",
"main": "static/build/main.js", "main": "static/build/main.js",
"copyright": "© 2020, Mirantis, Inc.", "copyright": "© 2020, Mirantis, Inc.",
"license": "MIT", "license": "MIT",
@ -38,7 +38,8 @@
"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": "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": "yarn typedocs-extensions-api && 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": "npx typedoc --plugin typedoc-plugin-markdown --readme none --name @k8slens/extensions --ignoreCompilerErrors --out docs/extensions/api --mode modules --excludePrivate --includes src/ src/extensions/extension-api.ts"
}, },
"config": { "config": {
"bundledKubectlVersion": "1.17.11", "bundledKubectlVersion": "1.17.11",
@ -280,7 +281,6 @@
"@types/electron-window-state": "^2.0.34", "@types/electron-window-state": "^2.0.34",
"@types/fs-extra": "^9.0.1", "@types/fs-extra": "^9.0.1",
"@types/hapi": "^18.0.3", "@types/hapi": "^18.0.3",
"@types/hard-source-webpack-plugin": "^1.0.1",
"@types/hoist-non-react-statics": "^3.3.1", "@types/hoist-non-react-statics": "^3.3.1",
"@types/html-webpack-plugin": "^3.2.3", "@types/html-webpack-plugin": "^3.2.3",
"@types/http-proxy": "^1.17.4", "@types/http-proxy": "^1.17.4",
@ -342,7 +342,6 @@
"file-loader": "^6.0.0", "file-loader": "^6.0.0",
"flex.box": "^3.4.4", "flex.box": "^3.4.4",
"fork-ts-checker-webpack-plugin": "^5.0.0", "fork-ts-checker-webpack-plugin": "^5.0.0",
"hard-source-webpack-plugin": "^0.13.1",
"hoist-non-react-statics": "^3.3.2", "hoist-non-react-statics": "^3.3.2",
"html-webpack-plugin": "^4.3.0", "html-webpack-plugin": "^4.3.0",
"identity-obj-proxy": "^3.0.0", "identity-obj-proxy": "^3.0.0",
@ -382,6 +381,8 @@
"ts-loader": "^7.0.5", "ts-loader": "^7.0.5",
"ts-node": "^8.10.2", "ts-node": "^8.10.2",
"type-fest": "^0.18.0", "type-fest": "^0.18.0",
"typedoc": "^0.19.2",
"typedoc-plugin-markdown": "^3.0.11",
"typeface-roboto": "^0.0.75", "typeface-roboto": "^0.0.75",
"typescript": "^4.0.2", "typescript": "^4.0.2",
"url-loader": "^4.1.0", "url-loader": "^4.1.0",
@ -389,6 +390,7 @@
"webpack-cli": "^3.3.11", "webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.11.0", "webpack-dev-server": "^3.11.0",
"webpack-node-externals": "^1.7.2", "webpack-node-externals": "^1.7.2",
"what-input": "^5.2.10",
"xterm": "^4.6.0", "xterm": "^4.6.0",
"xterm-addon-fit": "^0.4.0" "xterm-addon-fit": "^0.4.0"
} }

View File

@ -57,11 +57,12 @@ describe("user store tests", () => {
expect(us.preferences.colorTheme).toBe('light') expect(us.preferences.colorTheme).toBe('light')
}) })
it("correctly resets theme to default value", () => { it("correctly resets theme to default value", async () => {
const us = UserStore.getInstance<UserStore>(); const us = UserStore.getInstance<UserStore>();
us.isLoaded = true;
us.preferences.colorTheme = "some other theme"; us.preferences.colorTheme = "some other theme";
us.resetTheme(); await us.resetTheme();
expect(us.preferences.colorTheme).toBe(UserStore.defaultTheme); expect(us.preferences.colorTheme).toBe(UserStore.defaultTheme);
}) })

View File

@ -88,7 +88,8 @@ export class UserStore extends BaseStore<UserStoreModel> {
} }
@action @action
resetTheme() { async resetTheme() {
await this.whenLoaded;
this.preferences.colorTheme = UserStore.defaultTheme; this.preferences.colorTheme = UserStore.defaultTheme;
} }

View File

@ -1,7 +1,8 @@
// Save file to electron app directory (e.g. "/Users/$USER/Library/Application Support/Lens" for MacOS) // Save file to electron app directory (e.g. "/Users/$USER/Library/Application Support/Lens" for MacOS)
import path from "path"; import path from "path";
import { app, remote } from "electron"; import { app, remote } from "electron";
import { ensureDirSync, writeFileSync, WriteFileOptions } from "fs-extra"; import { ensureDirSync, writeFileSync } from "fs-extra";
import { WriteFileOptions } from "fs"
export function saveToAppFiles(filePath: string, contents: any, options?: WriteFileOptions): string { export function saveToAppFiles(filePath: string, contents: any, options?: WriteFileOptions): string {
const absPath = path.resolve((app || remote.app).getPath("userData"), filePath); const absPath = path.resolve((app || remote.app).getPath("userData"), filePath);

View File

@ -9,7 +9,6 @@ import * as App from "./app"
import * as EventBus from "./event-bus" import * as EventBus from "./event-bus"
import * as Store from "./stores" import * as Store from "./stores"
import * as Util from "./utils" import * as Util from "./utils"
import * as Registry from "../registries"
import * as ClusterFeature from "./cluster-feature" import * as ClusterFeature from "./cluster-feature"
// TODO: allow to expose windowManager.navigate() as Navigation.navigate() in runtime // TODO: allow to expose windowManager.navigate() as Navigation.navigate() in runtime
@ -21,5 +20,4 @@ export {
ClusterFeature, ClusterFeature,
Store, Store,
Util, Util,
Registry,
} }

View File

@ -29,3 +29,4 @@ export { Role, roleApi } from "../../renderer/api/endpoints";
export { RoleBinding, roleBindingApi } from "../../renderer/api/endpoints"; export { RoleBinding, roleBindingApi } from "../../renderer/api/endpoints";
export { ClusterRole, clusterRoleApi } from "../../renderer/api/endpoints"; export { ClusterRole, clusterRoleApi } from "../../renderer/api/endpoints";
export { ClusterRoleBinding, clusterRoleBindingApi } from "../../renderer/api/endpoints"; export { ClusterRoleBinding, clusterRoleBindingApi } from "../../renderer/api/endpoints";
export { CustomResourceDefinition, crdApi } from "../../renderer/api/endpoints";

View File

@ -14,7 +14,7 @@ export class ApiManager {
return this.apis.get(pathOrCallback) || this.apis.get(KubeApi.parseApi(pathOrCallback).apiBase); return this.apis.get(pathOrCallback) || this.apis.get(KubeApi.parseApi(pathOrCallback).apiBase);
} }
return Array.from(this.apis.values()).find(pathOrCallback); return Array.from(this.apis.values()).find(pathOrCallback ?? ((api: KubeApi) => true));
} }
registerApi(apiBase: string, api: KubeApi) { registerApi(apiBase: string, api: KubeApi) {

View File

@ -9,7 +9,7 @@ type AdditionalPrinterColumnsCommon = {
description: string; description: string;
} }
type AdditionalPrinterColumnsV1 = AdditionalPrinterColumnsCommon & { export type AdditionalPrinterColumnsV1 = AdditionalPrinterColumnsCommon & {
jsonPath: string; jsonPath: string;
} }
@ -120,9 +120,9 @@ export class CustomResourceDefinition extends KubeObject {
return JSON.stringify(this.spec.conversion); return JSON.stringify(this.spec.conversion);
} }
getPrinterColumns(ignorePriority = true) { getPrinterColumns(ignorePriority = true): AdditionalPrinterColumnsV1[] {
const columns = this.spec.versions.find(a => this.getVersion() == a.name)?.additionalPrinterColumns const columns = this.spec.versions.find(a => this.getVersion() == a.name)?.additionalPrinterColumns
?? this.spec.additionalPrinterColumns?.map(({JSONPath, ...rest}) => ({ ...rest, jsonPath: JSONPath })) // map to V1 shape ?? this.spec.additionalPrinterColumns?.map(({ JSONPath, ...rest }) => ({ ...rest, jsonPath: JSONPath })) // map to V1 shape
?? []; ?? [];
return columns return columns
.filter(column => column.name != "Age") .filter(column => column.name != "Age")
@ -149,4 +149,3 @@ export class CustomResourceDefinition extends KubeObject {
export const crdApi = new VersionedKubeApi<CustomResourceDefinition>({ export const crdApi = new VersionedKubeApi<CustomResourceDefinition>({
objectConstructor: CustomResourceDefinition objectConstructor: CustomResourceDefinition
}); });

View File

@ -5,6 +5,7 @@ export * from "./cluster.api"
export * from "./cluster-role.api" export * from "./cluster-role.api"
export * from "./cluster-role-binding.api" export * from "./cluster-role-binding.api"
export * from "./configmap.api" export * from "./configmap.api"
export * from "./crd.api"
export * from "./cron-job.api" export * from "./cron-job.api"
export * from "./daemon-set.api" export * from "./daemon-set.api"
export * from "./deployment.api" export * from "./deployment.api"

View File

@ -12,67 +12,83 @@ import { KubeObjectDetailsProps } from "../kube-object";
import { crdStore } from "./crd.store"; import { crdStore } from "./crd.store";
import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { Input } from "../input"; import { Input } from "../input";
import { CustomResourceDefinition } from "../../api/endpoints/crd.api"; import { AdditionalPrinterColumnsV1, CustomResourceDefinition } from "../../api/endpoints/crd.api";
interface Props extends KubeObjectDetailsProps<CustomResourceDefinition> { interface Props extends KubeObjectDetailsProps<CustomResourceDefinition> {
} }
function CrdColumnValue({ value }: { value: any[] | {} | string }) { function convertSpecValue(value: any): any {
if (Array.isArray(value)) { if (Array.isArray(value)) {
return <>{value.map((item, index) => <CrdColumnValue key={index} value={item} />)}</> return value.map(convertSpecValue)
} }
if (typeof(value) === 'object') return (
<Input if (typeof value === "object") {
readOnly return (
multiLine <Input
theme="round-black" readOnly
className="box grow" multiLine
value={JSON.stringify(value, null, 2)} theme="round-black"
/> className="box grow"
); value={JSON.stringify(value, null, 2)}
return <span>{value}</span>; />
)
}
return value
} }
@observer @observer
export class CrdResourceDetails extends React.Component<Props> { export class CrdResourceDetails extends React.Component<Props> {
@computed get crd() { @computed get crd() {
return crdStore.getByObject(this.props.object); return crdStore.getByObject(this.props.object);
} }
renderAdditionalColumns(crd: CustomResourceDefinition, columns: AdditionalPrinterColumnsV1[]) {
return columns.map(({ name, jsonPath: jp }) => (
<DrawerItem key={name} name={name} renderBoolean>
{convertSpecValue(jsonPath.value(crd, jp.slice(1)))}
</DrawerItem>
))
}
renderStatus(crd: CustomResourceDefinition, columns: AdditionalPrinterColumnsV1[]) {
const showStatus = !columns.find(column => column.name == "Status") && crd.status?.conditions;
if (!showStatus) {
return null
}
const conditions = crd.status.conditions
.filter(({ type, reason }) => type || reason)
.map(({ type, reason, message, status }) => ({ kind: type || reason, message, status }))
.map(({ kind, message, status }, index) => (
<Badge
key={kind + index} label={kind}
className={cssNames({ disabled: status === "False" }, kind.toLowerCase())}
tooltip={message}
/>
))
return (
<DrawerItem name={<Trans>Status</Trans>} className="status" labelsOnly>
{conditions}
</DrawerItem>
)
}
render() { render() {
const { object } = this.props; const { props: { object }, crd } = this;
const { crd } = this; if (!object || !crd) {
if (!object || !crd) return null; return null;
}
const className = cssNames("CrdResourceDetails", crd.getResourceKind()); const className = cssNames("CrdResourceDetails", crd.getResourceKind());
const extraColumns = crd.getPrinterColumns(); const extraColumns = crd.getPrinterColumns();
const showStatus = !extraColumns.find(column => column.name == "Status") && object.status?.conditions;
return ( return (
<div className={className}> <div className={className}>
<KubeObjectMeta object={object}/> <KubeObjectMeta object={object} />
{extraColumns.map(column => { {this.renderAdditionalColumns(object, extraColumns)}
const { name } = column; {this.renderStatus(object, extraColumns)}
const value = jsonPath.query(object, (column.jsonPath).slice(1));
return (
<DrawerItem key={name} name={name}>
<CrdColumnValue value={value} />
</DrawerItem>
)
})}
{showStatus && (
<DrawerItem name={<Trans>Status</Trans>} className="status" labelsOnly>
{object.status.conditions.map((condition, index) => {
const { type, reason, message, status } = condition;
const kind = type || reason;
if (!kind) return null;
return (
<Badge
key={kind + index} label={kind}
className={cssNames({ disabled: status === "False" }, kind.toLowerCase())}
tooltip={message}
/>
);
})}
</DrawerItem>
)}
</div> </div>
) )
} }

View File

@ -56,11 +56,11 @@ export class CrdResources extends React.Component<Props> {
[sortBy.age]: (item: KubeObject) => item.metadata.creationTimestamp, [sortBy.age]: (item: KubeObject) => item.metadata.creationTimestamp,
} }
extraColumns.forEach(column => { extraColumns.forEach(column => {
sortingCallbacks[column.name] = (item: KubeObject) => jsonPath.query(item, column.jsonPath.slice(1)) sortingCallbacks[column.name] = (item: KubeObject) => jsonPath.value(item, column.jsonPath.slice(1))
}) })
const ListView = KubeObjectListLayout;
return ( return (
<ListView <KubeObjectListLayout
className="CrdResources" className="CrdResources"
isClusterScoped={!isNamespaced} isClusterScoped={!isNamespaced}
store={store} store={store}
@ -85,9 +85,10 @@ export class CrdResources extends React.Component<Props> {
renderTableContents={(crdInstance: KubeObject) => [ renderTableContents={(crdInstance: KubeObject) => [
crdInstance.getName(), crdInstance.getName(),
isNamespaced && crdInstance.getNs(), isNamespaced && crdInstance.getNs(),
...extraColumns.map(column => { ...extraColumns.map(column => ({
return jsonPath.query(crdInstance, (column.jsonPath).slice(1)) renderBoolean: true,
}), children: jsonPath.value(crdInstance, column.jsonPath.slice(1)),
})),
crdInstance.getAge(), crdInstance.getAge(),
]} ]}
/> />

View File

@ -23,7 +23,6 @@
--font-weight-thin: 300; --font-weight-thin: 300;
--font-weight-normal: 400; --font-weight-normal: 400;
--font-weight-bold: 500; --font-weight-bold: 500;
--mainBackground: #1e2124;
--main-layout-header: 40px; --main-layout-header: 40px;
--drag-region-height: 22px --drag-region-height: 22px
} }

View File

@ -38,7 +38,8 @@ import { webFrame } from "electron";
import { clusterPageRegistry } from "../../extensions/registries/page-registry"; import { clusterPageRegistry } from "../../extensions/registries/page-registry";
import { DynamicPage } from "../../extensions/dynamic-page"; import { DynamicPage } from "../../extensions/dynamic-page";
import { extensionLoader } from "../../extensions/extension-loader"; import { extensionLoader } from "../../extensions/extension-loader";
import { appEventBus } from "../../common/event-bus" import { appEventBus } from "../../common/event-bus";
import whatInput from 'what-input';
@observer @observer
export class App extends React.Component { export class App extends React.Component {
@ -57,6 +58,7 @@ export class App extends React.Component {
window.addEventListener("online", () => { window.addEventListener("online", () => {
window.location.reload() window.location.reload()
}) })
whatInput.ask() // Start to monitor user input device
} }
get startURL() { get startURL() {

View File

@ -1,6 +1,6 @@
import "./drawer-item.scss"; import "./drawer-item.scss";
import React from "react"; import React from "react";
import { cssNames } from "../../utils"; import { cssNames, displayBooleans } from "../../utils";
export interface DrawerItemProps extends React.HTMLAttributes<any> { export interface DrawerItemProps extends React.HTMLAttributes<any> {
name: React.ReactNode; name: React.ReactNode;
@ -8,18 +8,21 @@ export interface DrawerItemProps extends React.HTMLAttributes<any> {
title?: string; title?: string;
labelsOnly?: boolean; labelsOnly?: boolean;
hidden?: boolean; hidden?: boolean;
renderBoolean?: boolean; // show "true" or "false" for all of the children elements are "typeof boolean"
} }
export class DrawerItem extends React.Component<DrawerItemProps> { export class DrawerItem extends React.Component<DrawerItemProps> {
render() { render() {
const { name, title, labelsOnly, children, hidden, ...elemProps } = this.props const { name, title, labelsOnly, children, hidden, className, renderBoolean, ...elemProps } = this.props
let { className } = this.props;
if (hidden) return null if (hidden) return null
className = cssNames("DrawerItem", className, { labelsOnly });
const classNames = cssNames("DrawerItem", className, { labelsOnly });
const content = displayBooleans(renderBoolean, children)
return ( return (
<div {...elemProps} className={className} title={title}> <div {...elemProps} className={classNames} title={title}>
<span className="name">{name}</span> <span className="name">{name}</span>
<span className="value">{children}</span> <span className="value">{content}</span>
</div> </div>
) )
} }

View File

@ -3,6 +3,7 @@
--size: 50%; --size: 50%;
--full-size: 75%; --full-size: 75%;
--spacing: #{$padding * 3}; --spacing: #{$padding * 3};
--icon-focus-color: white;
position: absolute; position: absolute;
background: $contentColor; background: $contentColor;

View File

@ -5,6 +5,7 @@
--big-size: 32px; --big-size: 32px;
--color-active: #{$iconActiveColor}; --color-active: #{$iconActiveColor};
--bgc-active: #{$iconActiveBackground}; --bgc-active: #{$iconActiveBackground};
--focus-color: var(--icon-focus-color, #{$lensBlue});
display: inline-flex; display: inline-flex;
flex-shrink: 0; flex-shrink: 0;
@ -106,7 +107,7 @@
&.active { &.active {
color: var(--color-active); color: var(--color-active);
box-shadow: 0 0 0 3px $iconActiveBackground; box-shadow: 0 0 0 2px $iconActiveBackground;
background-color: $iconActiveBackground; background-color: $iconActiveBackground;
} }
@ -115,8 +116,16 @@
transition: 250ms color, 250ms opacity, 150ms background-color, 150ms box-shadow; transition: 250ms color, 250ms opacity, 150ms background-color, 150ms box-shadow;
border-radius: 50%; border-radius: 50%;
&.focusable:focus { &.focusable:focus:not(:hover) {
@extend .active; box-shadow: 0 0 0 2px var(--focus-color);
[data-whatintent='mouse'] & {
box-shadow: none;
&.active {
box-shadow: 0 0 0 2px $iconActiveBackground;
}
}
} }
&:hover { &:hover {

View File

@ -235,7 +235,7 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
cellProps.className = cssNames(cellProps.className, headCell.className); cellProps.className = cssNames(cellProps.className, headCell.className);
} }
} }
return <TableCell key={index} {...cellProps}/> return <TableCell key={index} {...cellProps} />
}) })
} }
{renderItemMenu && ( {renderItemMenu && (
@ -277,7 +277,7 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
if (!isReady || !filters.length || hideFilters || !userSettings.showAppliedFilters) { if (!isReady || !filters.length || hideFilters || !userSettings.showAppliedFilters) {
return; return;
} }
return <PageFiltersList filters={filters}/> return <PageFiltersList filters={filters} />
} }
renderNoItems() { renderNoItems() {
@ -297,7 +297,7 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
</NoItems> </NoItems>
) )
} }
return <NoItems/> return <NoItems />
} }
renderHeaderContent(placeholders: IHeaderPlaceholders): ReactNode { renderHeaderContent(placeholders: IHeaderPlaceholders): ReactNode {
@ -344,12 +344,12 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
title: <h5 className="title">{title}</h5>, title: <h5 className="title">{title}</h5>,
info: this.renderInfo(), info: this.renderInfo(),
filters: <> filters: <>
{!isClusterScoped && <NamespaceSelectFilter/>} {!isClusterScoped && <NamespaceSelectFilter />}
<PageFiltersSelect allowEmpty disableFilters={{ <PageFiltersSelect allowEmpty disableFilters={{
[FilterType.NAMESPACE]: true, // namespace-select used instead [FilterType.NAMESPACE]: true, // namespace-select used instead
}}/> }} />
</>, </>,
search: <SearchInputUrl/>, search: <SearchInputUrl />,
} }
let header = this.renderHeaderContent(placeholders); let header = this.renderHeaderContent(placeholders);
if (customizeHeader) { if (customizeHeader) {
@ -381,7 +381,7 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
return ( return (
<div className="items box grow flex column"> <div className="items box grow flex column">
{!isReady && ( {!isReady && (
<Spinner center/> <Spinner center />
)} )}
{isReady && ( {isReady && (
<Table <Table
@ -406,8 +406,8 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
onClick={prevDefault(() => store.toggleSelectionAll(items))} onClick={prevDefault(() => store.toggleSelectionAll(items))}
/> />
)} )}
{renderTableHeader.map((cellProps, index) => <TableCell key={index} {...cellProps}/>)} {renderTableHeader.map((cellProps, index) => <TableCell key={index} {...cellProps} />)}
{renderItemMenu && <TableCell className="menu"/>} {renderItemMenu && <TableCell className="menu" />}
</TableHead> </TableHead>
)} )}
{ {

View File

@ -57,11 +57,11 @@ export class KubeObjectMenu extends React.Component<KubeObjectMenuProps> {
render() { render() {
const { remove, update, renderRemoveMessage, isEditable, isRemovable } = this; const { remove, update, renderRemoveMessage, isEditable, isRemovable } = this;
const { className, object, editable, removable, ...menuProps } = this.props; const { className, object, editable, removable, toolbar, ...menuProps } = this.props;
if (!object) return null; if (!object) return null;
const menuItems = kubeObjectMenuRegistry.getItemsForKind(object.kind, object.apiVersion).map((item, index) => { const menuItems = kubeObjectMenuRegistry.getItemsForKind(object.kind, object.apiVersion).map((item, index) => {
return <item.components.MenuItem object={object} key={`menu-item-${index}`} /> return <item.components.MenuItem object={object} key={`menu-item-${index}`} toolbar={toolbar} />
}) })
return ( return (
<MenuActions <MenuActions
@ -69,6 +69,7 @@ export class KubeObjectMenu extends React.Component<KubeObjectMenuProps> {
updateAction={isEditable ? update : undefined} updateAction={isEditable ? update : undefined}
removeAction={isRemovable ? remove : undefined} removeAction={isRemovable ? remove : undefined}
removeConfirmationMessage={renderRemoveMessage} removeConfirmationMessage={renderRemoveMessage}
toolbar={toolbar}
{...menuProps} {...menuProps}
> >
{menuItems} {menuItems}

View File

@ -38,6 +38,7 @@ export class MainLayout extends React.Component<MainLayoutProps> {
(sidebarWidth) => this.storage.merge({ sidebarWidth }) (sidebarWidth) => this.storage.merge({ sidebarWidth })
); );
toggleSidebar = () => { toggleSidebar = () => {
this.isPinned = !this.isPinned; this.isPinned = !this.isPinned;
this.isAccessible = false; this.isAccessible = false;

View File

@ -27,8 +27,9 @@ import { crdStore } from "../+custom-resources/crd.store";
import { CrdList, crdResourcesRoute, crdRoute, crdURL } from "../+custom-resources"; import { CrdList, crdResourcesRoute, crdRoute, crdURL } from "../+custom-resources";
import { CustomResources } from "../+custom-resources/custom-resources"; import { CustomResources } from "../+custom-resources/custom-resources";
import { navigation } from "../../navigation"; import { navigation } from "../../navigation";
import { isAllowedResource } from "../../../common/rbac"
import { clusterPageRegistry } from "../../../extensions/registries/page-registry"; import { clusterPageRegistry } from "../../../extensions/registries/page-registry";
import { isAllowedResource } from "../../../common/rbac";
import { Spinner } from "../spinner";
const SidebarContext = React.createContext<SidebarContextValue>({ pinned: false }); const SidebarContext = React.createContext<SidebarContextValue>({ pinned: false });
type SidebarContextValue = { type SidebarContextValue = {
@ -50,6 +51,10 @@ export class Sidebar extends React.Component<Props> {
} }
renderCustomResources() { renderCustomResources() {
if (crdStore.isLoading) {
return <Spinner centerHorizontal />
}
return Object.entries(crdStore.groups).map(([group, crds]) => { return Object.entries(crdStore.groups).map(([group, crds]) => {
const submenus = crds.map((crd) => { const submenus = crds.map((crd) => {
return { return {
@ -80,7 +85,7 @@ export class Sidebar extends React.Component<Props> {
<div className={cssNames("Sidebar flex column", className, { pinned: isPinned })}> <div className={cssNames("Sidebar flex column", className, { pinned: isPinned })}>
<div className="header flex align-center"> <div className="header flex align-center">
<NavLink exact to="/" className="box grow"> <NavLink exact to="/" className="box grow">
<Icon svg="logo-lens" className="logo-icon"/> <Icon svg="logo-lens" className="logo-icon" />
<div className="logo-text">Lens</div> <div className="logo-text">Lens</div>
</NavLink> </NavLink>
<Icon <Icon
@ -97,14 +102,14 @@ export class Sidebar extends React.Component<Props> {
isHidden={!isAllowedResource("nodes")} isHidden={!isAllowedResource("nodes")}
url={clusterURL()} url={clusterURL()}
text={<Trans>Cluster</Trans>} text={<Trans>Cluster</Trans>}
icon={<Icon svg="kube"/>} icon={<Icon svg="kube" />}
/> />
<SidebarNavItem <SidebarNavItem
id="nodes" id="nodes"
isHidden={!isAllowedResource("nodes")} isHidden={!isAllowedResource("nodes")}
url={nodesURL()} url={nodesURL()}
text={<Trans>Nodes</Trans>} text={<Trans>Nodes</Trans>}
icon={<Icon svg="nodes"/>} icon={<Icon svg="nodes" />}
/> />
<SidebarNavItem <SidebarNavItem
id="workloads" id="workloads"
@ -113,7 +118,7 @@ export class Sidebar extends React.Component<Props> {
routePath={workloadsRoute.path} routePath={workloadsRoute.path}
subMenus={Workloads.tabRoutes} subMenus={Workloads.tabRoutes}
text={<Trans>Workloads</Trans>} text={<Trans>Workloads</Trans>}
icon={<Icon svg="workloads"/>} icon={<Icon svg="workloads" />}
/> />
<SidebarNavItem <SidebarNavItem
id="config" id="config"
@ -122,7 +127,7 @@ export class Sidebar extends React.Component<Props> {
routePath={configRoute.path} routePath={configRoute.path}
subMenus={Config.tabRoutes} subMenus={Config.tabRoutes}
text={<Trans>Configuration</Trans>} text={<Trans>Configuration</Trans>}
icon={<Icon material="list"/>} icon={<Icon material="list" />}
/> />
<SidebarNavItem <SidebarNavItem
id="networks" id="networks"
@ -131,7 +136,7 @@ export class Sidebar extends React.Component<Props> {
routePath={networkRoute.path} routePath={networkRoute.path}
subMenus={Network.tabRoutes} subMenus={Network.tabRoutes}
text={<Trans>Network</Trans>} text={<Trans>Network</Trans>}
icon={<Icon material="device_hub"/>} icon={<Icon material="device_hub" />}
/> />
<SidebarNavItem <SidebarNavItem
id="storage" id="storage"
@ -139,14 +144,14 @@ export class Sidebar extends React.Component<Props> {
url={storageURL({ query })} url={storageURL({ query })}
routePath={storageRoute.path} routePath={storageRoute.path}
subMenus={Storage.tabRoutes} subMenus={Storage.tabRoutes}
icon={<Icon svg="storage"/>} icon={<Icon svg="storage" />}
text={<Trans>Storage</Trans>} text={<Trans>Storage</Trans>}
/> />
<SidebarNavItem <SidebarNavItem
id="namespaces" id="namespaces"
isHidden={!isAllowedResource("namespaces")} isHidden={!isAllowedResource("namespaces")}
url={namespacesURL()} url={namespacesURL()}
icon={<Icon material="layers"/>} icon={<Icon material="layers" />}
text={<Trans>Namespaces</Trans>} text={<Trans>Namespaces</Trans>}
/> />
<SidebarNavItem <SidebarNavItem
@ -154,7 +159,7 @@ export class Sidebar extends React.Component<Props> {
isHidden={!isAllowedResource("events")} isHidden={!isAllowedResource("events")}
url={eventsURL({ query })} url={eventsURL({ query })}
routePath={eventRoute.path} routePath={eventRoute.path}
icon={<Icon material="access_time"/>} icon={<Icon material="access_time" />}
text={<Trans>Events</Trans>} text={<Trans>Events</Trans>}
/> />
<SidebarNavItem <SidebarNavItem
@ -162,7 +167,7 @@ export class Sidebar extends React.Component<Props> {
url={appsURL({ query })} url={appsURL({ query })}
subMenus={Apps.tabRoutes} subMenus={Apps.tabRoutes}
routePath={appsRoute.path} routePath={appsRoute.path}
icon={<Icon material="apps"/>} icon={<Icon material="apps" />}
text={<Trans>Apps</Trans>} text={<Trans>Apps</Trans>}
/> />
<SidebarNavItem <SidebarNavItem
@ -170,7 +175,7 @@ export class Sidebar extends React.Component<Props> {
url={usersManagementURL({ query })} url={usersManagementURL({ query })}
routePath={usersManagementRoute.path} routePath={usersManagementRoute.path}
subMenus={UserManagement.tabRoutes} subMenus={UserManagement.tabRoutes}
icon={<Icon material="security"/>} icon={<Icon material="security" />}
text={<Trans>Access Control</Trans>} text={<Trans>Access Control</Trans>}
/> />
<SidebarNavItem <SidebarNavItem
@ -179,7 +184,7 @@ export class Sidebar extends React.Component<Props> {
url={crdURL()} url={crdURL()}
subMenus={CustomResources.tabRoutes} subMenus={CustomResources.tabRoutes}
routePath={crdRoute.path} routePath={crdRoute.path}
icon={<Icon material="extension"/>} icon={<Icon material="extension" />}
text={<Trans>Custom Resources</Trans>} text={<Trans>Custom Resources</Trans>}
> >
{this.renderCustomResources()} {this.renderCustomResources()}
@ -194,7 +199,7 @@ export class Sidebar extends React.Component<Props> {
url={url} url={url}
routePath={path} routePath={path}
text={title} text={title}
icon={<MenuIcon/>} icon={<MenuIcon />}
/> />
) )
})} })}
@ -257,7 +262,7 @@ class SidebarNavItem extends React.Component<SidebarNavItemProps> {
<div className={cssNames("nav-item", { active: isActive })} onClick={this.toggleSubMenu}> <div className={cssNames("nav-item", { active: isActive })} onClick={this.toggleSubMenu}>
{icon} {icon}
<span className="link-text">{text}</span> <span className="link-text">{text}</span>
<Icon className="expand-icon" material={this.isExpanded ? "keyboard_arrow_up" : "keyboard_arrow_down"}/> <Icon className="expand-icon" material={this.isExpanded ? "keyboard_arrow_up" : "keyboard_arrow_down"} />
</div> </div>
<ul className={cssNames("sub-menu", { active: isActive })}> <ul className={cssNames("sub-menu", { active: isActive })}>
{subMenus.map(({ title, url }) => ( {subMenus.map(({ title, url }) => (

View File

@ -34,6 +34,12 @@
margin-top: calc(var(--spinner-size) / -2); margin-top: calc(var(--spinner-size) / -2);
} }
&.centerHorizontal {
position: absolute;
left: 50%;
margin-left: calc(var(--spinner-size) / -2);
}
@keyframes rotate { @keyframes rotate {
0% { 0% {
transform: rotate(0deg); transform: rotate(0deg);

View File

@ -6,23 +6,19 @@ import { cssNames } from "../../utils";
export interface SpinnerProps extends React.HTMLProps<any> { export interface SpinnerProps extends React.HTMLProps<any> {
singleColor?: boolean; singleColor?: boolean;
center?: boolean; center?: boolean;
centerHorizontal?: boolean;
} }
export class Spinner extends React.Component<SpinnerProps, {}> { export class Spinner extends React.Component<SpinnerProps, {}> {
private elem: HTMLElement;
static defaultProps = { static defaultProps = {
singleColor: true, singleColor: true,
center: false, center: false,
}; };
render() { render() {
const { center, singleColor, ...props } = this.props; const { center, singleColor, centerHorizontal, className, ...props } = this.props;
let { className } = this.props; const classNames = cssNames('Spinner', className, { singleColor, center, centerHorizontal });
className = cssNames('Spinner', className, {
singleColor: singleColor, return <div {...props} className={classNames} />;
center: center,
});
return <div {...props} className={className} ref={e => this.elem = e}/>;
} }
} }

View File

@ -2,7 +2,7 @@ import "./table-cell.scss";
import type { TableSortBy, TableSortParams } from "./table"; import type { TableSortBy, TableSortParams } from "./table";
import React, { ReactNode } from "react"; import React, { ReactNode } from "react";
import { autobind, cssNames } from "../../utils"; import { autobind, cssNames, displayBooleans } from "../../utils";
import { Icon } from "../icon"; import { Icon } from "../icon";
import { Checkbox } from "../checkbox"; import { Checkbox } from "../checkbox";
@ -13,6 +13,7 @@ export interface TableCellProps extends React.DOMAttributes<HTMLDivElement> {
title?: ReactNode; title?: ReactNode;
checkbox?: boolean; // render cell with a checkbox checkbox?: boolean; // render cell with a checkbox
isChecked?: boolean; // mark checkbox as checked or not isChecked?: boolean; // mark checkbox as checked or not
renderBoolean?: boolean; // show "true" or "false" for all of the children elements are "typeof boolean"
sortBy?: TableSortBy; // column name, must be same as key in sortable object <Table sortable={}/> sortBy?: TableSortBy; // column name, must be same as key in sortable object <Table sortable={}/>
_sorting?: Partial<TableSortParams>; // <Table> sorting state, don't use this prop outside (!) _sorting?: Partial<TableSortParams>; // <Table> sorting state, don't use this prop outside (!)
_sort?(sortBy: TableSortBy): void; // <Table> sort function, don't use this prop outside (!) _sort?(sortBy: TableSortBy): void; // <Table> sort function, don't use this prop outside (!)
@ -52,20 +53,20 @@ export class TableCell extends React.Component<TableCellProps> {
const { checkbox, isChecked } = this.props; const { checkbox, isChecked } = this.props;
const showCheckbox = isChecked !== undefined; const showCheckbox = isChecked !== undefined;
if (checkbox && showCheckbox) { if (checkbox && showCheckbox) {
return <Checkbox value={isChecked}/> return <Checkbox value={isChecked} />
} }
} }
render() { render() {
const { className, checkbox, isChecked, sortBy, _sort, _sorting, _nowrap, children, title, ...cellProps } = this.props; const { className, checkbox, isChecked, sortBy, _sort, _sorting, _nowrap, children, title, renderBoolean: displayBoolean, ...cellProps } = this.props;
const classNames = cssNames("TableCell", className, { const classNames = cssNames("TableCell", className, {
checkbox: checkbox, checkbox: checkbox,
nowrap: _nowrap, nowrap: _nowrap,
sorting: this.isSortable, sorting: this.isSortable,
}); });
const content = title || children; const content = displayBooleans(displayBoolean, title || children)
return ( return (
<div {...cellProps} className={classNames} onClick={this.onClick}> <div {...cellProps} id={className} className={classNames} onClick={this.onClick}>
{this.renderCheckbox()} {this.renderCheckbox()}
{_nowrap ? <div className="content">{content}</div> : content} {_nowrap ? <div className="content">{content}</div> : content}
{this.renderSortIcon()} {this.renderSortIcon()}

View File

@ -48,6 +48,7 @@ export class ThemeStore {
await this.loadTheme(themeId); await this.loadTheme(themeId);
this.applyTheme(); this.applyTheme();
} catch (err) { } catch (err) {
logger.error(err);
userStore.resetTheme(); userStore.resetTheme();
} }
}, { }, {
@ -79,7 +80,7 @@ export class ThemeStore {
} }
return existingTheme; return existingTheme;
} catch (err) { } catch (err) {
logger.error(`Can't load theme "${themeId}": ${err}`); throw new Error(`Can't load theme "${themeId}": ${err}`);
} }
} }
@ -90,7 +91,7 @@ export class ThemeStore {
document.head.prepend(this.styles); document.head.prepend(this.styles);
} }
const cssVars = Object.entries(theme.colors).map(([cssName, color]) => { const cssVars = Object.entries(theme.colors).map(([cssName, color]) => {
return `--${cssName}: ${color} !important;` return `--${cssName}: ${color};`;
}); });
this.styles.textContent = `:root {\n${cssVars.join("\n")}}`; this.styles.textContent = `:root {\n${cssVars.join("\n")}}`;
// Adding universal theme flag which can be used in component styles // Adding universal theme flag which can be used in component styles

View File

@ -0,0 +1,18 @@
import React from "react"
import { displayBooleans } from "../display-booleans"
describe("displayBooleans tests", () => {
it("should not do anything to div's if shouldShow is false", () => {
expect(displayBooleans(false, <div></div>)).toStrictEqual(<div></div>)
})
it("should not do anything to booleans's if shouldShow is false", () => {
expect(displayBooleans(false, true)).toStrictEqual(true)
expect(displayBooleans(false, false)).toStrictEqual(false)
})
it("should stringify booleans when shouldShow is true", () => {
expect(displayBooleans(true, true)).toStrictEqual("true")
expect(displayBooleans(true, false)).toStrictEqual("false")
})
})

View File

@ -0,0 +1,15 @@
import React from "react"
export function displayBooleans(shouldShow: boolean, from: React.ReactNode): React.ReactNode {
if (shouldShow) {
if (typeof from === "boolean") {
return from.toString()
}
if (Array.isArray(from)) {
return from.map(node => displayBooleans(shouldShow, node))
}
}
return from
}

View File

@ -18,3 +18,4 @@ export * from "./isReactNode"
export * from "./convertMemory" export * from "./convertMemory"
export * from "./convertCpu" export * from "./convertCpu"
export * from "./metricUnitsToNumber" export * from "./metricUnitsToNumber"
export * from "./display-booleans"

View File

@ -2,15 +2,17 @@
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-alpha.4 (current version) ## 4.0.0-alpha.5 (current version)
- Extension API - Extension API
- Improved pod logs - Improved pod logs
- Mechanism for users to specify accessible namespaces
- Tray icon - Tray icon
- Add last-status information for container - Add last-status information for container
- Add LoadBalancer information to Ingress view - Add LoadBalancer information to Ingress view
- Move tracker to an extension - Move tracker to an extension
- Add support page (as an extension) - Add support page (as an extension)
- Ability to restart deployment
- Status bar visual fixes - Status bar visual fixes
- Fix proxy upgrade socket timeouts - Fix proxy upgrade socket timeouts
- Fix UI staleness after network issues - Fix UI staleness after network issues

View File

@ -4,7 +4,11 @@
"jsx": "react", "jsx": "react",
"target": "ES2017", "target": "ES2017",
"module": "ESNext", "module": "ESNext",
"lib": ["ESNext", "DOM", "DOM.Iterable"], "lib": [
"ESNext",
"DOM",
"DOM.Iterable"
],
"moduleResolution": "Node", "moduleResolution": "Node",
"sourceMap": true, "sourceMap": true,
"strict": false, "strict": false,

View File

@ -4,7 +4,6 @@ import ForkTsCheckerPlugin from "fork-ts-checker-webpack-plugin"
import { isDevelopment, isProduction, mainDir, buildDir } from "./src/common/vars"; import { isDevelopment, isProduction, mainDir, buildDir } from "./src/common/vars";
import nodeExternals from "webpack-node-externals"; import nodeExternals from "webpack-node-externals";
import ProgressBarPlugin from "progress-bar-webpack-plugin"; import ProgressBarPlugin from "progress-bar-webpack-plugin";
import HardSourceWebpackPlugin from 'hard-source-webpack-plugin';
export default function (): webpack.Configuration { export default function (): webpack.Configuration {
console.info('WEBPACK:main', require("./src/common/vars")) console.info('WEBPACK:main', require("./src/common/vars"))
@ -48,7 +47,6 @@ export default function (): webpack.Configuration {
plugins: [ plugins: [
new ProgressBarPlugin(), new ProgressBarPlugin(),
new ForkTsCheckerPlugin(), new ForkTsCheckerPlugin(),
isDevelopment && new HardSourceWebpackPlugin(),
].filter(Boolean) ].filter(Boolean)
} }
} }

View File

@ -6,7 +6,6 @@ import MiniCssExtractPlugin from "mini-css-extract-plugin";
import TerserPlugin from "terser-webpack-plugin"; import TerserPlugin from "terser-webpack-plugin";
import ForkTsCheckerPlugin from "fork-ts-checker-webpack-plugin" import ForkTsCheckerPlugin from "fork-ts-checker-webpack-plugin"
import ProgressBarPlugin from "progress-bar-webpack-plugin"; import ProgressBarPlugin from "progress-bar-webpack-plugin";
import HardSourceWebpackPlugin from 'hard-source-webpack-plugin';
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin' import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin'
export default [ export default [
@ -187,7 +186,6 @@ export function webpackLensRenderer({ showVars = true } = {}): webpack.Configura
filename: "[name].css", filename: "[name].css",
}), }),
isDevelopment && new HardSourceWebpackPlugin(),
isDevelopment && new webpack.HotModuleReplacementPlugin(), isDevelopment && new webpack.HotModuleReplacementPlugin(),
isDevelopment && new ReactRefreshWebpackPlugin(), isDevelopment && new ReactRefreshWebpackPlugin(),

106
yarn.lock
View File

@ -1967,13 +1967,6 @@
"@types/podium" "*" "@types/podium" "*"
"@types/shot" "*" "@types/shot" "*"
"@types/hard-source-webpack-plugin@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@types/hard-source-webpack-plugin/-/hard-source-webpack-plugin-1.0.1.tgz#4aecca35bafb7939bcf318bbf5a2710c3163cdd4"
integrity sha512-5eTPERZQj5RZLADk5o2Ip/XRLwgxOUeKzlIM3+czrhwA9pnVJAUYOm2fovbxkrIEQhuozQwc17fyH1ZXnSR/8g==
dependencies:
"@types/webpack" "*"
"@types/history@*", "@types/history@^4.7.3": "@types/history@*", "@types/history@^4.7.3":
version "4.7.6" version "4.7.6"
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.6.tgz#ed8fc802c45b8e8f54419c2d054e55c9ea344356" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.6.tgz#ed8fc802c45b8e8f54419c2d054e55c9ea344356"
@ -5204,7 +5197,7 @@ detect-file@^1.0.0:
resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7"
integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=
detect-indent@^5.0.0, detect-indent@~5.0.0: detect-indent@~5.0.0:
version "5.0.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d"
integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50=
@ -6359,7 +6352,7 @@ finalhandler@~1.1.2:
statuses "~1.5.0" statuses "~1.5.0"
unpipe "~1.0.0" unpipe "~1.0.0"
find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: find-cache-dir@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7"
integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==
@ -7035,25 +7028,6 @@ har-validator@~5.1.3:
ajv "^6.5.5" ajv "^6.5.5"
har-schema "^2.0.0" har-schema "^2.0.0"
hard-source-webpack-plugin@^0.13.1:
version "0.13.1"
resolved "https://registry.yarnpkg.com/hard-source-webpack-plugin/-/hard-source-webpack-plugin-0.13.1.tgz#a99071e25b232f1438a5bc3c99f10a3869e4428e"
integrity sha512-r9zf5Wq7IqJHdVAQsZ4OP+dcUSvoHqDMxJlIzaE2J0TZWn3UjMMrHqwDHR8Jr/pzPfG7XxSe36E7Y8QGNdtuAw==
dependencies:
chalk "^2.4.1"
find-cache-dir "^2.0.0"
graceful-fs "^4.1.11"
lodash "^4.15.0"
mkdirp "^0.5.1"
node-object-hash "^1.2.0"
parse-json "^4.0.0"
pkg-dir "^3.0.0"
rimraf "^2.6.2"
semver "^5.6.0"
tapable "^1.0.0-beta.5"
webpack-sources "^1.0.1"
write-json-file "^2.3.0"
harmony-reflect@^1.4.6: harmony-reflect@^1.4.6:
version "1.6.1" version "1.6.1"
resolved "https://registry.yarnpkg.com/harmony-reflect/-/harmony-reflect-1.6.1.tgz#c108d4f2bb451efef7a37861fdbdae72c9bdefa9" resolved "https://registry.yarnpkg.com/harmony-reflect/-/harmony-reflect-1.6.1.tgz#c108d4f2bb451efef7a37861fdbdae72c9bdefa9"
@ -7168,6 +7142,11 @@ he@^1.2.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
highlight.js@^10.2.0:
version "10.3.2"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.3.2.tgz#135fd3619a00c3cbb8b4cd6dbc78d56bfcbc46f1"
integrity sha512-3jRT7OUYsVsKvukNKZCtnvRcFyCJqSEIuIMsEybAXRiFSwpt65qjPd/Pr+UOdYt7WJlt+lj3+ypUsHiySBp/Jw==
history@^4.10.1, history@^4.9.0: history@^4.10.1, history@^4.9.0:
version "4.10.1" version "4.10.1"
resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3"
@ -9317,7 +9296,7 @@ lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.1
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
lodash@^4.15.0, lodash@^4.17.19: lodash@^4.17.19, lodash@^4.17.20:
version "4.17.20" version "4.17.20"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
@ -9402,6 +9381,11 @@ lru-cache@^5.1.1:
dependencies: dependencies:
yallist "^3.0.2" yallist "^3.0.2"
lunr@^2.3.9:
version "2.3.9"
resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1"
integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==
lz-string@^1.4.4: lz-string@^1.4.4:
version "1.4.4" version "1.4.4"
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
@ -9499,6 +9483,11 @@ marked@^1.1.0:
resolved "https://registry.yarnpkg.com/marked/-/marked-1.1.0.tgz#62504ad4d11550c942935ccc5e39d64e5a4c4e50" resolved "https://registry.yarnpkg.com/marked/-/marked-1.1.0.tgz#62504ad4d11550c942935ccc5e39d64e5a4c4e50"
integrity sha512-EkE7RW6KcXfMHy2PA7Jg0YJE1l8UPEZE8k45tylzmZM30/r1M1MUXWQfJlrSbsTeh7m/XTwHbWUENvAJZpp1YA== integrity sha512-EkE7RW6KcXfMHy2PA7Jg0YJE1l8UPEZE8k45tylzmZM30/r1M1MUXWQfJlrSbsTeh7m/XTwHbWUENvAJZpp1YA==
marked@^1.1.1:
version "1.2.3"
resolved "https://registry.yarnpkg.com/marked/-/marked-1.2.3.tgz#58817ba348a7c9398cb94d40d12e0d08df83af57"
integrity sha512-RQuL2i6I6Gn+9n81IDNGbL0VHnta4a+8ZhqvryXEniTb/hQNtf3i26hi1XWUhzb9BgVyWHKR3UO8MaHtKoYibw==
matcher@^3.0.0: matcher@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca" resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca"
@ -9740,7 +9729,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
minimatch@^3.0.4, minimatch@~3.0.2: minimatch@^3.0.0, minimatch@^3.0.4, minimatch@~3.0.2:
version "3.0.4" version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
@ -10150,11 +10139,6 @@ node-notifier@^7.0.0:
uuid "^7.0.3" uuid "^7.0.3"
which "^2.0.2" which "^2.0.2"
node-object-hash@^1.2.0:
version "1.4.2"
resolved "https://registry.yarnpkg.com/node-object-hash/-/node-object-hash-1.4.2.tgz#385833d85b229902b75826224f6077be969a9e94"
integrity sha512-UdS4swXs85fCGWWf6t6DMGgpN/vnlKeSGEQ7hJcrs7PBFoxoKLmibc3QRb7fwiYsjdL7PX8iI/TMSlZ90dgHhQ==
node-pty@^0.9.0: node-pty@^0.9.0:
version "0.9.0" version "0.9.0"
resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.9.0.tgz#8f9bcc0d1c5b970a3184ffd533d862c7eb6590a6" resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.9.0.tgz#8f9bcc0d1c5b970a3184ffd533d862c7eb6590a6"
@ -12921,7 +12905,7 @@ shell-env@^3.0.0:
execa "^1.0.0" execa "^1.0.0"
strip-ansi "^5.2.0" strip-ansi "^5.2.0"
shelljs@^0.8.2: shelljs@^0.8.2, shelljs@^0.8.4:
version "0.8.4" version "0.8.4"
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2"
integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ== integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==
@ -13673,7 +13657,7 @@ table@^5.2.3:
slice-ansi "^2.1.0" slice-ansi "^2.1.0"
string-width "^3.0.0" string-width "^3.0.0"
tapable@^1.0.0, tapable@^1.0.0-beta.5, tapable@^1.1.3: tapable@^1.0.0, tapable@^1.1.3:
version "1.1.3" version "1.1.3"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
@ -14196,6 +14180,35 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
typedoc-default-themes@^0.11.4:
version "0.11.4"
resolved "https://registry.yarnpkg.com/typedoc-default-themes/-/typedoc-default-themes-0.11.4.tgz#1bc55b7c8d1132844616ff6f570e1e2cd0eb7343"
integrity sha512-Y4Lf+qIb9NTydrexlazAM46SSLrmrQRqWiD52593g53SsmUFioAsMWt8m834J6qsp+7wHRjxCXSZeiiW5cMUdw==
typedoc-plugin-markdown@^3.0.11:
version "3.0.11"
resolved "https://registry.yarnpkg.com/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.0.11.tgz#358c32f4a0086c1dd2da7f56c4b46ade8a63204b"
integrity sha512-/BE/PqnIVbQJ525czM+T3CVaA1gVN9X1Le100z8TV/Lze8LZVkuAUiHRIgw9BKYFm9IQaB88W55k4EV6uUVwYQ==
dependencies:
handlebars "^4.7.6"
typedoc@^0.19.2:
version "0.19.2"
resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.19.2.tgz#842a63a581f4920f76b0346bb80eb2a49afc2c28"
integrity sha512-oDEg1BLEzi1qvgdQXc658EYgJ5qJLVSeZ0hQ57Eq4JXy6Vj2VX4RVo18qYxRWz75ifAaYuYNBUCnbhjd37TfOg==
dependencies:
fs-extra "^9.0.1"
handlebars "^4.7.6"
highlight.js "^10.2.0"
lodash "^4.17.20"
lunr "^2.3.9"
marked "^1.1.1"
minimatch "^3.0.0"
progress "^2.0.3"
semver "^7.3.2"
shelljs "^0.8.4"
typedoc-default-themes "^0.11.4"
typeface-roboto@^0.0.75: typeface-roboto@^0.0.75:
version "0.0.75" version "0.0.75"
resolved "https://registry.yarnpkg.com/typeface-roboto/-/typeface-roboto-0.0.75.tgz#98d5ba35ec234bbc7172374c8297277099cc712b" resolved "https://registry.yarnpkg.com/typeface-roboto/-/typeface-roboto-0.0.75.tgz#98d5ba35ec234bbc7172374c8297277099cc712b"
@ -14743,7 +14756,7 @@ webpack-node-externals@^1.7.2:
resolved "https://registry.yarnpkg.com/webpack-node-externals/-/webpack-node-externals-1.7.2.tgz#6e1ee79ac67c070402ba700ef033a9b8d52ac4e3" resolved "https://registry.yarnpkg.com/webpack-node-externals/-/webpack-node-externals-1.7.2.tgz#6e1ee79ac67c070402ba700ef033a9b8d52ac4e3"
integrity sha512-ajerHZ+BJKeCLviLUUmnyd5B4RavLF76uv3cs6KNuO8W+HuQaEs0y0L7o40NQxdPy5w0pcv8Ew7yPUAQG0UdCg== integrity sha512-ajerHZ+BJKeCLviLUUmnyd5B4RavLF76uv3cs6KNuO8W+HuQaEs0y0L7o40NQxdPy5w0pcv8Ew7yPUAQG0UdCg==
webpack-sources@^1.0.1, webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3:
version "1.4.3" version "1.4.3"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933"
integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==
@ -14806,6 +14819,11 @@ wgxpath@~1.0.0:
resolved "https://registry.yarnpkg.com/wgxpath/-/wgxpath-1.0.0.tgz#eef8a4b9d558cc495ad3a9a2b751597ecd9af690" resolved "https://registry.yarnpkg.com/wgxpath/-/wgxpath-1.0.0.tgz#eef8a4b9d558cc495ad3a9a2b751597ecd9af690"
integrity sha1-7vikudVYzEla06mit1FZfs2a9pA= integrity sha1-7vikudVYzEla06mit1FZfs2a9pA=
what-input@^5.2.10:
version "5.2.10"
resolved "https://registry.yarnpkg.com/what-input/-/what-input-5.2.10.tgz#f79f5b65cf95d75e55e6d580bb0a6b98174cad4e"
integrity sha512-7AQoIMGq7uU8esmKniOtZG3A+pzlwgeyFpkS3f/yzRbxknSL68tvn5gjE6bZ4OMFxCPjpaBd2udUTqlZ0HwrXQ==
whatwg-encoding@^1.0.5: whatwg-encoding@^1.0.5:
version "1.0.5" version "1.0.5"
resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0"
@ -14985,18 +15003,6 @@ write-file-atomic@^3.0.0, write-file-atomic@^3.0.3:
signal-exit "^3.0.2" signal-exit "^3.0.2"
typedarray-to-buffer "^3.1.5" typedarray-to-buffer "^3.1.5"
write-json-file@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-2.3.0.tgz#2b64c8a33004d54b8698c76d585a77ceb61da32f"
integrity sha1-K2TIozAE1UuGmMdtWFp3zrYdoy8=
dependencies:
detect-indent "^5.0.0"
graceful-fs "^4.1.2"
make-dir "^1.0.0"
pify "^3.0.0"
sort-keys "^2.0.0"
write-file-atomic "^2.0.0"
write@1.0.3: write@1.0.3:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3"