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:
commit
0f57ea91b1
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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 ""
|
||||
|
||||
@ -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} и закрыть"
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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", () => {
|
||||
@ -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) => {
|
||||
|
||||
@ -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]);
|
||||
});
|
||||
@ -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),
|
||||
|
||||
@ -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);
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -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) {
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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>
|
||||
)
|
||||
})}
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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';
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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> {
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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.
|
||||
*/}
|
||||
​
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 = (
|
||||
|
||||
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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}
|
||||
/>
|
||||
|
||||
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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}
|
||||
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
body.dragging {
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
export * from './draggable'
|
||||
@ -48,6 +48,11 @@
|
||||
float: left;
|
||||
margin: $spacing;
|
||||
|
||||
&:after {
|
||||
content: " ";
|
||||
display: block;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { isEmail, systemName } from "./input.validators";
|
||||
import { isEmail, systemName } from "../input_validators";
|
||||
|
||||
describe("input validation tests", () => {
|
||||
describe("isEmail tests", () => {
|
||||
@ -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 && (
|
||||
|
||||
1
src/renderer/components/resizing-anchor/index.ts
Normal file
1
src/renderer/components/resizing-anchor/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './resizing-anchor'
|
||||
46
src/renderer/components/resizing-anchor/resizing-anchor.scss
Normal file
46
src/renderer/components/resizing-anchor/resizing-anchor.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
281
src/renderer/components/resizing-anchor/resizing-anchor.tsx
Normal file
281
src/renderer/components/resizing-anchor/resizing-anchor.tsx
Normal 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}
|
||||
/>
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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 = {}> {
|
||||
|
||||
@ -58,8 +58,8 @@
|
||||
"colorVague": "#36393e",
|
||||
"colorTerminated": "#4c5053",
|
||||
"dockHeadBackground": "#2e3136",
|
||||
"dockInfoBackground": "#111111",
|
||||
"dockInfoBorderColor": "#4c5053",
|
||||
"dockInfoBackground": "#1e2125",
|
||||
"dockInfoBorderColor": "#303136",
|
||||
"terminalBackground": "#000000",
|
||||
"terminalForeground": "#ffffff",
|
||||
"terminalCursor": "#ffffff",
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { metricUnitsToNumber } from "./metricUnitsToNumber";
|
||||
import { metricUnitsToNumber } from "../metricUnitsToNumber";
|
||||
|
||||
describe("metricUnitsToNumber tests", () => {
|
||||
test("plain number", () => {
|
||||
@ -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
|
||||
|
||||
16
yarn.lock
16
yarn.lock
@ -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"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user