iobroker.mywebui 1.42.43 → 1.42.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/io-package.json +15 -1
- package/package.json +1 -1
- package/www/assets/icons/neon/expander.svg +1 -0
- package/www/assets/icons/neon/expanderClose.svg +1 -0
- package/www/assets/icons/neon/file.svg +1 -0
- package/www/assets/icons/neon/folder.svg +1 -0
- package/www/dist/frontend/common/IobrokerHandler.js +84 -3
- package/www/dist/frontend/config/CommandHandling.js +5 -3
- package/www/dist/frontend/config/IobrokerWebuiSolutionExplorer.js +247 -9
package/io-package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "mywebui",
|
|
4
|
-
"version": "1.42.
|
|
4
|
+
"version": "1.42.45",
|
|
5
5
|
"titleLang": {
|
|
6
6
|
"en": "mywebui",
|
|
7
7
|
"de": "mywebui",
|
|
@@ -29,6 +29,20 @@
|
|
|
29
29
|
"zh-cn": "使用万维网传送器的高锰用户接口"
|
|
30
30
|
},
|
|
31
31
|
"news": {
|
|
32
|
+
"1.42.45": {
|
|
33
|
+
"en": "design: neon glassmorphic theme for the solution explorer - dark gradient panel, glass project bar, glowing hover/selection, cyan icon set (wunderbaum CSS variables overridden, original icons untouched)",
|
|
34
|
+
"az": "dizayn: solution explorer üçün neon glassmorphic tema - tünd qradiyent panel, şüşə proyekt paneli, parlayan hover/seçim, cyan ikon dəsti (wunderbaum CSS dəyişənləri override edildi, orijinal ikonlara toxunulmadı)",
|
|
35
|
+
"tr": "tasarım: solution explorer için neon glassmorphic tema - koyu gradyan panel, cam proje çubuğu, parlayan hover/seçim, cyan ikon seti (wunderbaum CSS değişkenleri override edildi, orijinal ikonlara dokunulmadı)",
|
|
36
|
+
"ru": "дизайн: неоновая glassmorphic тема для solution explorer - тёмная градиентная панель, стеклянная панель проектов, светящиеся hover/выделение, голубой набор иконок (переопределены CSS-переменные wunderbaum, оригинальные иконки не тронуты)",
|
|
37
|
+
"de": "Design: Neon-Glassmorphic-Theme für den Solution Explorer - dunkles Gradient-Panel, Glas-Projektleiste, leuchtende Hover/Auswahl, Cyan-Icon-Set (wunderbaum-CSS-Variablen überschrieben, Original-Icons unverändert)"
|
|
38
|
+
},
|
|
39
|
+
"1.42.44": {
|
|
40
|
+
"en": "feature: multi-project support - project selector with create/delete in solution explorer; existing data stays as 'default' project, new projects under projects/<name>/; runtime preview passes ?project=, plain runtime.html always shows default (kiosk safe)",
|
|
41
|
+
"az": "yenilik: multi-proyekt dəstəyi - solution explorer-də yarat/sil düymələri ilə proyekt seçici; mövcud data 'default' proyekt kimi qalır, yeni proyektlər projects/<ad>/ altında; runtime önizləmə ?project= ötürür, parametrsiz runtime.html həmişə default göstərir (kiosk təhlükəsiz)",
|
|
42
|
+
"tr": "yenilik: çoklu proje desteği - solution explorer'da oluştur/sil düğmeleriyle proje seçici; mevcut veri 'default' proje olarak kalır, yeni projeler projects/<ad>/ altında; runtime önizleme ?project= geçirir, parametresiz runtime.html her zaman default gösterir (kiosk güvenli)",
|
|
43
|
+
"ru": "новое: поддержка нескольких проектов - селектор проектов с созданием/удалением в solution explorer; существующие данные остаются проектом 'default', новые проекты в projects/<имя>/; предпросмотр runtime передаёт ?project=, runtime.html без параметра всегда показывает default (безопасно для киосков)",
|
|
44
|
+
"de": "Neu: Multi-Projekt-Unterstützung - Projektauswahl mit Erstellen/Löschen im Solution Explorer; bestehende Daten bleiben als 'default'-Projekt, neue Projekte unter projects/<name>/; Runtime-Vorschau übergibt ?project=, runtime.html ohne Parameter zeigt immer default (kiosksicher)"
|
|
45
|
+
},
|
|
32
46
|
"1.42.43": {
|
|
33
47
|
"en": "refactor: all @gokturk413/@node-projects fork packages vendored into www/libs (edited directly in repo, removed from npm dependencies); build script made safe (gulp delAll no longer deletes www frontend sources)",
|
|
34
48
|
"az": "refaktor: bütün @gokturk413/@node-projects fork paketləri www/libs-ə vendorlandı (birbaşa repo-da redaktə olunur, npm asılılıqlarından çıxarıldı); build scripti təhlükəsiz edildi (gulp delAll artıq www frontend mənbələrini silmir)",
|
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="298" height="512" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd" viewBox="0 0 298 511.93"><path fill="#2ed6ff" fill-rule="nonzero" d="M70.77 499.85c-16.24 16.17-42.53 16.09-58.69-.15-16.17-16.25-16.09-42.54.15-58.7l185.5-185.03L12.23 70.93c-16.24-16.16-16.32-42.45-.15-58.7 16.16-16.24 42.45-16.32 58.69-.15l215.15 214.61c16.17 16.25 16.09 42.54-.15 58.7l-215 214.46z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="512" height="298" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd" viewBox="0 0 512 298.04"><path fill="#2ed6ff" fill-rule="nonzero" d="M70.94 285.81c-16.17 16.24-42.46 16.32-58.71.15-16.24-16.16-16.32-42.46-.15-58.7L226.57 12.23c16.16-16.24 42.46-16.32 58.7-.15l214.65 215.18c16.17 16.24 16.09 42.54-.15 58.7-16.25 16.17-42.54 16.09-58.71-.15L256 100.29 70.94 285.81z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?><svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 102.55 122.88" style="enable-background:new 0 0 102.55 122.88" xml:space="preserve"><style type="text/css">.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#2ed6ff;}</style><g><path fill="#2ed6ff" class="st0" d="M102.55,122.88H0V0h77.66l24.89,26.44L102.55,122.88L102.55,122.88z M96.13,115.98V30.44H73.44V5.91H6.51 v110.07H96.13L96.13,115.98z"/></g></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?><svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 122.88 95.21" style="enable-background:new 0 0 122.88 95.21" xml:space="preserve"><g><path fill="#2ed6ff" d="M2.48,20.74h4.5v-9.86c0-1.37,1.11-2.48,2.48-2.48h4.41V2.48c0-1.37,1.11-2.48,2.48-2.48h40.26c1.37,0,2.48,1.11,2.48,2.48 V8.4h54.3c1.37,0,2.48,1.11,2.48,2.48v9.86h4.53c1.37,0,2.48,1.11,2.48,2.48c0,0.18-0.02,0.36-0.06,0.52l-8.68,63.81 c-0.28,2.08-1.19,4.01-2.59,5.41c-1.38,1.38-3.21,2.24-5.36,2.24H14.7c-2.16,0-4.03-0.87-5.43-2.26c-1.41-1.41-2.31-3.35-2.54-5.46 l-6.72-64c-0.14-1.36,0.85-2.58,2.21-2.72C2.31,20.75,2.39,20.75,2.48,20.74L2.48,20.74L2.48,20.74z M9.46,25.71H5.23l6.43,61.27 c0.1,0.98,0.5,1.85,1.1,2.46c0.5,0.5,1.17,0.81,1.93,0.81h91.5c0.75,0,1.38-0.3,1.87-0.79c0.62-0.62,1.03-1.53,1.17-2.55 l8.32-61.19H9.46L9.46,25.71z M11.94,13.37v7.36l98.97-1.05v-6.31h-54.3c-1.37,0-2.48-1.11-2.48-2.48V4.97h-35.3v5.92 c0,1.37-1.11,2.48-2.48,2.48H11.94L11.94,13.37z"/></g></svg>
|
|
@@ -7,12 +7,24 @@ export class IobrokerHandler {
|
|
|
7
7
|
host;
|
|
8
8
|
connection;
|
|
9
9
|
adapterName = "mywebui";
|
|
10
|
-
configPath = "config/";
|
|
11
10
|
namespace = "mywebui.0";
|
|
12
11
|
namespaceFiles = this.namespace + '.data';
|
|
13
12
|
namespaceWidgets = this.namespace + '.widgets';
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
// multi project support: the 'default' project is the legacy flat 'config/' folder,
|
|
14
|
+
// every other project lives under 'projects/<name>/config/' with the same structure
|
|
15
|
+
#currentProject = 'default';
|
|
16
|
+
get currentProject() {
|
|
17
|
+
return this.#currentProject;
|
|
18
|
+
}
|
|
19
|
+
get configPath() {
|
|
20
|
+
return this.#currentProject === 'default' ? 'config/' : 'projects/' + this.#currentProject + '/config/';
|
|
21
|
+
}
|
|
22
|
+
get imagePrefix() {
|
|
23
|
+
return '/' + this.namespaceFiles + '/' + this.configPath + 'images/';
|
|
24
|
+
}
|
|
25
|
+
get additionalFilePrefix() {
|
|
26
|
+
return '/' + this.namespaceFiles + '/' + this.configPath + 'additionalfiles/';
|
|
27
|
+
}
|
|
16
28
|
config;
|
|
17
29
|
globalStylesheet;
|
|
18
30
|
fontDeclarationsStylesheet;
|
|
@@ -38,6 +50,75 @@ export class IobrokerHandler {
|
|
|
38
50
|
this.#cache.set('control', new Map());
|
|
39
51
|
this.#cache.set('3dscreen', new Map());
|
|
40
52
|
this.#cache.set('3dcontrol', new Map());
|
|
53
|
+
// active project: explicit ?project= url parameter wins (used by runtime/kiosk links),
|
|
54
|
+
// otherwise the config editor remembers the last selection per browser.
|
|
55
|
+
// plain runtime.html without parameter always shows the 'default' project,
|
|
56
|
+
// so kiosk displays are not affected by what is edited in the designer.
|
|
57
|
+
try {
|
|
58
|
+
const urlProject = new URLSearchParams(window.location.search).get('project');
|
|
59
|
+
if (urlProject)
|
|
60
|
+
this.#currentProject = this._cleanProjectName(urlProject);
|
|
61
|
+
else if (!window.location.pathname.endsWith('runtime.html'))
|
|
62
|
+
this.#currentProject = this._cleanProjectName(localStorage.getItem('webui-current-project') ?? 'default');
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
console.warn('could not determine current project', e);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
_cleanProjectName(name) {
|
|
69
|
+
name = (name ?? '').toString().trim().toLowerCase().replaceAll(' ', '-');
|
|
70
|
+
if (!/^[a-z0-9_-]+$/.test(name))
|
|
71
|
+
return 'default';
|
|
72
|
+
return name;
|
|
73
|
+
}
|
|
74
|
+
async getProjects() {
|
|
75
|
+
if (this._readyPromises)
|
|
76
|
+
await this.waitForReady();
|
|
77
|
+
try {
|
|
78
|
+
const list = await this._getObjectFromFile('projects.json');
|
|
79
|
+
if (Array.isArray(list?.projects)) {
|
|
80
|
+
const projects = list.projects.filter(x => /^[a-z0-9_-]+$/.test(x));
|
|
81
|
+
if (!projects.includes('default'))
|
|
82
|
+
projects.unshift('default');
|
|
83
|
+
return projects;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (e) { }
|
|
87
|
+
return ['default'];
|
|
88
|
+
}
|
|
89
|
+
async createProject(name) {
|
|
90
|
+
name = (name ?? '').toString().trim().toLowerCase().replaceAll(' ', '-');
|
|
91
|
+
if (!/^[a-z0-9_-]+$/.test(name))
|
|
92
|
+
throw new Error('invalid project name (use a-z, 0-9, -, _): ' + name);
|
|
93
|
+
const projects = await this.getProjects();
|
|
94
|
+
if (projects.includes(name))
|
|
95
|
+
throw new Error('project already exists: ' + name);
|
|
96
|
+
projects.push(name);
|
|
97
|
+
await this._saveObjectToFile({ projects: projects.filter(x => x !== 'default') }, 'projects.json');
|
|
98
|
+
await this._saveObjectToFile({ globalStyle: null, globalScript: null, globalConfig: null, fontDeclarations: null }, 'projects/' + name + '/config/config.json');
|
|
99
|
+
return name;
|
|
100
|
+
}
|
|
101
|
+
async removeProject(name) {
|
|
102
|
+
if (name === 'default')
|
|
103
|
+
throw new Error("the 'default' project cannot be deleted");
|
|
104
|
+
const projects = (await this.getProjects()).filter(x => x !== name && x !== 'default');
|
|
105
|
+
await this._saveObjectToFile({ projects }, 'projects.json');
|
|
106
|
+
try {
|
|
107
|
+
await this.connection.deleteFolder(this.namespaceFiles, '/projects/' + name);
|
|
108
|
+
}
|
|
109
|
+
catch (e) {
|
|
110
|
+
console.warn('error deleting project folder', e);
|
|
111
|
+
}
|
|
112
|
+
if (this.#currentProject === name)
|
|
113
|
+
this.setCurrentProject('default');
|
|
114
|
+
}
|
|
115
|
+
// switching reloads the page: global style/script/translations/custom controls
|
|
116
|
+
// are all project scoped, a clean reload is the only safe way to swap them
|
|
117
|
+
setCurrentProject(name) {
|
|
118
|
+
localStorage.setItem('webui-current-project', this._cleanProjectName(name));
|
|
119
|
+
const url = new URL(window.location.href);
|
|
120
|
+
url.searchParams.delete('project');
|
|
121
|
+
window.location.href = url.toString();
|
|
41
122
|
}
|
|
42
123
|
getNormalizedSignalName(id, relativeSignalPath, element) {
|
|
43
124
|
return (relativeSignalPath ?? '') + id;
|
|
@@ -14,16 +14,18 @@ export class CommandHandling {
|
|
|
14
14
|
let commandName = button.dataset['command'];
|
|
15
15
|
let commandParameter = button.dataset['commandParameter'];
|
|
16
16
|
if (commandName === 'runtime') {
|
|
17
|
+
// preview always opens the project currently active in the editor
|
|
18
|
+
const projectParam = iobrokerHandler.currentProject !== 'default' ? '?project=' + iobrokerHandler.currentProject : '';
|
|
17
19
|
let target = this.dockManager?.activeDocument?.resolvedElementContent;
|
|
18
20
|
if (target instanceof IobrokerWebuiScreenEditor) {
|
|
19
|
-
window.open("runtime.html#screenName=" + target.name);
|
|
21
|
+
window.open("runtime.html" + projectParam + "#screenName=" + target.name);
|
|
20
22
|
}
|
|
21
23
|
else if (target instanceof IobrokerWebui3DScreenEditor) {
|
|
22
24
|
const scName = target.sceneData?.name || target.getAttribute?.('scene-name') || '';
|
|
23
|
-
window.open("runtime.html#3dscreen=" + encodeURIComponent(scName));
|
|
25
|
+
window.open("runtime.html" + projectParam + "#3dscreen=" + encodeURIComponent(scName));
|
|
24
26
|
}
|
|
25
27
|
else {
|
|
26
|
-
window.open("runtime.html");
|
|
28
|
+
window.open("runtime.html" + projectParam);
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
31
|
else if (commandName === 'new') {
|
|
@@ -26,28 +26,201 @@ import { IobrokerWebuiScreensView } from "./IobrokerWebuiScreensView.js";
|
|
|
26
26
|
import { convertToXml, parseXml } from "../helper/XmlHelper.js";
|
|
27
27
|
export class IobrokerWebuiSolutionExplorer extends BaseCustomWebComponentConstructorAppend {
|
|
28
28
|
static template = html `
|
|
29
|
-
<div
|
|
29
|
+
<div style="display: flex; flex-direction: column; width: 100%; height: 100%;">
|
|
30
|
+
<div id="projectBar">
|
|
31
|
+
<select id="projectSelect" title="active project"></select>
|
|
32
|
+
<button id="projectAdd" title="create new project">+</button>
|
|
33
|
+
<button id="projectDel" title="delete current project">✕</button>
|
|
34
|
+
</div>
|
|
35
|
+
<div id="treeDiv" class="" style="overflow: auto; width:100%; flex: 1;">
|
|
36
|
+
</div>
|
|
30
37
|
</div>`;
|
|
31
38
|
static style = css `
|
|
39
|
+
:host {
|
|
40
|
+
display: block;
|
|
41
|
+
height: 100%;
|
|
42
|
+
box-sizing: border-box;
|
|
43
|
+
--neo-cyan: #2ed6ff;
|
|
44
|
+
--neo-purple: #a06bff;
|
|
45
|
+
--neo-pink: #ff4fa3;
|
|
46
|
+
--neo-text: #dce7ff;
|
|
47
|
+
--neo-text-dim: #8fa3cc;
|
|
48
|
+
--neo-glass: rgba(22, 34, 76, 0.55);
|
|
49
|
+
--neo-glass-border: rgba(80, 190, 255, 0.35);
|
|
50
|
+
background:
|
|
51
|
+
radial-gradient(ellipse 120% 60% at 80% -10%, rgba(110, 70, 220, 0.25), transparent 60%),
|
|
52
|
+
radial-gradient(ellipse 100% 50% at 0% 110%, rgba(0, 150, 255, 0.18), transparent 60%),
|
|
53
|
+
linear-gradient(165deg, #0b1228 0%, #111a3a 55%, #151d45 100%);
|
|
54
|
+
color: var(--neo-text);
|
|
55
|
+
|
|
56
|
+
/* wunderbaum theme variables (override its :host defaults) */
|
|
57
|
+
--wb-font-stack: 'Segoe UI', Roboto, sans-serif;
|
|
58
|
+
--wb-background-color: transparent;
|
|
59
|
+
--wb-node-text-color: var(--neo-text);
|
|
60
|
+
--wb-dim-color: var(--neo-text-dim);
|
|
61
|
+
--wb-filter-dim-color: var(--neo-text-dim);
|
|
62
|
+
--wb-filter-submatch-color: var(--neo-text-dim);
|
|
63
|
+
--wb-hover-color: rgba(90, 160, 255, 0.12);
|
|
64
|
+
--wb-hover-border-color: rgba(80, 190, 255, 0.3);
|
|
65
|
+
--wb-active-color: rgba(46, 214, 255, 0.2);
|
|
66
|
+
--wb-active-border-color: rgba(46, 214, 255, 0.55);
|
|
67
|
+
--wb-active-hover-color: rgba(46, 214, 255, 0.28);
|
|
68
|
+
--wb-active-hover-border-color: var(--neo-cyan);
|
|
69
|
+
--wb-active-color-grayscale: rgba(46, 214, 255, 0.14);
|
|
70
|
+
--wb-active-hover-color-grayscale: rgba(46, 214, 255, 0.2);
|
|
71
|
+
--wb-active-border-color-grayscale: rgba(80, 190, 255, 0.4);
|
|
72
|
+
--wb-alternate-row-color: transparent;
|
|
73
|
+
--wb-alternate-row-color-hover: rgba(90, 160, 255, 0.08);
|
|
74
|
+
--wb-focus-border-color: var(--neo-cyan);
|
|
75
|
+
--wb-border-color: rgba(80, 190, 255, 0.35);
|
|
76
|
+
--wb-drop-target-color: rgba(46, 214, 255, 0.18);
|
|
77
|
+
--wb-drop-source-color: rgba(160, 107, 255, 0.15);
|
|
78
|
+
}
|
|
79
|
+
|
|
32
80
|
* {
|
|
33
81
|
user-select: none;
|
|
34
82
|
-webkit-user-select: none;
|
|
35
83
|
cursor: pointer;
|
|
36
84
|
}
|
|
37
|
-
|
|
85
|
+
|
|
86
|
+
#projectBar {
|
|
87
|
+
display: flex;
|
|
88
|
+
gap: 5px;
|
|
89
|
+
padding: 6px 7px;
|
|
90
|
+
margin: 7px 7px 5px 7px;
|
|
91
|
+
align-items: center;
|
|
92
|
+
flex-shrink: 0;
|
|
93
|
+
background: var(--neo-glass);
|
|
94
|
+
border: 1px solid var(--neo-glass-border);
|
|
95
|
+
border-radius: 11px;
|
|
96
|
+
box-shadow: 0 0 12px rgba(46, 214, 255, 0.12), inset 0 0 18px rgba(46, 214, 255, 0.05);
|
|
97
|
+
backdrop-filter: blur(8px);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
#projectBar select {
|
|
101
|
+
flex: 1;
|
|
102
|
+
min-width: 0;
|
|
103
|
+
font-size: 13px;
|
|
104
|
+
color: var(--neo-text);
|
|
105
|
+
background: rgba(10, 18, 45, 0.6);
|
|
106
|
+
border: 1px solid rgba(80, 190, 255, 0.25);
|
|
107
|
+
border-radius: 7px;
|
|
108
|
+
padding: 3px 6px;
|
|
109
|
+
outline: none;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
#projectBar select option {
|
|
113
|
+
background: #101a3d;
|
|
114
|
+
color: var(--neo-text);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
#projectBar button {
|
|
118
|
+
border: 1px solid rgba(80, 190, 255, 0.35);
|
|
119
|
+
background: rgba(10, 18, 45, 0.6);
|
|
120
|
+
color: var(--neo-cyan);
|
|
121
|
+
border-radius: 7px;
|
|
122
|
+
font-size: 13px;
|
|
123
|
+
line-height: 18px;
|
|
124
|
+
width: 26px;
|
|
125
|
+
height: 26px;
|
|
126
|
+
padding: 0;
|
|
127
|
+
text-shadow: 0 0 6px rgba(46, 214, 255, 0.8);
|
|
128
|
+
transition: box-shadow 0.15s, background 0.15s;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
#projectBar button:hover {
|
|
132
|
+
background: rgba(46, 214, 255, 0.15);
|
|
133
|
+
box-shadow: 0 0 10px rgba(46, 214, 255, 0.45);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
#treeDiv {
|
|
137
|
+
margin: 0 4px 4px 4px;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
#treeDiv::-webkit-scrollbar {
|
|
141
|
+
width: 8px;
|
|
142
|
+
height: 8px;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
#treeDiv::-webkit-scrollbar-track {
|
|
146
|
+
background: transparent;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
#treeDiv::-webkit-scrollbar-thumb {
|
|
150
|
+
background: rgba(80, 190, 255, 0.25);
|
|
151
|
+
border-radius: 4px;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
#treeDiv::-webkit-scrollbar-thumb:hover {
|
|
155
|
+
background: rgba(80, 190, 255, 0.45);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
div.wunderbaum {
|
|
159
|
+
background: transparent;
|
|
160
|
+
color: var(--neo-text);
|
|
161
|
+
font-family: 'Segoe UI', Roboto, sans-serif;
|
|
162
|
+
font-size: 13px;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
div.wunderbaum div.wb-node-list div.wb-row {
|
|
166
|
+
border-radius: 7px;
|
|
167
|
+
transition: background 0.12s, box-shadow 0.12s;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
div.wunderbaum div.wb-node-list div.wb-row:hover {
|
|
171
|
+
background: rgba(90, 160, 255, 0.12);
|
|
172
|
+
box-shadow: inset 0 0 0 1px rgba(80, 190, 255, 0.18);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
div.wunderbaum div.wb-node-list div.wb-row.wb-active,
|
|
176
|
+
div.wunderbaum div.wb-node-list div.wb-row.wb-selected {
|
|
177
|
+
background: linear-gradient(90deg, rgba(46, 214, 255, 0.22), rgba(160, 107, 255, 0.16));
|
|
178
|
+
box-shadow: inset 0 0 0 1px rgba(46, 214, 255, 0.4), 0 0 10px rgba(46, 214, 255, 0.15);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
div.wunderbaum div.wb-node-list div.wb-row.wb-active:hover,
|
|
182
|
+
div.wunderbaum div.wb-node-list div.wb-row.wb-selected:hover {
|
|
183
|
+
background: linear-gradient(90deg, rgba(46, 214, 255, 0.3), rgba(160, 107, 255, 0.22));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
div.wunderbaum span.wb-node span.wb-title {
|
|
187
|
+
overflow-x: visible;
|
|
188
|
+
color: var(--neo-text);
|
|
189
|
+
text-shadow: 0 0 8px rgba(46, 214, 255, 0.12);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
div.wunderbaum div.wb-row.wb-active span.wb-node span.wb-title {
|
|
193
|
+
color: #ffffff;
|
|
194
|
+
text-shadow: 0 0 8px rgba(46, 214, 255, 0.55);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* neon recolor of the dark svg icons (folder/file/expander) */
|
|
198
|
+
div.wunderbaum i.wb-icon img,
|
|
199
|
+
div.wunderbaum i.wb-expander img {
|
|
200
|
+
filter: invert(72%) sepia(35%) saturate(1800%) hue-rotate(160deg) brightness(1.25) drop-shadow(0 0 2px rgba(46, 214, 255, 0.65));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
div.wunderbaum i.wb-icon,
|
|
204
|
+
div.wunderbaum i.wb-expander {
|
|
205
|
+
color: var(--neo-cyan);
|
|
206
|
+
}
|
|
207
|
+
|
|
38
208
|
div.wunderbaum span.wb-node i.wb-indent::before {
|
|
39
209
|
content: "";
|
|
40
210
|
}
|
|
41
|
-
|
|
211
|
+
|
|
42
212
|
i.wb-icon > span.wb-badge {
|
|
43
|
-
opacity:
|
|
213
|
+
opacity: 1;
|
|
44
214
|
font-size: 50%;
|
|
215
|
+
background: linear-gradient(135deg, var(--neo-cyan), var(--neo-purple));
|
|
216
|
+
color: #08122e;
|
|
217
|
+
border-radius: 8px;
|
|
218
|
+
padding: 1px 4px;
|
|
219
|
+
font-weight: bold;
|
|
220
|
+
text-shadow: none;
|
|
221
|
+
box-shadow: 0 0 6px rgba(46, 214, 255, 0.5);
|
|
45
222
|
}
|
|
46
|
-
|
|
47
|
-
div.wunderbaum span.wb-node span.wb-title {
|
|
48
|
-
overflow-x: visible;
|
|
49
|
-
}
|
|
50
|
-
|
|
223
|
+
|
|
51
224
|
div.wunderbaum span.wb-col {
|
|
52
225
|
overflow: visible;
|
|
53
226
|
}
|
|
@@ -85,9 +258,64 @@ export class IobrokerWebuiSolutionExplorer extends BaseCustomWebComponentConstru
|
|
|
85
258
|
constructor() {
|
|
86
259
|
super();
|
|
87
260
|
this._treeDiv = this._getDomElement('treeDiv');
|
|
261
|
+
this._projectSelect = this._getDomElement('projectSelect');
|
|
262
|
+
this._projectAdd = this._getDomElement('projectAdd');
|
|
263
|
+
this._projectDel = this._getDomElement('projectDel');
|
|
88
264
|
this.shadowRoot.adoptedStyleSheets = [wunderbaumStyle, defaultStyle, IobrokerWebuiSolutionExplorer.style];
|
|
89
265
|
this._createThumbnailTooltip();
|
|
90
266
|
}
|
|
267
|
+
async _initProjectBar() {
|
|
268
|
+
const fillSelect = async () => {
|
|
269
|
+
const projects = await iobrokerHandler.getProjects();
|
|
270
|
+
this._projectSelect.innerHTML = '';
|
|
271
|
+
for (const p of projects) {
|
|
272
|
+
const opt = document.createElement('option');
|
|
273
|
+
opt.value = p;
|
|
274
|
+
opt.textContent = p;
|
|
275
|
+
if (p === iobrokerHandler.currentProject)
|
|
276
|
+
opt.selected = true;
|
|
277
|
+
this._projectSelect.appendChild(opt);
|
|
278
|
+
}
|
|
279
|
+
// if the stored project does not exist anymore, fall back to default
|
|
280
|
+
if (!projects.includes(iobrokerHandler.currentProject))
|
|
281
|
+
iobrokerHandler.setCurrentProject('default');
|
|
282
|
+
};
|
|
283
|
+
await fillSelect();
|
|
284
|
+
this._projectSelect.onchange = () => {
|
|
285
|
+
if (this._projectSelect.value !== iobrokerHandler.currentProject)
|
|
286
|
+
iobrokerHandler.setCurrentProject(this._projectSelect.value);
|
|
287
|
+
};
|
|
288
|
+
this._projectAdd.onclick = async () => {
|
|
289
|
+
const name = prompt('New project name (a-z, 0-9, -, _):');
|
|
290
|
+
if (!name)
|
|
291
|
+
return;
|
|
292
|
+
try {
|
|
293
|
+
const created = await iobrokerHandler.createProject(name);
|
|
294
|
+
if (confirm("Project '" + created + "' created. Switch to it now?"))
|
|
295
|
+
iobrokerHandler.setCurrentProject(created);
|
|
296
|
+
else
|
|
297
|
+
await fillSelect();
|
|
298
|
+
}
|
|
299
|
+
catch (err) {
|
|
300
|
+
alert(err.message ?? err);
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
this._projectDel.onclick = async () => {
|
|
304
|
+
const current = iobrokerHandler.currentProject;
|
|
305
|
+
if (current === 'default') {
|
|
306
|
+
alert("The 'default' project cannot be deleted.");
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if (!confirm("Delete project '" + current + "' with ALL its screens, controls and files? This cannot be undone!"))
|
|
310
|
+
return;
|
|
311
|
+
try {
|
|
312
|
+
await iobrokerHandler.removeProject(current);
|
|
313
|
+
}
|
|
314
|
+
catch (err) {
|
|
315
|
+
alert(err.message ?? err);
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
}
|
|
91
319
|
_createThumbnailTooltip() {
|
|
92
320
|
this._thumbnailTooltip = document.createElement('div');
|
|
93
321
|
this._thumbnailTooltip.className = 'control-thumbnail-tooltip';
|
|
@@ -102,6 +330,7 @@ export class IobrokerWebuiSolutionExplorer extends BaseCustomWebComponentConstru
|
|
|
102
330
|
});
|
|
103
331
|
iobrokerHandler.imagesChanged.on(() => this._refreshNode('images'));
|
|
104
332
|
iobrokerHandler.additionalFilesChanged.on(() => this._refreshAdditionalFilesNode());
|
|
333
|
+
this._initProjectBar();
|
|
105
334
|
await sleep(100);
|
|
106
335
|
this._loadTree();
|
|
107
336
|
//Preload after a timeout
|
|
@@ -998,6 +1227,15 @@ export class IobrokerWebuiSolutionExplorer extends BaseCustomWebComponentConstru
|
|
|
998
1227
|
if (!this._tree) {
|
|
999
1228
|
this._tree = new Wunderbaum({
|
|
1000
1229
|
...defaultOptions,
|
|
1230
|
+
// neon themed copies of the default icons (originals are shared with the outline tree)
|
|
1231
|
+
iconMap: {
|
|
1232
|
+
expanderCollapsed: './assets/icons/neon/expander.svg',
|
|
1233
|
+
expanderExpanded: './assets/icons/neon/expanderClose.svg',
|
|
1234
|
+
folder: './assets/icons/neon/folder.svg',
|
|
1235
|
+
folderLazy: './assets/icons/neon/folder.svg',
|
|
1236
|
+
folderOpen: './assets/icons/neon/folder.svg',
|
|
1237
|
+
doc: './assets/icons/neon/file.svg',
|
|
1238
|
+
},
|
|
1001
1239
|
element: this._treeDiv,
|
|
1002
1240
|
source: await this.createTreeNodes(),
|
|
1003
1241
|
lazyLoad: async (e) => {
|