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

Merge branch 'master' into extensions-api

This commit is contained in:
Lauri Nevala 2020-10-06 14:52:27 +03:00
commit 0f57ea91b1
56 changed files with 815 additions and 616 deletions

View File

@ -1,23 +1,16 @@
import { Application } from "spectron";
let appPath = ""
switch(process.platform) {
case "win32":
appPath = "./dist/win-unpacked/Lens.exe"
break
case "linux":
appPath = "./dist/linux-unpacked/kontena-lens"
break
case "darwin":
appPath = "./dist/mac/Lens.app/Contents/MacOS/Lens"
break
const AppPaths: Partial<Record<NodeJS.Platform, string>> = {
"win32": "./dist/win-unpacked/Lens.exe",
"linux": "./dist/linux-unpacked/kontena-lens",
"darwin": "./dist/mac/Lens.app/Contents/MacOS/Lens",
}
export function setup(): Application {
return new Application({
// path to electron app
args: [],
path: appPath,
path: AppPaths[process.platform],
startTimeout: 30000,
waitTimeout: 30000,
chromeDriverArgs: ['remote-debugging-port=9222'],
@ -32,7 +25,7 @@ export async function tearDown(app: Application) {
await app.stop()
try {
process.kill(pid, 0);
} catch(e) {
} catch (e) {
return
}
}

View File

@ -49,7 +49,7 @@ msgstr "<0>Filtered</0>: {itemsCount} / {allItemsCount}"
#~ msgid "<0>Your browser does not support all Lens features. </0> Please consider using another browser."
#~ msgstr "<0>Your browser does not support all Lens features. </0> Please consider using another browser."
#: src/renderer/components/dock/create-resource.tsx:56
#: src/renderer/components/dock/create-resource.tsx:58
msgid "<0>{0}</0> successfully created"
msgstr "<0>{0}</0> successfully created"
@ -57,7 +57,7 @@ msgstr "<0>{0}</0> successfully created"
#~ msgid "A HTTP proxy server URL (format: http://<address>:<port>)"
#~ msgstr "A HTTP proxy server URL (format: http://<address>:<port>)"
#: src/renderer/components/input/input.validators.ts:46
#: src/renderer/components/input/input_validators.ts:46
msgid "A System Name must be lowercase DNS labels separated by dots. DNS labels are alphanumerics and dashes enclosed by alphanumerics."
msgstr "A System Name must be lowercase DNS labels separated by dots. DNS labels are alphanumerics and dashes enclosed by alphanumerics."
@ -87,7 +87,7 @@ msgstr "Account Name"
msgid "Active"
msgstr "Active"
#: src/renderer/components/+add-cluster/add-cluster.tsx:288
#: src/renderer/components/+add-cluster/add-cluster.tsx:289
#: src/renderer/components/cluster-manager/clusters-menu.tsx:130
msgid "Add Cluster"
msgstr "Add Cluster"
@ -112,7 +112,7 @@ msgstr "Add bindings to {name}"
#~ msgid "Add cluster"
#~ msgstr "Add cluster"
#: src/renderer/components/+add-cluster/add-cluster.tsx:305
#: src/renderer/components/+add-cluster/add-cluster.tsx:306
msgid "Add cluster(s)"
msgstr "Add cluster(s)"
@ -273,7 +273,7 @@ msgstr "App Version"
msgid "App crash at <0>{pageUrl}</0>"
msgstr "App crash at <0>{pageUrl}</0>"
#: src/renderer/components/dock/edit-resource.tsx:88
#: src/renderer/components/dock/edit-resource.tsx:87
msgid "Applying.."
msgstr "Applying.."
@ -404,7 +404,7 @@ msgstr "CPU:"
#: src/renderer/components/+workspaces/workspaces.tsx:133
#: src/renderer/components/confirm-dialog/confirm-dialog.tsx:44
#: src/renderer/components/dock/info-panel.tsx:97
#: src/renderer/components/dock/info-panel.tsx:85
#: src/renderer/components/wizard/wizard.tsx:130
msgid "Cancel"
msgstr "Cancel"
@ -440,7 +440,7 @@ msgstr "Chart"
msgid "Chart Release <0>{0}</0> successfully created."
msgstr "Chart Release <0>{0}</0> successfully created."
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:105
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:124
msgid "Chart: {0}"
msgstr "Chart: {0}"
@ -647,7 +647,7 @@ msgstr "Count"
#: src/renderer/components/+user-management-roles/add-role-dialog.tsx:73
#: src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx:212
#: src/renderer/components/+user-management-service-accounts/create-service-account-dialog.tsx:76
#: src/renderer/components/dock/create-resource.tsx:71
#: src/renderer/components/dock/create-resource.tsx:74
msgid "Create"
msgstr "Create"
@ -691,7 +691,7 @@ msgstr "Create new Secret"
msgid "Create new Service Account"
msgstr "Create new Service Account"
#: src/renderer/components/dock/dock.tsx:111
#: src/renderer/components/dock/dock.tsx:93
msgid "Create resource"
msgstr "Create resource"
@ -783,8 +783,8 @@ msgid "Default Runtime Class Name"
msgstr "Default Runtime Class Name"
#: src/renderer/components/+preferences/kubectl-binaries.tsx:30
msgid "Default:"
msgstr "Default:"
#~ msgid "Default:"
#~ msgstr "Default:"
#: src/renderer/components/+custom-resources/custom-resources.tsx:22
msgid "Definitions"
@ -848,15 +848,19 @@ msgstr "Domains"
msgid "Download file"
msgstr "Download file"
#: src/renderer/components/+preferences/kubectl-binaries.tsx:39
#: src/renderer/components/+preferences/kubectl-binaries.tsx:24
msgid "Download kubectl binaries"
msgstr "Download kubectl binaries"
#: src/renderer/components/+preferences/kubectl-binaries.tsx:37
msgid "Download kubectl binaries matching to Kubernetes cluster verison."
msgstr "Download kubectl binaries matching to Kubernetes cluster verison."
#~ msgid "Download kubectl binaries matching to Kubernetes cluster verison."
#~ msgstr "Download kubectl binaries matching to Kubernetes cluster verison."
#: src/renderer/components/+preferences/kubectl-binaries.tsx:41
#: src/renderer/components/+preferences/kubectl-binaries.tsx:26
msgid "Download kubectl binaries matching to Kubernetes cluster version."
msgstr "Download kubectl binaries matching to Kubernetes cluster version."
#: src/renderer/components/+preferences/kubectl-binaries.tsx:29
msgid "Download mirror for kubectl"
msgstr "Download mirror for kubectl"
@ -944,7 +948,7 @@ msgstr "Everything is fine in the Cluster"
#~ msgid "Excluded items with \"system:\" prefix"
#~ msgstr "Excluded items with \"system:\" prefix"
#: src/renderer/components/dock/dock.tsx:116
#: src/renderer/components/dock/dock.tsx:98
msgid "Exit full size mode"
msgstr "Exit full size mode"
@ -985,7 +989,7 @@ msgstr "Finalizers"
msgid "First seen"
msgstr "First seen"
#: src/renderer/components/dock/dock.tsx:116
#: src/renderer/components/dock/dock.tsx:98
msgid "Fit to window"
msgstr "Fit to window"
@ -1073,7 +1077,7 @@ msgstr "Hide"
msgid "High number of replicas may cause cluster performance issues"
msgstr "High number of replicas may cause cluster performance issues"
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:81
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:88
msgid "Home"
msgstr "Home"
@ -1128,7 +1132,7 @@ msgstr "Image"
msgid "ImagePullPolicy"
msgstr "ImagePullPolicy"
#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:80
#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:108
msgid "ImagePullSecrets"
msgstr "ImagePullSecrets"
@ -1153,8 +1157,8 @@ msgstr "Ingresses"
msgid "Init Containers"
msgstr "Init Containers"
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:76
#: src/renderer/components/dock/install-chart.tsx:128
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:83
#: src/renderer/components/dock/install-chart.tsx:127
msgid "Install"
msgstr "Install"
@ -1162,15 +1166,15 @@ msgstr "Install"
msgid "Installation complete!"
msgstr "Installation complete!"
#: src/renderer/components/dock/install-chart.tsx:128
#: src/renderer/components/dock/install-chart.tsx:127
msgid "Installing..."
msgstr "Installing..."
#: src/renderer/components/input/input.validators.ts:50
#: src/renderer/components/input/input_validators.ts:50
msgid "Invalid account ID"
msgstr "Invalid account ID"
#: src/renderer/components/input/input.validators.ts:16
#: src/renderer/components/input/input_validators.ts:16
msgid "Invalid number"
msgstr "Invalid number"
@ -1231,13 +1235,13 @@ msgstr "Key Size"
msgid "Keys"
msgstr "Keys"
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:87
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:94
msgid "Keywords"
msgstr "Keywords"
#: src/renderer/components/+events/event-details.tsx:57
#: src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx:79
#: src/renderer/components/dock/edit-resource.tsx:89
#: src/renderer/components/dock/edit-resource.tsx:88
msgid "Kind"
msgstr "Kind"
@ -1249,7 +1253,7 @@ msgstr "Kubeconfig"
msgid "Kubeconfig File"
msgstr "Kubeconfig File"
#: src/renderer/components/+preferences/kubectl-binaries.tsx:35
#: src/renderer/components/+preferences/kubectl-binaries.tsx:23
msgid "Kubectl Binary"
msgstr "Kubectl Binary"
@ -1343,7 +1347,7 @@ msgstr "Logs"
msgid "Logs copied to clipboard."
msgstr "Logs copied to clipboard."
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:84
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:91
msgid "Maintainers"
msgstr "Maintainers"
@ -1369,7 +1373,7 @@ msgstr "Max Pods"
msgid "Max Unavailable"
msgstr "Max Unavailable"
#: src/renderer/components/input/input.validators.ts:41
#: src/renderer/components/input/input_validators.ts:41
msgid "Maximum length is {maxLength}"
msgstr "Maximum length is {maxLength}"
@ -1441,11 +1445,11 @@ msgstr "Min Available"
msgid "Min Pods"
msgstr "Min Pods"
#: src/renderer/components/dock/dock.tsx:117
#: src/renderer/components/dock/dock.tsx:99
msgid "Minimize"
msgstr "Minimize"
#: src/renderer/components/input/input.validators.ts:36
#: src/renderer/components/input/input_validators.ts:36
msgid "Minimum length is {minLength}"
msgstr "Minimum length is {minLength}"
@ -1454,7 +1458,7 @@ msgstr "Minimum length is {minLength}"
msgid "Mount Options"
msgstr "Mount Options"
#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:84
#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:112
msgid "Mountable secrets"
msgstr "Mountable secrets"
@ -1510,7 +1514,7 @@ msgstr "Mounts"
#: src/renderer/components/+workloads-replicasets/replicasets.tsx:50
#: src/renderer/components/+workloads-statefulsets/statefulsets.tsx:40
#: src/renderer/components/+workspaces/workspaces.tsx:130
#: src/renderer/components/dock/edit-resource.tsx:90
#: src/renderer/components/dock/edit-resource.tsx:89
#: src/renderer/components/kube-object/kube-object-meta.tsx:20
msgid "Name"
msgstr "Name"
@ -1556,7 +1560,7 @@ msgstr "Names"
#: src/renderer/components/+workloads-jobs/jobs.tsx:38
#: src/renderer/components/+workloads-pods/pods.tsx:76
#: src/renderer/components/+workloads-statefulsets/statefulsets.tsx:41
#: src/renderer/components/dock/edit-resource.tsx:91
#: src/renderer/components/dock/edit-resource.tsx:90
#: src/renderer/components/dock/install-chart.tsx:122
#: src/renderer/components/dock/upgrade-chart.tsx:98
#: src/renderer/components/item-object-list/page-filters-select.tsx:57
@ -1600,7 +1604,7 @@ msgstr "Network Policies"
msgid "New logs since opening the dialog"
msgstr "New logs since opening the dialog"
#: src/renderer/components/dock/dock.tsx:104
#: src/renderer/components/dock/dock.tsx:86
msgid "New tab"
msgstr "New tab"
@ -1734,7 +1738,7 @@ msgstr "Ok"
msgid "Ok, got it!"
msgstr "Ok, got it!"
#: src/renderer/components/dock/dock.tsx:117
#: src/renderer/components/dock/dock.tsx:99
msgid "Open"
msgstr "Open"
@ -1939,7 +1943,7 @@ msgstr "Provisioner"
msgid "Proxy is used only for non-cluster communication."
msgstr "Proxy is used only for non-cluster communication."
#: src/renderer/components/+add-cluster/add-cluster.tsx:293
#: src/renderer/components/+add-cluster/add-cluster.tsx:294
msgid "Proxy settings"
msgstr "Proxy settings"
@ -2264,7 +2268,7 @@ msgstr "Runtime Class"
#: src/renderer/components/+config-secrets/secret-details.tsx:97
#: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:216
#: src/renderer/components/+workspaces/workspaces.tsx:132
#: src/renderer/components/dock/edit-resource.tsx:88
#: src/renderer/components/dock/edit-resource.tsx:87
msgid "Save"
msgstr "Save"
@ -2318,6 +2322,10 @@ msgstr "Secret"
msgid "Secret Name"
msgstr "Secret Name"
#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:72
msgid "Secret is not found"
msgstr "Secret is not found"
#: src/renderer/components/+config-secrets/add-secret-dialog.tsx:147
msgid "Secret name"
msgstr "Secret name"
@ -2472,7 +2480,7 @@ msgid "Shell"
msgstr "Shell"
#: src/renderer/components/+config-secrets/secret-details.tsx:93
#: src/renderer/components/+workloads-pods/pod-container-env.tsx:100
#: src/renderer/components/+workloads-pods/pod-container-env.tsx:101
#: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:215
#: src/renderer/components/drawer/drawer-param-toggler.tsx:19
msgid "Show"
@ -2580,12 +2588,12 @@ msgstr "Strategy Type"
msgid "Sub-object"
msgstr "Sub-object"
#: src/renderer/components/dock/info-panel.tsx:104
#: src/renderer/components/dock/info-panel.tsx:93
#: src/renderer/components/wizard/wizard.tsx:131
msgid "Submit"
msgstr "Submit"
#: src/renderer/components/dock/info-panel.tsx:105
#: src/renderer/components/dock/info-panel.tsx:94
msgid "Submitting.."
msgstr "Submitting.."
@ -2627,10 +2635,14 @@ msgstr "Telemetry & usage data is collected to continuously improve the Lens exp
msgid "Terminal"
msgstr "Terminal"
#: src/renderer/components/dock/dock.tsx:107
#: src/renderer/components/dock/dock.tsx:89
msgid "Terminal session"
msgstr "Terminal session"
#: src/renderer/components/+preferences/kubectl-binaries.tsx:38
msgid "The path to the kubectl binary on the system."
msgstr "The path to the kubectl binary on the system."
#: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:226
msgid "There are no logs available for container."
msgstr "There are no logs available for container."
@ -2639,11 +2651,11 @@ msgstr "There are no logs available for container."
msgid "There are no logs available."
msgstr "There are no logs available."
#: src/renderer/components/input/input.validators.ts:6
#: src/renderer/components/input/input_validators.ts:6
msgid "This field is required"
msgstr "This field is required"
#: src/renderer/components/input/input.validators.ts:31
#: src/renderer/components/input/input_validators.ts:31
msgid "This field must be a valid path"
msgstr "This field must be a valid path"
@ -2663,7 +2675,7 @@ msgstr "To"
msgid "To help us improve the product please report bugs to {slackLink} community or {githubLink} issues tracker."
msgstr "To help us improve the product please report bugs to {slackLink} community or {githubLink} issues tracker."
#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:76
#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:104
msgid "Tokens"
msgstr "Tokens"
@ -2730,12 +2742,12 @@ msgstr "Update"
msgid "Updated"
msgstr "Updated"
#: src/renderer/components/dock/upgrade-chart.tsx:105
#: src/renderer/components/dock/upgrade-chart.tsx:104
msgid "Updating.."
msgstr "Updating.."
#: src/renderer/components/+apps-releases/release-details.tsx:176
#: src/renderer/components/dock/upgrade-chart.tsx:105
#: src/renderer/components/dock/upgrade-chart.tsx:104
msgid "Upgrade"
msgstr "Upgrade"
@ -2799,7 +2811,7 @@ msgstr "Values"
msgid "Verbs"
msgstr "Verbs"
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:78
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:85
#: src/renderer/components/+apps-helm-charts/helm-charts.tsx:66
#: src/renderer/components/+apps-releases/release-details.tsx:185
#: src/renderer/components/+apps-releases/releases.tsx:91
@ -2866,11 +2878,11 @@ msgstr "Workspaces"
msgid "Workspaces are used to organize number of clusters into logical groups."
msgstr "Workspaces are used to organize number of clusters into logical groups."
#: src/renderer/components/input/input.validators.ts:11
#: src/renderer/components/input/input_validators.ts:11
msgid "Wrong email format"
msgstr "Wrong email format"
#: src/renderer/components/input/input.validators.ts:26
#: src/renderer/components/input/input_validators.ts:26
msgid "Wrong url format"
msgstr "Wrong url format"
@ -2953,7 +2965,7 @@ msgstr "singular"
msgid "timestamps"
msgstr "timestamps"
#: src/renderer/components/dock/create-resource.tsx:55
#: src/renderer/components/dock/create-resource.tsx:57
msgid "{0, plural, one {Resource} other {Resources}}"
msgstr "{0, plural, one {Resource} other {Resources}}"
@ -3005,6 +3017,6 @@ msgstr "{resourceType} <0>{resourceName}</0> updated."
msgid "{selectedCount, plural, one {<0>Remove item <1>{selectedNames}</1>?</0>} other {<2>Remove <3>{selectedCount}</3> items <4>{selectedNames}</4> {tail}?</2>}}"
msgstr "{selectedCount, plural, one {<0>Remove item <1>{selectedNames}</1>?</0>} other {<2>Remove <3>{selectedCount}</3> items <4>{selectedNames}</4> {tail}?</2>}}"
#: src/renderer/components/dock/info-panel.tsx:99
#: src/renderer/components/dock/info-panel.tsx:88
msgid "{submitLabel} & Close"
msgstr "{submitLabel} & Close"

View File

@ -49,7 +49,7 @@ msgstr ""
#~ msgid "<0>Your browser does not support all Lens features. </0> Please consider using another browser."
#~ msgstr ""
#: src/renderer/components/dock/create-resource.tsx:56
#: src/renderer/components/dock/create-resource.tsx:58
msgid "<0>{0}</0> successfully created"
msgstr ""
@ -57,7 +57,7 @@ msgstr ""
#~ msgid "A HTTP proxy server URL (format: http://<address>:<port>)"
#~ msgstr ""
#: src/renderer/components/input/input.validators.ts:46
#: src/renderer/components/input/input_validators.ts:46
msgid "A System Name must be lowercase DNS labels separated by dots. DNS labels are alphanumerics and dashes enclosed by alphanumerics."
msgstr ""
@ -87,7 +87,7 @@ msgstr ""
msgid "Active"
msgstr ""
#: src/renderer/components/+add-cluster/add-cluster.tsx:288
#: src/renderer/components/+add-cluster/add-cluster.tsx:289
#: src/renderer/components/cluster-manager/clusters-menu.tsx:130
msgid "Add Cluster"
msgstr ""
@ -112,7 +112,7 @@ msgstr ""
#~ msgid "Add cluster"
#~ msgstr ""
#: src/renderer/components/+add-cluster/add-cluster.tsx:305
#: src/renderer/components/+add-cluster/add-cluster.tsx:306
msgid "Add cluster(s)"
msgstr ""
@ -273,7 +273,7 @@ msgstr ""
msgid "App crash at <0>{pageUrl}</0>"
msgstr ""
#: src/renderer/components/dock/edit-resource.tsx:88
#: src/renderer/components/dock/edit-resource.tsx:87
msgid "Applying.."
msgstr ""
@ -404,7 +404,7 @@ msgstr ""
#: src/renderer/components/+workspaces/workspaces.tsx:133
#: src/renderer/components/confirm-dialog/confirm-dialog.tsx:44
#: src/renderer/components/dock/info-panel.tsx:97
#: src/renderer/components/dock/info-panel.tsx:85
#: src/renderer/components/wizard/wizard.tsx:130
msgid "Cancel"
msgstr ""
@ -440,7 +440,7 @@ msgstr ""
msgid "Chart Release <0>{0}</0> successfully created."
msgstr ""
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:105
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:124
msgid "Chart: {0}"
msgstr ""
@ -643,7 +643,7 @@ msgstr ""
#: src/renderer/components/+user-management-roles/add-role-dialog.tsx:73
#: src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx:212
#: src/renderer/components/+user-management-service-accounts/create-service-account-dialog.tsx:76
#: src/renderer/components/dock/create-resource.tsx:71
#: src/renderer/components/dock/create-resource.tsx:74
msgid "Create"
msgstr ""
@ -687,7 +687,7 @@ msgstr ""
msgid "Create new Service Account"
msgstr ""
#: src/renderer/components/dock/dock.tsx:111
#: src/renderer/components/dock/dock.tsx:93
msgid "Create resource"
msgstr ""
@ -779,8 +779,8 @@ msgid "Default Runtime Class Name"
msgstr ""
#: src/renderer/components/+preferences/kubectl-binaries.tsx:30
msgid "Default:"
msgstr ""
#~ msgid "Default:"
#~ msgstr ""
#: src/renderer/components/+custom-resources/custom-resources.tsx:22
msgid "Definitions"
@ -844,15 +844,19 @@ msgstr ""
msgid "Download file"
msgstr ""
#: src/renderer/components/+preferences/kubectl-binaries.tsx:39
#: src/renderer/components/+preferences/kubectl-binaries.tsx:24
msgid "Download kubectl binaries"
msgstr ""
#: src/renderer/components/+preferences/kubectl-binaries.tsx:37
msgid "Download kubectl binaries matching to Kubernetes cluster verison."
#~ msgid "Download kubectl binaries matching to Kubernetes cluster verison."
#~ msgstr ""
#: src/renderer/components/+preferences/kubectl-binaries.tsx:26
msgid "Download kubectl binaries matching to Kubernetes cluster version."
msgstr ""
#: src/renderer/components/+preferences/kubectl-binaries.tsx:41
#: src/renderer/components/+preferences/kubectl-binaries.tsx:29
msgid "Download mirror for kubectl"
msgstr ""
@ -935,7 +939,7 @@ msgstr ""
msgid "Everything is fine in the Cluster"
msgstr ""
#: src/renderer/components/dock/dock.tsx:116
#: src/renderer/components/dock/dock.tsx:98
msgid "Exit full size mode"
msgstr ""
@ -976,7 +980,7 @@ msgstr ""
msgid "First seen"
msgstr ""
#: src/renderer/components/dock/dock.tsx:116
#: src/renderer/components/dock/dock.tsx:98
msgid "Fit to window"
msgstr ""
@ -1064,7 +1068,7 @@ msgstr ""
msgid "High number of replicas may cause cluster performance issues"
msgstr ""
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:81
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:88
msgid "Home"
msgstr ""
@ -1119,7 +1123,7 @@ msgstr ""
msgid "ImagePullPolicy"
msgstr ""
#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:80
#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:108
msgid "ImagePullSecrets"
msgstr ""
@ -1144,8 +1148,8 @@ msgstr ""
msgid "Init Containers"
msgstr ""
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:76
#: src/renderer/components/dock/install-chart.tsx:128
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:83
#: src/renderer/components/dock/install-chart.tsx:127
msgid "Install"
msgstr ""
@ -1153,15 +1157,15 @@ msgstr ""
msgid "Installation complete!"
msgstr ""
#: src/renderer/components/dock/install-chart.tsx:128
#: src/renderer/components/dock/install-chart.tsx:127
msgid "Installing..."
msgstr ""
#: src/renderer/components/input/input.validators.ts:50
#: src/renderer/components/input/input_validators.ts:50
msgid "Invalid account ID"
msgstr ""
#: src/renderer/components/input/input.validators.ts:16
#: src/renderer/components/input/input_validators.ts:16
msgid "Invalid number"
msgstr ""
@ -1222,13 +1226,13 @@ msgstr ""
msgid "Keys"
msgstr ""
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:87
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:94
msgid "Keywords"
msgstr ""
#: src/renderer/components/+events/event-details.tsx:57
#: src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx:79
#: src/renderer/components/dock/edit-resource.tsx:89
#: src/renderer/components/dock/edit-resource.tsx:88
msgid "Kind"
msgstr ""
@ -1240,7 +1244,7 @@ msgstr ""
msgid "Kubeconfig File"
msgstr ""
#: src/renderer/components/+preferences/kubectl-binaries.tsx:35
#: src/renderer/components/+preferences/kubectl-binaries.tsx:23
msgid "Kubectl Binary"
msgstr ""
@ -1334,7 +1338,7 @@ msgstr ""
msgid "Logs copied to clipboard."
msgstr ""
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:84
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:91
msgid "Maintainers"
msgstr ""
@ -1360,7 +1364,7 @@ msgstr ""
msgid "Max Unavailable"
msgstr ""
#: src/renderer/components/input/input.validators.ts:41
#: src/renderer/components/input/input_validators.ts:41
msgid "Maximum length is {maxLength}"
msgstr ""
@ -1432,11 +1436,11 @@ msgstr ""
msgid "Min Pods"
msgstr ""
#: src/renderer/components/dock/dock.tsx:117
#: src/renderer/components/dock/dock.tsx:99
msgid "Minimize"
msgstr ""
#: src/renderer/components/input/input.validators.ts:36
#: src/renderer/components/input/input_validators.ts:36
msgid "Minimum length is {minLength}"
msgstr ""
@ -1445,7 +1449,7 @@ msgstr ""
msgid "Mount Options"
msgstr ""
#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:84
#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:112
msgid "Mountable secrets"
msgstr ""
@ -1501,7 +1505,7 @@ msgstr ""
#: src/renderer/components/+workloads-replicasets/replicasets.tsx:50
#: src/renderer/components/+workloads-statefulsets/statefulsets.tsx:40
#: src/renderer/components/+workspaces/workspaces.tsx:130
#: src/renderer/components/dock/edit-resource.tsx:90
#: src/renderer/components/dock/edit-resource.tsx:89
#: src/renderer/components/kube-object/kube-object-meta.tsx:20
msgid "Name"
msgstr ""
@ -1547,7 +1551,7 @@ msgstr ""
#: src/renderer/components/+workloads-jobs/jobs.tsx:38
#: src/renderer/components/+workloads-pods/pods.tsx:76
#: src/renderer/components/+workloads-statefulsets/statefulsets.tsx:41
#: src/renderer/components/dock/edit-resource.tsx:91
#: src/renderer/components/dock/edit-resource.tsx:90
#: src/renderer/components/dock/install-chart.tsx:122
#: src/renderer/components/dock/upgrade-chart.tsx:98
#: src/renderer/components/item-object-list/page-filters-select.tsx:57
@ -1591,7 +1595,7 @@ msgstr ""
msgid "New logs since opening the dialog"
msgstr ""
#: src/renderer/components/dock/dock.tsx:104
#: src/renderer/components/dock/dock.tsx:86
msgid "New tab"
msgstr ""
@ -1717,7 +1721,7 @@ msgstr ""
msgid "Ok, got it!"
msgstr ""
#: src/renderer/components/dock/dock.tsx:117
#: src/renderer/components/dock/dock.tsx:99
msgid "Open"
msgstr ""
@ -1922,7 +1926,7 @@ msgstr ""
msgid "Proxy is used only for non-cluster communication."
msgstr ""
#: src/renderer/components/+add-cluster/add-cluster.tsx:293
#: src/renderer/components/+add-cluster/add-cluster.tsx:294
msgid "Proxy settings"
msgstr ""
@ -2247,7 +2251,7 @@ msgstr ""
#: src/renderer/components/+config-secrets/secret-details.tsx:97
#: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:216
#: src/renderer/components/+workspaces/workspaces.tsx:132
#: src/renderer/components/dock/edit-resource.tsx:88
#: src/renderer/components/dock/edit-resource.tsx:87
msgid "Save"
msgstr ""
@ -2301,6 +2305,10 @@ msgstr ""
msgid "Secret Name"
msgstr ""
#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:72
msgid "Secret is not found"
msgstr ""
#: src/renderer/components/+config-secrets/add-secret-dialog.tsx:147
msgid "Secret name"
msgstr ""
@ -2455,7 +2463,7 @@ msgid "Shell"
msgstr ""
#: src/renderer/components/+config-secrets/secret-details.tsx:93
#: src/renderer/components/+workloads-pods/pod-container-env.tsx:100
#: src/renderer/components/+workloads-pods/pod-container-env.tsx:101
#: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:215
#: src/renderer/components/drawer/drawer-param-toggler.tsx:19
msgid "Show"
@ -2563,12 +2571,12 @@ msgstr ""
msgid "Sub-object"
msgstr ""
#: src/renderer/components/dock/info-panel.tsx:104
#: src/renderer/components/dock/info-panel.tsx:93
#: src/renderer/components/wizard/wizard.tsx:131
msgid "Submit"
msgstr ""
#: src/renderer/components/dock/info-panel.tsx:105
#: src/renderer/components/dock/info-panel.tsx:94
msgid "Submitting.."
msgstr ""
@ -2610,10 +2618,14 @@ msgstr ""
msgid "Terminal"
msgstr ""
#: src/renderer/components/dock/dock.tsx:107
#: src/renderer/components/dock/dock.tsx:89
msgid "Terminal session"
msgstr ""
#: src/renderer/components/+preferences/kubectl-binaries.tsx:38
msgid "The path to the kubectl binary on the system."
msgstr ""
#: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:226
msgid "There are no logs available for container."
msgstr ""
@ -2622,11 +2634,11 @@ msgstr ""
msgid "There are no logs available."
msgstr ""
#: src/renderer/components/input/input.validators.ts:6
#: src/renderer/components/input/input_validators.ts:6
msgid "This field is required"
msgstr ""
#: src/renderer/components/input/input.validators.ts:31
#: src/renderer/components/input/input_validators.ts:31
msgid "This field must be a valid path"
msgstr ""
@ -2646,7 +2658,7 @@ msgstr ""
msgid "To help us improve the product please report bugs to {slackLink} community or {githubLink} issues tracker."
msgstr ""
#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:76
#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:104
msgid "Tokens"
msgstr ""
@ -2713,12 +2725,12 @@ msgstr ""
msgid "Updated"
msgstr ""
#: src/renderer/components/dock/upgrade-chart.tsx:105
#: src/renderer/components/dock/upgrade-chart.tsx:104
msgid "Updating.."
msgstr ""
#: src/renderer/components/+apps-releases/release-details.tsx:176
#: src/renderer/components/dock/upgrade-chart.tsx:105
#: src/renderer/components/dock/upgrade-chart.tsx:104
msgid "Upgrade"
msgstr ""
@ -2782,7 +2794,7 @@ msgstr ""
msgid "Verbs"
msgstr ""
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:78
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:85
#: src/renderer/components/+apps-helm-charts/helm-charts.tsx:66
#: src/renderer/components/+apps-releases/release-details.tsx:185
#: src/renderer/components/+apps-releases/releases.tsx:91
@ -2849,11 +2861,11 @@ msgstr ""
msgid "Workspaces are used to organize number of clusters into logical groups."
msgstr ""
#: src/renderer/components/input/input.validators.ts:11
#: src/renderer/components/input/input_validators.ts:11
msgid "Wrong email format"
msgstr ""
#: src/renderer/components/input/input.validators.ts:26
#: src/renderer/components/input/input_validators.ts:26
msgid "Wrong url format"
msgstr ""
@ -2936,7 +2948,7 @@ msgstr ""
msgid "timestamps"
msgstr ""
#: src/renderer/components/dock/create-resource.tsx:55
#: src/renderer/components/dock/create-resource.tsx:57
msgid "{0, plural, one {Resource} other {Resources}}"
msgstr ""
@ -2988,6 +3000,6 @@ msgstr ""
msgid "{selectedCount, plural, one {<0>Remove item <1>{selectedNames}</1>?</0>} other {<2>Remove <3>{selectedCount}</3> items <4>{selectedNames}</4> {tail}?</2>}}"
msgstr ""
#: src/renderer/components/dock/info-panel.tsx:99
#: src/renderer/components/dock/info-panel.tsx:88
msgid "{submitLabel} & Close"
msgstr ""

View File

@ -50,7 +50,7 @@ msgstr "<0>Отфильтровано</0>: {itemsCount} / {allItemsCount}"
#~ msgid "<0>Your browser does not support all Lens features. </0> Please consider using another browser."
#~ msgstr "<0>Ваш браузер не поддерживает все возможности Lens. </0> Пожалуйста рассмотрите использование другого современного браузера."
#: src/renderer/components/dock/create-resource.tsx:56
#: src/renderer/components/dock/create-resource.tsx:58
msgid "<0>{0}</0> successfully created"
msgstr ""
@ -58,7 +58,7 @@ msgstr ""
#~ msgid "A HTTP proxy server URL (format: http://<address>:<port>)"
#~ msgstr ""
#: src/renderer/components/input/input.validators.ts:46
#: src/renderer/components/input/input_validators.ts:46
msgid "A System Name must be lowercase DNS labels separated by dots. DNS labels are alphanumerics and dashes enclosed by alphanumerics."
msgstr "Это поле может содержать только латинские буквы в нижнем регистре, номера и дефис."
@ -88,7 +88,7 @@ msgstr "Название аккаунта"
msgid "Active"
msgstr "Активный"
#: src/renderer/components/+add-cluster/add-cluster.tsx:288
#: src/renderer/components/+add-cluster/add-cluster.tsx:289
#: src/renderer/components/cluster-manager/clusters-menu.tsx:130
msgid "Add Cluster"
msgstr ""
@ -113,7 +113,7 @@ msgstr "Добавить привязки к {name}"
#~ msgid "Add cluster"
#~ msgstr ""
#: src/renderer/components/+add-cluster/add-cluster.tsx:305
#: src/renderer/components/+add-cluster/add-cluster.tsx:306
msgid "Add cluster(s)"
msgstr ""
@ -274,7 +274,7 @@ msgstr "Версия приложения"
msgid "App crash at <0>{pageUrl}</0>"
msgstr "Сбой работы приложения на <0>{pageUrl}</0>"
#: src/renderer/components/dock/edit-resource.tsx:88
#: src/renderer/components/dock/edit-resource.tsx:87
msgid "Applying.."
msgstr "Применение.."
@ -405,7 +405,7 @@ msgstr "CPU:"
#: src/renderer/components/+workspaces/workspaces.tsx:133
#: src/renderer/components/confirm-dialog/confirm-dialog.tsx:44
#: src/renderer/components/dock/info-panel.tsx:97
#: src/renderer/components/dock/info-panel.tsx:85
#: src/renderer/components/wizard/wizard.tsx:130
msgid "Cancel"
msgstr "Отмена"
@ -441,7 +441,7 @@ msgstr "Чарт"
msgid "Chart Release <0>{0}</0> successfully created."
msgstr "Релиз чарта <0>{0}</0> успешно создан."
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:105
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:124
msgid "Chart: {0}"
msgstr "Чарт: {0}"
@ -648,7 +648,7 @@ msgstr "Кол-во"
#: src/renderer/components/+user-management-roles/add-role-dialog.tsx:73
#: src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx:212
#: src/renderer/components/+user-management-service-accounts/create-service-account-dialog.tsx:76
#: src/renderer/components/dock/create-resource.tsx:71
#: src/renderer/components/dock/create-resource.tsx:74
msgid "Create"
msgstr "Создать"
@ -692,7 +692,7 @@ msgstr "Создать новый секрет"
msgid "Create new Service Account"
msgstr "Создать новый Service Account"
#: src/renderer/components/dock/dock.tsx:111
#: src/renderer/components/dock/dock.tsx:93
msgid "Create resource"
msgstr "Создать ресурс"
@ -784,8 +784,8 @@ msgid "Default Runtime Class Name"
msgstr ""
#: src/renderer/components/+preferences/kubectl-binaries.tsx:30
msgid "Default:"
msgstr ""
#~ msgid "Default:"
#~ msgstr ""
#: src/renderer/components/+custom-resources/custom-resources.tsx:22
msgid "Definitions"
@ -849,15 +849,19 @@ msgstr "Домены"
msgid "Download file"
msgstr "Скачать файл"
#: src/renderer/components/+preferences/kubectl-binaries.tsx:39
#: src/renderer/components/+preferences/kubectl-binaries.tsx:24
msgid "Download kubectl binaries"
msgstr ""
#: src/renderer/components/+preferences/kubectl-binaries.tsx:37
msgid "Download kubectl binaries matching to Kubernetes cluster verison."
#~ msgid "Download kubectl binaries matching to Kubernetes cluster verison."
#~ msgstr ""
#: src/renderer/components/+preferences/kubectl-binaries.tsx:26
msgid "Download kubectl binaries matching to Kubernetes cluster version."
msgstr ""
#: src/renderer/components/+preferences/kubectl-binaries.tsx:41
#: src/renderer/components/+preferences/kubectl-binaries.tsx:29
msgid "Download mirror for kubectl"
msgstr ""
@ -945,7 +949,7 @@ msgstr "В кластере все в порядке"
#~ msgid "Excluded items with \"system:\" prefix"
#~ msgstr "За исключением объектов с префиксом “system:”"
#: src/renderer/components/dock/dock.tsx:116
#: src/renderer/components/dock/dock.tsx:98
msgid "Exit full size mode"
msgstr "Выйти из полного размера"
@ -986,7 +990,7 @@ msgstr "Финализаторы"
msgid "First seen"
msgstr "Увиденно впервые"
#: src/renderer/components/dock/dock.tsx:116
#: src/renderer/components/dock/dock.tsx:98
msgid "Fit to window"
msgstr "По размеру окна"
@ -1074,7 +1078,7 @@ msgstr "Скрыть"
msgid "High number of replicas may cause cluster performance issues"
msgstr "Большое количество реплик может вызвать проблемы с производительностью кластера"
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:81
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:88
msgid "Home"
msgstr "Ссылка"
@ -1129,7 +1133,7 @@ msgstr "Изображение"
msgid "ImagePullPolicy"
msgstr "ImagePullPolicy"
#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:80
#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:108
msgid "ImagePullSecrets"
msgstr "ImagePullSecrets"
@ -1154,8 +1158,8 @@ msgstr "Ingresses"
msgid "Init Containers"
msgstr "Контейнеры инициализации"
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:76
#: src/renderer/components/dock/install-chart.tsx:128
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:83
#: src/renderer/components/dock/install-chart.tsx:127
msgid "Install"
msgstr "Установить"
@ -1163,15 +1167,15 @@ msgstr "Установить"
msgid "Installation complete!"
msgstr "Установка завершена!"
#: src/renderer/components/dock/install-chart.tsx:128
#: src/renderer/components/dock/install-chart.tsx:127
msgid "Installing..."
msgstr "Установка.."
#: src/renderer/components/input/input.validators.ts:50
#: src/renderer/components/input/input_validators.ts:50
msgid "Invalid account ID"
msgstr "Неверный ID аккаунта"
#: src/renderer/components/input/input.validators.ts:16
#: src/renderer/components/input/input_validators.ts:16
msgid "Invalid number"
msgstr "Неверный номер"
@ -1232,13 +1236,13 @@ msgstr "Размер ключа"
msgid "Keys"
msgstr "Ключи"
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:87
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:94
msgid "Keywords"
msgstr "Ключевые слова"
#: src/renderer/components/+events/event-details.tsx:57
#: src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx:79
#: src/renderer/components/dock/edit-resource.tsx:89
#: src/renderer/components/dock/edit-resource.tsx:88
msgid "Kind"
msgstr "Тип"
@ -1250,7 +1254,7 @@ msgstr "Файл конфигурации"
msgid "Kubeconfig File"
msgstr "Файл конфигурации"
#: src/renderer/components/+preferences/kubectl-binaries.tsx:35
#: src/renderer/components/+preferences/kubectl-binaries.tsx:23
msgid "Kubectl Binary"
msgstr ""
@ -1344,7 +1348,7 @@ msgstr "Логи"
msgid "Logs copied to clipboard."
msgstr "Скопировано."
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:84
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:91
msgid "Maintainers"
msgstr "Создатели"
@ -1370,7 +1374,7 @@ msgstr "Макс. подов"
msgid "Max Unavailable"
msgstr ""
#: src/renderer/components/input/input.validators.ts:41
#: src/renderer/components/input/input_validators.ts:41
msgid "Maximum length is {maxLength}"
msgstr "Максимальная длина {maxLength}"
@ -1442,11 +1446,11 @@ msgstr ""
msgid "Min Pods"
msgstr "Мин. подов"
#: src/renderer/components/dock/dock.tsx:117
#: src/renderer/components/dock/dock.tsx:99
msgid "Minimize"
msgstr "Минимизировать"
#: src/renderer/components/input/input.validators.ts:36
#: src/renderer/components/input/input_validators.ts:36
msgid "Minimum length is {minLength}"
msgstr "Минимальная длина {minLength}"
@ -1455,7 +1459,7 @@ msgstr "Минимальная длина {minLength}"
msgid "Mount Options"
msgstr "Опции монтирования"
#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:84
#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:112
msgid "Mountable secrets"
msgstr "Монтируемые секреты"
@ -1511,7 +1515,7 @@ msgstr "Установки"
#: src/renderer/components/+workloads-replicasets/replicasets.tsx:50
#: src/renderer/components/+workloads-statefulsets/statefulsets.tsx:40
#: src/renderer/components/+workspaces/workspaces.tsx:130
#: src/renderer/components/dock/edit-resource.tsx:90
#: src/renderer/components/dock/edit-resource.tsx:89
#: src/renderer/components/kube-object/kube-object-meta.tsx:20
msgid "Name"
msgstr "Имя"
@ -1557,7 +1561,7 @@ msgstr ""
#: src/renderer/components/+workloads-jobs/jobs.tsx:38
#: src/renderer/components/+workloads-pods/pods.tsx:76
#: src/renderer/components/+workloads-statefulsets/statefulsets.tsx:41
#: src/renderer/components/dock/edit-resource.tsx:91
#: src/renderer/components/dock/edit-resource.tsx:90
#: src/renderer/components/dock/install-chart.tsx:122
#: src/renderer/components/dock/upgrade-chart.tsx:98
#: src/renderer/components/item-object-list/page-filters-select.tsx:57
@ -1601,7 +1605,7 @@ msgstr "Network Policies"
msgid "New logs since opening the dialog"
msgstr "Новые логи с момента открытия диалога"
#: src/renderer/components/dock/dock.tsx:104
#: src/renderer/components/dock/dock.tsx:86
msgid "New tab"
msgstr "Новая вкладка"
@ -1735,7 +1739,7 @@ msgstr "Ок"
msgid "Ok, got it!"
msgstr ""
#: src/renderer/components/dock/dock.tsx:117
#: src/renderer/components/dock/dock.tsx:99
msgid "Open"
msgstr "Открыть"
@ -1940,7 +1944,7 @@ msgstr "Комиссия"
msgid "Proxy is used only for non-cluster communication."
msgstr ""
#: src/renderer/components/+add-cluster/add-cluster.tsx:293
#: src/renderer/components/+add-cluster/add-cluster.tsx:294
msgid "Proxy settings"
msgstr ""
@ -2265,7 +2269,7 @@ msgstr ""
#: src/renderer/components/+config-secrets/secret-details.tsx:97
#: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:216
#: src/renderer/components/+workspaces/workspaces.tsx:132
#: src/renderer/components/dock/edit-resource.tsx:88
#: src/renderer/components/dock/edit-resource.tsx:87
msgid "Save"
msgstr "Сохранить"
@ -2319,6 +2323,10 @@ msgstr "Секрет"
msgid "Secret Name"
msgstr "Название секрета"
#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:72
msgid "Secret is not found"
msgstr ""
#: src/renderer/components/+config-secrets/add-secret-dialog.tsx:147
msgid "Secret name"
msgstr "Имя секрета"
@ -2473,7 +2481,7 @@ msgid "Shell"
msgstr "Командная строка"
#: src/renderer/components/+config-secrets/secret-details.tsx:93
#: src/renderer/components/+workloads-pods/pod-container-env.tsx:100
#: src/renderer/components/+workloads-pods/pod-container-env.tsx:101
#: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:215
#: src/renderer/components/drawer/drawer-param-toggler.tsx:19
msgid "Show"
@ -2581,12 +2589,12 @@ msgstr "Тип стратегии"
msgid "Sub-object"
msgstr "Суб-объект"
#: src/renderer/components/dock/info-panel.tsx:104
#: src/renderer/components/dock/info-panel.tsx:93
#: src/renderer/components/wizard/wizard.tsx:131
msgid "Submit"
msgstr "Отправить"
#: src/renderer/components/dock/info-panel.tsx:105
#: src/renderer/components/dock/info-panel.tsx:94
msgid "Submitting.."
msgstr "Применение.."
@ -2628,10 +2636,14 @@ msgstr ""
msgid "Terminal"
msgstr "Терминал"
#: src/renderer/components/dock/dock.tsx:107
#: src/renderer/components/dock/dock.tsx:89
msgid "Terminal session"
msgstr "Сессия терминала"
#: src/renderer/components/+preferences/kubectl-binaries.tsx:38
msgid "The path to the kubectl binary on the system."
msgstr ""
#: src/renderer/components/+workloads-pods/pod-logs-dialog.tsx:226
msgid "There are no logs available for container."
msgstr "Для контейнера нет логов."
@ -2640,11 +2652,11 @@ msgstr "Для контейнера нет логов."
msgid "There are no logs available."
msgstr "Логи отсутствуют."
#: src/renderer/components/input/input.validators.ts:6
#: src/renderer/components/input/input_validators.ts:6
msgid "This field is required"
msgstr "Это обязательное поле"
#: src/renderer/components/input/input.validators.ts:31
#: src/renderer/components/input/input_validators.ts:31
msgid "This field must be a valid path"
msgstr ""
@ -2664,7 +2676,7 @@ msgstr "Из"
msgid "To help us improve the product please report bugs to {slackLink} community or {githubLink} issues tracker."
msgstr "Чтобы помочь нам улучшить продукт пожалуйста отправляйте ошибки на {slackLink} сообщество или {githubLink} трекер ошибок."
#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:76
#: src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx:104
msgid "Tokens"
msgstr "Токены"
@ -2731,12 +2743,12 @@ msgstr "Обновить"
msgid "Updated"
msgstr "Обновлено"
#: src/renderer/components/dock/upgrade-chart.tsx:105
#: src/renderer/components/dock/upgrade-chart.tsx:104
msgid "Updating.."
msgstr "Обновление.."
#: src/renderer/components/+apps-releases/release-details.tsx:176
#: src/renderer/components/dock/upgrade-chart.tsx:105
#: src/renderer/components/dock/upgrade-chart.tsx:104
msgid "Upgrade"
msgstr "Обновить"
@ -2800,7 +2812,7 @@ msgstr "Конфигурация"
msgid "Verbs"
msgstr "Определения"
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:78
#: src/renderer/components/+apps-helm-charts/helm-chart-details.tsx:85
#: src/renderer/components/+apps-helm-charts/helm-charts.tsx:66
#: src/renderer/components/+apps-releases/release-details.tsx:185
#: src/renderer/components/+apps-releases/releases.tsx:91
@ -2867,11 +2879,11 @@ msgstr ""
msgid "Workspaces are used to organize number of clusters into logical groups."
msgstr ""
#: src/renderer/components/input/input.validators.ts:11
#: src/renderer/components/input/input_validators.ts:11
msgid "Wrong email format"
msgstr "Неверный формат электронной почты"
#: src/renderer/components/input/input.validators.ts:26
#: src/renderer/components/input/input_validators.ts:26
msgid "Wrong url format"
msgstr "Неверный url формат"
@ -2954,7 +2966,7 @@ msgstr ""
msgid "timestamps"
msgstr "временные метки"
#: src/renderer/components/dock/create-resource.tsx:55
#: src/renderer/components/dock/create-resource.tsx:57
msgid "{0, plural, one {Resource} other {Resources}}"
msgstr "{0, plural, one {Ресурс} few {Ресурсы} many {Ресурсы} other {Ресурсы}}"
@ -3013,6 +3025,6 @@ msgstr ""
"other {<2>Удалить <3>{selectedCount}</3> элементов <4>{selectedNames}</4> {tail}?</2>}\n"
"}"
#: src/renderer/components/dock/info-panel.tsx:99
#: src/renderer/components/dock/info-panel.tsx:88
msgid "{submitLabel} & Close"
msgstr "{submitLabel} и закрыть"

View File

@ -2,7 +2,7 @@
"name": "kontena-lens",
"productName": "Lens",
"description": "Lens - The Kubernetes IDE",
"version": "3.6.5-rc.1",
"version": "3.6.5",
"main": "static/build/main.js",
"copyright": "© 2020, Mirantis, Inc.",
"license": "MIT",
@ -61,7 +61,6 @@
]
},
"jest": {
"testRegex": ".*_(spec|test)\\.[jt]sx?$",
"collectCoverage": false,
"verbose": true,
"testEnvironment": "node",

View File

@ -1,9 +1,9 @@
import fs from "fs";
import mockFs from "mock-fs";
import yaml from "js-yaml";
import { Cluster } from "../main/cluster";
import { ClusterStore } from "./cluster-store";
import { workspaceStore } from "./workspace-store";
import { Cluster } from "../../main/cluster";
import { ClusterStore } from "../cluster-store";
import { workspaceStore } from "../workspace-store";
const testDataIcon = fs.readFileSync("test-data/cluster-store-migration-icon.png")
@ -12,7 +12,7 @@ console.log("") // fix bug
let clusterStore: ClusterStore;
describe("empty config", () => {
beforeAll(() => {
beforeEach(() => {
ClusterStore.resetInstance();
const mockOpts = {
'tmp': {
@ -24,109 +24,120 @@ describe("empty config", () => {
return clusterStore.load();
})
afterAll(() => {
afterEach(() => {
mockFs.restore();
})
it("adds new cluster to store", async () => {
const cluster = new Cluster({
id: "foo",
contextName: "minikube",
preferences: {
terminalCWD: "/tmp",
icon: "data:;base64,iVBORw0KGgoAAAANSUhEUgAAA1wAAAKoCAYAAABjkf5",
clusterName: "minikube"
},
kubeConfigPath: ClusterStore.embedCustomKubeConfig("foo", "fancy foo config"),
workspace: workspaceStore.currentWorkspaceId
describe("with foo cluster added", () => {
beforeEach(() => {
clusterStore.addCluster(
new Cluster({
id: "foo",
contextName: "minikube",
preferences: {
terminalCWD: "/tmp",
icon: "data:image/jpeg;base64, iVBORw0KGgoAAAANSUhEUgAAA1wAAAKoCAYAAABjkf5",
clusterName: "minikube"
},
kubeConfigPath: ClusterStore.embedCustomKubeConfig("foo", "fancy foo config"),
workspace: workspaceStore.currentWorkspaceId
})
);
})
it("adds new cluster to store", async () => {
const storedCluster = clusterStore.getById("foo");
expect(storedCluster.id).toBe("foo");
expect(storedCluster.preferences.terminalCWD).toBe("/tmp");
expect(storedCluster.preferences.icon).toBe("data:image/jpeg;base64, iVBORw0KGgoAAAANSUhEUgAAA1wAAAKoCAYAAABjkf5");
})
it("adds cluster to default workspace", () => {
const storedCluster = clusterStore.getById("foo");
expect(storedCluster.workspace).toBe("default");
})
it("removes cluster from store", async () => {
await clusterStore.removeById("foo");
expect(clusterStore.getById("foo")).toBeUndefined();
})
it("sets active cluster", () => {
clusterStore.setActive("foo");
expect(clusterStore.activeCluster.id).toBe("foo");
})
})
describe("with prod and dev clusters added", () => {
beforeEach(() => {
clusterStore.addCluster(
new Cluster({
id: "prod",
contextName: "prod",
preferences: {
clusterName: "prod"
},
kubeConfigPath: ClusterStore.embedCustomKubeConfig("prod", "fancy config"),
workspace: "workstation"
}),
new Cluster({
id: "dev",
contextName: "dev",
preferences: {
clusterName: "dev"
},
kubeConfigPath: ClusterStore.embedCustomKubeConfig("dev", "fancy config"),
workspace: "workstation"
})
)
})
it("check if store can contain multiple clusters", () => {
expect(clusterStore.hasClusters()).toBeTruthy();
expect(clusterStore.clusters.size).toBe(2);
});
clusterStore.addCluster(cluster);
const storedCluster = clusterStore.getById(cluster.id);
expect(storedCluster.id).toBe(cluster.id);
expect(storedCluster.preferences.terminalCWD).toBe(cluster.preferences.terminalCWD);
expect(storedCluster.preferences.icon).toBe(cluster.preferences.icon);
})
it("adds cluster to default workspace", () => {
const storedCluster = clusterStore.getById("foo");
expect(storedCluster.workspace).toBe("default");
})
it("gets clusters by workspaces", () => {
const wsClusters = clusterStore.getByWorkspaceId("workstation");
const defaultClusters = clusterStore.getByWorkspaceId("default");
expect(defaultClusters.length).toBe(0);
expect(wsClusters.length).toBe(2);
expect(wsClusters[0].id).toBe("prod");
expect(wsClusters[1].id).toBe("dev");
})
it("check if store can contain multiple clusters", () => {
const prodCluster = new Cluster({
id: "prod",
contextName: "prod",
preferences: {
clusterName: "prod"
},
kubeConfigPath: ClusterStore.embedCustomKubeConfig("prod", "fancy config"),
workspace: "workstation"
});
const devCluster = new Cluster({
id: "dev",
contextName: "dev",
preferences: {
clusterName: "dev"
},
kubeConfigPath: ClusterStore.embedCustomKubeConfig("dev", "fancy config"),
workspace: "workstation"
});
clusterStore.addCluster(prodCluster);
clusterStore.addCluster(devCluster);
expect(clusterStore.hasClusters()).toBeTruthy();
expect(clusterStore.clusters.size).toBe(3);
});
it("check if cluster's kubeconfig file saved", () => {
const file = ClusterStore.embedCustomKubeConfig("boo", "kubeconfig");
expect(fs.readFileSync(file, "utf8")).toBe("kubeconfig");
})
it("gets clusters by workspaces", () => {
const wsClusters = clusterStore.getByWorkspaceId("workstation");
const defaultClusters = clusterStore.getByWorkspaceId("default");
expect(defaultClusters.length).toBe(1);
expect(wsClusters.length).toBe(2);
expect(wsClusters[0].id).toBe("prod");
expect(wsClusters[1].id).toBe("dev");
})
it("check if reorderring works for same from and to", () => {
clusterStore.swapIconOrders("workstation", 1, 1)
it("sets active cluster", () => {
clusterStore.setActive("foo");
expect(clusterStore.activeCluster.id).toBe("foo");
})
const clusters = clusterStore.getByWorkspaceId("workstation");
expect(clusters[0].id).toBe("prod")
expect(clusters[0].preferences.iconOrder).toBe(0)
expect(clusters[1].id).toBe("dev")
expect(clusters[1].preferences.iconOrder).toBe(1)
})
it("check if cluster's kubeconfig file saved", () => {
const file = ClusterStore.embedCustomKubeConfig("boo", "kubeconfig");
expect(fs.readFileSync(file, "utf8")).toBe("kubeconfig");
})
it("check if reorderring works for different from and to", () => {
clusterStore.swapIconOrders("workstation", 0, 1)
it("check if reorderring works for same from and to", () => {
clusterStore.swapIconOrders("workstation", 1, 1)
const clusters = clusterStore.getByWorkspaceId("workstation");
expect(clusters[0].id).toBe("dev")
expect(clusters[0].preferences.iconOrder).toBe(0)
expect(clusters[1].id).toBe("prod")
expect(clusters[1].preferences.iconOrder).toBe(1)
})
const clusters = clusterStore.getByWorkspaceId("workstation");
expect(clusters[0].id).toBe("prod")
expect(clusters[0].preferences.iconOrder).toBe(0)
expect(clusters[1].id).toBe("dev")
expect(clusters[1].preferences.iconOrder).toBe(1)
});
it("check if after icon reordering, changing workspaces still works", () => {
clusterStore.swapIconOrders("workstation", 1, 1)
clusterStore.getById("prod").workspace = "default"
it("check if reorderring works for different from and to", () => {
clusterStore.swapIconOrders("workstation", 0, 1)
const clusters = clusterStore.getByWorkspaceId("workstation");
expect(clusters[0].id).toBe("dev")
expect(clusters[0].preferences.iconOrder).toBe(0)
expect(clusters[1].id).toBe("prod")
expect(clusters[1].preferences.iconOrder).toBe(1)
});
it("check if after icon reordering, changing workspaces still works", () => {
clusterStore.swapIconOrders("workstation", 1, 1)
clusterStore.getById("prod").workspace = "default"
expect(clusterStore.getByWorkspaceId("workstation").length).toBe(1);
expect(clusterStore.getByWorkspaceId("default").length).toBe(2);
});
it("removes cluster from store", async () => {
await clusterStore.removeById("foo");
expect(clusterStore.getById("foo")).toBeUndefined();
expect(clusterStore.getByWorkspaceId("workstation").length).toBe(1);
expect(clusterStore.getByWorkspaceId("default").length).toBe(1);
})
})
})

View File

@ -10,7 +10,7 @@ jest.mock("electron", () => {
}
})
import { UserStore } from "./user-store"
import { UserStore } from "../user-store"
import { SemVer } from "semver"
import electron from "electron"

View File

@ -10,7 +10,7 @@ jest.mock("electron", () => {
}
})
import { WorkspaceStore } from "./workspace-store"
import { WorkspaceStore } from "../workspace-store"
describe("workspace store tests", () => {
describe("for an empty config", () => {

View File

@ -18,6 +18,17 @@ export const clusterIpc = {
},
}),
setFrameId: createIpcChannel({
channel: "cluster:set-frame-id",
handle: (clusterId: ClusterId, frameId?: number) => {
const cluster = clusterStore.getById(clusterId);
if (cluster) {
if (frameId) cluster.frameId = frameId; // save cluster's webFrame.routingId to be able to send push-updates
return cluster.pushState();
}
},
}),
refresh: createIpcChannel({
channel: "cluster:refresh",
handle: (clusterId: ClusterId) => {

View File

@ -1,4 +1,4 @@
import { splitArray } from "./splitArray";
import { splitArray } from "../splitArray";
describe("split array on element tests", () => {
test("empty array", () => {
@ -16,7 +16,7 @@ describe("split array on element tests", () => {
test("one elements, in array", () => {
expect(splitArray([1], 1)).toStrictEqual([[], [], true]);
});
test("ten elements, in front array", () => {
expect(splitArray([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 0)).toStrictEqual([[], [1, 2, 3, 4, 5, 6, 7, 8, 9], true]);
});

View File

@ -59,6 +59,7 @@ export class Cluster implements ClusterModel {
@observable online = false;
@observable accessible = false;
@observable ready = false;
@observable reconnecting = false;
@observable disconnected = true;
@observable failureReason: string;
@observable nodes = 0;
@ -110,7 +111,7 @@ export class Cluster implements ClusterModel {
protected bindEvents() {
logger.info(`[CLUSTER]: bind events`, this.getMeta());
const refreshTimer = setInterval(() => this.online && this.refresh(), 30000); // every 30s
const refreshTimer = setInterval(() => !this.disconnected && this.refresh(), 30000); // every 30s
this.eventDisposers.push(
reaction(this.getState, this.pushState),

View File

@ -1,11 +1,11 @@
import { IKubeApiParsed, parseKubeApi } from "./kube-api-parse";
import { IKubeApiParsed, parseKubeApi } from "../kube-api-parse";
interface KubeApi_Parse_Test {
interface KubeApiParseTestData {
url: string;
expected: Required<IKubeApiParsed>;
}
const tests: KubeApi_Parse_Test[] = [
const tests: KubeApiParseTestData[] = [
{
url: "/apis/apiextensions.k8s.io/v1beta1/customresourcedefinitions/prometheuses.monitoring.coreos.com",
expected: {
@ -125,11 +125,10 @@ const tests: KubeApi_Parse_Test[] = [
},
];
describe.only("parseApi unit tests", () => {
for (const i in tests) {
const { url: tUrl, expected:tExpect} = tests[i];
test(`test #${parseInt(i)+1}`, () => {
expect(parseKubeApi(tUrl)).toStrictEqual(tExpect);
describe("parseApi unit tests", () => {
for (const { url, expected } of tests) {
test(`testing "${url}"`, () => {
expect(parseKubeApi(url)).toStrictEqual(expected);
});
}
});

View File

@ -13,7 +13,7 @@ import { LensApp } from "./lens-app";
import { getLensRuntime } from "../extensions/lens-runtime";
type AppComponent = React.ComponentType & {
init?(): void;
init?(): Promise<void>;
}
export async function bootstrap(App: AppComponent) {

View File

@ -4,7 +4,7 @@ import { Input } from "../../input";
import { observable, autorun } from "mobx";
import { observer, disposeOnUnmount } from "mobx-react";
import { SubTitle } from "../../layout/sub-title";
import { isRequired } from "../../input/input.validators";
import { isRequired } from "../../input/input_validators";
interface Props {
cluster: Cluster;
@ -33,7 +33,7 @@ export class ClusterNameSetting extends React.Component<Props> {
render() {
return (
<>
<SubTitle title="Cluster Name"/>
<SubTitle title="Cluster Name" />
<p>Define cluster name.</p>
<Input
theme="round-black"

View File

@ -3,7 +3,7 @@ import { observable, autorun } from "mobx";
import { observer, disposeOnUnmount } from "mobx-react";
import { Cluster } from "../../../../main/cluster";
import { Input } from "../../input";
import { isUrl } from "../../input/input.validators";
import { isUrl } from "../../input/input_validators";
import { SubTitle } from "../../layout/sub-title";
interface Props {
@ -33,7 +33,7 @@ export class ClusterProxySetting extends React.Component<Props> {
render() {
return (
<>
<SubTitle title="HTTP Proxy"/>
<SubTitle title="HTTP Proxy" />
<p>HTTP Proxy server. Used for communicating with Kubernetes API.</p>
<Input
theme="round-black"

View File

@ -14,14 +14,15 @@ import { podsStore } from "../+workloads-pods/pods.store";
import { clusterStore } from "./cluster.store";
import { eventStore } from "../+events/event.store";
import { isAllowedResource } from "../../../common/rbac";
import { getHostedCluster } from "../../../common/cluster-store";
@observer
export class Cluster extends React.Component {
private dependentStores = [nodesStore, podsStore];
private watchers = [
interval(60, () => clusterStore.getMetrics()),
interval(20, () => eventStore.loadAll())
interval(60, () => { getHostedCluster().available && clusterStore.getMetrics()}),
interval(20, () => { getHostedCluster().available && eventStore.loadAll()})
];
@computed get isLoaded() {

View File

@ -8,7 +8,7 @@ import { _i18n } from "../../i18n";
import { Dialog, DialogProps } from "../dialog";
import { Wizard, WizardStep } from "../wizard";
import { Input } from "../input";
import { systemName } from "../input/input.validators";
import { systemName } from "../input/input_validators";
import { IResourceQuotaValues, resourceQuotaApi } from "../../api/endpoints/resource-quota.api";
import { Select } from "../select";
import { Icon } from "../icon";
@ -73,7 +73,7 @@ export class AddQuotaDialog extends React.Component<Props> {
const isCount = quota.startsWith("count/");
const icon = isCompute ? "memory" : isStorage ? "storage" : isCount ? "looks_one" : "";
return {
label: icon ? <span className="nobr"><Icon material={icon}/> {quota}</span> : quota,
label: icon ? <span className="nobr"><Icon material={icon} /> {quota}</span> : quota,
value: quota,
};
});
@ -151,7 +151,7 @@ export class AddQuotaDialog extends React.Component<Props> {
/>
</div>
<SubTitle title={<Trans>Namespace</Trans>}/>
<SubTitle title={<Trans>Namespace</Trans>} />
<NamespaceSelect
value={this.namespace}
placeholder={_i18n._(t`Namespace`)}
@ -160,7 +160,7 @@ export class AddQuotaDialog extends React.Component<Props> {
onChange={({ value }) => this.namespace = value}
/>
<SubTitle title={<Trans>Values</Trans>}/>
<SubTitle title={<Trans>Values</Trans>} />
<div className="flex gaps align-center">
<Select
className="quota-select"
@ -191,7 +191,7 @@ export class AddQuotaDialog extends React.Component<Props> {
<div key={quota} className="quota flex gaps inline align-center">
<div className="name">{quota}</div>
<div className="value">{value}</div>
<Icon material="clear" onClick={() => this.quotas[quota] = ""}/>
<Icon material="clear" onClick={() => this.quotas[quota] = ""} />
</div>
)
})}

View File

@ -8,7 +8,7 @@ import { _i18n } from "../../i18n";
import { Dialog, DialogProps } from "../dialog";
import { Wizard, WizardStep } from "../wizard";
import { Input } from "../input";
import { systemName } from "../input/input.validators";
import { systemName } from "../input/input_validators";
import { Secret, secretsApi, SecretType } from "../../api/endpoints";
import { SubTitle } from "../layout/sub-title";
import { NamespaceSelect } from "../+namespaces/namespace-select";
@ -184,7 +184,7 @@ export class AddSecretDialog extends React.Component<Props> {
<Wizard header={header} done={this.close}>
<WizardStep contentClass="flow column" nextLabel={<Trans>Create</Trans>} next={this.createSecret}>
<div className="secret-name">
<SubTitle title={<Trans>Secret name</Trans>}/>
<SubTitle title={<Trans>Secret name</Trans>} />
<Input
autoFocus required
placeholder={_i18n._(t`Name`)}
@ -194,7 +194,7 @@ export class AddSecretDialog extends React.Component<Props> {
</div>
<div className="flex auto gaps">
<div className="secret-namespace">
<SubTitle title={<Trans>Namespace</Trans>}/>
<SubTitle title={<Trans>Namespace</Trans>} />
<NamespaceSelect
themeName="light"
value={namespace}
@ -202,7 +202,7 @@ export class AddSecretDialog extends React.Component<Props> {
/>
</div>
<div className="secret-type">
<SubTitle title={<Trans>Secret type</Trans>}/>
<SubTitle title={<Trans>Secret type</Trans>} />
<Select
themeName="light"
options={this.types}

View File

@ -10,7 +10,7 @@ import { Wizard, WizardStep } from "../wizard";
import { namespaceStore } from "./namespace.store";
import { Namespace } from "../../api/endpoints";
import { Input } from "../input";
import { systemName } from "../input/input.validators";
import { systemName } from "../input/input_validators";
import { Notifications } from "../notifications";
interface Props extends DialogProps {

View File

@ -1,6 +1,6 @@
import React, { useState } from 'react';
import { Trans } from '@lingui/macro';
import { isPath } from '../input/input.validators';
import { isPath } from '../input/input_validators';
import { Checkbox } from '../checkbox';
import { Input } from '../input';
import { SubTitle } from '../layout/sub-title';

View File

@ -10,7 +10,7 @@ import { Wizard, WizardStep } from "../wizard";
import { SubTitle } from "../layout/sub-title";
import { serviceAccountsStore } from "./service-accounts.store";
import { Input } from "../input";
import { systemName } from "../input/input.validators";
import { systemName } from "../input/input_validators";
import { NamespaceSelect } from "../+namespaces/namespace-select";
import { Notifications } from "../notifications";
import { showDetails } from "../../navigation";
@ -62,14 +62,14 @@ export class CreateServiceAccountDialog extends React.Component<Props> {
>
<Wizard header={header} done={this.close}>
<WizardStep nextLabel={<Trans>Create</Trans>} next={this.createAccount}>
<SubTitle title={<Trans>Account Name</Trans>}/>
<SubTitle title={<Trans>Account Name</Trans>} />
<Input
autoFocus required
placeholder={_i18n._(t`Enter a name`)}
validators={systemName}
value={name} onChange={v => this.name = v.toLowerCase()}
/>
<SubTitle title={<Trans>Namespace</Trans>}/>
<SubTitle title={<Trans>Namespace</Trans>} />
<NamespaceSelect
themeName="light"
value={namespace}

View File

@ -10,7 +10,7 @@ import { CronJob, cronJobApi, jobApi, Job } from "../../api/endpoints";
import { Notifications } from "../notifications";
import { cssNames } from "../../utils";
import { Input } from "../input";
import { systemName, maxLength } from "../input/input.validators";
import { systemName, maxLength } from "../input/input_validators";
interface Props extends Partial<DialogProps> {
}

View File

@ -12,7 +12,7 @@ import { Icon } from "../icon";
import { Input } from "../input";
import { cssNames, prevDefault } from "../../utils";
import { Button } from "../button";
import { isRequired, Validator } from "../input/input.validators";
import { isRequired, Validator } from "../input/input_validators";
@observer
export class Workspaces extends React.Component {

View File

@ -7,7 +7,6 @@
.theme-light & {
border: 1px solid gainsboro;
border-radius: $radius;
.ace_scrollbar {
@include custom-scrollbar(dark);
@ -19,7 +18,6 @@
width: inherit;
height: inherit;
font-size: 90%;
border-radius: $radius;
}
// --Theme customization

View File

@ -45,8 +45,8 @@ export class App extends React.Component {
const clusterId = getHostedClusterId();
logger.info(`[APP]: Init dashboard, clusterId=${clusterId}, frameId=${frameId}`)
await Terminal.preloadFonts()
await clusterIpc.activate.invokeFromRenderer(clusterId, frameId);
await getHostedCluster().whenReady; // cluster.refresh() is done at this point
await clusterIpc.setFrameId.invokeFromRenderer(clusterId, frameId);
await getHostedCluster().whenReady; // cluster.activate() is done at this point
}
get startURL() {

View File

@ -18,13 +18,6 @@ export class Badge extends React.Component<Props> {
{label}
{children}
</span>
{ /**
* This is a zero-width-space. It makes there be a word seperation
* between adjacent Badge's because <span>'s are ignored for browers'
* word detection algorithmns use for determining the extent of the
* text to highlight on multi-click sequences.
*/}
&#8203;
</>
}
}

View File

@ -41,6 +41,17 @@
}
}
&.outlined {
color: inherit;
background: transparent;
&.active,
&:focus {
color: inherit;
box-shadow: 0 0 0 1px inset;
}
}
&.big {
font-size: 2.2 * $unit;
border-radius: 50px;

View File

@ -9,6 +9,7 @@ export interface ButtonProps extends ButtonHTMLAttributes<any>, TooltipDecorator
primary?: boolean;
accent?: boolean;
plain?: boolean;
outlined?: boolean;
hidden?: boolean;
active?: boolean;
big?: boolean;
@ -23,12 +24,12 @@ export class Button extends React.PureComponent<ButtonProps, {}> {
private button: HTMLButtonElement;
render() {
const { className, waiting, label, primary, accent, plain, hidden, active, big, round, tooltip, children, ...props } = this.props;
const { className, waiting, label, primary, accent, plain, hidden, active, big, round, outlined, tooltip, children, ...props } = this.props;
const btnProps = props as Partial<ButtonProps>;
if (hidden) return null;
btnProps.className = cssNames('Button', className, {
waiting, primary, accent, plain, active, big, round,
waiting, primary, accent, plain, active, big, round, outlined
});
const btnContent: ReactNode = (

View File

@ -39,6 +39,7 @@ export class CreateResource extends React.Component<Props> {
create = async () => {
if (this.error) return;
if (!this.data.trim()) return; // do not save when field is empty
const resources = jsYaml.safeLoadAll(this.data)
.filter(v => !!v) // skip empty documents if "---" pasted at the beginning or end
const createdResources: string[] = [];
@ -54,12 +55,14 @@ export class CreateResource extends React.Component<Props> {
errors.forEach(Notifications.error);
if (!createdResources.length) throw errors[0];
}
return (
const successMessage = (
<p>
<Plural value={createdResources.length} one="Resource" other="Resources"/>{" "}
<Trans><b>{createdResources.join(", ")}</b> successfully created</Trans>
</p>
)
Notifications.ok(successMessage)
return successMessage
}
render() {
@ -67,11 +70,6 @@ export class CreateResource extends React.Component<Props> {
const { className } = this.props;
return (
<div className={cssNames("CreateResource flex column", className)}>
<EditorPanel
tabId={tabId}
value={data}
onChange={onChange}
/>
<InfoPanel
tabId={tabId}
error={error}
@ -79,6 +77,11 @@ export class CreateResource extends React.Component<Props> {
submitLabel={_i18n._(t`Create`)}
showNotifications={false}
/>
<EditorPanel
tabId={tabId}
value={data}
onChange={onChange}
/>
</div>
)
}

View File

@ -14,10 +14,6 @@
left: 0;
bottom: 0;
z-index: 100;
> .resizer {
display: none;
}
}
}
@ -65,24 +61,7 @@
}
}
.resizer {
$height: 12px;
position: absolute;
top: -$height / 2;
left: 0;
right: 0;
bottom: 100%;
height: $height;
cursor: row-resize;
z-index: 10;
&.disabled {
pointer-events: none;
}
}
.AceEditor {
border: none;
}
}
}

View File

@ -27,8 +27,8 @@ export class DockStore {
];
protected storage = createStorage("dock", {}); // keep settings in localStorage
public defaultTabId = this.initialTabs[0].id;
public minHeight = 100;
public readonly defaultTabId = this.initialTabs[0].id;
public readonly minHeight = 100;
@observable isOpen = false;
@observable fullSize = false;
@ -45,11 +45,12 @@ export class DockStore {
}
get maxHeight() {
const mainLayoutHeader = 40;
const mainLayoutTabs = 33;
const mainLayoutMargin = 16;
const dockTabs = 33;
return window.innerHeight - mainLayoutHeader - mainLayoutTabs - mainLayoutMargin - dockTabs;
const mainLayoutHeader = 40
const mainLayoutTabs = 33
const mainLayoutMargin = 16
const dockTabs = 33
const preferedMax = window.innerHeight - mainLayoutHeader - mainLayoutTabs - mainLayoutMargin - dockTabs
return Math.max(preferedMax, this.minHeight) // don't let max < min
}
constructor() {
@ -65,7 +66,6 @@ export class DockStore {
});
// adjust terminal height if window size changes
this.checkMaxHeight();
window.addEventListener("resize", throttle(this.checkMaxHeight, 250));
}
@ -171,20 +171,19 @@ export class DockStore {
@action
selectTab(tabId: TabId) {
const tab = this.getTabById(tabId);
this.selectedTabId = tab ? tab.id : null;
this.selectedTabId = this.getTabById(tabId)?.id ?? null;
}
@action
setHeight(height: number) {
this.height = Math.max(0, Math.min(height, this.maxHeight));
setHeight(height?: number) {
this.height = Math.max(this.minHeight, Math.min(height || this.minHeight, this.maxHeight));
}
@action
reset() {
this.selectedTabId = this.defaultTabId;
this.tabs.replace(this.initialTabs);
this.height = this.defaultHeight;
this.setHeight(this.defaultHeight);
this.close();
}
}

View File

@ -4,7 +4,7 @@ import React, { Fragment } from "react";
import { observer } from "mobx-react";
import { Trans } from "@lingui/macro";
import { autobind, cssNames, prevDefault } from "../../utils";
import { Draggable, DraggableState } from "../draggable";
import { ResizingAnchor, ResizeDirection } from "../resizing-anchor";
import { Icon } from "../icon";
import { Tabs } from "../tabs/tabs";
import { MenuItem } from "../menu";
@ -29,28 +29,8 @@ interface Props {
@observer
export class Dock extends React.Component<Props> {
onResizeStart = () => {
const { isOpen, open, setHeight, minHeight } = dockStore;
if (!isOpen) {
open();
setHeight(minHeight);
}
}
onResize = ({ offsetY }: DraggableState) => {
const { isOpen, close, height, setHeight, minHeight, defaultHeight } = dockStore;
const newHeight = height + offsetY;
if (height > newHeight && newHeight < minHeight) {
setHeight(defaultHeight);
close();
}
else if (isOpen) {
setHeight(newHeight);
}
}
onKeydown = (evt: React.KeyboardEvent<HTMLElement>) => {
const { close, closeTab, selectedTab, fullSize, toggleFillSize } = dockStore;
const { close, closeTab, selectedTab } = dockStore;
if (!selectedTab) return;
const { code, ctrlKey, shiftKey } = evt.nativeEvent;
if (shiftKey && code === "Escape") {
@ -71,13 +51,13 @@ export class Dock extends React.Component<Props> {
@autobind()
renderTab(tab: IDockTab) {
if (isTerminalTab(tab)) {
return <TerminalTab value={tab}/>
return <TerminalTab value={tab} />
}
if (isCreateResourceTab(tab) || isEditResourceTab(tab)) {
return <DockTab value={tab} icon="edit"/>
return <DockTab value={tab} icon="edit" />
}
if (isInstallChartTab(tab) || isUpgradeChartTab(tab)) {
return <DockTab value={tab} icon={<Icon svg="install"/>}/>
return <DockTab value={tab} icon={<Icon svg="install" />} />
}
}
@ -86,11 +66,11 @@ export class Dock extends React.Component<Props> {
if (!isOpen || !tab) return;
return (
<div className="tab-content" style={{ flexBasis: height }}>
{isCreateResourceTab(tab) && <CreateResource tab={tab}/>}
{isEditResourceTab(tab) && <EditResource tab={tab}/>}
{isInstallChartTab(tab) && <InstallChart tab={tab}/>}
{isUpgradeChartTab(tab) && <UpgradeChart tab={tab}/>}
{isTerminalTab(tab) && <TerminalWindow tab={tab}/>}
{isCreateResourceTab(tab) && <CreateResource tab={tab} />}
{isEditResourceTab(tab) && <EditResource tab={tab} />}
{isInstallChartTab(tab) && <InstallChart tab={tab} />}
{isUpgradeChartTab(tab) && <UpgradeChart tab={tab} />}
{isTerminalTab(tab) && <TerminalWindow tab={tab} />}
</div>
)
}
@ -104,11 +84,16 @@ export class Dock extends React.Component<Props> {
onKeyDown={this.onKeydown}
tabIndex={-1}
>
<Draggable
className={cssNames("resizer", { disabled: !hasTabs() })}
horizontal={false}
onStart={this.onResizeStart}
onEnter={this.onResize}
<ResizingAnchor
disabled={!hasTabs()}
getCurrentExtent={() => dockStore.height}
minExtent={dockStore.minHeight}
maxExtent={dockStore.maxHeight}
direction={ResizeDirection.VERTICAL}
onStart={dockStore.open}
onMinExtentSubceed={dockStore.close}
onMinExtentExceed={dockStore.open}
onDrag={dockStore.setHeight}
/>
<div className="tabs-container flex align-center" onDoubleClick={prevDefault(toggle)}>
<Tabs
@ -121,11 +106,11 @@ export class Dock extends React.Component<Props> {
<div className="dock-menu box grow">
<MenuActions usePortal triggerIcon={{ material: "add", className: "new-dock-tab", tooltip: <Trans>New tab</Trans> }} closeOnScroll={false}>
<MenuItem className="create-terminal-tab" onClick={() => createTerminalTab()}>
<Icon small svg="terminal" size={15}/>
<Icon small svg="terminal" size={15} />
<Trans>Terminal session</Trans>
</MenuItem>
<MenuItem className="create-resource-tab" onClick={() => createResourceTab()}>
<Icon small material="create"/>
<Icon small material="create" />
<Trans>Create resource</Trans>
</MenuItem>
</MenuActions>
@ -133,7 +118,7 @@ export class Dock extends React.Component<Props> {
{hasTabs() && (
<>
<Icon
material={fullSize ? "fullscreen_exit": "fullscreen"}
material={fullSize ? "fullscreen_exit" : "fullscreen"}
tooltip={fullSize ? <Trans>Exit full size mode</Trans> : <Trans>Fit to window</Trans>}
onClick={toggleFillSize}
/>

View File

@ -90,11 +90,6 @@ export class EditResource extends React.Component<Props> {
const { kind, getNs, getName } = resource;
return (
<div className={cssNames("EditResource flex column", this.props.className)}>
<EditorPanel
tabId={tabId}
value={draft}
onChange={onChange}
/>
<InfoPanel
tabId={tabId}
error={error}
@ -109,6 +104,11 @@ export class EditResource extends React.Component<Props> {
</div>
)}
/>
<EditorPanel
tabId={tabId}
value={draft}
onChange={onChange}
/>
</div>
)
}

View File

@ -2,7 +2,7 @@
@include hidden-scrollbar;
background: $dockInfoBackground;
border-top: 1px solid $dockInfoBorderColor;
border-bottom: 1px solid $dockInfoBorderColor;
padding: $padding $padding * 2;
flex-shrink: 0;
@ -12,37 +12,12 @@
> .controls {
white-space: nowrap;
&:empty {
display: none;
}
flex: 1 1;
&:not(:empty) + .info {
border: 1px solid $borderColor;
border-top: 0;
border-bottom: 0;
min-height: 25px;
padding-left: $padding;
padding-right: $padding;
}
}
> .info {
@include hidden-scrollbar;
min-width: 40px; // min-space for icon
flex: 1 1;
white-space: nowrap;
text-overflow: ellipsis;
> div {
padding-right: $padding;
flex-shrink: 0;
}
.Icon {
margin: 0;
margin-right: $padding;
}
}
}

View File

@ -38,35 +38,29 @@ export class InfoPanel extends Component<Props> {
showNotifications: true,
}
@observable.ref result: ReactNode;
@observable error = "";
@observable waiting = false;
componentDidMount() {
disposeOnUnmount(this, [
reaction(() => this.props.tabId, () => {
this.result = ""
this.error = ""
this.waiting = false
})
])
}
@computed get errorInfo() {
return this.error || this.props.error;
return this.props.error;
}
submit = async () => {
const { showNotifications } = this.props;
this.result = "";
this.error = "";
this.waiting = true;
try {
this.result = await this.props.submit()
if (showNotifications) Notifications.ok(this.result);
const result = await this.props.submit();
if (showNotifications) Notifications.ok(result);
} catch (error) {
this.error = error.toString();
if (showNotifications) Notifications.error(this.error);
if (showNotifications) Notifications.error(error.toString());
} finally {
this.waiting = false
}
@ -81,27 +75,15 @@ export class InfoPanel extends Component<Props> {
dockStore.closeTab(this.props.tabId);
}
renderInfo() {
if (!this.props.showInlineInfo) {
renderErrorIcon() {
if (!this.props.showInlineInfo || !this.errorInfo) {
return;
}
const { result, errorInfo } = this;
return (
<>
{result && (
<div className="success flex align-center">
<Icon material="done" />
<span>{result}</span>
</div>
)}
{errorInfo && (
<div className="error flex align-center">
<Icon material="error_outline" />
<span>{errorInfo}</span>
</div>
)}
</>
)
<div className="error">
<Icon material="error_outline" tooltip={this.errorInfo}/>
</div>
);
}
render() {
@ -114,11 +96,13 @@ export class InfoPanel extends Component<Props> {
{controls}
</div>
<div className="info flex gaps align-center">
{waiting ? <><Spinner /> {submittingMessage}</> : this.renderInfo()}
{waiting ? <><Spinner /> {submittingMessage}</> : this.renderErrorIcon()}
</div>
<Button plain label={<Trans>Cancel</Trans>} onClick={close} />
<Button
primary active
active
outlined={showSubmitClose}
primary={!showSubmitClose}// one button always should be primary (blue)
label={submitLabel}
onClick={submit}
disabled={isDisabled}

View File

@ -174,11 +174,6 @@ export class InstallChart extends Component<Props> {
return (
<div className="InstallChart flex column">
<EditorPanel
tabId={tabId}
value={values}
onChange={this.onValuesChange}
/>
<InfoPanel
tabId={tabId}
controls={panelControls}
@ -188,6 +183,11 @@ export class InstallChart extends Component<Props> {
submittingMessage={_i18n._(t`Installing...`)}
showSubmitClose={false}
/>
<EditorPanel
tabId={tabId}
value={values}
onChange={this.onValuesChange}
/>
</div>
);
}

View File

@ -121,6 +121,8 @@ export class Terminal {
}
fit = () => {
// Since this function is debounced we need to read this value as late as possible
if (!this.isActive) return;
this.fitAddon.fit();
const { cols, rows } = this.xterm;
this.api.sendTerminalSize(cols, rows);
@ -150,7 +152,6 @@ export class Terminal {
}
onResize = () => {
if (!this.isActive) return;
this.fitLazy();
this.focus();
}
@ -176,8 +177,8 @@ export class Terminal {
if (this.xterm.hasSelection()) return false;
break;
// Ctrl+W: prevent unexpected terminal tab closing, e.g. editing file in vim
// https://github.com/kontena/lens-app/issues/156#issuecomment-534906480
// Ctrl+W: prevent unexpected terminal tab closing, e.g. editing file in vim
// https://github.com/kontena/lens-app/issues/156#issuecomment-534906480
case "KeyW":
evt.preventDefault();
break;

View File

@ -106,18 +106,13 @@ export class UpgradeChart extends React.Component<Props> {
themeName="outlined"
value={version}
options={versions}
formatOptionLabel={this.formatVersionLabel}
formatOptionLabel={this.formatVersionLabel}
onChange={({ value }: SelectOption) => this.version = value}
/>
</div>
)
return (
<div className={cssNames("UpgradeChart flex column", className)}>
<EditorPanel
tabId={tabId}
value={value}
onChange={onChange}
/>
<InfoPanel
tabId={tabId}
error={error}
@ -126,6 +121,11 @@ export class UpgradeChart extends React.Component<Props> {
submittingMessage={_i18n._(t`Updating..`)}
controls={controlsAndInfo}
/>
<EditorPanel
tabId={tabId}
value={value}
onChange={onChange}
/>
</div>
)
}

View File

@ -1,5 +0,0 @@
body.dragging {
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
}

View File

@ -1,119 +0,0 @@
import "./draggable.scss";
import React from "react";
import { cssNames, IClassName, noop } from "../../utils";
import throttle from "lodash/throttle";
export interface DraggableEventHandler {
(state: DraggableState): void;
}
interface Props {
className?: IClassName;
vertical?: boolean;
horizontal?: boolean;
onStart?: DraggableEventHandler;
onEnter?: DraggableEventHandler;
onEnd?: DraggableEventHandler;
}
export interface DraggableState {
inited?: boolean;
changed?: boolean;
initX?: number;
initY?: number;
pageX?: number;
pageY?: number;
offsetX?: number;
offsetY?: number;
}
const initState: DraggableState = {
inited: false,
changed: false,
offsetX: 0,
offsetY: 0,
};
export class Draggable extends React.PureComponent<Props, DraggableState> {
public state = initState;
static IS_DRAGGING = "dragging"
static defaultProps: Props = {
vertical: true,
horizontal: true,
onStart: noop,
onEnter: noop,
onEnd: noop,
};
constructor(props: Props) {
super(props);
document.addEventListener("mousemove", this.onDrag);
document.addEventListener("mouseup", this.onDragEnd);
}
componentWillUnmount() {
document.removeEventListener("mousemove", this.onDrag);
document.removeEventListener("mouseup", this.onDragEnd);
}
onDragInit = (evt: React.MouseEvent<any>) => {
document.body.classList.add(Draggable.IS_DRAGGING);
const { pageX, pageY } = evt;
this.setState({
inited: true,
initX: pageX,
initY: pageY,
pageX: pageX,
pageY: pageY,
})
}
onDrag = throttle((evt: MouseEvent) => {
const { vertical, horizontal, onEnter, onStart } = this.props;
const { inited, pageX, pageY } = this.state;
const offsetX = pageX - evt.pageX;
const offsetY = pageY - evt.pageY;
let changed = false;
if (horizontal && offsetX !== 0) changed = true;
if (vertical && offsetY !== 0) changed = true;
if (inited && changed) {
const start = !this.state.changed;
const state = Object.assign({}, this.state, {
changed: true,
pageX: evt.pageX,
pageY: evt.pageY,
offsetX: offsetX,
offsetY: offsetY,
});
if (start) onStart(state);
this.setState(state, () => onEnter(state));
}
}, 100)
onDragEnd = (evt: MouseEvent) => {
const { pageX, pageY } = evt;
const { inited, changed, initX, initY } = this.state;
if (inited) {
document.body.classList.remove(Draggable.IS_DRAGGING);
this.setState(initState, () => {
if (!changed) return;
const state = Object.assign({}, this.state, {
offsetX: initX - pageX,
offsetY: initY - pageY,
});
this.props.onEnd(state);
});
}
}
render() {
const { className, children } = this.props;
return (
<div className={cssNames("Draggable", className)} onMouseDown={this.onDragInit}>
{children}
</div>
);
}
}

View File

@ -1 +0,0 @@
export * from './draggable'

View File

@ -48,6 +48,11 @@
float: left;
margin: $spacing;
&:after {
content: " ";
display: block;
}
&.disabled {
opacity: 0.5;
}

View File

@ -1,4 +1,4 @@
import { isEmail, systemName } from "./input.validators";
import { isEmail, systemName } from "../input_validators";
describe("input validation tests", () => {
describe("isEmail tests", () => {

View File

@ -3,7 +3,7 @@ import "./input.scss";
import React, { DOMAttributes, InputHTMLAttributes, TextareaHTMLAttributes } from "react";
import { autobind, cssNames, debouncePromise } from "../../utils";
import { Icon } from "../icon";
import { conditionalValidators, Validator } from "./input.validators";
import { conditionalValidators, Validator } from "./input_validators";
import isString from "lodash/isString"
import isFunction from "lodash/isFunction"
import isBoolean from "lodash/isBoolean"
@ -288,9 +288,9 @@ export class Input extends React.Component<InputProps, State> {
return (
<div className={className}>
<label className="input-area flex gaps align-center">
{isString(iconLeft) ? <Icon material={iconLeft}/> : iconLeft}
{multiLine ? <textarea {...inputProps as any}/> : <input {...inputProps as any}/>}
{isString(iconRight) ? <Icon material={iconRight}/> : iconRight}
{isString(iconLeft) ? <Icon material={iconLeft} /> : iconLeft}
{multiLine ? <textarea {...inputProps as any} /> : <input {...inputProps as any} />}
{isString(iconRight) ? <Icon material={iconRight} /> : iconRight}
</label>
<div className="input-info flex gaps">
{!valid && dirty && (

View File

@ -0,0 +1 @@
export * from './resizing-anchor'

View File

@ -0,0 +1,46 @@
body.resizing {
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
}
.ResizingAnchor {
$dimension: 12px;
position: absolute;
z-index: 10;
&.disabled {
display: none;
}
&.vertical {
left: 0;
right: 0;
cursor: row-resize;
height: $dimension;
&.leading {
top: -$dimension / 2;
}
&.trailing {
bottom: -$dimension / 2;
}
}
&.horizontal {
top: 0;
bottom: 0;
cursor: col-resize;
width: $dimension;
&.leading {
left: -$dimension / 2;
}
&.trailing {
right: -$dimension / 2;
}
}
}

View File

@ -0,0 +1,281 @@
import "./resizing-anchor.scss";
import React from "react";
import { action, observable } from "mobx";
import _ from "lodash"
import { findDOMNode } from "react-dom";
import { cssNames, noop } from "../../utils";
export enum ResizeDirection {
HORIZONTAL = "horizontal",
VERTICAL = "vertical",
}
/**
* ResizeSide is for customizing where the area should be rendered.
* That location is determined in conjunction with the `ResizeDirection` using the following table:
*
* +----------+------------+----------+
* | | HORIZONTAL | VERTICAL |
* +----------+------------+----------+
* | LEADING | left | top |
* +----------+------------+----------+
* | TRAILING | right | bottom |
* +----------+------------+----------+
*/
export enum ResizeSide {
LEADING = "leading",
TRAILING = "trailing",
}
/**
* ResizeGrowthDirection determines how the anchor interprets the drag.
*
* Because the origin of the screen is top left a drag from bottom to top
* results in a negative directional delta. However, if the component being
* dragged grows in the opposite direction, this needs to be compensated for.
*/
export enum ResizeGrowthDirection {
TOP_TO_BOTTOM = 1,
BOTTOM_TO_TOP = -1,
LEFT_TO_RIGHT = 1,
RIGHT_TO_LEFT = -1,
}
interface Props {
direction: ResizeDirection;
/**
* getCurrentExtent should return the current prominent dimension in the
* given resizing direction. Width for HORIZONTAL and height for VERTICAL
*/
getCurrentExtent: () => number;
disabled?: boolean;
placement?: ResizeSide;
growthDirection?: ResizeGrowthDirection;
// Ability to restrict which mouse buttons are allowed to resize this component
// Reference: https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
onlyButtons?: number;
// onStart is called when the ResizeAnchor is first clicked (mouse down)
onStart?: () => void;
// onEnd is called when the ResizeAnchor is released (mouse up)
onEnd?: () => void;
/**
* onDrag is called whenever there is a mousemove event. All calls will be
* bounded by matching `onStart` and `onEnd` calls.
*/
onDrag?: (newExtent: number) => void;
// onDoubleClick is called when the the ResizeAnchor is double clicked
onDoubleClick?: () => void;
/**
* The following two extents represent the max and min values set to `onDrag`
*/
maxExtent?: number;
minExtent?: number;
/**
* The following events are triggerred with respect to the above values.
* - The "__Exceed" call will be made when the unbounded extent goes from
* < the above to >= the above
* - The "__Subceed" call is similar but is triggered when the unbounded
* extent goes from >= the above to < the above.
*/
onMaxExtentExceed?: () => void;
onMaxExtentSubceed?: () => void;
onMinExtentSubceed?: () => void;
onMinExtentExceed?: () => void;
}
interface Position {
readonly pageX: number;
readonly pageY: number;
}
/**
* Return the direction delta, but ignore drags leading up to a moved item
* 1. `->|` => return `false`
* 2. `<-|` => return `directed length (M, P2)` (negative)
* 3. `-|>` => return `directed length (M, P2)` (positive)
* 4. `<|-` => return `directed length (M, P2)` (negative)
* 5. `|->` => return `directed length (M, P2)` (positive)
* 6. `|<-` => return `false`
* @param P1 the starting position on the number line
* @param P2 the ending position on the number line
* @param M a third point that determines if the delta is meaningful
* @returns the directional difference between including appropriate sign.
*/
function directionDelta(P1: number, P2: number, M: number): number | false {
const delta = Math.abs(M - P2)
if (P1 < M) {
if (P2 >= M) {
// case 3
return delta
}
if (P2 < P1) {
// case 2
return -delta
}
// case 1
return false
}
if (P2 < M) {
// case 4
return -delta
}
if (P1 < P2) {
// case 5
return delta
}
// case 6
return false
}
export class ResizingAnchor extends React.PureComponent<Props> {
@observable lastMouseEvent?: MouseEvent
@observable.ref ref?: React.RefObject<HTMLDivElement>;
static defaultProps = {
onStart: noop,
onDrag: noop,
onEnd: noop,
onMaxExtentExceed: noop,
onMinExtentExceed: noop,
onMinExtentSubceed: noop,
onMaxExtentSubceed: noop,
onDoubleClick: noop,
disabled: false,
growthDirection: ResizeGrowthDirection.BOTTOM_TO_TOP,
maxExtent: Number.POSITIVE_INFINITY,
minExtent: 0,
placement: ResizeSide.LEADING,
}
static IS_RESIZING = "resizing"
constructor(props: Props) {
super(props)
if (props.maxExtent < props.minExtent) {
throw new Error("maxExtent must be >= minExtent")
}
this.ref = React.createRef<HTMLDivElement>()
}
componentWillUnmount() {
document.removeEventListener("mousemove", this.onDrag)
document.removeEventListener("mouseup", this.onDragEnd)
}
@action
onDragInit = (event: React.MouseEvent) => {
const { onStart, onlyButtons } = this.props
if (typeof onlyButtons === "number" && onlyButtons !== event.buttons) {
return
}
document.addEventListener("mousemove", this.onDrag)
document.addEventListener("mouseup", this.onDragEnd)
document.body.classList.add(ResizingAnchor.IS_RESIZING)
this.lastMouseEvent = undefined
onStart()
}
calculateDelta(from: Position, to: Position): number | false {
const node = this.ref.current
if (!node) {
return false
}
const boundingBox = node.getBoundingClientRect()
if (this.props.direction === ResizeDirection.HORIZONTAL) {
const barX = Math.round(boundingBox.x + (boundingBox.width / 2))
return directionDelta(from.pageX, to.pageX, barX)
} else { // direction === ResizeDirection.VERTICAL
const barY = Math.round(boundingBox.y + (boundingBox.height / 2))
return directionDelta(from.pageY, to.pageY, barY)
}
}
onDrag = _.throttle((event: MouseEvent) => {
/**
* Some notes to help understand the following:
* - A browser's origin point is in the top left of the screen
* - X increases going from left to right
* - Y increases going from top to bottom
* - Since the resize bar should always be a rectangle, use its centre
* line (in the resizing direction) as the line for determining if
* the bar has "jumped around"
*
* Desire:
* - Always ignore movement in the non-resizing direction
* - Figure out how much the user has "dragged" the resize bar
* - If the resize bar has jumped around, compensate by ignoring movement
* in the resizing direction if it is moving "towards" the resize bar's
* new location.
*/
if (!this.lastMouseEvent) {
this.lastMouseEvent = event
return
}
const { maxExtent, minExtent, getCurrentExtent, growthDirection } = this.props
const { onDrag, onMaxExtentExceed, onMinExtentSubceed, onMaxExtentSubceed, onMinExtentExceed } = this.props
const delta = this.calculateDelta(this.lastMouseEvent, event)
// always update the last mouse event
this.lastMouseEvent = event
if (delta === false) {
return
}
const previousExtent = getCurrentExtent()
const unboundedExtent = previousExtent + (delta * growthDirection)
const boundedExtent = Math.round(Math.max(minExtent, Math.min(maxExtent, unboundedExtent)))
onDrag(boundedExtent)
if (previousExtent <= minExtent && minExtent <= unboundedExtent) {
onMinExtentExceed()
} else if (previousExtent >= minExtent && minExtent >= unboundedExtent) {
onMinExtentSubceed()
}
if (previousExtent <= maxExtent && maxExtent <= unboundedExtent) {
onMaxExtentExceed()
} else if (previousExtent >= maxExtent && maxExtent >= unboundedExtent) {
onMaxExtentSubceed()
}
}, 100)
@action
onDragEnd = (_event: MouseEvent) => {
this.props.onEnd()
document.removeEventListener("mousemove", this.onDrag)
document.removeEventListener("mouseup", this.onDragEnd)
document.body.classList.remove(ResizingAnchor.IS_RESIZING)
}
render() {
const { disabled, direction, placement, onDoubleClick } = this.props
return <div
ref={this.ref}
className={cssNames("ResizingAnchor", direction, placement, { disabled })}
onMouseDown={this.onDragInit}
onDoubleClick={onDoubleClick}
/>
}
}

View File

@ -28,6 +28,7 @@ export function withTooltip<T extends React.ComponentType<any>>(Target: T): T {
const tooltipProps: TooltipProps = {
targetId: tooltipId,
tooltipOnParentHover: tooltipOverrideDisabled,
formatters: { narrow: true },
...(isReactNode(tooltip) ? { children: tooltip } : tooltip),
};
targetProps.id = tooltipId;

View File

@ -18,7 +18,11 @@ if (ipcRenderer) {
}
export function navigate(location: LocationDescriptor) {
const currentLocation = navigation.getPath();
navigation.push(location);
if (currentLocation === navigation.getPath()) {
navigation.goBack(); // prevent sequences of same url in history
}
}
export interface IURLParams<P = {}, Q = {}> {

View File

@ -58,8 +58,8 @@
"colorVague": "#36393e",
"colorTerminated": "#4c5053",
"dockHeadBackground": "#2e3136",
"dockInfoBackground": "#111111",
"dockInfoBorderColor": "#4c5053",
"dockInfoBackground": "#1e2125",
"dockInfoBorderColor": "#303136",
"terminalBackground": "#000000",
"terminalForeground": "#ffffff",
"terminalCursor": "#ffffff",

View File

@ -1,4 +1,4 @@
import { formatDuration } from "./formatDuration";
import { formatDuration } from "../formatDuration";
const second = 1000;
const minute = 60 * second;
@ -9,34 +9,34 @@ const week = 7 * day;
describe("human format durations", () => {
test("long formatted durations less than 24 hours long shouldn't have a 'd' component", () => {
const res = formatDuration(19 * 60 * 60 * 1000, false);
expect(res).not.toContain("d");
expect(res).toBe("19h");
});
test("long formatted durations more than a week have correct day count", () => {
const res = formatDuration(2 * week + 2 * day, false);
expect(res).toBe("2w 2d");
});
test("durations > 1/2 week shouldn't show 1w has passed", () => {
const res = formatDuration(5 * 24 * 60 * 60 * 1000, false);
expect(res).not.toContain("w");
expect(res).toBe("5d");
});
test("durations shouldn't include zero magnitude parts", () => {
const res = formatDuration(6 * day + 2 * minute, false);
expect(res).not.toContain("h");
expect(res).toBe("6d 2m");
});
test("seconds are ignored unless they are significant (< 1m)", () => {
const insignificant = formatDuration(1 * hour + 2 * minute + 31 * second, false);
expect(insignificant).not.toContain("s");
expect(insignificant).toBe("1h 2m");

View File

@ -1,4 +1,4 @@
import { metricUnitsToNumber } from "./metricUnitsToNumber";
import { metricUnitsToNumber } from "../metricUnitsToNumber";
describe("metricUnitsToNumber tests", () => {
test("plain number", () => {

View File

@ -2,7 +2,13 @@
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!
## 3.6.5-rc.1 (current version)
## 3.6.5 (current version)
- Prevent drawer close when revealing secret value
- Fix app crash when CRD conditions were not present
- Add support for Stacklight prometheus metrics
- Terminal: set NO_PROXY env for localhost communication
- Fix CPU/Memory usage metrics when using prometheus operator
- Display last-applied-configuration annotation
- Fix Notifications not to block items not visually under them from being interacted with
- Fix side bar not to scroll after clicking on lower menu item
- Auto-select context if only one context is present in pasted Kubeconfig

View File

@ -8944,17 +8944,17 @@ mobx-observable-history@^1.0.3:
history "^4.10.1"
mobx "^5.15.4"
mobx-react-lite@2:
version "2.0.7"
resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-2.0.7.tgz#1bfb3b4272668e288047cf0c7940b14e91cba284"
integrity sha512-YKAh2gThC6WooPnVZCoC+rV1bODAKFwkhxikzgH18wpBjkgTkkR9Sb0IesQAH5QrAEH/JQVmy47jcpQkf2Au3Q==
mobx-react-lite@>=2.2.0:
version "2.2.2"
resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-2.2.2.tgz#87c217dc72b4e47b22493daf155daf3759f868a6"
integrity sha512-2SlXALHIkyUPDsV4VTKVR9DW7K3Ksh1aaIv3NrNJygTbhXe2A9GrcKHZ2ovIiOp/BXilOcTYemfHHZubP431dg==
mobx-react@^6.2.2:
version "6.2.2"
resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-6.2.2.tgz#45e8e7c4894cac8399bba0a91060d7cfb8ea084b"
integrity sha512-Us6V4ng/iKIRJ8pWxdbdysC6bnS53ZKLKlVGBqzHx6J+gYPYbOotWvhHZnzh/W5mhpYXxlXif4kL2cxoWJOplQ==
version "6.3.0"
resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-6.3.0.tgz#7d11799f988bbdadc49e725081993b18baa20329"
integrity sha512-C14yya2nqEBRSEiJjPkhoWJLlV8pcCX3m2JRV7w1KivwANJqipoiPx9UMH4pm6QNMbqDdvJqoyl+LqNu9AhvEQ==
dependencies:
mobx-react-lite "2"
mobx-react-lite ">=2.2.0"
mobx@^5.15.4:
version "5.15.4"