diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..e09efb4
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,17 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [0.4.4-custom] - 2025-08
+### Fixed
+- fixed server removal issues
+
+### Improved
+- Redesigned Refresh button as dropdown + icon combination
+
+### Added
+- Delete Index functionality with checkbox selection
+- filtering for index selector in Structured Query tab
diff --git a/Readme.md b/Readme.md
index 3794a32..6559b5c 100644
--- a/Readme.md
+++ b/Readme.md
@@ -1,4 +1,27 @@
-# Multi Elasticsearch Head
+# Multi Elasticsearch Head - Custom
+
+This is a fork of [Multi Elasticsearch Head](https://github.com/vorapoap/elasticsearch-head-chrome) with additional features for improved usability in multi-tenant Elasticsearch environments.
+
+## 🚀 New Features (Custom - 2025-08)
+
+### Index Filtering
+- **Enhanced Index Selector**: Added filtering capability to the index selector in the Structured Query tab
+- **Multi-tenant Support**: Improved usability for environments with tenant-prefixed index names
+
+### Local Setting
+
+* you can use python.
+```
+cd /Users/~~/Documents/elasticsearch-head-chrome && python -m http.server 8000
+```
+
+* If you need local chrome extension, then run build.py
+```
+python build.py
+```
+
+-----
+## Original Project
This is a major improvement over Elasticsearch Head extension.
* Add dropdown for multiple Elasticsearch Head end-points
@@ -21,7 +44,7 @@ This was created because ElasticSearch 5 removed the ability to run ElasticSearc
## Installation
-Head over to [Multi Elasticsearch Head](https://chrome.google.com/webstore/detail/multi-elasticsearch-head/cpmmilfkofbeimbmgiclohpodggeheim) page on the Chrome Web Store.
+Head over to [Multi Elasticsearch Head](https://chrome.google.com/webstore/detail/multi-elasticsearch-head/cpmmilfkofbeimbmgiclohpodggeheim) page on the Chrome Web Store.
## Usage
@@ -40,4 +63,13 @@ To make this more convenient to use (ie: without having to enter in the remote E
`ssh -N -L 29200:myeshost:9200 myusername@jumphost`
* then use `chrome-extension://ffmkiejjmecolpfloofpjologoblkegm/elasticsearch-head/index.html?base_uri=http://localhost:29200` to get to the remote ES cluster.
+## License
+
+This project is licensed under the MIT License - see the original project for details.
+
+## Acknowledgments
+
+- Original project: [Multi Elasticsearch Head](https://github.com/lyfeyaj/elasticsearch-head-chrome)
+- Based on: [Elasticsearch Head](https://github.com/mobz/elasticsearch-head)
+
diff --git a/build.py b/build.py
new file mode 100644
index 0000000..a7d269a
--- /dev/null
+++ b/build.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python3
+import os
+import shutil
+import zipfile
+from pathlib import Path
+
+print('Building Elasticsearch Head Chrome Extension(Custom)...')
+
+build_dir = 'build'
+if os.path.exists(build_dir):
+ shutil.rmtree(build_dir)
+os.makedirs(build_dir)
+
+files_to_copy = [
+ 'manifest.json',
+ 'src/background.js',
+ 'elasticsearch-head/index.html',
+ 'elasticsearch-head/app.js',
+ 'elasticsearch-head/app.css',
+ 'elasticsearch-head/vendor.js',
+ 'elasticsearch-head/vendor.css',
+ 'elasticsearch-head/i18n.js',
+ 'elasticsearch-head/onload.js',
+ 'elasticsearch-head/base/favicon.png',
+ 'elasticsearch-head/base/jquery.min.js',
+ 'elasticsearch-head/base/loading.gif',
+ 'elasticsearch-head/base/purecss.css',
+ 'elasticsearch-head/base/reset.css',
+ 'elasticsearch-head/fonts/fontawesome-webfont.eot',
+ 'elasticsearch-head/fonts/fontawesome-webfont.svg',
+ 'elasticsearch-head/fonts/fontawesome-webfont.ttf',
+ 'elasticsearch-head/fonts/fontawesome-webfont.woff',
+ 'elasticsearch-head/fonts/FontAwesome.otf',
+ 'elasticsearch-head/lang/en_strings.js',
+ 'elasticsearch-head/lang/fr_strings.js',
+ 'elasticsearch-head/lang/ja_strings.js',
+ 'elasticsearch-head/lang/pt_strings.js',
+ 'elasticsearch-head/lang/tr_strings.js',
+ 'elasticsearch-head/lang/zh_strings.js',
+ 'elasticsearch-head/lang/zh-TW_strings.js',
+ 'icons/esmhead_16x16.png',
+ 'icons/esmhead_32x32.png',
+ 'icons/esmhead_48x48.png',
+ 'icons/esmhead_128x128.png'
+]
+
+for file in files_to_copy:
+ source_path = file
+ dest_path = os.path.join(build_dir, file)
+
+ if os.path.exists(source_path):
+ dest_dir = os.path.dirname(dest_path)
+ if not os.path.exists(dest_dir):
+ os.makedirs(dest_dir, exist_ok=True)
+
+ shutil.copy2(source_path, dest_path)
+ print(f"✓ Copied: {file}")
+ else:
+ print(f"✗ Missing: {file}")
+
+print('\nBuild completed successfully!')
+print('Extension files are ready in the "build" directory.')
+print('You can now load the extension in Chrome from the build directory.')
+
+create_zip = input('\nDo you want to create a ZIP file for distribution? (y/n): ').lower().strip()
+if create_zip == 'y':
+ zip_filename = 'elasticsearch-head-custom-v0.4.4.zip'
+
+ with zipfile.ZipFile(zip_filename, 'w', zipfile.ZIP_DEFLATED) as zipf:
+ for root, dirs, files in os.walk(build_dir):
+ for file in files:
+ file_path = os.path.join(root, file)
+ arcname = os.path.relpath(file_path, build_dir)
+ zipf.write(file_path, arcname)
+ print(f"✓ Added to ZIP: {arcname}")
+
+ print(f'\nZIP file created: {zip_filename}')
+ print('This ZIP file can be uploaded to the Chrome Web Store or distributed manually.')
diff --git a/doc/esmhead_custom_sc.png b/doc/esmhead_custom_sc.png
new file mode 100644
index 0000000..9e3821c
Binary files /dev/null and b/doc/esmhead_custom_sc.png differ
diff --git a/doc/esmhead_custom_sc0.png b/doc/esmhead_custom_sc0.png
new file mode 100644
index 0000000..0b54275
Binary files /dev/null and b/doc/esmhead_custom_sc0.png differ
diff --git a/elasticsearch-head/app.css b/elasticsearch-head/app.css
index 0e1c139..651f929 100644
--- a/elasticsearch-head/app.css
+++ b/elasticsearch-head/app.css
@@ -114,38 +114,66 @@ TABLE.table H3 {
text-align: left;
}
-.uiSplitButton {
- white-space: nowrap;
+.uiRefreshButton {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ background: #96c6eb;
+ border: 1px solid #668dc6;
+ border-radius: 4px;
+ padding: 4px;
+ color: white;
}
-.uiSplitButton .uiButton:first-child {
- margin-right: 0;
- display: inline-block;
+.uiRefreshButton-select {
+ background: transparent;
+ border: none;
+ color: white;
+ font-size: 13px;
+ padding: 4px 8px;
+ cursor: pointer;
+ outline: none;
}
-.uiSplitButton .uiButton:first-child .uiButton-content {
- border-right-width: 1;
- border-right-color: #5296c7;
- border-top-right-radius: 0;
- border-bottom-right-radius: 0;
+.uiRefreshButton-select option {
+ background: #96c6eb;
+ color: white;
+}
+
+.uiRefreshButton-icon {
+ background: transparent;
+ border: none;
+ color: white;
+ cursor: pointer;
+ padding: 4px 6px;
+ border-radius: 3px;
+ transition: background-color 0.2s ease;
+ display: flex;
+ align-items: center;
+ justify-content: center;
}
-.uiSplitButton .uiMenuButton {
- margin-left: 0;
+.uiRefreshButton-icon:hover {
+ background: rgba(255, 255, 255, 0.2);
}
-.uiSplitButton .uiButton:last-child .uiButton-content {
- border-radius: 2px;
- border-left-width: 1;
- border-left-color: #96c6eb;
- border-top-left-radius: 0;
- border-bottom-left-radius: 0;
- height: 15px;
+.uiRefreshButton-icon.disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+.uiRefreshButton-icon.disabled:hover {
+ background: transparent;
+}
+
+.refresh-icon {
+ font-size: 14px;
+ font-weight: bold;
}
-.uiSplitButton .uiButton:last-child .uiButton-label {
- padding: 2px 17px 2px 6px;
- margin-left: -8px;
+.uiRefreshButton.auto-refresh {
+ background: #2575b7;
+ border-color: #1e5a8a;
}
.uiToolbar {
@@ -258,11 +286,14 @@ TABLE.table H3 {
position: absolute;
background: #96c6eb;
color: white;
+ z-index: 1000;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
.uiMenuPanel LI {
list-style: none;
border-bottom: 1px solid #668dc6;
+ padding: 0;
}
.uiMenuPanel LI:hover {
@@ -273,7 +304,13 @@ TABLE.table H3 {
border-bottom: 0;
}
+.uiMenuPanel-item {
+ cursor: pointer;
+}
+
.uiMenuPanel-label {
+ font-size: 13px;
+ line-height: 1.4;
white-space: nowrap;
padding: 2px 10px 2px 10px;
cursor: pointer;
@@ -764,6 +801,12 @@ SPAN.uiJsonPretty-boolean {
top: 1px;
}
+.uiFilterBrowser-row BUTTON {
+ height: 22px;
+ position: relative;
+ top: 1px;
+}
+
.uiHeader {
padding: 3px 10px;
}
@@ -827,6 +870,95 @@ SPAN.uiJsonPretty-boolean {
margin-right: 5px;
}
+.uiIndexSelector {
+ display: inline;
+}
+
+.uiIndexSelector-customSelect {
+ position: relative;
+ display: inline-block;
+}
+
+.uiIndexSelector-selectInput {
+ padding: 4px 8px;
+ border: 1px solid #ccc;
+ border-radius: 3px;
+ font-size: 100%;
+ width: auto;
+ min-width: 200px;
+ background: #fff;
+ box-sizing: border-box;
+}
+
+.uiIndexSelector-selectInput:focus {
+ outline: none;
+ border-color: #397fca;
+ box-shadow: 0 0 3px rgba(57, 127, 202, 0.3);
+}
+
+.uiIndexSelector-selectInput::placeholder {
+ color: #999;
+ font-style: italic;
+}
+
+.uiIndexSelector-dropdown {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ right: 0;
+ background: white;
+ border: 1px solid #ccc;
+ border-top: none;
+ border-radius: 0 0 4px 4px;
+ max-height: 200px;
+ overflow-y: auto;
+ z-index: 1000;
+ display: none;
+}
+
+.uiIndexSelector-option {
+ padding: 8px 10px;
+ cursor: pointer;
+ border-bottom: 1px solid #eee;
+}
+
+.uiIndexSelector-option:hover {
+ background-color: #f5f5f5;
+}
+
+.uiIndexSelector-option:last-child {
+ border-bottom: none;
+}
+
+.uiIndexOverview-deleteButton {
+ margin-left: 10px;
+ background-color: #dc3545;
+ border-color: #dc3545;
+ color: white;
+}
+
+.uiIndexOverview-deleteButton:hover {
+ background-color: #c82333;
+ border-color: #bd2130;
+}
+
+.uiIndexOverview-deleteButton:disabled {
+ background-color: #6c757d;
+ border-color: #6c757d;
+ cursor: not-allowed;
+}
+
+.uiIndexOverview-checkbox {
+ margin: 0;
+ cursor: pointer;
+}
+
+.uiIndexOverview-selectedCount {
+ margin-left: 10px;
+ color: #6c757d;
+ font-size: 0.9em;
+}
+
.modalDialog {
display: none;
position: fixed;
diff --git a/elasticsearch-head/app.js b/elasticsearch-head/app.js
index 4668094..2875ece 100644
--- a/elasticsearch-head/app.js
+++ b/elasticsearch-head/app.js
@@ -1843,13 +1843,17 @@
(function ($, app, i18n) {
var ui = app.ns("ui");
- ui.RefreshButton = ui.SplitButton.extend({
+ ui.RefreshButton = ui.AbstractWidget.extend({
defaults: {
timer: -1,
},
init: function (parent) {
- this.config.label = i18n.text("General.RefreshResults");
this._super(parent);
+ this.value = this.config.timer;
+ this.el = $(this._main_template());
+ this.selectEl = this.el.find(".uiRefreshButton-select");
+ this.iconEl = this.el.find(".uiRefreshButton-icon");
+ this.attach(parent);
this.set(this.config.timer);
},
set: function (value) {
@@ -1857,25 +1861,57 @@
window.clearInterval(this._timer);
if (this.value > 0) {
this._timer = window.setInterval(this._refresh_handler, this.value);
+ this.el.addClass("auto-refresh");
+ } else {
+ this.el.removeClass("auto-refresh");
}
},
- _click_handler: function () {
- this._refresh_handler();
+ disable: function () {
+ this.selectEl.prop("disabled", true);
+ this.iconEl.addClass("disabled");
},
- _select_handler: function (el, event) {
- this.set(event.value);
- this.fire("change", this);
+ enable: function () {
+ this.selectEl.prop("disabled", false);
+ this.iconEl.removeClass("disabled");
},
_refresh_handler: function () {
this.fire("refresh", this);
},
- _getItems: function () {
- return [
- { text: i18n.text("General.ManualRefresh"), value: -1 },
- { text: i18n.text("General.RefreshQuickly"), value: 100 },
- { text: i18n.text("General.Refresh5seconds"), value: 5000 },
- { text: i18n.text("General.Refresh1minute"), value: 60000 },
- ];
+ _select_handler: function (jEv) {
+ var value = parseInt($(jEv.target).val());
+ this.set(value);
+ this.fire("change", this);
+ },
+ _icon_click_handler: function () {
+ this._refresh_handler();
+ },
+ _main_template: function () {
+ return {
+ tag: "DIV",
+ cls: "uiRefreshButton",
+ children: [
+ {
+ tag: "SELECT",
+ cls: "uiRefreshButton-select",
+ onchange: this._select_handler,
+ children: [
+ { tag: "OPTION", value: "-1", text: i18n.text("General.ManualRefresh") },
+ { tag: "OPTION", value: "5000", text: i18n.text("General.Refresh5seconds") },
+ { tag: "OPTION", value: "15000", text: i18n.text("General.Refresh15seconds") },
+ { tag: "OPTION", value: "30000", text: i18n.text("General.Refresh30seconds") },
+ ],
+ },
+ {
+ tag: "BUTTON",
+ cls: "uiRefreshButton-icon",
+ type: "button",
+ onclick: this._icon_click_handler,
+ children: [
+ { tag: "SPAN", cls: "refresh-icon", text: "↻" }
+ ],
+ },
+ ],
+ };
},
});
})(this.jQuery, this.app, this.i18n);
@@ -2133,11 +2169,31 @@
_getPosition: function (jEv) {
var right = !!$(jEv.target).parents(".pull-right").length;
var parent = $(jEv.target).closest("BUTTON");
- return parent
+ var basePosition = parent
.vOffset()
- .addY(parent.vSize().y)
- .addX(right ? parent.vSize().x - this.el.vOuterSize().x : 0)
- .asOffset();
+ .addY(parent.vSize().y);
+
+ if (right) {
+ basePosition = basePosition.addX(parent.vSize().x - this.el.vOuterSize().x);
+ }
+
+ var windowWidth = $(window).width();
+ var windowHeight = $(window).height();
+ var menuWidth = this.el.outerWidth();
+ var menuHeight = this.el.outerHeight();
+
+ if (basePosition.x + menuWidth > windowWidth) {
+ basePosition = basePosition.addX(windowWidth - basePosition.x - menuWidth - 10);
+ }
+
+ if (basePosition.y + menuHeight > windowHeight) {
+ basePosition = basePosition.addY(-parent.vSize().y - menuHeight);
+ }
+
+ if (basePosition.x < 10) basePosition = basePosition.addX(10 - basePosition.x);
+ if (basePosition.y < 10) basePosition = basePosition.addY(10 - basePosition.y);
+
+ return basePosition.asOffset();
},
});
})(this.app);
@@ -4433,7 +4489,10 @@
this.prefs = services.Preferences.instance();
this.cluster = this.config.cluster;
this.el = $.joey(this._main_template());
- this._updateServerSelect();
+
+ var currentUri = this.cluster ? this.cluster.base_uri : null;
+ this._updateServerSelect(currentUri);
+
this.cluster.get("", this._node_handler);
$("body").bind("onconnect", this._addServer_handler);
},
@@ -4447,6 +4506,11 @@
_reconnect_handler: function () {
var base_uri = this.el.find(".uiClusterConnect-uri").val();
+
+ if (!base_uri) {
+ return;
+ }
+
var url;
if (base_uri.indexOf("?") !== -1) {
url = base_uri.substring(0, base_uri.indexOf("?") - 1);
@@ -4484,10 +4548,10 @@
var options = [];
var added = false;
if (cluster_list) {
- for (i in cluster_list) {
- name = cluster_list[i].name;
- uri = cluster_list[i].uri;
- version = cluster_list[i].version;
+ for (var i in cluster_list) {
+ var name = cluster_list[i].name;
+ var uri = cluster_list[i].uri;
+ var version = cluster_list[i].version;
if (!uri) continue;
if (uri.indexOf("?") != -1) continue;
options.push(this._optionCluster_template(uri, name, version));
@@ -4497,41 +4561,74 @@
}
}
if (select_uri && !added) {
- options.push(this._optionCluster_template(uri, select_name));
+ options.push(this._optionCluster_template(select_uri, select_name));
}
this.el.find(".uiClusterSelector-select").empty().append(this._selectCluster_template(options));
if (select_uri) {
this.el.find(".uiClusterSelector-select select").val(select_uri);
+ } else if (options.length > 0) {
+ var firstOption = options[0];
+ this.el.find(".uiClusterSelector-select select").val(firstOption.value);
+ } else {
+ this.el.find(".uiClusterSelector-select select").val("");
}
},
_addServer_handler: function (event, data) {
var base_uri = data.base_uri;
- //this.el.find(".uiClusterSelector-select select").val();//this.el.find(".uiClusterConnect-uri").val();
- cluster_list = this.prefs.get("app-cluster_list");
+ var cluster_list = this.prefs.get("app-cluster_list");
if (!cluster_list) {
cluster_list = {};
}
+
+ if (cluster_list[base_uri]) {
+ return;
+ }
+
cluster_list[data.base_uri] = {
uri: data.base_uri,
name: data.cluster_name,
version: data.cluster_version,
};
- for (x in cluster_list) {
- if (x[x.length - 1] != "/") delete cluster_list[x]; // Clean up URL without trailing space.
- }
+
this.prefs.set("app-cluster_list", cluster_list);
this._updateServerSelect(base_uri, data.cluster_name);
},
_removeServer_handler: function () {
- cluster_list = this.prefs.get("app-cluster_list");
- delete cluster_list[this.el.find(".uiClusterSelector-select select").val()];
- this.prefs.set("app-cluster_list", cluster_list);
+ var selectedUri = this.el.find(".uiClusterSelector-select select").val();
+
+ if (!selectedUri) {
+ return;
+ }
+
+ var cluster_list = this.prefs.get("app-cluster_list");
+
+ if (cluster_list && cluster_list[selectedUri]) {
+ delete cluster_list[selectedUri];
+ this.prefs.set("app-cluster_list", cluster_list);
+ } else {
+ return;
+ }
+
this._updateServerSelect();
- this._reconnect_handler();
+
+ var remainingServers = Object.keys(cluster_list || {});
+ if (remainingServers.length > 0) {
+ var firstServer = remainingServers[0];
+ this.el.find(".uiClusterSelector-select select").val(firstServer);
+ this.el.find(".uiClusterConnect-uri").val(firstServer);
+ this._reconnect_handler();
+ } else {
+ this.el.find(".uiClusterConnect-uri").val("");
+ }
},
_changeCluster_handler: function () {
var uri = this.el.find(".uiClusterSelector-select select").val();
+
+ if (!uri) {
+ return;
+ }
+
this.el.find(".uiClusterConnect-uri").val(uri);
this._reconnect_handler();
this._updateServerSelect(uri, i18n.text("AnyRequest.Requesting"));
@@ -4568,9 +4665,25 @@
urlObject.password = data.password || "";
}
- this.el.find(".uiClusterConnect-uri").val(urlObject.toString());
+ var uri = urlObject.toString();
+
+ var cluster_list = this.prefs.get("app-cluster_list");
+ if (!cluster_list) {
+ cluster_list = {};
+ }
+
+ cluster_list[uri] = {
+ uri: uri,
+ name: "New Server",
+ version: null
+ };
+
+ this.prefs.set("app-cluster_list", cluster_list);
+
+ this._updateServerSelect(uri, "New Server");
+
+ this.el.find(".uiClusterConnect-uri").val(uri);
this._reconnect_handler();
- this._updateServerSelect(uri, i18n.text("AnyRequest.Requesting"));
}
}.bind(this),
}).open();
@@ -5018,6 +5131,7 @@
this.el = $(this._main_template());
this.attach(parent);
this.cluster = this.config.cluster;
+ this.allIndices = [];
this.update();
},
update: function () {
@@ -5025,13 +5139,116 @@
},
_update_handler: function (data) {
- var options = [];
+ this.allIndices = [];
var index_names = Object.keys(data.indices).sort();
for (var i = 0; i < index_names.length; i++) {
- name = index_names[i];
- options.push(this._option_template(name, data.indices[name]));
+ var name = index_names[i];
+ this.allIndices.push({
+ name: name,
+ data: data.indices[name]
+ });
}
- this.el.find(".uiIndexSelector-select").empty().append(this._select_template(options));
+ setTimeout(function() {
+ this._initializeOptions();
+ }.bind(this), 0);
+ },
+
+ _updateOptions: function() {
+ var filterInput = this.el.find(".uiIndexSelector-selectInput");
+ var filterValue = filterInput.length > 0 ? filterInput.val() || "" : "";
+ filterValue = filterValue.toLowerCase();
+
+ var filteredIndices = this.allIndices.filter(function(index) {
+ return index.name.toLowerCase().includes(filterValue);
+ });
+
+ var dropdown = this.el.find(".uiIndexSelector-dropdown");
+ if (dropdown.length > 0) {
+ dropdown.empty();
+
+ var maxLength = 0;
+ var optionTexts = [];
+
+ for (var i = 0; i < filteredIndices.length; i++) {
+ var index = filteredIndices[i];
+ var optionText = i18n.text("IndexSelector.NameWithDocs", index.name, index.data.primaries.docs.count);
+ optionTexts.push(optionText);
+ maxLength = Math.max(maxLength, optionText.length);
+ }
+
+ if (maxLength > 0) {
+ var estimatedWidth = Math.max(200, maxLength * 8);
+ filterInput.css('width', estimatedWidth + 'px');
+ }
+
+ for (var j = 0; j < filteredIndices.length; j++) {
+ var index = filteredIndices[j];
+ var optionEl = $("
")
+ .addClass("uiIndexSelector-option")
+ .attr("data-value", index.name)
+ .text(optionTexts[j])
+ .click(function(name) {
+ return function() {
+ this._optionSelected_handler(name);
+ }.bind(this);
+ }.bind(this)(index.name));
+ dropdown.append(optionEl);
+ }
+ }
+ },
+
+ _filterChanged_handler: function() {
+ this._updateOptions();
+ },
+
+ _showDropdown_handler: function() {
+ this.el.find(".uiIndexSelector-dropdown").show();
+ },
+
+ _hideDropdown_handler: function() {
+ setTimeout(function() {
+ this.el.find(".uiIndexSelector-dropdown").hide();
+ }.bind(this), 200);
+ },
+
+ _initializeOptions: function() {
+ var options = [];
+ var maxLength = 0;
+
+ for (var i = 0; i < this.allIndices.length; i++) {
+ var index = this.allIndices[i];
+ var optionText = i18n.text("IndexSelector.NameWithDocs", index.name, index.data.primaries.docs.count);
+ maxLength = Math.max(maxLength, optionText.length);
+ options.push(this._option_template(index.name, index.data));
+ }
+
+ var selectContainer = this.el.find(".uiIndexSelector-select");
+ if (selectContainer.length > 0) {
+ selectContainer.empty().append(this._select_template(options));
+
+ if (maxLength > 0) {
+ var estimatedWidth = Math.max(200, maxLength * 8);
+ this.el.find(".uiIndexSelector-selectInput").css('width', estimatedWidth + 'px');
+ }
+
+ var self = this;
+ this.el.find(".uiIndexSelector-selectInput").on("keyup", function() {
+ self._filterChanged_handler();
+ });
+
+ this.el.find(".uiIndexSelector-selectInput").on("focus", function() {
+ self._showDropdown_handler();
+ });
+
+ this.el.find(".uiIndexSelector-selectInput").on("blur", function() {
+ self._hideDropdown_handler();
+ });
+ }
+ },
+
+ _optionSelected_handler: function(name) {
+ this.el.find(".uiIndexSelector-selectInput").val(name);
+ this.el.find(".uiIndexSelector-dropdown").hide();
this._indexChanged_handler();
},
@@ -5042,23 +5259,41 @@
children: i18n.complex("IndexSelector.SearchIndexForDocs", {
tag: "SPAN",
cls: "uiIndexSelector-select",
- }),
+ })
};
},
_indexChanged_handler: function () {
- this.fire("indexChanged", this.el.find("SELECT").val());
+ this.fire("indexChanged", this.el.find(".uiIndexSelector-selectInput").val());
},
_select_template: function (options) {
- return { tag: "SELECT", children: options, onChange: this._indexChanged_handler };
+ return {
+ tag: "DIV",
+ cls: "uiIndexSelector-customSelect",
+ children: [
+ {
+ tag: "INPUT",
+ type: "text",
+ cls: "uiIndexSelector-selectInput",
+ placeholder: i18n.text("IndexSelector.FilterPlaceholder", "Type to filter indexes...")
+ },
+ {
+ tag: "DIV",
+ cls: "uiIndexSelector-dropdown",
+ children: options
+ }
+ ]
+ };
},
_option_template: function (name, index) {
return {
- tag: "OPTION",
- value: name,
+ tag: "DIV",
+ cls: "uiIndexSelector-option",
+ "data-value": name,
text: i18n.text("IndexSelector.NameWithDocs", name, index.primaries.docs.count),
+ onclick: this._optionSelected_handler.bind(this, name)
};
},
});
@@ -5155,6 +5390,16 @@
this.cluster = this.config.cluster;
this._clusterState = this.config.clusterState;
this._clusterState.on("data", this._refresh_handler);
+
+ this.selectedIndices = [];
+ this.maxSelection = 5;
+
+ this._deleteButton = new ui.Button({
+ label: i18n.text("IndexOverview.DeleteSelected"),
+ cls: "uiIndexOverview-deleteButton",
+ onclick: this._deleteSelected_handler,
+ });
+
this.el = $(this._main_template());
this._refresh_handler();
},
@@ -5249,7 +5494,17 @@
{
tag: "TR",
children: [
- { tag: "TH" },
+ {
+ tag: "TH",
+ children: [
+ {
+ tag: "INPUT",
+ type: "checkbox",
+ cls: "uiIndexOverview-checkbox",
+ onclick: this._selectAll_handler
+ }
+ ]
+ },
{ tag: "TH", children: [{ tag: "STRONG", text: "Aliases" }] },
{ tag: "TH", children: [{ tag: "STRONG", text: "Creation Date" }] },
@@ -5272,6 +5527,18 @@
return {
tag: "TR",
children: [
+ {
+ tag: "TD",
+ children: [
+ {
+ tag: "INPUT",
+ type: "checkbox",
+ cls: "uiIndexOverview-checkbox",
+ "data-index": index.name,
+ onclick: this._selectIndex_handler.bind(this, index.name)
+ }
+ ]
+ },
{ tag: "TD", children: [{ tag: "STRONG", text: index.name }] },
{ tag: "TD", text: index.aliases ? index.aliases.join(",") : "" },
{ tag: "TD", text: index.creationDate },
@@ -5301,12 +5568,126 @@
label: i18n.text("ClusterOverview.NewIndex"),
onclick: this._newIndex_handler,
}),
+ this._deleteButton,
+ {
+ tag: "SPAN",
+ cls: "uiIndexOverview-selectedCount",
+ text: i18n.text("IndexOverview.SelectMax")
+ }
],
}),
{ tag: "DIV", cls: "uiIndexOverview-table", children: this._indexViewEl },
],
};
},
+
+ _selectIndex_handler: function(indexName, ev) {
+ var checkbox = ev.target;
+ var isChecked = checkbox.checked;
+
+ if (isChecked) {
+ if (this.selectedIndices.length >= this.maxSelection) {
+ checkbox.checked = false;
+ alert(i18n.text("IndexOverview.TooManySelected"));
+ return;
+ }
+ this.selectedIndices.push(indexName);
+ } else {
+ var index = this.selectedIndices.indexOf(indexName);
+ if (index > -1) {
+ this.selectedIndices.splice(index, 1);
+ }
+ }
+
+ this._updateDeleteButton();
+ },
+
+ _selectAll_handler: function(ev) {
+ var checkbox = ev.target;
+ var isChecked = checkbox.checked;
+ var indexCheckboxes = this.el.find("tbody input[type='checkbox']");
+
+ if (isChecked) {
+ var maxToSelect = Math.min(this.maxSelection, indexCheckboxes.length);
+ indexCheckboxes.each(function(i, cb) {
+ if (i < maxToSelect) {
+ cb.checked = true;
+ var indexName = $(cb).data("index");
+ if (this.selectedIndices.indexOf(indexName) === -1) {
+ this.selectedIndices.push(indexName);
+ }
+ }
+ }.bind(this));
+ } else {
+ indexCheckboxes.prop("checked", false);
+ this.selectedIndices = [];
+ }
+
+ this._updateDeleteButton();
+ },
+
+ _updateDeleteButton: function() {
+ this._deleteButton.el.prop("disabled", this.selectedIndices.length === 0);
+ },
+
+ _deleteSelected_handler: function() {
+ if (this.selectedIndices.length === 0) {
+ alert(i18n.text("IndexOverview.NoIndexSelected"));
+ return;
+ }
+
+ var selectedNames = this.selectedIndices.join(", ");
+
+ if (prompt(i18n.text("IndexOverview.DeleteMessage", i18n.text("Command.DELETE"), selectedNames)) === i18n.text("Command.DELETE")) {
+ this._performDelete();
+ }
+ },
+
+ _performDelete: function() {
+ var self = this;
+ var deletedCount = 0;
+ var failedCount = 0;
+ var totalCount = this.selectedIndices.length;
+
+ var deleteNext = function(index) {
+ if (index >= self.selectedIndices.length) {
+ if (failedCount === 0) {
+ alert(i18n.text("IndexOverview.DeleteSuccess"));
+ } else {
+ alert(i18n.text("IndexOverview.DeleteFailed"));
+ }
+ self.selectedIndices = [];
+ self._updateDeleteButton();
+
+ // self._clusterState.refresh();
+ return;
+ }
+
+ var indexName = self.selectedIndices[index];
+
+ setTimeout(function() {
+ self.cluster.delete(
+ encodeURIComponent(indexName),
+ null,
+ function(response) {
+ deletedCount++;
+ setTimeout(function() {
+ deleteNext(index + 1);
+ }, 2000);
+ },
+ function(error) {
+ console.error("Failed to delete index:", indexName, error);
+ failedCount++;
+ setTimeout(function() {
+ deleteNext(index + 1);
+ }, 2000);
+ }
+ );
+ }, 1000);
+ };
+
+ deleteNext(0);
+ },
});
})(this.jQuery, this.app, this.i18n);
diff --git a/elasticsearch-head/lang/en_strings.js b/elasticsearch-head/lang/en_strings.js
index 8e164e6..2615145 100644
--- a/elasticsearch-head/lang/en_strings.js
+++ b/elasticsearch-head/lang/en_strings.js
@@ -8,9 +8,9 @@ i18n.setKeys({
"General.CloseGlyph": "X",
"General.RefreshResults": "Refresh",
"General.ManualRefresh": "Manual Refresh",
- "General.RefreshQuickly": "Refresh quickly",
"General.Refresh5seconds": "Refresh every 5 seconds",
- "General.Refresh1minute": "Refresh every minute",
+ "General.Refresh15seconds": "Refresh every 15 seconds",
+ "General.Refresh30seconds": "Refresh every 30 seconds",
"AliasForm.AliasName": "Alias Name",
"AliasForm.NewAliasForIndex": "New Alias for {0}",
"AliasForm.DeleteAliasMessage": "type ''{0}'' to delete {1}. There is no undo",
@@ -56,6 +56,14 @@ i18n.setKeys({
"IndexOverview.PageTitle": "Indices Overview",
"IndexSelector.NameWithDocs": "{0} ({1} docs)",
"IndexSelector.SearchIndexForDocs": "Search {0} for documents where:",
+ "IndexSelector.FilterPlaceholder": "Type to filter indexes...",
+ "IndexOverview.DeleteSelected": "Delete Selected",
+ "IndexOverview.SelectMax": "Select up to 5 indexes",
+ "IndexOverview.DeleteMessage": "type ''{0}'' to delete {1}. There is no undo",
+ "IndexOverview.NoIndexSelected": "Please select at least one index to delete",
+ "IndexOverview.TooManySelected": "You can select up to 5 indexes at once",
+ "IndexOverview.DeleteSuccess": "Selected indexes have been deleted successfully. Please refresh to see the updated list.",
+ "IndexOverview.DeleteFailed": "Failed to delete some indexes",
"FilterBrowser.OutputType": "Output Results: {0}",
"FilterBrowser.OutputSize": "Number of Results: {0}",
"Header.ClusterHealth": "cluster health: {0} ({1} of {2})",
diff --git a/elasticsearch-head/lang/fr_strings.js b/elasticsearch-head/lang/fr_strings.js
index b17d712..062218f 100644
--- a/elasticsearch-head/lang/fr_strings.js
+++ b/elasticsearch-head/lang/fr_strings.js
@@ -8,9 +8,9 @@ i18n.setKeys({
// "General.CloseGlyph": "X",
"General.RefreshResults": "Rafraîchir",
"General.ManualRefresh": "Rafraîchissement manuel",
- "General.RefreshQuickly": "Rafraîchissement rapide",
"General.Refresh5seconds": "Rafraîchissement toutes les 5 secondes",
- "General.Refresh1minute": "Rafraîchissement toutes les minutes",
+ "General.Refresh15seconds": "Rafraîchissement toutes les 15 secondes",
+ "General.Refresh30seconds": "Rafraîchissement toutes les 30 secondes",
"AliasForm.AliasName": "Alias",
"AliasForm.NewAliasForIndex": "Nouvel Alias pour {0}",
"AliasForm.DeleteAliasMessage": "Entrez ''{0}'' pour effacer {1}. Attention, action irréversible.",
@@ -55,6 +55,14 @@ i18n.setKeys({
"IndexCommand.ShutdownMessage": "Entrez ''{0}'' pour éteindre {1}. Le noeud NE PEUT PAS être redémarré depuis cette interface.",
// "IndexSelector.NameWithDocs": "{0} ({1} docs)",
"IndexSelector.SearchIndexForDocs": "Chercher dans {0} les documents correspondant à",
+ "IndexSelector.FilterPlaceholder": "Tapez pour filtrer les index...",
+ "IndexOverview.DeleteSelected": "Supprimer la Sélection",
+ "IndexOverview.SelectMax": "Sélectionnez jusqu'à 5 index",
+ "IndexOverview.DeleteMessage": "Entrez ''{0}'' pour effacer {1}. Attention, action irréversible.",
+ "IndexOverview.NoIndexSelected": "Veuillez sélectionner au moins un index à supprimer",
+ "IndexOverview.TooManySelected": "Vous pouvez sélectionner jusqu'à 5 index à la fois",
+ "IndexOverview.DeleteSuccess": "Les index sélectionnés ont été supprimés avec succès. Veuillez actualiser pour voir la liste mise à jour.",
+ "IndexOverview.DeleteFailed": "Échec de la suppression de certains index",
"FilterBrowser.OutputType": "Format d'affichage des résultats {0}",
"FilterBrowser.OutputSize": "Nombre de Résultats: {0}",
"Header.ClusterHealth": "Santé du cluster: {0} ({1} {2})",
diff --git a/elasticsearch-head/lang/ja_strings.js b/elasticsearch-head/lang/ja_strings.js
index 171b0c6..150c4f4 100644
--- a/elasticsearch-head/lang/ja_strings.js
+++ b/elasticsearch-head/lang/ja_strings.js
@@ -8,9 +8,9 @@ i18n.setKeys({
// "General.CloseGlyph": "X",
"General.RefreshResults": "リフレッシュ",
"General.ManualRefresh": "マニュアルモード",
- "General.RefreshQuickly": "クイックモード",
"General.Refresh5seconds": "5秒毎にリフレッシュ",
- "General.Refresh1minute": "1分毎にリフレッシュ",
+ "General.Refresh15seconds": "15秒毎にリフレッシュ",
+ "General.Refresh30seconds": "30秒毎にリフレッシュ",
"AliasForm.AliasName": "エイリアス名",
"AliasForm.NewAliasForIndex": "{0} の新しいエイリアス",
"AliasForm.DeleteAliasMessage": "インデックス ''{1}'' を削除するために ''{0}'' とタイプして下さい. この操作は undo できません.",
@@ -56,6 +56,14 @@ i18n.setKeys({
"IndexOverview.PageTitle": "インデックスのOverview",
// "IndexSelector.NameWithDocs": "{0} ({1} docs)",
"IndexSelector.SearchIndexForDocs": "Search {0} for documents where:",
+ "IndexSelector.FilterPlaceholder": "インデックスをフィルタするには入力してください...",
+ "IndexOverview.DeleteSelected": "選択削除",
+ "IndexOverview.SelectMax": "最大5つのインデックスを選択してください",
+ "IndexOverview.DeleteMessage": "インデックス ''{1}'' を削除するために ''{0}'' とタイプして下さい. この操作は undo できません.",
+ "IndexOverview.NoIndexSelected": "削除するインデックスを少なくとも1つ選択してください",
+ "IndexOverview.TooManySelected": "一度に最大5つのインデックスを選択できます",
+ "IndexOverview.DeleteSuccess": "選択されたインデックスが正常に削除されました。更新されたリストを表示するには更新してください。",
+ "IndexOverview.DeleteFailed": "一部のインデックスの削除に失敗しました",
"FilterBrowser.OutputType": "結果の出力形式: {0} ",
"FilterBrowser.OutputSize": "結果の取得サイズ: {0} ",
"Header.ClusterHealth": "cluster health: {0} ({1} of {2})",
diff --git a/elasticsearch-head/lang/pt_strings.js b/elasticsearch-head/lang/pt_strings.js
index d1066ba..0df0724 100644
--- a/elasticsearch-head/lang/pt_strings.js
+++ b/elasticsearch-head/lang/pt_strings.js
@@ -8,9 +8,9 @@ i18n.setKeys({
"General.CloseGlyph": "X",
"General.RefreshResults": "Atualizar",
"General.ManualRefresh": "Atualização Manual",
- "General.RefreshQuickly": "Atualização rápida",
"General.Refresh5seconds": "Atualização a cada 5 segundos",
- "General.Refresh1minute": "Atualização a cada minuto",
+ "General.Refresh15seconds": "Atualização a cada 15 segundos",
+ "General.Refresh30seconds": "Atualização a cada 30 segundos",
"AliasForm.AliasName": "Apelido",
"AliasForm.NewAliasForIndex": "Novo apelido para {0}",
"AliasForm.DeleteAliasMessage": "digite ''{0}'' para deletar {1}. Não há como voltar atrás",
@@ -32,7 +32,7 @@ i18n.setKeys({
"Browser.ResultSourcePanelTitle": "Resultado",
"Command.DELETE": "DELETAR",
"Command.SHUTDOWN": "DESLIGAR",
- "Command.DeleteAliasMessage": "Remover apelido?",
+ "Command.DeleteAliasMessage": "Remover apelido ''{0}'' do índice ''{1}''?",
"ClusterOverView.IndexName": "Nome do índice",
"ClusterOverview.NumShards": "Número de Shards",
"ClusterOverview.NumReplicas": "Número de Réplicas",
@@ -54,13 +54,24 @@ i18n.setKeys({
"IndexCommand.TextToAnalyze": "Texto para analizar",
"IndexCommand.ShutdownMessage": "digite ''{0}'' para desligar {1}. Nó NÃO PODE ser reiniciado à partir dessa interface",
"IndexOverview.PageTitle": "Visão geral dos índices",
- "IndexSelector.NameWithDocs": "{0} ({1} documentoss)",
+ "IndexSelector.NameWithDocs": "{0} ({1} documentos)",
"IndexSelector.SearchIndexForDocs": "Busca {0} por documentos onde:",
+ "IndexSelector.FilterPlaceholder": "Digite para filtrar índices...",
+ "IndexOverview.DeleteSelected": "Deletar Selecionados",
+ "IndexOverview.SelectMax": "Selecione até 5 índices",
+ "IndexOverview.DeleteMessage": "digite ''{0}'' para deletar {1}. Não há como voltar atrás",
+ "IndexOverview.NoIndexSelected": "Por favor, selecione pelo menos um índice para deletar",
+ "IndexOverview.TooManySelected": "Você pode selecionar até 5 índices de uma vez",
+ "IndexOverview.DeleteSuccess": "Índices selecionados foram deletados com sucesso. Por favor, atualize para ver a lista atualizada.",
+ "IndexOverview.DeleteFailed": "Falha ao deletar alguns índices",
"FilterBrowser.OutputType": "Resultados: {0}",
"FilterBrowser.OutputSize": "Número de Resultados: {0}",
"Header.ClusterHealth": "saúde do cluster: {0} ({1} {2})",
"Header.ClusterNotConnected": "saúde do cluster: não conectado",
"Header.Connect": "Conectar",
+ "Header.NewConnect" : "Novo",
+ "Header.ServerListAdd": "+",
+ "Header.ServerListRemove": "-",
"Nav.AnyRequest": "Qualquer requisição",
"Nav.Browser": "Navegador",
"Nav.ClusterHealth": "Saúde do Cluster",
@@ -103,10 +114,17 @@ i18n.setKeys({
"Sort.ByName": "Por nome",
"Sort.ByAddress": "Por endereço",
"Sort.ByType": "Por tipo",
+ "Preference.SortIndices": "Ordenar Índices",
+ "SortIndices.Descending": "Decrescente",
+ "SortIndices.Ascending": "Crescente",
"Preference.ViewAliases": "Ver Alias",
"ViewAliases.Grouped": "Agrupado",
"ViewAliases.List": "Lista",
"ViewAliases.None": "Nenhum",
+ "Preference.ViewIndexSelect": "Ver Índices",
+ "ViewIndexSelect.System": "Sistema",
+ "ViewIndexSelect.Normal": "Normal",
+ "ViewIndexSelect.Both": "Ambos",
"Overview.IndexFilter": "Filtar Índice",
"TableResults.Summary": "Buscado {0} de {1} shards. {2} resultados. {3} segundos",
"QueryFilter.AllIndices": "Todos os Índices",
diff --git a/elasticsearch-head/lang/tr_strings.js b/elasticsearch-head/lang/tr_strings.js
index 6b4cda3..776c304 100644
--- a/elasticsearch-head/lang/tr_strings.js
+++ b/elasticsearch-head/lang/tr_strings.js
@@ -8,8 +8,9 @@ i18n.setKeys({
"General.CloseGlyph": "X",
"General.RefreshResults": "Yenile",
"General.ManualRefresh": "Manuel Yenileme",
- "General.RefreshQuickly": "Hızlı yenile",
"General.Refresh5seconds": "5 saniyede bir yenile",
+ "General.Refresh15seconds": "15 saniyede bir yenile",
+ "General.Refresh30seconds": "30 saniyede bir yenile",
"General.Refresh1minute": "Her dakika yenile",
"AliasForm.AliasName": "Alternatif İsim",
"AliasForm.NewAliasForIndex": "{0} için yeni alternatif isim",
@@ -56,6 +57,14 @@ i18n.setKeys({
"IndexOverview.PageTitle": "Indeksler Genel Bakış",
"IndexSelector.NameWithDocs": "{0} ({1} döküman)",
"IndexSelector.SearchIndexForDocs": "{0} indeksinde ara:",
+ "IndexSelector.FilterPlaceholder": "İndeksleri filtrelemek için yazın...",
+ "IndexOverview.DeleteSelected": "Seçilenleri Sil",
+ "IndexOverview.SelectMax": "En fazla 5 indeks seçin",
+ "IndexOverview.DeleteMessage": "{1} silmek için ''{0}'' . Geriye dönüş yoktur.",
+ "IndexOverview.NoIndexSelected": "Silmek için en az bir indeks seçin",
+ "IndexOverview.TooManySelected": "Bir seferde en fazla 5 indeks seçebilirsiniz",
+ "IndexOverview.DeleteSuccess": "Seçilen indeksler başarıyla silindi. Güncellenmiş listeyi görmek için yenileyin.",
+ "IndexOverview.DeleteFailed": "Bazı indeksler silinemedi",
"FilterBrowser.OutputType": "Sonuç Formatı: {0}",
"FilterBrowser.OutputSize": "Sonuç Sayısı: {0}",
"Header.ClusterHealth": "Küme Durumu: {0} ({1} de {2})",
diff --git a/elasticsearch-head/lang/zh-TW_strings.js b/elasticsearch-head/lang/zh-TW_strings.js
index 5797deb..f34dad1 100644
--- a/elasticsearch-head/lang/zh-TW_strings.js
+++ b/elasticsearch-head/lang/zh-TW_strings.js
@@ -8,9 +8,9 @@ i18n.setKeys({
"General.CloseGlyph": "X",
"General.RefreshResults": "更新",
"General.ManualRefresh": "手動更新",
- "General.RefreshQuickly": "快速更新",
"General.Refresh5seconds": "每5秒更新",
- "General.Refresh1minute": "每1分鐘更新",
+ "General.Refresh15seconds": "每15秒更新",
+ "General.Refresh30seconds": "每30秒更新",
"AliasForm.AliasName": "别名",
"AliasForm.NewAliasForIndex": "為 {0} 建立新别名",
"AliasForm.DeleteAliasMessage": "輸入 ''{0}'' 删除 {1}. 此操作無法恢復",
diff --git a/elasticsearch-head/lang/zh_strings.js b/elasticsearch-head/lang/zh_strings.js
index cc5c272..22694b3 100644
--- a/elasticsearch-head/lang/zh_strings.js
+++ b/elasticsearch-head/lang/zh_strings.js
@@ -8,9 +8,9 @@ i18n.setKeys({
"General.CloseGlyph": "X",
"General.RefreshResults": "刷新",
"General.ManualRefresh": "手动刷新",
- "General.RefreshQuickly": "快速刷新",
"General.Refresh5seconds": "每5秒刷新",
- "General.Refresh1minute": "每1分钟刷新",
+ "General.Refresh15seconds": "每15秒刷新",
+ "General.Refresh30seconds": "每30秒刷新",
"AliasForm.AliasName": "别名",
"AliasForm.NewAliasForIndex": "为 {0} 创建新别名",
"AliasForm.DeleteAliasMessage": "输入 ''{0}'' 删除 {1}. 此操作无法恢复",
@@ -56,6 +56,14 @@ i18n.setKeys({
"IndexOverview.PageTitle": "索引概览",
"IndexSelector.NameWithDocs": "{0} ({1} 个文档)",
"IndexSelector.SearchIndexForDocs": "搜索 {0} 的文档, 查询条件:",
+ "IndexSelector.FilterPlaceholder": "输入以过滤索引...",
+ "IndexOverview.DeleteSelected": "删除选中",
+ "IndexOverview.SelectMax": "最多选择5个索引",
+ "IndexOverview.DeleteMessage": "输入 ''{0}'' 删除 {1}. 此操作无法恢复",
+ "IndexOverview.NoIndexSelected": "请至少选择一个要删除的索引",
+ "IndexOverview.TooManySelected": "一次最多可以选择5个索引",
+ "IndexOverview.DeleteSuccess": "选中的索引已成功删除。请刷新以查看更新后的列表。",
+ "IndexOverview.DeleteFailed": "删除某些索引失败",
"FilterBrowser.OutputType": "返回格式: {0}",
"FilterBrowser.OutputSize": "显示数量: {0}",
"Header.ClusterHealth": "集群健康值: {0} ({1} of {2})",
diff --git a/icons/esmhead_128x128.png b/icons/esmhead_128x128.png
index 705d69c..f178adb 100644
Binary files a/icons/esmhead_128x128.png and b/icons/esmhead_128x128.png differ
diff --git a/icons/esmhead_16x16.png b/icons/esmhead_16x16.png
index b332db2..7b843d5 100644
Binary files a/icons/esmhead_16x16.png and b/icons/esmhead_16x16.png differ
diff --git a/icons/esmhead_32x32.png b/icons/esmhead_32x32.png
index 9570f44..af46a32 100644
Binary files a/icons/esmhead_32x32.png and b/icons/esmhead_32x32.png differ
diff --git a/icons/esmhead_48x48.png b/icons/esmhead_48x48.png
index 6d6021f..0992181 100644
Binary files a/icons/esmhead_48x48.png and b/icons/esmhead_48x48.png differ
diff --git a/manifest.json b/manifest.json
index 719734a..d72ea36 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,8 +1,9 @@
{
"manifest_version": 3,
- "name": "Multi Elasticsearch Heads",
- "version": "0.4.3",
- "description": "Manage and visualize multuple Elasticsearch endpoints from within this Chrome extension",
+ "name": "Multi Elasticsearch Head(Custom)",
+ "version": "0.4.4",
+ "version_name": "0.4.4-custom",
+ "description": "Custom version of Multi Elasticsearch Head with real-time index filtering for multi-tenant environments",
"icons": {
"16": "icons/esmhead_16x16.png",
"32": "icons/esmhead_32x32.png",